Compare commits
	
		
			No commits in common. "latest" and "cd76b9f2f1583161ecc533f8809cb97a2e909a22" have entirely different histories.
		
	
	
		
			latest
			...
			cd76b9f2f1
		
	
		
					 20 changed files with 28 additions and 3570 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,4 +1 @@
 | 
				
			||||||
/target
 | 
					/target
 | 
				
			||||||
asar_symbols.txt
 | 
					 | 
				
			||||||
base_patch.json
 | 
					 | 
				
			||||||
enemizer_base_patch.json
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										254
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										254
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -8,39 +8,12 @@ version = "1.0.57"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
 | 
					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]]
 | 
					[[package]]
 | 
				
			||||||
name = "bin_comp"
 | 
					name = "bin_comp"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "anyhow",
 | 
					 "anyhow",
 | 
				
			||||||
 "enemize",
 | 
					 "serde",
 | 
				
			||||||
 "serde_json",
 | 
					 "serde_json",
 | 
				
			||||||
 "tempfile",
 | 
					 "tempfile",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -58,55 +31,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
					checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "clap"
 | 
					name = "enemize-rs"
 | 
				
			||||||
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"
 | 
					 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "anyhow",
 | 
					 "md5",
 | 
				
			||||||
 "clap",
 | 
					 | 
				
			||||||
 "lazy_static",
 | 
					 | 
				
			||||||
 "rand",
 | 
					 | 
				
			||||||
 "serde",
 | 
					 | 
				
			||||||
 "serde_json",
 | 
					 | 
				
			||||||
 "thiserror",
 | 
					 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -118,48 +46,6 @@ dependencies = [
 | 
				
			||||||
 "instant",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "instant"
 | 
					name = "instant"
 | 
				
			||||||
version = "0.1.12"
 | 
					version = "0.1.12"
 | 
				
			||||||
| 
						 | 
					@ -175,12 +61,6 @@ version = "1.0.2"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
 | 
					checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "lazy_static"
 | 
					 | 
				
			||||||
version = "1.4.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "libc"
 | 
					name = "libc"
 | 
				
			||||||
version = "0.2.126"
 | 
					version = "0.2.126"
 | 
				
			||||||
| 
						 | 
					@ -193,42 +73,6 @@ version = "0.7.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
 | 
					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]]
 | 
					[[package]]
 | 
				
			||||||
name = "proc-macro2"
 | 
					name = "proc-macro2"
 | 
				
			||||||
version = "1.0.39"
 | 
					version = "1.0.39"
 | 
				
			||||||
| 
						 | 
					@ -247,36 +91,6 @@ dependencies = [
 | 
				
			||||||
 "proc-macro2",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "redox_syscall"
 | 
					name = "redox_syscall"
 | 
				
			||||||
version = "0.2.13"
 | 
					version = "0.2.13"
 | 
				
			||||||
| 
						 | 
					@ -332,12 +146,6 @@ dependencies = [
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "strsim"
 | 
					 | 
				
			||||||
version = "0.10.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "syn"
 | 
					name = "syn"
 | 
				
			||||||
version = "1.0.95"
 | 
					version = "1.0.95"
 | 
				
			||||||
| 
						 | 
					@ -363,59 +171,12 @@ dependencies = [
 | 
				
			||||||
 "winapi",
 | 
					 "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]]
 | 
					[[package]]
 | 
				
			||||||
name = "unicode-ident"
 | 
					name = "unicode-ident"
 | 
				
			||||||
version = "1.0.0"
 | 
					version = "1.0.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
 | 
					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]]
 | 
					[[package]]
 | 
				
			||||||
name = "winapi"
 | 
					name = "winapi"
 | 
				
			||||||
version = "0.3.9"
 | 
					version = "0.3.9"
 | 
				
			||||||
| 
						 | 
					@ -432,15 +193,6 @@ version = "0.4.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 | 
					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]]
 | 
					[[package]]
 | 
				
			||||||
