From 6925db864c0f12685ee1f66f2398fbe0e4201fb6 Mon Sep 17 00:00:00 2001 From: MysterD Date: Sat, 12 Feb 2022 12:27:20 -0800 Subject: [PATCH] Added a custom version of PeachyPeach's crash screen for Windows --- Makefile | 10 +- build-windows-visual-studio/sm64ex.vcxproj | 1 + .../sm64ex.vcxproj.filters | 3 + src/pc/crash_handler.c | 494 ++++++++++++++++++ src/pc/djui/djui.c | 2 + src/pc/djui/djui.h | 1 + src/pc/network/discord/discord.c | 1 + src/pc/network/network.c | 19 + src/pc/network/network.h | 5 + src/pc/network/socket/socket.c | 1 + 10 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 src/pc/crash_handler.c 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", };