diff --git a/.gitignore b/.gitignore index 23fb76a..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ /target -asar_symbols.txt -base_patch.json -enemizer_base_patch.json diff --git a/Cargo.lock b/Cargo.lock index e82f6c3..664456c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,39 +8,12 @@ version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base_patch_generator" -version = "0.1.0" -dependencies = [ - "anyhow", - "enemize", - "md5", - "serde_json", -] - [[package]] name = "bin_comp" version = "0.1.0" dependencies = [ "anyhow", - "enemize", + "serde", "serde_json", "tempfile", ] @@ -58,55 +31,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "clap" -version = "3.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "indexmap", - "lazy_static", - "strsim", - "termcolor", - "textwrap", -] - -[[package]] -name = "clap_derive" -version = "3.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "enemize" +name = "enemize-rs" version = "0.1.0" dependencies = [ - "anyhow", - "clap", - "lazy_static", - "rand", - "serde", - "serde_json", - "thiserror", + "md5", ] [[package]] @@ -118,48 +46,6 @@ dependencies = [ "instant", ] -[[package]] -name = "getrandom" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "indexmap" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "instant" version = "0.1.12" @@ -175,12 +61,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.126" @@ -193,42 +73,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" -[[package]] -name = "os_str_bytes" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.39" @@ -247,36 +91,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "redox_syscall" version = "0.2.13" @@ -332,12 +146,6 @@ dependencies = [ "serde", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "syn" version = "1.0.95" @@ -363,59 +171,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - -[[package]] -name = "thiserror" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-ident" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "winapi" version = "0.3.9" @@ -432,15 +193,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index aee92b6..16d8175 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [workspace] members = [ - "base_patch_generator", - "bin_comp", "enemize", + "bin_comp", ] diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index c18e449..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Lyle Mantooth - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. 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 9d47946..0000000 --- a/base_patch_generator/src/main.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::Read; -use std::path::Path; - -use enemize::asar; -use enemize::rom::RomData; -use enemize::PatchSet; - -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 mut symbols_file = File::open(&symbols_path)?; - let mut symbols_text = String::new(); - symbols_file.read_to_string(&mut symbols_text)?; - let symbols = asar::load_symbols(&symbols_text)?; - - 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); - patches.save_to_file(Path::new(&output_path))?; - - Ok(()) -} diff --git a/enemize/Cargo.toml b/enemize/Cargo.toml index 6a55151..c59205e 100644 --- a/enemize/Cargo.toml +++ b/enemize/Cargo.toml @@ -1,15 +1,9 @@ [package] -name = "enemize" +name = "enemize-rs" 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" -clap = { version = "3.1.18", features = ["derive"] } -lazy_static = "1.4" -rand = "0.8" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -thiserror = "1" +serde = { version = 1.0, features = ["derive"] } diff --git a/enemize/src/asar.rs b/enemize/src/asar.rs deleted file mode 100644 index 033f07d..0000000 --- a/enemize/src/asar.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::collections::HashMap; - -pub type Symbols = HashMap; - -pub fn load_symbols(contents: &str) -> anyhow::Result { - let symbols = contents - .lines() - .filter_map(|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, - } - }) - .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); - - 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 -} diff --git a/enemize/src/bosses/mod.rs b/enemize/src/bosses/mod.rs deleted file mode 100644 index ea7692b..0000000 --- a/enemize/src/bosses/mod.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::marker::PhantomData; - -use serde::{Deserialize, Serialize}; - -use crate::InvalidEnumError; - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[repr(u8)] -#[serde(into = "u8", try_from = "u8")] -pub enum BossType { - Kholdstare, - Moldorm, - Mothula, - Vitreous, - Helmasaur, - Armos, - Lanmola, - Blind, - Arrghus, - Trinexx, - // Don't use these. They are only for manual settings passed in by the randomizer web app. - Agahnim, - Agahnim2, - Ganon, - NoBoss = 255, -} - -impl From for u8 { - fn from(t: BossType) -> u8 { - use BossType::*; - - match t { - Kholdstare => 0, - Moldorm => 1, - Mothula => 2, - Vitreous => 3, - Helmasaur => 4, - Armos => 5, - Lanmola => 6, - Blind => 7, - Arrghus => 8, - Trinexx => 9, - Agahnim => 10, - Agahnim2 => 11, - Ganon => 12, - NoBoss => 255, - } - } -} - -impl TryFrom for BossType { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Kholdstare), - 1 => Ok(Self::Moldorm), - 2 => Ok(Self::Mothula), - 3 => Ok(Self::Vitreous), - 4 => Ok(Self::Helmasaur), - 5 => Ok(Self::Armos), - 6 => Ok(Self::Lanmola), - 7 => Ok(Self::Blind), - 8 => Ok(Self::Arrghus), - 9 => Ok(Self::Trinexx), - 10 => Ok(Self::Agahnim), - 11 => Ok(Self::Agahnim2), - 12 => Ok(Self::Ganon), - 255 => Ok(Self::NoBoss), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} diff --git a/enemize/src/constants.rs b/enemize/src/constants.rs deleted file mode 100644 index 3becd7a..0000000 --- a/enemize/src/constants.rs +++ /dev/null @@ -1,114 +0,0 @@ -pub const ROOM_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; -pub const OVERWORLD_AREA_GRAPHICS_BLOCK: usize = 0x007A81; -pub const OVERWORLD_SPRITE_POINTER_TABLE: usize = 0x04C901; -pub const MOLDORM_EYE_COUNT_ADDRESS_VANILLA: usize = 0x0EDBB3; -pub const MOLDORM_EYE_COUNT_ADDRESS_ENEMIZER: usize = 0x1B0102; -pub const NEW_BOSS_GRAPHICS: usize = 0x1B0000; -pub const RANDOM_SPRITE_GRAPHICS: usize = 0x300000; -pub const ENEMIZER_FILE_LENGTH: usize = 0x200000; -pub const HIDDEN_ENEMY_CHANCE_POOL: usize = 0xD7BBB; - -pub(crate) const ORIGINAL_ROOM_POINTERS: [u8; 640] = [ - 0x62, 0xF4, 0x6C, 0xF4, 0x7A, 0xF4, 0xDD, 0xF5, 0x85, 0xF4, 0x90, 0xF4, 0x90, 0xF4, 0x97, 0xF4, - 0xA2, 0xF4, 0xA9, 0xF4, 0xB5, 0xF4, 0xC0, 0xF4, 0xCB, 0xF4, 0xD8, 0xF4, 0xDF, 0xF4, 0xEA, 0xF4, - 0xEA, 0xF4, 0xF1, 0xF4, 0xFC, 0xF4, 0x03, 0xF5, 0x11, 0xF5, 0x18, 0xF5, 0x23, 0xF5, 0x2E, 0xF5, - 0x73, 0xFC, 0x3A, 0xF5, 0x41, 0xF5, 0x4D, 0xF5, 0x58, 0xF5, 0x63, 0xF5, 0x6E, 0xF5, 0x79, 0xF5, - 0x84, 0xF5, 0x8B, 0xF5, 0x8B, 0xF5, 0x03, 0xF5, 0x92, 0xF5, 0x99, 0xF5, 0x99, 0xF5, 0xA6, 0xF5, - 0xB2, 0xF5, 0xBD, 0xF5, 0xC4, 0xF5, 0xCB, 0xF5, 0x73, 0xFC, 0xD6, 0xF5, 0xD6, 0xF5, 0xDD, 0xF5, - 0xE4, 0xF5, 0xEF, 0xF5, 0xFB, 0xF5, 0x06, 0xF6, 0x0D, 0xF6, 0x18, 0xF6, 0x1F, 0xF6, 0x18, 0xF6, - 0x26, 0xF6, 0x31, 0xF6, 0x3B, 0xF6, 0x46, 0xF6, 0x51, 0xF6, 0x58, 0xF6, 0x63, 0xF6, 0x6E, 0xF6, - 0x7A, 0xF6, 0x86, 0xF6, 0x91, 0xF6, 0x9D, 0xF6, 0xA4, 0xF6, 0xAB, 0xF6, 0xB6, 0xF6, 0xBD, 0xF6, - 0xBD, 0xF6, 0xBD, 0xF6, 0xC4, 0xF6, 0xD0, 0xF6, 0xDA, 0xF6, 0xE5, 0xF6, 0xF0, 0xF6, 0xFB, 0xF6, - 0x05, 0xF7, 0x13, 0xF7, 0x1E, 0xF7, 0x2C, 0xF7, 0x37, 0xF7, 0x42, 0xF7, 0x49, 0xF7, 0x50, 0xF7, - 0x57, 0xF7, 0x5E, 0xF7, 0x65, 0xF7, 0x6C, 0xF7, 0x73, 0xF7, 0x7E, 0xF7, 0x89, 0xF7, 0x94, 0xF7, - 0xA0, 0xF7, 0xA7, 0xF7, 0xA0, 0xF7, 0xB2, 0xF7, 0xBD, 0xF7, 0xC8, 0xF7, 0xD2, 0xF7, 0xDD, 0xF7, - 0xE4, 0xF7, 0xEB, 0xF7, 0xEB, 0xF7, 0xF7, 0xF7, 0x02, 0xF8, 0x0D, 0xF8, 0x14, 0xF8, 0x1F, 0xF8, - 0x1F, 0xF8, 0x2B, 0xF8, 0x36, 0xF8, 0x41, 0xF8, 0x48, 0xF8, 0x4F, 0xF8, 0x56, 0xF8, 0x63, 0xF8, - 0x70, 0xF8, 0x70, 0xF8, 0x70, 0xF8, 0x70, 0xF8, 0x7A, 0xF8, 0x81, 0xF8, 0x8B, 0xF8, 0x96, 0xF8, - 0xA1, 0xF8, 0xAC, 0xF8, 0xAC, 0xF8, 0xB3, 0xF8, 0xBA, 0xF8, 0xC1, 0xF8, 0xC8, 0xF8, 0xC8, 0xF8, - 0xD4, 0xF8, 0xD4, 0xF8, 0xDE, 0xF8, 0xDE, 0xF8, 0xE5, 0xF8, 0xF2, 0xF8, 0xF9, 0xF8, 0x04, 0xF9, - 0x04, 0xF9, 0x0B, 0xF9, 0x16, 0xF9, 0x1D, 0xF9, 0x28, 0xF9, 0x28, 0xF9, 0x2F, 0xF9, 0x3A, 0xF9, - 0x45, 0xF9, 0x50, 0xF9, 0x5B, 0xF9, 0x5B, 0xF9, 0x65, 0xF9, 0x6C, 0xF9, 0x76, 0xF9, 0x81, 0xF9, - 0x88, 0xF9, 0x93, 0xF9, 0x9A, 0xF9, 0x93, 0xF9, 0xA5, 0xF9, 0xAC, 0xF9, 0xB7, 0xF9, 0xC2, 0xF9, - 0xCC, 0xF9, 0xD3, 0xF9, 0xDD, 0xF9, 0xE4, 0xF9, 0xEF, 0xF9, 0xF6, 0xF9, 0xF6, 0xF9, 0x01, 0xFA, - 0x08, 0xFA, 0x14, 0xFA, 0x1E, 0xFA, 0x25, 0xFA, 0x2C, 0xFA, 0x37, 0xFA, 0x42, 0xFA, 0x0A, 0xF5, - 0x4D, 0xFA, 0x54, 0xFA, 0x5B, 0xFA, 0x62, 0xFA, 0x69, 0xFA, 0x74, 0xFA, 0x74, 0xFA, 0x7F, 0xFA, - 0x86, 0xFA, 0x92, 0xFA, 0x99, 0xFA, 0xA0, 0xFA, 0xA7, 0xFA, 0xB2, 0xFA, 0x0A, 0xF5, 0xB9, 0xFA, - 0xC0, 0xFA, 0xC7, 0xFA, 0xCE, 0xFA, 0xCE, 0xFA, 0xCE, 0xFA, 0xD5, 0xFA, 0xD5, 0xFA, 0xDF, 0xFA, - 0xDF, 0xFA, 0xEB, 0xFA, 0xF6, 0xFA, 0x01, 0xFB, 0x01, 0xFB, 0xB2, 0xFA, 0x0A, 0xF5, 0x01, 0xFB, - 0x01, 0xFB, 0x08, 0xFB, 0x0F, 0xFB, 0xCE, 0xFA, 0xCE, 0xFA, 0x1A, 0xFB, 0x1A, 0xFB, 0x21, 0xFB, - 0x2C, 0xFB, 0x37, 0xFB, 0x3E, 0xFB, 0x45, 0xFB, 0x4C, 0xFB, 0x4C, 0xFB, 0x53, 0xFB, 0x53, 0xFB, - 0x5A, 0xFB, 0x68, 0xFB, 0x68, 0xFB, 0x73, 0xFB, 0x7E, 0xFB, 0x7E, 0xFB, 0x8A, 0xFB, 0x94, 0xFB, - 0x53, 0xFB, 0x53, 0xFB, 0xA0, 0xFB, 0xA0, 0xFB, 0xA5, 0xFB, 0xA5, 0xFB, 0xAC, 0xFB, 0xAC, 0xFB, - 0xAC, 0xFB, 0xBA, 0xFB, 0xC1, 0xFB, 0xCC, 0xFB, 0xD7, 0xFB, 0xD7, 0xFB, 0xBA, 0xFB, 0xE3, 0xFB, - 0xEE, 0xFB, 0xFC, 0xFB, 0x03, 0xFC, 0x0A, 0xFC, 0x11, 0xFC, 0x18, 0xFC, 0x1F, 0xFC, 0x26, 0xFC, - 0x2D, 0xFC, 0x34, 0xFC, 0x3B, 0xFC, 0x42, 0xFC, 0x49, 0xFC, 0x50, 0xFC, 0x57, 0xFC, 0xF5, 0xFB, - 0xF5, 0xFB, 0x5E, 0xFC, 0x65, 0xFC, 0x6C, 0xFC, 0x73, 0xFC, 0x73, 0xFC, 0x7A, 0xFC, 0x81, 0xFC, - 0x0A, 0xFC, 0x88, 0xFC, 0x93, 0xFC, 0x9A, 0xFC, 0xF5, 0xFB, 0xA1, 0xFC, 0xAC, 0xFC, 0xB3, 0xFC, - 0xBA, 0xFC, 0x5E, 0xFC, 0x5E, 0xFC, 0xC1, 0xFC, 0xC8, 0xFC, 0xC8, 0xFC, 0xC8, 0xFC, 0xAC, 0xFC, - 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, - 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, - 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, 0xCF, 0xFC, -]; - -pub(crate) const ORIGINAL_ROOM_BLOCKS: [u8; 576] = [ - 0x00, 0x49, 0x00, 0x00, 0x46, 0x49, 0x0C, 0x1D, 0x48, 0x49, 0x13, 0x1D, 0x46, 0x49, 0x13, 0x0E, - 0x48, 0x49, 0x0C, 0x11, 0x48, 0x49, 0x0C, 0x10, 0x4F, 0x49, 0x4A, 0x50, 0x0E, 0x49, 0x4A, 0x11, - 0x46, 0x49, 0x12, 0x00, 0x00, 0x49, 0x00, 0x50, 0x00, 0x49, 0x00, 0x11, 0x48, 0x49, 0x0C, 0x00, - 0x00, 0x00, 0x37, 0x36, 0x48, 0x49, 0x4C, 0x11, 0x5D, 0x2C, 0x0C, 0x44, 0x00, 0x00, 0x4E, 0x00, - 0x0F, 0x00, 0x12, 0x10, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x0D, 0x17, 0x00, 0x16, 0x0D, 0x17, 0x1B, - 0x16, 0x0D, 0x17, 0x14, 0x15, 0x0D, 0x17, 0x15, 0x16, 0x0D, 0x18, 0x19, 0x16, 0x0D, 0x17, 0x19, - 0x16, 0x0D, 0x00, 0x00, 0x16, 0x0D, 0x18, 0x1B, 0x0F, 0x49, 0x4A, 0x11, 0x4B, 0x2A, 0x5C, 0x15, - 0x16, 0x49, 0x17, 0x1D, 0x00, 0x00, 0x00, 0x15, 0x16, 0x0D, 0x17, 0x10, 0x16, 0x49, 0x12, 0x00, - 0x16, 0x49, 0x0C, 0x11, 0x00, 0x00, 0x12, 0x10, 0x16, 0x0D, 0x00, 0x11, 0x16, 0x49, 0x0C, 0x00, - 0x16, 0x0D, 0x4C, 0x11, 0x0E, 0x0D, 0x4A, 0x11, 0x16, 0x1A, 0x17, 0x1B, 0x4F, 0x34, 0x4A, 0x50, - 0x35, 0x4D, 0x65, 0x36, 0x4A, 0x34, 0x4E, 0x00, 0x0E, 0x34, 0x4A, 0x11, 0x51, 0x34, 0x5D, 0x59, - 0x4B, 0x49, 0x4C, 0x11, 0x2D, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x12, 0x59, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x49, 0x2B, 0x2D, 0x46, 0x49, 0x1C, 0x52, 0x00, 0x49, 0x1C, 0x52, 0x5D, 0x49, 0x00, 0x52, - 0x46, 0x49, 0x13, 0x52, 0x4B, 0x4D, 0x4A, 0x5A, 0x47, 0x49, 0x1C, 0x52, 0x4B, 0x4D, 0x39, 0x36, - 0x1F, 0x2C, 0x2E, 0x52, 0x1F, 0x2C, 0x2E, 0x1D, 0x2F, 0x2C, 0x2E, 0x52, 0x2F, 0x2C, 0x2E, 0x31, - 0x1F, 0x1E, 0x30, 0x52, 0x51, 0x49, 0x13, 0x00, 0x4F, 0x49, 0x13, 0x50, 0x4F, 0x4D, 0x4A, 0x50, - 0x4B, 0x49, 0x4C, 0x2B, 0x1F, 0x20, 0x22, 0x53, 0x55, 0x3D, 0x42, 0x43, 0x1F, 0x1E, 0x23, 0x52, - 0x1F, 0x1E, 0x39, 0x3A, 0x1F, 0x1E, 0x3A, 0x3E, 0x1F, 0x1E, 0x3C, 0x3D, 0x40, 0x1E, 0x27, 0x3F, - 0x55, 0x1A, 0x42, 0x43, 0x1F, 0x1E, 0x2A, 0x52, 0x1F, 0x1E, 0x38, 0x52, 0x1F, 0x20, 0x28, 0x52, - 0x1F, 0x20, 0x26, 0x52, 0x1F, 0x2C, 0x25, 0x52, 0x1F, 0x20, 0x27, 0x52, 0x1F, 0x1E, 0x29, 0x52, - 0x1F, 0x2C, 0x3B, 0x52, 0x46, 0x49, 0x24, 0x52, 0x21, 0x41, 0x45, 0x33, 0x1F, 0x2C, 0x28, 0x31, - 0x1F, 0x0D, 0x29, 0x52, 0x1F, 0x1E, 0x27, 0x52, 0x1F, 0x20, 0x27, 0x53, 0x48, 0x49, 0x13, 0x52, - 0x0E, 0x1E, 0x4A, 0x50, 0x1F, 0x20, 0x26, 0x53, 0x15, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x2A, 0x52, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x5D, 0x49, 0x00, 0x52, 0x55, 0x49, 0x42, 0x43, - 0x61, 0x62, 0x63, 0x50, 0x61, 0x62, 0x63, 0x50, 0x61, 0x62, 0x63, 0x50, 0x61, 0x62, 0x63, 0x50, - 0x61, 0x62, 0x63, 0x50, 0x61, 0x62, 0x63, 0x50, 0x61, 0x56, 0x57, 0x50, 0x61, 0x62, 0x63, 0x50, - 0x61, 0x62, 0x63, 0x50, 0x61, 0x56, 0x57, 0x50, 0x61, 0x56, 0x63, 0x50, 0x61, 0x56, 0x57, 0x50, - 0x61, 0x56, 0x33, 0x50, 0x61, 0x56, 0x57, 0x50, 0x61, 0x62, 0x63, 0x50, 0x61, 0x62, 0x63, 0x50, -]; - -pub(crate) const ORIGINAL_OVERWORLD_BLOCKS: [u8; 272] = [ - 0x07, 0x07, 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x07, 0x07, 0x07, 0x10, 0x10, 0x10, 0x10, 0x04, - 0x06, 0x06, 0x00, 0x03, 0x03, 0x00, 0x0D, 0x0A, 0x06, 0x06, 0x01, 0x01, 0x01, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x06, 0x01, 0x01, 0x04, 0x05, 0x05, 0x06, 0x09, 0x0F, 0x00, 0x00, 0x0B, 0x0B, 0x05, - 0x08, 0x08, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x07, 0x07, 0x1A, 0x10, 0x10, 0x10, 0x10, 0x10, 0x07, 0x07, 0x1A, 0x10, 0x10, 0x10, 0x10, 0x04, - 0x06, 0x06, 0x00, 0x03, 0x03, 0x00, 0x0D, 0x0A, 0x06, 0x06, 0x1C, 0x1C, 0x1C, 0x02, 0x05, 0x05, - 0x06, 0x06, 0x06, 0x1C, 0x1C, 0x00, 0x05, 0x05, 0x06, 0x00, 0x0F, 0x00, 0x00, 0x23, 0x23, 0x05, - 0x1F, 0x1F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1F, 0x1F, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x13, 0x13, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x13, 0x13, 0x17, 0x14, 0x14, 0x14, 0x14, 0x16, - 0x15, 0x15, 0x12, 0x13, 0x13, 0x18, 0x16, 0x16, 0x15, 0x15, 0x13, 0x26, 0x26, 0x13, 0x17, 0x17, - 0x15, 0x15, 0x15, 0x26, 0x26, 0x13, 0x17, 0x17, 0x1B, 0x1D, 0x11, 0x13, 0x13, 0x18, 0x18, 0x17, - 0x16, 0x16, 0x13, 0x13, 0x13, 0x19, 0x19, 0x19, 0x16, 0x16, 0x18, 0x13, 0x18, 0x19, 0x19, 0x19, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x05, 0x05, 0x06, 0x09, 0x09, 0x09, 0x09, 0x09, 0x05, 0x05, 0x06, 0x09, 0x09, 0x09, 0x09, 0x03, -]; diff --git a/enemize/src/graph/item.rs b/enemize/src/graph/item.rs deleted file mode 100644 index 13f3f16..0000000 --- a/enemize/src/graph/item.rs +++ /dev/null @@ -1,554 +0,0 @@ -use std::collections::HashMap; -use std::hash::{Hash, Hasher}; - -use lazy_static::lazy_static; - -#[derive(Clone, Copy)] -pub enum ItemType { - Normal, - Bottle, - Special, - Progressive { level: u8 }, - Consumable { found: u8, used: u8 }, -} - -impl ItemType { - pub fn level(&self) -> u8 { - match self { - Self::Progressive { level } => *level, - _ => 0, - } - } - - pub fn increase_level(&mut self) { - match self { - Self::Progressive { ref mut level } => *level += 1, - _ => (), - } - } - - pub fn found(&self) -> u8 { - match self { - Self::Consumable { found, .. } => *found, - _ => 0, - } - } - - pub fn increase_count(&mut self) { - match self { - Self::Consumable { ref mut found, .. } => *found += 1, - _ => (), - } - } - - pub fn used(&self) -> u8 { - match self { - Self::Consumable { used, .. } => *used, - _ => 0, - } - } - - pub fn consume(&mut self) { - match self { - Self::Consumable { ref mut used, .. } => *used += 1, - _ => (), - } - } - - pub fn usable(&self) -> bool { - match self { - Self::Consumable { found, used } => found > used, - _ => false, - } - } -} - -#[derive(Clone, Copy)] -pub struct Item { - pub id: ItemId, - pub name: &'static str, - pub item_type: ItemType, -} - -impl Item { - pub fn normal(id: ItemId, name: &'static str) -> Self { - Self { - id, - name, - item_type: ItemType::Normal, - } - } - - pub fn bottle(id: ItemId, name: &'static str) -> Self { - Self { - id, - name, - item_type: ItemType::Bottle, - } - } - - pub fn special(id: ItemId, name: &'static str) -> Self { - Self { - id, - name, - item_type: ItemType::Special, - } - } - - pub fn progressive(id: ItemId, name: &'static str) -> Self { - Self { - id, - name, - item_type: ItemType::Progressive { level: 0 }, - } - } - - pub fn consumable(id: ItemId, name: &'static str) -> Self { - Self { - id, - name, - item_type: ItemType::Consumable { found: 0, used: 0 }, - } - } -} - -impl PartialEq for Item { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for Item {} - -impl Hash for Item { - fn hash(&self, state: &mut H) { - self.id.hash(state); - } -} - -#[derive(Clone, Copy, Eq, Hash, PartialEq)] -#[repr(u32)] -pub enum ItemId { - FightersSwordAndShield = 0x00, - MasterSword1 = 0x01, - TemperedSword = 0x02, - GoldenSword = 0x03, - FightersShield = 0x04, - FireShield = 0x05, - MirrorShield = 0x06, - FireRod = 0x07, - IceRod = 0x08, - Hammer = 0x09, - Hookshot = 0x0a, - Bow = 0x0b, - Boomerang = 0x0c, - Powder = 0x0d, - Bee = 0x0e, - Bombos = 0x0f, - Ether = 0x10, - Quake = 0x11, - Lamp = 0x12, - Shovel = 0x13, - Flute = 0x14, - Somaria = 0x15, - Bottle = 0x16, - PieceOfHeart = 0x17, - Byrna = 0x18, - Cape = 0x19, - Mirror = 0x1a, - L1Gloves = 0x1b, - L2Gloves = 0x1c, - Book = 0x1d, - Flippers = 0x1e, - MoonPearl = 0x1f, - Crystal = 0x20, - BugCatchingNet = 0x21, - BlueMail = 0x22, - RedMail = 0x23, - Key = 0x24, - Compass = 0x25, - HeartContainerNoAnimation = 0x26, - Bomb = 0x27, - ThreeBombs = 0x28, - Mushroom = 0x29, - MagicalBoomerang = 0x2a, - BottleRedPotion = 0x2b, - BottleGreenPotion = 0x2c, - BottleBluePotion = 0x2d, - RedPotion = 0x2e, - GreenPotion = 0x2f, - BluePotion = 0x30, - TenBombs = 0x31, - BigKey = 0x32, - DungeonMap = 0x33, - GreenRupee = 0x34, - BlueRupee = 0x35, - RedRupee = 0x36, - PendantGreen = 0x37, - PendantRed = 0x38, - PendantBlue = 0x39, - BowAndArrows = 0x3a, - BowAndSilverArrows = 0x3b, - BottleBee = 0x3c, - BottleFairy = 0x3d, - HeartContainer = 0x3e, - HeartContainerRefill = 0x3f, - OneHundredRupees = 0x40, - FiftyRupees = 0x41, - Heart = 0x42, - SingleArrow = 0x43, - TenArrows = 0x44, - SmallMagic = 0x45, - ThreeHundredRupees = 0x46, - RedRupee2 = 0x47, - BottleGoldenBee = 0x48, - FightersSword = 0x49, - FluteActive = 0x4a, - Boots = 0x4b, - MaxBombs = 0x4c, - MaxArrows = 0x4d, - HalfMagic = 0x4e, - QuarterMagic = 0x4f, - MasterSword2 = 0x50, - BombUpgrade5 = 0x51, - BombUpgrade10 = 0x52, - ArrowUpgrade5 = 0x53, - ArrowUpgrade10 = 0x54, - Programmable1 = 0x55, - Programmable2 = 0x56, - Programmable3 = 0x57, - SilverArrows = 0x58, - Rupoor = 0x59, - Nothing = 0x5a, - RedClock = 0x5b, - BlueClock = 0x5c, - GreenClock = 0x5d, - ProgressiveSword = 0x5e, - ProgressiveShield = 0x5f, - ProgressiveArmor = 0x60, - ProgressiveGloves = 0x61, - UniqueRngItem = 0x62, - NonUniqueRngItem = 0x63, - ProgressiveBow = 0x64, - ProgressiveBow2 = 0x65, - Triforce = 0x6a, - PowerStar = 0x6b, - TriforcePiece = 0x6c, - LightWorldMap = 0x70, - DarkWorldMap = 0x71, - GTMap = 0x72, - TurtleMap = 0x73, - ThievesMap = 0x74, - HeraMap = 0x75, - IceMap = 0x76, - SkullMap = 0x77, - MireMap = 0x78, - PodMap = 0x79, - SwampMap = 0x7a, - AgaMap = 0x7b, - DesertMap = 0x7c, - EasternMap = 0x7d, - HyruleMap = 0x7e, - SewersMap = 0x7f, - GTCompass = 0x82, - TurtleCompass = 0x83, - ThievesCompass = 0x84, - HeraCompass = 0x85, - IceCompass = 0x86, - SkullCompass = 0x87, - MireCompass = 0x88, - PodCompass = 0x89, - SwampCompass = 0x8a, - AgaCompass = 0x8b, - DesertCompass = 0x8c, - EasternCompass = 0x8d, - HyruleCompass = 0x8e, - SewersCompass = 0x8f, - SkeletonKey = 0x90, - Reserved0x91 = 0x91, - GTBigKey = 0x92, - TurtleBigKey = 0x93, - ThievesBigKey = 0x94, - HeraBigKey = 0x95, - IceBigKey = 0x96, - SkullBigKey = 0x97, - MireBigKey = 0x98, - PodBigKey = 0x99, - SwampBigKey = 0x9a, - AgaBigKey = 0x9b, - DesertBigKey = 0x9c, - EasternBigKey = 0x9d, - HyruleBigKey = 0x9e, - SewersBigKey = 0x9f, - SewersKey = 0xa0, - HyruleKey = 0xa1, - EasternKey = 0xa2, - DesertKey = 0xa3, - AgaKey = 0xa4, - SwampKey = 0xa5, - PodKey = 0xa6, - MireKey = 0xa7, - SkullKey = 0xa8, - IceKey = 0xa9, - HeraKey = 0xaa, - ThievesKey = 0xab, - TurtleKey = 0xac, - GTKey = 0xad, - Reserved0xAE = 0xae, - Reserved0xAF = 0xaf, - Agahnim1 = 0x01ff, - Agahnim1Boss = 0x02ff, - Agahnim2 = 0x03ff, - Agahnim2Boss = 0x04ff, - BigBomb = 0x05ff, - Crystal1 = 0x06ff, - Crystal2 = 0x07ff, - Crystal3 = 0x08ff, - Crystal4 = 0x09ff, - Crystal5 = 0x0aff, - Crystal6 = 0x0bff, - Crystal7 = 0x0cff, - DesertBoss = 0x0dff, - EasternBoss = 0x0eff, - Smith = 0x0fff, - Ganon = 0x10ff, - GTArmosBoss = 0x11ff, - GTLanmolasBoss = 0x12ff, - GTMoldormBoss = 0x13ff, - HeraBoss = 0x14ff, - IceBoss = 0x15ff, - IceBlock = 0x16ff, - L1Shield = 0x17ff, - L1Sword = 0x18ff, - L2Shield = 0x19ff, - L2Sword = 0x1aff, - L3Shield = 0x1bff, - L3Sword = 0x1cff, - L4Sword = 0x1dff, - MireBoss = 0x1eff, - MiseryMireToken = 0x1fff, - MireSwitch = 0x20ff, - PodBoss = 0x21ff, - PurpleChest = 0x22ff, - SkullBoss = 0x23ff, - SwampBoss = 0x24ff, - ThievesBoss = 0x25ff, - TurtleBoss = 0x26ff, - TurtleRockToken = 0x27ff, - ProgressiveMagic = 0x28ff, -} - -lazy_static! { - pub static ref ITEMS: HashMap = { - use ItemId::*; - - let mut items: HashMap = HashMap::new(); - - items.insert(FightersSwordAndShield, Item::normal(FightersSwordAndShield, "Fighters Sword and Shield")); - items.insert(MasterSword1, Item::normal(MasterSword1, "Master Sword")); - items.insert(TemperedSword, Item::normal(TemperedSword, "Tempered Sword")); - items.insert(GoldenSword, Item::normal(GoldenSword, "Golden Sword")); - items.insert(FightersShield, Item::normal(FightersShield, "Fighters Shield")); - items.insert(FireShield, Item::normal(FireShield, "Fire Shield")); - items.insert(FireRod, Item::normal(FireRod, "Fire Rod")); - items.insert(IceRod, Item::normal(IceRod, "Ice Rod")); - items.insert(Hammer, Item::normal(Hammer, "Hammer")); - items.insert(Hookshot, Item::normal(Hookshot, "Hookshot")); - items.insert(Bow, Item::normal(Bow, "Bow")); - items.insert(Boomerang, Item::normal(Boomerang, "Boomerang")); - items.insert(Powder, Item::normal(Powder, "Magic Powder")); - items.insert(Bee, Item::normal(Bee, "Bee")); - items.insert(Bombos, Item::normal(Bombos, "Bombos")); - items.insert(Ether, Item::normal(Ether, "Ether")); - items.insert(Quake, Item::normal(Quake, "Quake")); - items.insert(Lamp, Item::normal(Lamp, "Lamp")); - items.insert(Shovel, Item::normal(Shovel, "Shovel")); - items.insert(Flute, Item::normal(Flute, "Flute")); - items.insert(Somaria, Item::normal(Somaria, "Cane of Somaria")); - items.insert(Bottle, Item::bottle(Bottle, "Bottle")); - items.insert(PieceOfHeart, Item::normal(PieceOfHeart, "Piece of Heart")); - items.insert(Byrna, Item::normal(Byrna, "Cane of Byrna")); - items.insert(Cape, Item::normal(Cape, "Magic Cape")); - items.insert(Mirror, Item::normal(Mirror, "Magic Mirror")); - items.insert(L1Gloves, Item::progressive(L1Gloves, "Power Gloves")); - items.insert(L2Gloves, Item::progressive(L2Gloves, "Titan Mitts")); - items.insert(Book, Item::normal(Book, "Book of Mudora")); - items.insert(Flippers, Item::normal(Flippers, "Zora's Flippers")); - items.insert(MoonPearl, Item::normal(MoonPearl, "Moon Pearl")); - items.insert(Crystal, Item::special(Crystal, "Crystal")); - items.insert(BugCatchingNet, Item::normal(BugCatchingNet, "Bug Catching Net")); - items.insert(BlueMail, Item::normal(BlueMail, "Blue Mail")); - items.insert(RedMail, Item::normal(RedMail, "Red Mail")); - items.insert(Key, Item::consumable(Key, "Key")); - items.insert(Compass, Item::special(Compass, "Compass")); - items.insert(HeartContainerNoAnimation, Item::normal(HeartContainerNoAnimation, "Heart Container (no animation)")); - items.insert(Bomb, Item::normal(Bomb, "Bomb")); - items.insert(ThreeBombs, Item::normal(ThreeBombs, "3 Bombs")); - items.insert(Mushroom, Item::consumable(Mushroom, "Mushroom")); - items.insert(MagicalBoomerang, Item::normal(MagicalBoomerang, "Magical Boomerang")); - items.insert(BottleRedPotion, Item::bottle(BottleRedPotion, "Bottle (Red Potion)")); - items.insert(BottleGreenPotion, Item::bottle(BottleGreenPotion, "Bottle (Green Potion)")); - items.insert(BottleBluePotion, Item::bottle(BottleBluePotion, "Bottle (Blue Potion)")); - items.insert(RedPotion, Item::special(RedPotion, "Red Potion")); - items.insert(GreenPotion, Item::special(GreenPotion, "Green Potion")); - items.insert(BluePotion, Item::special(BluePotion, "Blue Potion")); - items.insert(TenBombs, Item::normal(TenBombs, "10 Bombs")); - items.insert(BigKey, Item::special(BigKey, "Big Key")); - items.insert(DungeonMap, Item::special(DungeonMap, "Dungeon Map")); - items.insert(GreenRupee, Item::normal(GreenRupee, "Green Rupee")); - items.insert(BlueRupee, Item::normal(BlueRupee, "Blue Rupee")); - items.insert(RedRupee, Item::normal(RedRupee, "Red Rupee")); - items.insert(PendantGreen, Item::special(PendantGreen, "Pendant of Courage")); - items.insert(PendantRed, Item::special(PendantRed, "Pendant of Wisdom")); - items.insert(PendantBlue, Item::special(PendantBlue, "Pendant of Power")); - items.insert(BowAndArrows, Item::special(BowAndArrows, "Bow and Arrows")); - items.insert(BowAndSilverArrows, Item::special(BowAndSilverArrows, "Bow and Silver Arrows")); - items.insert(BottleBee, Item::bottle(BottleBee, "Bottle (Bee)")); - items.insert(BottleFairy, Item::bottle(BottleFairy, "Bottle, (Fairy)")); - items.insert(HeartContainer, Item::normal(HeartContainer, "Heart Container")); - items.insert(HeartContainerRefill, Item::normal(HeartContainerRefill, "Heart Container (refill)")); - items.insert(OneHundredRupees, Item::normal(OneHundredRupees, "100 Rupees")); - items.insert(FiftyRupees, Item::normal(FiftyRupees, "50 Rupees")); - items.insert(Heart, Item::normal(Heart, "Heart")); - items.insert(SingleArrow, Item::normal(SingleArrow, "Single Arrow")); - items.insert(TenArrows, Item::normal(TenArrows, "10 Arrows")); - items.insert(SmallMagic, Item::normal(SmallMagic, "Small Magic Refill")); - items.insert(ThreeHundredRupees, Item::normal(ThreeHundredRupees, "300 Rupees")); - items.insert(RedRupee2, Item::normal(RedRupee2, "Red Rupee")); - items.insert(BottleGoldenBee, Item::bottle(BottleGoldenBee, "Bottle (Golden Bee)")); - items.insert(FightersSword, Item::normal(FightersSword, "Fighter's Sword")); - items.insert(FluteActive, Item::normal(FluteActive, "Flute (activated)")); - items.insert(Boots, Item::normal(Boots, "Pegasus Boots")); - items.insert(MaxBombs, Item::normal(MaxBombs, "Max Bomb Upgrade (50)")); - items.insert(MaxArrows, Item::normal(MaxArrows, "Max Arrow Upgrade (70)")); - items.insert(HalfMagic, Item::normal(HalfMagic, "Half Magic")); - items.insert(QuarterMagic, Item::normal(QuarterMagic, "Quarter Magic")); - items.insert(MasterSword2, Item::normal(MasterSword2, "Master Sword")); - items.insert(BombUpgrade5, Item::normal(BombUpgrade5, "Bomb Upgrade (5)")); - items.insert(BombUpgrade10, Item::normal(BombUpgrade10, "Bomb Upgrade (10)")); - items.insert(ArrowUpgrade5, Item::normal(ArrowUpgrade5, "Arrow Upgrade (5)")); - items.insert(ArrowUpgrade10, Item::normal(ArrowUpgrade10, "Arrow Upgrade (10)")); - items.insert(Programmable1, Item::normal(Programmable1, "Programmable 1")); - items.insert(Programmable2, Item::normal(Programmable2, "Programmable 2")); - items.insert(Programmable3, Item::normal(Programmable3, "Programmable 3")); - items.insert(SilverArrows, Item::normal(SilverArrows, "Silver Arrows")); - items.insert(Rupoor, Item::normal(Rupoor, "Rupoor")); - items.insert(Nothing, Item::normal(Nothing, "Nothing (Null Item)")); - items.insert(RedClock, Item::consumable(RedClock, "Red Clock")); - items.insert(BlueClock, Item::consumable(BlueClock, "Blue Clock")); - items.insert(GreenClock, Item::consumable(GreenClock, "Green Clock")); - items.insert(ProgressiveSword, Item::progressive(ProgressiveSword, "Progressive Sword")); - items.insert(ProgressiveShield, Item::progressive(ProgressiveShield, "Progressive Shield")); - items.insert(ProgressiveArmor, Item::progressive(ProgressiveArmor, "Progressive Armor")); - items.insert(ProgressiveGloves, Item::progressive(ProgressiveGloves, "Progressive Gloves")); - items.insert(UniqueRngItem, Item::special(UniqueRngItem, "Unique RNG Item (RNG Pool Single)")); - items.insert(NonUniqueRngItem, Item::special(NonUniqueRngItem, "Non-unique RNG Item (RNG Pool Multi)")); - items.insert(ProgressiveBow, Item::normal(ProgressiveBow, "Progressive Bow")); // *NOT* ProgressiveItem??? - items.insert(ProgressiveBow2, Item::normal(ProgressiveBow2, "Progressive Bow 2")); - items.insert(Triforce, Item::special(Triforce, "Triforce (Single Goal Item)")); - items.insert(PowerStar, Item::special(PowerStar, "Power Star (Multi Goal Item)")); - items.insert(TriforcePiece, Item::special(TriforcePiece, "Triforce Piece")); - items.insert(LightWorldMap, Item::special(LightWorldMap, "Light World Map")); - items.insert(DarkWorldMap, Item::special(DarkWorldMap, "Dark World Map")); - items.insert(GTMap, Item::special(GTMap, "Ganon's Tower Map")); - items.insert(TurtleMap, Item::special(TurtleMap, "Turtle Rock Map")); - items.insert(ThievesMap, Item::special(ThievesMap, "Thieves' Town Map")); - items.insert(HeraMap, Item::special(HeraMap, "Tower of Hera Map")); - items.insert(IceMap, Item::special(IceMap, "Ice Palace Map")); - items.insert(SkullMap, Item::special(SkullMap, "Skull Woods Map")); - items.insert(MireMap, Item::special(MireMap, "Misery Mire Map")); - items.insert(PodMap, Item::special(PodMap, "Palace of Darkness Map")); - items.insert(SwampMap, Item::special(SwampMap, "Swamp Palace Map")); - items.insert(AgaMap, Item::special(AgaMap, "Agahnim's Tower Map")); - items.insert(DesertMap, Item::special(DesertMap, "Desert Palace Map")); - items.insert(EasternMap, Item::special(EasternMap, "Eastern Palace Map")); - items.insert(HyruleMap, Item::special(HyruleMap, "Hyrule Castle Map")); - items.insert(SewersMap, Item::special(SewersMap, "Sewers Map")); // not the same as HyruleMap? - items.insert(GTCompass, Item::special(GTCompass, "Ganon's Tower Compass")); - items.insert(TurtleCompass, Item::special(TurtleCompass, "Turtle Rock Compass")); - items.insert(ThievesCompass, Item::special(ThievesCompass, "Thieves' Town Compass")); - items.insert(HeraCompass, Item::special(HeraCompass, "Tower of Hera Compass")); - items.insert(IceCompass, Item::special(IceCompass, "Ice Palace Compass")); - items.insert(SkullCompass, Item::special(SkullCompass, "Skull Woods Compass")); - items.insert(MireCompass, Item::special(MireCompass, "Misery Mire Compass")); - items.insert(PodCompass, Item::special(PodCompass, "Palace of Darkness Compass")); - items.insert(SwampCompass, Item::special(SwampCompass, "Swamp Palace Compass")); - items.insert(AgaCompass, Item::special(AgaCompass, "Agahnim's Tower Compass")); - items.insert(DesertCompass, Item::special(DesertCompass, "Desert Palace Compass")); - items.insert(EasternCompass, Item::special(EasternCompass, "Eastern Palace Compass")); - items.insert(HyruleCompass, Item::special(HyruleCompass, "Hyrule Castle Compass")); - items.insert(SewersCompass, Item::special(SewersCompass, "Sewers Compass")); // not the same as HyruleCompass? - items.insert(SkeletonKey, Item::special(SkeletonKey, "Skeleton Key")); - items.insert(Reserved0x91, Item::special(Reserved0x91, "")); - items.insert(GTBigKey, Item::special(GTBigKey, "Ganon's Tower Big Key")); - items.insert(TurtleBigKey, Item::special(TurtleBigKey, "Turtle Rock Big Key")); - items.insert(ThievesBigKey, Item::special(ThievesBigKey, "Thieves' Town Big Key")); - items.insert(HeraBigKey, Item::special(HeraBigKey, "Tower of Hera Big Key")); - items.insert(IceBigKey, Item::special(IceBigKey, "Ice Palace Big Key")); - items.insert(SkullBigKey, Item::special(SkullBigKey, "Skull Woods Big Key")); - items.insert(MireBigKey, Item::special(MireBigKey, "Misery Mire Big Key")); - items.insert(PodBigKey, Item::special(PodBigKey, "Palace of Darkness Big Key")); - items.insert(SwampBigKey, Item::special(SwampBigKey, "Swamp Palace Big Key")); - items.insert(AgaBigKey, Item::special(AgaBigKey, "Agahnim's Tower Big Key")); - items.insert(DesertBigKey, Item::special(DesertBigKey, "Desert Palace Big Key")); - items.insert(EasternBigKey, Item::special(EasternBigKey, "Eastern Palace Big Key")); - items.insert(HyruleBigKey, Item::special(HyruleBigKey, "Hyrule Castle Big Key")); - items.insert(SewersBigKey, Item::special(SewersBigKey, "Sewers Big Key")); // not the same as HyruleBigKey? - items.insert(SewersKey, Item::consumable(SewersKey, "Sewers Key")); - items.insert(HyruleKey, Item::consumable(HyruleKey, "Hyrule Castle Key")); // not the same as SewersKey? - items.insert(EasternKey, Item::consumable(EasternKey, "Eastern Palace Key")); - items.insert(DesertKey, Item::consumable(DesertKey, "Desert Palace Key")); - items.insert(AgaKey, Item::consumable(AgaKey, "Agahnim's Tower Key")); - items.insert(SwampKey, Item::consumable(SwampKey, "Swamp Palace Key")); - items.insert(PodKey, Item::consumable(PodKey, "Palace of Darkness Key")); - items.insert(MireKey, Item::consumable(MireKey, "Misery Mire Key")); - items.insert(SkullKey, Item::consumable(SkullKey, "Skull Woods Key")); - items.insert(IceKey, Item::consumable(IceKey, "Ice Palace Key")); - items.insert(HeraKey, Item::consumable(HeraKey, "Tower of Hera Key")); - items.insert(ThievesKey, Item::consumable(ThievesKey, "Thieves' Town Key")); - items.insert(TurtleKey, Item::consumable(TurtleKey, "Turtle Rock Key")); - items.insert(GTKey, Item::consumable(GTKey, "Ganon's Tower Key")); - items.insert(Reserved0xAE, Item::special(Reserved0xAE, "")); - items.insert(Reserved0xAF, Item::special(Reserved0xAF, "")); - items.insert(Agahnim1, Item::special(Agahnim1, "Agahnim 1 Defeated")); - items.insert(Agahnim1Boss, Item::special(Agahnim1Boss, "Agahnim Boss")); - items.insert(Agahnim2, Item::special(Agahnim2, "Agahnim 2 Defeated")); - items.insert(Agahnim2Boss, Item::special(Agahnim2Boss, "Agahnim 2 Boss")); - items.insert(BigBomb, Item::special(BigBomb, "Big Bomb")); - items.insert(Crystal1, Item::special(Crystal1, "Crystal 1")); - items.insert(Crystal2, Item::special(Crystal2, "Crystal 2")); - items.insert(Crystal3, Item::special(Crystal3, "Crystal 3")); - items.insert(Crystal4, Item::special(Crystal4, "Crystal 4")); - items.insert(Crystal5, Item::special(Crystal5, "Crystal 5")); - items.insert(Crystal6, Item::special(Crystal6, "Crystal 6")); - items.insert(Crystal7, Item::special(Crystal7, "Crystal 7")); - items.insert(DesertBoss, Item::special(DesertBoss, "Desert Boss")); - items.insert(EasternBoss, Item::special(EasternBoss, "Eastern Boss")); - items.insert(Smith, Item::special(Smith, "Frog Smith")); - items.insert(Ganon, Item::special(Ganon, "Ganon")); - items.insert(GTArmosBoss, Item::special(GTArmosBoss, "GT Armos Boss")); - items.insert(GTLanmolasBoss, Item::special(GTLanmolasBoss, "GT Lanmolas Boss")); - items.insert(GTMoldormBoss, Item::special(GTMoldormBoss, "GT Moldorm Boss")); - items.insert(HeraBoss, Item::special(HeraBoss, "Hera Boss")); - items.insert(IceBoss, Item::special(IceBoss, "Ice Boss")); - items.insert(IceBlock, Item::special(IceBlock, "Ice Palace Block")); - items.insert(L1Shield, Item::progressive(L1Shield, "L1 Shield")); - items.insert(L1Sword, Item::progressive(L1Sword, "L1 Sword")); - items.insert(L2Shield, Item::progressive(L2Shield, "L2 Shield")); - items.insert(L2Sword, Item::progressive(L2Sword, "L2 Sword")); - items.insert(L3Shield, Item::progressive(L3Shield, "L3 Shield")); - items.insert(L3Sword, Item::progressive(L3Sword, "L3 Sword")); - items.insert(L4Sword, Item::progressive(L4Sword, "L4 Sword")); - items.insert(MireBoss, Item::special(MireBoss, "Mire Boss")); - items.insert(MiseryMireToken, Item::special(MiseryMireToken, "Misery Mire Entrance Token")); - items.insert(MireSwitch, Item::special(MireSwitch, "Misery Mire Switch")); - items.insert(PodBoss, Item::special(PodBoss, "PoD Boss")); - items.insert(PurpleChest, Item::special(PurpleChest, "Purple Chest")); - items.insert(SkullBoss, Item::special(SkullBoss, "Skull Boss")); - items.insert(ThievesBoss, Item::special(ThievesBoss, "Thieves Boss")); - items.insert(TurtleBoss, Item::special(TurtleBoss, "Turtle Boss")); - items.insert(ProgressiveMagic, Item::special(ProgressiveMagic, "Progressive Magic")); - - items - }; -} diff --git a/enemize/src/graph/locations.rs b/enemize/src/graph/locations.rs deleted file mode 100644 index 1192e1a..0000000 --- a/enemize/src/graph/locations.rs +++ /dev/null @@ -1,1146 +0,0 @@ -use std::collections::HashMap; - -use lazy_static::lazy_static; - -use super::item::ItemId; - -#[derive(Debug, thiserror::Error)] -#[error("{0} is not a dungeon super tile")] -pub struct NotADungeonSuperTile(u8); - -#[derive(Clone, Copy, Eq, Hash, PartialEq)] -pub enum Dungeon { - HyruleCastle, - EasternPalace, - DesertPalace, - TowerOfHera, - AgahnimsTower, - PalaceOfDarkness, - SwampPalace, - SkullWoods, - ThievesTown, - IcePalace, - MiseryMire, - TurtleRock, - GanonsTower, -} - -pub fn get_dungeon_from_room(room_id: u8) -> Result { - for (dungeon, rooms) in DUNGEON_ROOMS.iter() { - if let Ok(_) = rooms.binary_search(&room_id) { - return Ok(*dungeon); - } - } - - Err(NotADungeonSuperTile(room_id)) -} - -const HYRULE_CASTLE_ROOMS: [u8; 21] = [ - 1, 2, 17, 18, 33, 34, 50, 65, 66, 80, 81, 82, 96, 97, 98, 112, 113, 114, 128, 129, 130, -]; - -const EASTERN_PALACE_ROOMS: [u8; 13] = [ - 137, 153, 168, 169, 170, 184, 185, 186, 200, 201, 216, 217, 218, -]; - -const DESERT_PALACE_ROOMS: [u8; 10] = [51, 67, 83, 99, 115, 116, 117, 131, 132, 133]; - -const TOWER_OF_HERA_ROOMS: [u8; 7] = [7, 23, 39, 49, 119, 135, 167]; - -const AGAHNIMS_TOWER_ROOMS: [u8; 7] = [32, 48, 64, 176, 192, 208, 224]; - -const PALACE_OF_DARKNESS_ROOMS: [u8; 14] = [9, 10, 11, 25, 26, 27, 42, 43, 58, 59, 74, 75, 90, 106]; - -const SWAMP_PALACE_ROOMS: [u8; 13] = [6, 22, 38, 40, 52, 53, 54, 55, 56, 70, 84, 102, 118]; - -const SKULL_WOODS_ROOMS: [u8; 9] = [41, 57, 73, 86, 87, 88, 89, 103, 104]; - -const THIEVES_TOWN_ROOMS: [u8; 12] = [68, 69, 100, 101, 171, 172, 187, 188, 203, 204, 219, 220]; - -const ICE_PALACE_ROOMS: [u8; 22] = [ - 14, 30, 31, 46, 62, 63, 78, 79, 94, 95, 110, 126, 127, 142, 158, 159, 174, 175, 190, 191, 206, - 222, -]; - -const MISERY_MIRE_ROOMS: [u8; 18] = [ - 144, 145, 146, 147, 151, 152, 160, 161, 162, 163, 177, 178, 179, 193, 194, 195, 209, 210, -]; - -const TURTLE_ROCK_ROOMS: [u8; 17] = [ - 4, 19, 20, 21, 35, 36, 164, 180, 181, 182, 183, 196, 197, 198, 199, 213, 214, -]; - -const GANONS_TOWER_ROOMS: [u8; 26] = [ - 12, 13, 28, 29, 61, 76, 77, 91, 92, 93, 107, 108, 109, 123, 124, 125, 139, 140, 141, 149, 150, - 155, 156, 157, 165, 166, -]; - -lazy_static! { - static ref DUNGEON_ROOMS: HashMap = { - use Dungeon::*; - - let mut rooms = HashMap::new(); - - rooms.insert(HyruleCastle, &HYRULE_CASTLE_ROOMS[..]); - rooms.insert(EasternPalace, &EASTERN_PALACE_ROOMS[..]); - rooms.insert(DesertPalace, &DESERT_PALACE_ROOMS[..]); - rooms.insert(TowerOfHera, &TOWER_OF_HERA_ROOMS[..]); - rooms.insert(AgahnimsTower, &AGAHNIMS_TOWER_ROOMS[..]); - rooms.insert(PalaceOfDarkness, &PALACE_OF_DARKNESS_ROOMS[..]); - rooms.insert(SwampPalace, &SWAMP_PALACE_ROOMS[..]); - rooms.insert(SkullWoods, &SKULL_WOODS_ROOMS[..]); - rooms.insert(ThievesTown, &THIEVES_TOWN_ROOMS[..]); - rooms.insert(IcePalace, &ICE_PALACE_ROOMS[..]); - rooms.insert(MiseryMire, &MISERY_MIRE_ROOMS[..]); - rooms.insert(TurtleRock, &TURTLE_ROCK_ROOMS[..]); - rooms.insert(GanonsTower, &GANONS_TOWER_ROOMS[..]); - - rooms - }; - pub static ref DUNGEON_KEYS: HashMap = { - use Dungeon::*; - use ItemId::*; - - let mut keys = HashMap::new(); - - keys.insert(HyruleCastle, HyruleKey); - keys.insert(EasternPalace, EasternKey); - keys.insert(DesertPalace, DesertKey); - keys.insert(TowerOfHera, HeraKey); - keys.insert(AgahnimsTower, AgaKey); - keys.insert(PalaceOfDarkness, PodKey); - keys.insert(SwampPalace, SwampKey); - keys.insert(SkullWoods, SkullKey); - keys.insert(ThievesTown, ThievesKey); - keys.insert(IcePalace, IceKey); - keys.insert(MiseryMire, MireKey); - keys.insert(TurtleRock, TurtleKey); - keys.insert(GanonsTower, GTKey); - - keys - }; - pub static ref DUNGEON_BIG_KEYS: HashMap = { - use Dungeon::*; - use ItemId::*; - - let mut keys = HashMap::new(); - - keys.insert(HyruleCastle, HyruleBigKey); - keys.insert(EasternPalace, EasternBigKey); - keys.insert(DesertPalace, DesertBigKey); - keys.insert(TowerOfHera, HeraBigKey); - keys.insert(AgahnimsTower, AgaBigKey); - keys.insert(PalaceOfDarkness, PodBigKey); - keys.insert(SwampPalace, SwampBigKey); - keys.insert(SkullWoods, SkullBigKey); - keys.insert(ThievesTown, ThievesBigKey); - keys.insert(IcePalace, IceBigKey); - keys.insert(MiseryMire, MireBigKey); - keys.insert(TurtleRock, TurtleBigKey); - keys.insert(GanonsTower, GTBigKey); - - keys - }; -} - -#[derive(Clone, Copy, Eq, PartialEq, Hash)] -#[repr(u16)] -pub enum SuperTile { - Ganon = 0x0, - HyruleCastleNorthCorridor = 0x1, - HyruleCastleSwitchRoom = 0x2, - HoulihanRoom = 0x3, - TurtleRockCrystalRollerRoom = 0x4, - EmptyCloneRoom0x05 = 0x5, - SwampPalaceArrghus = 0x6, - TowerOfHeraMoldorm = 0x7, - CaveHealingFairy = 0x8, - PalaceOfDarknessMedusaChestRoom = 0x9, - PalaceOfDarknessStalfossTrapRoom = 0xa, - PalaceOfDarknessTurtleRoom = 0xb, - GanonsTowerEntranceRoom = 0xc, - GanonsTowerAgahnim2 = 0xd, - IcePalaceEntranceRoom = 0xe, - EmptyCloneRoom0x0F = 0xf, - GanonEvacuationRoute = 0x10, - HyruleCastleBombableStockRoom = 0x11, - InsideSanctuary = 0x12, - TurtleRockHokkuBokkuKeyRoom2 = 0x13, - TurtleRockBigKeyRoom = 0x14, - TurtleRockUselessTubes = 0x15, - SwampPalaceSwimmingTreadmill = 0x16, - TowerOfHeraMoldormFallRoom = 0x17, - Cave0x18BigFairyDropEntrance = 0x18, - PalaceOfDarknessDarkMaze = 0x19, - PalaceOfDarknessBigChestRoom = 0x1a, - PalaceOfDarknessMimicsMovingWallRoom = 0x1b, - GanonsTowerIceArmos = 0x1c, - GanonsTowerFinalHallway = 0x1d, - IcePalaceBombFloorBariRoom = 0x1e, - IcePalacePengatorBigKeyRoom = 0x1f, - AgahnimsTowerAgahnim = 0x20, - HyruleCastleKeyRatRoom = 0x21, - HyruleCastleSewerTextTriggerRoom = 0x22, - TurtleRockWestExitToBalcony = 0x23, - TurtleRockDoubleHokkuBokkuBigChestRoom = 0x24, - EmptyCloneRoom0x25 = 0x25, - SwampPalaceStatueRoom = 0x26, - TowerOfHeraBigChest = 0x27, - SwampPalaceEntranceRoom = 0x28, - SkullWoodsMothula = 0x29, - PalaceOfDarknessBigHubRoom = 0x2a, - PalaceOfDarknessMapChestFairyRoom = 0x2b, - CaveHookshotCaveBackdoor = 0x2c, - EmptyCloneRoom0x2D = 0x2d, - IcePalaceCompassRoom = 0x2e, - CaveKakarikoWellHP = 0x2f, - AgahnimsTowerMaidenSacrificeChamber = 0x30, - TowerOfHeraHardhatBeetleRoom = 0x31, - HyruleCastleSewerKeyChestRoom = 0x32, - DesertPalaceLanmolas = 0x33, - SwampPalacePushBlockPuzzlePreBigKeyRoom = 0x34, - SwampPalaceBigKeyBsRoom = 0x35, - SwampPalaceBigChestRoom = 0x36, - SwampPalaceMapChestWaterFillRoom = 0x37, - SwampPalaceKeyPotRoom = 0x38, - SkullWoodsGibdoKeyMothulaHoleRoom = 0x39, - PalaceOfDarknessBombableFloorRoom = 0x3a, - PalaceOfDarknessSpikeBlockConveyorRoom = 0x3b, - CaveHookshotCave = 0x3c, - GanonsTowerTorchRoom2 = 0x3d, - IcePalaceStalfosKnightConveyorHellway = 0x3e, - IcePalaceMapChestRoom = 0x3f, - AgahnimsTowerFinalBridgeRoom = 0x40, - HyruleCastleFirstDarkRoom = 0x41, - HyruleCastle6RopesRoom = 0x42, - DesertPalaceTorchPuzzleMovingWallRoom = 0x43, - ThievesTownBigChestRoom = 0x44, - ThievesTownJailCellsRoom = 0x45, - SwampPalaceCompassChestRoom = 0x46, - EmptyCloneRoom0x47 = 0x47, - EmptyCloneRoom0x48 = 0x48, - SkullWoodsGibdoTorchPuzzleRoom = 0x49, - PalaceOfDarknessEntranceRoom = 0x4a, - PalaceOfDarknessWarpsSouthMimicRoom = 0x4b, - GanonsTowerMiniHelmasaurConveyorRoom = 0x4c, - GanonsTowerMoldormRoom = 0x4d, - IcePalaceBombJumpRoom = 0x4e, - IcePalaceCloneRoomFairyRoom = 0x4f, - HyruleCastleWestCorridor = 0x50, - HyruleCastleThroneRoom = 0x51, - HyruleCastleEastCorridor = 0x52, - DesertPalacePopos2BeamosHellwayRoom = 0x53, - SwampPalaceUpstairsPitsRoom = 0x54, - CastleSecretEntranceUncleRoom = 0x55, - SkullWoodsKeyPotTrapRoom = 0x56, - SkullWoodsBigKeyRoom = 0x57, - SkullWoodsBigChestRoom = 0x58, - SkullWoodsFinalSectionEntranceRoom = 0x59, - PalaceOfDarknessHelmasaurKing = 0x5a, - GanonsTowerSpikePitRoom = 0x5b, - GanonsTowerGanonBallZ = 0x5c, - GanonsTowerGauntlet123 = 0x5d, - IcePalaceLonelyFirebar = 0x5e, - IcePalaceHiddenChestSpikeFloorRoom = 0x5f, - HyruleCastleWestEntranceRoom = 0x60, - HyruleCastleMainEntranceRoom = 0x61, - HyruleCastleEastEntranceRoom = 0x62, - DesertPalaceFinalSectionEntranceRoom = 0x63, - ThievesTownWestAtticRoom = 0x64, - ThievesTownEastAtticRoom = 0x65, - SwampPalaceHiddenChestHiddenDoorRoom = 0x66, - SkullWoodsCompassChestRoom = 0x67, - SkullWoodsKeyChestTrapRoom = 0x68, - EmptyCloneRoom0x69 = 0x69, - PalaceOfDarknessRupeeRoom = 0x6a, - GanonsTowerMimicsRoom = 0x6b, - GanonsTowerLanmolasRoom = 0x6c, - GanonsTowerGauntlet45 = 0x6d, - IcePalacePengatorsRoom = 0x6e, - EmptyCloneRoom0x6F = 0x6f, - HyruleCastleSmallCorridorToJailCells = 0x70, - HyruleCastleBoomerangChestRoom = 0x71, - HyruleCastleMapChestRoom = 0x72, - DesertPalaceBigChestRoom = 0x73, - DesertPalaceMapChestRoom = 0x74, - DesertPalaceBigKeyChestRoom = 0x75, - SwampPalaceWaterDrainRoom = 0x76, - TowerOfHeraEntranceRoom = 0x77, - EmptyCloneRoom0x78 = 0x78, - EmptyCloneRoom0x79 = 0x79, - EmptyCloneRoom0x7A = 0x7a, - GanonsTowerMisc = 0x7b, - GanonsTowerEastSideCollapsingBridgeExplodingWallRoom = 0x7c, - GanonsTowerWinderWarpMazeRoom = 0x7d, - IcePalaceHiddenChestBombableFloorRoom = 0x7e, - IcePalaceBigSpikeTrapsRoom = 0x7f, - HyruleCastleJailCellRoom = 0x80, - HyruleCastleNextToChasmRoom = 0x81, - HyruleCastleBasementChasmRoom = 0x82, - DesertPalaceWestEntranceRoom = 0x83, - DesertPalaceMainEntranceRoom = 0x84, - DesertPalaceEastEntranceRoom = 0x85, - EmptyCloneRoom0x86 = 0x86, - TowerOfHeraTileRoom = 0x87, - EmptyCloneRoom0x88 = 0x88, - EasternPalaceFairyRoom = 0x89, - EmptyCloneRoom0x8A = 0x8a, - GanonsTowerBlockPuzzleSpikeSkipMapChestRoom = 0x8b, - GanonsTowerEastAndWestDownstairsBigChestRoom = 0x8c, - GanonsTowerTileTorchPuzzleRoom = 0x8d, - IcePalaceBlobsWithTetrisBarrier = 0x8e, - EmptyCloneRoom0x8F = 0x8f, - MiseryMireVitreous = 0x90, - MiseryMireFinalSwitchRoom = 0x91, - MiseryMireDarkBombWallSwitchesRoom = 0x92, - MiseryMireDarkCaneFLoorSwitchPuzzleRoom = 0x93, - EmptyCloneRoom0x94 = 0x94, - GanonsTowerFinalCollapsingBridgeRoom = 0x95, - GanonsTowerTorches1Room = 0x96, - MiseryMireTorchPuzzleMovingWallRoom = 0x97, - MiseryMireEntranceRoom = 0x98, - EasternPalaceEyegoreKeyRoom = 0x99, - EmptyCloneRoom0x9A = 0x9a, - GanonsTowerManySpikesWarpMazeRoom = 0x9b, - GanonsTowerInvisibleFloorMazeRoom = 0x9c, - GanonsTowerCompassChestInvisibleFloorRoom = 0x9d, - IcePalaceBigChestRoom = 0x9e, - IcePalaceIceFloorPotsKeyAndSwitch = 0x9f, - MiseryMirePreVitreousRoom = 0xa0, - MiseryMireFishRoom = 0xa1, - MiseryMireBridgeKeyChestRoom = 0xa2, - MiseryMireEmptyLConnectingRoom = 0xa3, - TurtleRockTrinexx = 0xa4, - GanonsTowerWizzrobesRooms = 0xa5, - GanonsTowerMoldormFallRoom = 0xa6, - TowerOfHeraFairyRoom = 0xa7, - EasternPalaceStalfosSpawnRoom = 0xa8, - EasternPalaceBigChestRoom = 0xa9, - EasternPalaceMapChestRoom = 0xaa, - ThievesTownMovingSpikesKeyPotRoom = 0xab, - ThievesTownBlindTheThief = 0xac, - EmptyCloneRoom0xAD = 0xad, - IcePalaceIceTRoom = 0xae, - IcePalaceIceBridgeRoom = 0xaf, - AgahnimsTowerCircleOfPots = 0xb0, - MiseryMireHourglassRoom = 0xb1, - MiseryMireSlugRoom = 0xb2, - MiseryMireSpikeKeyChestRoom = 0xb3, - TurtleRockPreTrinexxRoom = 0xb4, - TurtleRockDarkMaze = 0xb5, - TurtleRockChainChompsRoom = 0xb6, - TurtleRockMapChestKeyChestRollerRoom = 0xb7, - EasternPalaceBigKeyRoom = 0xb8, - EasternPalaceLobbyCannonballsRoom = 0xb9, - EasternPalaceDarkAntifairyKeyPotRoom = 0xba, - ThievesTownHellway = 0xbb, - ThievesTownConveyorToilet = 0xbc, - EmptyCloneRoom0xBD = 0xbd, - IcePalaceBlockPuzzleRoom = 0xbe, - IcePalaceCloneRoomSwitchRoom = 0xbf, - AgahnimsTowerDarkBridgeRoom = 0xc0, - MiseryMireCompassChestTileRoom = 0xc1, - MiseryMireBigHubRoom = 0xc2, - MiseryMireBigChestRoom = 0xc3, - TurtleRockFinalCrystalSwitchPuzzleRoom = 0xc4, - TurtleRockLaserBridge = 0xc5, - TurtleRockSomariaHub = 0xc6, - TurtleRockTorchPuzzle = 0xc7, - EasternPalaceArmosKnights = 0xc8, - EasternPalaceEntranceRoom = 0xc9, - UnknownRoom = 0xca, - ThievesTownNorthWestEntranceRoom = 0xcb, - ThievesTownNorthEastEntranceRoom = 0xcc, - EmptyCloneRoom0xCD = 0xcd, - IcePalaceHoleToKholdstareRoom = 0xce, - EmptyCloneRoom0xCF = 0xcf, - AgahnimsTowerDarkMaze = 0xd0, - MiseryMireConveyorSlugBigKeyRoom = 0xd1, - MiseryMireWizzrobesRoom = 0xd2, - EmptyCloneRoom0xD3 = 0xd3, - EmptyCloneRoom0xD4 = 0xd4, - TurtleRockLaserKeyRoom = 0xd5, - TurtleRockEntranceRoom = 0xd6, - EmptyCloneRoom0xD7 = 0xd7, - EasternPalacePreArmosKnightsRoom = 0xd8, - EasternPalaceCannonballRoom = 0xd9, - EasternPalacePotSwitchRoom = 0xda, - ThievesTownSouthWestEntranceRoom = 0xdb, - ThievesTownSouthEastEntranceRoom = 0xdc, - EmptyCloneRoom0xDD = 0xdd, - IcePalaceKholdstare = 0xde, - CaveBackwardsDeathMountainTopFloor = 0xdf, - AgahnimsTowerEntranceRoom = 0xe0, - CaveLostWoodsHP = 0xe1, - CaveLumberjacksTreeHP = 0xe2, - CaveMagicBat = 0xe3, - CaveLostOldManHouse = 0xe4, - CaveLostOldManHouseBack = 0xe5, - Cave0xE6 = 0xe6, - Cave0xE7 = 0xe7, - CaveSuperBunnyTop = 0xe8, - EmptyCloneRoom0xE9 = 0xe9, - CaveSpectacleRockHP = 0xea, - CaveBumperCaveTop = 0xeb, - EmptyCloneRoom0xEC = 0xec, - Cave0xED = 0xed, - CaveSpiralCave = 0xee, - CaveCrystalSwitch5ChestsRoom = 0xef, - CaveLostOldManStartingCaveBottom = 0xf0, - CaveLostOldManStartingCaveTop = 0xf1, - HouseOldWomanNextDoor = 0xf2, - HouseOldWomanSahasrahlasWifeMaybe = 0xf3, - HouseAngryBrothersWest = 0xf4, - HouseAngryBrothersEast = 0xf5, - EmptyCloneRoom0xF6 = 0xf6, - EmptyCloneRoom0xF7 = 0xf7, - CaveSuperBunnyBottom = 0xf8, - CaveSpectacleRockExit = 0xf9, - CaveSpectacleRockPrizeEntrance = 0xfa, - CaveBumperCaveBottom = 0xfb, - EmptyCloneRoom0xFC = 0xfc, - Cave0xFD = 0xfd, - CaveSpiralCaveExit = 0xfe, - CaveParadoxCaveMiddleEntrance = 0xff, - ShopInLostWoods0x100 = 0x100, - ScaredLadyHouses = 0x101, - SickKid = 0x102, - InnBushHouse = 0x103, - InsideLinksHouse = 0x104, - ShabadooHouse = 0x105, - ChestGameBombHouse = 0x106, - LibraryBombFarmRoom = 0x107, - ChickenHouse = 0x108, - InsideWitchHut = 0x109, - Aginah = 0x10a, - Dam = 0x10b, - MimicCave = 0x10c, - CaveOutsideMiseryMire = 0x10d, - CaveDarkHyliaLedgeHint = 0x10e, - ShopDarkWorldShops = 0x10f, - ShopFireShieldShop = 0x110, - ArcherGame = 0x111, - CaveShop = 0x112, - KingsTomb = 0x113, - WaterfallCaveMireHint = 0x114, - BigFairy = 0x115, - FatFairy = 0x116, - SpikeCave = 0x117, - ChestGamblingGame = 0x118, - BlindsHouse = 0x119, - Mutant = 0x11a, - MirrorCaveGroveAndTomb = 0x11b, - InsideBombShop = 0x11c, - BlindsBasement = 0x11d, - HypeCave = 0x11e, - KakarikoShopLumberjackHouse = 0x11f, - IceRodCave = 0x120, - SmithHouse = 0x121, - FortuneTellers = 0x122, - MiniMoldormCave = 0x123, - FiftyRupeeCaveBonkCave = 0x124, - TwentyRupeeCaveDarkHyliaSpikeHintCave = 0x125, - CheckerBoardCave = 0x126, - HammerPegCave = 0x127, - LostWoods = 0x8000, - LumberjackHouse = 0x8002, - DeathMountainWest = 0x8003, - DeathMountainEast = 0x8005, - DeathMountainTurtleWarp = 0x8007, - EntranceDeathMountain = 0x800a, - EntranceZorasDomain = 0x800f, - PathFromLostWoodsToKakariko = 0x8010, - KakarikoFortuneTeller = 0x8011, - NorthWestWhirlpoolWarp = 0x8012, - Sanctuary = 0x8013, - Graveyard = 0x8014, - River = 0x8015, - WitchHut = 0x8016, - EastOfWitchHut = 0x8017, - Kakariko = 0x8018, - CastleWoods = 0x801a, - HyruleCastle = 0x801b, - WitchGraveyardBridge = 0x801d, - EasternPalace = 0x801e, - Smithy = 0x8022, - StonyFieldNorth = 0x8025, - KakarikoMazeRace = 0x8028, - KakarikoLibrary = 0x8029, - HauntedGrove = 0x802a, - BetweenGroveAndLinksHouse = 0x802b, - LinksHouse = 0x802c, - StonyFieldSouth = 0x802d, - BetweenLakeHyliaAndEasternPalace = 0x802e, - FluteSpot5 = 0x802f, - Desert = 0x8030, - SouthOfHauntedGrove = 0x8032, - NorthwestSwamp = 0x8033, - NortheastSwamp = 0x8034, - LakeHylia = 0x8035, - IceCave = 0x8037, - DesertPass = 0x803a, - SouthwestSwamp = 0x803b, - SoutheastSwamp = 0x803c, - SouthShore = 0x803f, - ShullWoods = 0x8040, - DarkLumberjackShop = 0x8042, - DarkDeathMountainWest = 0x8043, - DarkDeathMountainEast = 0x8045, - TurtleRock = 0x8047, - BumperCave = 0x804a, - Catfish = 0x804f, - PathFromSkullWoodsToVillage = 0x8050, - OutcastFortuneTeller = 0x8051, - NorthWestPond = 0x8052, - DarkChapel = 0x8053, - DarkGraveyard = 0x8054, - DarkRiver = 0x8055, - DarkWitchHut = 0x8056, - EastOfDarkWitchHut = 0x8057, - VillageOfOutcasts = 0x8058, - FireShieldShopForest = 0x805a, - Pyramid = 0x805b, - BrokenBridge = 0x805d, - PalaceOfDarkness = 0x805e, - PurpleChestHouse = 0x8062, - DarkStonyFieldNorth = 0x8065, - DiggingGame = 0x8068, - TrappedFrog = 0x8069, - StumpysGrove = 0x806a, - BetweenGroveAndBombShop = 0x806b, - BombShop = 0x806c, - HammerPegBridge = 0x806d, - BetweenPODAndIceLake = 0x806e, - DarkFluteSpot5 = 0x806f, - MiseryMire = 0x8070, - SouthOfStumpysGrove = 0x8072, - DarkNorthwestSwamp = 0x8073, - DarkNortheastSwamp = 0x8074, - IceLake = 0x8075, - DarkIceCave = 0x8077, - BoxCanyonBetweenMireAndSwamp = 0x807a, - DarkSouthwestSwamp = 0x807b, - DarkSoutheastSwamp = 0x807c, - DarkSouthShore = 0x807f, - MasterSwordGladeAndUnderBridge = 0x8080, - ZorasDomain = 0x8081, -} - -use SuperTile::*; - -lazy_static! { - pub static ref ROOM_NAMES: HashMap = { - - let names = [ - (Ganon, "Ganon"), - (HyruleCastleNorthCorridor, "Hyrule Castle (North Corridor)"), - (HyruleCastleSwitchRoom, "Hyrule Castle (Switch Room)"), - (HoulihanRoom, "Houlihan Room"), - (TurtleRockCrystalRollerRoom, "Turtle Rock (Crystal Roller Room)"), - (EmptyCloneRoom0x05, "Empty Clone Room"), - (SwampPalaceArrghus, "Swamp Palace (Arrghus[Boss])"), - (TowerOfHeraMoldorm, "Tower of Hera (Moldorm[Boss])"), - (CaveHealingFairy, "Cave (Healing Fairy)"), - (PalaceOfDarknessMedusaChestRoom, "Palace of Darkness (Medusa Chest Room)"), - (PalaceOfDarknessStalfossTrapRoom, "Palace of Darkness (Stalfoss Trap Room)"), - (PalaceOfDarknessTurtleRoom, "Palace of Darkness (Turtle Room)"), - (GanonsTowerEntranceRoom, "Ganon's Tower (Entrance Room)"), - (GanonsTowerAgahnim2, "Ganaon's Tower (Agahnim2[Boss])"), - (IcePalaceEntranceRoom, "Ice Palace (Entrance Room)"), - (EmptyCloneRoom0x0F, "Empty Clone Room"), - (GanonEvacuationRoute, "Ganon Evacuation Route"), - (HyruleCastleBombableStockRoom, "Hyrule Castle (Bombable Stock Room)"), - (Sanctuary, "Sanctuary"), - (TurtleRockHokkuBokkuKeyRoom2, "Turtle Rock (Hokku-Bokku Key Room 2)"), - (TurtleRockBigKeyRoom, "Turtle Rock (Big Key Room)"), - (TurtleRockUselessTubes, "Turtle Rock (Useless Tubes)"), - (SwampPalaceSwimmingTreadmill, "Swamp Palace (Swimming Treadmill)"), - (TowerOfHeraMoldormFallRoom, "Tower of Hera (Moldorm Fall Room)"), - (Cave0x18BigFairyDropEntrance, "Big Fairy Drop Entrance Cave"), - (PalaceOfDarknessDarkMaze, "Palace of Darkness (Dark Maze)"), - (PalaceOfDarknessBigChestRoom, "Palace of Darkness (Big Chest Room)"), - (PalaceOfDarknessMimicsMovingWallRoom, "Palace of Darkness (Mimics / Moving Wall Room"), - (GanonsTowerIceArmos, "Ganon's Tower (Ice Armos)"), - (GanonsTowerFinalHallway, "Ganon's Tower (Final Hallway)"), - (IcePalaceBombFloorBariRoom, "Ice Palace (Bomb Floor / Bari Room)"), - (IcePalacePengatorBigKeyRoom, "Ice Palace (Pengator / Big Key Room)"), - (AgahnimsTowerAgahnim, "Agahnim's Tower (Agahnim[Boss])"), - (HyruleCastleKeyRatRoom, "Hyrule Castle (Key Rat Room)"), - (HyruleCastleSewerTextTriggerRoom, "Hyrule Castle (Sewer Text Trigger Room)"), - (TurtleRockWestExitToBalcony, "Turtle Rock (West Exit to Balcony)"), - (TurtleRockDoubleHokkuBokkuBigChestRoom, "Turtle Rock (Double Hokku-Bokku / Big Chest Room)"), - (EmptyCloneRoom0x25, "Empty Clone Room"), - (SwampPalaceStatueRoom, "Swamp Palace (Statue Room)"), - (TowerOfHeraBigChest, "Tower of Hera (Big Chest)"), - (SwampPalaceEntranceRoom, "Swamp Palace (Entrance Room)"), - (SkullWoodsMothula, "Skull Woods (Mothula[Boss])"), - (PalaceOfDarknessBigHubRoom, "Palace of Darkness (Big Hub Room)"), - (PalaceOfDarknessMapChestFairyRoom, "Palace of Darkness (Map Chest / Fairy Room)"), - (CaveHookshotCaveBackdoor, "Hookshot Cave Backdoor (Big Fairy)"), - (EmptyCloneRoom0x2D, "Empty Clone Room"), - (IcePalaceCompassRoom, "Ice Palace (Compass Room)"), - (CaveKakarikoWellHP, "Kakariko Well HP"), - (AgahnimsTowerMaidenSacrificeChamber, "Agahnim's Tower (Maiden Sacrifice Chamber)"), - (TowerOfHeraHardhatBeetleRoom, "Tower of Hera (Hardhat Beetles Room)"), - (HyruleCastleSewerKeyChestRoom, "Hyrule Castle (Sewer Key Chest Room)"), - (DesertPalaceLanmolas, "Desert Palace (Lanmolas[Boss])"), - (SwampPalacePushBlockPuzzlePreBigKeyRoom, "Swamp Palace (Push Block Puzzle / Pre-Big-Key Room"), - (SwampPalaceBigKeyBsRoom, "Swamp Palace (Big Key / BS Room)"), - (SwampPalaceBigChestRoom, "Swamp Palace (Big Chest Room)"), - (SwampPalaceMapChestWaterFillRoom, "Swamp Palace (Map Chest / Water Fill Room)"), - (SwampPalaceKeyPotRoom, "Swamp Palace (Key Pot Room)"), - (SkullWoodsGibdoKeyMothulaHoleRoom, "Skull Woods (Gibdo Key / Mothula Hole Room)"), - (PalaceOfDarknessBombableFloorRoom, "Palace of Darkness (Bombable Floor Room)"), - (PalaceOfDarknessSpikeBlockConveyorRoom, "Palace of Darkness (Spike Block / Conveyor Room)"), - (CaveHookshotCave, "Hookshot Cave"), - (GanonsTowerTorchRoom2, "Ganon's Tower (Torch Room 2)"), - (IcePalaceStalfosKnightConveyorHellway, "Ice Palace (Stalfos Knight / Conveyor Hellway)"), - (IcePalaceMapChestRoom, "Ice Palace (Map Chest Room)"), - (AgahnimsTowerFinalBridgeRoom, "Agahnim's Tower (Final Bridge Room)"), - (HyruleCastleFirstDarkRoom, "Hyrule Castle (First Dark Room)"), - (HyruleCastle6RopesRoom, "Hyrule Castle (6 Ropes Room)"), - (DesertPalaceTorchPuzzleMovingWallRoom, "Desert Palace (Torch Puzzle / Moving Wall Room)"), - (ThievesTownBigChestRoom, "Thieves' Town (Big Chest Room)"), - (ThievesTownJailCellsRoom, "Thieves' Town (Jail Cells Room)"), - (SwampPalaceCompassChestRoom, "Swamp Palace (Compass Chest Room)"), - (EmptyCloneRoom0x47, "Empty Clone Room"), - (EmptyCloneRoom0x48, "Empty Clone Room"), - (SkullWoodsGibdoTorchPuzzleRoom, "Skull Woods (Gibdo Torch Puzzle Room)"), - (PalaceOfDarknessEntranceRoom, "Palace of Darkness (Entrance Room)"), - (PalaceOfDarknessWarpsSouthMimicRoom, "Palace of Darkness (Warps / South Mimic Room)"), - (GanonsTowerMiniHelmasaurConveyorRoom, "Ganon's Tower (Mini-Helmasaur Conveyor Room)"), - (GanonsTowerMoldormRoom, "Ganon's Tower (Moldorm Room)"), - (IcePalaceBombJumpRoom, "Ice Palace (Bomb Jump Room)"), - (IcePalaceCloneRoomFairyRoom, "Ice Palace Clone Room (Fairy Room)"), - (HyruleCastleWestCorridor, "Hyrule Castle (West Corridor)"), - (HyruleCastleThroneRoom, "Hyrule Castle (Throne Room)"), - (HyruleCastleEastCorridor, "Hyrule Castle (East Corridor)"), - (DesertPalacePopos2BeamosHellwayRoom, "Desert Palace (Popos 2 / Beamos Hellway Room)"), - (SwampPalaceUpstairsPitsRoom, "Swamp Palace (Upstairs Pits Room)"), - (CastleSecretEntranceUncleRoom, "Castle Secret Entrance / Uncle Room"), - (SkullWoodsKeyPotTrapRoom, "Skull Woods (Key Pot / Trap Room)"), - (SkullWoodsBigKeyRoom, "Skull Woods (Big Key Room)"), - (SkullWoodsBigChestRoom, "Skull Woods (Big Chest Room)"), - (SkullWoodsFinalSectionEntranceRoom, "Skull Woods (Final Section Entrance Room)"), - (PalaceOfDarknessHelmasaurKing, "Palace of Darkness (Helmasaur King[Boss])"), - (GanonsTowerSpikePitRoom, "Ganon's Tower (Spike Pit Room)"), - (GanonsTowerGanonBallZ, "Ganon's Tower (Ganon-Ball Z)"), - (GanonsTowerGauntlet123, "Ganon's Tower (Gauntlet 1/2/3)"), - (IcePalaceLonelyFirebar, "Ice Palace (Lonely Firebar)"), - (IcePalaceHiddenChestSpikeFloorRoom, "Ice Palace (Hidden Chest / Spike Floor Room)"), - (HyruleCastleWestEntranceRoom, "Hyrule Castle (West Entrance Room)"), - (HyruleCastleMainEntranceRoom, "Hyrule Castle (Main Entrance Room)"), - (HyruleCastleEastEntranceRoom, "Hyrule Castle (East Entrance Room)"), - (DesertPalaceFinalSectionEntranceRoom, "Desert Palace (Final Section Entrance Room)"), - (ThievesTownWestAtticRoom, "Thieves' Town (West Attic Room)"), - (ThievesTownEastAtticRoom, "Thieves' Town (East Attic Room)"), - (SwampPalaceHiddenChestHiddenDoorRoom, "Swamp Palace (Hidden Chest / Hidden Door Room)"), - (SkullWoodsCompassChestRoom, "Skull Woods (Compass Chest Room)"), - (SkullWoodsKeyChestTrapRoom, "Skull Woods (Key Chest / Trap Room)"), - (EmptyCloneRoom0x69, "Empty Clone Room"), - (PalaceOfDarknessRupeeRoom, "Palace of Darkness (Rupee Room)"), - (GanonsTowerMimicsRoom, "Ganon's Tower (Mimics Room)"), - (GanonsTowerLanmolasRoom, "Ganon's Tower (Lanmolas Room"), - (GanonsTowerGauntlet45, "Ganon's Tower (Gauntlet 4/5)"), - (IcePalacePengatorsRoom, "Ice Palace (Pengators Room)"), - (EmptyCloneRoom0x6F, "Empty Clone Room"), - (HyruleCastleSmallCorridorToJailCells, "Hyrule Castle (Small Corridor to Jail Cells"), - (HyruleCastleBoomerangChestRoom, "Hyrule Castle (Boomerang Chest Room)"), - (HyruleCastleMapChestRoom, "Hyrule Castle (Map Chest Room)"), - (DesertPalaceBigChestRoom, "Desert Palace (Big Chest Room)"), - (DesertPalaceMapChestRoom, "Desert Palace (Map Chest Room)"), - (DesertPalaceBigKeyChestRoom, "Desert Palace (Big Key Chest Room)"), - (SwampPalaceWaterDrainRoom, "Swamp Palace (Water Drain Room)"), - (TowerOfHeraEntranceRoom, "Tower of Hera (Entrance Room)"), - (EmptyCloneRoom0x78, "Empty Clone Room"), - (EmptyCloneRoom0x79, "Empty Clone Room"), - (EmptyCloneRoom0x7A, "Empty Clone Room"), - (GanonsTowerMisc, "Ganon's Tower (Sideways Conveyors / 4 Chest 4 Shooter / Square Pit"), - (GanonsTowerEastSideCollapsingBridgeExplodingWallRoom, "Ganon's Tower (East Side Collapsing Bridge / Exploding Wall Room)"), - (GanonsTowerWinderWarpMazeRoom, "Ganon's Tower (Winder / Warp Maze Room)"), - (IcePalaceHiddenChestBombableFloorRoom, "Ice Palace (Hidden Chest / Bombable Floor Room)"), - (IcePalaceBigSpikeTrapsRoom, "Ice Palace (Big Spike Traps Room)"), - (HyruleCastleJailCellRoom, "Hyrule Castle (Jail Cell Room)"), - (HyruleCastleNextToChasmRoom, "Hyrule Castle (Next to Chasm Room)"), - (HyruleCastleBasementChasmRoom, "Hyrule Castle (Basement Chasm Room)"), - (DesertPalaceWestEntranceRoom, "Desert Palace (West Entrance Room)"), - (DesertPalaceMainEntranceRoom, "Desert Palace (Main Entrance Room)"), - (DesertPalaceEastEntranceRoom, "Desert Palace (East Entrance Room)"), - (EmptyCloneRoom0x86, "Empty Clone Room"), - (TowerOfHeraTileRoom, "Tower of Hera (Tile Room)"), - (EmptyCloneRoom0x88, "Empty Clone Room"), - (EasternPalaceFairyRoom, "Eastern Palace (Fairy Room)"), - (EmptyCloneRoom0x8A, "Empty Clone Room"), - (GanonsTowerBlockPuzzleSpikeSkipMapChestRoom, "Ganon's Tower (Block Puzzle / Spike Skip / Map Chest Room)"), - (GanonsTowerEastAndWestDownstairsBigChestRoom, "Ganon's Tower (East and West Downstairs / Big Chest Room)"), - (GanonsTowerTileTorchPuzzleRoom, "Ganon's Tower (Tile / Torch Puzzle Room)"), - (IcePalaceBlobsWithTetrisBarrier, "Ice Palace (Blobs with Tetris Barrier)"), - (EmptyCloneRoom0x8F, "Empty Clone Room"), - (MiseryMireVitreous, "Misery Mire (Vitreous[Boss])"), - (MiseryMireFinalSwitchRoom, "Misery Mire (Final Switch Room)"), - (MiseryMireDarkBombWallSwitchesRoom, "Misery Mire (Dark Bomb Wall / Switches Room)"), - (MiseryMireDarkCaneFLoorSwitchPuzzleRoom, "Misery Mire (Dark Cane FLoor Switch Puzzle Room)"), - (EmptyCloneRoom0x94, "Empty Clone Room"), - (GanonsTowerFinalCollapsingBridgeRoom, "Ganon's Tower (Final Collapsing Bridge Room)"), - (GanonsTowerTorches1Room, "Ganon's Tower (Torches 1 Room)"), - (MiseryMireTorchPuzzleMovingWallRoom, "Misery Mire (Torch Puzzle / Moving Wall Room)"), - (MiseryMireEntranceRoom, "Misery Mire (Entrance Room)"), - (EasternPalaceEyegoreKeyRoom, "Eastern Palace (Eyegore Key Room)"), - (EmptyCloneRoom0x9A, "Empty Clone Room"), - (GanonsTowerManySpikesWarpMazeRoom, "Ganon's Tower (Many Spikes / Warp Maze Room)"), - (GanonsTowerInvisibleFloorMazeRoom, "Ganon's Tower (Invisible Floor Maze Room)"), - (GanonsTowerCompassChestInvisibleFloorRoom, "Ganon's Tower (Compass Chest / Invisible Floor Room)"), - (IcePalaceBigChestRoom, "Ice Palace (Big Chest Room)"), - (IcePalaceIceFloorPotsKeyAndSwitch, "Ice Palace (Room with ice floor, key, and 4 wall rats)"), - (MiseryMirePreVitreousRoom, "Misery Mire (Pre-Vitreous Room)"), - (MiseryMireFishRoom, "Misery Mire (Fish Room)"), - (MiseryMireBridgeKeyChestRoom, "Misery Mire (Bridge Key Chest Room)"), - (MiseryMireEmptyLConnectingRoom, "Misery Mire (Empty 'L' Connecting Room)"), - (TurtleRockTrinexx, "Turtle Rock (Trinexx[Boss])"), - (GanonsTowerWizzrobesRooms, "Ganon's Tower (Wizzrobes Rooms)"), - (GanonsTowerMoldormFallRoom, "Ganon's Tower (Moldorm Fall Room)"), - (TowerOfHeraFairyRoom, "Tower of Hera (Fairy Room)"), - (EasternPalaceStalfosSpawnRoom, "Eastern Palace (Staflos Spawn Room)"), - (EasternPalaceBigChestRoom, "Eastern Palace (Big Chest Room)"), - (EasternPalaceMapChestRoom, "Eastern Palace (Map Chest Room)"), - (ThievesTownMovingSpikesKeyPotRoom, "Thieves' Town (Moving Spikes / Key Pot Room)"), - (ThievesTownBlindTheThief, "Thieves' Town (Blind the Thief[Boss])"), - (EmptyCloneRoom0xAD, "Empty Clone Room"), - (IcePalaceIceTRoom, "Ice Palace (Ice T Room)"), - (IcePalaceIceBridgeRoom, "Ice Palace (Ice Bridge Room)"), - (AgahnimsTowerCircleOfPots, "Agahnim's Tower (Circle of Pots)"), - (MiseryMireHourglassRoom, "Misery Mire (Hourglass Room)"), - (MiseryMireSlugRoom, "Misery Mire (Slug Room)"), - (MiseryMireSpikeKeyChestRoom, "Misery Mire (Spike Key Chest Room)"), - (TurtleRockPreTrinexxRoom, "Turtle Rock (Pre-Trinexx Room)"), - (TurtleRockDarkMaze, "Turtle Rock (Dark Maze)"), - (TurtleRockChainChompsRoom, "Turtle Rock (Chain Chomps Room)"), - (TurtleRockMapChestKeyChestRollerRoom, "Turtle Rock (Map Chest / Key Chest / Roller Room)"), - (EasternPalaceBigKeyRoom, "Eastern Palace (Big Key Room)"), - (EasternPalaceLobbyCannonballsRoom, "Eastern Palace (Lobby Cannonballs Room)"), - (EasternPalaceDarkAntifairyKeyPotRoom, "Eastern Palace (Dark Antifairy / Key Pot Room)"), - (ThievesTownHellway, "Thieves' Town (Hellway)"), - (ThievesTownConveyorToilet, "Thieves' Town (Conveyor Toilet)"), - (EmptyCloneRoom0xBD, "Empty Clone Room"), - (IcePalaceBlockPuzzleRoom, "Ice Palace (Block Puzzle Room)"), - (IcePalaceCloneRoomSwitchRoom, "Ice Palace Clone Room (Switch Room)"), - (AgahnimsTowerDarkBridgeRoom, "Agahnim's Tower (Dark Bridge Room)"), - (MiseryMireCompassChestTileRoom, "Misery Mire (Compass Chest / Tile Room)"), - (MiseryMireBigHubRoom, "Misery Mire (Big Hub Room)"), - (MiseryMireBigChestRoom, "Misery Mire (Big Chest Room)"), - (TurtleRockFinalCrystalSwitchPuzzleRoom, "Turtle Rock (Final Crystal Switch Puzzle Room)"), - (TurtleRockLaserBridge, "Turtle Rock (Laser Bridge)"), - (TurtleRockSomariaHub, "Turtle Rock (Somaria Hub Room)"), - (TurtleRockTorchPuzzle, "Turtle Rock (Torch Puzzle)"), - (EasternPalaceArmosKnights, "Eastern Palace (Armos Knights[Boss])"), - (EasternPalaceEntranceRoom, "Eastern Palace (Entrance Room)"), - (UnknownRoom, "Unused Room??"), - (ThievesTownNorthWestEntranceRoom, "Thieves' Town (North West Entrance Room)"), - (ThievesTownNorthEastEntranceRoom, "Thieves' Town (North East Entrance Room)"), - (EmptyCloneRoom0xCD, "Empty Clone Room"), - (IcePalaceHoleToKholdstareRoom, "Ice Palace (Hole to Kholdstare Room)"), - (EmptyCloneRoom0xCF, "Empty Clone Room"), - (AgahnimsTowerDarkMaze, "Agahnim's Tower (Dark Maze)"), - (MiseryMireConveyorSlugBigKeyRoom, "Misery Mire (Conveyor Slug / Big Key Room)"), - (MiseryMireWizzrobesRoom, "Misery Mire (Wizzrobes Room)"), - (EmptyCloneRoom0xD3, "Empty Clone Room"), - (EmptyCloneRoom0xD4, "Empty Clone Room"), - (TurtleRockLaserKeyRoom, "Turtle Rock (Laser Key Room)"), - (TurtleRockEntranceRoom, "Turtle Rock (Entrance Room)"), - (EmptyCloneRoom0xD7, "Empty Clone Room"), - (EasternPalacePreArmosKnightsRoom, "Eastern Palace (Pre-Armos-Knights Room)"), - (EasternPalaceCannonballRoom, "Eastern Palace (Cannonball Room)"), - (EasternPalacePotSwitchRoom, "Eastern Palace (Pot Switch Room)"), - (ThievesTownSouthWestEntranceRoom, "Thieves' Town (South West Entrance Room)"), - (ThievesTownSouthEastEntranceRoom, "Thieves Town (South East Entrance Room)"), - (EmptyCloneRoom0xDD, "Empty Clone Room"), - (IcePalaceKholdstare, "Ice Palace (Kholdstare[Boss])"), - (CaveBackwardsDeathMountainTopFloor, "Death Mountain Exit (Top)"), - (AgahnimsTowerEntranceRoom, "Agahnim's Tower (Entrance Room)"), - (CaveLostWoodsHP, "Cave (Lost Woods HP)"), - (CaveLumberjacksTreeHP, "Cave (Lumberjacks' Tree HP)"), - (CaveMagicBat, "Cave (Magic Bat)"), - (CaveLostOldManHouse, "Cave (Lost Old Man's House)"), - (CaveLostOldManHouseBack, "Cave (Lost Old Man's House Back)"), - (Cave0xE6, "Cave (Bunch of Keese)"), - (Cave0xE7, "Cave (Bunch of Keese 2)"), - (CaveSuperBunnyTop, "Cave (Super Bunny Top)"), - (EmptyCloneRoom0xE9, "Empty Clone Room"), - (CaveSpectacleRockHP, "Cave (Inside Spectacle Rock HP)"), - (CaveBumperCaveTop, "Cave (Bumper Cave Top)"), - (EmptyCloneRoom0xEC, "Empty Clone Room"), - (Cave0xED, "Cave (??)"), - (CaveSpiralCave, "Cave (Spiral Cave)"), - (CaveCrystalSwitch5ChestsRoom, "Cave (Crystal Switch / 5 Chests Room)"), - (CaveLostOldManStartingCaveBottom, "Cave (Lost Old Man Starting Cave Bottom)"), - (CaveLostOldManStartingCaveTop, "Cave (Lost Old Man Starting Cave Top)"), - (HouseOldWomanNextDoor, "House (Old Woman Next Door)"), - (HouseOldWomanSahasrahlasWifeMaybe, "House (Old Woman / Sahasrahla's Wife?)"), - (HouseAngryBrothersWest, "House (Angry Brothers West)"), - (HouseAngryBrothersEast, "House (Angry Brothers East)"), - (EmptyCloneRoom0xF6, "Empty Clone Room"), - (EmptyCloneRoom0xF7, "Empty Clone Room"), - (CaveSuperBunnyBottom, "Cave (Super Bunny Bottom)"), - (CaveSpectacleRockExit, "Cave (Spectacle Rock Exit)"), - (CaveSpectacleRockPrizeEntrance, "Cave (Spectacle Rock Prize Entrance)"), - (CaveBumperCaveBottom, "Cave (Bumper Cave Bottom)"), - (EmptyCloneRoom0xFC, "Empty Clone Room"), - (Cave0xFD, "Cave (Death Mountain)"), - (CaveSpiralCaveExit, "Cave (Spiral Cave Exit)"), - (CaveParadoxCaveMiddleEntrance, "Cave (Paradox Cave 'Middle' Entrance)"), - (ShopInLostWoods0x100, "Shop in Lost Woods"), - (ScaredLadyHouses, "Scared Ladies' Houses"), - (SickKid, "Sick Kid"), - (InnBushHouse, "Inn / BushHouse"), - (LinksHouse, "Link's House"), - (ShabadooHouse, "Sahasrahla's House"), - (ChestGameBombHouse, "Chest Game / Outcast Village Bomb House"), - (LibraryBombFarmRoom, "Library / Bomb Farm Room"), - (ChickenHouse, "Chicken House"), - (WitchHut, "Witch Hut"), - (Aginah, "Aginah's Cave"), - (Dam, "Dam"), - (MimicCave, "Mimic Cave"), - (CaveOutsideMiseryMire, "Cave Outside Misery Mire"), - (CaveDarkHyliaLedgeHint, "Cave (Dark Hylia Ledge Hint)"), - (ShopDarkWorldShops, "Shop (Dark World)"), - (ShopFireShieldShop, "Shop (Fire Shield Shop)"), - (ArcherGame, "Archery Game"), - (CaveShop, "Cave (Shop)"), - (KingsTomb, "King's Tomb"), - (WaterfallCaveMireHint, "Waterfall Cave / Cave (Mire Hint)"), - (BigFairy, "Big Fairy"), - (FatFairy, "Fat Fairy"), - (SpikeCave, "Spike Cave"), - (ChestGamblingGame, "Chest Gambling Game"), - (BlindsHouse, "Blind's House"), - (Mutant, "Mutant Hut"), - (MirrorCaveGroveAndTomb, "Mirror Caves (South of Grove / Graveyard Ledge)"), - (BombShop, "Bomb Shop"), - (BlindsBasement, "Blind's Basement"), - (HypeCave, "Hype Cave"), - (KakarikoShopLumberjackHouse, "Kakariko Shop / Lumberjacks' House"), - (IceRodCave, "Ice Rod Cave"), - (SmithHouse, "Smiths' House"), - (FortuneTellers, "Fortune Tellers"), - (MiniMoldormCave, "Mini-Moldorm Cave"), - (FiftyRupeeCaveBonkCave, "50 Rupee Cave / Bonk Cave"), - (TwentyRupeeCaveDarkHyliaSpikeHintCave, "20 Rupee Cave / Dark Hylia Spike Hint Cave"), - (CheckerBoardCave, "Checker Board Cave"), - (HammerPegCave, "Hammer Peg Cave"), - ]; - - HashMap::from_iter(names.into_iter()) - }; -} - -pub const NEED_KILLABLE_ROOMS: [SuperTile; 39] = [ - EasternPalaceBigKeyRoom, // For anti-fairy circle around pot switch - PalaceOfDarknessTurtleRoom, - PalaceOfDarknessMimicsMovingWallRoom, - PalaceOfDarknessWarpsSouthMimicRoom, - TurtleRockCrystalRollerRoom, // Greed room has a kill-all section - TurtleRockDoubleHokkuBokkuBigChestRoom, - TurtleRockChainChompsRoom, - SwampPalaceEntranceRoom, - IcePalaceEntranceRoom, // Not normally required to backtrack, except in doors intensity 3 - IcePalaceCompassRoom, - IcePalaceStalfosKnightConveyorHellway, - IcePalacePengatorsRoom, - TowerOfHeraHardhatBeetleRoom, - TowerOfHeraTileRoom, - ThievesTownBigChestRoom, // For trap room north of big chest - ThievesTownJailCellsRoom, // For room with stairs and big lift block - DesertPalacePopos2BeamosHellwayRoom, - DesertPalaceBigKeyChestRoom, - DesertPalaceEastEntranceRoom, - GanonsTowerTorchRoom2, - GanonsTowerGauntlet123, - GanonsTowerMimicsRoom, - GanonsTowerGauntlet45, - GanonsTowerMisc, - GanonsTowerWinderWarpMazeRoom, - GanonsTowerTileTorchPuzzleRoom, - GanonsTowerTorches1Room, - GanonsTowerWizzrobesRooms, - HyruleCastleBoomerangChestRoom, - EasternPalaceStalfosSpawnRoom, - EasternPalacePreArmosKnightsRoom, - AgahnimsTowerCircleOfPots, - AgahnimsTowerDarkBridgeRoom, // Not normally required to go backwards, except in basic/crossed doors, or doing glitch shenanigans. - AgahnimsTowerEntranceRoom, - MiseryMireSlugRoom, - MiseryMireWizzrobesRoom, - CaveCrystalSwitch5ChestsRoom, - MimicCave, - MiniMoldormCave, -]; - -// All the rooms that require special handling. - -/// These rooms need to be locked to GFX ID 28. -pub const FREEZOR_ROOMS: [SuperTile; 5] = [ - IcePalaceEntranceRoom, - IcePalaceHiddenChestBombableFloorRoom, - IcePalaceBlobsWithTetrisBarrier, - IcePalaceBigChestRoom, - IcePalaceBlockPuzzleRoom, -]; - -/// These rooms need to be locked to GFX ID 17. -pub const WATER_ROOMS: [SuperTile; 33] = [ - GanonsTowerEntranceRoom, - GanonsTowerAgahnim2, - SwampPalaceSwimmingTreadmill, - GanonsTowerIceArmos, - GanonsTowerFinalHallway, - SwampPalaceEntranceRoom, - SwampPalacePushBlockPuzzlePreBigKeyRoom, - SwampPalaceBigChestRoom, - SwampPalaceKeyPotRoom, - GanonsTowerTorchRoom2, - SwampPalaceCompassChestRoom, - GanonsTowerMiniHelmasaurConveyorRoom, - GanonsTowerMoldormRoom, - GanonsTowerSpikePitRoom, - GanonsTowerGanonBallZ, - GanonsTowerGauntlet123, - SwampPalaceHiddenChestHiddenDoorRoom, - GanonsTowerMimicsRoom, - GanonsTowerLanmolasRoom, - GanonsTowerGauntlet45, - GanonsTowerMisc, - GanonsTowerEastSideCollapsingBridgeExplodingWallRoom, - GanonsTowerWinderWarpMazeRoom, - GanonsTowerBlockPuzzleSpikeSkipMapChestRoom, - GanonsTowerEastAndWestDownstairsBigChestRoom, - GanonsTowerTileTorchPuzzleRoom, - GanonsTowerFinalCollapsingBridgeRoom, - GanonsTowerTorches1Room, - GanonsTowerManySpikesWarpMazeRoom, - GanonsTowerInvisibleFloorMazeRoom, - GanonsTowerCompassChestInvisibleFloorRoom, - GanonsTowerWizzrobesRooms, - GanonsTowerMoldormFallRoom, -]; - -pub const SHADOW_ROOMS: [SuperTile; 2] = [ - IcePalaceStalfosKnightConveyorHellway, - IcePalaceIceFloorPotsKeyAndSwitch, -]; - -pub const WALL_MASTER_ROOMS: [SuperTile; 5] = [ - SkullWoodsGibdoKeyMothulaHoleRoom, - SkullWoodsGibdoTorchPuzzleRoom, - SkullWoodsKeyPotTrapRoom, - SkullWoodsKeyChestTrapRoom, - GanonsTowerTileTorchPuzzleRoom, -]; - -pub const BUMPER_CRYSTAL_LASER_ROOMS: [SuperTile; 42] = [ - TurtleRockCrystalRollerRoom, - PalaceOfDarknessTurtleRoom, - TurtleRockHokkuBokkuKeyRoom2, - TowerOfHeraMoldormFallRoom, - PalaceOfDarknessMimicsMovingWallRoom, - IcePalaceBombFloorBariRoom, - PalaceOfDarknessBigHubRoom, - PalaceOfDarknessMapChestFairyRoom, - TowerOfHeraHardhatBeetleRoom, - GanonsTowerTorchRoom2, - AgahnimsTowerFinalBridgeRoom, - ThievesTownBigChestRoom, - PalaceOfDarknessEntranceRoom, - GanonsTowerMiniHelmasaurConveyorRoom, - SkullWoodsBigChestRoom, - SkullWoodsFinalSectionEntranceRoom, - GanonsTowerSpikePitRoom, - SkullWoodsCompassChestRoom, - SkullWoodsKeyChestTrapRoom, - GanonsTowerMimicsRoom, - TowerOfHeraEntranceRoom, - IcePalaceHiddenChestBombableFloorRoom, - TowerOfHeraTileRoom, - GanonsTowerBlockPuzzleSpikeSkipMapChestRoom, - MiseryMireFinalSwitchRoom, - MiseryMireDarkBombWallSwitchesRoom, - GanonsTowerTorches1Room, - GanonsTowerManySpikesWarpMazeRoom, - GanonsTowerCompassChestInvisibleFloorRoom, - MiseryMireFishRoom, - GanonsTowerWizzrobesRooms, - ThievesTownMovingSpikesKeyPotRoom, - TurtleRockChainChompsRoom, - IcePalaceCloneRoomSwitchRoom, - MiseryMireCompassChestTileRoom, - MiseryMireBigChestRoom, - TurtleRockFinalCrystalSwitchPuzzleRoom, - TurtleRockLaserBridge, - TurtleRockLaserKeyRoom, - TurtleRockEntranceRoom, - CaveBumperCaveTop, - CaveCrystalSwitch5ChestsRoom, -]; - -pub const PULL_SWITCH_ROOMS: [SuperTile; 4] = [ - HyruleCastleSwitchRoom, - SkullWoodsBigChestRoom, - ThievesTownWestAtticRoom, - Dam, -]; - -pub const TONGUE_ROOMS: [SuperTile; 7] = [ - TurtleRockCrystalRollerRoom, - TurtleRockWestExitToBalcony, - SwampPalaceBigKeyBsRoom, - SwampPalaceMapChestWaterFillRoom, - IcePalaceMapChestRoom, - SwampPalaceWaterDrainRoom, - IcePalaceHoleToKholdstareRoom, -]; - -pub const NO_STATUE_ROOMS: [SuperTile; 12] = [ - SwampPalaceSwimmingTreadmill, - SwampPalaceStatueRoom, - SwampPalaceEntranceRoom, - PalaceOfDarknessMapChestFairyRoom, - SwampPalacePushBlockPuzzlePreBigKeyRoom, - SwampPalaceBigChestRoom, - SwampPalaceCompassChestRoom, - SkullWoodsBigKeyRoom, - SwampPalaceWaterDrainRoom, - MiseryMireHourglassRoom, - MiseryMireBigHubRoom, - AgahnimsTowerDarkMaze, -]; - -pub const MOVING_CANNONS_ROOMS: [SuperTile; 2] = [ - GanonsTowerGanonBallZ, - DesertPalaceBigKeyChestRoom, -]; - -pub const CANNON_ROOMS: [SuperTile; 3] = [ - EasternPalaceLobbyCannonballsRoom, - EasternPalaceCannonballRoom, - MimicCave, // ??? -]; - -/// These rooms have no enemies to randomize, or are special bosses that can't be switched out. -pub const DONT_RANDOMIZE_ROOMS: [SuperTile; 8] = [ - Ganon, - HyruleCastleNorthCorridor, - HoulihanRoom, - GanonsTowerAgahnim2, - TurtleRockBigKeyRoom, - AgahnimsTowerAgahnim, - AgahnimsTowerMaidenSacrificeChamber, - IcePalaceBigSpikeTrapsRoom, // Anything else would be too easy? -]; - -pub const NO_SPECIAL_ENEMIES_IN_STANDARD_MODE_ROOMS: [SuperTile; 21] = [ - HyruleCastleNorthCorridor, - HyruleCastleSwitchRoom, - HyruleCastleBombableStockRoom, - HyruleCastleKeyRatRoom, - HyruleCastleSewerTextTriggerRoom, - HyruleCastleSewerKeyChestRoom, - HyruleCastleFirstDarkRoom, - HyruleCastle6RopesRoom, - HyruleCastleWestCorridor, - HyruleCastleThroneRoom, - HyruleCastleEastCorridor, - CastleSecretEntranceUncleRoom, - HyruleCastleWestEntranceRoom, - HyruleCastleMainEntranceRoom, - HyruleCastleEastEntranceRoom, - HyruleCastleSmallCorridorToJailCells, - HyruleCastleBoomerangChestRoom, - HyruleCastleMapChestRoom, - HyruleCastleJailCellRoom, - HyruleCastleNextToChasmRoom, - HyruleCastleBasementChasmRoom, -]; - -pub const BOSS_ROOMS: [SuperTile; 16] = [ - Ganon, - SwampPalaceArrghus, - TowerOfHeraMoldorm, - GanonsTowerAgahnim2, - GanonsTowerIceArmos, - AgahnimsTowerAgahnim, - SkullWoodsMothula, - DesertPalaceLanmolas, - GanonsTowerMoldormRoom, - PalaceOfDarknessHelmasaurKing, - GanonsTowerLanmolasRoom, - MiseryMireVitreous, - TurtleRockTrinexx, - ThievesTownBlindTheThief, - EasternPalaceArmosKnights, - IcePalaceKholdstare, -]; - -pub const DONT_USE_IMMOVABLE_ENEMIES: [SuperTile; 57] = [ - PalaceOfDarknessTurtleRoom, // for single terrorpin in L shaped section - SwampPalaceSwimmingTreadmill, - PalaceOfDarknessDarkMaze, // top placement will probably block maze - IcePalaceBombFloorBariRoom, // TODO: test - SwampPalaceStatueRoom, - TowerOfHeraBigChest, // TODO: test, top left placement before stairs up - SwampPalaceBigChestRoom, // bottom left waterbug - IcePalaceMapChestRoom, // spikes block stuff - HyruleCastle6RopesRoom, // only if two of them stack, but why chance it? - AgahnimsTowerFinalBridgeRoom, // spikes are bad here - SwampPalaceCompassChestRoom, - SkullWoodsGibdoTorchPuzzleRoom, - PalaceOfDarknessWarpsSouthMimicRoom, - IcePalaceBombJumpRoom, // for the zols in the small next room - CastleSecretEntranceUncleRoom, // TODO: test - SkullWoodsBigKeyRoom, - IcePalaceHiddenChestSpikeFloorRoom, // TODO: would this cause problems in OHKO since you can't hookshot across if middle mob is beamos, etc? - ThievesTownEastAtticRoom, // only if both bottom rats - PalaceOfDarknessRupeeRoom, // only if two terrorpins next to each other (but can happen twice) - DesertPalaceMapChestRoom, // only if both antlions in hallway to the south of chest - SwampPalaceWaterDrainRoom, // would need 3 mobs to be impassible to possibly softlock... - GanonsTowerWinderWarpMazeRoom, // very low chance - IcePalaceBigSpikeTrapsRoom, // TODO: what happens to beamos over a pit? - DesertPalaceWestEntranceRoom, // TODO: test - DesertPalaceMainEntranceRoom, // TODO: test - DesertPalaceEastEntranceRoom, // TODO: test - GanonsTowerEastAndWestDownstairsBigChestRoom, // TODO: test, probably safe? - GanonsTowerTileTorchPuzzleRoom, // TODO: test - MiseryMireDarkBombWallSwitchesRoom, // TODO: test - GanonsTowerFinalCollapsingBridgeRoom, // TODO: test, probably safe because of conveyor belts - MiseryMireEntranceRoom, - GanonsTowerManySpikesWarpMazeRoom, // TODO: test, middle spike covers warp, are we randomizing those? - GanonsTowerInvisibleFloorMazeRoom, // TODO: test - GanonsTowerCompassChestInvisibleFloorRoom, // TODO: test - IcePalaceBigChestRoom, // Big spikes will block - MiseryMirePreVitreousRoom, // TODO: test - EasternPalaceMapChestRoom, // TODO: test - IcePalaceIceBridgeRoom, - MiseryMireSpikeKeyChestRoom, // TODO: test lower stalfos blocking door - EasternPalaceDarkAntifairyKeyPotRoom, // TODO: test lower stalfos blocking door - ThievesTownHellway, // TODO: test, but should be ok - ThievesTownConveyorToilet, // TODO: test - TurtleRockSomariaHub, // technically a door is blocked off, but who would ever go there? (Door rando would) - ThievesTownNorthWestEntranceRoom, - IcePalaceHoleToKholdstareRoom, // spikes block stuff - AgahnimsTowerDarkMaze, // TODO: test - MiseryMireWizzrobesRoom, - TurtleRockLaserKeyRoom, - EasternPalacePreArmosKnightsRoom, - ThievesTownSouthEastEntranceRoom, // TODO: test - CaveBackwardsDeathMountainTopFloor, - CaveLostOldManHouse, - Cave0xE7, - CaveSpiralCave, - CaveSpectacleRockExit, // TODO: test, probably can get past - Cave0xFD, - MimicCave, -]; - -pub const DONT_USE_FLYING_ENEMIES: [SuperTile; 2] = [ - MiseryMireWizzrobesRoom, - MimicCave, -]; diff --git a/enemize/src/graph/mod.rs b/enemize/src/graph/mod.rs deleted file mode 100644 index eb2d62b..0000000 --- a/enemize/src/graph/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod dungeons; -pub mod item; -pub mod requirement; diff --git a/enemize/src/graph/requirement.rs b/enemize/src/graph/requirement.rs deleted file mode 100644 index 2bbece0..0000000 --- a/enemize/src/graph/requirement.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::collections::HashSet; - -use super::item::Item; - -pub struct Requirement(HashSet); - -impl Requirement { - pub fn new(items: &[Item]) -> Requirement { - Requirement(HashSet::from_iter(items.into_iter().map(|i| *i))) - } - - pub fn is_met(&self, items: &[Item]) -> bool { - let items = HashSet::from_iter(items.into_iter().map(|i| *i)); - self.0.is_subset(&items) - } -} diff --git a/enemize/src/lib.rs b/enemize/src/lib.rs index d130be2..d2ba5b0 100644 --- a/enemize/src/lib.rs +++ b/enemize/src/lib.rs @@ -1,80 +1,41 @@ -use std::fs::File; -use std::marker::PhantomData; +use std::io::Read; use std::path::{Path, PathBuf}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; +use serde::Serialize; -use crate::rom::RomData; - -pub mod asar; -pub mod bosses; -pub mod constants; -pub mod graph; -pub mod option_flags; -pub mod randomize; pub mod rom; -#[derive(Debug, Error)] -#[error("Not a valid value for {0:?}")] -pub struct InvalidEnumError(PhantomData); - -#[derive(Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Serialize)] pub struct Patch { pub address: usize, pub patch_data: Vec, } -#[derive(Default)] +#[derive(Serialize)] pub struct PatchSet { filename: PathBuf, - patches: Vec, + patches: Vec } impl PatchSet { - pub fn load(filename: &Path) -> Result { + pub fn load(filename: Path) -> Result { let patches = { - let file = File::open(filename)?; - let buffer = std::io::BufReader::new(file); + let mut file = File::open(filename)?; + let mut buffer = std::io::BufReader::new(file); serde_json::from_reader(buffer)? }; - Ok(PatchSet { + PatchSet { filename: filename.into(), - patches: patches, - }) + patches: patches + } } - 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); - } - - pub fn add_patches(&mut self, patches: Vec) { - self.patches.extend(patches); - } - - pub fn filename(&self) -> &Path { + 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 patchRom(&self, &mut rom: RomData) { + for patch in self.patches { rom.patch_data(patch); } } diff --git a/enemize/src/main.rs b/enemize/src/main.rs index fcb4f5e..e7a11a9 100644 --- a/enemize/src/main.rs +++ b/enemize/src/main.rs @@ -1,88 +1,3 @@ -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; -use std::time::Instant; - -use anyhow::ensure; -use clap::Parser; - -use enemize::{asar, rom::RomData}; - -const ASAR_SYMBOLS: &'static str = include_str!("../../asar_symbols.txt"); -const ENEMIZER_BASE_PATCH: &'static str = include_str!("../../enemizer_base_patch.json"); - -/// Randomizes enemy placements in The Legend of Zelda: A Link to the Past for the Super Nintendo -/// Entertainment System -#[derive(Debug, Parser)] -#[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 - #[clap(short, long)] - 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<()> { - let args = Args::parse(); - - 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 symbols = load_symbols()?; - - let mut raw_data = vec![]; - let mut rom_file = File::open(args.rom)?; - rom_file.read_to_end(&mut raw_data)?; - - raw_data.resize(2 * 1024 * 1024, 0); - - let mut rom = RomData::new(symbols, raw_data); - ensure!(!rom.is_enemizer(), "It appears that the provided base ROM is already enemized. Please ensure you are using an original game ROM."); - - // Oh noes! The max seed number is twice as likely to show up as any other! - // (That is, 2 out of 2 billion instead of 1.) - let seed = args.seed.unwrap_or_else(|| rand::random()).saturating_abs(); - - rom.randomize(ENEMIZER_BASE_PATCH, options, seed)?; - - let mut out_file = File::create(&args.output)?; - - if args.binary { - out_file.write_all(rom.get_rom_bytes())?; - out_file.flush()?; - println!("Generated SFC file {}", args.output.to_string_lossy()); - } else { - let patches = rom.generate_patch(); - serde_json::to_writer(out_file, &patches)?; - } - - println!("Seed generated in: {}ms", stopwatch.elapsed().as_millis()); - - Ok(()) -} - -fn load_symbols() -> anyhow::Result { - asar::load_symbols(ASAR_SYMBOLS) +fn main() { + println!("Hello, world!"); } diff --git a/enemize/src/option_flags.rs b/enemize/src/option_flags.rs deleted file mode 100644 index 0f683e3..0000000 --- a/enemize/src/option_flags.rs +++ /dev/null @@ -1,774 +0,0 @@ -use std::collections::HashMap; -use std::fmt; -use std::marker::PhantomData; -use std::path::PathBuf; - -use serde::{Deserialize, Serialize}; - -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, - pub tower_of_hera: String, - pub agahnims_tower: String, - pub palace_of_darkness: String, - pub swamp_palace: String, - pub skull_woods: String, - pub thieves_town: String, - pub ice_palace: String, - pub misery_mire: String, - pub turtle_rock: String, - pub ganons_tower1: String, - pub ganons_tower2: String, - pub ganons_tower3: String, - pub ganons_tower4: String, - pub ganon: String, -} - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(into = "u8", try_from = "u8")] -pub enum RandomizeEnemiesType { - Basic, - Normal, - Hard, - Chaos, - Insanity, -} - -impl From for u8 { - fn from(t: RandomizeEnemiesType) -> u8 { - use RandomizeEnemiesType::*; - - match t { - Basic => 0, - Normal => 1, - Hard => 2, - Chaos => 3, - Insanity => 4, - } - } -} - -impl TryFrom for RandomizeEnemiesType { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Basic), - 1 => Ok(Self::Normal), - 2 => Ok(Self::Hard), - 3 => Ok(Self::Chaos), - 4 => Ok(Self::Insanity), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(into = "u8", try_from = "u8")] -pub enum RandomizeEnemyHpType { - Easy, - Medium, - Hard, - Patty, -} - -impl From for u8 { - fn from(t: RandomizeEnemyHpType) -> u8 { - use RandomizeEnemyHpType::*; - - match t { - Easy => 0, - Medium => 1, - Hard => 2, - Patty => 3, - } - } -} - -impl TryFrom for RandomizeEnemyHpType { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Easy), - 1 => Ok(Self::Medium), - 2 => Ok(Self::Hard), - 3 => Ok(Self::Patty), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(into = "u8", try_from = "u8")] -pub enum RandomizeBossesType { - Basic, - Normal, - Chaos, -} - -impl From for u8 { - fn from(t: RandomizeBossesType) -> u8 { - use RandomizeBossesType::*; - - match t { - Basic => 0, - Normal => 1, - Chaos => 2, - } - } -} - -impl TryFrom for RandomizeBossesType { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Basic), - 1 => Ok(Self::Normal), - 2 => Ok(Self::Chaos), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(into = "u8", try_from = "u8")] -pub enum SwordType { - Normal, -} - -impl From for u8 { - fn from(_t: SwordType) -> u8 { - 0 - } -} - -impl TryFrom for SwordType { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Normal), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(into = "u8", try_from = "u8")] -pub enum ShieldType { - Normal, -} - -impl From for u8 { - fn from(_t: ShieldType) -> u8 { - 0 - } -} - -impl TryFrom for ShieldType { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Normal), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} - -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -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, - BigKey, -} - -impl From for u8 { - fn from(t: AbsorbableType) -> u8 { - use AbsorbableType::*; - - match t { - Heart => 0, - GreenRupee => 1, - BlueRupee => 2, - RedRupee => 3, - Bomb1 => 4, - Bomb4 => 5, - Bomb8 => 6, - SmallMagic => 7, - FullMagic => 8, - Arrow5 => 9, - Arrow10 => 10, - Fairy => 11, - Key => 12, - BigKey => 13, - } - } -} - -impl TryFrom for AbsorbableType { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Heart), - 1 => Ok(Self::GreenRupee), - 2 => Ok(Self::BlueRupee), - 3 => Ok(Self::RedRupee), - 4 => Ok(Self::Bomb1), - 5 => Ok(Self::Bomb4), - 6 => Ok(Self::Bomb8), - 7 => Ok(Self::SmallMagic), - 8 => Ok(Self::FullMagic), - 9 => Ok(Self::Arrow5), - 10 => Ok(Self::Arrow10), - 11 => Ok(Self::Fairy), - 12 => Ok(Self::Key), - 13 => Ok(Self::BigKey), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} - -impl fmt::Display for AbsorbableType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use AbsorbableType::*; - - let description = match self { - Heart => "Heart", - GreenRupee => "Green Rupee", - BlueRupee => "Blue Rupee", - RedRupee => "Red Rupee", - Bomb1 => "Bomb (1)", - Bomb4 => "Bomb (4)", - Bomb8 => "Bomb (8)", - SmallMagic => "Small Magic", - FullMagic => "Full Magic", - Arrow5 => "Arrow (5)", - Arrow10 => "Arrow (10)", - Fairy => "Fairy", - Key => "Key", - BigKey => "Big Key", - }; - - write!(f, "{}", description) - } -} - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(into = "u8", try_from = "u8")] -pub enum HeartBeepSpeed { - Normal, - Half, - Quarter, - Off, -} - -impl Default for HeartBeepSpeed { - fn default() -> Self { - HeartBeepSpeed::Normal - } -} - -impl From for u8 { - fn from(speed: HeartBeepSpeed) -> u8 { - use HeartBeepSpeed::*; - - match speed { - Normal => 0, - Half => 1, - Quarter => 2, - Off => 3, - } - } -} - -impl TryFrom for HeartBeepSpeed { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Normal), - 1 => Ok(Self::Half), - 2 => Ok(Self::Quarter), - 3 => Ok(Self::Off), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} - -#[derive(Clone, Copy, Debug, Deserialize, Serialize)] -#[serde(into = "u8", try_from = "u8")] -pub enum BeeLevel { - Level1, - Level2, - Level3, - Level4, -} - -impl fmt::Display for BeeLevel { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use BeeLevel::*; - - let description = match self { - Level1 => "Bees??", - Level2 => "Bees!", - Level3 => "Beeeeees!?", - Level4 => "Beeeeeeeeeeeeeeeeeeeees", - }; - - write!(f, "{}", description) - } -} - -impl From for u8 { - fn from(level: BeeLevel) -> u8 { - use BeeLevel::*; - - match level { - Level1 => 0, - Level2 => 1, - Level3 => 2, - Level4 => 3, - } - } -} - -impl TryFrom for BeeLevel { - type Error = InvalidEnumError; - - fn try_from(byte: u8) -> Result { - match byte { - 0 => Ok(Self::Level1), - 1 => Ok(Self::Level2), - 2 => Ok(Self::Level3), - 3 => Ok(Self::Level4), - _ => Err(InvalidEnumError(PhantomData)), - } - } -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(default, rename_all = "PascalCase")] -pub struct OptionFlags { - pub randomize_enemies: bool, - pub randomize_enemies_type: RandomizeEnemiesType, - pub randomize_bush_enemy_chance: bool, - - pub randomize_enemy_health_range: bool, - pub randomize_enemy_health_type: RandomizeEnemyHpType, - - pub randomize_enemy_damage: bool, - pub allow_enemy_zero_damage: bool, - pub shuffle_enemy_damage_groups: bool, - pub enemy_damage_chaos_mode: bool, - - //pub easy_mode_escape: bool, - pub enemies_absorbable: bool, - pub absorbable_spawn_rate: u8, - pub absorbable_types: HashMap, - - //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_damage: bool, - //pub randomize_boss_damage_min_amount: u8, - //pub randomize_boss_damage_max_amount: u8, - - //pub randomize_boss_behavior: bool, - pub randomize_dungeon_palettes: bool, - pub set_blackout_mode: bool, - - pub randomize_overworld_palettes: bool, - - pub randomize_sprite_palettes: bool, - pub set_advanced_sprite_palettes: bool, - pub puke_mode: bool, - pub negative_mode: bool, - pub grayscale_mode: bool, - - pub generate_spoilers: bool, - pub randomize_link_sprite_palette: bool, - pub randomize_pots: bool, - pub shuffle_music: bool, - pub bootleg_magic: bool, - pub debug_mode: bool, - //pub custom_bosses: bool, - pub heart_beep_speed: HeartBeepSpeed, - pub alternate_gfx: bool, - pub shield_graphics: PathBuf, - pub sword_graphics: PathBuf, - pub bee_mizer: bool, - pub bees_level: BeeLevel, - pub debug_force_enemy: bool, - pub debug_force_enemy_id: u8, - pub debug_force_boss: bool, - pub debug_force_boss_id: BossType, - pub debug_open_shutter_doors: bool, - pub debug_force_enemy_damage_zero: bool, - pub debug_show_room_id_in_rupee_counter: bool, - pub o_h_k_o: bool, - pub randomize_tile_trap_pattern: bool, - pub randomize_tile_trap_floor_tile: bool, - pub allow_killable_thief: bool, - pub randomize_sprite_on_hit: bool, - pub hero_mode: bool, - pub increase_brightness: bool, - pub mute_music_enable_msu_1: bool, - pub agahnim_bounce_balls: bool, - - pub use_manual_bosses: bool, - pub manual_bosses: ManualBosses, -} - -impl OptionFlags { - pub fn try_from_bytes(bytes: &[u8]) -> anyhow::Result { - let mut absorbable_types = HashMap::new(); - absorbable_types.insert(AbsorbableType::Heart, bytes[10] != 0); - absorbable_types.insert(AbsorbableType::GreenRupee, bytes[11] != 0); - absorbable_types.insert(AbsorbableType::BlueRupee, bytes[12] != 0); - absorbable_types.insert(AbsorbableType::RedRupee, bytes[13] != 0); - absorbable_types.insert(AbsorbableType::Bomb1, bytes[14] != 0); - absorbable_types.insert(AbsorbableType::Bomb4, bytes[15] != 0); - absorbable_types.insert(AbsorbableType::Bomb8, bytes[16] != 0); - absorbable_types.insert(AbsorbableType::SmallMagic, bytes[17] != 0); - absorbable_types.insert(AbsorbableType::FullMagic, bytes[18] != 0); - absorbable_types.insert(AbsorbableType::Arrow5, bytes[19] != 0); - absorbable_types.insert(AbsorbableType::Arrow10, bytes[20] != 0); - absorbable_types.insert(AbsorbableType::Fairy, bytes[21] != 0); - absorbable_types.insert(AbsorbableType::Key, bytes[21] != 1); - absorbable_types.insert(AbsorbableType::BigKey, bytes[23] != 0); - - Ok(OptionFlags { - randomize_enemies: bytes[0] != 0, - randomize_enemies_type: bytes[1].try_into()?, - randomize_bush_enemy_chance: bytes[2] != 0, - randomize_enemy_health_range: bytes[3] != 0, - 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, - enemies_absorbable: bytes[8] != 0, - absorbable_spawn_rate: bytes[9], - absorbable_types, - //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_dungeon_palettes: bytes[34] != 0, - set_blackout_mode: bytes[35] != 0, - randomize_overworld_palettes: bytes[36] != 0, - randomize_sprite_palettes: bytes[37] != 0, - set_advanced_sprite_palettes: bytes[38] != 0, - puke_mode: bytes[39] != 0, - negative_mode: bytes[40] != 0, - grayscale_mode: bytes[41] != 0, - generate_spoilers: bytes[42] != 0, - randomize_link_sprite_palette: bytes[43] != 0, - randomize_pots: bytes[44] != 0, - shuffle_music: bytes[45] != 0, - bootleg_magic: bytes[46] != 0, - debug_mode: bytes[47] != 0, - //custom_bosses: bytes[48] != 0, - heart_beep_speed: bytes[49].try_into()?, - alternate_gfx: bytes[50] != 0, - // Skip byte 51 (shield_graphics) - shuffle_enemy_damage_groups: bytes[52] != 0, - enemy_damage_chaos_mode: bytes[53] != 0, - // Skip byte 54 (sword_graphics) - bee_mizer: bytes[55] != 0, - bees_level: bytes[56].try_into()?, - debug_force_enemy: bytes[57] != 0, - debug_force_enemy_id: bytes[58], - debug_force_boss: bytes[59] != 0, - debug_force_boss_id: bytes[60].try_into()?, - debug_open_shutter_doors: bytes[61] != 0, - debug_force_enemy_damage_zero: bytes[62] != 0, - debug_show_room_id_in_rupee_counter: bytes[63] != 0, - o_h_k_o: bytes[64] != 0, - randomize_tile_trap_pattern: bytes[65] != 0, - randomize_tile_trap_floor_tile: bytes[66] != 0, - allow_killable_thief: bytes[67] != 0, - randomize_sprite_on_hit: bytes[68] != 0, - hero_mode: bytes[69] != 0, - increase_brightness: bytes[70] != 0, - mute_music_enable_msu_1: bytes[71] != 0, - agahnim_bounce_balls: bytes[72] != 0, - ..Default::default() - }) - } - - pub fn into_bytes(self) -> Vec { - let mut bytes = Vec::with_capacity(crate::rom::ENEMIZER_INFO_FLAGS_LENGTH); - - bytes.push(self.randomize_enemies as u8); - bytes.push(self.randomize_enemies_type as u8); - bytes.push(self.randomize_bush_enemy_chance as u8); - bytes.push(self.randomize_enemy_health_range as u8); - 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.enemies_absorbable as u8); - bytes.push(self.absorbable_spawn_rate); - - bytes.push( - self.absorbable_types - .get(&AbsorbableType::Heart) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::GreenRupee) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::BlueRupee) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::RedRupee) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::Bomb1) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::Bomb4) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::Bomb8) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::SmallMagic) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::FullMagic) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::Arrow5) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::Arrow10) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::Fairy) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::Key) - .copied() - .unwrap_or(false) as u8, - ); - bytes.push( - self.absorbable_types - .get(&AbsorbableType::BigKey) - .copied() - .unwrap_or(false) as u8, - ); - - bytes.push(0); //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_dungeon_palettes as u8); - bytes.push(self.set_blackout_mode as u8); - bytes.push(self.randomize_overworld_palettes as u8); - bytes.push(self.randomize_sprite_palettes as u8); - bytes.push(self.set_advanced_sprite_palettes as u8); - bytes.push(self.puke_mode as u8); - bytes.push(self.negative_mode as u8); - bytes.push(self.grayscale_mode as u8); - bytes.push(self.generate_spoilers as u8); - bytes.push(self.randomize_link_sprite_palette as u8); - bytes.push(self.randomize_pots as u8); - 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.heart_beep_speed as u8); - bytes.push(self.alternate_gfx as u8); - bytes.push(0); // self.shield_graphics - bytes.push(self.shuffle_enemy_damage_groups as u8); - bytes.push(self.enemy_damage_chaos_mode as u8); - bytes.push(0); // self.sword_graphics - bytes.push(self.bee_mizer as u8); - bytes.push(self.bees_level as u8); - - bytes.push(self.debug_force_enemy as u8); - bytes.push(self.debug_force_enemy_id as u8); - bytes.push(self.debug_force_boss as u8); - bytes.push(self.debug_force_boss_id as u8); - bytes.push(self.debug_open_shutter_doors as u8); - bytes.push(self.debug_force_enemy_damage_zero as u8); - bytes.push(self.debug_show_room_id_in_rupee_counter as u8); - bytes.push(self.o_h_k_o as u8); - bytes.push(self.randomize_tile_trap_pattern as u8); - bytes.push(self.randomize_tile_trap_floor_tile as u8); - bytes.push(self.allow_killable_thief as u8); - bytes.push(self.randomize_sprite_on_hit as u8); - bytes.push(self.hero_mode as u8); - bytes.push(self.increase_brightness as u8); - bytes.push(self.mute_music_enable_msu_1 as u8); - bytes.push(self.agahnim_bounce_balls as u8); - - bytes - } -} - -impl Default for OptionFlags { - fn default() -> Self { - Self { - randomize_enemies: true, - randomize_enemies_type: RandomizeEnemiesType::Chaos, - randomize_bush_enemy_chance: true, - randomize_enemy_health_range: false, - randomize_enemy_health_type: RandomizeEnemyHpType::Medium, - randomize_enemy_damage: false, - allow_enemy_zero_damage: false, - shuffle_enemy_damage_groups: false, - enemy_damage_chaos_mode: false, - //easy_mode_escape: false, - enemies_absorbable: false, - absorbable_spawn_rate: 0, - absorbable_types: HashMap::new(), - //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_dungeon_palettes: true, - set_blackout_mode: false, - randomize_overworld_palettes: true, - randomize_sprite_palettes: true, - set_advanced_sprite_palettes: false, - puke_mode: false, - negative_mode: false, - grayscale_mode: false, - generate_spoilers: true, - randomize_link_sprite_palette: false, - randomize_pots: true, - shuffle_music: false, - bootleg_magic: false, - debug_mode: false, - //custom_bosses: false, - heart_beep_speed: HeartBeepSpeed::Half, - alternate_gfx: false, - shield_graphics: ["shield_gfx", "normal.gfx"].iter().collect(), - sword_graphics: ["sword_gfx", "normal.gfx"].iter().collect(), - bee_mizer: false, - bees_level: BeeLevel::Level1, - debug_force_enemy: false, - debug_force_enemy_id: 0, - debug_force_boss: false, - debug_force_boss_id: BossType::Trinexx, - debug_open_shutter_doors: false, - debug_force_enemy_damage_zero: false, - debug_show_room_id_in_rupee_counter: false, - o_h_k_o: false, - randomize_tile_trap_pattern: false, - randomize_tile_trap_floor_tile: false, - allow_killable_thief: false, - randomize_sprite_on_hit: false, - hero_mode: false, - increase_brightness: false, - mute_music_enable_msu_1: false, - agahnim_bounce_balls: false, - use_manual_bosses: false, - manual_bosses: Default::default(), - } - } -} - -#[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/randomize.rs b/enemize/src/randomize.rs deleted file mode 100644 index a42438e..0000000 --- a/enemize/src/randomize.rs +++ /dev/null @@ -1,38 +0,0 @@ -use anyhow::ensure; - -use crate::option_flags::OptionFlags; -use crate::rom::RomData; -use crate::{Patch, PatchSet}; - -impl RomData { - pub fn randomize( - &mut self, - base_patch: &str, - option_flags: OptionFlags, - seed: i32, - ) -> anyhow::Result<()> { - ensure!( - self.is_randomizer(), - "Enemizer only supports randomizer ROMs for input." - ); - ensure!(!self.is_race(), "Enemizer does not support race roms."); - - if self.is_enemizer() { - // Reuse seed in ROM, not the one given. - self.reset_enemizer(); - } else { - self.set_enemizer_seed(seed); - } - - self.expand_rom(); - self.set_info_flags(option_flags)?; - - let patches: Vec = serde_json::from_str(base_patch)?; - - let mut patch_set = PatchSet::default(); - patch_set.add_patches(patches); - patch_set.patch_rom(self); - - Ok(()) - } -} diff --git a/enemize/src/rom.rs b/enemize/src/rom.rs index b4e8700..11a9874 100644 --- a/enemize/src/rom.rs +++ b/enemize/src/rom.rs @@ -1,307 +1,7 @@ -use std::collections::BTreeMap; -use std::iter; -use std::ops::{Index, IndexMut, Range}; +const ENEMIZER_INFO_SEED_OFFSET: usize = 0; +const ENEMIZER_INFO_SEED_LENGTH: usize = 12; +const ENEMIZER_INFO_VERSION_OFFSET: usize = ENEMIZER_INFO_SEED_OFFSET + ENEMIZER_INFO_SEED_LENGTH; +const ENEMIZER_INFO_VERSION_LENGTH: usize = 8; +const ENEMIZER_INFO_FLAGS_OFFSET: usize = ENEMIZER_INFO_VERSION_OFFSET + ENEMIZER_INFO_VERSION_LENGTH; +const ENEMIZER_INFO_FLAGS_LENGTH: usize = 0x50; -use anyhow::bail; - -use crate::asar::{pc_to_snes_address, snes_to_pc_address, Symbols}; -use crate::constants::*; -use crate::option_flags::OptionFlags; -use crate::Patch; - -pub const ENEMIZER_INFO_SEED_OFFSET: usize = 0; -pub const ENEMIZER_INFO_SEED_LENGTH: usize = 12; -pub const ENEMIZER_INFO_VERSION_OFFSET: usize = - ENEMIZER_INFO_SEED_OFFSET + ENEMIZER_INFO_SEED_LENGTH; -pub const ENEMIZER_INFO_VERSION_LENGTH: usize = 8; -pub const ENEMIZER_INFO_FLAGS_OFFSET: usize = - ENEMIZER_INFO_VERSION_OFFSET + ENEMIZER_INFO_VERSION_LENGTH; -pub const ENEMIZER_INFO_FLAGS_LENGTH: usize = 0x50; - -pub const RANDOMIZER_HIDDEN_ENEMIES_FLAG: usize = 0x00; -pub const CLOSE_BLIND_DOOR_FLAG: usize = 0x01; -pub const MOLDORM_EYES_FLAG: usize = 0x02; -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 RANDOMIZER_MODE_FLAG: usize = 0x180032; - -pub struct RomData { - asar_symbols: Symbols, - spoiler: String, - pub(crate) rom_data: Vec, - patch_data: BTreeMap, - pub(crate) seed: i32, -} - -impl RomData { - pub fn spoiler(&self) -> &str { - self.spoiler.as_str() - } - - pub fn reset_enemizer(&mut self) -> i32 { - use crate::constants::*; - - let seed = self.get_enemizer_seed(); - - self.rom_data[ROOM_HEADER_BANK_LOCATION] = 0x04; - - let dungeon_header_range = - 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); - self.rom_data[room_range].copy_from_slice(&ORIGINAL_ROOM_BLOCKS); - - let ow_gfx_range = OVERWORLD_AREA_GRAPHICS_BLOCK..(OVERWORLD_AREA_GRAPHICS_BLOCK + 272); - self.rom_data[ow_gfx_range].copy_from_slice(&ORIGINAL_OVERWORLD_BLOCKS); - - seed - } - - pub fn generate_patch(&self) -> Vec { - let mut patches = vec![]; - - for (&addr, &byte) in self.patch_data.iter() { - match patches - .last_mut() - .filter(|p: &&mut Patch| p.address + 1 == addr) - { - None => patches.push(Patch { - address: addr, - patch_data: vec![byte], - }), - Some(patch) => patch.patch_data.push(byte), - } - } - - patches - } - - pub fn get_rom_bytes(&self) -> &[u8] { - &self.rom_data - } - - fn set_rom_bytes(&mut self, bytes: &[u8], range: Range) { - self.rom_data.splice(range, bytes.into_iter().map(|&b| b)); - } - - fn set_patch_bytes(&mut self, range: Range) { - let slice = &self.rom_data[range.clone()]; - self.patch_data - .extend(iter::zip(range, slice.into_iter().map(|&b| b))); - } - - 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' - } - - pub fn is_randomizer(&self) -> bool { - let acceptable = [ - b"VT", // item rando - b"ER", // entrance rando - b"DR", // door rando - b"BM", // Berserker's multiworld - b"BD", // Berserker's multiworld doors - b"AP", // Archipelago - b"AD", // Archipelago with door rando - ]; - - acceptable - .iter() - .any(|abbr| &abbr[..] == &self.rom_data[0x7fc0..0x7fc2]) - || (self.rom_data.len() >= 0x20_0000 - && &self.rom_data[0x7fc0..0x7fcf] == 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") - } - - fn assert_rom_length(&self) { - assert!( - self.rom_data.len() >= ENEMIZER_FILE_LENGTH, - "You need to expand the rom before you can use Enemizer features." - ); - } - - pub fn get_enemizer_seed(&self) -> i32 { - self.seed - } - - 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_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..]; - - self.seed = i32::from_str_radix(seed_str, 10)?; - } - - Ok(self.seed) - } - - pub fn set_enemizer_seed(&mut self, seed: i32) { - self.assert_rom_length(); - - 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_end = seed_start + ENEMIZER_INFO_SEED_LENGTH; - self.seed = seed; - - self.set_rom_bytes(&bytes, seed_start..seed_end); - self.set_patch_bytes(seed_start..seed_end); - } - - 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.set_enemizer_version("6.0.32".to_owned()); - } - - 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_end = version_start + ENEMIZER_INFO_VERSION_LENGTH; - Ok(std::str::from_utf8( - &self.rom_data[version_start..version_end], - )?) - } else { - bail!("Not Enemizer Rom") - } - } - - pub fn set_enemizer_version(&mut self, version: String) { - self.assert_rom_length(); - - 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_end = version_start + ENEMIZER_INFO_VERSION_LENGTH; - self.set_rom_bytes(&bytes, version_start..version_end); - self.set_patch_bytes(version_start..version_end) - } - - pub fn set_info_flags(&mut self, flags: OptionFlags) -> anyhow::Result<()> { - let bytes = flags.into_bytes(); - if bytes.len() > 0x100 - ENEMIZER_INFO_FLAGS_OFFSET { - 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_end = flags_start + bytes.len(); - self.set_rom_bytes(&bytes, flags_start..flags_end); - self.set_patch_bytes(flags_start..flags_end); - - Ok(()) - } - - 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_end = flags_start + ENEMIZER_INFO_FLAGS_LENGTH; - OptionFlags::try_from_bytes(&self.rom_data[flags_start..flags_end]).ok() - } else { - None - } - } - - 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_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["room_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]); - } - } -} - -impl Index for RomData { - type Output = u8; - - fn index(&self, index: usize) -> &u8 { - &self.rom_data[index] - } -} - -impl IndexMut for RomData { - fn index_mut(&mut self, index: usize) -> &mut u8 { - &mut self.rom_data[index] - } -} diff --git a/prepare.sh b/prepare.sh deleted file mode 100644 index d532d49..0000000 --- a/prepare.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -if [[ ! -s base_patch.json || ! -s asar_symbols.txt ]]; then -cargo run -p bin_comp -- assembly/src/main.asm base_patch.json asar_symbols.txt -fi -if [[ ! -s enemizer_base_patch.json ]]; then -cargo run -p base_patch_generator -- "$1" base_patch.json asar_symbols.txt enemizer_base_patch.json -fi -cp enemizer_base_patch.json target/debug/enemizerBasePatch.json