From 7e8331508c82f459b5fa3def0f8863ab82ce8a35 Mon Sep 17 00:00:00 2001 From: Dinopony Date: Sat, 22 Mar 2025 16:11:54 +0100 Subject: [PATCH 1/2] Proof of concept of editing save data tables --- Container.py | 16 ++++++++++++++++ data/IsoAddresses.py | 8 ++++++++ data/RamAddresses.py | 11 ++++++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Container.py b/Container.py index f19bd86..b6de5d0 100644 --- a/Container.py +++ b/Container.py @@ -1,4 +1,5 @@ import hashlib +import math import shutil import mmap from typing import Any, Callable, TYPE_CHECKING, Optional @@ -137,6 +138,21 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No for address in addresses.MEMCARD_GAME_NAMES: patch.write_token(APTokenTypes.WRITE, address, generated_game_name.encode()) + # Change what data gets saved on the memory card to enable saving custom stuff + custom_data_length = 4 * math.ceil((ram.custom_data_table_end - ram.custom_data_table) / 4) + for address in addresses.MEMCARD_SAVED_DATA_TABLES: + # Merge two contiguous entries to free an entry for custom data + patch.write_token(APTokenTypes.WRITE, address + 0x94, bytes([0x70])) + # Make the freed entry point on our custom data with the proper length + patch.write_token(APTokenTypes.WRITE, address + 0xA0, ram.custom_data_table.to_bytes(4, 'little')) + patch.write_token(APTokenTypes.WRITE, address + 0xA4, custom_data_length.to_bytes(4, 'little')) + # Change the default save file contained in MISC.WAD to reflect above changes in size + address = addresses.DEFAULT_MEMCARD_SAVE_FILE + global_data_block_size = 0x1CF8 + custom_data_length + patch.write_token(APTokenTypes.WRITE, address, global_data_block_size.to_bytes(4, 'little')) + global_data_block_size -= 8 + patch.write_token(APTokenTypes.WRITE, address + 0x08, global_data_block_size.to_bytes(4, 'little')) + """--------------- Multiple planets ---------------""" diff --git a/data/IsoAddresses.py b/data/IsoAddresses.py index 005f5d7..8818f46 100644 --- a/data/IsoAddresses.py +++ b/data/IsoAddresses.py @@ -201,7 +201,15 @@ class AddressesSCUS97268: 0xB6BF1144, 0xB9803FBC, 0xBAFC7AF0, 0xBF9C6DF8, 0xC3315BFC, 0xC62D316C, 0xC8F428B0, 0xCA5AE1C0, 0xCD97DC9C, 0xD00C1DA8, 0xD240DDF0, 0xD6790700, 0xDAEE2B1C, 0xDCCAB6B8, 0xDE86B62C, 0xDFA214B8, 0xE0682840, 0xE151F054 ] + MEMCARD_GAME_NAMES: list[int] = [0x3010BD, 0x3010D5, 0x3010ED, 0x30110D, 0x30112D, 0x301141, 0x30115D] + MEMCARD_SAVED_DATA_TABLES: list[int] = [ + 0x003AF8B8, 0x949B5C84, + 0x9D292724, 0x9EB1F958, 0xA193E324, 0xA5DDA684, 0xA8EF558C, 0xACA9CBC8, 0xAE398904, 0xB070F218, 0xB2F6AE6C, + 0xB6B82B1C, 0xB978CFF8, 0xBAF57CD4, 0xBF94F7F4, 0xC32A12EC, 0xC625C84C, 0xC8EC991C, 0xCA5416AC, 0xCD908DAC, + 0xD003BF84, 0xD239E950, 0xD6712A84, 0xDAE6DCAC, 0xDCC32E6C, 0xDE7F0B4C, 0xDF9AD70C, 0xE060BE78, 0xE14A9C2C + ] + DEFAULT_MEMCARD_SAVE_FILE: int = 0x948F31F4 ROLL_RANDOM_NUMBER_FUNCS: list[int] = [ 0x00400F60, 0x94A06324, diff --git a/data/RamAddresses.py b/data/RamAddresses.py index 35914d3..548112b 100644 --- a/data/RamAddresses.py +++ b/data/RamAddresses.py @@ -354,11 +354,12 @@ def __init__(self, game_version: str): # As one might expect, this is way too much and most of this data is empty, but still saved on the memcard. # We use the table for Feltzin since spaceship systems don't use that mechanic at all, but still have that # table. - self.feltzin_kill_count_table: int = self.enemy_kill_count_table + (FELTZIN_SYSTEM.number * 0x400) - self.tabora_wrench_cutscene_flag: int = self.feltzin_kill_count_table + 0x1 - self.aranos_wrench_cutscene_flag: int = self.feltzin_kill_count_table + 0x2 - self.custom_text_notification_trigger: int = self.feltzin_kill_count_table + 0x3 - self.bolt_pack_count: int = self.feltzin_kill_count_table + 0x4 + self.custom_data_table: int = self.enemy_kill_count_table + (FELTZIN_SYSTEM.number * 0x400) + self.bolt_pack_count: int = self.custom_data_table + self.tabora_wrench_cutscene_flag: int = self.custom_data_table + 0x1 + self.aranos_wrench_cutscene_flag: int = self.custom_data_table + 0x2 + self.custom_text_notification_trigger: int = self.custom_data_table + 0x3 + self.custom_data_table_end: int = self.custom_data_table + 0x4 class PlanetAddresses(NamedTuple): From edcdf802397b270c0b1a2df3bdc3eba37afceaa4 Mon Sep 17 00:00:00 2001 From: Dinopony Date: Sun, 23 Mar 2025 21:54:02 +0100 Subject: [PATCH 2/2] Fixed custom data being not being saved outside of Feltzin --- Container.py | 75 ++++++++++++++++++++------------------------ data/IsoAddresses.py | 70 ++++++++++++++++++++--------------------- data/Items.py | 52 +++++++++++++++++++----------- data/RamAddresses.py | 34 +++++++++++--------- 4 files changed, 122 insertions(+), 109 deletions(-) diff --git a/Container.py b/Container.py index b6de5d0..8ecfe98 100644 --- a/Container.py +++ b/Container.py @@ -1,5 +1,4 @@ import hashlib -import math import shutil import mmap from typing import Any, Callable, TYPE_CHECKING, Optional @@ -138,21 +137,6 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No for address in addresses.MEMCARD_GAME_NAMES: patch.write_token(APTokenTypes.WRITE, address, generated_game_name.encode()) - # Change what data gets saved on the memory card to enable saving custom stuff - custom_data_length = 4 * math.ceil((ram.custom_data_table_end - ram.custom_data_table) / 4) - for address in addresses.MEMCARD_SAVED_DATA_TABLES: - # Merge two contiguous entries to free an entry for custom data - patch.write_token(APTokenTypes.WRITE, address + 0x94, bytes([0x70])) - # Make the freed entry point on our custom data with the proper length - patch.write_token(APTokenTypes.WRITE, address + 0xA0, ram.custom_data_table.to_bytes(4, 'little')) - patch.write_token(APTokenTypes.WRITE, address + 0xA4, custom_data_length.to_bytes(4, 'little')) - # Change the default save file contained in MISC.WAD to reflect above changes in size - address = addresses.DEFAULT_MEMCARD_SAVE_FILE - global_data_block_size = 0x1CF8 + custom_data_length - patch.write_token(APTokenTypes.WRITE, address, global_data_block_size.to_bytes(4, 'little')) - global_data_block_size -= 8 - patch.write_token(APTokenTypes.WRITE, address + 0x08, global_data_block_size.to_bytes(4, 'little')) - """--------------- Multiple planets ---------------""" @@ -264,9 +248,13 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No current platinum bolt count. This changes it to read a single byte that we control to get that count instead. This is done to decouple the platinum bolt count from platinum bolt locations checked. This same concept is also applied to nanotech boosts below. """ + upper_half, lower_half = MIPS.get_address_halves(ram.platinum_bolt_count) for address in addresses.PLAT_BOLT_COUNT_FUNCS: - patch.write_token(APTokenTypes.WRITE, address + 0x4, bytes([0x13, 0x00, 0x00, 0x10])) - patch.write_token(APTokenTypes.WRITE, address + 0x8, bytes([0xE4, 0xB2, 0x46, 0x90])) + patch.write_token(APTokenTypes.WRITE, address, bytes([ + *upper_half, 0x02, 0x3C, # lui v0, + 0x13, 0x00, 0x00, 0x10, # b (+0x13) + *lower_half, 0x46, 0x90 # _lbu a2,(v0) + ])) # For some reason, the "Weapons" menu sets the secondary inventory flag for any weapon you hover with your cursor. # This is a problem for us since secondary inventory is tied to locations, so we just disable that behavior. @@ -274,6 +262,7 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No patch.write_token(APTokenTypes.WRITE, address + 0x408, MIPS.nop()) # Same for nanotech boosts + upper_half, lower_half = MIPS.get_address_halves(ram.nanotech_boost_count) for address, spaceish_wars_address in zip(addresses.NANOTECH_COUNT_FUNCS, addresses.SPACEISH_WARS_FUNCS): # Inject a custom procedure run on each tick of the main loop of each planet. # It will be called through the NANOTECH_COUNT_FUNC since we are removing a few instructions there, @@ -281,13 +270,16 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No planet = ram.planet[IsoAddresses.get_planet_id_from_iso_address(address)] patch.write_token(APTokenTypes.WRITE, spaceish_wars_address, custom_main_loop(ram, planet)) - patch.write_token(APTokenTypes.WRITE, address + 0x70, bytes([0xE5, 0xB2, 0xA5, 0x90])) # lbu a1,-0x4D1B(a1) - patch.write_token(APTokenTypes.WRITE, address + 0x74, bytes([0x00, 0x00, 0xA4, 0x8F])) # lw a0,0x0(sp) - patch.write_token(APTokenTypes.WRITE, address + 0x78, bytes([0x21, 0x10, 0x85, 0x00])) # addu v0,a0,a1 - patch.write_token(APTokenTypes.WRITE, address + 0x7C, MIPS.jal(planet.spaceish_wars_func)) - patch.write_token(APTokenTypes.WRITE, address + 0x80, bytes([0x00, 0x00, 0xA2, 0xAF])) # sw v0,0x0(sp) - patch.write_token(APTokenTypes.WRITE, address + 0x84, bytes([0x07, 0x00, 0x00, 0x10])) # beq zero,zero,0x7 - patch.write_token(APTokenTypes.WRITE, address + 0x88, MIPS.nop()) + patch.write_token(APTokenTypes.WRITE, address + 0x6C, bytes([ + *upper_half, 0x05, 0x3C, # lui a1, + *lower_half, 0xA5, 0x90, # lbu a1,(a1) + 0x00, 0x00, 0xA4, 0x8F, # lw a0,0x0(sp) + 0x21, 0x10, 0x85, 0x00, # addu v0,a0,a1 + *MIPS.jal(planet.spaceish_wars_func), + 0x00, 0x00, 0xA2, 0xAF, # _sw v0,0x0(sp) + 0x07, 0x00, 0x00, 0x10, # beq zero,zero,0x7 + *MIPS.nop() + ])) # Prevent Platinum Bolt received message popup at the end of ship races. for address in addresses.RACE_CONTROLLER_FUNCS: @@ -652,22 +644,23 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No patch.write_token(APTokenTypes.WRITE, address + 0x440, bytes([0x67, 0x7B, 0x63, 0x90])) # Make Hypnotist check AP controlled Hypnomatic Part Count # instead of normal address to determine total parts collected. - patch.write_token(APTokenTypes.WRITE, address + 0x19C, bytes([0x1A, 0x00, 0x03, 0x3C])) - patch.write_token(APTokenTypes.WRITE, address + 0x1A0, bytes([0xE6, 0xB2, 0x62, 0x90])) - patch.write_token(APTokenTypes.WRITE, address + 0x1A4, bytes([0x03, 0x00, 0x42, 0x28])) - patch.write_token(APTokenTypes.WRITE, address + 0x1A8, bytes([0x14, 0x00, 0x40, 0x14])) - patch.write_token(APTokenTypes.WRITE, address + 0x1AC, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x1B0, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x1B4, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x1B8, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x1BC, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x218, bytes([0x1A, 0x00, 0x03, 0x3C])) - patch.write_token(APTokenTypes.WRITE, address + 0x21C, bytes([0xE6, 0xB2, 0x62, 0x90])) - patch.write_token(APTokenTypes.WRITE, address + 0x220, bytes([0xD9, 0x27, 0x04, 0x24])) - patch.write_token(APTokenTypes.WRITE, address + 0x224, bytes([0xFF, 0xFF, 0x06, 0x24])) - patch.write_token(APTokenTypes.WRITE, address + 0x228, bytes([0x03, 0x00, 0x03, 0x24])) - patch.write_token(APTokenTypes.WRITE, address + 0x22C, bytes([0x22, 0x28, 0x62, 0x00])) - patch.write_token(APTokenTypes.WRITE, address + 0x230, bytes([0x24, 0x00, 0x00, 0x10])) + upper_half, lower_half = MIPS.get_address_halves(ram.hypnomatic_part_count) + patch.write_token(APTokenTypes.WRITE, address + 0x19C, bytes([ + *upper_half, 0x03, 0x3C, + *lower_half, 0x62, 0x90, + 0x03, 0x00, 0x42, 0x28, + 0x14, 0x00, 0x40, 0x14, + *(MIPS.nop() * 5) + ])) + patch.write_token(APTokenTypes.WRITE, address + 0x218, bytes([ + *upper_half, 0x03, 0x3C, + *lower_half, 0x62, 0x90, + 0xD9, 0x27, 0x04, 0x24, + 0xFF, 0xFF, 0x06, 0x24, + 0x03, 0x00, 0x03, 0x24, + 0x22, 0x28, 0x62, 0x00, + 0x24, 0x00, 0x00, 0x10 + ])) # Replace code that gives Hypnomatic with code that just sets Secondary Inventory flag. patch.write_token(APTokenTypes.WRITE, address + 0x4A0, bytes([0x01, 0x00, 0x04, 0x24])) patch.write_token(APTokenTypes.WRITE, address + 0x4A4, bytes([0x77, 0x8B, 0x84, 0xA3])) diff --git a/data/IsoAddresses.py b/data/IsoAddresses.py index 8818f46..110cd59 100644 --- a/data/IsoAddresses.py +++ b/data/IsoAddresses.py @@ -1,4 +1,4 @@ -from worlds.rac2.data.Planets import * +from worlds.rac2.data import Planets def get_planet_id_from_iso_address(address): @@ -203,13 +203,13 @@ class AddressesSCUS97268: ] MEMCARD_GAME_NAMES: list[int] = [0x3010BD, 0x3010D5, 0x3010ED, 0x30110D, 0x30112D, 0x301141, 0x30115D] - MEMCARD_SAVED_DATA_TABLES: list[int] = [ - 0x003AF8B8, 0x949B5C84, - 0x9D292724, 0x9EB1F958, 0xA193E324, 0xA5DDA684, 0xA8EF558C, 0xACA9CBC8, 0xAE398904, 0xB070F218, 0xB2F6AE6C, - 0xB6B82B1C, 0xB978CFF8, 0xBAF57CD4, 0xBF94F7F4, 0xC32A12EC, 0xC625C84C, 0xC8EC991C, 0xCA5416AC, 0xCD908DAC, - 0xD003BF84, 0xD239E950, 0xD6712A84, 0xDAE6DCAC, 0xDCC32E6C, 0xDE7F0B4C, 0xDF9AD70C, 0xE060BE78, 0xE14A9C2C - ] - DEFAULT_MEMCARD_SAVE_FILE: int = 0x948F31F4 + # MEMCARD_SAVED_DATA_TABLES: list[int] = [ + # 0x003AF8B8, 0x949B5C84, + # 0x9D292724, 0x9EB1F958, 0xA193E324, 0xA5DDA684, 0xA8EF558C, 0xACA9CBC8, 0xAE398904, 0xB070F218, 0xB2F6AE6C, + # 0xB6B82B1C, 0xB978CFF8, 0xBAF57CD4, 0xBF94F7F4, 0xC32A12EC, 0xC625C84C, 0xC8EC991C, 0xCA5416AC, 0xCD908DAC, + # 0xD003BF84, 0xD239E950, 0xD6712A84, 0xDAE6DCAC, 0xDCC32E6C, 0xDE7F0B4C, 0xDF9AD70C, 0xE060BE78, 0xE14A9C2C + # ] + # DEFAULT_MEMCARD_SAVE_FILE: int = 0x948F31F4 ROLL_RANDOM_NUMBER_FUNCS: list[int] = [ 0x00400F60, 0x94A06324, @@ -219,33 +219,33 @@ class AddressesSCUS97268: ] PLANET_WADS = { - ARANOS_TUTORIAL.number: [0x9D04C000, 0x9DF0C27F], # LEVEL0.WAD - OOZLA.number: [0x9E909800, 0x9FDE9D53], # LEVEL1.WAD - MAKTAR_NEBULA.number: [0xA1753800, 0xA2B143F7], # LEVEL2.WAD - ENDAKO.number: [0xA5B5E000, 0xA6C3185F], # LEVEL3.WAD - BARLOW.number: [0xA8CD7000, 0xAA4689DB], # LEVEL4.WAD - FELTZIN_SYSTEM.number: [0xAC85B800, 0xAD8452CF], # LEVEL5.WAD - NOTAK.number: [0xAE11F800, 0xAF3A2BDF], # LEVEL6.WAD - SIBERIUS.number: [0xB04BB800, 0xB19FB52F], # LEVEL7.WAD - TABORA.number: [0xB2D04000, 0xB40AA02F], # LEVEL8.WAD - DOBBO.number: [0xB690A000, 0xB7AFD2DF], # LEVEL9.WAD - HRUGIS_CLOUD.number: [0xB955E000, 0xBA3188BF], # LEVEL10.WAD - JOBA.number: [0xBAD77000, 0xBC2CEA9F], # LEVEL11.WAD - TODANO.number: [0xBF6F8000, 0xC07B628F], # LEVEL12.WAD - BOLDAN.number: [0xC3067800, 0xC418DAAF], # LEVEL13.WAD - ARANOS_PRISON.number: [0xC5FE2800, 0xC706FF9F], # LEVEL14.WAD - GORN.number: [0xC8C75000, 0xC99E65BF], # LEVEL15.WAD - SNIVELAK.number: [0xCA2D4800, 0xCB4563DF], # LEVEL16.WAD - SMOLG.number: [0xCD6E7800, 0xCE6E081F], # LEVEL17.WAD - DAMOSEL.number: [0xCFDEE800, 0xD0ECDABF], # LEVEL18.WAD - GRELBIN.number: [0xD2144800, 0xD39392D7], # LEVEL19.WAD - YEEDIL.number: [0xD64EA000, 0xD794D25B], # LEVEL20.WAD - INSOMNIAC_MUSEUM.number: [0xDAC72800, 0xDB9300DF], # LEVEL21.WAD - DOBBO_ORBIT.number: [0xDCA0C800, 0xDD708D2F], # LEVEL22.WAD - DAMOSEL_ORBIT.number: [0xDE57C000, 0xDF1811CF], # LEVEL23.WAD - SHIP_SHACK.number: [0xDF7F1800, 0xDFFE82D6], # LEVEL24.WAD - WUPASH_NEBULA.number: [0xE03D5000, 0xE0C12E8C], # LEVEL25.WAD - JAMMING_ARRAY.number: [0xE12CC800, 0xE1D47A8F], # LEVEL26.WAD + Planets.ARANOS_TUTORIAL.number: [0x9D04C000, 0x9DF0C27F], # LEVEL0.WAD + Planets.OOZLA.number: [0x9E909800, 0x9FDE9D53], # LEVEL1.WAD + Planets.MAKTAR_NEBULA.number: [0xA1753800, 0xA2B143F7], # LEVEL2.WAD + Planets.ENDAKO.number: [0xA5B5E000, 0xA6C3185F], # LEVEL3.WAD + Planets.BARLOW.number: [0xA8CD7000, 0xAA4689DB], # LEVEL4.WAD + Planets.FELTZIN_SYSTEM.number: [0xAC85B800, 0xAD8452CF], # LEVEL5.WAD + Planets.NOTAK.number: [0xAE11F800, 0xAF3A2BDF], # LEVEL6.WAD + Planets.SIBERIUS.number: [0xB04BB800, 0xB19FB52F], # LEVEL7.WAD + Planets.TABORA.number: [0xB2D04000, 0xB40AA02F], # LEVEL8.WAD + Planets.DOBBO.number: [0xB690A000, 0xB7AFD2DF], # LEVEL9.WAD + Planets.HRUGIS_CLOUD.number: [0xB955E000, 0xBA3188BF], # LEVEL10.WAD + Planets.JOBA.number: [0xBAD77000, 0xBC2CEA9F], # LEVEL11.WAD + Planets.TODANO.number: [0xBF6F8000, 0xC07B628F], # LEVEL12.WAD + Planets.BOLDAN.number: [0xC3067800, 0xC418DAAF], # LEVEL13.WAD + Planets.ARANOS_PRISON.number: [0xC5FE2800, 0xC706FF9F], # LEVEL14.WAD + Planets.GORN.number: [0xC8C75000, 0xC99E65BF], # LEVEL15.WAD + Planets.SNIVELAK.number: [0xCA2D4800, 0xCB4563DF], # LEVEL16.WAD + Planets.SMOLG.number: [0xCD6E7800, 0xCE6E081F], # LEVEL17.WAD + Planets.DAMOSEL.number: [0xCFDEE800, 0xD0ECDABF], # LEVEL18.WAD + Planets.GRELBIN.number: [0xD2144800, 0xD39392D7], # LEVEL19.WAD + Planets.YEEDIL.number: [0xD64EA000, 0xD794D25B], # LEVEL20.WAD + Planets.INSOMNIAC_MUSEUM.number: [0xDAC72800, 0xDB9300DF], # LEVEL21.WAD + Planets.DOBBO_ORBIT.number: [0xDCA0C800, 0xDD708D2F], # LEVEL22.WAD + Planets.DAMOSEL_ORBIT.number: [0xDE57C000, 0xDF1811CF], # LEVEL23.WAD + Planets.SHIP_SHACK.number: [0xDF7F1800, 0xDFFE82D6], # LEVEL24.WAD + Planets.WUPASH_NEBULA.number: [0xE03D5000, 0xE0C12E8C], # LEVEL25.WAD + Planets.JAMMING_ARRAY.number: [0xE12CC800, 0xE1D47A8F], # LEVEL26.WAD } # Oozla diff --git a/data/Items.py b/data/Items.py index 3a19ff5..f35a510 100644 --- a/data/Items.py +++ b/data/Items.py @@ -32,26 +32,42 @@ class EquipmentData(ItemData): oclass_id: Optional[int] = 0x0047 # Wrench model icon_id: Optional[int] = 0xEA75 # Question Mark icon `?` +# Unreferenced equipment offsets: +# - 00: ??? +# - 01: ??? +# - 04: "Hydro-Pack" +# - 06: "Armor" +# - 0A: "Wrench" +# - 0B: ??? +# - 0F: Unused Weapon 1 +# - 21: ??? +# - 22: Unused Weapon 2 +# - 23: Unused Weapon 3 +# - 28: Unused Weapon 4 +# - 2F: "Megacorp Helmet +# - 30: "Biker Helmet" +# - 34: ??? + # Gadgets/Items -HELI_PACK = EquipmentData(1, "Heli-Pack", 2, oclass_id=0x025F, icon_id=0xEA8D) -THRUSTER_PACK = EquipmentData(2, "Thruster-Pack", 3, oclass_id=0x0260, icon_id=0xEA7E) -MAPPER = EquipmentData(3, "Mapper", 5, oclass_id=0x1235, icon_id=0xEA9F) -ARMOR_MAGNETIZER = EquipmentData(4, "Armor Magnetizer", 7, oclass_id=0x112F, icon_id=0xEA9A) -LEVITATOR = EquipmentData(5, "Levitator", 8, oclass_id=0x096C, icon_id=0xEA90) -SWINGSHOT = EquipmentData(6, "Swingshot", 13, oclass_id=0x00D0, icon_id=0xEA8B) -GRAVITY_BOOTS = EquipmentData(7, "Gravity Boots", 19, oclass_id=0x00AD, icon_id=0xEA88) -GRIND_BOOTS = EquipmentData(8, "Grindboots", 20, oclass_id=0x00C3, icon_id=0xEA8C) -GLIDER = EquipmentData(9, "Glider", 21, icon_id=0xEA91) -DYNAMO = EquipmentData(10, "Dynamo", 36, oclass_id=0x0825, icon_id=0xEA7F) -ELECTROLYZER = EquipmentData(11, "Electrolyzer", 38, oclass_id=0x0870, icon_id=0xEA81) -THERMANATOR = EquipmentData(12, "Thermanator", 39, oclass_id=0x0FB0, icon_id=0xEA82) -TRACTOR_BEAM = EquipmentData(13, "Tractor Beam", 46, oclass_id=0x00BC, icon_id=0xEA80) -QWARK_STATUETTE = EquipmentData(14, "Qwark Statuette", 49, icon_id=0xEA9C) -BOX_BREAKER = EquipmentData(15, "Box Breaker", 50, oclass_id=0x1238, icon_id=0xEAA1) -INFILTRATOR = EquipmentData(16, "Infiltrator", 51, oclass_id=0x0BD3, icon_id=0xEA83) -CHARGE_BOOTS = EquipmentData(17, "Charge Boots", 54, oclass_id=0x0E70, icon_id=0xEA89) -HYPNOMATIC = EquipmentData(18, "Hypnomatic", 55, oclass_id=0x0950, icon_id=0xEA84) +HELI_PACK = EquipmentData(1, "Heli-Pack", 0x2, oclass_id=0x025F, icon_id=0xEA8D) +THRUSTER_PACK = EquipmentData(2, "Thruster-Pack", 0x3, oclass_id=0x0260, icon_id=0xEA7E) +MAPPER = EquipmentData(3, "Mapper", 0x5, oclass_id=0x1235, icon_id=0xEA9F) +ARMOR_MAGNETIZER = EquipmentData(4, "Armor Magnetizer", 0x7, oclass_id=0x112F, icon_id=0xEA9A) +LEVITATOR = EquipmentData(5, "Levitator", 0x8, oclass_id=0x096C, icon_id=0xEA90) +SWINGSHOT = EquipmentData(6, "Swingshot", 0xD, oclass_id=0x00D0, icon_id=0xEA8B) +GRAVITY_BOOTS = EquipmentData(7, "Gravity Boots", 0x13, oclass_id=0x00AD, icon_id=0xEA88) +GRIND_BOOTS = EquipmentData(8, "Grindboots", 0x14, oclass_id=0x00C3, icon_id=0xEA8C) +GLIDER = EquipmentData(9, "Glider", 0x15, icon_id=0xEA91) +DYNAMO = EquipmentData(10, "Dynamo", 0x24, oclass_id=0x0825, icon_id=0xEA7F) +ELECTROLYZER = EquipmentData(11, "Electrolyzer", 0x26, oclass_id=0x0870, icon_id=0xEA81) +THERMANATOR = EquipmentData(12, "Thermanator", 0x27, oclass_id=0x0FB0, icon_id=0xEA82) +TRACTOR_BEAM = EquipmentData(13, "Tractor Beam", 0x2E, oclass_id=0x00BC, icon_id=0xEA80) +QWARK_STATUETTE = EquipmentData(14, "Qwark Statuette", 0x31, icon_id=0xEA9C) +BOX_BREAKER = EquipmentData(15, "Box Breaker", 0x32, oclass_id=0x1238, icon_id=0xEAA1) +INFILTRATOR = EquipmentData(16, "Infiltrator", 0x33, oclass_id=0x0BD3, icon_id=0xEA83) +CHARGE_BOOTS = EquipmentData(17, "Charge Boots", 0x36, oclass_id=0x0E70, icon_id=0xEA89) +HYPNOMATIC = EquipmentData(18, "Hypnomatic", 0x37, oclass_id=0x0950, icon_id=0xEA84) @dataclass diff --git a/data/RamAddresses.py b/data/RamAddresses.py index 548112b..a7a7e01 100644 --- a/data/RamAddresses.py +++ b/data/RamAddresses.py @@ -70,6 +70,7 @@ def __init__(self, game_version: str): self.inventory: int = 0x1A7AF8 self.secondary_inventory: int = 0x1A7B30 self.vendor_list: int = 0x1A7B68 + # self.savefile_play_time: int = 0x1A7BC0 self.unlocked_planets: int = 0x1A7BC8 self.loaded_flag: int = 0x1A7BE5 self.highlighted_planets: int = 0x1A7BE8 @@ -344,22 +345,25 @@ def __init__(self, game_version: str): # Custom addresses ################################################ - # I use some unused addresses at the end of the platinum bolt table to store some extra data for AP. - self.platinum_bolt_count: int = self.platinum_bolt_table + 0x6C - self.nanotech_boost_count: int = self.platinum_bolt_table + 0x6D - self.hypnomatic_part_count: int = self.platinum_bolt_table + 0x6E + # We use secondary inventory for unused weapons to store "global" data that needs to be saved to memcard + self.platinum_bolt_count: int = self.secondary_inventory + 0x0F # Unused weapon 1 + self.nanotech_boost_count: int = self.secondary_inventory + 0x22 # Unused weapon 2 + self.hypnomatic_part_count: int = self.secondary_inventory + 0x23 # Unused weapon 3 + self.bolt_pack_count: int = self.secondary_inventory + 0x28 # Unused weapon 4 - # The "enemy kill count table" is actually one table per planet of 0x400 bytes each, holding the number - # of times two enemies were killed (one in the upper half, the other in the lower half of the byte). - # As one might expect, this is way too much and most of this data is empty, but still saved on the memcard. - # We use the table for Feltzin since spaceship systems don't use that mechanic at all, but still have that - # table. - self.custom_data_table: int = self.enemy_kill_count_table + (FELTZIN_SYSTEM.number * 0x400) - self.bolt_pack_count: int = self.custom_data_table - self.tabora_wrench_cutscene_flag: int = self.custom_data_table + 0x1 - self.aranos_wrench_cutscene_flag: int = self.custom_data_table + 0x2 - self.custom_text_notification_trigger: int = self.custom_data_table + 0x3 - self.custom_data_table_end: int = self.custom_data_table + 0x4 + # Put the new Tabora wrench cutscene flag inside the platinum bolt table for Tabora so it gets saved along + # other planet specific data + self.tabora_wrench_cutscene_flag: int = self.platinum_bolt_table + (TABORA.number * 4) + 3 + # Put the new Aranos Prison wrench cutscene flag inside the platinum bolt table for Aranos so it gets + # saved along other planet specific data + self.aranos_wrench_cutscene_flag: int = self.platinum_bolt_table + (ARANOS_PRISON.number * 4) + 1 + + # The "enemy kill count table" is a 0x400 table for each, holding the number of times two enemies were + # killed (one in the upper half, the other in the lower half of the byte). + # We use Feltzin's table as a spot to put temporary data, since spaceship systems don't use that mechanic + # at all but still have that table. + self.temporary_data_table: int = self.enemy_kill_count_table + (FELTZIN_SYSTEM.number * 0x400) + self.custom_text_notification_trigger: int = self.temporary_data_table class PlanetAddresses(NamedTuple):