diff --git a/.gitignore b/.gitignore index 066f646..7415e30 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ undefined_syms_auto.txt # qemu-irix compilers tools/ido5.3_compiler/ tools/ido7.1_compiler/ + +# diffing directory +expected/ diff --git a/Makefile b/Makefile index d6510c5..dc36526 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,9 @@ O_FILES := $(foreach file,$(C_FILES),$(BUILD_DIR)/$(file:.c=.c.o)) \ $(foreach file,$(S_FILES),$(BUILD_DIR)/$(file:.s=.s.o)) \ $(foreach file,$(DATA_FILES),$(BUILD_DIR)/$(file:.bin=.bin.o)) \ -DEP_FILES := $(O_FILES:.o=.d) +DECOMP_C_OBJS := $(filter %.c.o,$(filter-out $(BUILD_DIR)/src/libultra%,$(O_FILES))) + +DEP_FILES := $(O_FILES:.o=.d) $(DECOMP_C_OBJS:.o=.asmproc.d) SPLAT_YAML := splat.yaml SPLAT = $(PYTHON) tools/n64splat/split.py $(SPLAT_YAML) @@ -245,7 +247,6 @@ build/src/libultra/io/gbpakreadwrite.c.o: CC := $(CC_OLD) build/src/libultra/io/gbpakselectbank.c.o: CC := $(CC_OLD) # run ASM-processor on non-libultra source files -DECOMP_C_OBJS := $(filter %.c.o,$(filter-out $(BUILD_DIR)/src/libultra%,$(O_FILES))) $(DECOMP_C_OBJS): CC := $(ASMPROC) $(ASMPROC_FLAGS) $(CC) -- $(AS) $(ASFLAGS) -- # turn off syntax checking errors for libultra diff --git a/diff_settings.py b/diff_settings.py index cc62fa5..7b9d48b 100644 --- a/diff_settings.py +++ b/diff_settings.py @@ -2,6 +2,6 @@ def apply(config, args): config['baseimg'] = 'baserom.z64' - config['myimg'] = 'pokemonstadium.z64' - config['mapfile'] = 'build/pokemonstadium.map' + config['myimg'] = 'build/pokestadium.z64' + config['mapfile'] = 'build/pokestadium.map' config['source_directories'] = ['.'] diff --git a/include/macros.h b/include/macros.h index b1558f1..d304614 100644 --- a/include/macros.h +++ b/include/macros.h @@ -28,7 +28,7 @@ // global typings typedef unsigned long uintptr_t; - +// cast thru uintptr_t for integer to pointer casts #define INT2VOID(i) ((void *)(uintptr_t)(i)) #define SCREEN_WIDTH 320 diff --git a/splat.yaml b/splat.yaml index 6afde13..2e9834c 100644 --- a/splat.yaml +++ b/splat.yaml @@ -36,7 +36,7 @@ segments: - [0x1F70, bin, noppad_1F70] # Extra nop align like earlier. Weird - [0x1F80, c, dp_intro] # dp intro code - [0x28E0, c, memmap] - - [0x2EC0, asm] # intro loader + - [0x2EC0, c, intro_loader] # intro loader - [0x3640, asm] # - [0x3A80, asm] # transition loader - [0x3FB0, asm] # PRES-JPEG decoder diff --git a/src/intro_loader.c b/src/intro_loader.c new file mode 100644 index 0000000..03f4eee --- /dev/null +++ b/src/intro_loader.c @@ -0,0 +1,319 @@ +#include +#include +#include "intro_loader.h" + +// local to this file +extern struct Unk800A6070 D_800A6070; + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" + +void func_800022C0(void *arg0, void *arg1) { + D_800A6070.unk20 = (void *)(ALIGN16((uintptr_t)arg0) + 0x10); + D_800A6070.unk24 = (void *)(((uintptr_t)arg1 & ~0xF) - 0x10); + D_800A6070.unk1C = (uintptr_t)D_800A6070.unk24 - (uintptr_t)D_800A6070.unk20; + D_800A6070.unk30 = NULL; + + D_800A6070.unk28 = D_800A6070.unk20 - 1; + D_800A6070.unk28->unk00 = NULL; + D_800A6070.unk28->unk04 = NULL; + D_800A6070.unk28->unk08 = NULL; + D_800A6070.unk28->unk0C = 0; + + D_800A6070.unk2C = D_800A6070.unk24; + D_800A6070.unk2C->unk00 = NULL; + D_800A6070.unk2C->unk04 = NULL; + D_800A6070.unk28->unk08 = NULL; + D_800A6070.unk28->unk0C = 0; + + osCreateMesgQueue(&D_800A6070.queue, D_800A6070.msgs, ARRAY_COUNT(D_800A6070.msgs)); + osSendMesg(&D_800A6070.queue, NULL, OS_MESG_NOBLOCK); +} + +struct UnkNodeThing *func_80002380(u32 arg0, s32 arg1) { + struct UnkNodeThing *node; + struct UnkNodeThing *ret; + + arg0 = ALIGN16(arg0) + sizeof(struct UnkNodeThing); + ret = NULL; + if ((arg0 != 0) && (D_800A6070.unk1C >= (uintptr_t)arg0)) { + if (arg1 == 0) { + D_800A6070.unk1C -= (uintptr_t)arg0; + node = (void *)((uintptr_t)D_800A6070.unk28 + (uintptr_t)arg0); + D_800A6070.unk28->unk04 = node; + node->unk00 = D_800A6070.unk28; + node->unk04 = NULL; + node->unk08 = 0; + node->unk0C = 0; + ret = D_800A6070.unk28; + D_800A6070.unk28 = node; + ret = ret + 1; + } else if (arg1 == 1) { + D_800A6070.unk1C -= (uintptr_t)arg0; + node = (void *)((uintptr_t)D_800A6070.unk2C - (uintptr_t)arg0); + D_800A6070.unk2C->unk00 = node; + node->unk04 = D_800A6070.unk2C; + node->unk00 = NULL; + node->unk08 = 0; + node->unk0C = 0; + D_800A6070.unk2C = node; + ret = node + 1; + } + } + return ret; +} + + +uintptr_t func_80002430(struct UnkNodeThing *arg0, s32 arg1); + +#ifdef NON_MATCHING +uintptr_t func_80002430(struct UnkNodeThing *arg0, s32 arg1) { + // struct UnkNodeThing *node; + struct UnkNodeThing *otherNode; + struct UnkNodeThing *prev; + uintptr_t priorAddr; + + priorAddr = (uintptr_t)(arg0 - 1); // v1 + + if (priorAddr < (uintptr_t)D_800A6070.unk28) { + do { + prev = D_800A6070.unk28->unk00; // t6 + D_800A6070.unk28 = prev; + if (arg1) { + if (prev->unk08 != NULL) { + prev->unk08(prev + 1, prev->unk0C); + } + } + D_800A6070.unk1C = D_800A6070.unk1C + (uintptr_t)D_800A6070.unk28->unk04 - (uintptr_t)D_800A6070.unk28; + D_800A6070.unk28->unk04 = NULL; + } while (priorAddr != (uintptr_t)D_800A6070.unk28); + } else { + otherNode = D_800A6070.unk2C; + if (priorAddr >= (uintptr_t)otherNode && priorAddr >= (uintptr_t)otherNode) { + do { + if (arg1) { + if (otherNode->unk08 != NULL) { + otherNode->unk08(otherNode + 1, otherNode->unk0C); + otherNode = D_800A6070.unk2C; + } + } + prev = otherNode->unk04; + D_800A6070.unk2C = prev; + D_800A6070.unk1C = D_800A6070.unk1C + (uintptr_t)prev - (uintptr_t)prev->unk00; + prev->unk00 = NULL; + otherNode = D_800A6070.unk2C; + } while (priorAddr >= (uintptr_t)otherNode); + } + } + + return func_80002764(); +} + +#else +#pragma GLOBAL_ASM("asm/nonmatchings/intro_loader/func_80002430.s") +#endif + +struct UnkNodeThing *func_8000254C(u32 arg0, s32 arg1, s32 arg2, void *arg3) { + struct UnkNodeThing *node; + + osRecvMesg(&D_800A6070.queue, NULL, OS_MESG_BLOCK); + node = func_80002380(arg0, arg1); + if (node != NULL) { + func_80002A14(node, arg2, arg3); + } + osSendMesg(&D_800A6070.queue, NULL, OS_MESG_NOBLOCK); + + return node; +} + +struct UnkNodeThing *func_800025C4(u32 arg0, s32 arg1) { + struct UnkNodeThing *node; + + osRecvMesg(&D_800A6070.queue, NULL, OS_MESG_BLOCK); + node = func_80002380(arg0, arg1); + osSendMesg(&D_800A6070.queue, NULL, OS_MESG_NOBLOCK); + return node; +} + +void func_80002620(struct UnkNodeThing *arg0) { + if (arg0 != NULL) { + osRecvMesg(&D_800A6070.queue, NULL, OS_MESG_BLOCK); + func_80002430(arg0, 1); + osSendMesg(&D_800A6070.queue, NULL, OS_MESG_NOBLOCK); + } + func_80002764(); +} + +#ifdef NON_MATCHING +struct UnkNodeThing *func_80002680(struct UnkNodeThing *arg0, uintptr_t arg1) { + uintptr_t diff; + struct UnkNodeThing *sp28; + struct UnkNodeThing *prior; + struct UnkNodeThing *priorPrev; + s32 sp20; + void *sp1C; + + + sp28 = NULL; + osRecvMesg(&D_800A6070.queue, NULL, OS_MESG_BLOCK); + // temp_v1 = arg0 - 0x10; + prior = arg0 - 1; + // temp_a0 = temp_v1->unk4; + priorPrev = prior->unk04; + // temp_v0 = temp_a0 - arg0; + diff = (uintptr_t)priorPrev - (uintptr_t)arg0; + + if (D_800A6070.unk28 == priorPrev) { + // uintptr_t aligned; + + // temp_a3 = (arg1 + 0xF) & ~0xF; + // aligned = ALIGN16(arg1); + arg1 = ALIGN16(arg1); + // if (temp_v0 < temp_a3) { + if (diff >= arg1 || D_800A6070.unk1C >= (arg1 - diff)) { + sp20 = prior->unk0C; + sp1C = prior->unk08; + func_80002430(arg0, 0); + sp28 = func_80002380(arg1, 0); + func_80002A14(sp28, sp20, sp1C); + } + } + osSendMesg(&D_800A6070.queue, NULL, OS_MESG_NOBLOCK); + return sp28; +} +#else +#pragma GLOBAL_ASM("asm/nonmatchings/intro_loader/func_80002680.s") +#endif + +// this is a weird type, so maybe it should be cast into UnkNodeThing? +uintptr_t func_80002764(void) { + s32 val = (s32)D_800A6070.unk1C - sizeof(struct UnkNodeThing); + + if (val < 0) { + val = 0; + } + + return val; +} + +void func_80002784(s32 arg0) { + struct UnkNodeThing *node; + struct UnkNodeThing *sp20; // prev? + struct UnkNodeThing *sp1C; // data? + uintptr_t sp18; // next? + + osRecvMesg(&D_800A6070.queue, NULL, OS_MESG_BLOCK); + sp18 = D_800A6070.unk1C; + sp20 = D_800A6070.unk28; + sp1C = D_800A6070.unk2C; + node = func_80002380(0x10, 0); + if (node != NULL) { + (node-1)->unk0C = arg0; + node->unk0C = (uintptr_t)D_800A6070.unk30; + node->unk00 = (struct UnkNodeThing *) sp18; + node->unk04 = sp20; + node->unk08 = (void *) sp1C; + D_800A6070.unk30 = node; + } + osSendMesg(&D_800A6070.queue, NULL, OS_MESG_NOBLOCK); + func_80002764(); +} + +void func_80002838(struct UnkNodeThing *arg0) { + struct UnkNodeThing *node; + struct UnkNodeThing *s0; + void *s2, *s3; + + s0 = arg0; + osRecvMesg(&D_800A6070.queue, NULL, OS_MESG_BLOCK); + while (TRUE) { + node = D_800A6070.unk30; + s2 = node->unk04; + s3 = node->unk08; + D_800A6070.unk1C = (uintptr_t)node->unk00; + D_800A6070.unk30 = INT2VOID(node->unk0C); + + if (s0 == NULL) { break; } + // odd non-matching, but it works + node = node - 1; + if ((uintptr_t)s0 == (uintptr_t)node->unk0C) { break; } + if (D_800A6070.unk30 == NULL) { break; } + } + + s0 = D_800A6070.unk2C; + while ((uintptr_t)s3 > (uintptr_t)s0) { + if (s0->unk08 != NULL) { + s0->unk08(s0 + 1, s0->unk0C); + } + s0 = s0->unk04; + } + + s0 = D_800A6070.unk28->unk00; + while ((uintptr_t)s2 <= (uintptr_t)s0) { + if (s0->unk08 != NULL) { + s0->unk08(s0 + 1, s0->unk0C); + } + s0 = s0->unk00; + } + + D_800A6070.unk28 = s2; + D_800A6070.unk2C = s3; + osSendMesg(&D_800A6070.queue, NULL, OS_MESG_NOBLOCK); + func_80002764(); +} + +#ifdef MIPS_TO_C +// unused function? +struct UnkNodeThing *func_80002960(uintptr_t arg0, s32 *arg1) { + struct UnkNodeThing *node; // v0? + struct UnkNodeThing *otherNode; + + node = D_800A6070.unk28->unk00; + while (node != NULL) { + otherNode = node->unk04; + if (arg0 >= (uintptr_t)(node + 1) && arg0 < (uintptr_t)otherNode) { + if (arg1 != NULL) { + *arg1 = node->unk0C; + } + return (node + 1); + } + node = node->unk00; + } + // L800029B8 + node = D_800A6070.unk2C; + otherNode = node->unk04; + while (otherNode != NULL) { + if (arg0 >= (uintptr_t)(node + 1) && arg0 < (uintptr_t)otherNode) { + if (arg1 != NULL) { + *arg1 = node->unk0C; + } + return node + 1; + } + node = otherNode; + otherNode = otherNode->unk04; + } + // L80002A08 + return NULL; +} +#else +#pragma GLOBAL_ASM("asm/nonmatchings/intro_loader/func_80002960.s") +#endif + +void func_80002A14(struct UnkNodeThing *arg0, s32 arg1, void *arg2) { + struct UnkNodeThing *node = INT2VOID((uintptr_t)arg0 - sizeof(struct UnkNodeThing)); + node->unk08 = arg2; + node->unk0C = arg1; +} + +uintptr_t func_80002A24(struct UnkNodeThing *arg0) { + struct UnkNodeThing *prior = arg0 - 1; + + return (uintptr_t)prior->unk04 - (uintptr_t)arg0; +} + +struct Unk800A6070 *func_80002A30(void) { + return &D_800A6070; +} + +#pragma GCC diagnostic pop diff --git a/src/intro_loader.h b/src/intro_loader.h new file mode 100644 index 0000000..e5c9dd9 --- /dev/null +++ b/src/intro_loader.h @@ -0,0 +1,51 @@ +#ifndef _SRC_INTRO_LOADER_H_ +#define _SRC_INTRO_LOADER_H_ + +#include +#include +#include + +struct UnkNodeThing { + /* 0x00 */ struct UnkNodeThing *unk00; + /* 0x04 */ struct UnkNodeThing *unk04; + // this looks like it can be any type of pointer? + // or are there multiple list node types in a union? + /* 0x08 */ void (*unk08)(struct UnkNodeThing *, s32); + /* 0x0C */ s32 unk0C; +}; // sizeof == 0x10 + +// "known" types for +08 and +0C: +// fn pointer +// void (*unk08)(struct UnkNodeThing *, s32 +// s32 unk0C +// more nodes +// struct UnkNodeThing *unk08 +// struct UnkNodeThing *unk0C + +struct Unk800A6070 { + /* 0x00 */ OSMesg msgs[1]; + /* 0x04 */ OSMesgQueue queue; + /* 0x1C */ uintptr_t unk1C; + // these pointers might be to different variants of this struct + // or, they could be to different lists... + /* 0x20 */ struct UnkNodeThing *unk20; + /* 0x24 */ struct UnkNodeThing *unk24; + /* 0x28 */ struct UnkNodeThing *unk28; + /* 0x2C */ struct UnkNodeThing *unk2C; + /* 0x30 */ struct UnkNodeThing *unk30; +}; // sizeof >= 0x34 + +// functions +void func_800022C0(void *arg0, void *arg1); +struct UnkNodeThing *func_8000254C(u32 arg0, s32 arg1, s32 arg2, void *arg3); +struct UnkNodeThing *func_800025C4(u32 arg0, s32 arg1); +void func_80002620(struct UnkNodeThing *arg0); +struct UnkNodeThing *func_80002680(struct UnkNodeThing *arg0, uintptr_t arg1); +uintptr_t func_80002764(void); +void func_80002784(s32 arg0); +void func_80002838(struct UnkNodeThing *arg0); +void func_80002A14(struct UnkNodeThing *, s32, void *); +uintptr_t func_80002A24(struct UnkNodeThing *arg0); +struct Unk800A6070 *func_80002A30(void); + +#endif /* _SRC_INTRO_LOADER_H_ */ diff --git a/utils/m2ctx.py b/utils/m2ctx.py new file mode 100755 index 0000000..e7d93b3 --- /dev/null +++ b/utils/m2ctx.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +import argparse +import os +import shutil +import sys +import subprocess +from pathlib import Path + +# Project-specific +# assuming that this is in tools +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +ROOT_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "..")) +CPP_FLAGS = [ + "-undef", + "-D__sgi", + "-D_LANGUAGE_C", + "-DF3DEX_GBI_2", + "-DNDEBUG", + "-D_MIPS_SZLONG=32", + "-DSCRIPT(...)={}" + "-D__attribute__(...)=", + "-D__asm__(...)=", + "-DGLOBAL_ASM(...)=", + "-ffreestanding", + "-nostdinc", + "-Iinclude", + "-Iinclude/PR", + "-Iinclude/audio", + "-Isrc" +] + +def import_c_file(in_file) -> str: + in_file = os.path.relpath(in_file, ROOT_DIR) + # Prefer clang as C preprocessor if installed on the system + if shutil.which('clang') is not None: + cpp = ['clang', '-E', '-P', '-x', 'c', '-Wno-trigraphs'] + else: + cpp = ['gcc', '-E', '-P'] + + cpp_getdefines = [*cpp, "-dM", *CPP_FLAGS, in_file] + cpp_procfile = [*cpp, *CPP_FLAGS, in_file] + + out_text = "" + try: + out_text += subprocess.check_output(cpp_getdefines, cwd=ROOT_DIR, encoding="utf-8") + out_text += subprocess.check_output(cpp_procfile, cwd=ROOT_DIR, encoding="utf-8") + except subprocess.CalledProcessError: + print( + "Failed to preprocess input file, when running command:\n", + cpp_getdefines, + file=sys.stderr, + ) + sys.exit(1) + + if not out_text: + print("Output is empty - aborting") + sys.exit(1) + return out_text + +def main(): + parser = argparse.ArgumentParser( + description="Create a context file which can be used for m2c" + ) + parser.add_argument( + "c_file", + help="File from which to create context", + ) + parser.add_argument( + "ctx_file", + nargs='?', + help="save context to file if present, otherwise print to stdout" + ) + args = parser.parse_args() + + output = import_c_file(args.c_file) + + if args.ctx_file is not None: + with open(os.path.join(ROOT_DIR, args.ctx_file), "w", encoding="UTF-8") as f: + f.write(output) + else: + print(output) + +if __name__ == "__main__": + main() \ No newline at end of file