diff --git a/base_patch_generator/src/main.rs b/base_patch_generator/src/main.rs index f3412d3..e089a9a 100644 --- a/base_patch_generator/src/main.rs +++ b/base_patch_generator/src/main.rs @@ -49,7 +49,8 @@ fn main() -> Result<(), anyhow::Error> { patches.add_patches(rom_patches); println!("Writing output file {}", output_path); - patches.save_to_file(Path::new(&output_path))?; + let out_file = File::create(&output_path)?; + serde_json::to_writer(out_file, &patches)?; Ok(()) } diff --git a/enemize/src/lib.rs b/enemize/src/lib.rs index 63e07af..aae5429 100644 --- a/enemize/src/lib.rs +++ b/enemize/src/lib.rs @@ -24,6 +24,7 @@ pub struct Patch { pub patch_data: Vec, } +#[derive(Deserialize, Serialize)] pub struct PatchSet { filename: PathBuf, patches: Vec, @@ -42,22 +43,6 @@ impl PatchSet { }) } - pub fn save(&self) -> anyhow::Result<()> { - let file = File::create(&self.filename)?; - let buffer = std::io::BufWriter::new(file); - serde_json::to_writer(buffer, &self.patches)?; - - Ok(()) - } - - pub fn save_to_file(&self, filename: &Path) -> anyhow::Result<()> { - let file = File::create(filename)?; - let buffer = std::io::BufWriter::new(file); - serde_json::to_writer(buffer, &self.patches)?; - - Ok(()) - } - pub fn add_patch(&mut self, patch: Patch) { self.patches.push(patch); } diff --git a/enemize/src/main.rs b/enemize/src/main.rs index 17da211..0e3cc09 100644 --- a/enemize/src/main.rs +++ b/enemize/src/main.rs @@ -17,26 +17,18 @@ const ENEMIZER_BASE_PATCH: &'static str = "enemizer_base_patch.json"; #[clap(author, version, about)] struct Args { /// path to the base rom file - #[clap(short, long)] rom: PathBuf, - /// path to the enemizerOptions.json - #[clap(short, long)] - enemizer: PathBuf, /// seed number #[clap(short, long)] seed: Option, - /// path to the intended output file + /// path to the enemizerOptions.json #[clap(short, long)] + enemizer: PathBuf, + /// path to the intended output file output: PathBuf, /// operate in binary mode (takes already randomized SFC and applies enemizer directly to ROM) #[clap(long)] binary: bool, - /// path to base2patched.json (not used) - #[clap(long)] - base: Option, - /// path to the randomizerPatch.json (not used) - #[clap(long)] - randomizer: Option, } fn main() -> anyhow::Result<()> { @@ -44,12 +36,7 @@ fn main() -> anyhow::Result<()> { let stopwatch = Instant::now(); - let options = { - let mut opts_file = File::open(args.enemizer)?; - let mut json = String::new(); - opts_file.read_to_string(&mut json)?; - serde_json::from_str(&json)? - }; + let options = serde_json::from_reader(File::open(args.enemizer)?)?; let symbols = load_symbols()?; let mut raw_data = vec![]; diff --git a/enemize/src/option_flags.rs b/enemize/src/option_flags.rs index c3f1f0d..a8d2fd9 100644 --- a/enemize/src/option_flags.rs +++ b/enemize/src/option_flags.rs @@ -9,7 +9,6 @@ use crate::bosses::BossType; use crate::InvalidEnumError; #[derive(Debug, Default, Deserialize, Serialize)] -#[serde(rename_all = "PascalCase")] pub struct ManualBosses { pub eastern_palace: String, pub desert_palace: String, @@ -184,22 +183,18 @@ impl TryFrom for ShieldType { } #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[serde(into = "u8", try_from = "u8")] pub enum AbsorbableType { Heart, GreenRupee, BlueRupee, RedRupee, - #[serde(alias = "Bomb_1")] Bomb1, - #[serde(alias = "Bomb_4")] Bomb4, - #[serde(alias = "Bomb_8")] Bomb8, SmallMagic, FullMagic, - #[serde(alias = "Arrow_5")] Arrow5, - #[serde(alias = "Arrow_10")] Arrow10, Fairy, Key, @@ -321,7 +316,6 @@ impl TryFrom for HeartBeepSpeed { } #[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(into = "u8", try_from = "u8")] pub enum BeeLevel { Level1, Level2, @@ -386,26 +380,26 @@ pub struct OptionFlags { pub shuffle_enemy_damage_groups: bool, pub enemy_damage_chaos_mode: bool, - //pub easy_mode_escape: bool, + pub easy_mode_escape: bool, pub enemies_absorbable: bool, pub absorbable_spawn_rate: u8, pub absorbable_types: HashMap, - //pub boss_madness: bool, + pub boss_madness: bool, pub randomize_bosses: bool, pub randomize_bosses_type: RandomizeBossesType, - //pub randomize_boss_health: bool, - //pub randomize_boss_health_min_amount: u8, - //pub randomize_boss_health_max_amount: u8, + pub randomize_boss_health: bool, + pub randomize_boss_health_min_amount: u8, + pub randomize_boss_health_max_amount: u8, - //pub randomize_boss_damage: bool, - //pub randomize_boss_damage_min_amount: u8, - //pub randomize_boss_damage_max_amount: u8, + pub randomize_boss_damage: bool, + pub randomize_boss_damage_min_amount: u8, + pub randomize_boss_damage_max_amount: u8, - //pub randomize_boss_behavior: bool, + pub randomize_boss_behavior: bool, pub randomize_dungeon_palettes: bool, pub set_blackout_mode: bool, @@ -424,7 +418,7 @@ pub struct OptionFlags { pub shuffle_music: bool, pub bootleg_magic: bool, pub debug_mode: bool, - //pub custom_bosses: bool, + pub custom_bosses: bool, pub heart_beep_speed: HeartBeepSpeed, pub alternate_gfx: bool, pub shield_graphics: PathBuf, @@ -478,20 +472,20 @@ impl OptionFlags { randomize_enemy_health_type: bytes[4].try_into()?, randomize_enemy_damage: bytes[5] != 0, allow_enemy_zero_damage: bytes[6] != 0, - //easy_mode_escape: bytes[7] != 0, + easy_mode_escape: bytes[7] != 0, enemies_absorbable: bytes[8] != 0, absorbable_spawn_rate: bytes[9], absorbable_types, - //boss_madness: bytes[24] != 0, + boss_madness: bytes[24] != 0, randomize_bosses: bytes[25] != 0, randomize_bosses_type: bytes[26].try_into()?, - //randomize_boss_health: bytes[27] != 0, - //randomize_boss_health_min_amount: bytes[28], - //randomize_boss_health_max_amount: bytes[29], - //randomize_boss_damage: bytes[30] != 0, - //randomize_boss_damage_min_amount: bytes[31], - //randomize_boss_damage_max_amount: bytes[32], - //randomize_boss_behavior: bytes[33] != 0, + randomize_boss_health: bytes[27] != 0, + randomize_boss_health_min_amount: bytes[28], + randomize_boss_health_max_amount: bytes[29], + randomize_boss_damage: bytes[30] != 0, + randomize_boss_damage_min_amount: bytes[31], + randomize_boss_damage_max_amount: bytes[32], + randomize_boss_behavior: bytes[33] != 0, randomize_dungeon_palettes: bytes[34] != 0, set_blackout_mode: bytes[35] != 0, randomize_overworld_palettes: bytes[36] != 0, @@ -506,7 +500,7 @@ impl OptionFlags { shuffle_music: bytes[45] != 0, bootleg_magic: bytes[46] != 0, debug_mode: bytes[47] != 0, - //custom_bosses: bytes[48] != 0, + custom_bosses: bytes[48] != 0, heart_beep_speed: bytes[49].try_into()?, alternate_gfx: bytes[50] != 0, // Skip byte 51 (shield_graphics) @@ -545,7 +539,7 @@ impl OptionFlags { bytes.push(self.randomize_enemy_health_type as u8); bytes.push(self.randomize_enemy_damage as u8); bytes.push(self.allow_enemy_zero_damage as u8); - bytes.push(0); //bytes.push(self.easy_mode_escape as u8); + bytes.push(self.easy_mode_escape as u8); bytes.push(self.enemies_absorbable as u8); bytes.push(self.absorbable_spawn_rate); @@ -634,16 +628,16 @@ impl OptionFlags { .unwrap_or(false) as u8, ); - bytes.push(0); //bytes.push(self.boss_madness as u8); + bytes.push(self.boss_madness as u8); bytes.push(self.randomize_bosses as u8); bytes.push(self.randomize_bosses_type as u8); - bytes.push(0); //bytes.push(self.randomize_boss_health as u8); - bytes.push(0); //bytes.push(self.randomize_boss_health_min_amount); - bytes.push(0); //bytes.push(self.randomize_boss_health_max_amount); - bytes.push(0); //bytes.push(self.randomize_boss_damage as u8); - bytes.push(0); //bytes.push(self.randomize_boss_damage_min_amount); - bytes.push(0); //bytes.push(self.randomize_boss_damage_max_amount); - bytes.push(0); //bytes.push(self.randomize_boss_behavior as u8); + bytes.push(self.randomize_boss_health as u8); + bytes.push(self.randomize_boss_health_min_amount); + bytes.push(self.randomize_boss_health_max_amount); + bytes.push(self.randomize_boss_damage as u8); + bytes.push(self.randomize_boss_damage_min_amount); + bytes.push(self.randomize_boss_damage_max_amount); + bytes.push(self.randomize_boss_behavior as u8); bytes.push(self.randomize_dungeon_palettes as u8); bytes.push(self.set_blackout_mode as u8); bytes.push(self.randomize_overworld_palettes as u8); @@ -658,7 +652,7 @@ impl OptionFlags { bytes.push(self.shuffle_music as u8); bytes.push(self.bootleg_magic as u8); bytes.push(self.debug_mode as u8); - bytes.push(0); //bytes.push(self.custom_bosses as u8); + bytes.push(self.custom_bosses as u8); bytes.push(self.heart_beep_speed as u8); bytes.push(self.alternate_gfx as u8); bytes.push(0); // self.shield_graphics @@ -701,20 +695,20 @@ impl Default for OptionFlags { allow_enemy_zero_damage: false, shuffle_enemy_damage_groups: false, enemy_damage_chaos_mode: false, - //easy_mode_escape: false, + easy_mode_escape: false, enemies_absorbable: false, absorbable_spawn_rate: 0, absorbable_types: HashMap::new(), - //boss_madness: false, + boss_madness: false, randomize_bosses: true, randomize_bosses_type: RandomizeBossesType::Chaos, - //randomize_boss_health: false, - //randomize_boss_health_min_amount: 0, - //randomize_boss_health_max_amount: 0, - //randomize_boss_damage: false, - //randomize_boss_damage_min_amount: 0, - //randomize_boss_damage_max_amount: 0, - //randomize_boss_behavior: false, + randomize_boss_health: false, + randomize_boss_health_min_amount: 0, + randomize_boss_health_max_amount: 0, + randomize_boss_damage: false, + randomize_boss_damage_min_amount: 0, + randomize_boss_damage_max_amount: 0, + randomize_boss_behavior: false, randomize_dungeon_palettes: true, set_blackout_mode: false, randomize_overworld_palettes: true, @@ -729,7 +723,7 @@ impl Default for OptionFlags { shuffle_music: false, bootleg_magic: false, debug_mode: false, - //custom_bosses: false, + custom_bosses: false, heart_beep_speed: HeartBeepSpeed::Half, alternate_gfx: false, shield_graphics: ["shield_gfx", "normal.gfx"].iter().collect(), @@ -757,17 +751,3 @@ impl Default for OptionFlags { } } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_option_flags_serde() { - let empty = "{}"; - let actual: OptionFlags = serde_json::from_str(empty).expect("Can't deserialize empty"); - let expected = serde_json::to_string(&OptionFlags::default()).expect("Can't serialize default"); - - assert_eq!(serde_json::to_string(&actual).expect("Can't roundtrip"), expected); - } -} diff --git a/enemize/src/rom.rs b/enemize/src/rom.rs index fe22d41..2ae8d66 100644 --- a/enemize/src/rom.rs +++ b/enemize/src/rom.rs @@ -25,8 +25,8 @@ pub const RANDOM_SPRITE_FLAG: usize = 0x03; pub const AGAHNIM_BOUNCE_BALLS_FLAG: usize = 0x04; pub const ENABLE_MIMIC_OVERRIDE_FLAG: usize = 0x05; pub const ENABLE_TERRORPIN_AI_FIX_FLAG: usize = 0x06; -pub const CHECKSUM_COMPLIMENT_ADDRESS: usize = 0x7fdc; -pub const CHECKSUM_ADDRESS: usize = 0x7fde; +pub const CHECKSUM_COMPLIMENT_ADDRESS: usize = 0x7FDC; +pub const CHECKSUM_ADDRESS: usize = 0x7FDE; pub const RANDOMIZER_MODE_FLAG: usize = 0x180032; pub struct RomData { @@ -53,7 +53,7 @@ impl RomData { DUNGEON_HEADER_POINTER_TABLE..(DUNGEON_HEADER_POINTER_TABLE + 640); self.rom_data[dungeon_header_range].copy_from_slice(&ORIGINAL_ROOM_POINTERS); - let room_range = 0x5b97..(0x5b97 + 576); + let room_range = 0x5B97..(0x5B97 + 576); self.rom_data[room_range].copy_from_slice(&ORIGINAL_ROOM_BLOCKS); let ow_gfx_range = OVERWORLD_AREA_GRAPHICS_BLOCK..(OVERWORLD_AREA_GRAPHICS_BLOCK + 272); @@ -117,15 +117,15 @@ impl RomData { acceptable .iter() - .any(|abbr| &abbr[..] == &self.rom_data[0x7fc0..0x7fc2]) + .any(|abbr| &abbr[..] == &self.rom_data[0x7FC0..0x7Fc1]) || (self.rom_data.len() >= 0x20_0000 - && &self.rom_data[0x7fc0..0x7fcf] == b"ZELDANODENSETSU") + && &self.rom_data[0x7FC0..0x7FCE] == b"ZELDANODENSETSU") } pub fn is_race(&self) -> bool { self.is_randomizer() && (&self.rom_data[0x180213..0x180214] == &[1, 0] - || &self.rom_data[0x7fc0..0x7fca] == b"VT TOURNEY") + || &self.rom_data[0x7FC0..0x7FC9] == b"VT TOURNEY") } fn assert_rom_length(&self) { @@ -169,8 +169,8 @@ impl RomData { pub fn expand_rom(&mut self) { self.rom_data.resize(0x40_0000, 0); // Update header length. - self.rom_data[0x7fd7] = 0x0c; - self.set_patch_bytes(0x7fd7..0x7fd8); + self.rom_data[0x7FD7] = 0x0C; + self.set_patch_bytes(0x7FD7..0x7FD8); self.set_enemizer_version("6.0.32".to_owned()); } @@ -261,7 +261,7 @@ impl RomData { let table_base = DUNGEON_HEADER_POINTER_TABLE; let header_base = self.asar_symbols["room_header_table"]; - // Change room header bank (at 0xb5e7) to 0x04. + // 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;