From d2445f0396ef85cea5b145102c8760849f05599f Mon Sep 17 00:00:00 2001 From: Pk11 Date: Wed, 18 Mar 2026 10:39:29 -0500 Subject: [PATCH] Scripting: Normalize output of FormatBytes (#947) * Scripting: Normalize output of FormatBytes * lua: Add flag to use locale when formatting bytes Co-Authored-By: ihaveahax --------- Co-authored-by: ihaveahax --- arm9/source/common/ui.c | 12 ++++++++---- arm9/source/common/ui.h | 2 +- arm9/source/filesys/fsutil.c | 4 ++-- arm9/source/godmode.c | 28 ++++++++++++++-------------- arm9/source/lua/gm9lua.h | 7 ++++--- arm9/source/lua/gm9ui.c | 9 +++++++-- arm9/source/utils/gameutil.c | 2 +- arm9/source/utils/scripting.c | 6 +++--- resources/lua-doc.md | 9 ++++----- 9 files changed, 44 insertions(+), 35 deletions(-) diff --git a/arm9/source/common/ui.c b/arm9/source/common/ui.c index c8207ae..1b2a818 100644 --- a/arm9/source/common/ui.c +++ b/arm9/source/common/ui.c @@ -637,8 +637,12 @@ void FormatNumber(char* str, u64 number) { // str should be 32 byte in size } } -void FormatBytes(char* str, u64 bytes) { // str should be 32 byte in size, just to be safe - const char* units[] = {STR_BYTE, STR_KB, STR_MB, STR_GB}; +void FormatBytes(char* str, u64 bytes, bool useLocale) { // str should be 32 byte in size, just to be safe + static const char* normalizedUnits[] = {" Byte", " kB", " MB", " GB"}; + const char* localizedUnits[] = {STR_BYTE, STR_KB, STR_MB, STR_GB}; + + const char** units = useLocale ? localizedUnits : normalizedUnits; + const char *separator = useLocale ? STR_DECIMAL_SEPARATOR : "."; if (bytes == (u64) -1) snprintf(str, 32, "%s", STR_INVALID); else if (bytes < 1024) snprintf(str, 32, "%llu%s", bytes, units[0]); @@ -646,7 +650,7 @@ void FormatBytes(char* str, u64 bytes) { // str should be 32 byte in size, just u32 scale = 1; u64 bytes100 = (bytes * 100) >> 10; for(; (bytes100 >= 1024*100) && (scale < 3); scale++, bytes100 >>= 10); - snprintf(str, 32, "%llu%s%llu%s", bytes100 / 100, STR_DECIMAL_SEPARATOR, (bytes100 % 100) / 10, units[scale]); + snprintf(str, 32, "%llu%s%llu%s", bytes100 / 100, separator, (bytes100 % 100) / 10, units[scale]); } } @@ -956,7 +960,7 @@ u32 ShowFileScrollPrompt(int n, const DirEntry** options, bool hide_ext, const c while (true) { for (int i = scroll; i < scroll+n_show; i++) { char bytestr[16]; - FormatBytes(bytestr, options[i]->size); + FormatBytes(bytestr, options[i]->size, true); char content_str[UTF_BUFFER_BYTESIZE(fname_len)]; char temp_str[256]; diff --git a/arm9/source/common/ui.h b/arm9/source/common/ui.h index 4f51c0b..18cd6a3 100644 --- a/arm9/source/common/ui.h +++ b/arm9/source/common/ui.h @@ -81,7 +81,7 @@ void WordWrapString(char* str, int llen); void ResizeString(char* dest, const char* orig, int nlength, int tpos, bool align_right); void TruncateString(char* dest, const char* orig, int nlength, int tpos); void FormatNumber(char* str, u64 number); -void FormatBytes(char* str, u64 bytes); +void FormatBytes(char* str, u64 bytes, bool useLocale); void PRINTF_ARGS(1) ShowString(const char *format, ...); void PRINTF_ARGS(2) ShowStringF(u16* screen, const char *format, ...); diff --git a/arm9/source/filesys/fsutil.c b/arm9/source/filesys/fsutil.c index 6b9cfd0..f415836 100644 --- a/arm9/source/filesys/fsutil.c +++ b/arm9/source/filesys/fsutil.c @@ -755,8 +755,8 @@ bool PathCopy(const char* destdir, const char* orig, u32* flags) { char dsizestr[32]; TruncateString(deststr, dest, 36, 8); TruncateString(origstr, orig, 36, 8); - FormatBytes(osizestr, osize); - FormatBytes(dsizestr, dvfile.size); + FormatBytes(osizestr, osize, true); + FormatBytes(dsizestr, dvfile.size, true); if (dvfile.size > osize) { if (!ShowPrompt(true, STR_FILE_SMALLER_THAN_SPACE_SIZES_CONTINUE, origstr, osizestr, deststr, dsizestr)) return false; diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index 10ccc35..a3b0da2 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -252,8 +252,8 @@ void DrawTopBar(const char* curr_path) { char tempstr[UTF_BUFFER_BYTESIZE(19)]; ResizeString(tempstr, STR_LOADING, 19, 19, true); DrawString(TOP_SCREEN, tempstr, bartxt_rx, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR); - FormatBytes(bytestr0, GetFreeSpace(curr_path)); - FormatBytes(bytestr1, GetTotalSpace(curr_path)); + FormatBytes(bytestr0, GetFreeSpace(curr_path), true); + FormatBytes(bytestr1, GetTotalSpace(curr_path), true); snprintf(tempstr, sizeof(tempstr), "%s/%s", bytestr0, bytestr1); DrawStringF(TOP_SCREEN, bartxt_rx, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%19.19s", tempstr); show_time = false; @@ -262,7 +262,7 @@ void DrawTopBar(const char* curr_path) { if (true) { // allocated mem const u32 bartxt_rx = SCREEN_WIDTH_TOP - (9*FONT_WIDTH_EXT) - bartxt_x; char bytestr[32]; - FormatBytes(bytestr, mem_allocated()); + FormatBytes(bytestr, mem_allocated(), true); DrawStringF(TOP_SCREEN, bartxt_rx, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%9.9s", bytestr); show_time = false; } @@ -399,7 +399,7 @@ void DrawDirContents(DirStruct* contents, u32 cursor, u32* scroll) { char namestr[UTF_BUFFER_BYTESIZE(str_width - 10)]; char rawbytestr[32], bytestr[UTF_BUFFER_BYTESIZE(10)]; color_font = (cursor != offset_i) ? COLOR_ENTRY(curr_entry) : COLOR_STD_FONT; - FormatBytes(rawbytestr, curr_entry->size); + FormatBytes(rawbytestr, curr_entry->size, true); ResizeString(bytestr, (curr_entry->type == T_DIR) ? STR_DIR : (curr_entry->type == T_DOTDOT) ? "(..)" : rawbytestr, 10, 10, true); ResizeString(namestr, curr_entry->name, str_width - 10, str_width - 20, false); snprintf(tempstr, sizeof(tempstr), "%s%s", namestr, bytestr); @@ -1031,7 +1031,7 @@ u32 CartRawDump(void) { // input dump size dsize = cdata->cart_size; - FormatBytes(bytestr, dsize); + FormatBytes(bytestr, dsize, true); dsize = ShowHexPrompt(dsize, 8, STR_CART_DETECTED_SIZE_INPUT_BELOW, cname, bytestr); if (!dsize || (dsize == (u64) -1)) { free(cdata); @@ -1113,13 +1113,13 @@ u32 DirFileAttrMenu(const char* path, const char *name) { ShowString("%s", drv ? STR_ANALYZING_DRIVE : STR_ANALYZING_DIR); if (!DirInfo(path, &tsize, &tdirs, &tfiles)) return 1; - FormatBytes(bytestr, tsize); + FormatBytes(bytestr, tsize, true); if (drv) { // drive specific char freestr[32], drvsstr[32], usedstr[32]; - FormatBytes(freestr, GetFreeSpace(path)); - FormatBytes(drvsstr, GetTotalSpace(path)); - FormatBytes(usedstr, GetTotalSpace(path) - GetFreeSpace(path)); + FormatBytes(freestr, GetFreeSpace(path), true); + FormatBytes(drvsstr, GetTotalSpace(path), true); + FormatBytes(usedstr, GetTotalSpace(path) - GetFreeSpace(path), true); snprintf(sizestr, sizeof(sizestr), STR_N_FILES_N_SUBDIRS_TOTAL_SIZE_FREE_USED_TOTAL, tfiles, tdirs, bytestr, freestr, usedstr, drvsstr); } else { // dir specific @@ -1127,7 +1127,7 @@ u32 DirFileAttrMenu(const char* path, const char *name) { } } else { // for files char bytestr[32]; - FormatBytes(bytestr, fno.fsize); + FormatBytes(bytestr, fno.fsize, true); snprintf(sizestr, sizeof(sizestr), STR_FILESIZE_X, bytestr); } @@ -2018,7 +2018,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan } current_dir->entry[i].marked = false; } - FormatBytes(savingsstr, savings); + FormatBytes(savingsstr, savings, true); if (n_other) ShowPrompt(false, STR_N_OF_N_FILES_TRIMMED_N_OF_N_NOT_OF_SAME_TYPE_X_SAVED, n_success, n_marked, n_other, n_marked, savingsstr); else ShowPrompt(false, STR_N_OF_N_FILES_TRIMMED_X_SAVED, n_success, n_marked, savingsstr); @@ -2029,9 +2029,9 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan char tsizestr[32]; char csizestr[32]; char dsizestr[32]; - FormatBytes(tsizestr, trimsize); - FormatBytes(csizestr, currentsize); - FormatBytes(dsizestr, currentsize - trimsize); + FormatBytes(tsizestr, trimsize, true); + FormatBytes(csizestr, currentsize, true); + FormatBytes(dsizestr, currentsize - trimsize, true); if (!trimsize || trimsize > currentsize) { ShowPrompt(false, "%s\n%s", pathstr, STR_FILE_CANT_BE_TRIMMED); diff --git a/arm9/source/lua/gm9lua.h b/arm9/source/lua/gm9lua.h index 41db975..58065d8 100644 --- a/arm9/source/lua/gm9lua.h +++ b/arm9/source/lua/gm9lua.h @@ -14,10 +14,11 @@ #define EXPLORER (1UL<<16) #define ENCRYPTED (1UL<<17) #define SIG_CHECK (1UL<<18) +#define USE_LOCALE (1UL<<19) -#define FLAGS_STR "no_cancel", "silent", "calc_sha", "sha1", "skip", "overwrite", "append", "all", "recursive", "to_emunand", "legit", "first", "include_dirs", "explorer", "encrypted", "sig_check" -#define FLAGS_CONSTS NO_CANCEL, SILENT, CALC_SHA, USE_SHA1, SKIP_ALL, OVERWRITE_ALL, APPEND_ALL, ASK_ALL, RECURSIVE, TO_EMUNAND, LEGIT, FIND_FIRST, INCLUDE_DIRS, EXPLORER, ENCRYPTED, SIG_CHECK -#define FLAGS_COUNT 16 +#define FLAGS_STR "no_cancel", "silent", "calc_sha", "sha1", "skip", "overwrite", "append", "all", "recursive", "to_emunand", "legit", "first", "include_dirs", "explorer", "encrypted", "sig_check", "use_locale" +#define FLAGS_CONSTS NO_CANCEL, SILENT, CALC_SHA, USE_SHA1, SKIP_ALL, OVERWRITE_ALL, APPEND_ALL, ASK_ALL, RECURSIVE, TO_EMUNAND, LEGIT, FIND_FIRST, INCLUDE_DIRS, EXPLORER, ENCRYPTED, SIG_CHECK, USE_LOCALE +#define FLAGS_COUNT 17 #define LUASCRIPT_EXT "lua" #define LUASCRIPT_MAX_SIZE STD_BUFFER_SIZE diff --git a/arm9/source/lua/gm9ui.c b/arm9/source/lua/gm9ui.c index 09df395..e3acba1 100644 --- a/arm9/source/lua/gm9ui.c +++ b/arm9/source/lua/gm9ui.c @@ -284,11 +284,16 @@ static int ui_ask_selection(lua_State* L) { } static int ui_format_bytes(lua_State* L) { - CheckLuaArgCount(L, 1, "ui.format_bytes"); + bool extra = CheckLuaArgCountPlusExtra(L, 1, "ui.format_bytes"); lua_Integer size = luaL_checkinteger(L, 1); + u32 flags = 0; + if (extra) { + flags = GetFlagsFromTable(L, 2, flags, USE_LOCALE); + } + char bytesstr[32] = { 0 }; - FormatBytes(bytesstr, (u64)size); + FormatBytes(bytesstr, (u64)size, flags & USE_LOCALE); lua_pushstring(L, bytesstr); return 1; diff --git a/arm9/source/utils/gameutil.c b/arm9/source/utils/gameutil.c index 325d38b..98f83a6 100644 --- a/arm9/source/utils/gameutil.c +++ b/arm9/source/utils/gameutil.c @@ -3578,7 +3578,7 @@ u32 ShowGameCheckerInfo(const char* path) { for (u32 i = 0; i < content_count; i++, chunk++) content_size += getbe64(chunk->size); } - FormatBytes(bytestr, content_size); + FormatBytes(bytestr, content_size, true); // check ticket if (ticket && ValidateTicket(ticket) == 0) diff --git a/arm9/source/utils/scripting.c b/arm9/source/utils/scripting.c index 461496c..73be46e 100644 --- a/arm9/source/utils/scripting.c +++ b/arm9/source/utils/scripting.c @@ -453,7 +453,7 @@ void upd_var(const char* name) { if (!name || (strncmp(name, "SDSIZE", _VAR_NAME_LEN) == 0)) { u64 sdsize = GetTotalSpace("0:"); char sdsize_str[32+1]; - FormatBytes(sdsize_str, sdsize); + FormatBytes(sdsize_str, sdsize, false); set_var("SDSIZE", sdsize_str); } @@ -461,7 +461,7 @@ void upd_var(const char* name) { if (!name || (strncmp(name, "SDFREE", _VAR_NAME_LEN) == 0)) { u64 sdfree = GetFreeSpace("0:"); char sdfree_str[32+1]; - FormatBytes(sdfree_str, sdfree); + FormatBytes(sdfree_str, sdfree, false); set_var("SDFREE", sdfree_str); } @@ -469,7 +469,7 @@ void upd_var(const char* name) { if (!name || (strncmp(name, "NANDSIZE", _VAR_NAME_LEN) == 0)) { u64 nandsize = GetNandSizeSectors(NAND_SYSNAND) * 0x200; char nandsize_str[32+1]; - FormatBytes(nandsize_str, nandsize); + FormatBytes(nandsize_str, nandsize, false); set_var("NANDSIZE", nandsize_str); } } diff --git a/resources/lua-doc.md b/resources/lua-doc.md index 227059c..1ace623 100644 --- a/resources/lua-doc.md +++ b/resources/lua-doc.md @@ -340,15 +340,14 @@ Display a scrollable text viewer from a text file. #### ui.format_bytes -* `string ui.format_bytes(int bytes)` +* `string ui.format_bytes(int bytes[, table opts {bool use_locale}])` -Format a number with `Byte`, `kB`, `MB`, or `GB`. - -> [!NOTE] -> This is affected by localization and may return different text if the language is not English. +Format a number with `Byte`, `kB`, `MB`, or `GB`. By default this will always use English style formatting. Enable `use_locale` to format in the user's selected language. * **Arguments** * `bytes` - Size to format + * `opts` (optional) - Option flags + * `use_locale` - Format in the user's selected locale * **Returns:** formatted string #### ui.check_key