summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcadmic <cadmic24@gmail.com>2024-09-07 11:37:15 -0700
committerGitHub <noreply@github.com>2024-09-08 03:37:15 +0900
commite52d135e15be227326b58d75d6779fbb1db6f232 (patch)
treece30d6c22b9e6f213ff5047c7174e4ba674dda4f
parentc27b83ac29a7c143ab8193682355cfab44f86f63 (diff)
[ntsc-1.2] Import n64dd code from Decompollaborate/n64dd (#2136)
* Import n64dd code from Decompollaborate/n64dd Co-authored-by: Anghelo Carvajal <angheloalf95@gmail.com> Co-authored-by: Elliptic Ellipsis <elliptic.ellipsis@gmail.com> * Don't stub out missing textures * Remove @brief * Add low-effort file comments * Match OoT if style * Fix n64dd matching due to int vs long * Fix English n64dd error textures * Compress n64dd segment * Align n64dd to 0x40 --------- Co-authored-by: Anghelo Carvajal <angheloalf95@gmail.com> Co-authored-by: Elliptic Ellipsis <elliptic.ellipsis@gmail.com>
-rw-r--r--assets/xml/n64dd/error_textures.xml8
-rw-r--r--baseroms/ntsc-1.2/config.yml4
-rwxr-xr-xextract_assets.py2
-rw-r--r--include/functions.h2
-rw-r--r--include/n64dd.h80
-rw-r--r--include/variables.h1
-rw-r--r--include/z64.h4
-rw-r--r--spec13
-rw-r--r--src/n64dd/n64dd_801C8000.c345
-rw-r--r--src/n64dd/n64dd_801C8940.c398
-rw-r--r--src/n64dd/n64dd_801C9440.c263
-rw-r--r--src/n64dd/n64dd_801C9B70.c172
-rw-r--r--src/n64dd/n64dd_801CA0B0.c243
-rw-r--r--src/n64dd/n64dd_data_buffer.c5
-rw-r--r--src/n64dd/n64dd_error_bodies.c47
-rw-r--r--src/n64dd/n64dd_error_headers.c5
-rw-r--r--src/n64dd/n64dd_error_textures.c23
-rw-r--r--src/n64dd/z_n64dd.c584
-rw-r--r--src/overlays/actors/ovl_Fishing/z_fishing.c2
-rw-r--r--tools/disasm/ntsc-1.2/variables.txt2
-rwxr-xr-xtools/fix_bss.py1
-rw-r--r--undefined_syms.txt25
22 files changed, 2199 insertions, 30 deletions
diff --git a/assets/xml/n64dd/error_textures.xml b/assets/xml/n64dd/error_textures.xml
new file mode 100644
index 000000000..6db2ab37d
--- /dev/null
+++ b/assets/xml/n64dd/error_textures.xml
@@ -0,0 +1,8 @@
+<Root>
+ <File Name="n64dd" OutName="n64dd_error_textures">
+ <Texture Name="gN64DDError41JPNTex" OutName="n64dd_error_41_jpn" Format="i4" Width="192" Height="16" Offset="0x0"/>
+ <Texture Name="gN64DDError41ENGTex" OutName="n64dd_error_41_eng" Format="i4" Width="192" Height="16" Offset="0x600"/>
+ <Texture Name="gN64DDPleaseReadManualJPNTex" OutName="n64dd_please_read_manual_jpn" Format="i4" Width="320" Height="64" Offset="0xC00"/>
+ <Texture Name="gN64DDPleaseReadManualENGTex" OutName="n64dd_please_read_manual_eng" Format="i4" Width="320" Height="64" Offset="0x3400"/>
+ </File>
+</Root>
diff --git a/baseroms/ntsc-1.2/config.yml b/baseroms/ntsc-1.2/config.yml
index a64d9547e..306b95348 100644
--- a/baseroms/ntsc-1.2/config.yml
+++ b/baseroms/ntsc-1.2/config.yml
@@ -75,6 +75,10 @@ assets:
xml_path: assets/xml/code/fbdemo_wipe1.xml
start_offset: 0xEB610
end_offset: 0xEBFA0
+- name: n64dd/error_textures
+ xml_path: assets/xml/n64dd/error_textures.xml
+ start_offset: 0xC120
+ end_offset: 0x11D20
- name: misc/link_animetion
xml_path: assets/xml/misc/link_animetion.xml
- name: misc/z_select_static
diff --git a/extract_assets.py b/extract_assets.py
index 905004cfd..da9715a5a 100755
--- a/extract_assets.py
+++ b/extract_assets.py
@@ -32,7 +32,7 @@ def ExtractFile(assetConfig: version_config.AssetConfig, outputPath: Path, outpu
execStr = f"{zapdPath} e -eh -i {xmlPath} -b {globalBaseromSegmentsDir} -o {outputPath} -osf {outputSourcePath} -gsf 1 -rconf {configPath} --cs-float both {ZAPDArgs}"
- if name.startswith("code/") or name.startswith("overlays/"):
+ if name.startswith("code/") or name.startswith("n64dd/") or name.startswith("overlays/"):
assert assetConfig.start_offset is not None
assert assetConfig.end_offset is not None
diff --git a/include/functions.h b/include/functions.h
index 403cd89b2..ee7a203a6 100644
--- a/include/functions.h
+++ b/include/functions.h
@@ -1395,6 +1395,8 @@ void Audio_SetCutsceneFlag(s8 flag);
void Audio_PlaySfxIfNotInCutscene(u16 sfxId);
void func_800F6964(u16);
void Audio_StopBgmAndFanfare(u16 fadeOutDuration);
+void func_800F6B3C(void);
+void func_800F6BDC(void);
void Audio_PreNMI(void);
void Audio_SetNatureAmbienceChannelIO(u8 channelIdxRange, u8 ioPort, u8 ioData);
void Audio_PlayNatureAmbienceSequence(u8 natureAmbienceId);
diff --git a/include/n64dd.h b/include/n64dd.h
index 4f117f682..6931eac26 100644
--- a/include/n64dd.h
+++ b/include/n64dd.h
@@ -2,6 +2,7 @@
#define N64DD_H
#include "ultra64.h"
+#include "ultra64/leo.h"
#include "z64pause.h"
#include "z64scene.h"
#include "z64map_mark.h"
@@ -59,6 +60,35 @@ typedef struct n64ddStruct_80121220 {
s32 (*unk_78)(struct PlayState*, void*, void*);
} n64ddStruct_80121220; // size = ?
+typedef struct struct_801E0D18 {
+ /* 0x00 */ LEOCmd unk_00;
+ /* 0x1C */ OSMesgQueue unk_1C;
+ /* 0x38 */ LEODiskID diskId;
+ /* 0x58 */ UNK_TYPE unk_58;
+ /* 0x5C */ UNK_TYPE unk_5C;
+ /* 0x60 */ u32 unk_60;
+ /* 0x64 */ u8 unk_64;
+ /* 0x65 */ u8 unk_65;
+ /* 0x66 */ u8 unk_66;
+ /* 0x68 */ s32 unk_68;
+ /* 0x6C */ s32 unk_6C;
+} struct_801E0D18; // size = 0x70
+
+typedef struct struct_801D9D50 {
+ /* 0x00 */ u8 unk_00; // command enum
+ /* 0x04 */ s32 unk_04;
+ /* 0x08 */ u8 unk_08;
+ /* 0x0C */ void (*unk_0C)(void*, void*, void*);
+ /* 0x10 */ s32 unk_10;
+ /* 0x14 */ void (*unk_14)(void*, uintptr_t, size_t);
+ /* 0x18 */ void* unk_18;
+ /* 0x1C */ void* unk_1C; // either OSMesgQueue* (command 0) or integer LBA (commands 2 and 3)
+ /* 0x20 */ void* unk_20; // either OSMesgQueue* (command 0) or integer byte size (commands 3 and 4)
+ /* 0x24 */ OSId unk_24;
+ /* 0x28 */ void* unk_28;
+ /* 0x2C */ OSPri unk_2C;
+} struct_801D9D50; // size = 0x30
+
void func_800AD410(void);
void func_800AD488(void);
n64ddStruct_80121220* func_800AD4C0(n64ddStruct_80121220* arg0);
@@ -77,6 +107,35 @@ void func_801C7C1C(void* dest, s32 offset, s32 size);
void func_801C7E78(void);
void n64dd_SetDiskVersion(s32 arg0);
+s32 func_801C8000(struct_801D9D50* arg0);
+s32 func_801C81C4(void);
+void func_801C81EC(struct_801E0D18* arg0);
+void func_801C8298(struct_801E0D18* arg0);
+void func_801C82E0(struct_801E0D18* arg0);
+void func_801C832C(struct_801E0D18* arg0);
+void func_801C83A0(struct_801E0D18* arg0);
+void func_801C8414(struct_801E0D18* arg0);
+s32 func_801C873C(struct_801E0D18* arg0);
+
+void func_801C8AA8(void);
+s32 func_801C91E0(struct_801E0D18*);
+s32 func_801C9260(struct_801E0D18*);
+s32 func_801C9334(struct_801E0D18*);
+s32 func_801C93C4(struct_801E0D18*);
+
+void func_801C94F8(u8* arg0, u16 arg1);
+void func_801C9A10(u8* arg0, s32 arg1, u8* str);
+void func_801C9B50(s32 arg0, void (*arg1)(void*, uintptr_t, size_t));
+
+u8* func_801C9E28(s32 errorNum);
+u8* func_801C9EC0(void);
+u8* func_801C9F90(s32 errorNum);
+u8* func_801C9FFC(void);
+u8* func_801CA030(s32 errorNum);
+u8* func_801CA070(void);
+
+void func_801CA1F0(void* charTexBuf, s32 posX, s32 posY, s32 dx, s32 dy, s32 cy, void* frameBuf, s32 screenWidth);
+
extern n64ddStruct_800FEE70_pointers D_800FEE70;
extern n64ddStruct_80121220* B_80121220;
@@ -86,4 +145,25 @@ extern u8 D_80121212;
extern vu8 D_80121213;
extern vu8 D_80121214;
+extern s32 (*D_801D2E54)(struct_801E0D18*);
+
+extern u8 B_801DC000[];
+
+extern s32 D_801D2E90;
+extern OSMesgQueue* B_801E0D10[2];
+
+extern s32 D_801D2EA0;
+extern s32 D_801D2EA8;
+extern s32 B_801E0F60;
+extern s32 B_801E0F64;
+extern void (*D_801D2EB4)(void*, void*, void*);
+
+// Error messages
+extern const char* D_801D2ED0[]; // "Error Number " array
+extern const char* D_801D2EE0[2][8][4]; // Array of error message strings
+
+// Error textures
+extern u64 gN64DDError41Texs[2][0x600 / sizeof(u64)];
+extern u64 gN64DDPleaseReadManualTexs[2][0x2800 / sizeof(u64)];
+
#endif
diff --git a/include/variables.h b/include/variables.h
index bf4fb15c5..dc01f712d 100644
--- a/include/variables.h
+++ b/include/variables.h
@@ -203,6 +203,7 @@ extern PreNmiBuff* gAppNmiBufferPtr;
extern uintptr_t gSegments[NUM_SEGMENTS];
extern Scheduler gScheduler;
extern PadMgr gPadMgr;
+extern IrqMgr gIrqMgr;
extern volatile OSTime gAudioThreadUpdateTimeTotalPerGfxTask;
extern volatile OSTime gGfxTaskSentToNextReadyMinusAudioThreadUpdateTime;
extern volatile OSTime gRSPAudioTimeTotal;
diff --git a/include/z64.h b/include/z64.h
index 7a6187a48..377a760ce 100644
--- a/include/z64.h
+++ b/include/z64.h
@@ -76,6 +76,8 @@
#define THREAD_PRI_DMAMGR_LOW 10 // Used when decompressing files
#define THREAD_PRI_GRAPH 11
#define THREAD_PRI_AUDIOMGR 12
+#define THREAD_PRI_N64DD 13
+#define THREAD_PRI_DDMSG 13
#define THREAD_PRI_PADMGR 14
#define THREAD_PRI_MAIN 15
#define THREAD_PRI_SCHED 15
@@ -90,6 +92,8 @@
#define THREAD_ID_GRAPH 4
#define THREAD_ID_SCHED 5
#define THREAD_ID_PADMGR 7
+#define THREAD_ID_N64DD 8
+#define THREAD_ID_DDMSG 9
#define THREAD_ID_AUDIOMGR 10
#define THREAD_ID_DMAMGR 18
#define THREAD_ID_IRQMGR 19
diff --git a/spec b/spec
index 378485e84..0f9befd96 100644
--- a/spec
+++ b/spec
@@ -788,7 +788,18 @@ endseg
#if PLATFORM_N64
beginseg
name "n64dd"
- // TODO: remaining n64dd files
+ compress
+ align 0x40
+ include "$(BUILD_DIR)/src/n64dd/z_n64dd.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_data_buffer.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_801C8000.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_801C8940.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_801C9440.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_801C9B70.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_error_headers.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_error_bodies.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_error_textures.o"
+ include "$(BUILD_DIR)/src/n64dd/n64dd_801CA0B0.o"
include "$(BUILD_DIR)/src/libleo/api/readwrite.o"
include "$(BUILD_DIR)/src/libleo/leo/leofunc.o"
include "$(BUILD_DIR)/src/libleo/leo/leoram.o"
diff --git a/src/n64dd/n64dd_801C8000.c b/src/n64dd/n64dd_801C8000.c
new file mode 100644
index 000000000..3bf7159aa
--- /dev/null
+++ b/src/n64dd/n64dd_801C8000.c
@@ -0,0 +1,345 @@
+// Does some command processing
+#include "global.h"
+#include "n64dd.h"
+#include "versions.h"
+
+void func_801C8554(void);
+void func_801C8578(void* arg0, void* arg1, OSId id, void* sp, OSPri pri);
+void func_801C8638(void (*arg0)(void*, void*, void*), s32 arg1, void (*arg2)(void*, uintptr_t, size_t));
+void func_801C868C(void* arg0, void* arg1, void* arg2, u8 arg3);
+s8 func_801C8770(void);
+s32 func_801C87C0(void);
+s32 func_801C87FC(void);
+s32 func_801C8844(void);
+s32 func_801C885C(void);
+s32 func_801C88AC(void);
+s32 func_801C88FC(void);
+
+s32 D_801D2E60 = 0;
+s32 D_801D2E64 = 0;
+
+typedef struct struct_801D2E68 {
+ /* 0x0 */ void (*unk_0)(struct_801E0D18*);
+ /* 0x4 */ s32 (*unk_4)(struct_801E0D18*);
+} struct_801D2E68; // size = 0x8
+
+struct_801D2E68 D_801D2E68[5] = {
+ { func_801C81EC, func_801C91E0 }, { func_801C832C, func_801C9260 }, { func_801C83A0, func_801C9260 },
+ { func_801C8414, func_801C9334 }, { func_801C8414, func_801C93C4 },
+};
+
+s32 D_801D2E90 = 0;
+
+OSMesgQueue* B_801E0D10[2];
+struct_801E0D18 B_801E0D18;
+OSMesg B_801E0D88[1];
+OSMesg B_801E0D90[8];
+OSThread B_801E0DB0;
+
+s32 func_801C8000(struct_801D9D50* arg0) {
+ switch (arg0->unk_00) {
+ case 0:
+ if (func_801C8844() != 0) {
+ return 1;
+ }
+ func_801C8554();
+ break;
+ case 1:
+ func_801C8578(arg0->unk_1C, arg0->unk_20, arg0->unk_24, arg0->unk_28, arg0->unk_2C);
+ break;
+ case 2:
+ func_801C8638(arg0->unk_0C, arg0->unk_10, arg0->unk_14);
+ break;
+ case 3:
+ if (func_801C8844() != 0) {
+ return 1;
+ }
+ func_801C868C(arg0->unk_18, arg0->unk_1C, arg0->unk_20, 1);
+ break;
+ case 4:
+ if (func_801C8844() != 0) {
+ return 1;
+ }
+ func_801C868C(arg0->unk_18, arg0->unk_1C, arg0->unk_20, 2);
+ break;
+ case 5:
+ arg0->unk_08 = func_801C8770();
+ return arg0->unk_08;
+ case 7:
+ return func_801C87FC();
+ case 6:
+ return func_801C8844();
+ case 8:
+ arg0->unk_04 = func_801C87C0();
+ return arg0->unk_04;
+ case 9:
+ return func_801C885C();
+ case 10:
+ return func_801C88AC();
+ case 11:
+ return func_801C88FC();
+ case 12:
+ D_801D2EA0 = 0;
+ break;
+ case 13:
+ D_801D2EA0 = 1;
+ break;
+ }
+ return 0;
+}
+
+void func_801C819C(UNK_TYPE arg0) {
+ if (arg0 != 0) {
+ D_801D2E60 = 1;
+ } else {
+ D_801D2E60 = 2;
+ }
+}
+
+s32 func_801C81C4(void) {
+ return D_801D2E60;
+}
+
+s32 func_801C81D4(void) {
+ return D_801D2E64 == 1;
+}
+
+void func_801C81EC(struct_801E0D18* arg0) {
+ osCreateMesgQueue(&arg0->unk_1C, B_801E0D88, ARRAY_COUNT(B_801E0D88));
+
+ if (gCurrentRegion == 1) {
+ arg0->unk_68 = LeoCJCreateLeoManager(LEO_PRIORITY_WRK, LEO_PRIORITY_INT, B_801E0D90, ARRAY_COUNT(B_801E0D90));
+ } else {
+ arg0->unk_68 = LeoCACreateLeoManager(LEO_PRIORITY_WRK, LEO_PRIORITY_INT, B_801E0D90, ARRAY_COUNT(B_801E0D90));
+ }
+
+ if ((arg0->unk_68 == LEO_ERROR_DEVICE_COMMUNICATION_FAILURE) || (arg0->unk_68 == LEO_ERROR_GOOD)) {
+ D_801D2E64 = 1;
+ }
+}
+
+void func_801C8298(struct_801E0D18* arg0) {
+ LEOCmd sp1C;
+
+ // TODO: passing a pointer as a logical block address?
+ LeoSeek(&sp1C, (u32)&arg0->diskId, &arg0->unk_1C);
+ osRecvMesg(&arg0->unk_1C, (OSMesg*)&arg0->unk_68, OS_MESG_BLOCK);
+}
+
+void func_801C82E0(struct_801E0D18* arg0) {
+ LEOCmd sp1C;
+
+ LeoSpdlMotor(&sp1C, 4, &arg0->unk_1C);
+ osRecvMesg(&arg0->unk_1C, (OSMesg*)&arg0->unk_68, OS_MESG_BLOCK);
+}
+
+void func_801C832C(struct_801E0D18* arg0) {
+ s32 sp34;
+ s32 startLBA = arg0->unk_5C;
+
+ if (LeoByteToLBA(startLBA, arg0->unk_60, &sp34) == LEO_ERROR_GOOD) {
+ OSMesgQueue* sp28 = &arg0->unk_1C;
+
+ LeoReadWrite(&arg0->unk_00, OS_READ, startLBA, (void*)arg0->unk_58, sp34, sp28);
+ osRecvMesg(sp28, (OSMesg*)&arg0->unk_68, OS_MESG_BLOCK);
+ }
+}
+
+void func_801C83A0(struct_801E0D18* arg0) {
+ s32 sp34;
+ s32 startLBA = arg0->unk_58;
+
+ if (LeoByteToLBA(startLBA, arg0->unk_60, &sp34) == LEO_ERROR_GOOD) {
+ OSMesgQueue* sp28 = &arg0->unk_1C;
+
+ LeoReadWrite(&arg0->unk_00, OS_WRITE, startLBA, (void*)arg0->unk_5C, sp34, sp28);
+ osRecvMesg(sp28, (OSMesg*)&arg0->unk_68, OS_MESG_BLOCK);
+ }
+}
+
+void func_801C8414(struct_801E0D18* arg0) {
+ arg0->unk_68 = 9;
+}
+
+void func_801C8424(struct_801E0D18* arg0) {
+ struct_801D2E68* temp_v0;
+ s32 (*temp_s2)(struct_801E0D18*);
+ void (*aux)(struct_801E0D18*);
+ s32 temp_v0_2;
+
+ arg0->unk_68 = -1;
+ if ((func_801C81D4() != 0) || (arg0->unk_64 == 0)) {
+ arg0->unk_66 = 1;
+ temp_v0 = &D_801D2E68[arg0->unk_64];
+ aux = temp_v0->unk_0;
+ temp_s2 = temp_v0->unk_4;
+ do {
+ aux(arg0);
+ temp_v0_2 = temp_s2(arg0);
+ } while (temp_v0_2 == 2);
+ arg0->unk_6C = temp_v0_2;
+ if (arg0->unk_64 == 0) {
+ func_801C819C(temp_v0_2);
+ }
+ arg0->unk_66 = 0;
+ }
+}
+
+void func_801C84D4(void* arg) {
+ while (true) {
+ struct_801E0D18* sp24;
+
+ osRecvMesg(B_801E0D10[0], (OSMesg*)&sp24, OS_MESG_BLOCK);
+ func_801C8424(sp24);
+ osSendMesg(B_801E0D10[1], NULL, OS_MESG_BLOCK);
+ }
+}
+
+void func_801C8554(void) {
+ osDestroyThread(&B_801E0DB0);
+}
+
+void func_801C8578(void* arg0, void* arg1, OSId id, void* sp, OSPri pri) {
+ B_801E0D10[0] = (OSMesgQueue*)arg0;
+ B_801E0D10[1] = (OSMesgQueue*)arg1;
+ osCreateThread(&B_801E0DB0, id, &func_801C84D4, NULL, sp, pri);
+ osStartThread(&B_801E0DB0);
+}
+
+void func_801C85F0(struct_801E0D18* arg0, s32 arg1) {
+ if (arg1 == 1) {
+ func_801C8424(arg0);
+ } else {
+ osSendMesg(B_801E0D10[0], arg0, OS_MESG_BLOCK);
+ }
+}
+
+void func_801C8638(void (*arg0)(void*, void*, void*), s32 arg1, void (*arg2)(void*, uintptr_t, size_t)) {
+ s32 var0 = 0;
+
+ func_801C9B50(arg1, arg2);
+ D_801D2EB4 = arg0;
+ B_801E0D18.unk_64 = var0;
+ B_801E0D18.unk_65 = var0;
+ func_801C85F0(&B_801E0D18, 0);
+}
+
+void func_801C868C(void* arg0, void* arg1, void* arg2, u8 arg3) {
+ s32 var0 = (s32)arg0;
+ s32 var1 = (s32)arg1;
+ s32 var2 = (s32)arg2;
+ s32 var3 = arg3;
+ s32 var4 = 4;
+
+ if (D_801D2E90 == 1) {
+ D_801D2E90 = 0;
+ B_801E0D18.unk_64 = var4;
+ func_801C85F0(&B_801E0D18, 1);
+ if (B_801E0D18.unk_6C == 3 || B_801E0D18.unk_6C == 4) {
+ return;
+ }
+ }
+
+ B_801E0D18.unk_58 = var0;
+ B_801E0D18.unk_5C = var1;
+ B_801E0D18.unk_60 = var2;
+ B_801E0D18.unk_64 = var3;
+ func_801C85F0(&B_801E0D18, 0);
+}
+
+s32 func_801C873C(struct_801E0D18* arg0) {
+ u8 sp1F;
+
+ arg0->unk_68 = LeoTestUnitReady(&sp1F);
+ return !(sp1F & LEO_TEST_UNIT_MR);
+}
+
+s8 func_801C8770(void) {
+ s32 temp = func_801C873C(&B_801E0D18);
+
+ if (B_801E0D18.unk_68 == LEO_ERROR_BUSY) {
+ return 0;
+ }
+
+ if (!temp) {
+ return 2;
+ }
+
+ return 1;
+}
+
+s32 func_801C87C0(void) {
+ if (func_801C8844() == 0) {
+ if (B_801E0D18.unk_68 != 0) {
+ return B_801E0D18.unk_68;
+ }
+ }
+
+ return -1;
+}
+
+s32 func_801C87FC(void) {
+ s32* new_var = &B_801E0D18.unk_68;
+ s32 temp_v0;
+
+ if (func_801C8844() == 0) {
+ temp_v0 = B_801E0D18.unk_6C;
+
+ if ((temp_v0 == 3) || (temp_v0 == 4)) {
+ return *new_var;
+ }
+ }
+
+ return 0;
+}
+
+s32 func_801C8844(void) {
+ return B_801E0D18.unk_66 == 1;
+}
+
+s32 func_801C885C(void) {
+ B_801E0D18.unk_64 = 3;
+ func_801C85F0(&B_801E0D18, 1);
+
+#if OOT_VERSION > NTSC_1_0
+ D_801D2E90 = 0;
+#endif
+
+ if ((B_801E0D18.unk_6C == 3) || (B_801E0D18.unk_6C == 4)) {
+ return -1;
+ }
+ return B_801E0D18.unk_6C == 0;
+}
+
+s32 func_801C88AC(void) {
+ s32 phi_v0;
+
+ B_801E0D18.unk_64 = 4;
+ func_801C85F0(&B_801E0D18, 1);
+
+#if OOT_VERSION > NTSC_1_0
+ D_801D2E90 = 0;
+#endif
+
+ if ((B_801E0D18.unk_6C == 3) || (B_801E0D18.unk_6C == 4)) {
+ return -1;
+ }
+ return B_801E0D18.unk_6C == 0;
+}
+
+s32 func_801C88FC(void) {
+ s32 temp = 0;
+ s32 phi_v1;
+
+ if (LeoDriveExist()) {
+ phi_v1 = 8;
+ } else {
+ phi_v1 = 0;
+ }
+
+ temp = phi_v1 == temp;
+
+ B_801E0D18.unk_68 = phi_v1;
+
+ return temp;
+}
diff --git a/src/n64dd/n64dd_801C8940.c b/src/n64dd/n64dd_801C8940.c
new file mode 100644
index 000000000..437a23dcf
--- /dev/null
+++ b/src/n64dd/n64dd_801C8940.c
@@ -0,0 +1,398 @@
+// Lower-level command processing in a background thread
+#include "global.h"
+#include "n64dd.h"
+
+s32 D_801D2EA0 = 0;
+u8* D_801D2EA4 = NULL;
+s32 D_801D2EA8 = 0;
+u8* D_801D2EAC = NULL;
+u8* D_801D2EB0 = NULL;
+void (*D_801D2EB4)(void*, void*, void*) = NULL;
+
+s32 B_801E0F60;
+s32 B_801E0F64;
+
+// Set error message title texture?
+void func_801C8940(s32 errorNum) {
+ D_801D2EA4 = func_801C9E28(errorNum);
+ D_801D2EA8 = 1;
+}
+
+// Clear error message title texture?
+void func_801C8974(void) {
+ if (D_801D2EA8 == 1) {
+ D_801D2EA4 = func_801C9EC0();
+ D_801D2EA8 = 0;
+ }
+}
+
+// Set error message something
+void func_801C89B8(s32 errorNum) {
+ D_801D2EAC = func_801C9F90(errorNum);
+ B_801E0F60 = 1;
+}
+
+// Clear error message something
+void func_801C89EC(void) {
+ if (B_801E0F60 == 1) {
+ D_801D2EAC = func_801C9FFC();
+ B_801E0F60 = 0;
+ }
+}
+
+// Set error message something
+void func_801C8A30(s32 errorNum) {
+ D_801D2EB0 = func_801CA030(errorNum);
+ B_801E0F64 = 1;
+}
+
+// Clear error message something
+void func_801C8A64(void) {
+ if (B_801E0F64 == 1) {
+ D_801D2EB0 = func_801CA070();
+ B_801E0F64 = 0;
+ }
+}
+
+void func_801C8AA8(void) {
+ osRecvMesg(B_801E0D10[1], NULL, OS_MESG_NOBLOCK);
+
+ if ((D_801D2EB4 != NULL) && (D_801D2EA0 == 0)) {
+ u32 temp_v0 = osSetIntMask(OS_IM_NONE);
+ void* sp20 = D_801D2EA4;
+ void* sp1C = D_801D2EAC;
+ void* sp18 = D_801D2EB0;
+
+ D_801D2EA4 = NULL;
+ D_801D2EAC = NULL;
+ D_801D2EB0 = NULL;
+ osSetIntMask(temp_v0);
+ D_801D2EB4(sp20, sp1C, sp18);
+ }
+}
+
+void func_801C8B58(s32 arg0, s32 arg1, s32 arg2) {
+ func_801C8940(arg0);
+ func_801C89B8(arg1);
+ func_801C8A30(arg2);
+}
+
+void func_801C8B90(void) {
+ func_801C8974();
+ func_801C89EC();
+ func_801C8A64();
+}
+
+s32 func_801C8BC0(struct_801E0D18* arg0) {
+ if ((arg0->unk_68 < 0x25) || (arg0->unk_68 >= 0x29)) {
+ if ((arg0->unk_68 != 0x1F) && (arg0->unk_68 != 0x20)) {
+ func_801C8940(arg0->unk_68);
+ func_801C89B8(3);
+ }
+ }
+ LeoClearQueue();
+ return 4;
+}
+
+s32 func_801C8C1C(struct_801E0D18* arg0) {
+ s32 var_s0;
+
+ do {
+ var_s0 = 0;
+ Sleep_Msec(60);
+ func_801C82E0(arg0);
+
+ switch (arg0->unk_68) {
+ case 0x2A:
+ func_801C8A64();
+ return 0;
+ case 0x22:
+ func_801C8A64();
+ LeoClearQueue();
+ return 3;
+ case 0:
+ func_801C8A30(6);
+ FALLTHROUGH;
+ case 0x23:
+ var_s0 = 1;
+ break;
+ }
+ } while (var_s0 != 0);
+
+ func_801C8A64();
+ return func_801C8BC0(arg0);
+}
+
+s32 func_801C8CEC(struct_801E0D18* arg0) {
+ switch (arg0->unk_68) {
+ case 0x22:
+ func_801C8B90();
+ LeoClearQueue();
+ return 3;
+ case 0x2:
+ func_801C8940(arg0->unk_68);
+ func_801C8A30(5);
+ return 9;
+ case 0x0:
+ func_801C8B90();
+ return 0;
+ case 0x2B:
+ if (arg0->unk_65 == 0) {
+ func_801C8B90();
+ arg0->unk_65 = 2;
+ return 1;
+ }
+ FALLTHROUGH;
+ default:
+ func_801C8B90();
+ return func_801C8BC0(arg0);
+ case 0x23:
+ return 9;
+ }
+}
+
+s32 func_801C8DC0(struct_801E0D18* arg0) {
+ s32 temp_v0;
+
+ while (true) {
+ func_801C8298(arg0);
+ switch (arg0->unk_68) {
+ case 0x31:
+ func_801C8940(arg0->unk_68);
+ func_801C89B8(2);
+ return 5;
+ case 0x2A:
+ func_801C8B90();
+ return 5;
+ }
+ temp_v0 = func_801C8CEC(arg0);
+ if (temp_v0 != 9) {
+ return temp_v0;
+ }
+ Sleep_Msec(60);
+ }
+}
+
+s32 func_801C8E70(struct_801E0D18* arg0) {
+ s32 temp_a0;
+ s32 temp_v0;
+
+ while (true) {
+ Sleep_Msec(60);
+ func_801C8298(arg0);
+
+ switch (arg0->unk_68) {
+ case 0x23:
+ continue;
+ case 0x31:
+ func_801C8940(arg0->unk_68);
+ func_801C89B8(2);
+ FALLTHROUGH;
+ case 0x2A:
+ func_801C8A30(4);
+ continue;
+ }
+
+ temp_v0 = func_801C8CEC(arg0);
+ if (temp_v0 != 9) {
+ return temp_v0;
+ }
+ }
+}
+
+s32 func_801C8F1C(struct_801E0D18* arg0) {
+ if (D_801D2E54 != NULL) {
+ return D_801D2E54(arg0);
+ }
+ return 1;
+}
+
+s32 func_801C8F58(struct_801E0D18* arg0) {
+ s32 temp_v0;
+
+ while (true) {
+ temp_v0 = func_801C8E70(arg0);
+ if (temp_v0 == 3 || temp_v0 == 4) {
+ return temp_v0;
+ }
+
+ // Fake match?
+ if ((temp_v0 & 0xFFFFFFFF) == 0) {
+ if (func_801C8F1C(arg0) != 0) {
+ return 2;
+ }
+ func_801C89B8(1);
+ temp_v0 = func_801C8C1C(arg0);
+ if (temp_v0 != 0) {
+ return temp_v0;
+ }
+ func_801C89EC();
+ }
+ }
+}
+
+s32 func_801C9000(struct_801E0D18* arg0) {
+ s32 phi_s0;
+ s32 temp_s4;
+
+ while (true) {
+ phi_s0 = func_801C8E70(arg0);
+ if (phi_s0 == 3 || phi_s0 == 4) {
+ return phi_s0;
+ }
+
+ // Fake match?
+ if ((phi_s0 & 0xFFFFFFFF) == 0) {
+ func_801C8B90();
+
+ temp_s4 = func_801C8F1C(arg0);
+ if (temp_s4 == 3 || temp_s4 == 4) {}
+ if (temp_s4 != 0) {
+ return 2;
+ }
+
+ func_801C89B8(1);
+
+ phi_s0 = func_801C8C1C(arg0);
+ if (phi_s0 == 3 || phi_s0 == 4) {}
+ if (phi_s0 != 0) {
+ return phi_s0;
+ }
+
+ func_801C89EC();
+ if (temp_s4 != 0) {
+ return phi_s0;
+ }
+ }
+ }
+}
+
+s32 func_801C90C4(struct_801E0D18* arg0) {
+ func_801C8940(arg0->unk_68);
+ func_801C89B8(2);
+ return func_801C9000(arg0);
+}
+
+s32 func_801C90FC(struct_801E0D18* arg0) {
+ func_801C8940(arg0->unk_68);
+ return func_801C9000(arg0);
+}
+
+s32 func_801C912C(struct_801E0D18* arg0) {
+ s32 i = 0;
+ s32 temp_v0;
+
+ do {
+ LeoResetClear();
+
+ temp_v0 = func_801C8DC0(arg0);
+ if (temp_v0 == 3 || temp_v0 == 4) {
+ return temp_v0;
+ }
+ if (temp_v0 != 1) {
+ if (temp_v0 == 0) {
+ return temp_v0;
+ } else {
+ return temp_v0;
+ }
+ }
+
+ Sleep_Msec(250);
+ } while (i++ < 30);
+
+ return func_801C8BC0(arg0);
+}
+
+s32 func_801C91E0(struct_801E0D18* arg0) {
+ if (arg0->unk_68 == 0x29) {
+ return func_801C8BC0(arg0);
+ }
+
+ if (osMemSize < 0x800000) {
+ // LEO_ERROR_RAMPACK_NOT_CONNECTED?
+ func_801C8B58(0x2C, 0, 3);
+ return 4;
+ }
+
+ if (func_801C912C(arg0) == 0) {
+ D_801D2E90 = 1;
+ }
+ return 0;
+}
+
+s32 func_801C9260(struct_801E0D18* arg0) {
+ s32 temp_v0;
+
+ switch (arg0->unk_68) {
+ case 0x17:
+ func_801C873C(arg0);
+ temp_v0 = func_801C8C1C(arg0);
+ if (temp_v0 != 0) {
+ return temp_v0;
+ }
+ func_801C8B90();
+ return 2;
+
+ case 0x2:
+ return func_801C8F58(arg0);
+
+ case 0x22:
+ LeoClearQueue();
+ return 3;
+
+ case 0x31:
+ return func_801C90C4(arg0);
+
+ case 0x2F:
+ return func_801C9000(arg0);
+
+ case 0x2A:
+ return func_801C90FC(arg0);
+
+ case 0x0:
+ return 0;
+
+ case 0x23:
+ return 2;
+ }
+
+ return func_801C8BC0(arg0);
+}
+
+s32 func_801C9334(struct_801E0D18* arg0) {
+ while (true) {
+ u32 temp_v0 = func_801C8DC0(arg0);
+
+ if (temp_v0 == 3 || temp_v0 == 4 || temp_v0 == 5) {
+ return temp_v0;
+ }
+
+ // Fake match?
+ if ((temp_v0 & 0xFFFFFFFF) == 0) {
+ if (func_801C8F1C(arg0) != 0) {
+ return 0;
+ }
+ return 7;
+ }
+
+ if (1) {}
+ }
+}
+
+s32 func_801C93C4(struct_801E0D18* arg0) {
+ s32 temp_v0;
+ s32 temp_v0_2;
+
+ while (true) {
+ temp_v0_2 = func_801C9334(arg0);
+ if (temp_v0_2 != 7) {
+ return temp_v0_2;
+ }
+ func_801C89B8(1);
+ temp_v0 = func_801C8C1C(arg0);
+ if (temp_v0 != 0) {
+ return temp_v0;
+ }
+ func_801C89EC();
+ }
+}
diff --git a/src/n64dd/n64dd_801C9440.c b/src/n64dd/n64dd_801C9440.c
new file mode 100644
index 000000000..28ab8b562
--- /dev/null
+++ b/src/n64dd/n64dd_801C9440.c
@@ -0,0 +1,263 @@
+// Some text-handling functions
+#include "global.h"
+#include "n64dd.h"
+
+void (*D_801D2EC0)(void*, uintptr_t, size_t) = NULL;
+
+s32 B_801E0F70;
+
+/**
+ * Seems to work out if a pair of bytes is a valid EUC-JP character, although there may be additions to the font that
+ * make the strange first check make more sense.
+ *
+ * @param bytes Array 2 bytes to test
+ * @return boolean
+ */
+int func_801C9440(u8* bytes) {
+ // This is every possible first byte. May make more sense what was intended with the font files?
+ if (((*bytes >= 0x8E) && (*bytes <= 0xFE)) || ((*bytes != 0x8F) && (*bytes != 0xA0))) {
+ bytes++;
+ return (*bytes >= 0xA0) && (*bytes <= 0xFE);
+ }
+ return false;
+}
+
+/**
+ * A crude check for a valid 2-byte Shift-JIS character
+ *
+ * @param bytes Array containing a pair of bytes to test
+ * @return boolean
+ */
+int func_801C9494(u8* bytes) {
+ // Allowable first bytes.
+ if (((*bytes >= 0x81) && (*bytes <= 0x9F)) || ((*bytes >= 0xE0) && (*bytes <= 0xFC))) {
+ bytes++;
+ // Allowable second bytes.
+ return (*bytes >= 0x40) && (*bytes <= 0xFC) && *bytes != 0x7F;
+ }
+ return false;
+}
+
+// Extracts 2 bytes from a bytepacked big-endian short.
+void func_801C94F8(u8* arg0, u16 arg1) {
+ arg0[0] = arg1 >> 8;
+ arg0[1] = arg1 & 0xFF;
+}
+
+// Convert EUC-JP to JIS X 0208
+u16 func_801C9514(u16 eucjpCh) {
+ return eucjpCh - 0x8080;
+}
+
+/**
+ * Converts a JIS X 0208 codepoint to a Shift-JIS one.
+ *
+ * @param jisCodepoint Two bytes, each between 0x21 and 0x7E, packed big-endian into a short.
+ * @return u16 Shift-JIS character representation (expected to be big-endian)
+ */
+u16 func_801C9534(u16 jisCodepoint) {
+ u8 hiByte = (jisCodepoint >> 8) & 0xFF;
+ u8 loByte = jisCodepoint & 0xFF;
+
+ if (hiByte & 1) {
+ loByte += 0x1F;
+ if (loByte >= 0x7F) {
+ loByte++;
+ }
+ } else {
+ loByte += 0x7E;
+ }
+
+ hiByte = (hiByte - 0x21) / 2 + 0x81;
+ if (hiByte >= 0xA0) {
+ hiByte += 0x40;
+ }
+
+ return (hiByte << 8) + loByte;
+}
+
+void func_801C95C0(void* arg0, uintptr_t arg1, size_t arg2) {
+ if (D_801D2EC0 != NULL) {
+ D_801D2EC0(arg0, arg1, arg2);
+ }
+}
+
+s32 func_801C95F0(u8* arg0) {
+ return LeoGetKAdr(func_801C9534(func_801C9514((arg0[0] << 8) | arg0[1]))) + DDROM_FONT_START;
+}
+
+s32 func_801C963C(s32* arg0, int* dx, int* dy, int* cy, u8 arg4) {
+ s32 temp_v0;
+ s32 temp_v1;
+
+ u16 temp = arg4 - 0x20;
+ temp += 0xC0 * B_801E0F70;
+
+ temp_v0 = LeoGetAAdr(temp, dx, dy, cy);
+ temp_v1 = temp_v0 & 0xF;
+ *arg0 = (temp_v0 - temp_v1) + DDROM_FONT_START;
+ return temp_v1;
+}
+
+s32 func_801C969C(void* arg0, int* dx, int* dy, int* cy, u8* arg4) {
+ s32 sp24;
+ s32 phi_v1;
+
+ if (func_801C9440(arg4)) {
+ sp24 = func_801C95F0(arg4);
+ *dx = 16;
+ *dy = 16;
+ *cy = 11;
+ phi_v1 = 0;
+ } else {
+ phi_v1 = func_801C963C(&sp24, dx, dy, cy, *arg4);
+ }
+
+ func_801C95C0(arg0, sp24, 0x80);
+ return phi_v1;
+}
+
+// return boolean
+s32 func_801C9740(u8* arg0, s32 arg1, u8 arg2) {
+ if (arg1 == 1) {
+ *arg0 = arg2 << 4;
+ return false;
+ } else {
+ *arg0 |= arg2;
+ return true;
+ }
+}
+
+u8* func_801C9778(u8* arg0, s32* arg1, s32 arg2) {
+ arg0 += (arg2 >> 1);
+
+ if (((*arg1 == 1) && !(arg2 & 1)) || ((*arg1 == 0) && (arg2 & 1))) {
+ arg0 += arg2 & 1;
+ *arg1 = 1;
+ } else {
+ *arg1 = 0;
+ }
+ return arg0;
+}
+
+s32 func_801C97C4(u8** arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4, u8* arg5, s32 dx, s32 dy, s32 cy) {
+ u8* var_fp;
+ u8* var_s0;
+ s32 var_s1;
+ s32 x;
+ s32 sp4C;
+ s32 var_s4;
+ s32 sp44;
+
+ sp4C = arg2;
+ sp44 = dx + arg3 + arg4;
+ var_s4 = 1;
+ var_fp = func_801C9778(*arg0, &sp4C, arg3);
+ arg1 >>= 1;
+ var_fp += ((11 - cy) * arg1);
+ if (dx & 1) {
+ dx++;
+ }
+ while (dy--) {
+ var_s0 = var_fp;
+ var_s1 = sp4C;
+ for (x = 0; x < dx; x++) {
+ if (var_s4 == 1) {
+ if (func_801C9740(var_s0, var_s1, *arg5 >> 4)) {
+ var_s0++;
+ }
+ } else {
+ if (func_801C9740(var_s0, var_s1, *arg5 & 0xF)) {
+ var_s0++;
+ }
+ arg5++;
+ }
+ var_s4 ^= 1;
+ var_s1 ^= 1;
+ }
+ var_fp += arg1;
+ }
+ *arg0 = func_801C9778(*arg0, &arg2, sp44);
+ return arg2;
+}
+
+void func_801C9954(u8* bytes, s32* arg1, s32* arg2) {
+ u8 prevCh;
+ u8 nextCh;
+
+ if (func_801C9440(bytes)) {
+ *arg1 = *arg2 = 0;
+ return;
+ }
+
+ *arg1 = *arg2 = 0;
+
+ prevCh = bytes[-1];
+ nextCh = bytes[1];
+
+ switch (bytes[0]) {
+ case ' ':
+ *arg1 = *arg2 = 0;
+ return;
+
+ case 'I':
+ case 'i':
+#if OOT_NTSC
+ *arg2 = 2;
+ if (1) {}
+#endif
+
+ *arg1 = 2;
+ *arg2 = 2;
+
+ if (prevCh == ' ') {
+ *arg1 = 0;
+ }
+ if (nextCh == ' ') {
+ *arg2 = 0;
+ }
+ return;
+ }
+
+#if OOT_NTSC
+ *arg2 = 1;
+ if (1) {}
+#endif
+
+ *arg1 = 1;
+ *arg2 = 1;
+}
+
+void func_801C9A10(u8* arg0, s32 arg1, u8* str) {
+ u8 sp80[0xA0];
+ u8* temp_s1;
+ int dx;
+ int dy;
+ int cy;
+ s32 var_s2;
+ s32 sp68;
+ s32 sp64;
+ s32 temp_v1;
+
+ temp_s1 = (u8*)((((uintptr_t)&sp80 + 0xF) / 0x10) * 0x10);
+ var_s2 = 1;
+ if (str != NULL) {
+ while (*str != '\0') {
+ func_801C9954(str, &sp68, &sp64);
+ temp_v1 = func_801C969C(temp_s1, &dx, &dy, &cy, str);
+ if (dx & 1) {
+ dx++;
+ }
+ var_s2 = func_801C97C4(&arg0, arg1, var_s2, sp68, sp64, &temp_s1[temp_v1], dx, dy, cy);
+ if (func_801C9440(str)) {
+ str++;
+ }
+ str++;
+ }
+ }
+}
+
+void func_801C9B50(s32 arg0, void (*arg1)(void*, uintptr_t, size_t)) {
+ B_801E0F70 = arg0;
+ D_801D2EC0 = arg1;
+}
diff --git a/src/n64dd/n64dd_801C9B70.c b/src/n64dd/n64dd_801C9B70.c
new file mode 100644
index 000000000..d7d2f6384
--- /dev/null
+++ b/src/n64dd/n64dd_801C9B70.c
@@ -0,0 +1,172 @@
+// User-facing error handling
+#include "global.h"
+#include "n64dd.h"
+
+u8 B_801E0F80[0x600];
+u8 B_801E1580[0x2800];
+u8 B_801E3D80[0x1400];
+
+/**
+ * Converts a number in decimal to a hexadecimal number with the same digits, e.g. 1234 -> 0x1234.
+ *
+ * Will only work on nonnegative numbers.
+ *
+ * @param decNumber Number in decimal to convert, e.g. 1234
+ * @return s32 Hexadecimal number with the same digits as decNumber, e.g. 0x1234
+ */
+s32 func_801C9B70(s32 decNumber) {
+ s32 currPlaceValue;
+ s32 currExponent = 0;
+ s32 accumulatedHexDigits = 0;
+ s32 remainingDecDigits;
+
+ // Nothing to do if only one digit.
+ if (decNumber < 10) {
+ return decNumber;
+ }
+
+ // Find the place value / exponent of the most significant digit in decNumber.
+ for (currPlaceValue = 1; 10 * currPlaceValue <= decNumber; currPlaceValue *= 10) {
+ currExponent++;
+ }
+
+ remainingDecDigits = decNumber;
+
+ // Transfer the digits to hex one at a time.
+ while (currExponent--) {
+ // Place the most significant remaining digit at the end of the hex output.
+ accumulatedHexDigits |= remainingDecDigits / currPlaceValue;
+ accumulatedHexDigits *= 0x10; // Shift left one hex digit.
+
+ remainingDecDigits %= currPlaceValue; // Remove most significant of the remaining digits.
+
+ currPlaceValue /= 10;
+ }
+
+ accumulatedHexDigits += remainingDecDigits; // Only one digit left in the remainingDecDigits.
+ return accumulatedHexDigits;
+}
+
+// n64ddError_GetLanguage
+s32 func_801C9C48(void) {
+ return (gCurrentRegion == 1) ? 0 : 1;
+}
+
+// n64ddError_Memset
+void func_801C9C74(u8* dest, u8 value, u32 count) {
+ while (count--) {
+ *dest++ = value;
+ }
+}
+
+// n64ddError_GetErrorHeader
+const char* func_801C9CA4(void) {
+ return D_801D2ED0[func_801C9C48()];
+}
+
+// n64ddError_WriteNumberJP
+// Writes a 2-digit number to the char buffer provided
+// Character indices for numbers in the error code (EUC-JP)
+void func_801C9CD4(u8* buf, s32 number) {
+ s32 temp_v0 = func_801C9B70(number);
+ u16 character;
+
+ if (number >= 10) {
+ character = ((temp_v0 >> 4) + 0xA3B0); // '0'
+ } else {
+ character = 0xA1A1; // ' '
+ }
+
+ func_801C94F8(buf, character);
+ buf += 2;
+ func_801C94F8(buf, ((temp_v0 & 0xF) + 0xA3B0)); // '0'
+}
+
+// n64ddError_WriteNumberEN
+// Writes a 2-digit number to the char buffer provided
+// Character indices for numbers in the error code (ASCII)
+void func_801C9D54(u8* buf, s32 number) {
+ s32 temp_v0 = func_801C9B70(number);
+
+ if (number >= 10) {
+ *buf = (temp_v0 >> 4) + '0';
+ } else {
+ *buf = ' ';
+ }
+ buf++;
+ *buf = (temp_v0 & 0xF) + '0';
+}
+
+void func_801C9DB8(u8* arg0, s32 errorNum) {
+ u8* errorHeader = (u8*)func_801C9CA4();
+
+ //! @bug: both of these functions will write to the pointer target, but errorHeader points to a string literal,
+ //! which is meant to be const.
+ if (gCurrentRegion == 1) {
+ func_801C9CD4(&errorHeader[12], errorNum);
+ } else {
+ func_801C9D54(&errorHeader[13], errorNum);
+ }
+ func_801C9A10(arg0, 192, errorHeader);
+}
+
+u8* func_801C9E28(s32 errorNum) {
+ func_801C9EC0();
+
+ if (errorNum == 41) {
+ return (u8*)gN64DDError41Texs[func_801C9C48()];
+ }
+
+ // 31,32, 37,38,39,40
+ if (((errorNum >= 37) && (errorNum < 41)) || (errorNum == 31) || (errorNum == 32)) {
+ return B_801E0F80;
+ } else {
+ func_801C9DB8(B_801E0F80, errorNum);
+ return B_801E0F80;
+ }
+}
+
+// Clear something
+u8* func_801C9EC0(void) {
+ func_801C9C74(B_801E0F80, 0, 0x600);
+ return B_801E0F80;
+}
+
+// Prints the error message body (?)
+void func_801C9EF4(u8* arg0, s32 errorNum, s32 lineCount) {
+ s32 i;
+
+ for (i = 0; i < lineCount; i++, arg0 += 0xA00) {
+ u8* line = (u8*)D_801D2EE0[func_801C9C48()][errorNum][i];
+
+ if (1) {}
+ func_801C9A10(arg0, 320, line);
+ }
+}
+
+u8* func_801C9F90(s32 errorNum) {
+ func_801C9FFC();
+ if (errorNum == 3) {
+ return (u8*)gN64DDPleaseReadManualTexs[func_801C9C48()];
+ }
+ func_801C9EF4(B_801E1580, errorNum, 4);
+ return B_801E1580;
+}
+
+// Clear something
+u8* func_801C9FFC(void) {
+ func_801C9C74(B_801E1580, 0, 0x2800);
+ return B_801E1580;
+}
+
+u8* func_801CA030(s32 errorNum) {
+ func_801CA070();
+ func_801C9EF4(B_801E3D80, errorNum, 2);
+ return B_801E3D80;
+}
+
+// Clear something
+u8* func_801CA070(void) {
+ func_801C9C74(B_801E3D80, 0, 0x1400);
+ return B_801E3D80;
+}
diff --git a/src/n64dd/n64dd_801CA0B0.c b/src/n64dd/n64dd_801CA0B0.c
new file mode 100644
index 000000000..8758e0fd6
--- /dev/null
+++ b/src/n64dd/n64dd_801CA0B0.c
@@ -0,0 +1,243 @@
+#include "global.h"
+#include "n64dd.h"
+
+// Draws text to framebuffer
+typedef struct struct_801CA704 {
+ /* 0x00 */ PrintCallback callback;
+ /* 0x04 */ void* charTexBuf;
+ /* 0x08 */ u16 unk_08;
+ /* 0x0A */ u16 posX;
+ /* 0x0C */ u16 posY;
+ /* 0x10 */ Color_RGBA8_u32 color;
+ /* 0x14 */ u16 baseX;
+ /* 0x16 */ u16 baseY;
+ /* 0x18 */ u16 endX;
+ /* 0x1A */ u16 endY;
+ /* 0x1C */ u8 sjisPrevByte;
+ /* 0x20 */ void* frameBuf;
+ /* 0x24 */ u16 screenWidth;
+ /* 0x26 */ u16 screenHeight;
+} struct_801CA704;
+
+// clang-format off
+u32 D_801D8BE0[0x5F] = {
+ 0x00009D14, 0x00232A14, 0x00296314, 0x002F8A14, 0x00457E18, 0x0063CA14, 0x0084AA14, 0x00A03314,
+ 0x00A45E14, 0x00BB4E14, 0x00CA6514, 0x00D3770E, 0x00E33302, 0x00E78108, 0x00EB2102, 0x00EC6C14,
+ 0x01008A14, 0x01163A14, 0x01217A14, 0x01377A14, 0x014D8A14, 0x01638A14, 0x01798A14, 0x018F7A14,
+ 0x01A58A14, 0x01BB8A14, 0x01D1170E, 0x01D5490E, 0x01DF770E, 0x01EF740A, 0x01F9770E, 0x02097A14,
+ 0x021FDC16, 0x024DBA14, 0x026E7A14, 0x0284BA14, 0x02A59A14, 0x02C16A14, 0x02D26A14, 0x02E3BA14,
+ 0x03048A14, 0x031A1A14, 0x03206A14, 0x03318A14, 0x03476A14, 0x0358CA14, 0x03799A14, 0x0395CA14,
+ 0x03B67A14, 0x03CCCA14, 0x03ED8A14, 0x04037A14, 0x04196A14, 0x042A8A14, 0x0440AA14, 0x045CDA14,
+ 0x04839A14, 0x049F9A14, 0x04BB7A14, 0x04D14D14, 0x04DF5C14, 0x04F33D14, 0x05015216, 0x050681FA,
+ 0x050A9D14, 0x052D9810, 0x05449A14, 0x05609810, 0x05779A14, 0x05939810, 0x05AA5A14, 0x05BB9C10,
+ 0x05DC7A14, 0x05F22A14, 0x05F83D14, 0x06067A14, 0x061C2A14, 0x0622D810, 0x06427810, 0x06549810,
+ 0x066B9B10, 0x06899B10, 0x06A74810, 0x06B06810, 0x06BE5A14, 0x06CF770E, 0x06DF870E, 0x06EFB70E,
+ 0x0707670E, 0x07138A0E, 0x0729670E, 0x07356D14, 0x074A1F16, 0x07526D14, 0x07675216,
+};
+// clang-format on
+
+// Loads character texture to buffer
+s32 func_801CA0B0(s32 charCode, void* charTexBuf, int* dx, int* dy, int* cy) {
+ s32 offset;
+ OSPiHandle* handle;
+ OSMesgQueue queue;
+ OSMesg msgBuf[1];
+ OSIoMesg mesg;
+
+ handle = osDriveRomInit();
+ if (charCode >= 0x20 && charCode < 0x7F) { // ASCII
+ offset = LeoGetAAdr2(D_801D8BE0[charCode - 0x20], dx, dy, cy);
+ } else if (charCode >= 0x8140) { // Shift-JIS
+ offset = LeoGetKAdr(charCode);
+ *dx = 16;
+ *dy = 16;
+ *cy = 11;
+ } else {
+ return -1;
+ }
+
+ osCreateMesgQueue(&queue, msgBuf, ARRAY_COUNT(msgBuf));
+
+ // clang-format off
+ mesg.hdr.retQueue = &queue; \
+ mesg.devAddr = offset + DDROM_FONT_START; \
+ mesg.dramAddr = charTexBuf; \
+ mesg.size = 0x80; \
+ mesg.hdr.pri = 0;
+ // clang-format on
+
+ handle->transferInfo.cmdType = 2;
+ osEPiStartDma(handle, &mesg, 0);
+ osRecvMesg(&queue, NULL, OS_MESG_BLOCK);
+
+ return 0;
+}
+
+const u16 D_801D9390[16] = {
+ 0x0001, 0x1085, 0x2109, 0x318D, 0x4211, 0x5295, 0x6319, 0x739D,
+ 0x8C63, 0x9CE7, 0xAD6B, 0xBDEF, 0xCE73, 0xDEF7, 0xEF7B, 0xFFFF,
+};
+
+// Maps 4-bit intensity to a 16-bit color
+u16 func_801CA1D4(u32 arg0) {
+ return D_801D9390[arg0 % ARRAY_COUNT(D_801D9390)];
+}
+
+void func_801CA1F0(void* charTexBuf, s32 posX, s32 posY, s32 dx, s32 dy, s32 cy, void* frameBuf, s32 screenWidth) {
+ s32 intensity;
+ s32 x;
+ s32 y;
+ u8* src = charTexBuf;
+ u16* dst = frameBuf;
+
+ for (y = 0; y < dy; y++) {
+ for (x = 0; x < dx; x++) {
+ if (!(x & 1)) {
+ intensity = *src >> 4;
+ } else {
+ intensity = *src & 0xF;
+ src++;
+ }
+ dst[posX + x + ((posY + (11 - cy) + y) * screenWidth)] = func_801CA1D4(intensity);
+ }
+ if (dx & 1) {
+ src++;
+ }
+ }
+}
+
+void func_801CA2F8(struct_801CA704* arg0, u32 r, u32 g, u32 b, u32 a) {
+ arg0->color.r = r;
+ arg0->color.g = g;
+ arg0->color.b = b;
+ arg0->color.a = a;
+}
+
+void func_801CA314(struct_801CA704* arg0, s32 arg1, s32 arg2) {
+ arg0->posX = arg0->baseX + arg1;
+ arg0->posY = arg0->baseY + arg2;
+}
+
+void func_801CA334(struct_801CA704* arg0, s32 baseX, s32 baseY, s32 endX, s32 endY) {
+ arg0->baseX = baseX;
+ arg0->baseY = baseY;
+ arg0->endX = endX;
+ arg0->endY = endY;
+}
+
+void func_801CA350(struct_801CA704* arg0, void* frameBuf, s32 screenWidth, s32 screenHeight) {
+ arg0->frameBuf = (u8*)frameBuf + 0x20000000;
+ arg0->screenWidth = screenWidth;
+ arg0->screenHeight = screenHeight;
+ func_801CA334(arg0, 0, 0, screenWidth - 1, screenHeight - 1);
+}
+
+void func_801CA3B4(struct_801CA704* arg0, void* charTexBuf, s32 arg2) {
+ arg0->charTexBuf = (u8*)charTexBuf + 0x20000000;
+ arg0->unk_08 = arg2;
+}
+
+void func_801CA3CC(struct_801CA704* arg0, char c) {
+ s32 charCode;
+ int dx;
+ int dy;
+ int cy;
+
+ if (arg0->sjisPrevByte != 0) {
+ charCode = (arg0->sjisPrevByte << 8) | c;
+ } else {
+ if (c >= 0x80 && c < 0x99) {
+ arg0->sjisPrevByte = c;
+ return;
+ }
+ charCode = c;
+ }
+
+ arg0->sjisPrevByte = 0;
+ if (func_801CA0B0(charCode, arg0->charTexBuf, &dx, &dy, &cy) == 0) {
+ if (arg0->posX + dx > arg0->endX) {
+ arg0->posX = arg0->baseX;
+ if (arg0->posY + 16 > arg0->endY) {
+ arg0->posY = arg0->baseY;
+ } else {
+ arg0->posY += 16;
+ }
+ }
+ func_801CA1F0(arg0->charTexBuf, arg0->posX, arg0->posY, dx, dy, cy, arg0->frameBuf, arg0->screenWidth);
+ arg0->posX += (dx == 16 ? dx : dx + 2);
+ }
+}
+
+void func_801CA4F4(struct_801CA704* arg0, char c) {
+ if (c >= ' ' && c <= 0xFF) {
+ func_801CA3CC(arg0, c);
+ } else {
+ switch (c) {
+ case '\n':
+ arg0->posY += 32;
+ FALLTHROUGH;
+ case '\r':
+ arg0->posX = arg0->baseX;
+ break;
+ case '\t':
+ do {
+ func_801CA3CC(arg0, ' ');
+ } while ((arg0->posX - arg0->baseX) % 256);
+ break;
+ case '\0':
+ break;
+ }
+ }
+}
+
+void func_801CA5BC(struct_801CA704* arg0, const char* str, s32 arg2, size_t count) {
+ const char* s = str;
+ s32 n = arg2 * count;
+
+ while (n != 0) {
+ func_801CA4F4(arg0, *s++);
+ n--;
+ }
+}
+
+void func_801CA618(struct_801CA704* arg0, const char* str) {
+ while (*str != 0) {
+ func_801CA4F4(arg0, *str++);
+ }
+}
+
+void* func_801CA670(void* arg, const char* str, size_t count) {
+ func_801CA5BC(arg, str, 1, count);
+ return arg;
+}
+
+void func_801CA6A0(struct_801CA704* arg0) {
+ arg0->callback = &func_801CA670;
+ arg0->posX = 0;
+ arg0->posY = 0;
+ arg0->baseX = 0;
+ arg0->baseY = 0;
+ arg0->endX = 0;
+ arg0->endY = 0;
+ arg0->color.rgba = 0;
+ arg0->sjisPrevByte = 0;
+ arg0->charTexBuf = NULL;
+}
+
+void func_801CA6D8(struct_801CA704* arg0) {
+}
+
+s32 func_801CA6E4(struct_801CA704* arg0, const char* fmt, va_list args) {
+ return PrintUtils_VPrintf(&arg0->callback, fmt, args);
+}
+
+s32 func_801CA704(struct_801CA704* arg0, const char* fmt, ...) {
+ s32 ret;
+ va_list args;
+
+ va_start(args, fmt);
+ ret = func_801CA6E4(arg0, fmt, args);
+ va_end(args);
+
+ return ret;
+}
diff --git a/src/n64dd/n64dd_data_buffer.c b/src/n64dd/n64dd_data_buffer.c
new file mode 100644
index 000000000..6aacae2d5
--- /dev/null
+++ b/src/n64dd/n64dd_data_buffer.c
@@ -0,0 +1,5 @@
+#include "global.h"
+#include "n64dd.h"
+
+// Buffer used for reading from the disk?
+u8 B_801DC000[0x4D10];
diff --git a/src/n64dd/n64dd_error_bodies.c b/src/n64dd/n64dd_error_bodies.c
new file mode 100644
index 000000000..757e1afb6
--- /dev/null
+++ b/src/n64dd/n64dd_error_bodies.c
@@ -0,0 +1,47 @@
+#include "global.h"
+#include "n64dd.h"
+
+const char* D_801D2EE0[2][8][4] = {
+ {
+ { "  メモリー拡張パックが正しく差し込", "  んでありますか?        ", NULL, NULL },
+ { "  間違ったディスクが差し込まれてい", "  る可能性があります。正しいディス",
+ "  クに交換してください。     ", NULL },
+ { "  注意:アクセスランプ点滅中にディ", "  スクを抜かないでください。詳しく",
+ "  は、取扱説明書をお読みください。", NULL },
+ { "  詳しくは、取扱説明書をお読みくだ", "  さい。             ", NULL, NULL },
+ { "   ディスクを差し込んでください。", NULL, NULL, NULL },
+ { "  ディスクを差し込み直してください。", NULL, NULL, NULL },
+ { "   ディスクを取り出してください。", NULL, NULL, NULL },
+ { NULL, NULL, NULL, NULL },
+ },
+ {
+ {
+ " Are you sure the Expansion",
+ " Pak is inserted correctly?",
+ NULL,
+ NULL,
+ },
+ {
+ " Check to see if the proper",
+ " disk is inserted.",
+ " Please insert the disk the",
+ " correct disk.",
+ },
+ {
+ " Do not remove the disk when",
+ " the access light is flashing.",
+ " Please read the instruction",
+ " manual for details.",
+ },
+ {
+ " Please read the instruction",
+ " manual for details.",
+ NULL,
+ NULL,
+ },
+ { " Please insert a disk.", NULL, NULL, NULL },
+ { " Please insert the disk again.", NULL, NULL, NULL },
+ { " Please remove the disk.", NULL, NULL, NULL },
+ { NULL, NULL, NULL, NULL },
+ },
+};
diff --git a/src/n64dd/n64dd_error_headers.c b/src/n64dd/n64dd_error_headers.c
new file mode 100644
index 000000000..b14d80fba
--- /dev/null
+++ b/src/n64dd/n64dd_error_headers.c
@@ -0,0 +1,5 @@
+#include "global.h"
+#include "n64dd.h"
+
+// Padding in .rodata suggests that these are in a separate file
+const char* D_801D2ED0[4] = { "エラー番号   ", "Error Number ", NULL, NULL };
diff --git a/src/n64dd/n64dd_error_textures.c b/src/n64dd/n64dd_error_textures.c
new file mode 100644
index 000000000..037ed7835
--- /dev/null
+++ b/src/n64dd/n64dd_error_textures.c
@@ -0,0 +1,23 @@
+#include "ultra64.h"
+
+#if PLATFORM_N64
+
+u64 gN64DDError41Texs[2][0x600 / sizeof(u64)] = {
+ {
+#include "assets/n64dd/error_textures/n64dd_error_41_jpn.i4.inc.c"
+ },
+ {
+#include "assets/n64dd/error_textures/n64dd_error_41_eng.i4.inc.c"
+ },
+};
+
+u64 gN64DDPleaseReadManualTexs[2][0x2800 / sizeof(u64)] = {
+ {
+#include "assets/n64dd/error_textures/n64dd_please_read_manual_jpn.i4.inc.c"
+ },
+ {
+#include "assets/n64dd/error_textures/n64dd_please_read_manual_eng.i4.inc.c"
+ },
+};
+
+#endif
diff --git a/src/n64dd/z_n64dd.c b/src/n64dd/z_n64dd.c
new file mode 100644
index 000000000..4e90806e9
--- /dev/null
+++ b/src/n64dd/z_n64dd.c
@@ -0,0 +1,584 @@
+// Main interface for the 64DD from the rest of the game. Starts background
+// threads and provides functions to submit commands to them.
+#include "global.h"
+#include "fault.h"
+#include "n64dd.h"
+#include "stack.h"
+#include "versions.h"
+
+typedef struct struct_801D9C30 {
+ /* 0x000 */ s32 unk_000; // disk start
+ /* 0x004 */ s32 unk_004; // disk end
+ /* 0x008 */ uintptr_t unk_008; // ram start
+ /* 0x00C */ uintptr_t unk_00C; // ram end
+ /* 0x010 */ UNK_PTR unk_010;
+ /* 0x014 */ char unk_014[0x104];
+} struct_801D9C30; // size = 0x118
+
+typedef struct struct_801D9B90 {
+ /* 0x00 */ OSMesg unk_00[30];
+ /* 0x78 */ OSMesgQueue unk_78;
+ /* 0x90 */ IrqMgrClient unk_90;
+ /* 0x98 */ IrqMgr* unk_98;
+} struct_801D9B90; // size = 0x9C
+
+s32 func_801C7A1C(struct_801E0D18* arg0);
+
+void* D_801D2E50 = &B_801DC000;
+s32 (*D_801D2E54)(struct_801E0D18*) = func_801C7A1C;
+
+struct_801D9B90 B_801D9B90;
+struct_801D9C30 B_801D9C30;
+struct_801D9C30* B_801D9D48;
+struct_801D9D50 B_801D9D50;
+OSMesgQueue B_801D9D80;
+OSMesgQueue B_801D9D98;
+OSMesg B_801D9DB0[1];
+OSMesg B_801D9DB4[1];
+volatile u8 B_801D9DB8;
+volatile OSTime B_801D9DC0;
+s32 B_801D9DC8; // 1 if disk gameName is correct, 2 otherwise
+void* B_801D9DCC;
+void* B_801D9DD0;
+void* B_801D9DD4;
+OSThread B_801D9DD8;
+STACK(B_801D9F88, 0x1000);
+StackEntry B_801DAF88;
+STACK(B_801DAFA8, 0x1000);
+StackEntry B_801DBFA8;
+UNK_TYPE B_801DBFC4; // unused?
+
+u32 func_801C6E80(void) {
+#if OOT_NTSC
+ return LeoDriveExist();
+#else
+ return 0;
+#endif
+}
+
+void func_801C6EA0(Gfx** gfxP) {
+}
+
+void func_801C6EAC(void) {
+ if (D_80121214 == 0) {
+ func_800F6BDC();
+ D_80121214 = 1;
+ }
+}
+
+s32 func_801C6EF0(void) {
+ return D_80121214 != 0;
+}
+
+s32 func_801C6F08(void) {
+ if (D_80121214 != 0) {
+ return 1;
+ }
+ return 1;
+}
+
+void func_801C6F30(void) {
+ func_801C6EAC();
+ while (func_801C6F08() == 0) {
+ Sleep_Usec(16666); // 100000 / 6
+ }
+}
+
+void func_801C6F78(void) {
+ if (D_80121214 != 0) {
+ D_80121214 = 0;
+ func_800F6B3C();
+ }
+}
+
+// boolean
+s32 func_801C6FAC(void) {
+ if (D_80121213 == 0) {
+ return false;
+ } else {
+ D_80121213 = 0;
+ return true;
+ }
+}
+
+void func_801C6FD8(void) {
+ while (!func_801C6FAC()) {
+ Sleep_Usec(16666); // 100000 / 6
+ }
+}
+
+// Adds a HungupAndCrash
+void func_801C7018(void) {
+ if (D_80121213 != 0) {
+#if OOT_VERSION == NTSC_1_0
+ Fault_AddHungupAndCrash("../z_n64dd.c", 503);
+#elif OOT_VERSION == NTSC_1_1
+ Fault_AddHungupAndCrash("../z_n64dd.c", 551);
+#else
+ Fault_AddHungupAndCrash("../z_n64dd.c", 573);
+#endif
+ }
+ D_80121213 = 1;
+}
+
+s32 func_801C7064(void) {
+ B_801D9D50.unk_00 = 5;
+ return (&func_801C8000)(&B_801D9D50);
+}
+
+s32 func_801C7098(void) {
+ s32 phi_v1;
+
+#if OOT_VERSION <= NTSC_1_1
+ if (0) {}
+#endif
+
+ B_801D9D50.unk_00 = 10;
+ phi_v1 = (&func_801C8000)(&B_801D9D50);
+ if (phi_v1 < 0) {
+ func_800D31A0();
+ }
+ return phi_v1;
+}
+
+s32 func_801C70E4(void) {
+ return B_801D9DC8 == 1;
+}
+
+// Used by EnMag and FileChoose
+s32 func_801C70FC(void) {
+ return func_801C70E4();
+}
+
+void func_801C711C(void* arg) {
+ static void* B_801DBFC8;
+ struct_801D9B90* arg0 = (struct_801D9B90*)arg;
+ s16* sp58;
+ s32 var_s0;
+ void* temp_v0;
+
+ sp58 = NULL;
+ arg0->unk_98 = &gIrqMgr;
+ osCreateMesgQueue(&arg0->unk_78, arg0->unk_00, ARRAY_COUNT(arg0->unk_00));
+ IrqMgr_AddClient(arg0->unk_98, &arg0->unk_90, &arg0->unk_78);
+ var_s0 = 0;
+ do {
+ osRecvMesg(&arg0->unk_78, (OSMesg*)&sp58, OS_MESG_BLOCK);
+ switch (*sp58) {
+ case 1:
+ temp_v0 = osViGetNextFramebuffer();
+ if (B_801DBFC8 != temp_v0) {
+ B_801DBFC8 = temp_v0;
+ B_801D9DB8 = 1;
+ }
+ func_801C8AA8();
+ break;
+ case 4:
+ LeoReset();
+ break;
+ case 3:
+ var_s0 = 1;
+ break;
+ }
+ } while (var_s0 == 0);
+ IrqMgr_RemoveClient(arg0->unk_98, &arg0->unk_90);
+}
+
+#if OOT_VERSION > NTSC_1_0
+void func_801C7B28_ne2(void) {
+ s32 temp;
+
+ if (B_801D9DC0 != 0) {
+ temp = (osGetTime() - B_801D9DC0) * 64 / 3000;
+ if (1000000 - temp > 0) {
+ Sleep_Usec(1000000 - temp);
+ }
+ }
+}
+#endif
+
+void func_801C7268(void) {
+ s32 pad;
+ s32 sp20;
+ s32 sp1C;
+
+ sp20 = func_801C6EF0();
+ if (sp20 == 0) {
+ func_801C6F30();
+ }
+ B_801D9DB8 = 1;
+ B_801D9DC0 = 0;
+ if (func_801C7064() == 1) {
+ func_801C7098();
+ } else if (B_801D9DC8 != 0) {
+ B_801D9DC8 = 0;
+ }
+#if OOT_VERSION == NTSC_1_0
+ if (B_801D9DC0 != 0) {
+ sp1C = (osGetTime() - B_801D9DC0) * 64 / 3000;
+
+ // Remnants from debug statements?
+ (void)(osGetTime() - B_801D9DC0);
+ (void)((osGetTime() - B_801D9DC0) * 64 / 3000);
+ (void)((osGetTime() - B_801D9DC0) * 64 / 3000);
+
+ if (1000000 - sp1C > 0) {
+ Sleep_Usec(1000000 - sp1C);
+ }
+ }
+#else
+ if (D_801D2EA8 == 1 || B_801E0F60 == 1 || B_801E0F64 == 1) {
+ B_801D9DC0 = osGetTime();
+ }
+ func_801C7B28_ne2();
+#endif
+ if (sp20 == 0) {
+ func_801C6F78();
+ }
+}
+
+// Clears framebuffer
+void func_801C7438(void* arg0) {
+ u16* var_v0;
+
+ for (var_v0 = (u16*)arg0; var_v0 < (u16*)arg0 + SCREEN_WIDTH * SCREEN_HEIGHT; var_v0++) {
+ *var_v0 = 1;
+ }
+}
+
+void func_801C746C(void* arg0, void* arg1, void* arg2) {
+ void* sp2C;
+
+ if (arg0 != NULL || arg1 != NULL || arg2 != NULL) {
+ sp2C = (u8*)osViGetNextFramebuffer() + 0x20000000;
+ if ((u32)sp2C & 0xFFFFFF) {
+ if (B_801D9DB8 != 0) {
+ B_801D9DB8 = 0;
+ func_801C7438(sp2C);
+ B_801D9DC0 = osGetTime();
+ }
+ if (arg0 != NULL) {
+ func_801CA1F0(arg0, 96, 32, 192, 16, 11, sp2C, SCREEN_WIDTH);
+ }
+ if (arg1 != NULL) {
+ func_801CA1F0(arg1, 0, 80, 320, 64, 11, sp2C, SCREEN_WIDTH);
+ }
+ if (arg2 != NULL) {
+ func_801CA1F0(arg2, 0, 176, 320, 32, 11, sp2C, SCREEN_WIDTH);
+ }
+#if OOT_VERSION <= NTSC_1_1
+ osViBlack(0);
+#endif
+ }
+ }
+}
+
+void func_801C75BC(void* arg0, void* arg1, void* arg2) {
+ s32 temp;
+
+ if (arg0 == NULL && arg1 == NULL && arg2 == NULL) {
+ return;
+ }
+
+ if (B_801D9DB8) {}
+
+ if (arg0 != 0) {
+ B_801D9DCC = arg0;
+ }
+ if (arg1 != 0) {
+ B_801D9DD0 = arg1;
+ }
+ if (arg2 != 0) {
+ B_801D9DD4 = arg2;
+ }
+ func_801C746C(arg0, arg1, arg2);
+}
+
+void func_801C761C(void) {
+ Sleep_Msec(100);
+ func_801C746C(B_801D9DCC, B_801D9DD0, B_801D9DD4);
+}
+
+s32 func_801C7658(void) {
+ if (D_80121212 != 0) {
+ return 0;
+ }
+
+#if OOT_VERSION <= NTSC_1_1
+ StackCheck_Init(&B_801DAF88, B_801D9F88, STACK_TOP(B_801D9F88), 0, 0x100, "ddmsg");
+ osCreateThread(&B_801D9DD8, THREAD_ID_DDMSG, &func_801C711C, &B_801D9B90, STACK_TOP(B_801D9F88), THREAD_PRI_DDMSG);
+ osStartThread(&B_801D9DD8);
+#endif
+
+ osCreateMesgQueue(&B_801D9D80, B_801D9DB0, ARRAY_COUNT(B_801D9DB0));
+ osCreateMesgQueue(&B_801D9D98, B_801D9DB4, ARRAY_COUNT(B_801D9DB4));
+
+ StackCheck_Init(&B_801DBFA8, B_801DAFA8, STACK_TOP(B_801DAFA8), 0, 0x100, "n64dd");
+
+ B_801D9D50.unk_1C = &B_801D9D80;
+ B_801D9D50.unk_20 = &B_801D9D98;
+ B_801D9D50.unk_24 = THREAD_ID_N64DD;
+ B_801D9D50.unk_28 = STACK_TOP(B_801DAFA8);
+ B_801D9D50.unk_2C = THREAD_PRI_N64DD;
+ B_801D9D50.unk_00 = 1;
+
+ (&func_801C8000)(&B_801D9D50);
+
+ D_80121213 = 1;
+ func_801C6FD8();
+
+ B_801D9D50.unk_00 = 2;
+ B_801D9D50.unk_10 = 6;
+ B_801D9D50.unk_14 = &DmaMgr_DmaFromDriveRom;
+ B_801D9D50.unk_0C = &func_801C75BC;
+ (&func_801C8000)(&B_801D9D50);
+
+ B_801D9D50.unk_00 = 13;
+ (&func_801C8000)(&B_801D9D50);
+
+#if OOT_VERSION > NTSC_1_1
+ StackCheck_Init(&B_801DAF88, B_801D9F88, STACK_TOP(B_801D9F88), 0, 0x100, "ddmsg");
+ osCreateThread(&B_801D9DD8, THREAD_ID_DDMSG, &func_801C711C, &B_801D9B90, STACK_TOP(B_801D9F88), THREAD_PRI_DDMSG);
+ osStartThread(&B_801D9DD8);
+#endif
+
+ return 0;
+}
+
+s32 func_801C7818(void) {
+#if OOT_VERSION > NTSC_1_0
+ B_801D9DB8 = 1;
+ B_801D9DC0 = 0;
+#endif
+
+ B_801D9D50.unk_00 = 12;
+ (&func_801C8000)(&B_801D9D50);
+
+ while (func_801C81C4() == 0) {
+ // the number 16666 sounds like it could be 1 frame (at 60 frames per second)
+ Sleep_Usec(1000000 * 1 / 60);
+ }
+
+#if OOT_VERSION > NTSC_1_0
+ if (D_801D2EA8 == 1 || B_801E0F60 == 1 || B_801E0F64 == 1) {
+ B_801D9DC0 = osGetTime();
+ }
+ func_801C7B28_ne2();
+#endif
+
+ if (func_801C81C4() != 2) {
+ func_801C761C();
+ func_800D31A0();
+ return -3;
+ }
+
+ func_801C7018();
+ D_80121212 = 1;
+ return 0;
+}
+
+s32 func_801C78B8(void) {
+ s32 phi_v1 = func_801C7658();
+
+ if (phi_v1 == 0) {
+ phi_v1 = func_801C7818();
+ }
+ return phi_v1;
+}
+
+s32 func_801C78F0(void) {
+ B_801D9D50.unk_00 = 0;
+ return (&func_801C8000)(&B_801D9D50);
+}
+
+void func_801C7920(s32 arg0, void* arg1, s32 arg2) {
+ B_801D9D50.unk_18 = arg1;
+ B_801D9D50.unk_1C = (void*)arg0;
+ B_801D9D50.unk_20 = (void*)arg2;
+ B_801D9D50.unk_00 = 3;
+ (&func_801C8000)(&B_801D9D50);
+ osGetTime();
+ B_801D9D50.unk_00 = 6;
+ while ((&func_801C8000)(&B_801D9D50) != 0) {
+ Sleep_Usec(16666); // 100000 / 6
+ }
+ B_801D9D50.unk_00 = 7;
+ if ((&func_801C8000)(&B_801D9D50) != 0) {
+ func_800D31A0();
+ }
+}
+
+void func_801C79CC(void* arg0, s32 arg1, s32 arg2) {
+ B_801D9D50.unk_18 = arg0;
+ B_801D9D50.unk_1C = (void*)arg1;
+ B_801D9D50.unk_20 = (void*)arg2;
+ B_801D9D50.unk_00 = 4;
+ (&func_801C8000)(&B_801D9D50);
+}
+
+void func_801C7A10(LEODiskID* arg0) {
+}
+
+// Checks diskId, sets B_801D9DC8 and returns true if diskId is correct
+s32 func_801C7A1C(struct_801E0D18* arg0) {
+ static LEODiskID B_801DBFD0;
+ static s32 B_801DBFF0; // bool
+
+ func_801C7A10(&arg0->diskId);
+ if (!B_801DBFF0) {
+#if OOT_NTSC
+ if (bcmp(arg0->diskId.gameName, "EZLJ", 4) == 0 || bcmp(arg0->diskId.gameName, "EZLE", 4) == 0)
+#else
+ if (bcmp(arg0->diskId.gameName, "EZLP", 4) == 0)
+#endif
+ {
+ B_801DBFD0 = arg0->diskId;
+ B_801DBFF0 = true;
+ B_801D9DC8 = 1;
+ } else {
+ B_801D9DC8 = 2;
+ }
+ } else if (bcmp(&B_801DBFD0, &arg0->diskId, sizeof(LEODiskID)) == 0) {
+ B_801D9DC8 = 1;
+ } else {
+ B_801D9DC8 = 2;
+ }
+ return B_801D9DC8 == 1;
+}
+
+// Translates byte position to LBA and byte offset
+s32 func_801C7B48(s32 arg0, s32* arg1, s32* arg2) {
+ s32 sp2C;
+ s32 temp_v0_2;
+ s32 sp24;
+ s32 sp20;
+ s32 temp_v0;
+
+ temp_v0_2 = LeoByteToLBA(1, arg0 + 1, &sp2C);
+ if (temp_v0_2 != LEO_ERROR_GOOD) {
+ return temp_v0_2;
+ }
+ sp24 = sp2C - 1;
+ if (sp2C == 1) {
+ sp20 = 0;
+ } else {
+ temp_v0 = LeoLBAToByte(1, sp24, &sp20);
+ if (temp_v0 != LEO_ERROR_GOOD) {
+ return temp_v0;
+ }
+ }
+ *arg1 = sp24 + 1;
+ *arg2 = arg0 - sp20;
+ return LEO_ERROR_GOOD;
+}
+
+s32 func_801C7BEC(s32 startLBA) {
+ s32 bytes;
+
+ if (LeoLBAToByte(startLBA, 1, &bytes) == LEO_ERROR_GOOD) {
+ return bytes;
+ }
+ return 0;
+}
+
+// Copies bytes from disk to arg0
+void func_801C7C1C(void* dest, s32 offset, s32 size) {
+ s32 sp5C;
+ s32 sp58;
+ s32 sp54;
+ s32 sp50;
+ void* sp4C;
+ s32 var_s0;
+ s32 var_s1;
+ s32 temp_v1_2;
+
+ func_801C6FD8();
+ func_801C6F30();
+ B_801D9DB8 = 1;
+ B_801D9DC0 = 0;
+ func_801C7B48(offset, &sp5C, &sp54);
+ func_801C7B48(offset + size, &sp58, &sp50);
+ sp4C = D_801D2E50;
+ if (sp5C == sp58) {
+ func_801C7920(sp5C, sp4C, func_801C7BEC(sp5C));
+ bcopy((u8*)sp4C + sp54, dest, size);
+ } else {
+ var_s1 = 0;
+ func_801C7920(sp5C, sp4C, func_801C7BEC(sp5C));
+ bcopy((u8*)sp4C + sp54, dest, func_801C7BEC(sp5C) - sp54);
+ if (sp5C + 1 < sp58) {
+ for (var_s0 = sp5C + 1; var_s0 < sp58; var_s0++) {
+ var_s1 += func_801C7BEC(var_s0);
+ }
+ func_801C7920(sp5C + 1, (u8*)dest + func_801C7BEC(sp5C) - sp54, var_s1);
+ }
+ if (sp50 > 0) {
+ func_801C7920(sp58, sp4C, func_801C7BEC(sp58));
+ bcopy((u8*)sp4C, (u8*)dest + func_801C7BEC(sp5C) - sp54 + var_s1, sp50);
+ }
+ }
+#if OOT_VERSION == NTSC_1_0
+ if (B_801D9DC0 != 0) {
+ temp_v1_2 = (osGetTime() - B_801D9DC0) * 64 / 3000;
+ if (1000000 - temp_v1_2 > 0) {
+ Sleep_Usec(1000000 - temp_v1_2);
+ }
+ }
+#else
+ func_801C7B28_ne2();
+#endif
+ func_801C7018();
+ func_801C6F78();
+}
+
+void func_801C7E78(void) {
+}
+
+s32 func_801C7E80(void) {
+ s32 sp24;
+ s32 sp20;
+ s32 sp1C;
+ uintptr_t sp18;
+
+ if (B_801D9D48 != NULL) {
+ return -1;
+ }
+ B_801D9D48 = &B_801D9C30;
+ func_801C7C1C(B_801D9D48, 0x1060, 0x118);
+ sp24 = B_801D9D48->unk_004 - B_801D9D48->unk_000;
+ sp20 = B_801D9D48->unk_00C - B_801D9D48->unk_008;
+ sp18 = B_801D9D48->unk_008 + sp24;
+ func_801C7C1C((void*)B_801D9D48->unk_008, B_801D9D48->unk_000, sp24);
+ bzero((void*)sp18, sp20 - sp24);
+ func_800AD4C0(B_801D9D48->unk_010);
+ return 0;
+}
+
+s32 func_801C7F24(void) {
+ uintptr_t temp_a0;
+ struct_801D9C30* temp_v0;
+
+ if (B_801D9D48 == 0) {
+ return -1;
+ }
+
+ // Function from code
+ func_800AD51C();
+
+ temp_v0 = B_801D9D48;
+ temp_a0 = temp_v0->unk_008;
+ bzero((void*)temp_a0, temp_v0->unk_00C - temp_a0);
+ bzero(B_801D9D48, sizeof(struct_801D9C30));
+ B_801D9D48 = 0;
+ return 0;
+}
+
+void n64dd_SetDiskVersion(s32 arg0) {
+ if (arg0 != 0) {
+ if (B_801D9D48 == 0) {
+ func_801C7E80();
+ }
+ } else if (B_801D9D48 != 0) {
+ func_801C7F24();
+ }
+}
diff --git a/src/overlays/actors/ovl_Fishing/z_fishing.c b/src/overlays/actors/ovl_Fishing/z_fishing.c
index d7d65652e..d59c379b4 100644
--- a/src/overlays/actors/ovl_Fishing/z_fishing.c
+++ b/src/overlays/actors/ovl_Fishing/z_fishing.c
@@ -14,7 +14,7 @@
#include "cic6105.h"
#endif
-#pragma increment_block_number "gc-eu:149 gc-eu-mq:149 gc-jp:151 gc-jp-ce:151 gc-jp-mq:151 gc-us:151 gc-us-mq:151"
+#pragma increment_block_number "gc-eu:148 gc-eu-mq:148 gc-jp:150 gc-jp-ce:150 gc-jp-mq:150 gc-us:150 gc-us-mq:150"
#define FLAGS ACTOR_FLAG_4
diff --git a/tools/disasm/ntsc-1.2/variables.txt b/tools/disasm/ntsc-1.2/variables.txt
index ce115492f..d657081eb 100644
--- a/tools/disasm/ntsc-1.2/variables.txt
+++ b/tools/disasm/ntsc-1.2/variables.txt
@@ -19,8 +19,6 @@ D_80121212 = 0x80121AE2; // size:0x1 type:u8
_n64ddSegmentStart = 0x801C7740;
_n64ddSegmentRomStart = 0x00B8ADA0;
_n64ddSegmentRomEnd = 0x00B9DA70;
-D_801DA410 = 0x801DA410;
-D_801E8090 = 0x801E8090;
D_800FEE70 = 0x800FF4B0; // size:0xB0 type:n64ddStruct_800FEE70_pointers
gPadMgr = 0x8011DBD0; // size:0x468 type:PadMgr
gFaultMgr = 0x80121B60; // size:0x5D8 type:FaultMgr_v1
diff --git a/tools/fix_bss.py b/tools/fix_bss.py
index 8f5f16436..6217e822f 100755
--- a/tools/fix_bss.py
+++ b/tools/fix_bss.py
@@ -189,6 +189,7 @@ def compare_pointers(version: str) -> dict[Path, BssSection]:
if not (
mapfile_segment.name.startswith("..boot")
or mapfile_segment.name.startswith("..code")
+ or mapfile_segment.name.startswith("..n64dd")
or mapfile_segment.name.startswith("..ovl_")
):
continue
diff --git a/undefined_syms.txt b/undefined_syms.txt
index b5b1b772f..deba8affd 100644
--- a/undefined_syms.txt
+++ b/undefined_syms.txt
@@ -9,28 +9,3 @@ D_0F000000 = 0x0F000000;
// z_bg_mjin
D_06000000 = 0x06000000;
-
-#if PLATFORM_N64
-
-// code_n64dd_800AD410.c
-func_801C6E80 = 0x801C7740;
-func_801C7C1C = 0x801C8510;
-D_801DA410 = 0x801DA410;
-D_801E8090 = 0x801E8090;
-
-// game.c
-func_801C7E78 = 0x801C86F0;
-func_801C6EA0 = 0x801C7760;
-
-// z_en_mag.c
-func_801C70FC = 0x801C79BC;
-
-// z_setup.c
-n64dd_SetDiskVersion = 0x801C8808;
-
-// z_title.c
-func_801C7818 = 0x801C8090;
-func_801C7268 = 0x801C7BC4;
-func_801C7658 = 0x801C7ED0;
-
-#endif