Initial Support for new game version: Final Mix (#59)

* Initial Support for new game version: Final Mix

* Rephrased and renamed a few things
This commit is contained in:
Noah McQueen 2024-09-04 06:58:02 -06:00 committed by GitHub
parent 0c600c281f
commit 6b154ced98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 173 additions and 34 deletions

2
.gitignore vendored
View File

@ -11,7 +11,9 @@ undefined_funcs_auto.txt
.ninja_log
.splache
build.ninja
SLPS_251.05*
SLPS_251.98*
*.iso
*.bin

View File

@ -1,12 +1,41 @@
# Kingdom Hearts Decompilation
# Kingdom Hearts De:Compiled
We're currently just targeting the main game executable, which is an elf file `SLPS_251.05` with sha1 `9dabbf867a7ec2a030df99ba1ed969f2deef0488`.
A decompilation of the Playstation 2 releases of Kingdom Hearts.
## Supported Versions
| Game Version | ELF | Sha1 |
|--------------------------|---------------|------------------------------------------|
| Original Japanese | `SLPS_251.05` |`9dabbf867a7ec2a030df99ba1ed969f2deef0488`|
| Final Mix (JP Exclusive) | `SLPS_251.98` |`e70bda789916142aafb53d85cef2e806b35ad8d8`|
---
### Dependencies
We require some python deps you can obtain by running `pip install -U -r requirements.txt`.
Some python dependencies are required, which you can obtain by running `pip install -U -r requirements.txt`.
---
### Setup
1. Extract `SLPS_251.05` from an ISO of the Kingdom Hearts (JP) and place it in the root of the repo.
2. `./configure.py`
3. `ninja`
1. Extract the ELF file from an ISO of the game version you're targeting and place it in the root of the repo.
2. Run `./configure.py` to generate the build files.
- Optionally, specify the game version you're targeting with the `--version`/`-v` flag. Defaults to the original Japanese version if not specified.
- You can clear existing configurations with the `--clean`/`-c` flag
- eg: `./configure.py -c -v fm` will clear the existing build configuration and generate a new one for the Final Mix version.
3. Run `ninja` to build the project. Final output will be stored by version in the `build` directory.
---
### Notes
No game assets are published in this repository. This includes any files required to run the game, such as the game's executable, ISO, or any files extracted from it.
This repository targets the game's main executable, an elf file named uniquely by the game's serial number. A copy of the elf file is required to build the project, and must be provided by the user by extracting it from a legal copy of the game.
>[!WARNING]
> Additional asset extraction is currently only supported by the JP version of the game.
Additional assets that are not used in this project may optionally be extracted by running `./tools/iso/extract.py` on the game's ISO. The extracted files are in the `kingdom` directory.

View File

@ -1 +0,0 @@
9dabbf867a7ec2a030df99ba1ed969f2deef0488 build/SLPS_251.05

1
config/fm/checksum.sha1 Normal file
View File

@ -0,0 +1 @@
e70bda789916142aafb53d85cef2e806b35ad8d8 build/fm/SLPS_251.98

View File

@ -0,0 +1,33 @@
//=============================
// worldfile.c
//=============================
worldfile_getAbbr = 0x00112270; // type:func
worldfile_getNameNoSet = 0x00112298; // type:func
worldfile_getNames = 0x00112320; // type:func
worldfile_getBinImgName = 0x00112400; // type:func
//=============================
// worldfile.c
//=============================
worldAbbrs = 0x002BCC28; // size:0x58
worldDataExt = 0x002BCC80; // size:0x8
roomArchiveExt = 0x002BCC88; // size:0x8
D_002B91E0 = 0x002BCC90; // size:0x4
D_002B91E4 = 0x002BCC94; // size:0x4
D_002B91E8 = 0x002BCC98; // size:0x4
D_002B91EC = 0x002BCC9C; // size:0x4
D_002B8678 = 0x002BC128; // size:0x4
D_002B8680 = 0x002BC130; // size:0x4
worldDataFile = 0x002BCCA0; // size:0x40
worldBinImgFile = 0x002BCCE0; // size:0x40
roomArchiveRaw = 0x002BCD20; // size:0x40
roomArchiveFile = 0x002BCD60; // size:0x40
sprintf = 0x00250EB0; // type:func
strcat = 0x00250F40; // type:func
strcpy = 0x002511B0; // type:func

View File

1
config/jp/checksum.sha1 Normal file
View File

@ -0,0 +1 @@
9dabbf867a7ec2a030df99ba1ed969f2deef0488 build/jp/SLPS_251.05

View File

@ -0,0 +1,2 @@
D_555ED0 = 0x555ED0;
D_00555ED0 = 0x00555ED0;

48
config/kh.fm.yaml Normal file
View File

@ -0,0 +1,48 @@
name: Kingdom Hearts (Final Mix)
sha1: e70bda789916142aafb53d85cef2e806b35ad8d8
options:
basename: SLPS_251.98
target_path: SLPS_251.98
base_path: ..
compiler: EEGCC
find_file_boundaries: False
platform: ps2
create_undefined_funcs_auto: True
undefined_funcs_auto_path: config/fm/undefined_funcs_auto.txt
create_undefined_syms_auto: True
undefined_syms_auto_path: config/fm/undefined_syms_auto.txt
symbol_addrs_path: config/fm/symbol_addrs.txt
asm_path: asm
src_path: src
build_path: build/fm
extensions_path: tools/splat_ext
section_order: [".text", ".data", ".rodata", ".bss"]
auto_link_sections: [".data", ".rodata", ".bss"]
subalign: 8
disasm_unknown: True
named_regs_for_c_funcs: False
segments:
- [0, databin, elf_header]
- name: main
type: code
start: 0x80
vram: 0x100000
bss_size: 0x1E0F80
subsegments:
- [0x80, asm, crt0]
- [0x258, asm]
- [0x122F0, c, worldfile]
- [0x12518, asm]
- [0x172CF0, data]
- [0x1BCCA8, .data, worldfile]
- [0x1BCE20, data]
- [0x38CC90, rodata]
- [0x38CFF0, .rodata, worldfile]
- [0x38D088, rodata]
- [0x394100, databin] # .reginfo / segment_1.2
- [0x394118, databin]
- [0x3A0CC8]

View File

@ -3,21 +3,21 @@ sha1: 9dabbf867a7ec2a030df99ba1ed969f2deef0488
options:
basename: SLPS_251.05
target_path: SLPS_251.05
base_path: .
base_path: ..
compiler: EEGCC
find_file_boundaries: False
platform: ps2
create_undefined_funcs_auto: True
undefined_funcs_auto_path: undefined_funcs_auto.txt
undefined_funcs_auto_path: config/jp/undefined_funcs_auto.txt
create_undefined_syms_auto: True
undefined_syms_auto_path: undefined_syms_auto.txt
symbol_addrs_path: symbol_addrs.txt
undefined_syms_auto_path: config/jp/undefined_syms_auto.txt
symbol_addrs_path: config/jp/symbol_addrs.txt
asm_path: asm
src_path: src
build_path: build
build_path: build/jp
extensions_path: tools/splat_ext
section_order: [".text", ".data", ".rodata", ".bss"]
auto_all_sections: [".data", ".rodata", ".bss"]
auto_link_sections: [".data", ".rodata", ".bss"]
subalign: 8
disasm_unknown: True
named_regs_for_c_funcs: False
@ -689,7 +689,8 @@ segments:
- [0x38DA70, rodata, libm/sf_atan]
# - [0x38DAC4, rodata, libm/sf_floor]
- [0x38DAC8, rodata, libm/sf_scalbn]
- [0x38DAD8, rodata, lib/libmc]
- [0x38DAD8, .rodata, lib/libmc]
- [0x38DB60, rodata]
- [0x38DB80, databin] # .reginfo / segment_1.2
- [0x38DB98, databin]
- [0x39A680]

View File

@ -15,12 +15,14 @@ from splat.segtypes.linker_entry import LinkerEntry
ROOT = Path(__file__).parent.resolve()
TOOLS_DIR = ROOT / "tools"
YAML_FILE = "kh.jp.yaml"
VERSION = "jp"
BASENAME = "SLPS_251.05"
YAML_FILE = f"config/kh.{VERSION}.yaml"
LD_PATH = f"{BASENAME}.ld"
ELF_PATH = f"build/{BASENAME}"
MAP_PATH = f"build/{BASENAME}.map"
PRE_ELF_PATH = f"build/{BASENAME}.elf"
ELF_PATH = f"build/{VERSION}/{BASENAME}"
MAP_PATH = f"build/{VERSION}/{BASENAME}.map"
PRE_ELF_PATH = f"build/{VERSION}/{BASENAME}.elf"
COMMON_INCLUDES = "-Iinclude -isystem include/sdk/ee -isystem include/gcc"
@ -94,7 +96,9 @@ def build_stuff(linker_entries: List[LinkerEntry]):
# Rules
cross = "mips-linux-gnu-"
ld_args = f"-EL -T undefined_syms.txt -T undefined_syms_auto.txt -T undefined_funcs_auto.txt -Map $mapfile -T $in -o $out"
config = f"config/{VERSION}"
ld_args = f"-EL -T {config}/undefined_syms.txt -T {config}/undefined_syms_auto.txt -T {config}/undefined_funcs_auto.txt -Map $mapfile -T $in -o $out"
ninja.rule(
"as",
@ -179,13 +183,19 @@ def build_stuff(linker_entries: List[LinkerEntry]):
ninja.build(
ELF_PATH + ".ok",
"sha1sum",
"checksum.sha1",
f"config/{VERSION}/checksum.sha1",
implicit=[ELF_PATH],
)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Configure the project")
parser.add_argument(
"-v",
"--version",
help="Game version to configure for",
choices=["jp", "fm"],
)
parser.add_argument(
"-c",
"--clean",
@ -194,9 +204,28 @@ if __name__ == "__main__":
)
args = parser.parse_args()
if args.version:
VERSION = args.version
else:
VERSION = "jp"
BASENAME = {
"jp": "SLPS_251.05",
"fm": "SLPS_251.98",
}[VERSION]
LD_PATH = f"{BASENAME}.ld"
ELF_PATH = f"build/{VERSION}/{BASENAME}"
MAP_PATH = f"build/{VERSION}/{BASENAME}.map"
PRE_ELF_PATH = f"build/{VERSION}/{BASENAME}.elf"
if args.clean:
clean()
print(f"Kingdom Hearts De:Compiled ~ Generating build configuration for {VERSION}")
YAML_FILE = f"config/kh.{VERSION}.yaml"
split.main([YAML_FILE], modes="all", verbose=False)
linker_entries = split.linker_writer.entries

View File

@ -1,4 +1,4 @@
spimdisasm>=1.18.0
spimdisasm>=1.28.1
rabbitizer>=1.8.0
splat64>=0.21.0
splat64>=0.27.0
tqdm

View File

@ -29,12 +29,6 @@ typedef struct {
s32 semaidRegFunc;
s32 mcRunCmdNo;
// todo: rodata split, blocked by libc constants (0x38DB60 to rodata end)
char D_0048DA58[24]; // "bind error libmc \n";
char D_0048DA70[40]; // "libmc: too old release of mcserv.irx\n";
char D_0048DA98[40]; // "libmc: too old release of mcman.irx\n";
char D_0048DAC0[32]; // "sceMcUdCheckNewCard RPC faild\n";
sceSifClientData mcClientID;
SifParams mcSifParams;
McStatus mcStatus;
@ -66,7 +60,7 @@ s32 sceMcInit(void) {
while (TRUE) {
if (sceSifBindRpc(&mcClientID, 0x80000400, 0) < 0) {
printf(D_0048DA58);
printf("bind error libmc \n");
do {
/* infinite loop */
} while (TRUE);
@ -87,12 +81,12 @@ s32 sceMcInit(void) {
mcClientID.serve = NULL;
return sifServerStat - 100;
} else if (mcRetVal[1] < 0x20A) {
printf(D_0048DA70);
printf("libmc: too old release of mcserv.irx\n");
mcClientID.serve = NULL;
return sceMcIniOldMcserv;
} else {
if (mcRetVal[2] < 0x20E) {
printf(D_0048DA98);
printf("libmc: too old release of mcman.irx\n");
mcClientID.serve = NULL;
return sceMcIniOldMcman;
}
@ -344,7 +338,7 @@ INCLUDE_ASM("asm/nonmatchings/lib/libmc", sceMcGetInfo);
s32 sceMcUdCheckNewCard(void) {
if (sceSifCallRpc(&mcClientID, 0x35, 0, &mcSifParams, sizeof(mcSifParams), &mcRetVal, 4, NULL, NULL) != 0) {
printf(D_0048DAC0);
printf("sceMcUdCheckNewCard RPC faild\n");
return -1;
}
return mcRetVal[0];

View File

@ -141,11 +141,11 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("iso_path", help="Path to ISO file", type=Path)
parser.add_argument("out_dir", help="Path to output directory", type=Path)
args = parser.parse_args()
kingdom_files: List[KingdomFile] = []
filenames = get_filenames()
out_dir: str = "kingdom"
print("Reading ISO...\r", end="")
with open(args.iso_path, "rb") as f:
@ -167,7 +167,7 @@ if __name__ == "__main__":
for entry in kingdom_files:
if entry.filename is None:
entry.filename = f"unknown/{entry.hash:08X}.bin"
path: Path = args.out_dir / entry.filename
path: Path = out_dir / Path(entry.filename)
pbar.set_description("Extracting " + path.name.ljust(14))
iso_bytes.seek(cnf_start + entry.iso_block * BLOCK_LENGTH)
contents = iso_bytes.read(entry.length)