diff --git a/config/sym_extern.us.txt b/config/sym_extern.us.txt index 76c3e17..34352e7 100644 --- a/config/sym_extern.us.txt +++ b/config/sym_extern.us.txt @@ -1,7 +1,7 @@ D_8009A000 = 0x8009A000; D_8009A004 = 0x8009A004; D_8009A008 = 0x8009A008; -_work = 0x8009C6E4; +Savemap = 0x8009C6E4; func_800A0000 = 0x800A0000; func_800A0030 = 0x800A0030; func_800A00BC = 0x800A00BC; diff --git a/config/sym_ovl_export.us.txt b/config/sym_ovl_export.us.txt index 2e39171..4ec13ba 100644 --- a/config/sym_ovl_export.us.txt +++ b/config/sym_ovl_export.us.txt @@ -2031,6 +2031,7 @@ func_801D3668 = 0x801d3668; func_801D3698 = 0x801d3698; func_801D370C = 0x801d370c; func_801D4CC0 = 0x801d4cc0; +g_MenuStartMode = 0x801e2cf8; menus = 0x801e379c; func_800A0B48 = 0x800a0b48; func_800A0BE4 = 0x800a0be4; diff --git a/include/game.h b/include/game.h index b3e2ecb..70895ba 100644 --- a/include/game.h +++ b/include/game.h @@ -74,10 +74,14 @@ typedef union { typedef struct { u32 checksum; u8 leader_level; - u8 party[3]; + u8 leader_portrait; + u8 portrait2; + u8 portrait3; s8 leader_name[0x10]; - s32 unk18; - s32 unk1C; + u16 leader_hp; + u16 leader_hp_max; + u16 leader_mp; + u16 leader_mp_max; s32 gil; s32 time; s8 place_name[0x20]; @@ -132,23 +136,32 @@ typedef struct { /* 0x4F8 */ u8 partyID[4]; /* 0x4FC */ u16 inventory[MAX_INVENTORY_COUNT]; /* 0x77C */ s32 materia[MAX_MATERIA_COUNT]; - /* 0xA9C */ u8 unkA9C[0xE0]; + /* 0xA9C */ s32 yuffie_stolen_materia[48]; + /* 0xB5C */ u8 unk_b5c[32]; /* 0xB7C */ s32 gil; /* 0xB80 */ s32 time; - /* 0xB84 */ u8 D_8009D268[0x20]; - /* 0xBA4 */ u8 D_8009D288[0x18]; - /* 0xBBC */ u16 D_8009D2A0; - /* 0xBBE */ u8 D_8009D2A2; - /* 0xBBF */ u8 D_8009D2A3; - /* 0xBC0 */ u8 unkBC0[0x10]; - /* 0xBD0 */ u8 unkBD0[0x10]; - /* 0xBE0 */ u8 unkBE0[15]; - /* 0xBEF */ u8 unkBEF; - /* 0xBF0 */ u8 unkBF0[0x420]; - /* 0x1010 */ u16 unk1010; - /* 0x1012 */ u8 unk1012; - /* 0x1013 */ u8 unk1013; - /* 0x1014 */ u8 unk1014[0xC4]; + /* 0xB84 */ s32 countdown_timer_seconds; + /* 0xB88 */ s32 game_timer_fraction; + /* 0xB8C */ s32 countdown_timer_fraction; + /* 0xB90 */ s32 worldmap_exit_action; + /* 0xB94 */ u16 current_module; + /* 0xB96 */ u16 current_location_id; + /* 0xB98 */ u16 padding2; + /* 0xB9A */ s16 field_x; + /* 0xB9C */ s16 field_y; + /* 0xB9E */ u16 field_triangle; + /* 0xBA0 */ u8 field_direction; + /* 0xBA1 */ u8 step_id; + /* 0xBA2 */ u8 step_offset; + /* 0xBA3 */ u8 padding3; + /* 0xBA4 */ u8 memory_bank_1[256]; + /* 0xCA4 */ u8 memory_bank_2[256]; + /* 0xDA4 */ u8 memory_bank_3[256]; + /* 0xEA4 */ u8 memory_bank_4[256]; + /* 0xFA4 */ u8 memory_bank_5[256]; + /* 0x10A4 */ u16 phs_locking_mask; + /* 0x10A6 */ u16 phs_visibility_mask; + /* 0x10A8 */ u8 unk_10a8[48]; /* 0x10D8 */ u8 battle_speed; /* 0x10D9 */ u8 battle_msg_speed; /* 0x10DA */ u16 config; @@ -299,7 +312,7 @@ extern s16 D_8009A000; extern s32 D_8009A004; extern s32 D_8009A008; extern s32 D_8009A024[8]; -extern SaveWork _work; // 0x8009C6E4 +extern SaveWork Savemap; // 0x8009C6E4 extern u8 D_8009CBDC[]; extern u8 D_8009D684; extern u8 D_8009D686; diff --git a/src/battle/battle.c b/src/battle/battle.c index b62cfaa..e55c715 100644 --- a/src/battle/battle.c +++ b/src/battle/battle.c @@ -766,7 +766,8 @@ void func_800AE078(void) {} INCLUDE_ASM("asm/us/battle/nonmatchings/battle", func_800AE080); void func_800AE234(void) { - D_80063014->unk214 = _work.D_8009D2A2 + _work.D_8009D2A3 * 256; + D_80063014->unk214 = + Savemap.memory_bank_1[26] + Savemap.memory_bank_1[27] * 256; } INCLUDE_ASM("asm/us/battle/nonmatchings/battle", func_800AE25C); @@ -962,10 +963,10 @@ s32 func_800B2C60(s32 arg0) { static void func_800B2CAC(s32 arg0, s32 arg1) { switch (arg0) { case 0: - D_800F83A6 = _work.D_8009D288[arg1]; + D_800F83A6 = Savemap.memory_bank_1[arg1]; return; case 1: - _work.D_8009D288[arg1] = D_800F83A6; + Savemap.memory_bank_1[arg1] = D_800F83A6; return; } } diff --git a/src/battle/battle1.c b/src/battle/battle1.c index 7fb56e4..ece1d1d 100644 --- a/src/battle/battle1.c +++ b/src/battle/battle1.c @@ -390,7 +390,7 @@ INCLUDE_ASM("asm/us/battle/nonmatchings/battle1", func_800C5CC0); INCLUDE_ASM("asm/us/battle/nonmatchings/battle1", func_800C5E94); -s32 func_800C60F4(void) { return _work.battle_msg_speed / 4 + 4; } +s32 func_800C60F4(void) { return Savemap.battle_msg_speed / 4 + 4; } static void func_800C610C(void) { while (D_801518DC) { diff --git a/src/main/18B8.c b/src/main/18B8.c index 20e62a3..c2d9dae 100644 --- a/src/main/18B8.c +++ b/src/main/18B8.c @@ -1409,13 +1409,13 @@ INCLUDE_ASM("asm/us/main/nonmatchings/18B8", func_80025380); s32 func_8002542C(s32 arg0) { s32 i; for (i = 0; i < MAX_MATERIA_COUNT; i++) { - if (_work.materia[i] == -1) { - _work.materia[i] = arg0; + if (Savemap.materia[i] == -1) { + Savemap.materia[i] = arg0; if (func_8002603C(arg0 & 0xFF) == 10) { - _work.unkBEF |= 1; + Savemap.memory_bank_1[75] |= 1; } if ((arg0 & 0xFF) == 44) { - _work.unkBEF |= 2; + Savemap.memory_bank_1[75] |= 2; } return -1; } @@ -1451,7 +1451,7 @@ Unk8009D84C* func_80025788(s32 arg0) { Unk8009D84C* partyMember; partyMember = (Unk8009D84C*)0xFF; - if (_work.partyID[arg0] != 0xFF) { + if (Savemap.partyID[arg0] != 0xFF) { return &D_8009D84C[arg0]; } return 0xFF; @@ -1474,7 +1474,7 @@ INCLUDE_ASM("asm/us/main/nonmatchings/18B8", func_80025B10); INCLUDE_ASM("asm/us/main/nonmatchings/18B8", func_80025B48); -s32 SYS_gil(void) { return _work.gil; } +s32 SYS_gil(void) { return Savemap.gil; } void func_80025B8C(u_long* image) { RECT rect; diff --git a/src/menu/bginmenu.c b/src/menu/bginmenu.c index 43ef219..5c6d513 100644 --- a/src/menu/bginmenu.c +++ b/src/menu/bginmenu.c @@ -52,8 +52,8 @@ static void func_801D01BC(void) {} INCLUDE_ASM("asm/us/menu/nonmatchings/bginmenu", func_801D01C4); #else // -O1 -extern u8 D_8009C778[]; // _work.party -extern u8 D_8009C798[]; // _work.party +extern u8 D_8009C778[]; // Savemap.party +extern u8 D_8009C798[]; // Savemap.party s32 func_801D01C4(s32 arg0) { s32 var_a1; s32 var_a2; @@ -103,7 +103,7 @@ INCLUDE_ASM("asm/us/menu/nonmatchings/bginmenu", func_801D0500); void func_801D05C4(s32 arg0) { s32 i; - _work.unk1013 = 0; + Savemap.memory_bank_5[111] = 0; switch (arg0) { case 0: for (i = 0; i < 21; i++) { @@ -134,7 +134,7 @@ void func_801D05C4(s32 arg0) { } break; } - _work.unk1013 = 1; + Savemap.memory_bank_5[111] = 1; } void func_801D0704(s32 arg0) { diff --git a/src/menu/cnfgmenu.c b/src/menu/cnfgmenu.c index 9232671..bc89a7d 100644 --- a/src/menu/cnfgmenu.c +++ b/src/menu/cnfgmenu.c @@ -172,31 +172,31 @@ static void func_801D01C8(void) { func_80026F44(40, y + (i - 1) * 18, D_801D1AE8[33], 5); for (i = 0; i < 2; i++) { func_80026F44(165 + i * 65, y, D_801D1AE8[15 + i], - -((_work.config & 3) == i) & 7); + -((Savemap.config & 3) == i) & 7); } for (i = 0; i < 2; i++) { func_80026F44(165 + i * 65, y + 18, D_801D1E48[i], - -(((_work.config >> 2) & 3) == i) & 7); + -(((Savemap.config >> 2) & 3) == i) & 7); } for (i = 0; i < 2; i++) { func_80026F44(165 + i * 65, y + 0x24, D_801D1EA8[i], - -(((_work.config >> 4) & 3) == i) & 7); + -(((Savemap.config >> 4) & 3) == i) & 7); } temp_s4 = y + 54; func_80026F44( - 165, temp_s4, D_801D1F08[0], -(((_work.config >> 6) & 3) == 0) & 7); + 165, temp_s4, D_801D1F08[0], -(((Savemap.config >> 6) & 3) == 0) & 7); x = func_80026B70(D_801D1F08[0]); - func_80026F44( - x + 175, temp_s4, D_801D1F08[1], -(((_work.config >> 6) & 3) == 1) & 7); + func_80026F44(x + 175, temp_s4, D_801D1F08[1], + -(((Savemap.config >> 6) & 3) == 1) & 7); func_80026F44(x + func_80026B70(D_801D1F08[1]) + 185, temp_s4, - D_801D1F08[2], -(((_work.config >> 6) & 3) == 2) & 7); + D_801D1F08[2], -(((Savemap.config >> 6) & 3) == 2) & 7); for (i = 0; i < 2; i++) { func_80026F44(165 + i * 65, y + 126, D_801D1F08[3 + i], - -(((_work.config >> 8) & 3) == i) & 7); + -(((Savemap.config >> 8) & 3) == i) & 7); } for (i = 0; i < 3; i++) { - _setting = D_801D248C[((_work.config >> 10) & 7) * 3 + i]; - setting = &D_801D248C[((_work.config >> 10) & 7) * 3]; + _setting = D_801D248C[((Savemap.config >> 10) & 7) * 3 + i]; + setting = &D_801D248C[((Savemap.config >> 10) & 7) * 3]; func_80026F44(189 + i * 52, y + 0x90, D_801D1F08[12 + setting[i]], 7); } func_80026F44(149, y + 0x90, D_801D1F08[16], 7); @@ -205,21 +205,21 @@ static void func_801D01C8(void) { rect.y = y + 72; rect.w = 8; rect.h = 11; - rect.x = (_work.battle_speed >> 1) + 184; + rect.x = (Savemap.battle_speed >> 1) + 184; func_80028030(&rect); // battle message speed value rect.y = y + 0x5A; rect.w = 8; rect.h = 11; - rect.x = (_work.battle_msg_speed >> 1) + 184; + rect.x = (Savemap.battle_msg_speed >> 1) + 184; func_80028030(&rect); // field message speed value rect.y = y + 108; rect.w = 8; rect.h = 11; - rect.x = (_work.field_msg_speed >> 1) + 184; + rect.x = (Savemap.field_msg_speed >> 1) + 184; func_80028030(&rect); // speed values @@ -238,7 +238,7 @@ static void func_801D01C8(void) { } // magic order ID - func_80029114(173, y + 146, ((_work.config >> 0xA) & 7) + 1, 1, 7); + func_80029114(173, y + 146, ((Savemap.config >> 0xA) & 7) + 1, 1, 7); rect.x = 0; rect.y = 0; diff --git a/src/menu/savemenu.c b/src/menu/savemenu.c index 74e09de..1ec37e5 100644 --- a/src/menu/savemenu.c +++ b/src/menu/savemenu.c @@ -602,7 +602,7 @@ INCLUDE_ASM("asm/us/menu/nonmatchings/savemenu", func_801D224C); INCLUDE_ASM("asm/us/menu/nonmatchings/savemenu", func_801D2408); -static const char* D_801E2CB8[] = { +const char* D_801E2CB8[] = { "FF7/SAVE01/00:00", "FF7/SAVE02/00:00", "FF7/SAVE03/00:00", "FF7/SAVE04/00:00", "FF7/SAVE05/00:00", "FF7/SAVE06/00:00", @@ -627,7 +627,7 @@ static s16 func_801D2A34(s32 save_id) { D_801E3D50 = slot; ret = func_801D2408(&sp10, D_801E2CB8[slot]); if (!(s16)ret) { - memcpy(&D_801E3864[slot], &_work.header, sizeof(SaveHeader)); + memcpy(&D_801E3864[slot], &Savemap.header, sizeof(SaveHeader)); } return ret; } diff --git a/src/menu/title.c b/src/menu/title.c index d9896c1..4d29d57 100644 --- a/src/menu/title.c +++ b/src/menu/title.c @@ -499,16 +499,16 @@ static s32 func_801D3AB0(s32 arg0) { } var_s1 = (s16)func_801D1F40(var_a0_3); if (var_s1 == 0) { - if (_work.header.checksum != + if (Savemap.header.checksum != (u16)func_801D1950( - sizeof(SaveWork) - 4, &_work.header.leader_level)) { + sizeof(SaveWork) - 4, &Savemap.header.leader_level)) { g_MenuStartMode = START_MENU_MODE_SELECT_FILE; func_801D2B58(3); func_8001F6C0(D_801E2CFC[31], 0); } else { func_801D2B58(0xD0); D_801E3D54 = 2; - func_801D2D10(_work.config & 3); + func_801D2D10(Savemap.config & 3); } } else { g_MenuStartMode = START_MENU_MODE_SELECT_FILE; @@ -615,7 +615,7 @@ s32 func_801D4CC0(void) { PutDispEnv(&D_801E3EEC[1]); PutDrawEnv(&D_801E3E34[1]); for (i = 0; i < 3; i++) { - if (_work.partyID[i] != 0xFF) { + if (Savemap.partyID[i] != 0xFF) { func_80020058(i); func_8001786C((u8)i); } diff --git a/tools/builder/cmd/decompile.go b/tools/builder/cmd/decompile.go index 5513d0e..2939db0 100644 --- a/tools/builder/cmd/decompile.go +++ b/tools/builder/cmd/decompile.go @@ -5,11 +5,18 @@ import ( "github.com/xeeynamo/ff7-decomp/tools/builder/deps" ) +var ( + fixStructs bool +) + var decompileCmd = &cobra.Command{ Use: "dec ", Short: "Replaces INCLUDE_ASM with approximated decompiled function", SilenceErrors: true, RunE: func(cmd *cobra.Command, args []string) error { + if fixStructs { + args = append(args, "--fix-structs") + } if err := deps.Decompile(args...); err != nil { return err } @@ -18,5 +25,6 @@ var decompileCmd = &cobra.Command{ } func init() { + decompileCmd.Flags().BoolVar(&fixStructs, "fix-structs", false, "Replace D_8009XXXX symbols with Savemap.field_name references") rootCmd.AddCommand(decompileCmd) } diff --git a/tools/decompile.py b/tools/decompile.py index b85a70c..1c2d1ae 100755 --- a/tools/decompile.py +++ b/tools/decompile.py @@ -15,6 +15,7 @@ import tempfile import m2ctx import m2c.m2c.main as m2c +import fix_structs from contextlib import redirect_stdout from pathlib import Path from typing import Optional @@ -27,6 +28,7 @@ def __init__(self, func_root: Path, function_path: Path, args: argparse.Namespac self.name: str = args.function self.version: str = args.version self._overlay: str = args.overlay + self.fix_structs: bool = args.fix_structs # These directories/paths should be considered to be specific to the function self.root: Path = func_root @@ -110,11 +112,14 @@ def _run_m2c(self) -> str: return f.getvalue() def _guess_unknown_type(self, decompiled_code: str) -> str: - return ( + code = ( decompiled_code.replace("? func_", "/*?*/ void func_") .replace("extern ? D_", "extern /*?*/s32 D_") .replace("extern ?* D_", "extern /*?*/u8* D_") ) + if self.fix_structs: + code = fix_structs.fix_symbols(code) + return code def _infer_src_path(self) -> Optional[Path]: inferred_c_files = ( @@ -229,4 +234,10 @@ def main(args: argparse.Namespace) -> None: type=str, required=False, ) + parser.add_argument( + "--fix-structs", + action="store_true", + default=False, + help="Replace D_8009XXXX symbols with Savemap.field_name references", + ) main(parser.parse_args()) diff --git a/tools/fix_structs.py b/tools/fix_structs.py new file mode 100644 index 0000000..13498da --- /dev/null +++ b/tools/fix_structs.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +"""Post-processing script to replace D_8009XXXX symbols with Savemap.field_name. + +This script takes decompiled code and replaces raw address symbols that fall within +the Savemap struct with proper struct member accesses. + +Usage: + python fix_structs.py < input.c > output.c + python fix_structs.py input.c + cat file.c | python fix_structs.py +""" + +import re +import sys + +SAVEMAP_BASE = 0x8009C6E4 + +SAVEMAP_FIELDS = [ + # (offset, size, name, element_size) - element_size > 0 means array + # SaveHeader (embedded) + (0x000, 4, "header.checksum", 0), + (0x004, 1, "header.leader_level", 0), + (0x005, 1, "header.leader_portrait", 0), + (0x006, 1, "header.portrait2", 0), + (0x007, 1, "header.portrait3", 0), + (0x008, 16, "header.leader_name", 1), + (0x018, 2, "header.leader_hp", 0), + (0x01A, 2, "header.leader_hp_max", 0), + (0x01C, 2, "header.leader_mp", 0), + (0x01E, 2, "header.leader_mp_max", 0), + (0x020, 4, "header.gil", 0), + (0x024, 4, "header.time", 0), + (0x028, 32, "header.place_name", 1), + (0x048, 12, "header.menu_color", 4), + # SavePartyMember party[9] - 0x84 bytes each + (0x054, 0x4A4, "party", 0x84), + # Rest of SaveWork + (0x4F8, 4, "partyID", 1), + (0x4FC, 640, "inventory", 2), + (0x77C, 800, "materia", 4), + (0xA9C, 192, "yuffie_stolen_materia", 4), + (0xB5C, 32, "unk_b5c", 1), + (0xB7C, 4, "gil", 0), + (0xB80, 4, "time", 0), + (0xB84, 4, "countdown_timer_seconds", 0), + (0xB88, 4, "game_timer_fraction", 0), + (0xB8C, 4, "countdown_timer_fraction", 0), + (0xB90, 4, "worldmap_exit_action", 0), + (0xB94, 2, "current_module", 0), + (0xB96, 2, "current_location_id", 0), + (0xB98, 2, "padding2", 0), + (0xB9A, 2, "field_x", 0), + (0xB9C, 2, "field_y", 0), + (0xB9E, 2, "field_triangle", 0), + (0xBA0, 1, "field_direction", 0), + (0xBA1, 1, "step_id", 0), + (0xBA2, 1, "step_offset", 0), + (0xBA3, 1, "padding3", 0), + (0xBA4, 256, "memory_bank_1", 1), + (0xCA4, 256, "memory_bank_2", 1), + (0xDA4, 256, "memory_bank_3", 1), + (0xEA4, 256, "memory_bank_4", 1), + (0xFA4, 256, "memory_bank_5", 1), + (0x10A4, 2, "phs_locking_mask", 0), + (0x10A6, 2, "phs_visibility_mask", 0), + (0x10A8, 48, "unk_10a8", 1), + (0x10D8, 1, "battle_speed", 0), + (0x10D9, 1, "battle_msg_speed", 0), + (0x10DA, 2, "config", 0), + (0x10DC, 16, "button_config", 1), + (0x10EC, 1, "field_msg_speed", 0), + (0x10ED, 1, "D_8009D7D1", 0), + (0x10EE, 2, "D_8009D7D2", 0), + (0x10F0, 4, "D_8009D7D4", 0), +] + +SAVEMAP_END = SAVEMAP_BASE + 0x10F4 + + +def build_offset_map(): + """Build a map from offset to (field_name, element_size, field_offset).""" + offset_map = {} + for field_offset, size, name, elem_size in SAVEMAP_FIELDS: + if elem_size > 0: + for i in range(size // elem_size): + offset_map[field_offset + i * elem_size] = (name, elem_size, field_offset) + else: + offset_map[field_offset] = (name, 0, field_offset) + return offset_map + + +def address_to_field(addr, offset_map): + """Convert an address to a Savemap.field reference.""" + if addr < SAVEMAP_BASE or addr >= SAVEMAP_END: + return None + + offset = addr - SAVEMAP_BASE + + if offset in offset_map: + name, elem_size, field_offset = offset_map[offset] + if elem_size > 0: + idx = (offset - field_offset) // elem_size + return f"Savemap.{name}[{idx}]" + return f"Savemap.{name}" + + for field_offset, size, name, elem_size in SAVEMAP_FIELDS: + if field_offset <= offset < field_offset + size: + if elem_size > 0: + idx = (offset - field_offset) // elem_size + remainder = (offset - field_offset) % elem_size + if remainder == 0: + return f"Savemap.{name}[{idx}]" + return f"Savemap.{name}[{idx}] + 0x{remainder:X}" + return f"Savemap.{name}" + + return f"Savemap + 0x{offset:X}" + + +def fix_symbols(code): + """Replace D_8009XXXX symbols with Savemap.field references.""" + offset_map = build_offset_map() + symbol_pattern = re.compile(r'\bD_(8009[0-9A-Fa-f]{4})\b') + extern_pattern = re.compile(r'^extern\s+(?:/\*\?\*/\s*)?\w+\*?\s+Savemap\.[^;]+;\s*\n', re.MULTILINE) + + def replace_symbol(match): + addr = int(match.group(1), 16) + field = address_to_field(addr, offset_map) + if field: + return field + return match.group(0) + + result = symbol_pattern.sub(replace_symbol, code) + result = extern_pattern.sub('', result) + return result + + +def main(): + if len(sys.argv) > 1: + with open(sys.argv[1], 'r') as f: + code = f.read() + result = fix_symbols(code) + print(result, end='') + else: + code = sys.stdin.read() + result = fix_symbols(code) + print(result, end='') + + +if __name__ == "__main__": + main()