diff --git a/source/config.c b/source/config.c index ee7ba1a..67846a3 100644 --- a/source/config.c +++ b/source/config.c @@ -246,8 +246,8 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode) initScreens(); - drawString(CONFIG_TITLE, true, 10, 10, COLOR_TITLE); - drawString("Press A to select, START to save", true, 10, 10 + SPACING_Y, COLOR_TITLE); + drawString(true, 10, 10, COLOR_TITLE, CONFIG_TITLE); + drawString(true, 10, 10 + SPACING_Y, COLOR_TITLE, "Press A to select, START to save"); //Character to display a selected option char selected = 'x'; @@ -260,8 +260,8 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode) if(!multiOptions[i].visible) continue; multiOptions[i].posY = endPos + SPACING_Y; - endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE); - drawCharacter(selected, true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE); + endPos = drawString(true, 10, multiOptions[i].posY, COLOR_WHITE, multiOptionsText[i]); + drawCharacter(true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE, selected); } endPos += SPACING_Y / 2; @@ -272,8 +272,8 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode) if(!singleOptions[i].visible) continue; singleOptions[i].posY = endPos + SPACING_Y; - endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color); - if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color); + endPos = drawString(true, 10, singleOptions[i].posY, color, singleOptionsText[i]); + if(singleOptions[i].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[i].posY, color, selected); if(color == COLOR_RED) { @@ -283,7 +283,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode) } } - drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE); + drawString(false, 10, 10, COLOR_WHITE, optionsDescription[selectedOption]); //Boring configuration menu while(true) @@ -347,21 +347,21 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode) //The user moved to a different option, print the old option in white and the new one in red. Only print 'x's if necessary if(oldSelectedOption < multiOptionsAmount) { - drawString(multiOptionsText[oldSelectedOption], true, 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE); - drawCharacter(selected, true, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE); + drawString(true, 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE, multiOptionsText[oldSelectedOption]); + drawCharacter(true, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE, selected); } else { u32 singleOldSelected = oldSelectedOption - multiOptionsAmount; - drawString(singleOptionsText[singleOldSelected], true, 10, singleOptions[singleOldSelected].posY, COLOR_WHITE); - if(singleOptions[singleOldSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE); + drawString(true, 10, singleOptions[singleOldSelected].posY, COLOR_WHITE, singleOptionsText[singleOldSelected]); + if(singleOptions[singleOldSelected].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE, selected); } - if(isMultiOption) drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED); - else drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED); + if(isMultiOption) drawString(true, 10, multiOptions[selectedOption].posY, COLOR_RED, multiOptionsText[selectedOption]); + else drawString(true, 10, singleOptions[singleSelected].posY, COLOR_RED, singleOptionsText[singleSelected]); - drawString(optionsDescription[oldSelectedOption], false, 10, 10, COLOR_BLACK); - drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE); + drawString(false, 10, 10, COLOR_BLACK, optionsDescription[oldSelectedOption]); + drawString(false, 10, 10, COLOR_WHITE, optionsDescription[selectedOption]); } else { @@ -369,7 +369,7 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode) if(isMultiOption) { u32 oldEnabled = multiOptions[selectedOption].enabled; - drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK); + drawCharacter(true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK, selected); multiOptions[selectedOption].enabled = (oldEnabled == 3 || !multiOptions[selectedOption].posXs[oldEnabled + 1]) ? 0 : oldEnabled + 1; if(selectedOption == BRIGHTNESS) updateBrightness(multiOptions[BRIGHTNESS].enabled); @@ -378,13 +378,13 @@ void configMenu(bool isSdMode, bool oldPinStatus, u32 oldPinMode) { bool oldEnabled = singleOptions[singleSelected].enabled; singleOptions[singleSelected].enabled = !oldEnabled; - if(oldEnabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_BLACK); + if(oldEnabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_BLACK, selected); } } //In any case, if the current option is enabled (or a multiple choice option is selected) we must display a red 'x' - if(isMultiOption) drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED); - else if(singleOptions[singleSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED); + if(isMultiOption) drawCharacter(true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED, selected); + else if(singleOptions[singleSelected].enabled) drawCharacter(true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED, selected); } //Preserve the last-used boot options (first 9 bits) diff --git a/source/draw.c b/source/draw.c index b8e9f46..420b5a0 100644 --- a/source/draw.c +++ b/source/draw.c @@ -31,6 +31,8 @@ #include "utils.h" #include "fs.h" #include "font.h" +#include +#include "fmt.h" bool loadSplash(void) { @@ -57,7 +59,7 @@ bool loadSplash(void) return true; } -void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color) +void drawCharacter(bool isTopScreen, u32 posX, u32 posY, u32 color, char character) { u8 *select = isTopScreen ? fbs[0].top_left : fbs[0].bottom; @@ -77,7 +79,7 @@ void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 col } } -u32 drawString(const char *string, bool isTopScreen, u32 posX, u32 posY, u32 color) +u32 drawString(bool isTopScreen, u32 posX, u32 posY, u32 color, const char *string) { for(u32 i = 0, line_i = 0; i < strlen(string); i++) switch(string[i]) @@ -100,11 +102,22 @@ u32 drawString(const char *string, bool isTopScreen, u32 posX, u32 posY, u32 col if(string[i] == ' ') break; //Spaces at the start look weird } - drawCharacter(string[i], isTopScreen, posX + line_i * SPACING_X, posY, color); + drawCharacter(isTopScreen, posX + line_i * SPACING_X, posY, color, string[i]); line_i++; break; } return posY; -} \ No newline at end of file +} + +u32 drawFormattedString(bool isTopScreen, u32 posX, u32 posY, u32 color, const char *fmt, ...) +{ + char buf[DRAW_MAX_FORMATTED_STRING_SIZE + 1]; + va_list args; + va_start(args, fmt); + vsprintf(buf, fmt, args); + va_end(args); + + return drawString(isTopScreen, posX, posY, color, buf); +} diff --git a/source/draw.h b/source/draw.h index 9929f78..cc03309 100644 --- a/source/draw.h +++ b/source/draw.h @@ -38,6 +38,9 @@ #define COLOR_BLACK 0x000000 #define COLOR_YELLOW 0x00FFFF +#define DRAW_MAX_FORMATTED_STRING_SIZE 512 + bool loadSplash(void); -void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color); -u32 drawString(const char *string, bool isTopScreen, u32 posX, u32 posY, u32 color); \ No newline at end of file +void drawCharacter(bool isTopScreen, u32 posX, u32 posY, u32 color, char character); +u32 drawString(bool isTopScreen, u32 posX, u32 posY, u32 color, const char *string); +u32 drawFormattedString(bool isTopScreen, u32 posX, u32 posY, u32 color, const char *fmt, ...); diff --git a/source/exceptions.c b/source/exceptions.c index 5a9b3ad..81c2586 100644 --- a/source/exceptions.c +++ b/source/exceptions.c @@ -27,6 +27,7 @@ #include "screen.h" #include "draw.h" #include "utils.h" +#include "fmt.h" #include "../build/bundled.h" void installArm9Handlers(void) @@ -119,7 +120,7 @@ void detectAndProcessExceptionDumps(void) }; const char *specialExceptions[] = { - "(kernel panic)", "(svcBreak)" + "kernel panic", "svcBreak" }; const char *registerNames[] = { @@ -127,99 +128,82 @@ void detectAndProcessExceptionDumps(void) "SP", "LR", "PC", "CPSR", "FPEXC" }; - char hexString[] = "00000000"; - initScreens(); - drawString("An exception occurred", true, 10, 10, COLOR_RED); - u32 posY = drawString(dumpHeader->processor == 11 ? "Processor: ARM11 (core )" : "Processor: ARM9", true, 10, 30, COLOR_WHITE); - if(dumpHeader->processor == 11) drawCharacter('0' + dumpHeader->core, true, 10 + 29 * SPACING_X, 30, COLOR_WHITE); - - posY = drawString("Exception type: ", true, 10, posY + SPACING_Y, COLOR_WHITE); - drawString(handledExceptionNames[dumpHeader->type], true, 10 + 17 * SPACING_X, posY, COLOR_WHITE); + drawString(true, 10, 10, COLOR_RED, "An exception occurred"); + u32 posY; + if(dumpHeader->processor == 11) posY = drawFormattedString(true, 10, 30, COLOR_WHITE, "Processor: ARM11 (core %u)", dumpHeader->core); + else posY = drawString(true, 10, 30, COLOR_WHITE, "Processor: ARM9"); if(dumpHeader->type == 2) { if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 4) { u32 instr = *(vu32 *)(stackDump - 4); - if(instr == 0xE12FFF7E) drawString(specialExceptions[0], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE); - else if(instr == 0xEF00003C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE); + if(instr == 0xE12FFF7E) + posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s (%s)", handledExceptionNames[dumpHeader->type], specialExceptions[0]); + else if(instr == 0xEF00003C) + posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s (%s)", handledExceptionNames[dumpHeader->type], specialExceptions[1]); + else + posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]); } else if((regs[16] & 0x20) != 0 && dumpHeader->codeDumpSize >= 2) { u16 instr = *(vu16 *)(stackDump - 2); - if(instr == 0xDF3C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE); + if(instr == 0xDF3C) + posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s (%s)", handledExceptionNames[dumpHeader->type], specialExceptions[0]); + else + posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]); } } + else + posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Exception type: %s", handledExceptionNames[dumpHeader->type]); if(dumpHeader->processor == 11 && dumpHeader->additionalDataSize != 0) - { - char processName[45] = "Current process: "; - memcpy(processName + 17, (void *)additionalData, 8); - hexItoa(*(vu32 *)(additionalData + 12), hexString, 8, true); - concatenateStrings(processName, " ("); - concatenateStrings(processName, hexString); - hexItoa(*(vu32 *)(additionalData + 8), hexString, 8, true); - concatenateStrings(processName, hexString); - concatenateStrings(processName, ")"); - posY = drawString(processName, true, 10, posY + SPACING_Y, COLOR_WHITE); - } - + posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, + "Current process: %.8s (%016llX)", (const char *)additionalData, *(vu64 *)(additionalData + 8)); posY += SPACING_Y; for(u32 i = 0; i < 17; i += 2) { - posY = drawString(registerNames[i], true, 10, posY + SPACING_Y, COLOR_WHITE); - hexItoa(regs[i], hexString, 8, true); - drawString(hexString, true, 10 + 7 * SPACING_X, posY, COLOR_WHITE); + posY = drawFormattedString(true, 10, posY + SPACING_Y, COLOR_WHITE, "%-7s%08X", registerNames[i], regs[i]); if(i != 16 || dumpHeader->processor != 9) - { - drawString(registerNames[i + 1], true, 10 + 22 * SPACING_X, posY, COLOR_WHITE); - hexItoa(i == 16 ? regs[20] : regs[i + 1], hexString, 8, true); - drawString(hexString, true, 10 + 29 * SPACING_X, posY, COLOR_WHITE); - } + posY = drawFormattedString(true, 10 + 22 * SPACING_X, posY, COLOR_WHITE, "%-7s%08X", registerNames[i + 1], regs[i + 1]); } posY += SPACING_Y; u32 mode = regs[16] & 0xF; if(dumpHeader->type == 3 && (mode == 7 || mode == 11)) - posY = drawString("Incorrect dump: failed to dump code and/or stack", true, 10, posY + SPACING_Y, COLOR_YELLOW) + SPACING_Y; + posY = drawString(true, 10, posY + SPACING_Y, COLOR_YELLOW, "Incorrect dump: failed to dump code and/or stack") + SPACING_Y; - u32 posYBottom = drawString("Stack dump:", false, 10, 10, COLOR_WHITE) + SPACING_Y; + u32 posYBottom = drawString(false, 10, 10, COLOR_WHITE, "Stack dump:") + SPACING_Y; for(u32 line = 0; line < 19 && stackDump < additionalData; line++) { - hexItoa(regs[13] + 8 * line, hexString, 8, true); - posYBottom = drawString(hexString, false, 10, posYBottom + SPACING_Y, COLOR_WHITE); - drawCharacter(':', false, 10 + 8 * SPACING_X, posYBottom, COLOR_WHITE); + posYBottom = drawFormattedString(false, 10, posYBottom + SPACING_Y, COLOR_WHITE, "%08X:", regs[13] + 8 * line); for(u32 i = 0; i < 8 && stackDump < additionalData; i++, stackDump++) - { - char byteString[] = "00"; - hexItoa(*stackDump, byteString, 2, false); - drawString(byteString, false, 10 + 10 * SPACING_X + 3 * i * SPACING_X, posYBottom, COLOR_WHITE); - } + drawFormattedString(false, 10 + 10 * SPACING_X + 3 * i * SPACING_X, posYBottom, COLOR_WHITE, "%02X", *stackDump); } - char path[36] = "dumps/", - fileName[] = "crash_dump_00000000.dmp"; - - concatenateStrings(path, dumpHeader->processor == 9 ? "arm9" : "arm11"); - findDumpFile(path, fileName); - concatenateStrings(path, "/"); - concatenateStrings(path, fileName); + char folderPath[36], + path[36], + fileName[] = "crash_dump_00010000.dmp"; + + sprintf(folderPath, "dumps/arm%u", dumpHeader->processor); + findDumpFile(folderPath, fileName); + sprintf(path, "%s/%s", folderPath, fileName); if(fileWrite((void *)dumpHeader, path, dumpHeader->totalSize)) { - posY = drawString("You can find a dump in the following file:", true, 10, posY + SPACING_Y, COLOR_WHITE); - posY = drawString(path, true, 10, posY + SPACING_Y, COLOR_WHITE) + SPACING_Y; + posY = drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, "You can find a dump in the following file:"); + posY = drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, path) + SPACING_Y; } - else posY = drawString("Error writing the dump file", true, 10, posY + SPACING_Y, COLOR_RED); + else posY = drawString(true, 10, posY + SPACING_Y, COLOR_RED, "Error writing the dump file"); - drawString("Press any button to shutdown", true, 10, posY + SPACING_Y, COLOR_WHITE); + drawString(true, 10, posY + SPACING_Y, COLOR_WHITE, "Press any button to shutdown"); memset32((void *)dumpHeader, 0, dumpHeader->totalSize); diff --git a/source/firm.c b/source/firm.c index b33226f..0371149 100755 --- a/source/firm.c +++ b/source/firm.c @@ -32,6 +32,7 @@ #include "emunand.h" #include "crypto.h" #include "screen.h" +#include "fmt.h" #include "../build/bundled.h" static Firm *firm = (Firm *)0x24000000; @@ -325,11 +326,10 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo if(loadFromStorage) { - char fileName[24] = "sysmodules/"; + char fileName[64]; //Read modules from files if they exist - concatenateStrings(fileName, moduleName); - concatenateStrings(fileName, ".cxi"); + sprintf(fileName, "%.8s.cxi", moduleName); dstModuleSize = getFileSize(fileName); @@ -349,7 +349,7 @@ static inline void copySection0AndInjectSystemModules(FirmwareType firmType, boo const u8 *module; - if(firmType == NATIVE_FIRM && memcmp(moduleName, "loader", 6) == 0) + if(firmType == NATIVE_FIRM && memcmp(moduleName, "loader", 6 + 1) == 0) { module = injector_bin; dstModuleSize = injector_bin_size; diff --git a/source/fmt.c b/source/fmt.c new file mode 100644 index 0000000..288d590 --- /dev/null +++ b/source/fmt.c @@ -0,0 +1,411 @@ +// TuxSH's changes: add support for 64-bit numbers, remove floating-point code +// TODO! fix left-padding handling with > 10 characters. + +#include "fmt.h" +#include "strings.h" + +/* File : barebones/ee_printf.c + This file contains an implementation of ee_printf that only requires a method to output a char to a UART without pulling in library code. + +This code is based on a file that contains the following: + Copyright (C) 2002 Michael Ringgaard. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +*/ + +//#include +//#include + +#define ZEROPAD (1<<0) /* Pad with zero */ +#define SIGN (1<<1) /* Unsigned/signed long */ +#define PLUS (1<<2) /* Show plus */ +#define SPACE (1<<3) /* Spacer */ +#define LEFT (1<<4) /* Left justified */ +#define HEX_PREP (1<<5) /* 0x */ +#define UPPERCASE (1<<6) /* 'ABCDEF' */ + +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +static char *lower_digits = "0123456789abcdefghijklmnopqrstuvwxyz"; +static char *upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/* +static size_t strnlen(const char *s, size_t count); + +static size_t strnlen(const char *s, size_t count) +{ + const char *sc; + for (sc = s; *sc != '\0' && count--; ++sc); + return sc - s; +} +*/ + +static int ee_skip_atoi(const char **s) +{ + int i = 0; + while (is_digit(**s)) i = i*10 + *((*s)++) - '0'; + return i; +} + +static char *ee_number(char *str, long long num, int base, int size, int precision, int type) +{ + char c, sign, tmp[66]; + char *dig = lower_digits; + int i; + + if (type & UPPERCASE) dig = upper_digits; + if (type & LEFT) type &= ~ZEROPAD; + if (base < 2 || base > 36) return 0; + + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) + { + if (num < 0) + { + sign = '-'; + num = -num; + size--; + } + else if (type & PLUS) + { + sign = '+'; + size--; + } + else if (type & SPACE) + { + sign = ' '; + size--; + } + } + + if (type & HEX_PREP) + { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + + i = 0; + + if (num == 0) + tmp[i++] = '0'; + else + { + while (num != 0) + { + tmp[i++] = dig[(unsigned long long)num % (unsigned long long)base]; + num = (long long)((unsigned long long)num / (unsigned long long) base); + } + } + + if (i > precision) precision = i; + size -= precision; + if (!(type & (ZEROPAD | LEFT))) while (size-- > 0) *str++ = ' '; + if (sign) *str++ = sign; + + if (type & HEX_PREP) + { + if (base == 8) + *str++ = '0'; + else if (base == 16) + { + *str++ = '0'; + *str++ = lower_digits[33]; + } + } + + if (!(type & LEFT)) while (size-- > 0) *str++ = c; + while (i < precision--) *str++ = '0'; + while (i-- > 0) *str++ = tmp[i]; + while (size-- > 0) *str++ = ' '; + + return str; +} + +static char *eaddr(char *str, unsigned char *addr, int size, int precision __attribute__((unused)), int type) +{ + char tmp[24]; + char *dig = lower_digits; + int i, len; + + if (type & UPPERCASE) dig = upper_digits; + len = 0; + for (i = 0; i < 6; i++) + { + if (i != 0) tmp[len++] = ':'; + tmp[len++] = dig[addr[i] >> 4]; + tmp[len++] = dig[addr[i] & 0x0F]; + } + + if (!(type & LEFT)) while (len < size--) *str++ = ' '; + for (i = 0; i < len; ++i) *str++ = tmp[i]; + while (len < size--) *str++ = ' '; + + return str; +} + +static char *iaddr(char *str, unsigned char *addr, int size, int precision __attribute__((unused)), int type) +{ + char tmp[24]; + int i, n, len; + + len = 0; + for (i = 0; i < 4; i++) + { + if (i != 0) tmp[len++] = '.'; + n = addr[i]; + + if (n == 0) + tmp[len++] = lower_digits[0]; + else + { + if (n >= 100) + { + tmp[len++] = lower_digits[n / 100]; + n = n % 100; + tmp[len++] = lower_digits[n / 10]; + n = n % 10; + } + else if (n >= 10) + { + tmp[len++] = lower_digits[n / 10]; + n = n % 10; + } + + tmp[len++] = lower_digits[n]; + } + } + + if (!(type & LEFT)) while (len < size--) *str++ = ' '; + for (i = 0; i < len; ++i) *str++ = tmp[i]; + while (len < size--) *str++ = ' '; + + return str; +} + +static int ee_vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + long long num; + int i, base; + char *str; + char *s; + int flags; // Flags to number() + + int field_width; // Width of output field + int precision; // Min. # of digits for integers; max number of chars for from string + int qualifier; // 'h', 'l', or 'L' for integer fields + int qualifier2; + + for (str = buf; *fmt; fmt++) + { + if (*fmt != '%') + { + *str++ = *fmt; + continue; + } + + // Process flags + flags = 0; +repeat: + fmt++; // This also skips first '%' + switch (*fmt) + { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= HEX_PREP; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + // Get field width + field_width = -1; + if (is_digit(*fmt)) + field_width = ee_skip_atoi(&fmt); + else if (*fmt == '*') + { + fmt++; + field_width = va_arg(args, int); + if (field_width < 0) + { + field_width = -field_width; + flags |= LEFT; + } + } + + // Get the precision + precision = -1; + if (*fmt == '.') + { + ++fmt; + if (is_digit(*fmt)) + precision = ee_skip_atoi(&fmt); + else if (*fmt == '*') + { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) precision = 0; + } + + // Get the conversion qualifier + qualifier = qualifier2 = -1; + if(*fmt == 'L') + qualifier = *fmt++; + else if (*fmt == 'l') + { + qualifier = *fmt++; + if(*fmt == 'l') qualifier2 = *fmt++; + } + else if (*fmt == 'h') + { + qualifier = *fmt++; + if(*fmt == 'h') qualifier2 = *fmt++; + } + + // Default base + base = 10; + + switch (*fmt) + { + case 'c': + if (!(flags & LEFT)) while (--field_width > 0) *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) s = ""; + if(precision != -1) + len = strnlen(s, precision); + else + len = strlen(s); + if (!(flags & LEFT)) while (len < field_width--) *str++ = ' '; + for (i = 0; i < len; ++i) *str++ = *s++; + while (len < field_width--) *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) + { + field_width = 2 * sizeof(void *); + flags |= ZEROPAD; + } + str = ee_number(str, (unsigned long) va_arg(args, void *), 16, field_width, precision, flags); + continue; + + case 'A': + flags |= UPPERCASE; + + case 'a': + if (qualifier == 'l') + str = eaddr(str, va_arg(args, unsigned char *), field_width, precision, flags); + else + str = iaddr(str, va_arg(args, unsigned char *), field_width, precision, flags); + continue; + + // Integer number formats - set up the flags and "break" + case 'o': + base = 8; + break; + + case 'X': + flags |= UPPERCASE; + + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + + case 'u': + break; + + default: + if (*fmt != '%') *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + + // bugfixes here: + + if(flags & SIGN) + { + if (qualifier == 'l' && qualifier2 == 'l') + num = va_arg(args, long long int); + else if(qualifier == 'l') + num = va_arg(args, long int); +/* else if(qualifier == 'h' && qualifier == 'h') + num = va_arg(args, signed char); + else if(qualifier == 'h') + num = va_arg(args, short);*/ + else + num = va_arg(args, int); + } + else + { + if (qualifier == 'l' && qualifier2 == 'l') + num = va_arg(args, unsigned long long int); + else if(qualifier == 'l') + num = va_arg(args, unsigned long int); +/* else if(qualifier == 'h' && qualifier == 'h') + num = va_arg(args, unsigned char); + else if(qualifier == 'h') + num = va_arg(args, unsigned short);*/ + else + num = va_arg(args, unsigned int); + } + + str = ee_number(str, num, base, field_width, precision, flags); + } + + *str = '\0'; + return str - buf; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + return ee_vsprintf(buf, fmt, args); +} + +int sprintf(char *buf, const char *fmt, ...) +{ + int n = 0; + va_list args; + va_start(args, fmt); + n = ee_vsprintf(buf, fmt, args); + va_end(args); + return n; +} diff --git a/source/fmt.h b/source/fmt.h new file mode 100644 index 0000000..f5867d7 --- /dev/null +++ b/source/fmt.h @@ -0,0 +1,6 @@ +#pragma once +#include "memory.h" +#include + +int vsprintf(char *buf, const char *fmt, va_list args); +int sprintf(char *buf, const char *fmt, ...); diff --git a/source/fs.c b/source/fs.c index fc62534..446a8bd 100644 --- a/source/fs.c +++ b/source/fs.c @@ -23,6 +23,7 @@ #include "fs.h" #include "memory.h" #include "strings.h" +#include "fmt.h" #include "crypto.h" #include "cache.h" #include "screen.h" @@ -141,9 +142,9 @@ void loadPayload(u32 pressed, const char *payloadPath) DIR dir; FILINFO info; FRESULT result; - char path[22] = "payloads"; + char path[128]; - result = f_findfirst(&dir, &info, path, pattern); + result = f_findfirst(&dir, &info, "payloads", pattern); if(result != FR_OK) return; @@ -151,8 +152,7 @@ void loadPayload(u32 pressed, const char *payloadPath) if(!info.fname[0]) return; - concatenateStrings(path, "/"); - concatenateStrings(path, info.altname); + sprintf(path, "payloads/%s", info.altname); payloadSize = fileRead(payloadAddress, path, maxPayloadSize); } else payloadSize = fileRead(payloadAddress, payloadPath, maxPayloadSize); @@ -207,12 +207,12 @@ void payloadMenu(void) initScreens(); - drawString("Luma3DS chainloader", true, 10, 10, COLOR_TITLE); - drawString("Press A to select, START to quit", true, 10, 10 + SPACING_Y, COLOR_TITLE); + drawString(true, 10, 10, COLOR_TITLE, "Luma3DS chainloader"); + drawString(true, 10, 10 + SPACING_Y, COLOR_TITLE, "Press A to select, START to quit"); for(u32 i = 0, posY = 10 + 3 * SPACING_Y, color = COLOR_RED; i < payloadNum; i++, posY += SPACING_Y) { - drawString(payloadList[i], true, 10, posY, color); + drawString(true, 10, posY, color, payloadList[i]); if(color == COLOR_RED) color = COLOR_WHITE; } @@ -249,15 +249,13 @@ void payloadMenu(void) if(oldSelectedPayload == selectedPayload) continue; - drawString(payloadList[oldSelectedPayload], true, 10, 10 + (3 + oldSelectedPayload) * SPACING_Y, COLOR_WHITE); - drawString(payloadList[selectedPayload], true, 10, 10 + (3 + selectedPayload) * SPACING_Y, COLOR_RED); + drawString(true, 10, 10 + (3 + oldSelectedPayload) * SPACING_Y, COLOR_WHITE, payloadList[oldSelectedPayload]); + drawString(true, 10, 10 + (3 + selectedPayload) * SPACING_Y, COLOR_RED, payloadList[selectedPayload]); } if(pressed == BUTTON_A) { - concatenateStrings(path, "/"); - concatenateStrings(path, payloadList[selectedPayload]); - concatenateStrings(path, ".bin"); + sprintf(path, "payloads/%s.bin", payloadList[selectedPayload]); loadPayload(0, path); error("The payload is too large or corrupted."); } @@ -274,14 +272,14 @@ u32 firmRead(void *dest, u32 firmType) {"00000003", "20000003"}, {"00000001", "20000001"}}; - char path[48] = "1:/title/00040138/"; - concatenateStrings(path, firmFolders[firmType][ISN3DS ? 1 : 0]); - concatenateStrings(path, "/content"); + char folderPath[48]; + char path[48]; + sprintf(folderPath, "1:/title/00040138/%s/content", firmFolders[firmType][ISN3DS ? 1 : 0]); DIR dir; u32 firmVersion = 0xFFFFFFFF; - if(f_opendir(&dir, path) != FR_OK) goto exit; + if(f_opendir(&dir, folderPath) != FR_OK) goto exit; FILINFO info; @@ -302,10 +300,7 @@ u32 firmRead(void *dest, u32 firmType) if(firmVersion == 0xFFFFFFFF) goto exit; //Complete the string with the .app name - concatenateStrings(path, "/00000000.app"); - - //Convert back the .app name from integer to array - hexItoa(firmVersion, path + 35, 8, false); + sprintf(path, "%s/%08x.app", folderPath, firmVersion); if(fileRead(dest, path, 0x400000 + sizeof(Cxi) + 0x200) <= sizeof(Cxi) + 0x200) firmVersion = 0xFFFFFFFF; @@ -313,22 +308,20 @@ exit: return firmVersion; } -void findDumpFile(const char *path, char *fileName) +void findDumpFile(const char *folderPath, char *fileName) { DIR dir; FRESULT result; - u32 n = 0; - while(n <= 99999999) + for(u32 n = 0; n <= 99999999; n++) { FILINFO info; - result = f_findfirst(&dir, &info, path, fileName); + sprintf(fileName, "crash_dump_%08u.dmp", n); + result = f_findfirst(&dir, &info, folderPath, fileName); if(result != FR_OK || !info.fname[0]) break; - - decItoa(++n, fileName + 11, 8); } if(result == FR_OK) f_closedir(&dir); -} \ No newline at end of file +} diff --git a/source/fs.h b/source/fs.h index 14f26c0..9632fa5 100644 --- a/source/fs.h +++ b/source/fs.h @@ -34,4 +34,4 @@ void fileDelete(const char *path); void loadPayload(u32 pressed, const char *payloadPath); void payloadMenu(void); u32 firmRead(void *dest, u32 firmType); -void findDumpFile(const char *path, char *fileName); \ No newline at end of file +void findDumpFile(const char *folderPath, char *fileName); diff --git a/source/main.c b/source/main.c index 4026844..c4af584 100644 --- a/source/main.c +++ b/source/main.c @@ -31,6 +31,7 @@ #include "buttons.h" #include "pin.h" #include "crypto.h" +#include "fmt.h" extern CfgData configData; extern ConfigurationStatus needConfig; @@ -263,8 +264,8 @@ boot: if(res != 0) { - char patchesError[] = "Failed to apply FIRM patch(es)."; - decItoa(res, patchesError + 16, 2); + char patchesError[64]; + sprintf(patchesError, "Failed to apply %u FIRM patch(es).", res); error(patchesError); } diff --git a/source/pin.c b/source/pin.c index b7d0246..b1b12c3 100644 --- a/source/pin.c +++ b/source/pin.c @@ -50,11 +50,10 @@ void newPin(bool allowSkipping, u32 pinMode) u8 length = 4 + 2 * (pinMode - 1); - drawString("Enter a new PIN using ABXY and the DPad", true, 10, 10, COLOR_TITLE); - drawString(allowSkipping ? "Press START to skip, SELECT to reset" : "Press SELECT to reset", true, 10, 10 + SPACING_Y, COLOR_TITLE); + drawString(true, 10, 10, COLOR_TITLE, "Enter a new PIN using ABXY and the DPad"); + drawString(true, 10, 10 + SPACING_Y, COLOR_TITLE, allowSkipping ? "Press START to skip, SELECT to reset" : "Press SELECT to reset"); - drawString("PIN ( digits): ", true, 10, 10 + 3 * SPACING_Y, COLOR_WHITE); - drawCharacter('0' + length, true, 10 + 5 * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE); + drawFormattedString(true, 10, 10 + 3 * SPACING_Y, COLOR_WHITE, "PIN (%u digits): ", length); //Pad to AES block length with zeroes __attribute__((aligned(4))) u8 enteredPassword[AES_BLOCK_SIZE] = {0}; @@ -67,7 +66,7 @@ void newPin(bool allowSkipping, u32 pinMode) if(reset) { for(u32 i = 0; i < cnt; i++) - drawCharacter((char)enteredPassword[i], true, 10 + (16 + 2 * i) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_BLACK); + drawCharacter(true, 10 + (16 + 2 * i) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_BLACK, (char)enteredPassword[i]); cnt = 0; reset = false; @@ -97,7 +96,7 @@ void newPin(bool allowSkipping, u32 pinMode) enteredPassword[cnt] = (u8)pinKeyToLetter(pressed); //Visualize character on screen - drawCharacter(enteredPassword[cnt], true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE); + drawCharacter(true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE, enteredPassword[cnt]); cnt++; } @@ -143,11 +142,10 @@ bool verifyPin(u32 pinMode) initScreens(); - drawString("Enter the PIN using ABXY and the DPad to proceed", true, 10, 10, COLOR_TITLE); - drawString("Press START to shutdown, SELECT to clear", true, 10, 10 + SPACING_Y, COLOR_TITLE); + drawString(true, 10, 10, COLOR_TITLE, "Enter the PIN using ABXY and the DPad to proceed"); + drawString(true, 10, 10 + SPACING_Y, COLOR_TITLE, "Press START to shutdown, SELECT to clear"); - drawString("PIN ( digits): ", true, 10, 10 + 3 * SPACING_Y, COLOR_WHITE); - drawCharacter('0' + lengthBlock[0], true, 10 + 5 * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE); + drawFormattedString(true, 10, 10 + 3 * SPACING_Y, COLOR_WHITE, "PIN (%u digits): ", lengthBlock[0]); const char *messageFile = "pinmessage.txt"; char message[801]; @@ -157,7 +155,7 @@ bool verifyPin(u32 pinMode) if(messageSize != 0) { message[messageSize] = 0; - drawString(message, false, 10, 10, COLOR_WHITE); + drawString(false, 10, 10, COLOR_WHITE, message); } //Pad to AES block length with zeroes @@ -172,7 +170,7 @@ bool verifyPin(u32 pinMode) if(reset) { for(u32 i = 0; i < cnt; i++) - drawCharacter('*', true, 10 + (16 + 2 * i) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_BLACK); + drawCharacter(true, 10 + (16 + 2 * i) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_BLACK, '*'); cnt = 0; reset = false; @@ -201,7 +199,7 @@ bool verifyPin(u32 pinMode) enteredPassword[cnt] = (u8)pinKeyToLetter(pressed); //Visualize character on screen - drawCharacter('*', true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE); + drawCharacter(true, 10 + (16 + 2 * cnt) * SPACING_X, 10 + 3 * SPACING_Y, COLOR_WHITE, '*'); if(++cnt < lengthBlock[0]) continue; @@ -212,9 +210,9 @@ bool verifyPin(u32 pinMode) { reset = true; - drawString("Wrong PIN, try again", true, 10, 10 + 5 * SPACING_Y, COLOR_RED); + drawString(true, 10, 10 + 5 * SPACING_Y, COLOR_RED, "Wrong PIN, try again"); } } return true; -} \ No newline at end of file +} diff --git a/source/strings.c b/source/strings.c index e135955..113d123 100644 --- a/source/strings.c +++ b/source/strings.c @@ -32,35 +32,12 @@ u32 strlen(const char *string) return stringEnd - string; } -void concatenateStrings(char *destination, const char *source) +u32 strnlen(const char *string, u32 maxlen) { - u32 i = strlen(source), - j = strlen(destination); + u32 size; + for(size = 0; size < maxlen && *string; string++, size++); - memcpy(&destination[j], source, i + 1); -} - -void hexItoa(u32 number, char *out, u32 digits, bool fillString) -{ - const char hexDigits[] = "0123456789ABCDEF"; - u32 i; - - for(i = 0; number > 0; i++) - { - out[digits - 1 - i] = hexDigits[number & 0xF]; - number >>= 4; - } - - if(fillString) while(i < digits) out[digits - 1 - i++] = '0'; -} - -void decItoa(u32 number, char *out, u32 digits) -{ - for(u32 i = 0; number > 0; i++) - { - out[digits - 1 - i] = '0' + number % 10; - number /= 10; - } + return size; } u32 hexAtoi(const char *in, u32 digits) @@ -83,4 +60,4 @@ u32 decAtoi(const char *in, u32 digits) res = *tmp - '0' + res * 10; return res; -} \ No newline at end of file +} diff --git a/source/strings.h b/source/strings.h index 5a09378..5aa917b 100644 --- a/source/strings.h +++ b/source/strings.h @@ -25,8 +25,6 @@ #include "types.h" u32 strlen(const char *string); -void concatenateStrings(char *destination, const char *source); -void hexItoa(u32 number, char *out, u32 digits, bool fillString); -void decItoa(u32 number, char *out, u32 digits); +u32 strnlen(const char *string, u32 maxlen); u32 hexAtoi(const char *in, u32 digits); -u32 decAtoi(const char *in, u32 digits); \ No newline at end of file +u32 decAtoi(const char *in, u32 digits); diff --git a/source/utils.c b/source/utils.c index 24afc2a..1f2fe8f 100644 --- a/source/utils.c +++ b/source/utils.c @@ -110,9 +110,9 @@ void error(const char *message) { initScreens(); - drawString("An error has occurred:", true, 10, 10, COLOR_RED); - u32 posY = drawString(message, true, 10, 30, COLOR_WHITE); - drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE); + drawString(true, 10, 10, COLOR_RED, "An error has occurred:"); + u32 posY = drawString(true, 10, 30, COLOR_WHITE, message); + drawString(true, 10, posY + 2 * SPACING_Y, COLOR_WHITE, "Press any button to shutdown"); waitInput(false); }