name = "winapi-x86_64-pc-windows-gnu"
 | 
					name = "winapi-x86_64-pc-windows-gnu"
 | 
				
			||||||
version = "0.4.0"
 | 
					version = "0.4.0"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
[workspace]
 | 
					[workspace]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
members = [
 | 
					members = [
 | 
				
			||||||
    "base_patch_generator",
 | 
					 | 
				
			||||||
    "bin_comp",
 | 
					 | 
				
			||||||
    "enemize",
 | 
					    "enemize",
 | 
				
			||||||
 | 
					    "bin_comp",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								LICENSE.txt
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								LICENSE.txt
									
										
									
									
									
								
							| 
						 | 
					@ -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.
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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"
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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(())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,9 @@
 | 
				
			||||||
[package]
 | 
					[package]
 | 
				
			||||||
name = "enemize"
 | 
					name = "enemize-rs"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
edition = "2021"
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
					# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
anyhow = "1"
 | 
					serde = { version = 1.0, features = ["derive"] }
 | 
				
			||||||
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"
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,42 +0,0 @@
 | 
				
			||||||
use std::collections::HashMap;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub type Symbols = HashMap<String, usize>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn load_symbols(contents: &str) -> anyhow::Result<Symbols> {
 | 
					 | 
				
			||||||
    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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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<BossType> 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<u8> for BossType {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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)),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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,
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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<H: Hasher>(&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<ItemId, Item> = {
 | 
					 | 
				
			||||||
        use ItemId::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut items: HashMap<ItemId, Item> = 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, "<Reserved>"));
 | 
					 | 
				
			||||||
        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, "<Reserved>"));
 | 
					 | 
				
			||||||
        items.insert(Reserved0xAF, Item::special(Reserved0xAF, "<Reserved>"));
 | 
					 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
pub mod dungeons;
 | 
					 | 
				
			||||||
pub mod item;
 | 
					 | 
				
			||||||
pub mod requirement;
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,16 +0,0 @@
 | 
				
			||||||
use std::collections::HashSet;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use super::item::Item;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub struct Requirement(HashSet<Item>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,80 +1,41 @@
 | 
				
			||||||
use std::fs::File;
 | 
					use std::io::Read;
 | 
				
			||||||
use std::marker::PhantomData;
 | 
					 | 
				
			||||||
use std::path::{Path, PathBuf};
 | 
					use std::path::{Path, PathBuf};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use serde::{Deserialize, Serialize};
 | 
					use serde::Serialize;
 | 
				
			||||||
use thiserror::Error;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
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;
 | 
					pub mod rom;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Error)]
 | 
					#[derive(Serialize)]
 | 
				
			||||||
#[error("Not a valid value for {0:?}")]
 | 
					 | 
				
			||||||
pub struct InvalidEnumError<T>(PhantomData<T>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Deserialize, Serialize)]
 | 
					 | 
				
			||||||
#[serde(rename_all = "camelCase")]
 | 
					 | 
				
			||||||
pub struct Patch {
 | 
					pub struct Patch {
 | 
				
			||||||
    pub address: usize,
 | 
					    pub address: usize,
 | 
				
			||||||
    pub patch_data: Vec<u8>,
 | 
					    pub patch_data: Vec<u8>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Serialize)]
 | 
				
			||||||
pub struct PatchSet {
 | 
					pub struct PatchSet {
 | 
				
			||||||
    filename: PathBuf,
 | 
					    filename: PathBuf,
 | 
				
