diff --git a/stratosphere/fatal/source/fatal_config.cpp b/stratosphere/fatal/source/fatal_config.cpp index 5583e7a0b..f2adae661 100644 --- a/stratosphere/fatal/source/fatal_config.cpp +++ b/stratosphere/fatal/source/fatal_config.cpp @@ -48,6 +48,28 @@ IEvent *GetFatalSettingsEvent() { return g_fatal_settings_event; } +static void SetupConfigLanguages() { + FatalConfig *config = GetFatalConfig(); + + /* Defaults. */ + config->error_msg = u8"Error Code: 2%03d-%04d (0x%x)\n"; + + if (config->quest_flag) { + config->error_desc = u8"Please call 1-800-875-1852 for service.\n"; + } else { + config->error_desc = u8"An error has occured.\n\n" + u8"Please press the POWER Button to restart the console. If you are\n" + u8"unable to restart the console, hold the POWER Button for 12 seconds\n" + u8"to turn the console off.\n\n" + u8"If the problem persists, refer to the Nintendo Support Website.\n" + u8"nintendo.com/switch/error\n"; + } + + /* TODO: Try to load dynamically. */ + /* FsStorage message_storage; */ + /* TODO: if (R_SUCCEEDED(fsOpenDataStorageByDataId(0x010000000000081D, "fatal_msg"))) { ... } */ +} + void InitializeFatalConfig() { FatalConfig *config = GetFatalConfig(); @@ -61,4 +83,6 @@ void InitializeFatalConfig() { setsysGetSettingsItemValue("fatal", "quest_reboot_interval_second", &config->quest_reboot_interval_second, sizeof(config->quest_reboot_interval_second)); setsysGetFlag(SetSysFlag_Quest, &config->quest_flag); + + SetupConfigLanguages(); } diff --git a/stratosphere/fatal/source/fatal_config.hpp b/stratosphere/fatal/source/fatal_config.hpp index 105534bfc..5656feda0 100644 --- a/stratosphere/fatal/source/fatal_config.hpp +++ b/stratosphere/fatal/source/fatal_config.hpp @@ -26,6 +26,9 @@ struct FatalConfig { bool transition_to_fatal; bool show_extra_info; bool quest_flag; + const char *error_msg; + const char *error_desc; + const char *quest_desc; }; IEvent *GetFatalSettingsEvent(); diff --git a/stratosphere/fatal/source/fatal_font.cpp b/stratosphere/fatal/source/fatal_font.cpp index 31b45833a..92712f39d 100644 --- a/stratosphere/fatal/source/fatal_font.cpp +++ b/stratosphere/fatal/source/fatal_font.cpp @@ -32,7 +32,7 @@ static u16 *g_fb = nullptr; static u32 (*g_unswizzle_func)(u32, u32) = nullptr; static u16 g_font_color = 0xFFFF; static float g_font_sz = 16.0f; -static u32 g_cur_x = 0, g_cur_y = 0; +static u32 g_line_x = 0, g_cur_x = 0, g_cur_y = 0; static PlFontData g_font; static PlFontData g_fonts[PlSharedFontType_Total]; @@ -70,7 +70,7 @@ static void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) { } } -void FontManager::PrintLine(const char *str) { +static void DrawString(const char *str, bool add_line) { FT_UInt glyph_index; FT_GlyphSlot slot = g_face->glyph; @@ -78,9 +78,14 @@ void FontManager::PrintLine(const char *str) { u32 cur_x = g_cur_x, cur_y = g_cur_y; ON_SCOPE_EXIT { - /* Advance to next line. */ - /* g_cur_x = g_cur_x; */ - g_cur_y = cur_y + (g_face->size->metrics.height >> 6); + if (add_line) { + /* Advance to next line. */ + g_cur_x = g_line_x; + g_cur_y = cur_y + (g_face->size->metrics.height >> 6); + } else { + g_cur_x = cur_x; + g_cur_y = cur_y; + } }; for (u32 i = 0; i < len; ) { @@ -90,7 +95,7 @@ void FontManager::PrintLine(const char *str) { i += unit_count; if (cur_char == '\n') { - cur_x = g_cur_x; + cur_x = g_line_x; cur_y += g_face->size->metrics.height >> 6; continue; } @@ -114,6 +119,10 @@ void FontManager::PrintLine(const char *str) { } } +void FontManager::PrintLine(const char *str) { + return DrawString(str, true); +} + void FontManager::PrintFormatLine(const char *format, ...) { va_list va_arg; va_start(va_arg, format); @@ -124,21 +133,46 @@ void FontManager::PrintFormatLine(const char *format, ...) { PrintLine(char_buf); } +void FontManager::Print(const char *str) { + return DrawString(str, false); +} + +void FontManager::PrintFormat(const char *format, ...) { + va_list va_arg; + va_start(va_arg, format); + + char char_buf[0x400]; + vsnprintf(char_buf, sizeof(char_buf), format, va_arg); + + Print(char_buf); +} + + void FontManager::SetFontColor(u16 color) { g_font_color = color; } void FontManager::SetPosition(u32 x, u32 y) { + g_line_x = x; g_cur_x = x; g_cur_y = y; } +u32 FontManager::GetX() { + return g_cur_x; +} + +u32 FontManager::GetY() { + return g_cur_y; +} + void FontManager::SetFontSize(float fsz) { g_font_sz = fsz; g_ft_err = FT_Set_Char_Size(g_face, 0, static_cast(g_font_sz * 64.0f), 96, 96); } void FontManager::AddSpacingLines(float num_lines) { + g_cur_x = g_line_x; g_cur_y += static_cast((static_cast(g_face->size->metrics.height) * num_lines) / 64.0f); } diff --git a/stratosphere/fatal/source/fatal_font.hpp b/stratosphere/fatal/source/fatal_font.hpp index c0cb35456..e98d4e4cd 100644 --- a/stratosphere/fatal/source/fatal_font.hpp +++ b/stratosphere/fatal/source/fatal_font.hpp @@ -31,8 +31,12 @@ class FontManager { static void SetFontColor(u16 color); static void SetPosition(u32 x, u32 y); + static u32 GetX(); + static u32 GetY(); static void SetFontSize(float fsz); static void AddSpacingLines(float num_lines); static void PrintLine(const char *str); static void PrintFormatLine(const char *format, ...); + static void Print(const char *str); + static void PrintFormat(const char *format, ...); }; \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index 1fffada4d..90132aa1a 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -172,6 +172,7 @@ Result ShowFatalTask::PrepareScreenForDrawing() { Result ShowFatalTask::ShowFatal() { Result rc = 0; + const FatalConfig *config = GetFatalConfig(); if (R_FAILED((rc = PrepareScreenForDrawing()))) { *(volatile u32 *)(0xCAFEBABE) = rc; @@ -196,17 +197,132 @@ Result ShowFatalTask::ShowFatal() { /* Draw the atmosphere logo in the bottom right corner. */ for (size_t y = 0; y < AMS_LOGO_HEIGHT; y++) { for (size_t x = 0; x < AMS_LOGO_WIDTH; x++) { - tiled_buf[GetPixelOffset(FatalScreenWidth - AMS_LOGO_WIDTH - 32 + x, FatalScreenHeight - AMS_LOGO_HEIGHT - 32 + y)] = AMS_LOGO_BIN[y * AMS_LOGO_WIDTH + x]; + tiled_buf[GetPixelOffset(FatalScreenWidth - AMS_LOGO_WIDTH - 32 + x, 32 + y)] = AMS_LOGO_BIN[y * AMS_LOGO_WIDTH + x]; } } /* TODO: Actually draw meaningful shit here. */ FontManager::SetPosition(32, 64); - FontManager::PrintFormatLine(u8"A fatal error occurred: 2%03d-%04d", R_MODULE(this->ctx->error_code), R_DESCRIPTION(this->ctx->error_code)); + FontManager::SetFontSize(16.0f); + FontManager::PrintFormatLine("Title: %016lx", this->title_id); FontManager::AddSpacingLines(0.5f); - FontManager::PrintFormatLine(u8"Firmware: %s (Atmosphère %u.%u.%u-%s)", GetFatalConfig()->firmware_version.display_version, - CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision()); + FontManager::PrintFormatLine(config->error_msg, R_MODULE(this->ctx->error_code), R_DESCRIPTION(this->ctx->error_code), this->ctx->error_code); + FontManager::PrintLine(config->error_desc); + FontManager::AddSpacingLines(0.5f); + FontManager::PrintFormatLine(u8"Firmware: %s (Atmosphère %u.%u.%u-%s)", GetFatalConfig()->firmware_version.display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision()); + /* Add a line. */ + for (size_t x = 32; x < FatalScreenWidth - 32; x++) { + tiled_buf[GetPixelOffset(x, FontManager::GetY())] = 0xFFFF; + } + + + FontManager::AddSpacingLines(1.5f); + + u32 backtrace_y = FontManager::GetY(); + u32 backtrace_x = 0; + + /* Print GPRs. */ + FontManager::SetFontSize(14.0f); + FontManager::PrintLine("General Purpose Registers"); + FontManager::AddSpacingLines(0.5f); + if (this->ctx->cpu_ctx.is_aarch32) { + for (size_t i = 0; i < (NumAarch32Gprs / 2); i++) { + u32 x = FontManager::GetX(); + FontManager::PrintFormat("%s:", Aarch32GprNames[i]); + FontManager::SetPosition(x + 47, FontManager::GetY()); + FontManager::PrintFormat("0x%08x ", this->ctx->cpu_ctx.aarch32_ctx.r[i]); + x = FontManager::GetX(); + FontManager::PrintFormat("%s:", Aarch32GprNames[i + (NumAarch32Gprs / 2)]); + FontManager::SetPosition(x + 47, FontManager::GetY()); + FontManager::PrintFormat("0x%08x ", this->ctx->cpu_ctx.aarch32_ctx.r[i]); + + + FontManager::PrintLine(""); + FontManager::SetPosition(32, FontManager::GetY()); + } + } else { + for (size_t i = 0; i < NumAarch64Gprs / 2; i++) { + u32 x = FontManager::GetX(); + FontManager::PrintFormat("%s:", Aarch64GprNames[i]); + FontManager::SetPosition(x + 47, FontManager::GetY()); + FontManager::PrintFormat("0x%016lx ", this->ctx->cpu_ctx.aarch64_ctx.x[i]); + x = FontManager::GetX(); + FontManager::PrintFormat("%s:", Aarch64GprNames[i + (NumAarch64Gprs / 2)]); + FontManager::SetPosition(x + 47, FontManager::GetY()); + FontManager::PrintFormat("0x%016lx ", this->ctx->cpu_ctx.aarch64_ctx.x[i]); + + if (i == (NumAarch64Gprs / 2) - 1) { + FontManager::Print(" "); + backtrace_x = FontManager::GetX(); + } + + FontManager::PrintLine(""); + FontManager::SetPosition(32, FontManager::GetY()); + } + } + + /* Print Backtrace. */ + FontManager::SetPosition(backtrace_x, backtrace_y); + if (this->ctx->cpu_ctx.is_aarch32) { + FontManager::PrintFormatLine("Backtrace (Start Address = 0x%08x)", this->ctx->cpu_ctx.aarch32_ctx.start_address); + FontManager::AddSpacingLines(0.5f); + for (u32 i = 0; i < Aarch32CpuContext::MaxStackTraceDepth / 2; i++) { + u32 bt_cur = 0, bt_next = 0; + if (i < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { + bt_cur = this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i]; + } + if (i + Aarch32CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { + bt_next = this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i + Aarch32CpuContext::MaxStackTraceDepth / 2]; + } + + if (i < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { + u32 x = FontManager::GetX(); + FontManager::PrintFormat("BT[%02X]: ", i); + FontManager::SetPosition(x + 76, FontManager::GetY()); + FontManager::PrintFormat("0x%08x ", bt_cur); + } + + if (i + Aarch32CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { + u32 x = FontManager::GetX(); + FontManager::PrintFormat("BT[%02X]: ", i + Aarch32CpuContext::MaxStackTraceDepth / 2); + FontManager::SetPosition(x + 76, FontManager::GetY()); + FontManager::PrintFormat("0x%08x ", bt_next); + } + + FontManager::PrintLine(""); + FontManager::SetPosition(backtrace_x, FontManager::GetY()); + } + } else { + FontManager::PrintFormatLine("Backtrace (Start Address = 0x%016lx)", this->ctx->cpu_ctx.aarch64_ctx.start_address); + FontManager::AddSpacingLines(0.5f); + for (u32 i = 0; i < Aarch64CpuContext::MaxStackTraceDepth / 2; i++) { + u64 bt_cur = 0, bt_next = 0; + if (i < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { + bt_cur = this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i]; + } + if (i + Aarch64CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { + bt_next = this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i + Aarch64CpuContext::MaxStackTraceDepth / 2]; + } + + if (i < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { + u32 x = FontManager::GetX(); + FontManager::PrintFormat("BT[%02X]: ", i); + FontManager::SetPosition(x + 76, FontManager::GetY()); + FontManager::PrintFormat("0x%016lx ", bt_cur); + } + + if (i + Aarch64CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { + u32 x = FontManager::GetX(); + FontManager::PrintFormat("BT[%02X]: ", i + Aarch64CpuContext::MaxStackTraceDepth / 2); + FontManager::SetPosition(x + 76, FontManager::GetY()); + FontManager::PrintFormat("0x%016lx ", bt_next); + } + + FontManager::PrintLine(""); + FontManager::SetPosition(backtrace_x, FontManager::GetY()); + } + } /* Enqueue the buffer. */ framebufferEnd(&fb); diff --git a/stratosphere/fatal/source/fatal_types.hpp b/stratosphere/fatal/source/fatal_types.hpp index 83abe9d0e..06c435364 100644 --- a/stratosphere/fatal/source/fatal_types.hpp +++ b/stratosphere/fatal/source/fatal_types.hpp @@ -26,13 +26,16 @@ enum FatalResult : Result { FatalResult_InRepairWithoutTimeReviserCartridge = 0xCA3, }; +static constexpr size_t NumAarch64Gprs = 32; +static constexpr size_t NumAarch32Gprs = 16; + struct Aarch64CpuContext { using RegisterType = u64; static constexpr size_t MaxStackTraceDepth = 0x20; /* Registers, exception context. N left names for these fields in fatal .rodata. */ union { - RegisterType x[31]; + RegisterType x[NumAarch64Gprs]; struct { RegisterType _x[29]; RegisterType fp; @@ -60,9 +63,9 @@ struct Aarch32CpuContext { /* Registers, exception context. N left names for these fields in fatal .rodata. */ union { - RegisterType r[16]; + RegisterType r[NumAarch32Gprs]; struct { - RegisterType _x[11]; + RegisterType _r[11]; RegisterType fp; RegisterType ip; RegisterType sp; @@ -86,7 +89,7 @@ struct Aarch32CpuContext { struct FatalCpuContext { union { Aarch64CpuContext aarch64_ctx; - Aarch64CpuContext aarch32_ctx; + Aarch32CpuContext aarch32_ctx; }; bool is_aarch32; @@ -102,3 +105,58 @@ static_assert(sizeof(Aarch64CpuContext) == 0x248, "Aarch64CpuContext definition! static_assert(sizeof(Aarch32CpuContext) == 0xE0, "Aarch32CpuContext definition!"); static_assert(sizeof(FatalCpuContext) == 0x250, "FatalCpuContext definition!"); static_assert(std::is_pod_v, "FatalCpuContext definition!"); + +static constexpr const char *Aarch64GprNames[NumAarch64Gprs] = { + u8"X0", + u8"X1", + u8"X2", + u8"X3", + u8"X4", + u8"X5", + u8"X6", + u8"X7", + u8"X8", + u8"X9", + u8"X10", + u8"X11", + u8"X12", + u8"X13", + u8"X14", + u8"X15", + u8"X16", + u8"X17", + u8"X18", + u8"X19", + u8"X20", + u8"X22", + u8"X23", + u8"X24", + u8"X25", + u8"X26", + u8"X27", + u8"X28", + u8"FP", + u8"LR", + u8"SP", + u8"PC", +}; + +static constexpr const char *Aarch32GprNames[NumAarch32Gprs] = { + u8"R0", + u8"R1", + u8"R2", + u8"R3", + u8"R4", + u8"R5", + u8"R6", + u8"R7", + u8"R8", + u8"R9", + u8"R10", + u8"FP", + u8"IP", + u8"LR", + u8"SP", + u8"PC", +}; +