From 147e19865be3abcc5bede05153dd60747c6836b8 Mon Sep 17 00:00:00 2001 From: Dany Thach Date: Fri, 30 Dec 2022 18:19:22 +0100 Subject: [PATCH] Exportable CHR ROM --- src-tauri/src/main.rs | 67 ++++++++++++++++++++++++++----------- src-tauri/src/nes_parser.rs | 58 -------------------------------- src/index.html | 5 ++- src/script.js | 20 ++++++++--- src/style.css | 23 +++++++++++++ 5 files changed, 89 insertions(+), 84 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b8bb0c7..8ea0179 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -6,8 +6,7 @@ mod nes_parser; use std::fs::File; -use std::io::Error; -use std::io::Read; +use std::io::{Error, Read, Seek, Write}; use std::path::PathBuf; use tauri::Manager; use nes_parser::INesVersion; @@ -35,7 +34,6 @@ struct INesData { tv_system: bool, } -// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command #[tauri::command] fn select_file(app: tauri::AppHandle) { tauri::api::dialog::FileDialogBuilder::default() @@ -68,18 +66,6 @@ fn select_file(app: tauri::AppHandle) { return; } match version { - Ok(INesVersion::INes20) => { - let header = nes_parser::INes20Header::from_rom(&contents); - match header { - Ok(header) => { - println!("PRG ROM size: {}", header.get_prg_rom_size()); - }, - Err(error) => { - app.emit_all("error", Payload { message: nes_parser::get_error_message(error).into() }).unwrap(); - return; - } - } - }, Ok(INesVersion::INes) => { let header = nes_parser::INesHeader::from_rom(&contents); match header { @@ -110,8 +96,11 @@ fn select_file(app: tauri::AppHandle) { }, } }, + Ok(INesVersion::INes20) => { + app.emit_all("error", Payload { message: "Can't parse iNES 2.0 ROMs yet. Sorry!".into() }).unwrap(); + }, Ok(INesVersion::INesArchaic) => { - app.emit_all("error", Payload { message: "Gotta parse an old iNES version huh".into() }).unwrap(); + app.emit_all("error", Payload { message: "Can't parse Archaic iNES ROMs yet. Sorry!".into() }).unwrap(); }, Err(error) => { app.emit_all("error", Payload { message: nes_parser::get_error_message(error).into() }).unwrap(); @@ -123,7 +112,7 @@ fn select_file(app: tauri::AppHandle) { } #[tauri::command] -fn download_chr_ram(filepath: &str, app: tauri::AppHandle) { +fn download_chr_rom(filepath: &str, app: tauri::AppHandle) { let path_buf: PathBuf = PathBuf::from(filepath); if !path_buf.is_file() { app.emit_all("error", Payload { message: "File not found".into() }).unwrap(); @@ -154,11 +143,49 @@ fn download_chr_ram(filepath: &str, app: tauri::AppHandle) { app.emit_all("error", Payload { message: "This ROM has no CHR ROM".into() }).unwrap(); return; } - let chr_rom_pointer: u32 = + let chr_rom_pointer: u64 = 16 + // header size (if header.has_trainer() { 512 } else { 0 }) + - header.prg_rom_size(); + (header.prg_rom_size() as u64); + let chr_rom_size: u32 = header.chr_rom_size(); let mut chr_rom: Vec = Vec::new(); + let seek_result: Result = file.seek(std::io::SeekFrom::Start(chr_rom_pointer as u64)); + if seek_result.is_err() { + app.emit_all("error", Payload { message: "Unable to find CHR ROM".into() }).unwrap(); + return; + } + let read_result: Result = file.take(chr_rom_size as u64).read_to_end(&mut chr_rom); + if read_result.is_err() { + app.emit_all("error", Payload { message: "Unable to read CHR ROM".into() }).unwrap(); + return; + } + tauri::api::dialog::FileDialogBuilder::default() + .set_file_name("chr_rom.bin") + .save_file(move |file_path: Option| { + match file_path { + Some(file_path) => { + let output_file: Result = File::create(file_path); + match output_file { + Ok(mut output_file) => { + let write_result: Result<(), Error> = output_file.write_all(&chr_rom); + if write_result.is_err() { + app.emit_all("error", Payload { message: "Unable to write file".into() }).unwrap(); + return; + } + app.emit_all("success", Payload { message: "CHR ROM exported successfully!".into() }).unwrap(); + return; + }, + Err(_) => { + app.emit_all("error", Payload { message: "Unable to write file".into() }).unwrap(); + return; + } + } + }, + None => { + + } + } + }); /* TODO find CHR ROM and download it */ } Err(error) => { @@ -178,7 +205,7 @@ fn download_chr_ram(filepath: &str, app: tauri::AppHandle) { fn main() { tauri::Builder::default() - .invoke_handler(tauri::generate_handler![select_file, download_chr_ram]) + .invoke_handler(tauri::generate_handler![select_file, download_chr_rom]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/nes_parser.rs b/src-tauri/src/nes_parser.rs index e37b1f7..b7e0314 100644 --- a/src-tauri/src/nes_parser.rs +++ b/src-tauri/src/nes_parser.rs @@ -101,64 +101,6 @@ impl INesHeader { } -pub struct INes20Header { - prg_rom_size: u16, - chr_rom_size: u16, - mirroring_type: bool, - has_battery: bool, - has_trainer: bool, - has_four_screen_vram: bool, - console_type: u8, - mapper: u16, - submapper: u8, - prg_ram_shift_count: u8, - prg_nvram_shift_count: u8, - chr_ram_shift_count: u8, - chr_nvram_shift_count: u8, - cpu_type: u8, - console_specific_type: u8, - miscellaneous_roms: u8, - default_expansion_device: u8, -} - -impl INes20Header { - - pub fn from_rom(rom_bytes: &Vec) -> Result { - if rom_bytes[0] != 0x4e || rom_bytes[1] != 0x45 || rom_bytes[2] != 0x53 || rom_bytes[3] != 0x1a { - return Err(INesError::NotAnINesFile); - } - Ok( - INes20Header { - prg_rom_size: (rom_bytes[4] as u16) | (((rom_bytes[9] as u16) << 8) & 0x0f00), - chr_rom_size: (rom_bytes[5] as u16) | (((rom_bytes[9] as u16) << 4) & 0x0f00), - mirroring_type: (rom_bytes[6] & 0x01) != 0, - has_battery: (rom_bytes[6] & 0x02) != 0, - has_trainer: (rom_bytes[6] & 0x04) != 0, - has_four_screen_vram: (rom_bytes[6] & 0x08) != 0, - console_type: rom_bytes[7] & 0x03, - mapper: - (((rom_bytes[6] as u16) >> 4) & 0x000f) | - (((rom_bytes[7] as u16) ) & 0x00f0) | - (((rom_bytes[8] as u16) << 8) & 0x0f00), - submapper: (rom_bytes[8] >> 4) & 0x0f, - prg_ram_shift_count: rom_bytes[10] & 0x0f, - prg_nvram_shift_count: (rom_bytes[10] >> 4) & 0x0f, - chr_ram_shift_count: rom_bytes[11] & 0x0f, - chr_nvram_shift_count: (rom_bytes[11] >> 4) & 0x0f, - cpu_type: rom_bytes[12] & 0x03, - console_specific_type: rom_bytes[13], - miscellaneous_roms: rom_bytes[14] & 0x03, - default_expansion_device: rom_bytes[16] & 0x3f, - } - ) - } - - pub fn get_prg_rom_size(self) -> u16 { - self.prg_rom_size - } - -} - pub fn get_error_message(error: INesError) -> &'static str { match error { INesError::NotAnINesFile => "Not an iNES file", diff --git a/src/index.html b/src/index.html index d3e231e..b6fa45d 100644 --- a/src/index.html +++ b/src/index.html @@ -22,11 +22,14 @@
    -
      +

      +
      +

      +
      \ No newline at end of file diff --git a/src/script.js b/src/script.js index 7f46280..ee23d5d 100644 --- a/src/script.js +++ b/src/script.js @@ -19,7 +19,7 @@ listen("loaded_ines", e => { ["File path", e.payload.filepath], ["File name", e.payload.filename], ["PRG ROM size", e.payload.prg_rom_size+" bytes"], - ["CHR ROM size", e.payload.chr_rom_size+" bytes"], + ["CHR ROM size", e.payload.chr_rom_size+" bytes"+(0 < e.payload.chr_rom_size ? "" : " (uses CHR RAM)")], ["Mirroring Type", e.payload.mirroring_type ? "Vertical" : "Horizontal"], ["Has persistent memory", e.payload.has_battery ? "Yes" : "No"], ["Has trainer", e.payload.has_trainer ? "Yes" : "No"], @@ -35,13 +35,15 @@ listen("loaded_ines", e => { domElement.innerHTML = ""+el[0]+": "+el[1]; fileProperties.appendChild(domElement); }); + actions.innerHTML = ''; if(e.payload.chr_rom_size > 0) { let chrRomDownloadButton = document.createElement("button"); chrRomDownloadButton.type = "button"; - chrRomDownloadButton.innerText = "Download CHR RAM"; + chrRomDownloadButton.innerText = "Download CHR ROM"; + chrRomDownloadButton.classList.add("download-chr-rom-button") let filepath = e.payload.filepath; chrRomDownloadButton.addEventListener("click", e => { - invoke("download_chr_ram", { + invoke("download_chr_rom", { filepath: filepath, }) }); @@ -58,11 +60,19 @@ listen("loading", e => { document.body.classList.add("is-loading"); }); - listen("error", e => { +listen("error", e => { document.body.classList.remove( "is-loading", - "file-loaded", ); document.body.classList.add("no-file-selected", "has-error"); errorMessage.innerText = e.payload.message; +}); + +listen("success", e => { + document.body.classList.remove( + "is-loading", + "has-error", + ); + document.body.classList.add("has-success"); + successMessage.innerText = e.payload.message; }); \ No newline at end of file diff --git a/src/style.css b/src/style.css index 8226946..0eaafc6 100644 --- a/src/style.css +++ b/src/style.css @@ -53,4 +53,27 @@ main { body.has-error .error-wrapper { display: block; +} + +.success-wrapper { + display: none; + background-color: #a3cfbb; + color: #0a3622; + border: 1px solid #0a3622; + border-radius: 0.5rem; + padding: 0.5rem 2rem; + margin-top: 1rem; +} + +body.has-success .success-wrapper { + display: block; +} + +.actions-wrapper .download-chr-rom-button { + display: block; + margin: 0 auto; + background-color: #aaa; + border: 0; + border-radius: 0.25rem; + padding: 0.5rem 1rem; } \ No newline at end of file