From 183a42e7e0147b77ece0f0688caa7d882efd9b5f Mon Sep 17 00:00:00 2001 From: weirdbeardgame Date: Sun, 2 Aug 2026 22:42:08 -0500 Subject: [PATCH] Added File.c, added Tex.h, added KuonMenu.h, Menu.h, Cdvd.h Added missing struct defines, missing function defines Implement File --- config/kuon.yaml | 2 +- configure.py | 2 +- src/cs4/Seimei/fslib/lib/FsFont.h | 6 + src/cs4/Seimei/src/Cdvd.h | 115 ++++++++++++++ src/cs4/Seimei/src/File.c | 226 +++++++++++++++++++++++++-- src/cs4/Seimei/src/File.h | 3 + src/cs4/Seimei/src/Game.h | 1 + src/cs4/Seimei/src/Tex.h | 68 ++++++++ src/cs4/Seimei/src/Unk.h | 2 + src/cs4/Seimei/src/menu/KuonMenu.h | 24 +++ src/cs4/Seimei/src/menu/Menu.h | 27 ++++ tools/python/annotate.py | 240 +++++++++++++++++++++++++++++ 12 files changed, 705 insertions(+), 11 deletions(-) create mode 100644 src/cs4/Seimei/fslib/lib/FsFont.h create mode 100644 src/cs4/Seimei/src/Cdvd.h create mode 100644 src/cs4/Seimei/src/Tex.h create mode 100644 src/cs4/Seimei/src/menu/KuonMenu.h create mode 100644 src/cs4/Seimei/src/menu/Menu.h create mode 100644 tools/python/annotate.py diff --git a/config/kuon.yaml b/config/kuon.yaml index 249e139..efb39eb 100644 --- a/config/kuon.yaml +++ b/config/kuon.yaml @@ -249,7 +249,7 @@ segments: - [0x032110, asmtu, cs4/Seimei/src/Camera] - [0x034930, asmtu, cs4/Seimei/src/Debug] - [0x034D10, asmtu, cs4/Seimei/src/Draw] - - [0x038710, asmtu, cs4/Seimei/src/File] + - [0x038710, c, cs4/Seimei/src/File] - [0x038D00, asmtu, cs4/Seimei/src/Game] - [0x039A00, asmtu, cs4/Seimei/src/Load] - [0x03A380, c, cs4/Seimei/src/Main] diff --git a/configure.py b/configure.py index 506da88..cce9d9f 100755 --- a/configure.py +++ b/configure.py @@ -101,7 +101,7 @@ def make_compiler_cmd(config_dir: Path, src_path: Path, language: str): game_cc_dir = f"{tools_dir}/cc/{COMPILER}" - compiler_includes = f"-I{src_path.parent / 'src'} -I{src_path.parent / 'include'} -Iinclude/sdk/ee" + compiler_includes = f"-I{src_path.parent / 'src'} -I{src_path.parent / 'include'} -Iinclude/sdk/ee -I../src/cs4/Seimei/fslib" assembler_includes = f"-I{src_path.parent / 'include'} -I{src_path.parent}" common_includes = f"{compiler_includes} {assembler_includes}" game_compile_cmd = f"{WIBO} {game_cc_dir}/mwccps2.exe -c {common_includes} {COMPILER_FLAGS}" diff --git a/src/cs4/Seimei/fslib/lib/FsFont.h b/src/cs4/Seimei/fslib/lib/FsFont.h new file mode 100644 index 0000000..30a3cf9 --- /dev/null +++ b/src/cs4/Seimei/fslib/lib/FsFont.h @@ -0,0 +1,6 @@ +#ifndef FSFONT_H +#define FSFONT_H + +signed int FsFontSetTexBuf(signed int id /* r2 */, void * pTexFileBuf /* r2 */); + +#endif // FSFONT_H diff --git a/src/cs4/Seimei/src/Cdvd.h b/src/cs4/Seimei/src/Cdvd.h new file mode 100644 index 0000000..7d0a398 --- /dev/null +++ b/src/cs4/Seimei/src/Cdvd.h @@ -0,0 +1,115 @@ +#ifndef CDVD_H +#define CDVD_H + +/* + Compile unit: C:\cs4\Seimei\src\Cdvd.c + Producer: MW MIPS C Compiler + Language: C +*/ +static // total size: 0x14C +struct /* @anon1 */ { + // Members + // total size: 0x24 + struct /* @anon0 */ { + // Members + unsigned int lsn; // offset 0x0, size 0x4 + unsigned int size; // offset 0x4, size 0x4 + char name[16]; // offset 0x8, size 0x10 + unsigned char date[8]; // offset 0x18, size 0x8 + unsigned int flag; // offset 0x20, size 0x4 + } cdlfile; // offset 0x0, size 0x24 + char fname[128]; // offset 0x24, size 0x80 + char sname[128]; // offset 0xA4, size 0x80 + signed int state; // offset 0x124, size 0x4 + signed int fileType; // offset 0x128, size 0x4 + signed int index; // offset 0x12C, size 0x4 + signed int fhd; // offset 0x130, size 0x4 + signed int size; // offset 0x134, size 0x4 + signed int sector; // offset 0x138, size 0x4 + unsigned int align; // offset 0x13C, size 0x4 + unsigned char retry; // offset 0x140, size 0x1 + unsigned char unused[3]; // offset 0x141, size 0x3 + unsigned char * * lplpAddr; // offset 0x144, size 0x4 + unsigned char * lpDmyData; // offset 0x148, size 0x4 +} * lpSetCdvd; // size: 0x4, address: 0x257560 +static // total size: 0x14C +struct /* @anon1 */ { + // Members + // total size: 0x24 + struct /* @anon0 */ { + // Members + unsigned int lsn; // offset 0x0, size 0x4 + unsigned int size; // offset 0x4, size 0x4 + char name[16]; // offset 0x8, size 0x10 + unsigned char date[8]; // offset 0x18, size 0x8 + unsigned int flag; // offset 0x20, size 0x4 + } cdlfile; // offset 0x0, size 0x24 + char fname[128]; // offset 0x24, size 0x80 + char sname[128]; // offset 0xA4, size 0x80 + signed int state; // offset 0x124, size 0x4 + signed int fileType; // offset 0x128, size 0x4 + signed int index; // offset 0x12C, size 0x4 + signed int fhd; // offset 0x130, size 0x4 + signed int size; // offset 0x134, size 0x4 + signed int sector; // offset 0x138, size 0x4 + unsigned int align; // offset 0x13C, size 0x4 + unsigned char retry; // offset 0x140, size 0x1 + unsigned char unused[3]; // offset 0x141, size 0x3 + unsigned char * * lplpAddr; // offset 0x144, size 0x4 + unsigned char * lpDmyData; // offset 0x148, size 0x4 +} * lpLastCdvd; // size: 0x4, address: 0x257564 +static // total size: 0x14C +struct /* @anon1 */ { + // Members + // total size: 0x24 + struct /* @anon0 */ { + // Members + unsigned int lsn; // offset 0x0, size 0x4 + unsigned int size; // offset 0x4, size 0x4 + char name[16]; // offset 0x8, size 0x10 + unsigned char date[8]; // offset 0x18, size 0x8 + unsigned int flag; // offset 0x20, size 0x4 + } cdlfile; // offset 0x0, size 0x24 + char fname[128]; // offset 0x24, size 0x80 + char sname[128]; // offset 0xA4, size 0x80 + signed int state; // offset 0x124, size 0x4 + signed int fileType; // offset 0x128, size 0x4 + signed int index; // offset 0x12C, size 0x4 + signed int fhd; // offset 0x130, size 0x4 + signed int size; // offset 0x134, size 0x4 + signed int sector; // offset 0x138, size 0x4 + unsigned int align; // offset 0x13C, size 0x4 + unsigned char retry; // offset 0x140, size 0x1 + unsigned char unused[3]; // offset 0x141, size 0x3 + unsigned char * * lplpAddr; // offset 0x144, size 0x4 + unsigned char * lpDmyData; // offset 0x148, size 0x4 +} * lpCdvdRead; // size: 0x4, address: 0x257568 +// total size: 0x14C +struct /* @anon1 */ { + // Members + // total size: 0x24 + struct /* @anon0 */ { + // Members + unsigned int lsn; // offset 0x0, size 0x4 + unsigned int size; // offset 0x4, size 0x4 + char name[16]; // offset 0x8, size 0x10 + unsigned char date[8]; // offset 0x18, size 0x8 + unsigned int flag; // offset 0x20, size 0x4 + } cdlfile; // offset 0x0, size 0x24 + char fname[128]; // offset 0x24, size 0x80 + char sname[128]; // offset 0xA4, size 0x80 + signed int state; // offset 0x124, size 0x4 + signed int fileType; // offset 0x128, size 0x4 + signed int index; // offset 0x12C, size 0x4 + signed int fhd; // offset 0x130, size 0x4 + signed int size; // offset 0x134, size 0x4 + signed int sector; // offset 0x138, size 0x4 + unsigned int align; // offset 0x13C, size 0x4 + unsigned char retry; // offset 0x140, size 0x1 + unsigned char unused[3]; // offset 0x141, size 0x3 + unsigned char * * lplpAddr; // offset 0x144, size 0x4 + unsigned char * lpDmyData; // offset 0x148, size 0x4 +} CdvdReadSt[256]; // size: 0x14C00, address: 0xA01200 +unsigned int GlobalLoadTimer; // size: 0x4, address: 0x257894 + +#endif // CDVD_H diff --git a/src/cs4/Seimei/src/File.c b/src/cs4/Seimei/src/File.c index b80c698..a422641 100644 --- a/src/cs4/Seimei/src/File.c +++ b/src/cs4/Seimei/src/File.c @@ -1,19 +1,227 @@ +#include "File.h" +#include "Cdvd.h" +#include "Game.h" +#include "Model.h" +#include "Tex.h" +#include "Unk.h" #include "common.h" +#include "lib/FsFont.h" +#include "menu/KuonMenu.h" +#include "menu/Menu.h" -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", AfterFileReadFunc); +unsigned char ScePath[128]; // size: 0x80, address: 0x3254B0 +unsigned char RootPath[128]; // size: 0x80, address: 0x325530 -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", FileCheckAsyncBusy); +void AfterFileReadFunc(u8 *lpAddr, s32 index, s32 fileType, s32 size) { + switch (fileType) { + case 0x0: + case 0x80: + RemapBinderFile(lpAddr); + SetMdlBndFile(index); + if (GameSt.GameMode != 4) { + if (index == 0) { + MyAppear(0, &MdlMng[0xA0], VW, 0.0f); + } + if ((GameSt.PlayerId != 2) && (index == 0x10E)) { + InitMyWeapon(GameSt.PlayerId); + break; + } + } + break; + case 0x1: + RemapCamData(lpAddr); + break; -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", FileBackReadControl); + case 0x2: + RemapMapFile(lpAddr); + ReadMapMdlFile(lpAddr); + break; -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", FileReadControl); + case 0x3: + break; -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", FileRead); + case 0x4: + break; -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", InitFileRead); + case 0x5: + RemapMdlLink(lpAddr); + SetMdlData(index, lpAddr); + break; -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", GetRootPath); + case 0x6: + RemapMdlLink(lpAddr); + SetTexList(lpAddr, 2); + break; -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", ReadRootPath); + case 0x7: + RemapMdlLink(lpAddr); + SetTexList(lpAddr, 2); + FsFontSetTexBuf(2, + GetTexMngByName(GetGameTexList(), "iconfont.tm2")->lpTm2); + ItemMng_CallBack_FinishLoadFile_ItemList(); + break; -INCLUDE_ASM("config/asm/nonmatchings/cs4/Seimei/src/File", InitFileSystem); + case 0x8: + RemapMdlLink(lpAddr); + SetTexList(lpAddr, 1); + SetMapTexListFile(index); + break; + + case 0x9: + RemapMdlLink(lpAddr); + SetAniData(index, lpAddr); + break; + + case 0xA: + RemapMdlLink(lpAddr); + SetHdlData(index, lpAddr); + break; + + case 0xB: + RemapMdlLink(lpAddr); + SetSdlData(index, lpAddr); + break; + + case 0xC: + break; + + case 0xD: + RemapBinderFile(lpAddr); + Rta_BndReadEndCallBack(); + break; + + case 0xE: + break; + + case 0x11: + RemapMdlLink(lpAddr); + SetTexList(lpAddr, 1); + break; + + case 0xF: + break; + + case 0x10: + RemapBinderFile(lpAddr); + + if ((Menu_GetLastReloadType() & 0xFFFF) == 0x8000) { + Menu_FileReadCallBack(0x8000); + KnMenuMng_ReMakeMenu(KUON_MENU__MAKE_TITLE); + break; + } + + if ((Menu_GetLastReloadType(0x8000) & 0xFFFF) == 0x4000) { + Menu_FileReadCallBack(KUON_MENU_RELOAD_FLAG_GAME); + KnMenuMng_ReMakeMenu(KUON_MENU__MAKE_GAME); + SetFixMemoryEndIndex(); + break; + } + break; + + case 0x12: + SetScript(size, lpAddr); + break; + + case 0x13: + break; + + case 0x14: + break; + + case 0x81: + break; + + case 0x15: + RemapDramaData(lpAddr); + if (index != 0) { + SetDramaData(); + break; + } + break; + + case 0x16: + SND_LoadSecFromFileInMemory(index & 0xFF, lpAddr); + break; + + case 0x21: + SND_LoadRandSeFromFileInMemory(lpAddr); + break; + + case 0x17: + RemapBinderFile(lpAddr); + break; + + case 0x18: + SND_SqPrepare(); + break; + + case 0x19: + SetSceneBndFile(); + break; + + case 0x1A: + break; + + case 0x1B: + SfxFIO_CallBack_FinishLoadSfxBindFile(); + break; + + case 0x1C: + break; + + case 0x1D: + SetRiddleData(); + break; + + case 0x1E: + break; + + case 0x1F: + RemapMiz(lpAddr); + break; + + case 0x20: + break; + + case 0x82: + break; + } + return; +} + +int FileCheckAsyncBusy(void) { return CdvdCheckAsyncBusy(); } + +void FileBackReadControl(void) { CdvdBackReadProc(); } + +void FileReadControl(int bDraw) { CdvdAsyncProc(bDraw); } + +signed int FileRead(char *filename /* r2 */, signed int size /* r2 */, + unsigned char **lpBuf /* r2 */, + unsigned char bSynch /* r2 */, signed int index /* r2 */, + signed int filetype /* r2 */, unsigned int align /* r2 */) { + return CdvdRead(filename, size, lpBuf, bSynch, index, filetype, align); +} + +void InitFileRead(void) { InitCdvdRead(); } + +unsigned char *GetRootPath() { return RootPath; } + +void ReadRootPath() { + memset(RootPath, 0, 0x80); + memset(ScePath, 0, 0x80); + sprintf((char *)RootPath, "cdrom0:\\"); + sprintf((char *)ScePath, "cdrom0:\\"); +} + +void InitFileSystem() { + /* anonymous block */ + // Range: 0x138C20 -> 0x138C78 + + sceSifInitRpc(0); + sceSifInitIopHeap(); + sceSifLoadFileReset(); + sceFsReset(); + while (!sceCdInit(0)) { + } + sceCdMmode(2); +} \ No newline at end of file diff --git a/src/cs4/Seimei/src/File.h b/src/cs4/Seimei/src/File.h index dd81bc5..ebda138 100644 --- a/src/cs4/Seimei/src/File.h +++ b/src/cs4/Seimei/src/File.h @@ -1,6 +1,9 @@ #ifndef FILE_H #define FILE_H +extern unsigned char ScePath[128]; // size: 0x80, address: 0x3254B0 +extern unsigned char RootPath[128]; // size: 0x80, address: 0x325530 + void InitFileRead(); void InitFileSystem(); void ReadRootPath(); diff --git a/src/cs4/Seimei/src/Game.h b/src/cs4/Seimei/src/Game.h index 95acc98..53bbef6 100644 --- a/src/cs4/Seimei/src/Game.h +++ b/src/cs4/Seimei/src/Game.h @@ -52,4 +52,5 @@ extern GameState GameSt; // size: 0x30, address: 0x3A5E10 int GameLoop(); void InitGameData(); void InitGameDataAfterLoad(); +void * GetGameTexList(); #endif // GAME_H diff --git a/src/cs4/Seimei/src/Tex.h b/src/cs4/Seimei/src/Tex.h new file mode 100644 index 0000000..fa6cfff --- /dev/null +++ b/src/cs4/Seimei/src/Tex.h @@ -0,0 +1,68 @@ +#ifndef TEX_H +#define TEX_H + +/* + Compile unit: C:\cs4\Seimei\src\Tex.c + Producer: MW MIPS C Compiler + Language: C + Code range: 0x0013FA80 -> 0x0013FB18 +*/ +// Range: 0x13FA80 -> 0x13FB18 +// total size: 0x60 +struct /* @anon0 */ { + // Members + unsigned char bUse; // offset 0x0, size 0x1 + unsigned char unused0; // offset 0x1, size 0x1 + unsigned short id; // offset 0x2, size 0x2 + // total size: 0x20 + struct /* @anon1 */ { + // Members + unsigned int size; // offset 0x0, size 0x4 + unsigned int wnum; // offset 0x4, size 0x4 + unsigned char dummy[5]; // offset 0x8, size 0x5 + unsigned char compare; // offset 0xD, size 0x1 + unsigned short tex_id; // offset 0xE, size 0x2 + // total size: 0x8 + struct /* @anon5 */ { + // Members + unsigned long TBP0 : 14; // offset 0x0, size 0x4 + unsigned long TBW : 6; // offset 0x0, size 0x4 + unsigned long PSM : 6; // offset 0x0, size 0x4 + unsigned long TW : 4; // offset 0x0, size 0x4 + unsigned long TH : 4; // offset 0x0, size 0x4 + unsigned long TCC : 1; // offset 0x0, size 0x4 + unsigned long TFX : 2; // offset 0x0, size 0x4 + unsigned long CBP : 14; // offset 0x0, size 0x4 + unsigned long CPSM : 4; // offset 0x0, size 0x4 + unsigned long CSM : 1; // offset 0x0, size 0x4 + unsigned long CSA : 5; // offset 0x0, size 0x4 + unsigned long CLD : 3; // offset 0x0, size 0x4 + } tex0; // offset 0x10, size 0x8 + // total size: 0x8 + struct /* @anon7 */ { + // Members + unsigned long LCM : 1; // offset 0x0, size 0x4 + unsigned long pad01 : 1; // offset 0x0, size 0x4 + unsigned long MXL : 3; // offset 0x0, size 0x4 + unsigned long MMAG : 1; // offset 0x0, size 0x4 + unsigned long MMIN : 3; // offset 0x0, size 0x4 + unsigned long MTBA : 1; // offset 0x0, size 0x4 + unsigned long pad10 : 9; // offset 0x0, size 0x4 + unsigned long L : 2; // offset 0x0, size 0x4 + unsigned long pad21 : 11; // offset 0x0, size 0x4 + unsigned long K : 12; // offset 0x0, size 0x4 + unsigned long pad44 : 20; // offset 0x0, size 0x4 + } tex1; // offset 0x18, size 0x8 + } * lpTm2; // offset 0x4, size 0x4 + void * lpTexList; // offset 0x8, size 0x4 + signed int unused1[1]; // offset 0xC, size 0x4 + unsigned char texName[32]; // offset 0x10, size 0x20 + unsigned char texNameBase[32]; // offset 0x30, size 0x20 + signed int texW; // offset 0x50, size 0x4 + signed int texH; // offset 0x54, size 0x4 + signed int texTBW; // offset 0x58, size 0x4 + signed int texPSM; // offset 0x5C, size 0x4 +} * GetTexMngByName(void * lpTexList /* r18 */, char * texname /* r17 */); + + +#endif // TEX_H diff --git a/src/cs4/Seimei/src/Unk.h b/src/cs4/Seimei/src/Unk.h index ff0aeec..7dff7c1 100644 --- a/src/cs4/Seimei/src/Unk.h +++ b/src/cs4/Seimei/src/Unk.h @@ -31,6 +31,8 @@ extern struct /* @anon0 */ { unsigned short cam_id; // offset 0x6, size 0x2 }* lpSceneLoad; // size: 0x4, address: 0x257530 +static float VZ[4]; // size: 0x10, address: 0x228720 +static float VW[4]; // size: 0x10, address: 0x228730 #endif // UNK_H diff --git a/src/cs4/Seimei/src/menu/KuonMenu.h b/src/cs4/Seimei/src/menu/KuonMenu.h new file mode 100644 index 0000000..64670e8 --- /dev/null +++ b/src/cs4/Seimei/src/menu/KuonMenu.h @@ -0,0 +1,24 @@ +#ifndef KUONMENU_H +#define KUONMENU_H + +enum _KUON_MENU__RELOAD_FLAG_EN { + KUON_MENU_RELOAD_FLAG_NONE = 0, + KUON_MENU_RELOAD_FLAG_TITLE_ = 1, + KUON_MENU_RELOAD_FLAG_START = 2, + KUON_MENU_RELOAD_FLAG_MAIN = 4, + KUON_MENU_RELOAD_FLAG_SAVE = 8, + KUON_MENU_RELOAD_FLAG_LOAD = 16, + KUON_MENU_RELOAD_FLAG_DEAD = 32, + KUON_MENU_RELOAD_FLAG_MM_TOP = 64, + KUON_MENU_RELOAD_FLAG_MM_EQUIP = 128, + KUON_MENU_RELOAD_FLAG_MM_ITEM = 256, + KUON_MENU_RELOAD_FLAG_MM_BOOK = 512, + KUON_MENU_RELOAD_FLAG_MM_MAP = 1024, + KUON_MENU_RELOAD_FLAG_MM_SET = 2048, + KUON_MENU_RELOAD_FLAG_MM_END = 4096, + KUON_MENU_RELOAD_FLAG_DOC = 8192, + KUON_MENU_RELOAD_FLAG_GAME = 16384, + KUON_MENU_RELOAD_FLAG_TITLE = -32768, +}; + +#endif // KUONMENU_H diff --git a/src/cs4/Seimei/src/menu/Menu.h b/src/cs4/Seimei/src/menu/Menu.h new file mode 100644 index 0000000..b17b59c --- /dev/null +++ b/src/cs4/Seimei/src/menu/Menu.h @@ -0,0 +1,27 @@ +#ifndef MENU_H +#define MENU_H + +enum _KUON_MENU__TYPE_EN { + KUON_MENU__TEST_MENU = 0, + KUON_MENU__TITLE_MENU = 1, + KUON_MENU__START_MENU = 2, + KUON_MENU__MAIN_MENU = 3, + KUON_MENU__SAVE_MENU = 4, + KUON_MENU__LOAD_MENU = 5, + KUON_MENU__MAP_MENU = 6, + KUON_MENU__ITEM_MENU = 7, + KUON_MENU__MSG_MENU = 8, + KUON_MENU__SET_MENU = 9, + KUON_MENU__END_SAVE_MENU = 10, + KUON_MENU__DEAD_MENU = 11, + KUON_MENU__MENU_NUM = 12, + KUON_MENU__MAKE_NONE = 100, + KUON_MENU__MAKE_TITLE = 101, + KUON_MENU__MAKE_GAME = 102, + KUON_MENU__DMY_MENU = 999, +}; + + + + +#endif // MENU_H diff --git a/tools/python/annotate.py b/tools/python/annotate.py new file mode 100644 index 0000000..2f6e455 --- /dev/null +++ b/tools/python/annotate.py @@ -0,0 +1,240 @@ +''' +alessatool/annotate + +mark up assembly with line numbers. +can also be used to create full-tu scratches in decomp.me. + +example: + +alessatool annotate \ + --asm-path silent-hill-2/config/SLUS_202.28/asm/Effect2/hh_class_water_03.s \ + --tu + +run `alessatool annotate --help` for more information. +''' + +from sys import stdout, stdin +from pathlib import Path +from subprocess import run +from dataclasses import dataclass +from constants import * +from struct import unpack + +@dataclass +class AnnotationArgs: + vram_start: int | None + vram_end: int | None + elf_path: Path + asm_path: Path | None + out_path: Path + addr2line_path: Path + line_file_path: Path | None + tu: bool + encoding: str + stdout: bool + verbose: bool + +def annotate_asm(args: AnnotationArgs): + if args.asm_path: + with open(args.asm_path, "r") as asm_file: + asm_contents = asm_file.read() + else: + asm_contents = stdin.read() + + asm_lines = asm_contents.splitlines() + asm_line_index = 0 + + vram_start = args.vram_start + vram_end = args.vram_end + + if vram_start is None or vram_end is None: + vram_start, vram_end = find_vram_bounds(asm_lines) + + line_file_path = get_line_file_path(args) + + if line_file_path is None or not line_file_path.exists(): + addresses = (f"0x{v:X}" for v in range(vram_start, vram_end, 0x4)) + proc = run([args.addr2line_path, "-e", args.elf_path, *addresses], capture_output=True, encoding=args.encoding) + addr2line_output_lines = proc.stdout.splitlines() + else: + # parse the binary line number file. + + # the format is a list of u_shorts, one per line number. + # there should be one line number per vram address, and each vram + # address should be exactly 4 bytes apart, mirroring how the addr2line + # output is formatted + + with open(line_file_path, "rb") as line_file: + line_data = line_file.read() + line_numbers = unpack(f"<{len(line_data) // 2}H", line_data) + compile_unit = args.asm_path.with_suffix(".c").name + addr2line_output_lines = list(map(lambda n : to_addr2line_format(compile_unit, n), line_numbers)) + + main_tu_name = None + prev_tu_name = None + prev_line_number = -1 + function_count = 0 + is_in_function_label = False + is_in_text_section = False + current_vram_addr = vram_start + annotated_asm_lines = [] + + for addr_index in range(0, len(addr2line_output_lines) - 1): + line = addr2line_output_lines[addr_index] + + if line.startswith("?"): + current_vram_addr += 0x4 + continue + + separator_index = line.rfind(":") + current_line_number = int(line[separator_index+1:]) + + if current_line_number == prev_line_number: + current_vram_addr += 0x4 + continue + + vram_addr_str = f"{current_vram_addr:X}" + + current_tu_name = line[0:separator_index] + if current_tu_name != main_tu_name and current_tu_name != prev_tu_name: + if main_tu_name: + print(f"[warn] alessatool/annotate: address at {vram_addr_str} belongs to {current_tu_name}") + else: + main_tu_name = current_tu_name + prev_tu_name = current_tu_name + + while True: + if asm_line_index >= len(asm_lines): + raise AssertionError( + f"address 0x{current_vram_addr:X} not found in asm" + ) + + asm_line = asm_lines[asm_line_index] + + if asm_line.startswith(SECTION_DIRECTIVE): + is_in_text_section = asm_line.startswith(TEXT_SECTION_DIRECTIVE) + + if not is_in_text_section: + annotated_asm_lines.append(asm_line) + asm_line_index += 1 + continue + + if line_has_vram_addr(asm_line, vram_addr_str): + break + + should_append_asm_line = True + if args.tu: + asm_line_trimmed = asm_line.strip() + + # track when we go in & out of function symbols + if asm_line_trimmed.startswith(FUNCTION_SYMBOL_LABEL): + function_count += 1 + is_in_function_label = True + annotated_asm_lines.append(f"{UNIQUE_TEXT_SECTION_DIRECTIVE}{function_count}") + annotated_asm_lines.append("") + elif asm_line_trimmed.startswith(END_FUNCTION_SYMBOL_LABEL): + is_in_function_label = False + + # remove `nop`s + if not is_in_function_label and asm_line_trimmed.endswith("nop"): + should_append_asm_line = False + + # remove `macro.inc` include directive + elif asm_line_trimmed == INCLUDE_MACRO_INC_DIRECTIVE: + should_append_asm_line = False + + if should_append_asm_line: + annotated_asm_lines.append(asm_line) + + asm_line_index += 1 + + annotated_asm_lines.append(f"\t.loc 1 {current_line_number}") + annotated_asm_lines.append(asm_line) + asm_line_index += 1 + + prev_line_number = current_line_number + current_vram_addr += 0x4 + + while asm_line_index < len(asm_lines) - 1: + annotated_asm_lines.append(asm_lines[asm_line_index]) + asm_line_index += 1 + + assert main_tu_name, "no valid compilation unit found" + annotated_asm_lines = [ + ".section .debug", + ".previous", + ".text", + f".file 1 \"{main_tu_name}\"", + *annotated_asm_lines + ] + + append_final_new_line(annotated_asm_lines) + annotated_asm_contents = "\n".join(annotated_asm_lines) + + if not args.stdout and args.out_path: + with open(args.out_path, "w") as out_file: + out_file.write(annotated_asm_contents) + if args.verbose: + print(f"alessatool/annotate: wrote asm to {args.out_path}") + else: + stdout.write(annotated_asm_contents) + +def get_line_file_path(args: AnnotationArgs): + if args.line_file_path is not None: + return args.line_file_path + + if args.elf_path.name == SH2_SERIAL and args.asm_path and "Event/stage" in args.asm_path.as_posix(): + return Path(f"{TOOLS}/alessatool/dwarf") / Path(args.asm_path.name).with_suffix(".line") + + return None + +def to_addr2line_format(compile_unit: str, line_number: int): + return f"{compile_unit}:{line_number}" + +def find_vram_bounds(asm_lines: list[str]): + start = None + passed_first_label = False + end = None + + for line in asm_lines: + # assumes first address comment is on line after first label + if "label" in line and not passed_first_label: + passed_first_label = True + continue + + if passed_first_label and start is None: + start = get_vram_addr_from_line(line) + + # assumes .text section comes first + if line.startswith(SECTION_DIRECTIVE) and not line.startswith(TEXT_SECTION_DIRECTIVE): + break + + vram_addr = get_vram_addr_from_line(line) + if vram_addr is None: + continue + + end = vram_addr + + return (start, end) + +def get_vram_addr_from_line(line: str): + if "/*" not in line: + return None + + _before_comment, after_comment = line.split("/* ", 1) + _file_addr, vram_addr, _rest = after_comment.split(" ", 2) + + try: + return int(vram_addr, 0x10) + except: + return None + +def line_has_vram_addr(line: str, addr_str: str) -> bool: + if addr_str not in line or "*/" not in line: + return False + + return line.index("*/") > line.index(addr_str) + +def append_final_new_line(lines: list[str]): + if lines[-1] != "": + lines.append("")