| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  | import hashlib | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 00:43:39 -07:00
										 |  |  | import json | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | import Utils | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  | from Utils import read_snes_rom | 
					
						
							|  |  |  | from worlds.Files import APDeltaPatch | 
					
						
							| 
									
										
										
										
											2023-04-08 16:52:34 -04:00
										 |  |  | from .variaRandomizer.utils.utils import openFile | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-28 00:43:05 +00:00
										 |  |  | SMJUHASH = '21f3e98df4780ee1c667b84e57d88675' | 
					
						
							| 
									
										
										
										
											2022-10-31 22:42:11 -07:00
										 |  |  | SM_ROM_MAX_PLAYERID = 65535 | 
					
						
							|  |  |  | SM_ROM_PLAYERDATA_COUNT = 202 | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class SMDeltaPatch(APDeltaPatch): | 
					
						
							| 
									
										
										
										
											2022-06-28 00:43:05 +00:00
										 |  |  |     hash = SMJUHASH | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  |     game = "Super Metroid" | 
					
						
							|  |  |  |     patch_file_ending = ".apsm" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def get_source_data(cls) -> bytes: | 
					
						
							|  |  |  |         return get_base_rom_bytes() | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | def get_base_rom_bytes(file_name: str = "") -> bytes: | 
					
						
							|  |  |  |     base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None) | 
					
						
							|  |  |  |     if not base_rom_bytes: | 
					
						
							|  |  |  |         file_name = get_base_rom_path(file_name) | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  |         base_rom_bytes = bytes(read_snes_rom(open(file_name, "rb"))) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         basemd5 = hashlib.md5() | 
					
						
							|  |  |  |         basemd5.update(base_rom_bytes) | 
					
						
							| 
									
										
										
										
											2022-06-28 00:43:05 +00:00
										 |  |  |         if SMJUHASH != basemd5.hexdigest(): | 
					
						
							|  |  |  |             raise Exception('Supplied Base Rom does not match known MD5 for Japan+US release. ' | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                             'Get the correct game and version, then dump it') | 
					
						
							|  |  |  |         get_base_rom_bytes.base_rom_bytes = base_rom_bytes | 
					
						
							|  |  |  |     return base_rom_bytes | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | def get_base_rom_path(file_name: str = "") -> str: | 
					
						
							|  |  |  |     options = Utils.get_options() | 
					
						
							|  |  |  |     if not file_name: | 
					
						
							|  |  |  |         file_name = options["sm_options"]["rom_file"] | 
					
						
							|  |  |  |     if not os.path.exists(file_name): | 
					
						
							| 
									
										
										
										
											2022-03-31 05:08:15 +02:00
										 |  |  |         file_name = Utils.user_path(file_name) | 
					
						
							| 
									
										
										
										
											2021-11-13 09:40:20 -05:00
										 |  |  |     return file_name | 
					
						
							| 
									
										
										
										
											2022-07-26 00:43:39 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | def get_sm_symbols(sym_json_path) -> dict: | 
					
						
							| 
									
										
										
										
											2023-04-08 16:52:34 -04:00
										 |  |  |     with openFile(sym_json_path, "r") as stream: | 
					
						
							| 
									
										
										
										
											2022-07-26 00:43:39 -07:00
										 |  |  |         symbols = json.load(stream) | 
					
						
							|  |  |  |         symboltable = {} | 
					
						
							|  |  |  |         for name, sixdigitaddr in symbols.items(): | 
					
						
							|  |  |  |             (bank, addr_within_bank) = sixdigitaddr.split(":") | 
					
						
							|  |  |  |             bank = int(bank, 16) | 
					
						
							|  |  |  |             addr_within_bank = int(addr_within_bank, 16) | 
					
						
							|  |  |  |             # categorize addresses using snes lorom mapping: | 
					
						
							|  |  |  |             # (reference: https://en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map) | 
					
						
							|  |  |  |             if (bank >= 0x70 and bank <= 0x7d): | 
					
						
							|  |  |  |                 offset_within_rom_file = None | 
					
						
							|  |  |  |                 # SRAM is not continuous, but callers may want it in continuous terms | 
					
						
							|  |  |  |                 # SRAM @ data bank $70-$7D, addr_within_bank $0000-$7FFF | 
					
						
							|  |  |  |                 # | 
					
						
							|  |  |  |                 # symbol aka snes    offestwithincontinuousSRAM | 
					
						
							|  |  |  |                 # ---------------    -------------------------- | 
					
						
							|  |  |  |                 # $70:0000-7FFF   ->  0x0000- 7FFF | 
					
						
							|  |  |  |                 # $71:0000-7FFF   ->  0x8000- FFFF | 
					
						
							|  |  |  |                 # $72:0000-7FFF   -> 0x10000-17FFF | 
					
						
							|  |  |  |                 # etc... | 
					
						
							|  |  |  |                 offset_within_continuous_sram = (bank - 0x70) * 0x8000 + addr_within_bank | 
					
						
							|  |  |  |                 offset_within_wram = None | 
					
						
							|  |  |  |             elif bank == 0x7e or bank == 0x7f or (bank == 0x00 and addr_within_bank <= 0x1fff): | 
					
						
							|  |  |  |                 offset_within_rom_file = None | 
					
						
							|  |  |  |                 offset_within_continuous_sram = None | 
					
						
							|  |  |  |                 offset_within_wram = addr_within_bank | 
					
						
							|  |  |  |                 if bank == 0x7f: | 
					
						
							|  |  |  |                     offset_within_wram += 0x10000 | 
					
						
							|  |  |  |             elif bank >= 0x80: | 
					
						
							|  |  |  |                 offset_within_rom_file = ((bank - 0x80) * 0x8000) + (addr_within_bank % 0x8000) | 
					
						
							|  |  |  |                 offset_within_continuous_sram = None | 
					
						
							|  |  |  |                 offset_within_wram = None | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 offset_within_rom_file = None | 
					
						
							|  |  |  |                 offset_within_continuous_sram = None | 
					
						
							|  |  |  |                 offset_within_wram = None | 
					
						
							|  |  |  |             symboltable[name] = {"bank": bank, | 
					
						
							|  |  |  |                                  "addr_within_bank": addr_within_bank, | 
					
						
							|  |  |  |                                  "offset_within_rom_file": offset_within_rom_file, | 
					
						
							|  |  |  |                                  "offset_within_continuous_sram": offset_within_continuous_sram, | 
					
						
							|  |  |  |                                  "offset_within_wram": offset_within_wram | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |         return symboltable |