diff --git a/Cargo.lock b/Cargo.lock index 5de52f3..77fdf28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,16 +8,6 @@ version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" -[[package]] -name = "base_patch_generator" -version = "0.1.0" -dependencies = [ - "anyhow", - "enemize", - "md5", - "serde_json", -] - [[package]] name = "bin_comp" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 636360e..16d8175 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,4 @@ members = [ "enemize", "bin_comp", - "base_patch_generator", ] diff --git a/base_patch_generator/Cargo.toml b/base_patch_generator/Cargo.toml deleted file mode 100644 index 746828c..0000000 --- a/base_patch_generator/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "base_patch_generator" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow = "1.0" -enemize = { path = "../enemize" } -md5 = "0.7.0" -serde_json = "1.0" diff --git a/base_patch_generator/src/main.rs b/base_patch_generator/src/main.rs deleted file mode 100644 index 4abf411..0000000 --- a/base_patch_generator/src/main.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::Read; -use std::path::Path; - -use enemize::PatchSet; -use enemize::asar; -use enemize::rom::RomData; - -fn main() -> Result<(), anyhow::Error> { - let mut args = env::args(); - - let output_path = args.next_back().expect("No output file"); - let symbols_path = args.next_back().expect("No symbols file"); - let patch_path = args.next_back().expect("No patch file"); - let rom_path = args.next_back().expect("No ROM file"); - - let mut rom_bytes = vec![]; - let mut rom_file = File::open(&rom_path)?; - rom_file.read_to_end(&mut rom_bytes)?; - - rom_bytes = if rom_bytes.len() % 1024 == 512 { - rom_bytes.into_iter().skip(512).collect() - } else { - rom_bytes - }; - - let expected = [ - 0x03, 0xa6, 0x39, 0x45, 0x39, 0x81, 0x91, 0x33, 0x7e, 0x89, 0x6e, 0x57, 0x71, 0xf7, 0x71, - 0x73, - ]; - let actual: [u8; 16] = md5::compute(&rom_bytes).into(); - assert_eq!(expected, actual, "Invalid rom file"); - - println!("Applying Patch to rom"); - - rom_bytes.resize(4 * 1024 * 1024, 0); - - let symbols = asar::load_symbols(Path::new(&symbols_path))?; - - let mut rom = RomData::new(symbols, rom_bytes); - - let mut patches = PatchSet::load(Path::new(&patch_path))?; - patches.patch_rom(&mut rom); - - rom.move_room_headers(); - - let rom_patches = rom.generate_patch(); - patches.add_patches(rom_patches); - - println!("Writing output file {}", output_path); - let out_file = File::create(&output_path)?; - serde_json::to_writer(out_file, &patches)?; - - Ok(()) -} diff --git a/enemize/src/asar.rs b/enemize/src/asar.rs index f405e9d..6324302 100644 --- a/enemize/src/asar.rs +++ b/enemize/src/asar.rs @@ -1,44 +1,33 @@ use std::collections::HashMap; use std::fs::File; -use std::io::{BufReader, BufRead}; +use std::io::{BufReader, Read}; use std::path::Path; -pub type Symbols = HashMap; +pub type Symbols = HashMap; -pub fn load_symbols(filename: &Path) -> anyhow::Result { - let file = File::open(filename)?; - let reader = BufReader::new(file); +impl Symbols { + pub fn load(filename: Path) -> anyhow::Result { + let file = File::open(filename)?; + let mut reader = BufReader::new(file); - let symbols = reader.lines().filter_map(|l| l.ok().and_then(|line| { - let mut words = line.split_ascii_whitespace(); - match (words.next(), words.next(), words.next()) { - // Get only two-word lines. - (Some(address), Some(symbol), None) => - Some((symbol.to_owned(), address.to_owned())), - - _ => None - } - }) - .and_then(|(symbol, mut address)| { + reader.lines().filter_map(|l| l.ok().and_then(|line| { + let words: Vec = line.split_ascii_whitespace().collect(); + if words.len() == 2 { + Some((words[1], words[0])) + } else { + None + } + })) + .filter_map(|(symbol, mut address)| { if let Some(colon_at) = address.find(':') { address.remove(colon_at); } - // Filter out the ones that don't have a hexadecimal number as the first word. let snes_address = u32::from_str_radix(&address, 16).ok()?; - let pc_address = snes_to_pc_address(snes_address); + let pc_address = (snes_address & 0x7FFF) + ((addr / 2) & 0xFF8000); Some((symbol, pc_address)) - })).collect(); - - Ok(symbols) -} - -pub fn snes_to_pc_address(snes: u32) -> usize { - ((snes & 0x7FFF) + ((snes / 2) & 0xFF8000)) as usize -} - -pub fn pc_to_snes_address(pc: usize) -> u32 { - ((pc & 0x7FFF) + ((pc & 0xFF8000) * 2)) as u32 + }).collect() + } } diff --git a/enemize/src/constants.rs b/enemize/src/constants.rs index 630ff5b..716dfde 100644 --- a/enemize/src/constants.rs +++ b/enemize/src/constants.rs @@ -1,4 +1,4 @@ -pub const ROOM_HEADER_BANK_LOCATION: usize = 0x0B5E7; +pub const ROM_HEADER_BANK_LOCATION: usize = 0x0B5E7; pub const DUNGEON_HEADER_POINTER_TABLE: usize = 0x271E2; pub const DUNGEON_SPRITE_POINTER_TABLE: usize = 0x4D62E; pub const OBJECT_DATA_POINTER_TABLE: usize = 0xF8000; diff --git a/enemize/src/lib.rs b/enemize/src/lib.rs index e5c016e..201bba8 100644 --- a/enemize/src/lib.rs +++ b/enemize/src/lib.rs @@ -7,7 +7,6 @@ use thiserror::Error; use crate::rom::RomData; -pub mod asar; pub mod bosses; pub mod constants; pub mod option_flags; @@ -42,20 +41,12 @@ impl PatchSet { }) } - pub fn add_patch(&mut self, patch: Patch) { - self.patches.push(patch); - } - - pub fn add_patches(&mut self, patches: Vec) { - self.patches.extend(patches); - } - pub fn filename(&self) -> &Path { self.filename.as_path() } - pub fn patch_rom(&self, rom: &mut RomData) { - for patch in self.patches.iter() { + pub fn patch_rom(self, rom: &mut RomData) { + for patch in self.patches { rom.patch_data(patch); } } diff --git a/enemize/src/rom.rs b/enemize/src/rom.rs index 8b7a2e0..2807194 100644 --- a/enemize/src/rom.rs +++ b/enemize/src/rom.rs @@ -4,7 +4,6 @@ use std::ops::Range; use anyhow::bail; -use crate::asar::{Symbols, snes_to_pc_address, pc_to_snes_address}; use crate::constants::*; use crate::option_flags::OptionFlags; use crate::Patch; @@ -29,7 +28,8 @@ pub const CHECKSUM_ADDRESS: usize = 0x7FDE; pub const RANDOMIZER_MODE_FLAG: usize = 0x180032; pub struct RomData { - asar_symbols: Symbols, + pub enemizer_info_table_base_address: usize, + pub enemizer_option_flags_base_address: usize, spoiler: String, rom_data: Vec, patch_data: BTreeMap, @@ -65,8 +65,8 @@ impl RomData { pub fn is_enemizer(&self) -> bool { self.rom_data.len() == ENEMIZER_FILE_LENGTH - && self.rom_data[self.asar_symbols["enemizer_info_table"] + ENEMIZER_INFO_SEED_OFFSET] == b'E' - && self.rom_data[self.asar_symbols["enemizer_info_table"] + ENEMIZER_INFO_SEED_OFFSET + 1] == b'N' + && self.rom_data[self.enemizer_info_table_base_address + ENEMIZER_INFO_SEED_OFFSET] == b'E' + && self.rom_data[self.enemizer_info_table_base_address + ENEMIZER_INFO_SEED_OFFSET + 1] == b'N' } fn assert_rom_length(&self) { @@ -79,7 +79,7 @@ impl RomData { pub fn derive_enemizer_seed(&mut self) -> anyhow::Result { if self.seed < 0 && self.is_enemizer() { - let seed_start = self.asar_symbols["enemizer_info_table"] + ENEMIZER_INFO_SEED_OFFSET; + let seed_start = self.enemizer_info_table_base_address + ENEMIZER_INFO_SEED_OFFSET; let seed_end = seed_start + ENEMIZER_INFO_SEED_LENGTH; let seed_bytes = &self.rom_data[seed_start..seed_end]; let seed_str = &std::str::from_utf8(seed_bytes)?.trim_end_matches('\0')[2..]; @@ -96,7 +96,7 @@ impl RomData { let seed_str = format!("EN{:<10}", seed); let bytes = seed_str.as_bytes().to_owned(); - let seed_start = self.asar_symbols["enemizer_info_table"] + ENEMIZER_INFO_SEED_OFFSET; + let seed_start = self.enemizer_info_table_base_address + ENEMIZER_INFO_SEED_OFFSET; let seed_end = seed_start + ENEMIZER_INFO_SEED_LENGTH; self.seed = seed; @@ -106,7 +106,7 @@ impl RomData { pub fn get_enemizer_version(&self) -> anyhow::Result<&str> { if self.is_enemizer() { - let version_start = self.asar_symbols["enemizer_info_table"] + ENEMIZER_INFO_VERSION_OFFSET; + let version_start = self.enemizer_info_table_base_address + ENEMIZER_INFO_VERSION_OFFSET; let version_end = version_start + ENEMIZER_INFO_VERSION_LENGTH; Ok(std::str::from_utf8(&self.rom_data[version_start..version_end])?) } else { @@ -120,7 +120,7 @@ impl RomData { let mut bytes = version.into_bytes(); bytes.resize(ENEMIZER_INFO_VERSION_LENGTH, 0); - let version_start = self.asar_symbols["enemizer_info_table"] + ENEMIZER_INFO_VERSION_OFFSET; + let version_start = self.enemizer_info_table_base_address + ENEMIZER_INFO_VERSION_OFFSET; let version_end = version_start + ENEMIZER_INFO_VERSION_LENGTH; self.set_rom_bytes(&bytes, version_start..version_end); self.set_patch_bytes(version_start..version_end) @@ -132,7 +132,7 @@ impl RomData { bail!("Option flags is too long to fit in the space allocated. Need to move data/code in asm file."); } - let flags_start = self.asar_symbols["enemizer_info_table"] + ENEMIZER_INFO_FLAGS_OFFSET; + let flags_start = self.enemizer_info_table_base_address + ENEMIZER_INFO_FLAGS_OFFSET; let flags_end = flags_start + bytes.len(); self.set_rom_bytes(&bytes, flags_start..flags_end); self.set_patch_bytes(flags_start..flags_end); @@ -142,7 +142,7 @@ impl RomData { pub fn get_info_flags(&self) -> Option { if self.is_enemizer() { - let flags_start = self.asar_symbols["enemizer_info_table"] + ENEMIZER_INFO_FLAGS_OFFSET; + let flags_start = self.enemizer_info_table_base_address + ENEMIZER_INFO_FLAGS_OFFSET; let flags_end = flags_start + ENEMIZER_INFO_FLAGS_LENGTH; OptionFlags::try_from_bytes(&self.rom_data[flags_start..flags_end]).ok() } else { @@ -150,72 +150,15 @@ impl RomData { } } - pub fn new(asar_symbols: Symbols, rom_data: Vec) -> Self { - Self { - asar_symbols, - spoiler: String::new(), - rom_data: rom_data, - patch_data: BTreeMap::new(), - seed: -1, - } - } - - fn get_flag(&self, offset: usize) -> bool { - let flag_idx = self.asar_symbols["EnemizerFlags"] + offset; - self.rom_data[flag_idx] == 1 - } - - fn set_flag(&mut self, offset: usize, val: bool) { - let flag_idx = self.asar_symbols["EnemizerFlags"] + offset; - self.rom_data[flag_idx] = if val { 1 } else { 0 }; - self.set_patch_bytes(flag_idx..(flag_idx + 1)); - } - pub fn patch_bytes(&mut self, address: usize, patch_data: Vec) { self.rom_data .splice(address..(address + patch_data.len()), patch_data); } - pub fn patch_data(&mut self, patch: &Patch) { + pub fn patch_data(&mut self, patch: Patch) { self.set_rom_bytes( &patch.patch_data, patch.address..(patch.address + patch.patch_data.len()), ); } - - pub fn move_room_headers(&mut self) { - let table_base = DUNGEON_HEADER_POINTER_TABLE; - let header_base = self.asar_symbols["rom_header_table"]; - - // Change room header bank (at 0xB5E7) to 0x04. - let new_room_bank = self.rom_data[self.asar_symbols["moved_room_header_bank_value_address"]]; - self.rom_data[ROOM_HEADER_BANK_LOCATION] = new_room_bank; - - // Copy header table. - for i in 0..320 { - // Get i'th room's pointer. - // Pointers are 16bits, with a hard-coded bank. - let room_pointer = [ - self.rom_data[table_base + (i * 2)], - self.rom_data[table_base + (i * 2) + 1], - 4, - 0 - ]; - let snes_address = u32::from_le_bytes(room_pointer); - let pc_address = snes_to_pc_address(snes_address); - - // Copy i'th room's headers to new room_header_table. - let header_start = header_base + (i * 14); - self.rom_data.copy_within(pc_address..(pc_address + 14), header_start); - } - - // Repoint the pointer table to the new header table. - for i in 0..320 { - let snes = pc_to_snes_address(header_base + (i * 14)).to_le_bytes(); - - assert_eq!(snes[2], new_room_bank, "We changed banks in the middle of moving the room headers! This should have been caught by dev team, unless you were playing with files you shouldn't touch."); - - self.rom_data[(table_base + (i * 2))..(table_base + (i * 2) + 1)].copy_from_slice(&snes[0..1]); - } - } }