			||||||
    patches: Vec<Patch>,
 | 
					    patches: Vec<Patch>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl PatchSet {
 | 
					impl PatchSet {
 | 
				
			||||||
    pub fn load(filename: &Path) -> Result<PatchSet, anyhow::Error> {
 | 
					    pub fn load(filename: Path) -> Result<PatchSet, anyhow::Error> {
 | 
				
			||||||
        let patches = {
 | 
					        let patches = {
 | 
				
			||||||
            let file = File::open(filename)?;
 | 
					            let mut file = File::open(filename)?;
 | 
				
			||||||
            let buffer = std::io::BufReader::new(file);
 | 
					            let mut buffer = std::io::BufReader::new(file);
 | 
				
			||||||
            serde_json::from_reader(buffer)?
 | 
					            serde_json::from_reader(buffer)?
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        Ok(PatchSet {
 | 
					        PatchSet {
 | 
				
			||||||
            filename: filename.into(),
 | 
					            filename: filename.into(),
 | 
				
			||||||
            patches: patches,
 | 
					            patches: patches
 | 
				
			||||||
        })
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn save(&self) -> anyhow::Result<()> {
 | 
					    pub fn filename(&self) -> Path {
 | 
				
			||||||
        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<Patch>) {
 | 
					 | 
				
			||||||
        self.patches.extend(patches);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn filename(&self) -> &Path {
 | 
					 | 
				
			||||||
        self.filename.as_path()
 | 
					        self.filename.as_path()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn patch_rom(&self, rom: &mut RomData) {
 | 
					    pub fn patchRom(&self, &mut rom: RomData) {
 | 
				
			||||||
        for patch in self.patches.iter() {
 | 
					        for patch in self.patches {
 | 
				
			||||||
            rom.patch_data(patch);
 | 
					            rom.patch_data(patch);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,88 +1,3 @@
 | 
				
			||||||
use std::fs::File;
 | 
					fn main() {
 | 
				
			||||||
use std::io::prelude::*;
 | 
					    println!("Hello, world!");
 | 
				
			||||||
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<i32>,
 | 
					 | 
				
			||||||
    /// 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<PathBuf>,
 | 
					 | 
				
			||||||
    /// path to the randomizerPatch.json (not used)
 | 
					 | 
				
			||||||
    #[clap(long)]
 | 
					 | 
				
			||||||
    randomizer: Option<PathBuf>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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::Symbols> {
 | 
					 | 
				
			||||||
    asar::load_symbols(ASAR_SYMBOLS)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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<RandomizeEnemiesType> for u8 {
 | 
					 | 
				
			||||||
    fn from(t: RandomizeEnemiesType) -> u8 {
 | 
					 | 
				
			||||||
        use RandomizeEnemiesType::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        match t {
 | 
					 | 
				
			||||||
            Basic => 0,
 | 
					 | 
				
			||||||
            Normal => 1,
 | 
					 | 
				
			||||||
            Hard => 2,
 | 
					 | 
				
			||||||
            Chaos => 3,
 | 
					 | 
				
			||||||
            Insanity => 4,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TryFrom<u8> for RandomizeEnemiesType {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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<RandomizeEnemyHpType> for u8 {
 | 
					 | 
				
			||||||
    fn from(t: RandomizeEnemyHpType) -> u8 {
 | 
					 | 
				
			||||||
        use RandomizeEnemyHpType::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        match t {
 | 
					 | 
				
			||||||
            Easy => 0,
 | 
					 | 
				
			||||||
            Medium => 1,
 | 
					 | 
				
			||||||
            Hard => 2,
 | 
					 | 
				
			||||||
            Patty => 3,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TryFrom<u8> for RandomizeEnemyHpType {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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<RandomizeBossesType> for u8 {
 | 
					 | 
				
			||||||
    fn from(t: RandomizeBossesType) -> u8 {
 | 
					 | 
				
			||||||
        use RandomizeBossesType::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        match t {
 | 
					 | 
				
			||||||
            Basic => 0,
 | 
					 | 
				
			||||||
            Normal => 1,
 | 
					 | 
				
			||||||
            Chaos => 2,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TryFrom<u8> for RandomizeBossesType {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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<SwordType> for u8 {
 | 
					 | 
				
			||||||
    fn from(_t: SwordType) -> u8 {
 | 
					 | 
				
			||||||
        0
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TryFrom<u8> for SwordType {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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<ShieldType> for u8 {
 | 
					 | 
				
			||||||
    fn from(_t: ShieldType) -> u8 {
 | 
					 | 
				
			||||||
        0
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TryFrom<u8> for ShieldType {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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<AbsorbableType> 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<u8> for AbsorbableType {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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<HeartBeepSpeed> for u8 {
 | 
					 | 
				
			||||||
    fn from(speed: HeartBeepSpeed) -> u8 {
 | 
					 | 
				
			||||||
        use HeartBeepSpeed::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        match speed {
 | 
					 | 
				
			||||||
            Normal => 0,
 | 
					 | 
				
			||||||
            Half => 1,
 | 
					 | 
				
			||||||
            Quarter => 2,
 | 
					 | 
				
			||||||
            Off => 3,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TryFrom<u8> for HeartBeepSpeed {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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<BeeLevel> for u8 {
 | 
					 | 
				
			||||||
    fn from(level: BeeLevel) -> u8 {
 | 
					 | 
				
			||||||
        use BeeLevel::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        match level {
 | 
					 | 
				
			||||||
            Level1 => 0,
 | 
					 | 
				
			||||||
            Level2 => 1,
 | 
					 | 
				
			||||||
            Level3 => 2,
 | 
					 | 
				
			||||||
            Level4 => 3,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl TryFrom<u8> for BeeLevel {
 | 
					 | 
				
			||||||
    type Error = InvalidEnumError<Self>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn try_from(byte: u8) -> Result<Self, Self::Error> {
 | 
					 | 
				
			||||||
        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<AbsorbableType, bool>,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //pub boss_madness: bool,
 | 
					 | 
				
			||||||
    pub randomize_bosses: bool,
 | 
					 | 
				
			||||||
    pub randomize_bosses_type: RandomizeBossesType,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //pub randomize_boss_health: bool,
 | 
					 | 
				
			||||||
    //pub randomize_boss_health_min_amount: u8,
 | 
					 | 
				
			||||||
    //pub randomize_boss_health_max_amount: u8,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //pub randomize_boss_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<Self> {
 | 
					 | 
				
			||||||
        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<u8> {
 | 
					 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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<Patch> = serde_json::from_str(base_patch)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut patch_set = PatchSet::default();
 | 
					 | 
				
			||||||
        patch_set.add_patches(patches);
 | 
					 | 
				
			||||||
        patch_set.patch_rom(self);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,307 +1,7 @@
 | 
				
			||||||
use std::collections::BTreeMap;
 | 
					const ENEMIZER_INFO_SEED_OFFSET: usize = 0;
 | 
				
			||||||
use std::iter;
 | 
					const ENEMIZER_INFO_SEED_LENGTH: usize = 12;
 | 
				
			||||||
use std::ops::{Index, IndexMut, Range};
 | 
					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<u8>,
 | 
					 | 
				
			||||||
    patch_data: BTreeMap<usize, u8>,
 | 
					 | 
				
			||||||
    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<Patch> {
 | 
					 | 
				
			||||||
        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<usize>) {
 | 
					 | 
				
			||||||
        self.rom_data.splice(range, bytes.into_iter().map(|&b| b));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn set_patch_bytes(&mut self, range: Range<usize>) {
 | 
					 | 
				
			||||||
        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<i32> {
 | 
					 | 
				
			||||||
        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<OptionFlags> {
 | 
					 | 
				
			||||||
        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<u8>) -> 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<usize> for RomData {
 | 
					 | 
				
			||||||
    type Output = u8;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn index(&self, index: usize) -> &u8 {
 | 
					 | 
				
			||||||
        &self.rom_data[index]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl IndexMut<usize> for RomData {
 | 
					 | 
				
			||||||
    fn index_mut(&mut self, index: usize) -> &mut u8 {
 | 
					 | 
				
			||||||
        &mut self.rom_data[index]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue