Scripting: Normalize output of FormatBytes (#947)

* Scripting: Normalize output of FormatBytes

* lua: Add flag to use locale when formatting bytes

Co-Authored-By: ihaveahax <ian@ianburgwin.net>

---------

Co-authored-by: ihaveahax <ian@ianburgwin.net>
This commit is contained in:
Pk11 2026-03-18 10:39:29 -05:00 committed by GitHub
parent 22939ec6ad
commit d2445f0396
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 44 additions and 35 deletions

View File

@ -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 void FormatBytes(char* str, u64 bytes, bool useLocale) { // str should be 32 byte in size, just to be safe
const char* units[] = {STR_BYTE, STR_KB, STR_MB, STR_GB}; 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); if (bytes == (u64) -1) snprintf(str, 32, "%s", STR_INVALID);
else if (bytes < 1024) snprintf(str, 32, "%llu%s", bytes, units[0]); 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; u32 scale = 1;
u64 bytes100 = (bytes * 100) >> 10; u64 bytes100 = (bytes * 100) >> 10;
for(; (bytes100 >= 1024*100) && (scale < 3); scale++, bytes100 >>= 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) { while (true) {
for (int i = scroll; i < scroll+n_show; i++) { for (int i = scroll; i < scroll+n_show; i++) {
char bytestr[16]; char bytestr[16];
FormatBytes(bytestr, options[i]->size); FormatBytes(bytestr, options[i]->size, true);
char content_str[UTF_BUFFER_BYTESIZE(fname_len)]; char content_str[UTF_BUFFER_BYTESIZE(fname_len)];
char temp_str[256]; char temp_str[256];

View File

@ -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 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 TruncateString(char* dest, const char* orig, int nlength, int tpos);
void FormatNumber(char* str, u64 number); 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(1) ShowString(const char *format, ...);
void PRINTF_ARGS(2) ShowStringF(u16* screen, const char *format, ...); void PRINTF_ARGS(2) ShowStringF(u16* screen, const char *format, ...);

View File

@ -755,8 +755,8 @@ bool PathCopy(const char* destdir, const char* orig, u32* flags) {
char dsizestr[32]; char dsizestr[32];
TruncateString(deststr, dest, 36, 8); TruncateString(deststr, dest, 36, 8);
TruncateString(origstr, orig, 36, 8); TruncateString(origstr, orig, 36, 8);
FormatBytes(osizestr, osize); FormatBytes(osizestr, osize, true);
FormatBytes(dsizestr, dvfile.size); FormatBytes(dsizestr, dvfile.size, true);
if (dvfile.size > osize) { if (dvfile.size > osize) {
if (!ShowPrompt(true, STR_FILE_SMALLER_THAN_SPACE_SIZES_CONTINUE, origstr, osizestr, deststr, dsizestr)) if (!ShowPrompt(true, STR_FILE_SMALLER_THAN_SPACE_SIZES_CONTINUE, origstr, osizestr, deststr, dsizestr))
return false; return false;

View File

@ -252,8 +252,8 @@ void DrawTopBar(const char* curr_path) {
char tempstr[UTF_BUFFER_BYTESIZE(19)]; char tempstr[UTF_BUFFER_BYTESIZE(19)];
ResizeString(tempstr, STR_LOADING, 19, 19, true); ResizeString(tempstr, STR_LOADING, 19, 19, true);
DrawString(TOP_SCREEN, tempstr, bartxt_rx, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR); DrawString(TOP_SCREEN, tempstr, bartxt_rx, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR);
FormatBytes(bytestr0, GetFreeSpace(curr_path)); FormatBytes(bytestr0, GetFreeSpace(curr_path), true);
FormatBytes(bytestr1, GetTotalSpace(curr_path)); FormatBytes(bytestr1, GetTotalSpace(curr_path), true);
snprintf(tempstr, sizeof(tempstr), "%s/%s", bytestr0, bytestr1); snprintf(tempstr, sizeof(tempstr), "%s/%s", bytestr0, bytestr1);
DrawStringF(TOP_SCREEN, bartxt_rx, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%19.19s", tempstr); DrawStringF(TOP_SCREEN, bartxt_rx, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%19.19s", tempstr);
show_time = false; show_time = false;
@ -262,7 +262,7 @@ void DrawTopBar(const char* curr_path) {
if (true) { // allocated mem if (true) { // allocated mem
const u32 bartxt_rx = SCREEN_WIDTH_TOP - (9*FONT_WIDTH_EXT) - bartxt_x; const u32 bartxt_rx = SCREEN_WIDTH_TOP - (9*FONT_WIDTH_EXT) - bartxt_x;
char bytestr[32]; 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); DrawStringF(TOP_SCREEN, bartxt_rx, bartxt_start, COLOR_STD_BG, COLOR_TOP_BAR, "%9.9s", bytestr);
show_time = false; show_time = false;
} }
@ -399,7 +399,7 @@ void DrawDirContents(DirStruct* contents, u32 cursor, u32* scroll) {
char namestr[UTF_BUFFER_BYTESIZE(str_width - 10)]; char namestr[UTF_BUFFER_BYTESIZE(str_width - 10)];
char rawbytestr[32], bytestr[UTF_BUFFER_BYTESIZE(10)]; char rawbytestr[32], bytestr[UTF_BUFFER_BYTESIZE(10)];
color_font = (cursor != offset_i) ? COLOR_ENTRY(curr_entry) : COLOR_STD_FONT; 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(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); ResizeString(namestr, curr_entry->name, str_width - 10, str_width - 20, false);
snprintf(tempstr, sizeof(tempstr), "%s%s", namestr, bytestr); snprintf(tempstr, sizeof(tempstr), "%s%s", namestr, bytestr);
@ -1031,7 +1031,7 @@ u32 CartRawDump(void) {
// input dump size // input dump size
dsize = cdata->cart_size; dsize = cdata->cart_size;
FormatBytes(bytestr, dsize); FormatBytes(bytestr, dsize, true);
dsize = ShowHexPrompt(dsize, 8, STR_CART_DETECTED_SIZE_INPUT_BELOW, cname, bytestr); dsize = ShowHexPrompt(dsize, 8, STR_CART_DETECTED_SIZE_INPUT_BELOW, cname, bytestr);
if (!dsize || (dsize == (u64) -1)) { if (!dsize || (dsize == (u64) -1)) {
free(cdata); free(cdata);
@ -1113,13 +1113,13 @@ u32 DirFileAttrMenu(const char* path, const char *name) {
ShowString("%s", drv ? STR_ANALYZING_DRIVE : STR_ANALYZING_DIR); ShowString("%s", drv ? STR_ANALYZING_DRIVE : STR_ANALYZING_DIR);
if (!DirInfo(path, &tsize, &tdirs, &tfiles)) if (!DirInfo(path, &tsize, &tdirs, &tfiles))
return 1; return 1;
FormatBytes(bytestr, tsize); FormatBytes(bytestr, tsize, true);
if (drv) { // drive specific if (drv) { // drive specific
char freestr[32], drvsstr[32], usedstr[32]; char freestr[32], drvsstr[32], usedstr[32];
FormatBytes(freestr, GetFreeSpace(path)); FormatBytes(freestr, GetFreeSpace(path), true);
FormatBytes(drvsstr, GetTotalSpace(path)); FormatBytes(drvsstr, GetTotalSpace(path), true);
FormatBytes(usedstr, GetTotalSpace(path) - GetFreeSpace(path)); FormatBytes(usedstr, GetTotalSpace(path) - GetFreeSpace(path), true);
snprintf(sizestr, sizeof(sizestr), STR_N_FILES_N_SUBDIRS_TOTAL_SIZE_FREE_USED_TOTAL, snprintf(sizestr, sizeof(sizestr), STR_N_FILES_N_SUBDIRS_TOTAL_SIZE_FREE_USED_TOTAL,
tfiles, tdirs, bytestr, freestr, usedstr, drvsstr); tfiles, tdirs, bytestr, freestr, usedstr, drvsstr);
} else { // dir specific } else { // dir specific
@ -1127,7 +1127,7 @@ u32 DirFileAttrMenu(const char* path, const char *name) {
} }
} else { // for files } else { // for files
char bytestr[32]; char bytestr[32];
FormatBytes(bytestr, fno.fsize); FormatBytes(bytestr, fno.fsize, true);
snprintf(sizestr, sizeof(sizestr), STR_FILESIZE_X, bytestr); 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; 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, 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); 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); 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 tsizestr[32];
char csizestr[32]; char csizestr[32];
char dsizestr[32]; char dsizestr[32];
FormatBytes(tsizestr, trimsize); FormatBytes(tsizestr, trimsize, true);
FormatBytes(csizestr, currentsize); FormatBytes(csizestr, currentsize, true);
FormatBytes(dsizestr, currentsize - trimsize); FormatBytes(dsizestr, currentsize - trimsize, true);
if (!trimsize || trimsize > currentsize) { if (!trimsize || trimsize > currentsize) {
ShowPrompt(false, "%s\n%s", pathstr, STR_FILE_CANT_BE_TRIMMED); ShowPrompt(false, "%s\n%s", pathstr, STR_FILE_CANT_BE_TRIMMED);

View File

@ -14,10 +14,11 @@
#define EXPLORER (1UL<<16) #define EXPLORER (1UL<<16)
#define ENCRYPTED (1UL<<17) #define ENCRYPTED (1UL<<17)
#define SIG_CHECK (1UL<<18) #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_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 #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 16 #define FLAGS_COUNT 17
#define LUASCRIPT_EXT "lua" #define LUASCRIPT_EXT "lua"
#define LUASCRIPT_MAX_SIZE STD_BUFFER_SIZE #define LUASCRIPT_MAX_SIZE STD_BUFFER_SIZE

View File

@ -284,11 +284,16 @@ static int ui_ask_selection(lua_State* L) {
} }
static int ui_format_bytes(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); lua_Integer size = luaL_checkinteger(L, 1);
u32 flags = 0;
if (extra) {
flags = GetFlagsFromTable(L, 2, flags, USE_LOCALE);
}
char bytesstr[32] = { 0 }; char bytesstr[32] = { 0 };
FormatBytes(bytesstr, (u64)size); FormatBytes(bytesstr, (u64)size, flags & USE_LOCALE);
lua_pushstring(L, bytesstr); lua_pushstring(L, bytesstr);
return 1; return 1;

View File

@ -3578,7 +3578,7 @@ u32 ShowGameCheckerInfo(const char* path) {
for (u32 i = 0; i < content_count; i++, chunk++) for (u32 i = 0; i < content_count; i++, chunk++)
content_size += getbe64(chunk->size); content_size += getbe64(chunk->size);
} }
FormatBytes(bytestr, content_size); FormatBytes(bytestr, content_size, true);
// check ticket // check ticket
if (ticket && ValidateTicket(ticket) == 0) if (ticket && ValidateTicket(ticket) == 0)

View File

@ -453,7 +453,7 @@ void upd_var(const char* name) {
if (!name || (strncmp(name, "SDSIZE", _VAR_NAME_LEN) == 0)) { if (!name || (strncmp(name, "SDSIZE", _VAR_NAME_LEN) == 0)) {
u64 sdsize = GetTotalSpace("0:"); u64 sdsize = GetTotalSpace("0:");
char sdsize_str[32+1]; char sdsize_str[32+1];
FormatBytes(sdsize_str, sdsize); FormatBytes(sdsize_str, sdsize, false);
set_var("SDSIZE", sdsize_str); set_var("SDSIZE", sdsize_str);
} }
@ -461,7 +461,7 @@ void upd_var(const char* name) {
if (!name || (strncmp(name, "SDFREE", _VAR_NAME_LEN) == 0)) { if (!name || (strncmp(name, "SDFREE", _VAR_NAME_LEN) == 0)) {
u64 sdfree = GetFreeSpace("0:"); u64 sdfree = GetFreeSpace("0:");
char sdfree_str[32+1]; char sdfree_str[32+1];
FormatBytes(sdfree_str, sdfree); FormatBytes(sdfree_str, sdfree, false);
set_var("SDFREE", sdfree_str); set_var("SDFREE", sdfree_str);
} }
@ -469,7 +469,7 @@ void upd_var(const char* name) {
if (!name || (strncmp(name, "NANDSIZE", _VAR_NAME_LEN) == 0)) { if (!name || (strncmp(name, "NANDSIZE", _VAR_NAME_LEN) == 0)) {
u64 nandsize = GetNandSizeSectors(NAND_SYSNAND) * 0x200; u64 nandsize = GetNandSizeSectors(NAND_SYSNAND) * 0x200;
char nandsize_str[32+1]; char nandsize_str[32+1];
FormatBytes(nandsize_str, nandsize); FormatBytes(nandsize_str, nandsize, false);
set_var("NANDSIZE", nandsize_str); set_var("NANDSIZE", nandsize_str);
} }
} }

View File

@ -340,15 +340,14 @@ Display a scrollable text viewer from a text file.
#### ui.format_bytes #### 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`. 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.
> [!NOTE]
> This is affected by localization and may return different text if the language is not English.
* **Arguments** * **Arguments**
* `bytes` - Size to format * `bytes` - Size to format
* `opts` (optional) - Option flags
* `use_locale` - Format in the user's selected locale
* **Returns:** formatted string * **Returns:** formatted string
#### ui.check_key #### ui.check_key