diff --git a/native/switch-ota-launcher/README.md b/native/switch-ota-launcher/README.md index 010a2767..96417bcc 100644 --- a/native/switch-ota-launcher/README.md +++ b/native/switch-ota-launcher/README.md @@ -69,6 +69,6 @@ Manifest: `scripts/switch/ota_launcher.manifest`. - Zip extraction uses `switch-zziplib` (`ota_unzip.c`) on device. - OTA replaces launcher + game from the install zip (NACP versions stay aligned). -- HTTPS uses Mozilla CA bundle in romfs (`cacert.pem`, fetched at build time). +- HTTPS uses Mozilla CA bundle in romfs (`cacert.pem`, fetched at build time). `ota_net_init()` mounts romfs before the quiet release check. - Sphaira HOME forwarders cache metadata until reinstalled (see docs/switch-install.md). - Release runner: `switch-dev` + (`install_devkitpro_deps.sh` **or** Docker) diff --git a/native/switch-ota-launcher/include/ota_net.h b/native/switch-ota-launcher/include/ota_net.h index dcd86916..cd0c44c1 100644 --- a/native/switch-ota-launcher/include/ota_net.h +++ b/native/switch-ota-launcher/include/ota_net.h @@ -10,6 +10,10 @@ extern "C" { /* fraction is 0..1 when Content-Length is known, else -1 for indeterminate. */ typedef void (*ota_net_progress_fn)(void *userdata, double fraction); +/* Mount romfs (CA bundle) and init libcurl. Call before any HTTPS download. */ +int ota_net_init(void); +void ota_net_shutdown(void); + /* Download URL into memory buffer (caller frees *out). Returns 0 on success. */ int ota_net_download_buffer(const char *url, long timeout_ms, char **out, size_t *out_len, char *err, size_t err_len); diff --git a/native/switch-ota-launcher/src/main.c b/native/switch-ota-launcher/src/main.c index 9ee2f5a1..e2ed7840 100644 --- a/native/switch-ota-launcher/src/main.c +++ b/native/switch-ota-launcher/src/main.c @@ -232,13 +232,18 @@ int main(int argc, char **argv) { (void)argc; (void)argv; + const char *install = SD_INSTALL_DIR; + #if defined(__SWITCH__) socketInitializeDefault(); padConfigureInput(1, HidNpadStyleSet_NpadStandard); -#endif - - const char *install = SD_INSTALL_DIR; + if (ota_net_init() == 0) { + run_update_flow(install); + ota_net_shutdown(); + } +#else run_update_flow(install); +#endif char game[192]; snprintf(game, sizeof(game), "%s/%s", install, OTA_GAME_NRO_NAME); diff --git a/native/switch-ota-launcher/src/ota_net.c b/native/switch-ota-launcher/src/ota_net.c index 80b1510c..7b897b04 100644 --- a/native/switch-ota-launcher/src/ota_net.c +++ b/native/switch-ota-launcher/src/ota_net.c @@ -12,6 +12,31 @@ #if defined(__SWITCH__) #define OTA_CA_BUNDLE "romfs:/cacert.pem" +static int g_net_ready = 0; + +static int ota_ca_bundle_ready(void) { + FILE *f = fopen(OTA_CA_BUNDLE, "rb"); + if (!f) return 0; + fclose(f); + return 1; +} + +int ota_net_init(void) { + if (g_net_ready) return 0; + if (R_FAILED(romfsInit())) return -1; + if (!ota_ca_bundle_ready()) return -1; + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) return -1; + g_net_ready = 1; + return 0; +} + +void ota_net_shutdown(void) { + if (!g_net_ready) return; + curl_global_cleanup(); + romfsExit(); + g_net_ready = 0; +} + static void ota_net_configure_tls(CURL *curl) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); @@ -60,6 +85,11 @@ static int xfer_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, cu } return 0; } +#else + +int ota_net_init(void) { return 0; } +void ota_net_shutdown(void) {} + #endif int ota_net_download_buffer(const char *url, long timeout_ms, char **out, size_t *out_len, diff --git a/native/switch-ota-launcher/src/ota_ui.c b/native/switch-ota-launcher/src/ota_ui.c index e11262db..6cf44888 100644 --- a/native/switch-ota-launcher/src/ota_ui.c +++ b/native/switch-ota-launcher/src/ota_ui.c @@ -261,7 +261,7 @@ static void blit_logo(u32 *fb, u32 stride_px, int dst_x, int dst_y, int max_w) { static void load_logo(void) { if (g_logo) return; - romfsInit(); + /* romfs is mounted in ota_net_init() before the release check runs. */ int w = 0, h = 0, n = 0; unsigned char *data = stbi_load("romfs:/logo.png", &w, &h, &n, 4); if (!data || w <= 0 || h <= 0) { @@ -299,7 +299,6 @@ void ota_ui_shutdown(void) { free(g_logo); g_logo = NULL; g_logo_w = g_logo_h = 0; - romfsExit(); g_ready = 0; } diff --git a/scripts/switch/selftest_build_switch.sh b/scripts/switch/selftest_build_switch.sh index 7a796d09..1f8be38d 100755 --- a/scripts/switch/selftest_build_switch.sh +++ b/scripts/switch/selftest_build_switch.sh @@ -416,8 +416,10 @@ else fi if grep -q 'ota_ui_prompt_update' "$ROOT/native/switch-ota-launcher/src/main.c" \ + && grep -q 'ota_net_init' "$ROOT/native/switch-ota-launcher/src/main.c" \ && grep -q 'framebufferCreate\|COL_RAIL' "$ROOT/native/switch-ota-launcher/src/ota_ui.c" \ - && grep -q '^ROMFS' "$ROOT/native/switch-ota-launcher/Makefile" + && grep -q '^ROMFS' "$ROOT/native/switch-ota-launcher/Makefile" \ + && grep -q 'cacert.pem' "$ROOT/native/switch-ota-launcher/Makefile" then ok "launcher UI uses framebuffer (no prompt when up to date)" else diff --git a/test/switch-nro-ota.spec.test.js b/test/switch-nro-ota.spec.test.js index f2688bac..b0f75e2c 100644 --- a/test/switch-nro-ota.spec.test.js +++ b/test/switch-nro-ota.spec.test.js @@ -512,7 +512,8 @@ test('AC-010: Fonte do launcher e Makefile DEVKITPRO existem @spec:AC-010', () = assert.match(otaUi, /ota_ui_sanitize_ascii/); assert.match(otaUi, /draw_text_wrapped_centered/); assert.match(otaUi, /ota_ui_alert_error/); - assert.match(read('native/switch-ota-launcher/src/ota_net.c'), /XFERINFOFUNCTION|xfer_progress/); + assert.match(read('native/switch-ota-launcher/src/ota_net.c'), /ota_net_init/); + assert.match(read('native/switch-ota-launcher/src/main.c'), /ota_net_init/); assert.match(read('native/switch-ota-launcher/src/ota_net.c'), /CURLOPT_SSL_VERIFYPEER,\s*1L/); assert.match(read('native/switch-ota-launcher/src/ota_net.c'), /romfs:\/cacert\.pem/); assert.doesNotMatch(read('native/switch-ota-launcher/src/ota_net.c'), /CURLOPT_SSL_VERIFYPEER,\s*0L/);