Drop newlib printf: log via BDK s_vprintf, add debug.ini log switch

log_write was the only vsnprintf user and pulled in newlib's printf,
dtoa, malloc and soft-float code (~44 KB). The payload now fits the
RCM limit again (146800 -> 102801 bytes); the build fails if it grows
past it. debug.ini log=1 writes sd:/config/omninx/install.log.
This commit is contained in:
niklascfw committed 2026-11-12 22:50:20 +01:00
1 parent 555ff07ae4
commit 702cce7d3e
7 files changed
+205 -156

No files matched your search

+6
View File
@@ -45,6 +45,12 @@ Create the folder `sd:/config/omninx/` on the SD card if it does not exist. Same
|-----|-----------------|
| `skip_hekate_8gb_post_copy` | After a successful copy: the optional `hekate_8GB` / RAM menu and related file moves (see `install_hekate_8gb_post_copy` in `install.c`) |
### Logging
| Key | Effect |
|-----|--------|
| `log` | Writes `sd:/config/omninx/install.log` with every deleted path, every copied file and the final result. Nothing is skipped. Slower on large packs; meant for test runs. |
## Behaviour
- On startup, if `debug.ini` exists and parses successfully, the payload prints a **DEBUG-MODUS** banner listing which skips are active.
+1 -1
View File
@@ -60,7 +60,7 @@ OBJS_RAMTEST := $(filter-out $(BUILDDIR)/$(TARGET)/main.o,$(OBJS)) \
all: $(OUTPUTDIR)/$(OUTPUT_NAME)
$(eval BIN_SIZE = $(shell wc -c < $(OUTPUTDIR)/$(OUTPUT_NAME)))
@echo "Payload size is $(BIN_SIZE) bytes (max $(MAX_PAYLOAD_SIZE) for RCM)"
@if [ $(BIN_SIZE) -gt $(MAX_PAYLOAD_SIZE) ]; then echo "WARNING: payload exceeds the RCM size limit"; fi
@if [ $(BIN_SIZE) -gt $(MAX_PAYLOAD_SIZE) ]; then echo "ERROR: payload exceeds the RCM size limit"; exit 1; fi
clean:
@rm -rf $(BUILDDIR)
+127 -122
View File
@@ -1,123 +1,128 @@
/*
* Copyright (c) 2019-2020 shchmue
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sprintf.h"
#include <stdarg.h>
static void _putc(char *buffer, const char c) {
*buffer = c;
}
static u32 _puts(char *buffer, const char *s) {
u32 count = 0;
for (; *s; s++, count++)
_putc(buffer + count, *s);
return count;
}
static u32 _putn(char *buffer, u32 v, int base, char fill, int fcnt) {
char buf[0x121];
static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
char *p;
int c = fcnt;
if (base > 36)
return 0;
p = buf + 0x120;
*p = 0;
do {
c--;
*--p = digits[v % base];
v /= base;
} while (v);
if (fill != 0) {
while (c > 0) {
*--p = fill;
c--;
}
}
return _puts(buffer, p);
}
u32 s_printf(char *buffer, const char *fmt, ...) {
va_list ap;
int fill, fcnt;
u32 count = 0;
va_start(ap, fmt);
while(*fmt) {
if (*fmt == '%') {
fmt++;
fill = 0;
fcnt = 0;
if ((*fmt >= '0' && *fmt <= '9') || *fmt == ' ') {
fcnt = *fmt;
fmt++;
if (*fmt >= '0' && *fmt <= '9') {
fill = fcnt;
fcnt = *fmt - '0';
fmt++;
} else {
fill = ' ';
fcnt -= '0';
}
}
switch (*fmt) {
case 'c':
_putc(buffer + count, va_arg(ap, u32));
count++;
break;
case 's':
count += _puts(buffer + count, va_arg(ap, char *));
break;
case 'd':
count += _putn(buffer + count, va_arg(ap, u32), 10, fill, fcnt);
break;
case 'p':
case 'P':
case 'x':
case 'X':
count += _putn(buffer + count, va_arg(ap, u32), 16, fill, fcnt);
break;
case '%':
_putc(buffer + count, '%');
count++;
break;
case '\0':
goto out;
default:
_putc(buffer + count, '%');
count++;
_putc(buffer + count, *fmt);
count++;
break;
}
} else {
_putc(buffer + count, *fmt);
count++;
}
fmt++;
}
out:
buffer[count] = 0;
va_end(ap);
return count;
/*
* Copyright (c) 2019-2020 shchmue
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sprintf.h"
#include <stdarg.h>
static void _putc(char *buffer, const char c) {
*buffer = c;
}
static u32 _puts(char *buffer, const char *s) {
u32 count = 0;
for (; *s; s++, count++)
_putc(buffer + count, *s);
return count;
}
static u32 _putn(char *buffer, u32 v, int base, char fill, int fcnt) {
char buf[0x121];
static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
char *p;
int c = fcnt;
if (base > 36)
return 0;
p = buf + 0x120;
*p = 0;
do {
c--;
*--p = digits[v % base];
v /= base;
} while (v);
if (fill != 0) {
while (c > 0) {
*--p = fill;
c--;
}
}
return _puts(buffer, p);
}
u32 s_vprintf(char *buffer, const char *fmt, va_list ap) {
int fill, fcnt;
u32 count = 0;
while(*fmt) {
if (*fmt == '%') {
fmt++;
fill = 0;
fcnt = 0;
if ((*fmt >= '0' && *fmt <= '9') || *fmt == ' ') {
fcnt = *fmt;
fmt++;
if (*fmt >= '0' && *fmt <= '9') {
fill = fcnt;
fcnt = *fmt - '0';
fmt++;
} else {
fill = ' ';
fcnt -= '0';
}
}
switch (*fmt) {
case 'c':
_putc(buffer + count, va_arg(ap, u32));
count++;
break;
case 's':
count += _puts(buffer + count, va_arg(ap, char *));
break;
case 'd':
count += _putn(buffer + count, va_arg(ap, u32), 10, fill, fcnt);
break;
case 'p':
case 'P':
case 'x':
case 'X':
count += _putn(buffer + count, va_arg(ap, u32), 16, fill, fcnt);
break;
case '%':
_putc(buffer + count, '%');
count++;
break;
case '\0':
goto out;
default:
_putc(buffer + count, '%');
count++;
_putc(buffer + count, *fmt);
count++;
break;
}
} else {
_putc(buffer + count, *fmt);
count++;
}
fmt++;
}
out:
buffer[count] = 0;
return count;
}
u32 s_printf(char *buffer, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
u32 count = s_vprintf(buffer, fmt, ap);
va_end(ap);
return count;
}
+26 -24
View File
@@ -1,24 +1,26 @@
/*
* Copyright (c) 2019-2020 shchmue
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SPRINTF_H_
#define _SPRINTF_H_
#include "types.h"
u32 s_printf(char *buffer, const char *fmt, ...);
#endif
/*
* Copyright (c) 2019-2020 shchmue
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SPRINTF_H_
#define _SPRINTF_H_
#include "types.h"
#include <stdarg.h>
u32 s_printf(char *buffer, const char *fmt, ...);
u32 s_vprintf(char *buffer, const char *fmt, va_list ap);
#endif
+13 -7
View File
@@ -9,14 +9,15 @@
#include <string.h>
#include <utils/sprintf.h>
#include <stdarg.h>
#include <stdio.h>
#define FS_BUFFER_SIZE 0x100000 // 1MB copy buffer
#define LOG_BUFFER_SIZE 512
#define LOG_BUFFER_SIZE 1024 // fits two 255-char paths plus text
#define LOG_SYNC_EVERY 16
// Log file handle
static FIL log_file;
static bool log_enabled = false;
static u32 log_lines;
static char log_buf[LOG_BUFFER_SIZE];
// Initialize log file
@@ -24,6 +25,7 @@ void log_init(const char *path) {
int res = f_open(&log_file, path, FA_WRITE | FA_CREATE_ALWAYS);
if (res == FR_OK) {
log_enabled = true;
log_lines = 0;
log_write("=== OmniNX Installer Log ===\n\n");
}
}
@@ -44,15 +46,19 @@ void log_write(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
// Format the message
vsnprintf(log_buf, LOG_BUFFER_SIZE, fmt, args);
// BDK formatter (no newlib printf). Callers keep paths <= 255 chars.
u32 len = s_vprintf(log_buf, fmt, args);
va_end(args);
// Write to file
UINT bw;
f_write(&log_file, log_buf, strlen(log_buf), &bw);
f_sync(&log_file); // Flush to ensure data is written
f_write(&log_file, log_buf, len, &bw);
if (++log_lines % LOG_SYNC_EVERY == 0)
f_sync(&log_file);
}
bool log_is_enabled(void) {
return log_enabled;
}
// Convert FatFS error code to string
+1
View File
@@ -18,3 +18,4 @@ int folder_delete(const char *path);
void log_init(const char *path);
void log_close(void);
void log_write(const char *fmt, ...);
bool log_is_enabled(void);
+31 -2
View File
@@ -289,7 +289,9 @@ int folder_copy_with_progress_v2(const char *src, const char *dst, const char *d
install_set_color(COLOR_WHITE);
// Perform the copy with progress updates
log_write("COPY TREE %s -> %s (%d Eintraege)\n", src, dst, (u32)total);
res = folder_copy_progress_recursive(src, dst, &copied, total, start_x, start_y, display_name, &last_percent);
log_write("COPY TREE %s: %s\n", src, fs_error_str(res));
// Final update - overwrite the same line
gfx_con_setpos(start_x, start_y);
@@ -499,6 +501,7 @@ int delete_path_lists_grouped(const char *folder_display_name, ...) {
res = f_unlink(path);
}
log_write("DELETE %s: %s\n", path, fs_error_str(res));
if (res == FR_OK || res == FR_NO_FILE)
deleted++;
else
@@ -1343,6 +1346,7 @@ static void install_extras_post_copy(void)
}
#define INSTALL_DEBUG_INI "sd:/config/omninx/debug.ini"
#define INSTALL_LOG_PATH "sd:/config/omninx/install.log"
typedef struct {
bool active;
@@ -1356,6 +1360,7 @@ typedef struct {
bool skip_update_staging_cleanup;
bool skip_update_horizon_oc;
bool skip_hekate_8gb_post_copy;
bool log;
} install_debug_opts_t;
static void install_debug_load(install_debug_opts_t *d)
@@ -1397,6 +1402,8 @@ static void install_debug_load(install_debug_opts_t *d)
d->skip_update_horizon_oc = install_ini_kv_truth(kv->val);
else if (strcmp(kv->key, "skip_hekate_8gb_post_copy") == 0)
d->skip_hekate_8gb_post_copy = install_ini_kv_truth(kv->val);
else if (strcmp(kv->key, "log") == 0)
d->log = install_ini_kv_truth(kv->val);
}
break;
}
@@ -1405,7 +1412,7 @@ static void install_debug_load(install_debug_opts_t *d)
d->active = d->skip_clean_backup || d->skip_clean_wipe || d->skip_clean_restore
|| d->skip_clean_install || d->skip_clean_staging_cleanup || d->skip_update_cleanup
|| d->skip_update_install || d->skip_update_staging_cleanup || d->skip_update_horizon_oc
|| d->skip_hekate_8gb_post_copy;
|| d->skip_hekate_8gb_post_copy || d->log;
}
static void install_debug_print_banner(const install_debug_opts_t *d)
@@ -1435,17 +1442,39 @@ static void install_debug_print_banner(const install_debug_opts_t *d)
gfx_printf(" ueberspringe: HorizonOC Update (Backup/Restore)\n");
if (d->skip_hekate_8gb_post_copy)
gfx_printf(" ueberspringe: extras RAM / Hekate-Auswahl / Post-Copy\n");
if (d->log)
gfx_printf(" Log: %s\n", INSTALL_LOG_PATH);
install_set_color(COLOR_WHITE);
gfx_printf("\n");
}
static int perform_installation_steps(omninx_variant_t pack_variant, install_mode_t mode, const install_debug_opts_t *d);
// Main installation function
int perform_installation(omninx_variant_t pack_variant, install_mode_t mode) {
int res;
install_debug_opts_t dbg;
install_debug_load(&dbg);
install_debug_print_banner(&dbg);
if (dbg.log) {
log_init(INSTALL_LOG_PATH);
log_write("Version: %s\nModus: %s\nVariante: %d\n\n", VERSION,
mode == INSTALL_MODE_UPDATE ? "Update" : "Clean", (u32)pack_variant);
}
int res = perform_installation_steps(pack_variant, mode, &dbg);
if (log_is_enabled()) {
log_write("\n=== Ende: res=%d (%s) ===\n", (u32)res, fs_error_str(res));
log_close();
}
return res;
}
static int perform_installation_steps(omninx_variant_t pack_variant, install_mode_t mode, const install_debug_opts_t *d) {
int res;
const install_debug_opts_t dbg = *d;
if (mode == INSTALL_MODE_UPDATE) {
if (pack_variant == VARIANT_OC && !dbg.skip_update_horizon_oc) {
bool had_horizon_cfg = install_path_exists(HORIZON_OC_CONFIG_PATH);