diff --git a/Makefile b/Makefile
index ee01fb00..12195f5c 100644
--- a/Makefile
+++ b/Makefile
@@ -647,6 +647,10 @@ endif
ifeq ($(WINDOWS_BUILD),1)
CC_CHECK := $(CC) -fsyntax-only -fsigned-char $(BACKEND_CFLAGS) $(INCLUDE_CFLAGS) -Wall -Wextra -Wno-format-security $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -DWINSOCK
CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(BACKEND_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv -DWINSOCK
+
+ ifeq ($(TARGET_BITS), 32)
+ BACKEND_LDFLAGS += -ldbghelp
+ endif
else ifeq ($(TARGET_WEB),1)
CC_CHECK := $(CC) -fsyntax-only -fsigned-char $(BACKEND_CFLAGS) $(INCLUDE_CFLAGS) -Wall -Wextra -Wno-format-security $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -s USE_SDL=2
CFLAGS := $(OPT_FLAGS) $(INCLUDE_CFLAGS) $(BACKEND_CFLAGS) $(VERSION_CFLAGS) $(GRUCODE_CFLAGS) -fno-strict-aliasing -fwrapv -s USE_SDL=2
@@ -1160,7 +1164,11 @@ $(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c
$(BUILD_DIR)/%.o: %.s
$(AS) $(ASFLAGS) -MD $(BUILD_DIR)/$*.d -o $@ $<
-
+ifeq ($(WINDOWS_BUILD),1)
+all: COOP_EXE_MAP
+COOP_EXE_MAP: $(EXE)
+ @objdump -t $(EXE) > $(BUILD_DIR)/coop.map
+endif
$(EXE): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(BUILD_DIR)/$(RPC_LIBS) $(BUILD_DIR)/$(DISCORD_SDK_LIBS) $(BUILD_DIR)/$(MOD_DIR)
$(LD) -L $(BUILD_DIR) -o $@ $(O_FILES) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(LDFLAGS) $(EXTRA_INCLUDES)
diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj
index 6ff0acf0..4a31efa7 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj
+++ b/build-windows-visual-studio/sm64ex.vcxproj
@@ -453,6 +453,7 @@
+
diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters
index b79a4bff..c88a8c7c 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj.filters
+++ b/build-windows-visual-studio/sm64ex.vcxproj.filters
@@ -4881,6 +4881,9 @@
Source Files\src\pc\network
+
+ Source Files\src\pc
+
diff --git a/src/pc/crash_handler.c b/src/pc/crash_handler.c
new file mode 100644
index 00000000..385de3f8
--- /dev/null
+++ b/src/pc/crash_handler.c
@@ -0,0 +1,494 @@
+// Adapted from PeachyPeach's sm64pc-omm
+
+#if defined(_WIN32)
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "config.h"
+#include "pc/gfx/gfx_window_manager_api.h"
+#include "pc/gfx/gfx_dxgi.h"
+#include "pc/gfx/gfx_sdl.h"
+#include "pc/gfx/gfx_pc.h"
+#include "game/game_init.h"
+#include "game/ingame_menu.h"
+#include "game/segment2.h"
+#include "game/mario.h"
+#include "gfx_dimensions.h"
+#include "src/pc/djui/djui.h"
+#include "pc/network/network.h"
+#include "pc/gfx/gfx_rendering_api.h"
+#include "dbghelp.h"
+
+#if IS_64_BIT
+ #define CRASH_HANDLER_TYPE LONG
+ #define SYMBOL_INCREMENT 16
+ #define SYMBOL_SCAN_FORMAT "%016llX"
+ #define MACHINE_TYPE IMAGE_FILE_MACHINE_AMD64
+ #define ARCHITECTURE_STR "64-bit"
+#else
+ #define CRASH_HANDLER_TYPE LONG WINAPI
+ #define SYMBOL_INCREMENT 9
+ #define SYMBOL_SCAN_FORMAT "%08X"
+ #define MACHINE_TYPE IMAGE_FILE_MACHINE_I386
+ #define ARCHITECTURE_STR "32-bit"
+#endif
+
+#define PTR long long unsigned int)(uintptr_t
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#define MEMNEW(typ, cnt) calloc(sizeof(typ), cnt)
+#define STRING(str, size, fmt, ...) char str[size]; snprintf(str, size, fmt, __VA_ARGS__);
+
+#define DEF(smex, smms, r96a, xalo, sm74, smsr) smex
+#define load_gfx_memory_pool() DEF(config_gfx_pool(), config_gfx_pool(), config_gfx_pool(), select_gfx_pool(), select_gfx_pool(), select_gfx_pool())
+#define init_scene_rendering() DEF(init_render_image(), init_render_image(), init_render_image(), init_rcp(), init_rcp(), init_rcp())
+
+static struct {
+ u32 code;
+ const char *error;
+ const char *message;
+} sCrashHandlerErrors[] = {
+ { EXCEPTION_ACCESS_VIOLATION, "Segmentation Fault", "The game tried to %s at address 0x%016llX." },
+ { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array Out Of Bounds", "The game tried to access an element out of the array bounds." },
+ { EXCEPTION_DATATYPE_MISALIGNMENT, "Data Misalignment", "The game tried to access misaligned data." },
+ { EXCEPTION_BREAKPOINT, "Breakpoint", "The game reached a breakpoint." },
+ { EXCEPTION_FLT_DENORMAL_OPERAND, "Float Denormal Operand", "The game tried to perform a floating point operation with a denormal operand." },
+ { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Float Division By Zero", "The game tried to divide a floating point number by zero." },
+ { EXCEPTION_FLT_INEXACT_RESULT, "Float Inexact Result", "The game couldn't represent the result of a floating point operation as a decimal fraction." },
+ { EXCEPTION_FLT_INVALID_OPERATION, "Float Invalid Operation", "The game tried to perform an invalid floating point operation." },
+ { EXCEPTION_FLT_OVERFLOW, "Float Overflow", "An overflow occurred with a floating point number." },
+ { EXCEPTION_FLT_STACK_CHECK, "Float Stack Overflow", "The game performed a floating point operation resulting in a stack overflow." },
+ { EXCEPTION_FLT_UNDERFLOW, "Float Underflow", "An underflow occurred with a floating point number." },
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal Instruction", "The game tried to execute an invalid instruction." },
+ { EXCEPTION_IN_PAGE_ERROR, "Page Error", "The game tried to %s at address 0x%016llX." },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer Division By Zero", "The game tried to divide an integer by zero." },
+ { EXCEPTION_INT_OVERFLOW, "Integer Overflow", "An overflow occurred with an integer." },
+ { EXCEPTION_PRIV_INSTRUCTION, "Instruction Not Allowed", "The game tried to execute an invalid instruction." },
+ { EXCEPTION_STACK_OVERFLOW, "Stack Overflow", "The game performed an operation resulting in a stack overflow." },
+ { 0, "Unknown Exception", "An unknown exception occurred." },
+};
+
+typedef struct {
+ s32 x, y;
+ u8 r, g, b;
+ char s[128];
+} CrashHandlerText;
+static CrashHandlerText sCrashHandlerText[128 + 256];
+
+#define crash_handler_set_text(_x_, _y_, _r_, _g_, _b_, _fmt_, ...) \
+{ \
+ if (_x_ == -1) { \
+ pText->x = ((pText - 1)->x + strlen((pText - 1)->s) * 4); \
+ } else { \
+ pText->x = _x_; \
+ } \
+ pText->y = _y_; \
+ pText->r = _r_; \
+ pText->g = _g_; \
+ pText->b = _b_; \
+ snprintf(pText->s, 128, _fmt_, __VA_ARGS__); \
+ pText++; \
+}
+
+void render_create_dl_ortho_matrix() {
+ static const Mtx sIdentMatrix = { {
+ { 1.f, 0.f, 0.f, 0.f },
+ { 0.f, 1.f, 0.f, 0.f },
+ { 0.f, 0.f, 1.f, 0.f },
+ { 0.f, 0.f, 0.f, 1.f },
+ } };
+ static const Mtx sOrthoMatrix = { {
+ { 2.f / SCREEN_WIDTH, 0.f, 0.f, 0.f },
+ { 0.f, 2.f / SCREEN_HEIGHT, 0.f, 0.f },
+ { 0.f, 0.f, -0.1f, 0.f },
+ { -1.f, -1.f, 0.f, 1.f },
+ } };
+ gSPPerspNormalize(gDisplayListHead++, 0xFFFF);
+ gSPMatrix(gDisplayListHead++, &sIdentMatrix, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
+ gSPMatrix(gDisplayListHead++, &sIdentMatrix, G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
+ gSPMatrix(gDisplayListHead++, &sOrthoMatrix, G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH);
+}
+
+static void crash_handler_produce_one_frame() {
+ // Start frame
+ gfx_start_frame();
+ load_gfx_memory_pool();
+ init_scene_rendering();
+
+ float minAspectRatio = 1.743468f;
+ float aspectScale = 1.0f;
+ if (gfx_current_dimensions.aspect_ratio < minAspectRatio) {
+ aspectScale = gfx_current_dimensions.aspect_ratio / minAspectRatio;
+ }
+
+ // Fix scaling issues
+ extern Vp D_8032CF00;
+ gSPViewport(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&D_8032CF00));
+ gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, BORDER_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT - BORDER_HEIGHT);
+
+ // Clear screen
+ create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_LEFT_EDGE(0), 240.f, 0.f);
+ create_dl_scale_matrix(MENU_MTX_NOPUSH, (GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_HEIGHT) / 130.f, 3.f, 1.f);
+ gDPSetEnvColor(gDisplayListHead++, 0x02, 0x06, 0x0F, 0xFF);
+ gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box);
+ gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
+
+ // Print text
+ const struct DjuiFont* font = gDjuiFonts[0];
+ if (font->textBeginDisplayList != NULL) {
+ gSPDisplayList(gDisplayListHead++, font->textBeginDisplayList);
+ }
+
+ for (CrashHandlerText* text = sCrashHandlerText; text->s[0] != 0; ++text) {
+ s32 x = GFX_DIMENSIONS_RECT_FROM_LEFT_EDGE(text->x * aspectScale);
+ s32 y = SCREEN_HEIGHT - 8 - text->y * aspectScale;
+ gDPPipeSync(gDisplayListHead++);
+ gDPSetEnvColor(gDisplayListHead++, text->r, text->g, text->b, 0xFF);
+ create_dl_translation_matrix(DJUI_MTX_PUSH, x, y, 0);
+
+ // translate scale
+ f32 fontSize = 10.0f * aspectScale;
+ create_dl_scale_matrix(DJUI_MTX_NOPUSH, fontSize, fontSize, 1.0f);
+
+ // set color
+ gDPSetEnvColor(gDisplayListHead++, text->r, text->g, text->b, 0xFF);
+
+ // render the line
+ f32 addX = 0;
+ size_t length = strlen(text->s);
+ for (size_t i = 0; i < length; i++) {
+ char c = text->s[i];
+ f32 charWidth = 0.4f;
+
+ if (c <= 0x20 || c >= 0x7F) {
+ addX += charWidth;
+ continue;
+ }
+
+ if (addX != 0) {
+ create_dl_translation_matrix(DJUI_MTX_NOPUSH, addX, 0, 0);
+ addX = 0;
+ }
+ // render
+ font->render_char(c);
+ create_dl_translation_matrix(DJUI_MTX_NOPUSH, charWidth, 0, 0);
+ }
+
+ // pop
+ gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
+ }
+
+ // Render frame
+ end_master_display_list();
+ alloc_display_list(0);
+ display_and_vsync();
+ gfx_end_frame();
+}
+
+#if !IS_64_BIT
+static ULONG CaptureStackWalkBackTrace(CONTEXT* ctx, DWORD FramesToSkip, DWORD FramesToCapture, void* BackTrace[]) {
+
+ HANDLE process = GetCurrentProcess();
+ HANDLE thread = GetCurrentThread();
+
+ STACKFRAME64 stack;
+ memset(&stack, 0, sizeof(STACKFRAME64));
+#if IS_64_BIT
+ stack.AddrPC.Offset = (*ctx).Rip;
+ stack.AddrPC.Mode = AddrModeFlat;
+ stack.AddrStack.Offset = (*ctx).Rsp;
+ stack.AddrStack.Mode = AddrModeFlat;
+ stack.AddrFrame.Offset = (*ctx).Rbp;
+ stack.AddrFrame.Mode = AddrModeFlat;
+#else
+ stack.AddrPC.Offset = (*ctx).Eip;
+ stack.AddrPC.Mode = AddrModeFlat;
+ stack.AddrStack.Offset = (*ctx).Esp;
+ stack.AddrStack.Mode = AddrModeFlat;
+ stack.AddrFrame.Offset = (*ctx).Ebp;
+ stack.AddrFrame.Mode = AddrModeFlat;
+#endif
+
+ ULONG frame = 0;
+ for (frame = 0; ; frame++)
+ {
+ if (!StackWalk64(MACHINE_TYPE, process, thread, &stack, ctx, NULL, NULL, NULL, NULL)) { break; }
+ if (frame < FramesToSkip || frame >= FramesToCapture) { continue; }
+ BackTrace[frame+1] = (void*)(intptr_t)stack.AddrPC.Offset;
+ }
+ return frame;
+}
+#endif
+
+static void crash_handler_add_info_str(CrashHandlerText** pTextP, f32 x, f32 y, char* title, char* value) {
+ CrashHandlerText* pText = *pTextP;
+ crash_handler_set_text(x, y, 0xFF, 0xFF, 0x00, "%s", title);
+ crash_handler_set_text(-1, y, 0xFF, 0xFF, 0xFF, "%s", ": ");
+ crash_handler_set_text(-1, y, 0x00, 0xFF, 0xFF, "%s", value);
+ *pTextP = pText;
+}
+
+static void crash_handler_add_info_int(CrashHandlerText** pTextP, f32 x, f32 y, char* title, int value) {
+ CrashHandlerText* pText = *pTextP;
+ crash_handler_set_text(x, y, 0xFF, 0xFF, 0x00, "%s", title);
+ crash_handler_set_text(-1, y, 0xFF, 0xFF, 0xFF, "%s", ": ");
+ crash_handler_set_text(-1, y, 0x00, 0xFF, 0xFF, "%d", value);
+ *pTextP = pText;
+}
+
+static CRASH_HANDLER_TYPE crash_handler(EXCEPTION_POINTERS *ExceptionInfo) {
+ memset(sCrashHandlerText, 0, sizeof(sCrashHandlerText));
+ CrashHandlerText *pText = &sCrashHandlerText[0];
+ gDjuiDisabled = true;
+
+ // Exception report
+ crash_handler_set_text(8, -4, 0xFF, 0x80, 0x00, "%s", "Please report this crash with a consistent way to reproduce it.");
+
+ // Exception address, code, type and info
+ if (ExceptionInfo && ExceptionInfo->ExceptionRecord) {
+ PEXCEPTION_RECORD er = ExceptionInfo->ExceptionRecord;
+ crash_handler_set_text( 8, 4, 0xFF, 0x00, 0x00, "%s", "Exception occurred at address ");
+ crash_handler_set_text(-1, 4, 0xFF, 0xFF, 0x00, "0x%016llX", (PTR) er->ExceptionAddress);
+ crash_handler_set_text(-1, 4, 0xFF, 0x00, 0x00, "%s", " with error code ");
+ crash_handler_set_text(-1, 4, 0xFF, 0x00, 0xFF, "0x%08X", (u32) er->ExceptionCode);
+ crash_handler_set_text(-1, 4, 0xFF, 0x00, 0x00, "%s", ":");
+ for (s32 i = 0; i != ARRAY_SIZE(sCrashHandlerErrors); ++i) {
+ if (sCrashHandlerErrors[i].code == (u32) er->ExceptionCode || sCrashHandlerErrors[i].code == 0) {
+ crash_handler_set_text( 8, 12, 0xFF, 0x00, 0x00, "%s", sCrashHandlerErrors[i].error);
+ crash_handler_set_text(-1, 12, 0xFF, 0xFF, 0xFF, "%s", " - ");
+ if (er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || er->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
+ crash_handler_set_text(-1, 12, 0xFF, 0xFF, 0xFF, sCrashHandlerErrors[i].message, (er->ExceptionInformation[0] ? "write" : "read"), (PTR)er->ExceptionInformation[1]);
+ } else {
+ crash_handler_set_text(-1, 12, 0xFF, 0xFF, 0xFF, "%s", sCrashHandlerErrors[i].message);
+ }
+ break;
+ }
+ }
+ } else {
+ crash_handler_set_text(8, 4, 0xFF, 0x00, 0x00, "%s", "An unknown exception occurred somewhere in the game's code.");
+ crash_handler_set_text(8, 12, 0x80, 0x80, 0x80, "%s", "Unable to retrieve the exception info.");
+ }
+
+ // Registers
+ if (ExceptionInfo && ExceptionInfo->ContextRecord) {
+ PCONTEXT cr = ExceptionInfo->ContextRecord;
+ crash_handler_set_text( 8, 22, 0xFF, 0xFF, 0xFF, "%s", "Registers:");
+#if IS_64_BIT
+ crash_handler_set_text( 8, 30, 0xFF, 0xFF, 0xFF, "RSP: 0x%016llX", (PTR)cr->Rsp);
+ crash_handler_set_text(-1, 30, 0xFF, 0xFF, 0xFF, " RBP: 0x%016llX", (PTR)cr->Rbp);
+ crash_handler_set_text(-1, 30, 0xFF, 0xFF, 0xFF, " RIP: 0x%016llX", (PTR)cr->Rip);
+ crash_handler_set_text( 8, 38, 0xFF, 0xFF, 0xFF, "RAX: 0x%016llX", (PTR)cr->Rax);
+ crash_handler_set_text(-1, 38, 0xFF, 0xFF, 0xFF, " RBX: 0x%016llX", (PTR)cr->Rbx);
+ crash_handler_set_text(-1, 38, 0xFF, 0xFF, 0xFF, " RCX: 0x%016llX", (PTR)cr->Rcx);
+ crash_handler_set_text(-1, 38, 0xFF, 0xFF, 0xFF, " RDX: 0x%016llX", (PTR)cr->Rdx);
+ crash_handler_set_text( 8, 46, 0xFF, 0xFF, 0xFF, "R08: 0x%016llX", (PTR)cr->R8);
+ crash_handler_set_text(-1, 46, 0xFF, 0xFF, 0xFF, " R09: 0x%016llX", (PTR)cr->R9);
+ crash_handler_set_text(-1, 46, 0xFF, 0xFF, 0xFF, " R10: 0x%016llX", (PTR)cr->R10);
+ crash_handler_set_text(-1, 46, 0xFF, 0xFF, 0xFF, " R11: 0x%016llX", (PTR)cr->R11);
+ crash_handler_set_text( 8, 54, 0xFF, 0xFF, 0xFF, "R12: 0x%016llX", (PTR)cr->R12);
+ crash_handler_set_text(-1, 54, 0xFF, 0xFF, 0xFF, " R13: 0x%016llX", (PTR)cr->R13);
+ crash_handler_set_text(-1, 54, 0xFF, 0xFF, 0xFF, " R14: 0x%016llX", (PTR)cr->R14);
+ crash_handler_set_text(-1, 54, 0xFF, 0xFF, 0xFF, " R15: 0x%016llX", (PTR)cr->R15);
+ crash_handler_set_text( 8, 62, 0xFF, 0xFF, 0xFF, "RSI: 0x%016llX", (PTR)cr->Rsi);
+ crash_handler_set_text(-1, 62, 0xFF, 0xFF, 0xFF, " RDI: 0x%016llX", (PTR)cr->Rdi);
+#else
+ crash_handler_set_text( 8, 30, 0xFF, 0xFF, 0xFF, "EAX: 0x%016llX", (PTR)cr->Eax);
+ crash_handler_set_text(-1, 30, 0xFF, 0xFF, 0xFF, " EBX: 0x%016llX", (PTR)cr->Ebx);
+ crash_handler_set_text(-1, 30, 0xFF, 0xFF, 0xFF, " ECX: 0x%016llX", (PTR)cr->Ecx);
+ crash_handler_set_text( 8, 38, 0xFF, 0xFF, 0xFF, "EDX: 0x%016llX", (PTR)cr->Edx);
+ crash_handler_set_text(-1, 38, 0xFF, 0xFF, 0xFF, " ESI: 0x%016llX", (PTR)cr->Esi);
+ crash_handler_set_text(-1, 38, 0xFF, 0xFF, 0xFF, " EDI: 0x%016llX", (PTR)cr->Edi);
+ crash_handler_set_text(-1, 38, 0xFF, 0xFF, 0xFF, " EBP: 0x%016llX", (PTR)cr->Ebp);
+ crash_handler_set_text( 8, 46, 0xFF, 0xFF, 0xFF, "EIP: 0x%016llX", (PTR)cr->Eip);
+ crash_handler_set_text(-1, 46, 0xFF, 0xFF, 0xFF, " ESP: 0x%016llX", (PTR)cr->Esp);
+ crash_handler_set_text(-1, 46, 0xFF, 0xFF, 0xFF, " CS: 0x%016llX", (PTR)cr->SegCs);
+ crash_handler_set_text(-1, 46, 0xFF, 0xFF, 0xFF, " DS: 0x%016llX", (PTR)cr->SegDs);
+ crash_handler_set_text( 8, 54, 0xFF, 0xFF, 0xFF, " ES: 0x%016llX", (PTR)cr->SegEs);
+ crash_handler_set_text(-1, 54, 0xFF, 0xFF, 0xFF, " FS: 0x%016llX", (PTR)cr->SegFs);
+ crash_handler_set_text(-1, 54, 0xFF, 0xFF, 0xFF, " GS: 0x%016llX", (PTR)cr->SegGs);
+ crash_handler_set_text(-1, 54, 0xFF, 0xFF, 0xFF, " SS: 0x%016llX", (PTR)cr->SegSs);
+ crash_handler_set_text( 8, 62, 0xFF, 0xFF, 0xFF, "DR0: 0x%016llX", (PTR)cr->Dr0);
+ crash_handler_set_text(-1, 62, 0xFF, 0xFF, 0xFF, " DR1: 0x%016llX", (PTR)cr->Dr1);
+#endif
+ } else {
+ crash_handler_set_text(8, 22, 0xFF, 0xFF, 0xFF, "%s", "Registers:");
+ crash_handler_set_text(8, 30, 0x80, 0x80, 0x80, "%s", "Unable to access the registers.");
+ }
+
+ // Stack trace
+ crash_handler_set_text(8, 72, 0xFF, 0xFF, 0xFF, "%s", "Stack trace:");
+ if (ExceptionInfo && ExceptionInfo->ContextRecord) {
+ static const char sGlobalFunctionIdentifier[] = "(sec1)(fl0x00)(ty20)(scl2)(nx0)0x";
+ static const char sStaticFunctionIdentifier[] = "(sec1)(fl0x00)(ty20)(scl3)(nx0)0x";
+ typedef struct Symbol { uintptr_t offset; char name[128]; struct Symbol *next; } Symbol;
+ Symbol *symbols = NULL;
+ Symbol* symbol0 = NULL;
+
+ // Load symbols
+ char filename[256] = { 0 };
+ if (GetModuleFileName(NULL, filename, sizeof(filename))) {
+ int index = strlen(filename);
+ while (--index > 0) {
+ if (filename[index] == '\\') {
+ filename[index] = '\0';
+ break;
+ }
+ }
+ strncat(filename, "\\coop.map", 255);
+ } else {
+ snprintf(filename, 256, "%s", "coop.map");
+ }
+
+ FILE *f = fopen(filename, "r");
+ if (f) {
+ char buffer[1024];
+ while (fgets(buffer, 1024, f)) {
+
+ // Remove spaces
+ char bufferNoSpace[1024] = { 0 };
+ for (char *p0 = buffer, *p1 = bufferNoSpace; *p0 != 0; ++p0) {
+ if (*p0 > 0x20) {
+ *(p1++) = *p0;
+ }
+ }
+
+ // Try to find identifiers
+ char *id0 = strstr(bufferNoSpace, sGlobalFunctionIdentifier);
+ char *id1 = strstr(bufferNoSpace, sStaticFunctionIdentifier);
+ if (id0 || id1) {
+ char *addr = (char *) max((uintptr_t) id0, (uintptr_t) id1) + sizeof(sGlobalFunctionIdentifier) - 1;
+ char* name = addr + SYMBOL_INCREMENT;
+
+ // New symbol
+ Symbol *newSymbol = MEMNEW(Symbol, 1);
+ snprintf(newSymbol->name, 128, "%s", name); *name = 0;
+ sscanf(addr, SYMBOL_SCAN_FORMAT, &newSymbol->offset);
+ newSymbol->next = NULL;
+
+ // Store symbol
+ if (symbols == NULL) {
+ symbols = newSymbol;
+ } else {
+ for (Symbol *symbol = symbols;; symbol = symbol->next) {
+ if (symbol->next == NULL) {
+ symbol->next = newSymbol;
+ break;
+ }
+ if (symbol->next->offset > newSymbol->offset) {
+ newSymbol->next = symbol->next;
+ symbol->next = newSymbol;
+ break;
+ }
+ }
+ }
+
+ // Reference
+ if (memcmp(newSymbol->name, "set_mario_action", sizeof("set_mario_action")) == 0) {
+ symbol0 = newSymbol;
+ }
+ }
+ }
+ fclose(f);
+ }
+ uintptr_t addr0 = (symbol0 ? ((uintptr_t) set_mario_action - symbol0->offset) : 0);
+
+ // Unwind and print call stack
+ void *stack[64];
+#if IS_64_BIT
+ s32 frames = CaptureStackBackTrace(6, 64, stack, NULL);
+#else
+ s32 frames = CaptureStackWalkBackTrace(ExceptionInfo->ContextRecord, 0, 64, stack);
+#endif
+ for (s32 i = 1, j = 0; i < frames && j < 15; ++i) {
+ s32 y = 80 + j++ * 8;
+ crash_handler_set_text( 8, y, 0xFF, 0xFF, 0x00, "0x%016llX", (PTR) stack[i]);
+ crash_handler_set_text(-1, y, 0xFF, 0xFF, 0xFF, "%s", ": ");
+ for (Symbol *symbol = symbols;; symbol = symbol->next) {
+ if (symbol == NULL || symbol->next == NULL) {
+ if (j != 0) {
+ crash_handler_set_text(-1, y, 0x00, 0xFF, 0xFF, "%s", "????");
+ }
+ break;
+ } else {
+ uintptr_t offset = (uintptr_t) stack[i] - addr0;
+ if (symbol->next->offset > offset) {
+ crash_handler_set_text(-1, y, 0x00, 0xFF, 0xFF, "%s", symbol->name);
+ crash_handler_set_text(-1, y, 0xFF, 0xFF, 0xFF, " + 0x%llX", (PTR)(offset - symbol->offset));
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ crash_handler_set_text(8, 116, 0x80, 0x80, 0x80, "%s", "Unable to unwind the call stack.");
+ }
+
+ // Info
+ crash_handler_add_info_str(&pText, 340, -4 + (8 * 0), "Arch", ARCHITECTURE_STR);
+ crash_handler_add_info_str(&pText, 340, -4 + (8 * 1), "Network", (gNetworkType == NT_SERVER) ? "Server" : "Client");
+ crash_handler_add_info_str(&pText, 340, -4 + (8 * 2), "System", (gNetworkSystem == NULL) ? "null" : gNetworkSystem->name);
+ crash_handler_add_info_int(&pText, 340, -4 + (8 * 3), "Players", network_player_connected_count());
+ int syncObjects = 0;
+ for (int i = 0; i < MAX_SYNC_OBJECTS; i++) {
+ if (gSyncObjects[i].o != NULL) { syncObjects++; }
+ }
+ crash_handler_add_info_int(&pText, 340, -4 + (8 * 4), "SyncObj", syncObjects);
+
+ // Packets
+ crash_handler_set_text(260, 64, 0xFF, 0xFF, 0xFF, "%s", "Packets:");
+ {
+ int x = 260;
+ int y = 72;
+ u8 index = gDebugPacketOnBuffer;
+ for (int i = 0; i < 256; i++) {
+ u8 brightness = (gDebugPacketIdBuffer[index] * 5) % 200;
+ if (gDebugPacketSentBuffer[index]) {
+ crash_handler_set_text(x, y, 0xFF, 0xFF, brightness, "%02X", gDebugPacketIdBuffer[index]);
+ } else {
+ crash_handler_set_text(x, y, brightness, 0xFF, 0xFF, "%02X", gDebugPacketIdBuffer[index]);
+ }
+ index--;
+ y += 8;
+ if (y >= 72 + (16 * 8)) {
+ y = 72;
+ x += 10;
+ }
+ }
+ }
+
+ // sounds
+ if (SDL_WasInit(SDL_INIT_AUDIO) || SDL_InitSubSystem(SDL_INIT_AUDIO) == 0) {
+ SDL_AudioSpec want, have;
+ want.freq = 32000;
+ want.format = AUDIO_S16SYS;
+ want.channels = 1;
+ want.samples = 0x200;
+ want.callback = NULL;
+ want.userdata = NULL;
+ s32 device = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
+ if (device) {
+ SDL_PauseAudioDevice(device, 0);
+ }
+ }
+
+ // Main loop
+ while (true) {
+#if defined(WAPI_SDL1) || defined(WAPI_SDL2)
+ gfx_sdl.main_loop(crash_handler_produce_one_frame);
+#elif defined(WAPI_DXGI)
+ gfx_dxgi.main_loop(crash_handler_produce_one_frame);
+#endif
+ }
+ exit(0);
+}
+
+__attribute__((constructor)) static void init_crash_handler() {
+ SetUnhandledExceptionFilter(crash_handler);
+}
+
+#endif
diff --git a/src/pc/djui/djui.c b/src/pc/djui/djui.c
index a49434d8..15ed389c 100644
--- a/src/pc/djui/djui.c
+++ b/src/pc/djui/djui.c
@@ -9,6 +9,7 @@ static Gfx* sSavedDisplayListHead = NULL;
struct DjuiRoot* gDjuiRoot = NULL;
static struct DjuiText* sDjuiPauseOptions = NULL;
bool gDjuiInMainMenu = true;
+bool gDjuiDisabled = false;
void djui_init(void) {
gDjuiRoot = djui_root_create();
@@ -48,6 +49,7 @@ void djui_render_patch(void) {
}
void djui_render(void) {
+ if (gDjuiDisabled) { return; }
sSavedDisplayListHead = gDisplayListHead;
create_dl_ortho_matrix();
diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h
index 622ebfd1..d58fd428 100644
--- a/src/pc/djui/djui.h
+++ b/src/pc/djui/djui.h
@@ -59,6 +59,7 @@
extern struct DjuiRoot* gDjuiRoot;
extern bool gDjuiInMainMenu;
+extern bool gDjuiDisabled;
void djui_init(void);
void djui_connect_menu_open(void);
diff --git a/src/pc/network/discord/discord.c b/src/pc/network/discord/discord.c
index ef229727..5e35d649 100644
--- a/src/pc/network/discord/discord.c
+++ b/src/pc/network/discord/discord.c
@@ -230,4 +230,5 @@ struct NetworkSystem gNetworkSystemDiscord = {
.send = ns_discord_network_send,
.shutdown = ns_discord_shutdown,
.requireServerBroadcast = false,
+ .name = "Discord",
};
diff --git a/src/pc/network/network.c b/src/pc/network/network.c
index 461a5bc7..9bdd3cb7 100644
--- a/src/pc/network/network.c
+++ b/src/pc/network/network.c
@@ -38,6 +38,10 @@ u32 gNetworkAreaTimer = 0;
void* gNetworkServerAddr = NULL;
bool gNetworkSentJoin = false;
+u8 gDebugPacketIdBuffer[256] = { 0xFF };
+u8 gDebugPacketSentBuffer[256] = { 0 };
+u8 gDebugPacketOnBuffer = 0;
+
struct StringLinkedList gRegisteredMods = { 0 };
struct ServerSettings gServerSettings = {
@@ -137,6 +141,17 @@ void network_on_loaded_area(void) {
}
}
+static void network_remember_debug_packet(u8 id, bool sent) {
+ if (id == PACKET_ACK) { return; }
+ if (id == PACKET_KEEP_ALIVE) { return; }
+ if (id == PACKET_DEBUG_SYNC) { return; }
+ if (id == PACKET_PLAYER && id == gDebugPacketIdBuffer[gDebugPacketOnBuffer]) { return; }
+ if (id == PACKET_OBJECT && id == gDebugPacketIdBuffer[gDebugPacketOnBuffer]) { return; }
+ gDebugPacketOnBuffer++;
+ gDebugPacketIdBuffer[gDebugPacketOnBuffer] = id;
+ gDebugPacketSentBuffer[gDebugPacketOnBuffer] = sent;
+}
+
bool network_allow_unknown_local_index(enum PacketType packetType) {
return (packetType == PACKET_JOIN_REQUEST)
|| (packetType == PACKET_KICK)
@@ -230,6 +245,8 @@ void network_send_to(u8 localIndex, struct Packet* p) {
}
p->sent = true;
+ network_remember_debug_packet(p->packetType, true);
+
gNetworkPlayers[localIndex].lastSent = clock_elapsed();
}
@@ -293,6 +310,8 @@ void network_receive(u8 localIndex, void* addr, u8* data, u16 dataLength) {
return;
}
+ network_remember_debug_packet(p.buffer[0], false);
+
// execute packet
packet_receive(&p);
}
diff --git a/src/pc/network/network.h b/src/pc/network/network.h
index b8c5a020..673363ea 100644
--- a/src/pc/network/network.h
+++ b/src/pc/network/network.h
@@ -51,6 +51,7 @@ struct NetworkSystem {
int (*send)(u8 localIndex, void* addr, u8* data, u16 dataLength);
void (*shutdown)(void);
bool requireServerBroadcast;
+ char* name;
};
struct SyncObject {
@@ -110,6 +111,10 @@ extern struct ServerSettings gServerSettings;
extern struct StringLinkedList gRegisteredMods;
extern bool gNetworkSentJoin;
+extern u8 gDebugPacketIdBuffer[];
+extern u8 gDebugPacketSentBuffer[];
+extern u8 gDebugPacketOnBuffer;
+
// network.c
void network_set_system(enum NetworkSystemType nsType);
bool network_init(enum NetworkType inNetworkType);
diff --git a/src/pc/network/socket/socket.c b/src/pc/network/socket/socket.c
index 6b075c2c..55e07d62 100644
--- a/src/pc/network/socket/socket.c
+++ b/src/pc/network/socket/socket.c
@@ -176,4 +176,5 @@ struct NetworkSystem gNetworkSystemSocket = {
.send = ns_socket_send,
.shutdown = ns_socket_shutdown,
.requireServerBroadcast = true,
+ .name = "Socket",
};