From 41068014860aaccd43afec7a4c721f959462a472 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Sun, 11 Oct 2026 22:21:31 +0100 Subject: [PATCH 01/15] dns.mitm: forward queries for hostnames not in the hosts file --- .../source/dns_mitm/dnsmitm_resolver_impl.cpp | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp index 0d514c6ce..c0b7e28b6 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp @@ -128,7 +128,7 @@ namespace ams::mitm::socket::resolver { } if (query == nullptr) { - return 0; + return -1; } struct ams::socket::ares_addrinfo_node node = { @@ -182,13 +182,15 @@ namespace ams::mitm::socket::resolver { return result; } - void CancelAresAddrInfoQuery(u32 query_id) { + bool CancelAresAddrInfoQuery(u32 query_id) { for (auto it = g_ares_addr_info_queries.begin(); it != g_ares_addr_info_queries.end(); ++it) { if (it->first == static_cast(query_id)) { g_ares_addr_info_queries.erase(it); - break; + return true; } } + + return false; } Result ResolverImpl::GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out out_host_error, sf::Out out_errno, const sf::OutBuffer &out_hostent, sf::Out out_size) { @@ -353,7 +355,7 @@ namespace ams::mitm::socket::resolver { LogDebug("[%016lx]: Queued query with id 0x%lx\n", m_client_info.program_id.value, query_id); - /* Forward the query. The provided socket EventFd must be signalled by having bsdsocket write 1 to the associated socket. */ + /* Forward to session. The provided socket EventFd must be signalled by having bsdsocket write 1 to the associated socket. */ /* This is fine though, we only care about replacing with our redirection during fetch. */ R_TRY(sfdnsresQueryAddrInfoRequestWithOptionsFwd(m_forward_service.get(), static_cast(client_pid.GetValue().value), node.GetPointer(), node.GetSize(), srv.GetPointer(), srv.GetSize(), serialized_hint.GetPointer(), serialized_hint.GetSize(), reinterpret_cast(out_query_id.GetPointer()), fd, options_version, options.GetPointer(), options.GetSize(), num_options, reinterpret_cast(out_retval.GetPointer()), reinterpret_cast(out_errno.GetPointer()))); @@ -371,6 +373,9 @@ namespace ams::mitm::socket::resolver { LogDebug("[%016lx]: FetchAddrInfoRequestWithOptions(%d)\n", m_client_info.program_id.value, query_id); const auto size = DequeueAresAddrInfoQuery(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), query_id); + + /* If we don't have this query queued, then the hostname is not in the hosts file. Forward to session. */ + R_UNLESS(size >= 0, sm::mitm::ResultShouldForwardToSession()); LogDebug("[%016lx]: Dequeued query with size 0x%lx\n", m_client_info.program_id.value, size); @@ -386,7 +391,12 @@ namespace ams::mitm::socket::resolver { LogDebug("[%016lx]: CancelQuery(%d)\n", m_client_info.program_id.value, query_id); - CancelAresAddrInfoQuery(query_id); + const auto found = CancelAresAddrInfoQuery(query_id); + + /* If we don't have this query queued, then we never intercepted it. Forward to session. */ + R_UNLESS(found, sm::mitm::ResultShouldForwardToSession()); + + LogDebug("[%016lx]: Cancelled query\n", m_client_info.program_id.value); R_SUCCEED(); } From eccd39312679c941995a54bc02498f928a727e01 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Sun, 11 Oct 2026 22:38:45 +0100 Subject: [PATCH 02/15] fs.mitm: add entries for regional versions of Trails in the Sky 2nd Chapter (closes #2879) --- stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp index 314f2db8f..470753f77 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfs.cpp @@ -51,10 +51,12 @@ namespace ams::mitm::fs { /* Unknown heap sensitivity. */ { 0x01002C9022770000, 16_MB, 0_MB }, - /* Trails in the Sky 2nd Chapter. */ + /* Trails in the Sky 2nd Chapter (Global, Asia and Japan versions). */ /* Requirement ~16 MB.*/ /* No particular heap sensitivity. */ { 0x01007A2027548000, 8_MB, 0_MB }, + { 0x0100E8B028B54000, 8_MB, 0_MB }, + { 0x01002270268E0000, 8_MB, 0_MB }, /* STAR WARS: Knights of the Old Republic II: The Sith Lords. */ /* Requirement ? MB. 16 MB stolen heap fixes a crash, though. */ From c7e6c35e900b52c364d557bdbfe554c857158d97 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Sun, 11 Oct 2026 22:59:40 +0100 Subject: [PATCH 03/15] libstrat: fix building for windows target --- .../include/stratosphere/socket/socket_types.hpp | 4 ++++ libraries/libstratosphere/source/util/zstd.inc | 16 +++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp index b9bd3a0bf..5d54dda64 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp @@ -167,7 +167,11 @@ namespace ams::socket { int ai_family; int ai_socktype; int ai_protocol; + #if defined(ATMOSPHERE_OS_WINDOWS) + int ai_addrlen; + #else socklen_t ai_addrlen; + #endif struct sockaddr *ai_addr; struct ares_addrinfo_node *ai_next; }; diff --git a/libraries/libstratosphere/source/util/zstd.inc b/libraries/libstratosphere/source/util/zstd.inc index 72ea32f00..ba2708cab 100644 --- a/libraries/libstratosphere/source/util/zstd.inc +++ b/libraries/libstratosphere/source/util/zstd.inc @@ -46857,12 +46857,14 @@ size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, /*-************************************* * Dependencies ***************************************/ -/* qsort_r is an extension. */ -#if defined(__linux) || defined(__linux__) || defined(linux) || defined(__gnu_linux__) || \ - defined(__CYGWIN__) || defined(__MSYS__) -#if !defined(_GNU_SOURCE) && !defined(__ANDROID__) /* NDK doesn't ship qsort_r(). */ -#define _GNU_SOURCE -#endif +/* qsort_r is an extension. + * + * Android NDK does not ship qsort_r(). + */ +#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__CYGWIN__) || defined(__MSYS__) +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif #endif #include /* fprintf */ @@ -47799,7 +47801,7 @@ static void stableSort(COVER_ctx_t *ctx) { qsort_r(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); -#elif defined(_GNU_SOURCE) +#elif (defined(__linux__) && !defined(__ANDROID__)) || defined(__CYGWIN__) || defined(__MSYS__) qsort_r(ctx->suffix, ctx->suffixSize, sizeof(U32), (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp), ctx); From fa19cc38b7c80bff0af5829459550de6b1e474f4 Mon Sep 17 00:00:00 2001 From: borntohonk <6264306+borntohonk@users.noreply.github.com> Date: Mon, 12 Oct 2026 21:04:08 +0200 Subject: [PATCH 04/15] libstrat: fix building for non-windows targets (#2881) --- libraries/libstratosphere/source/util/zstd.inc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/libstratosphere/source/util/zstd.inc b/libraries/libstratosphere/source/util/zstd.inc index ba2708cab..9cf0843b3 100644 --- a/libraries/libstratosphere/source/util/zstd.inc +++ b/libraries/libstratosphere/source/util/zstd.inc @@ -47714,7 +47714,10 @@ typedef struct { unsigned d; } COVER_ctx_t; -#if !defined(_GNU_SOURCE) && !defined(__APPLE__) && !defined(_MSC_VER) +#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__CYGWIN__) || defined(__MSYS__) +# define COVER_QSORT_GNU 1 +#endif +#if !defined(COVER_QSORT_GNU) && !defined(__APPLE__) && !defined(_MSC_VER) /* C90 only offers qsort() that needs a global context. */ static COVER_ctx_t *g_coverCtx = NULL; #endif @@ -47763,7 +47766,7 @@ static int COVER_cmp8(COVER_ctx_t *ctx, const void *lp, const void *rp) { */ #if (defined(_WIN32) && defined(_MSC_VER)) || defined(__APPLE__) static int WIN_CDECL COVER_strict_cmp(void* g_coverCtx, const void* lp, const void* rp) { -#elif defined(_GNU_SOURCE) +#elif defined(COVER_QSORT_GNU) static int COVER_strict_cmp(const void *lp, const void *rp, void *g_coverCtx) { #else /* C90 fallback.*/ static int COVER_strict_cmp(const void *lp, const void *rp) { @@ -47779,7 +47782,7 @@ static int COVER_strict_cmp(const void *lp, const void *rp) { */ #if (defined(_WIN32) && defined(_MSC_VER)) || defined(__APPLE__) static int WIN_CDECL COVER_strict_cmp8(void* g_coverCtx, const void* lp, const void* rp) { -#elif defined(_GNU_SOURCE) +#elif defined(COVER_QSORT_GNU) static int COVER_strict_cmp8(const void *lp, const void *rp, void *g_coverCtx) { #else /* C90 fallback.*/ static int COVER_strict_cmp8(const void *lp, const void *rp) { @@ -47801,7 +47804,7 @@ static void stableSort(COVER_ctx_t *ctx) { qsort_r(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); -#elif (defined(__linux__) && !defined(__ANDROID__)) || defined(__CYGWIN__) || defined(__MSYS__) +#elif defined(COVER_QSORT_GNU) qsort_r(ctx->suffix, ctx->suffixSize, sizeof(U32), (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp), ctx); From 06870a20e7b8f50f2f404a0e7de9d3bcd08c2e42 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Mon, 12 Oct 2026 20:37:25 +0100 Subject: [PATCH 05/15] libstrat: synchronize zstd.inc with https://github.com/hexkyz/zstd/tree/zbic Implement a more robust platform check cherry-picked from zstd's dev branch --- .../libstratosphere/source/util/zstd.inc | 63 +++++++++++++------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/libraries/libstratosphere/source/util/zstd.inc b/libraries/libstratosphere/source/util/zstd.inc index 9cf0843b3..58b47ded8 100644 --- a/libraries/libstratosphere/source/util/zstd.inc +++ b/libraries/libstratosphere/source/util/zstd.inc @@ -46867,6 +46867,8 @@ size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, # endif #endif +#define __STDC_WANT_LIB_EXT1__ 1 /* request C11 Annex K, which includes qsort_s() */ + #include /* fprintf */ #include /* malloc, free, qsort_r */ @@ -46877,6 +46879,7 @@ size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, # define ZDICT_STATIC_LINKING_ONLY #endif +/**** skipping file: ../common/debug.h ****/ /**** skipping file: ../common/mem.h ****/ /**** skipping file: ../common/pool.h ****/ /**** skipping file: ../common/threading.h ****/ @@ -47533,6 +47536,31 @@ void COVER_dictSelectionFree(COVER_dictSelection_t selection); #define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB)) #define COVER_DEFAULT_SPLITPOINT 1.0 +/** + * Select the qsort() variant used by cover + */ +#define ZDICT_QSORT_MIN 0 +#define ZDICT_QSORT_C90 ZDICT_QSORT_MIN +#define ZDICT_QSORT_GNU 1 +#define ZDICT_QSORT_APPLE 2 +#define ZDICT_QSORT_MSVC 3 +#define ZDICT_QSORT_C11 ZDICT_QSORT_MAX +#define ZDICT_QSORT_MAX 4 + +#ifndef ZDICT_QSORT +# if defined(__APPLE__) +# define ZDICT_QSORT ZDICT_QSORT_APPLE /* uses qsort_r() with a different order for parameters */ +# elif (defined(__linux__) && !defined(__ANDROID__)) || defined(__CYGWIN__) || defined(__MSYS__) +# define ZDICT_QSORT ZDICT_QSORT_GNU /* uses qsort_r() */ +# elif defined(_WIN32) && defined(_MSC_VER) +# define ZDICT_QSORT ZDICT_QSORT_MSVC /* uses qsort_s() with a different order for parameters */ +# elif defined(STDC_LIB_EXT1) && (STDC_LIB_EXT1 > 0) /* C11 Annex K */ +# define ZDICT_QSORT ZDICT_QSORT_C11 /* uses qsort_s() */ +# else +# define ZDICT_QSORT ZDICT_QSORT_C90 /* uses standard qsort() which is not re-entrant (requires global variable) */ +# endif +#endif + /*-************************************* * Console display ***************************************/ @@ -47714,11 +47742,8 @@ typedef struct { unsigned d; } COVER_ctx_t; -#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__CYGWIN__) || defined(__MSYS__) -# define COVER_QSORT_GNU 1 -#endif -#if !defined(COVER_QSORT_GNU) && !defined(__APPLE__) && !defined(_MSC_VER) -/* C90 only offers qsort() that needs a global context. */ +#if ZDICT_QSORT == ZDICT_QSORT_C90 +/* Use global context for non-reentrant sort functions */ static COVER_ctx_t *g_coverCtx = NULL; #endif @@ -47764,9 +47789,9 @@ static int COVER_cmp8(COVER_ctx_t *ctx, const void *lp, const void *rp) { /** * Same as COVER_cmp() except ties are broken by pointer value */ -#if (defined(_WIN32) && defined(_MSC_VER)) || defined(__APPLE__) +#if (ZDICT_QSORT == ZDICT_QSORT_MSVC) || (ZDICT_QSORT == ZDICT_QSORT_APPLE) static int WIN_CDECL COVER_strict_cmp(void* g_coverCtx, const void* lp, const void* rp) { -#elif defined(COVER_QSORT_GNU) +#elif (ZDICT_QSORT == ZDICT_QSORT_GNU) || (ZDICT_QSORT == ZDICT_QSORT_C11) static int COVER_strict_cmp(const void *lp, const void *rp, void *g_coverCtx) { #else /* C90 fallback.*/ static int COVER_strict_cmp(const void *lp, const void *rp) { @@ -47780,9 +47805,9 @@ static int COVER_strict_cmp(const void *lp, const void *rp) { /** * Faster version for d <= 8. */ -#if (defined(_WIN32) && defined(_MSC_VER)) || defined(__APPLE__) +#if (ZDICT_QSORT == ZDICT_QSORT_MSVC) || (ZDICT_QSORT == ZDICT_QSORT_APPLE) static int WIN_CDECL COVER_strict_cmp8(void* g_coverCtx, const void* lp, const void* rp) { -#elif defined(COVER_QSORT_GNU) +#elif (ZDICT_QSORT == ZDICT_QSORT_GNU) || (ZDICT_QSORT == ZDICT_QSORT_C11) static int COVER_strict_cmp8(const void *lp, const void *rp, void *g_coverCtx) { #else /* C90 fallback.*/ static int COVER_strict_cmp8(const void *lp, const void *rp) { @@ -47799,26 +47824,28 @@ static int COVER_strict_cmp8(const void *lp, const void *rp) { * Hopefully when C11 become the norm, we will be able * to clean it up. */ -static void stableSort(COVER_ctx_t *ctx) { -#if defined(__APPLE__) +static void stableSort(COVER_ctx_t *ctx) +{ + DEBUG_STATIC_ASSERT(ZDICT_QSORT_MIN <= ZDICT_QSORT && ZDICT_QSORT <= ZDICT_QSORT_MAX); +#if (ZDICT_QSORT == ZDICT_QSORT_APPLE) qsort_r(ctx->suffix, ctx->suffixSize, sizeof(U32), ctx, (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); -#elif defined(COVER_QSORT_GNU) +#elif (ZDICT_QSORT == ZDICT_QSORT_GNU) qsort_r(ctx->suffix, ctx->suffixSize, sizeof(U32), (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp), ctx); -#elif defined(_WIN32) && defined(_MSC_VER) +#elif (ZDICT_QSORT == ZDICT_QSORT_MSVC) + qsort_s(ctx->suffix, ctx->suffixSize, sizeof(U32), + (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp), + ctx); +#elif (ZDICT_QSORT == ZDICT_QSORT_C11) qsort_s(ctx->suffix, ctx->suffixSize, sizeof(U32), (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp), ctx); -#elif defined(__OpenBSD__) - g_coverCtx = ctx; - mergesort(ctx->suffix, ctx->suffixSize, sizeof(U32), - (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); #else /* C90 fallback.*/ g_coverCtx = ctx; - /* TODO(cavalcanti): implement a reentrant qsort() when is not available. */ + /* TODO(cavalcanti): implement a reentrant qsort() when _r is not available. */ qsort(ctx->suffix, ctx->suffixSize, sizeof(U32), (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp)); #endif From 4c233196dda5f5b1e72519d9cc6015fab3e0e69d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Br=C3=A4ucker?= <7303810+chrisbraucker@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:48:41 +0200 Subject: [PATCH 06/15] Fall back to RSA device certificate for cal0 authenticity check (#2887) * Fix device ID check in cal0 backup path Only the ECC device certificate was used, which may be absent on some devices. Adds a fallback to use the RSA device certificate which is present on those. * Move cert validity check into device ID check * Fix ecc case condition in cases where ECC is not blank and not valid, instead of aborting, it falls through to RSA --- .../ams_mitm/source/amsmitm_prodinfo_utils.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/stratosphere/ams_mitm/source/amsmitm_prodinfo_utils.cpp b/stratosphere/ams_mitm/source/amsmitm_prodinfo_utils.cpp index 1e419f8f8..c56b3f47b 100644 --- a/stratosphere/ams_mitm/source/amsmitm_prodinfo_utils.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_prodinfo_utils.cpp @@ -256,7 +256,6 @@ namespace ams::mitm { bool IsValid(const CalibrationInfo &cal) { return IsValidHeader(cal) && IsValid(cal.GetBlock()) && - IsValid(cal.GetBlock()) && IsValid(cal.GetBlock()) && IsValid(cal.GetBlock()) && cal.GetBlock().ssl_certificate_size <= sizeof(cal.GetBlock().ssl_certificate) && @@ -267,7 +266,8 @@ namespace ams::mitm { IsValidSerialNumber(cal); } - bool ContainsCorrectDeviceId(const EccB233DeviceCertificateBlock &block, u64 device_id) { + template + bool ContainsCorrectDeviceId(const Block &block, u64 device_id) { static constexpr size_t DeviceIdOffset = 0xC6; char found_device_id_str[sizeof("0011223344556677")] = {}; ON_SCOPE_EXIT { std::memset(found_device_id_str, 0, sizeof(found_device_id_str)); }; @@ -279,7 +279,14 @@ namespace ams::mitm { } bool ContainsCorrectDeviceId(const CalibrationInfo &cal) { - return ContainsCorrectDeviceId(cal.GetBlock(), exosphere::GetDeviceId()); + const auto &ecc = cal.GetBlock(); + if (!IsBlank(ecc)) { + return IsValid(ecc) && ContainsCorrectDeviceId(ecc, exosphere::GetDeviceId()); + } + + /* Newer consoles may leave the ECC certificate unprogrammed. */ + const auto &rsa = cal.GetBlock(); + return !IsBlank(rsa) && IsValid(rsa) && ContainsCorrectDeviceId(rsa, exosphere::GetDeviceId()); } bool IsValidForSecureBackup(const CalibrationInfo &cal) { From 90cd78a3a31a411deeacce4429da9690b1ac9012 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Wed, 14 Oct 2026 22:08:48 +0100 Subject: [PATCH 07/15] dns.mitm: trust bsdsocket to generate query ids Further testing confirmed that bsdsocket ensures each query is unique and per-process. Not only this allows us to simplify the mitm logic, but also fix a noticeable slowdown that was being caused by clients not being informed that bsdsocket's queue was full. --- .../source/dns_mitm/dnsmitm_resolver_impl.cpp | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp index c0b7e28b6..1326f79c2 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp @@ -28,11 +28,11 @@ namespace ams::mitm::socket::resolver { std::string hostname; ams::socket::InAddrT redirect_addr; u16 redirect_port; - const struct ams::socket::ares_addrinfo_hints *hint; + bool use_hint; + struct ams::socket::ares_addrinfo_hints hint; }; std::map> g_ares_addr_info_queries; - ssize_t g_current_ares_addr_info_query_id; } @@ -100,21 +100,17 @@ namespace ams::mitm::socket::resolver { return result; } - ssize_t QueueAresAddrInfoQuery(const char *hostname, ams::socket::InAddrT redirect_addr, u16 redirect_port, const struct ams::socket::ares_addrinfo_hints *hint) { - ssize_t query_id = g_current_ares_addr_info_query_id; - + void QueueAresAddrInfoQuery(ssize_t query_id, const char *hostname, ams::socket::InAddrT redirect_addr, u16 redirect_port, const struct ams::socket::ares_addrinfo_hints *hint) { AresAddrInfoQuery query; query.hostname = hostname; query.redirect_addr = redirect_addr; query.redirect_port = redirect_port; - query.hint = hint; - + query.use_hint = hint != nullptr; + if (query.use_hint) { + query.hint = *hint; + } + g_ares_addr_info_queries.emplace(query_id, std::make_unique(query)); - - /* NOTE: This really just increases ad infinitum. Should we bother checking it? */ - ++g_current_ares_addr_info_query_id; - - return query_id; } ssize_t DequeueAresAddrInfoQuery(u8 * const dst, size_t dst_size, u32 query_id) { @@ -141,8 +137,8 @@ namespace ams::mitm::socket::resolver { .ai_next = nullptr, }; - if (query->hint != nullptr) { - std::memcpy(reinterpret_cast(std::addressof(node.ai_flags)), reinterpret_cast(query->hint), sizeof(struct ams::socket::ares_addrinfo_hints)); + if (query->use_hint) { + std::memcpy(std::addressof(node.ai_flags), std::addressof(query->hint), sizeof(query->hint)); } switch (node.ai_family) { @@ -178,7 +174,7 @@ namespace ams::mitm::socket::resolver { const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ai); AMS_ABORT_UNLESS(result >= 0); - g_ares_addr_info_queries.erase(query_id); + g_ares_addr_info_queries.erase(static_cast(query_id)); return result; } @@ -343,7 +339,7 @@ namespace ams::mitm::socket::resolver { } } - LogDebug("[%016lx]: Queueing redirection %s:%u to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF); + LogDebug("[%016lx]: Queueing query for redirecting %s:%u to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF); const bool use_hint = serialized_hint.GetPointer() != nullptr; struct ams::socket::ares_addrinfo_hints hint = {}; @@ -351,18 +347,21 @@ namespace ams::mitm::socket::resolver { AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0); } - const auto query_id = QueueAresAddrInfoQuery(hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr); - - LogDebug("[%016lx]: Queued query with id 0x%lx\n", m_client_info.program_id.value, query_id); - /* Forward to session. The provided socket EventFd must be signalled by having bsdsocket write 1 to the associated socket. */ - /* This is fine though, we only care about replacing with our redirection during fetch. */ + /* This is fine though, we only care about replacing with our redirection during fetch. */ + /* Additionally, a failure at this point can be safely handled by bsdsocket, it simply won't create a new query. */ R_TRY(sfdnsresQueryAddrInfoRequestWithOptionsFwd(m_forward_service.get(), static_cast(client_pid.GetValue().value), node.GetPointer(), node.GetSize(), srv.GetPointer(), srv.GetSize(), serialized_hint.GetPointer(), serialized_hint.GetSize(), reinterpret_cast(out_query_id.GetPointer()), fd, options_version, options.GetPointer(), options.GetSize(), num_options, reinterpret_cast(out_retval.GetPointer()), reinterpret_cast(out_errno.GetPointer()))); - /* Replace with our own query. */ - *out_query_id = query_id; - *out_retval = 0; - *out_errno = 0; + /* If no errors were returned, trust the id bsdsocket generated and queue the query. */ + /* When bsdsocket's queue is full, retval and errno will reflect that and the returned query id will be -1. */ + /* This is expected by the client. We must drop this query and let the client see the errors for it to schedule a new query. */ + if ((*out_retval == 0) && (*out_errno == 0)) { + ssize_t query_id = static_cast(*out_query_id); + QueueAresAddrInfoQuery(query_id, hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr); + LogDebug("[%016lx]: Queued query with id 0x%lx\n", m_client_info.program_id.value, query_id); + } else { + LogDebug("[%016lx]: Dropping last query, queue is full\n", m_client_info.program_id.value); + } R_SUCCEED(); } From 2fec6889830112de95905979810690aba7cf7815 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Wed, 14 Oct 2026 22:23:03 +0100 Subject: [PATCH 08/15] libstrat: account for sockaddr as well in windows target --- .../include/stratosphere/socket/socket_types.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp index 5d54dda64..2435b8801 100644 --- a/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/socket/socket_types.hpp @@ -169,10 +169,11 @@ namespace ams::socket { int ai_protocol; #if defined(ATMOSPHERE_OS_WINDOWS) int ai_addrlen; + void *ai_addr; #else socklen_t ai_addrlen; - #endif struct sockaddr *ai_addr; + #endif struct ares_addrinfo_node *ai_next; }; From 0165f5dcc1f2f587b7c42a177fb46c6925ebf3da Mon Sep 17 00:00:00 2001 From: hexkyz Date: Thu, 15 Oct 2026 20:25:15 +0100 Subject: [PATCH 09/15] web: better browser memory patching strategy --- .../nintendo/nx/kern_k_system_control.cpp | 5 ++- .../source/ldr_embedded_web_patches.inc | 44 +++++++++---------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index de463ce6b..360fb69ee 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -358,9 +358,10 @@ namespace ams::kern::board::nintendo::nx { /* NOTE: On 20.0.0+ (and even more-so 21.0.0+) the browser requires much more memory in the applet pool in order to function. */ /* Thus, we have to reduce our extra system memory size by 26 MB to compensate. */ /* NOTE: On 23.0.0+ we are patching memory allocation for each browser applet since there are only 3 of them now. */ - /* Given that the root "Reserved" region is 0x43E000 bytes and the "ReservedTbd" subregion is 0x540000 bytes, we can safely reclaim 9MB.*/ + /* Reclaiming unused memory and lying to the browser about memory limits allows us to go back to 40 MB of extra system memory. */ + /* TODO: Deduplicate post 23.0.0 and pre 20.0.0. For now, this structure helps with readability and is still subject to change. */ if (kern::GetTargetFirmware() >= ams::TargetFirmware_23_0_0) { - constexpr size_t ExtraSystemMemoryForAtmosphere_23_0_0 = 16_MB; + constexpr size_t ExtraSystemMemoryForAtmosphere_23_0_0 = 40_MB; return base_pool_size - ExtraSystemMemoryForAtmosphere_23_0_0 - KTraceBufferSize; } else if (kern::GetTargetFirmware() >= ams::TargetFirmware_21_0_0) { constexpr size_t ExtraSystemMemoryForAtmosphere_21_0_0 = 7_MB; diff --git a/stratosphere/loader/source/ldr_embedded_web_patches.inc b/stratosphere/loader/source/ldr_embedded_web_patches.inc index 825123005..53e476c81 100644 --- a/stratosphere/loader/source/ldr_embedded_web_patches.inc +++ b/stratosphere/loader/source/ldr_embedded_web_patches.inc @@ -15,41 +15,37 @@ */ /* Breakdown of each patch in order: */ -/* #1 - Patch the 0xDEEF000 size check branch to NOP. */ +/* #1 - Set TotalAvailableMemorySize to 1GB. */ /* #2 - Change the root heap "Reserved" region allocation size from 0x43E000 to 0. */ -/* #3 - Change the root heap "Scene" size from 0x4580000 to 0x4040000. */ -/* #4 - Change the scene heap "ReservedTbd" region allocation size from 0x540000 to 0. */ -/* #5 - Patch the linking of "ReservedTbd" to the parent heap with a NOP. */ -/* #6 - Patch the initial memory block allocation size from 0xB400000 to 0xAC00000. */ -/* #7 - Patch the initial memory block initialization size from 0xB400000 to 0xAC00000. */ +/* #3 - Patch the initial memory block allocation size from 0xB400000 to 0xB000000. */ +/* #4 - Patch the initial memory block heap size from 0xB400000 to 0xB000000. */ +/* #5 - Patch the browser engine memory capacity from 0xAA00000 to 0x9000000. */ +/* #6 - Patch the browser engine memory allocation size from 0xAA00000 to 0x9000000. */ constexpr inline const EmbeddedPatchEntry SystemWebMemoryPatches_23_0_0[] = { - { 0x861DD4, "\x1F\x20\x03\xD5", 4 }, + { 0x6113A8, "\x09\x00\xA8\xD2", 4 }, { 0x861E80, "\x02\x00\x80\x52", 4 }, - { 0x861E6C, "\x82\x80\xA0\x52", 4 }, - { 0x8C1718, "\x02\x00\x80\x52", 4 }, - { 0x8C172C, "\x1F\x20\x03\xD5", 4 }, - { 0x861D44, "\x01\x58\xA1\x52", 4 }, - { 0x861D5C, "\x03\x58\xA1\x52", 4 }, + { 0x861D44, "\x01\x60\xA1\x52", 4 }, + { 0x861D5C, "\x03\x60\xA1\x52", 4 }, + { 0x8623D0, "\x01\x20\xA1\x52", 4 }, + { 0x862498, "\x01\x20\xA1\x52", 4 }, }; constexpr inline const EmbeddedPatchEntry OpenWebMemoryPatches_23_0_0[] = { - { 0x828DBC, "\x1F\x20\x03\xD5", 4 }, + { 0x611428, "\x09\x00\xA8\xD2", 4 }, { 0x828E68, "\x02\x00\x80\x52", 4 }, - { 0x828E54, "\x82\x80\xA0\x52", 4 }, - { 0x74C564, "\x02\x00\x80\x52", 4 }, - { 0x74C578, "\x1F\x20\x03\xD5", 4 }, - { 0x828D2C, "\x01\x58\xA1\x52", 4 }, - { 0x828D44, "\x03\x58\xA1\x52", 4 }, + { 0x828D2C, "\x01\x60\xA1\x52", 4 }, + { 0x828D44, "\x03\x60\xA1\x52", 4 }, + { 0x8293B8, "\x01\x20\xA1\x52", 4 }, + { 0x829480, "\x01\x20\xA1\x52", 4 }, }; constexpr inline const EmbeddedPatchEntry OfflineWebMemoryPatches_23_0_0[] = { - { 0x130698, "\x1F\x20\x03\xD5", 4 }, + { 0x6DE0DC, "\x09\x00\xA8\xD2", 4 }, { 0x130744, "\x02\x00\x80\x52", 4 }, - { 0x130730, "\x82\x80\xA0\x52", 4 }, - { 0x4D728, "\x02\x00\x80\x52", 4 }, - { 0x4D738, "\x1F\x20\x03\xD5", 4 }, - { 0x130614, "\x01\x58\xA1\x52", 4 }, - { 0x130624, "\x02\x58\xA1\x52", 4 }, + { 0x130614, "\x01\x60\xA1\x52", 4 }, + { 0x130624, "\x02\x60\xA1\x52", 4 }, + { 0x130C30, "\x01\x20\xA1\x52", 4 }, + { 0x130CD0, "\x01\x20\xA1\x52", 4 }, }; constexpr inline const EmbeddedPatch WebMemoryPatches[] = { From 43c6da000cf1e959b7ba3952b0d06b9d18559c8f Mon Sep 17 00:00:00 2001 From: hexkyz Date: Thu, 15 Oct 2026 22:06:50 +0100 Subject: [PATCH 10/15] dns.mitm: allow bsdsocket to cleanup --- .../source/dns_mitm/dnsmitm_resolver_impl.cpp | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp index 1326f79c2..2e7f7e091 100644 --- a/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp +++ b/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp @@ -319,8 +319,6 @@ namespace ams::mitm::socket::resolver { } Result ResolverImpl::QueryAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &node, const sf::InAutoSelectBuffer &srv, const sf::InAutoSelectBuffer &serialized_hint, sf::Out out_query_id, u32 fd, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out out_retval, sf::Out out_errno) { - AMS_UNUSED(client_pid, fd, options_version, options, num_options); - const char *hostname = reinterpret_cast(node.GetPointer()); LogDebug("[%016lx]: QueryAddrInfoRequestWithOptions(%s, %s)\n", m_client_info.program_id.value, hostname, reinterpret_cast(srv.GetPointer())); @@ -367,16 +365,19 @@ namespace ams::mitm::socket::resolver { } Result ResolverImpl::FetchAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, u32 query_id, u32 version, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out out_size, sf::Out out_retval, sf::Out out_errno) { - AMS_UNUSED(client_pid, version); - - LogDebug("[%016lx]: FetchAddrInfoRequestWithOptions(%d)\n", m_client_info.program_id.value, query_id); + LogDebug("[%016lx]: FetchAddrInfoRequestWithOptions(0x%x)\n", m_client_info.program_id.value, query_id); const auto size = DequeueAresAddrInfoQuery(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), query_id); /* If we don't have this query queued, then the hostname is not in the hosts file. Forward to session. */ R_UNLESS(size >= 0, sm::mitm::ResultShouldForwardToSession()); - LogDebug("[%016lx]: Dequeued query with size 0x%lx\n", m_client_info.program_id.value, size); + LogDebug("[%016lx]: Dequeued query 0x%x with size 0x%lx\n", m_client_info.program_id.value, query_id, size); + + /* Forward to session. The socket EventFd associated with this query must be cleared by having bsdsocket write 0 to the socket. */ + /* However, we want to keep our redirection, so we pass nullptr to bsdsocket forcing the command to end early with an error. */ + /* We discard the error and replace the output, pretending the command was successful while returning our redirected addrinfo to the client instead. */ + R_DISCARD(sfdnsresFetchAddrInfoRequestWithOptionsFwd(m_forward_service.get(), static_cast(client_pid.GetValue().value), query_id, version, nullptr, 0, reinterpret_cast(out_size.GetPointer()), reinterpret_cast(out_retval.GetPointer()), reinterpret_cast(out_errno.GetPointer()))); *out_size = size; *out_retval = 0; @@ -386,16 +387,18 @@ namespace ams::mitm::socket::resolver { } Result ResolverImpl::CancelQuery(const sf::ClientProcessId &client_pid, u32 query_id) { - AMS_UNUSED(client_pid); - - LogDebug("[%016lx]: CancelQuery(%d)\n", m_client_info.program_id.value, query_id); + LogDebug("[%016lx]: CancelQuery(0x%x)\n", m_client_info.program_id.value, query_id); const auto found = CancelAresAddrInfoQuery(query_id); /* If we don't have this query queued, then we never intercepted it. Forward to session. */ R_UNLESS(found, sm::mitm::ResultShouldForwardToSession()); - LogDebug("[%016lx]: Cancelled query\n", m_client_info.program_id.value); + LogDebug("[%016lx]: Cancelled query 0x%x\n", m_client_info.program_id.value, query_id); + + /* Forward to session. Ensures that bsdsocket cleans up. */ + /* We still want to discard any errors and return success in case this was a query we had queued. */ + R_DISCARD(sfdnsresCancelQueryFwd(m_forward_service.get(), static_cast(client_pid.GetValue().value), query_id)); R_SUCCEED(); } From d9654e3bd016d85961b641cfc580a13e9132204d Mon Sep 17 00:00:00 2001 From: hexkyz Date: Thu, 15 Oct 2026 22:29:04 +0100 Subject: [PATCH 11/15] emummc: update README for 23.0.1 --- emummc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emummc/README.md b/emummc/README.md index 752223788..0d921bd3c 100644 --- a/emummc/README.md +++ b/emummc/README.md @@ -2,7 +2,7 @@ *A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw*** ### Supported Horizon Versions -**1.0.0 - 23.0.0** +**1.0.0 - 23.0.1** ## Features * Arbitrary SDMMC backend selection From a8258fb31f3e954c51aacda5a0c346136731479f Mon Sep 17 00:00:00 2001 From: hexkyz Date: Thu, 15 Oct 2026 22:35:01 +0100 Subject: [PATCH 12/15] ams: bump version and add enum version support for 23.0.1 --- .../libstratosphere/include/stratosphere/hos/hos_types.hpp | 1 + libraries/libvapours/include/vapours/ams/ams_api_version.h | 4 ++-- .../libvapours/include/vapours/ams/ams_target_firmware.h | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp b/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp index 70f9bec97..9c90970a5 100644 --- a/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/hos/hos_types.hpp @@ -104,6 +104,7 @@ namespace ams::hos { Version_22_1_0 = ::ams::TargetFirmware_22_1_0, Version_22_5_0 = ::ams::TargetFirmware_22_5_0, Version_23_0_0 = ::ams::TargetFirmware_23_0_0, + Version_23_0_1 = ::ams::TargetFirmware_23_0_1, Version_Current = ::ams::TargetFirmware_Current, diff --git a/libraries/libvapours/include/vapours/ams/ams_api_version.h b/libraries/libvapours/include/vapours/ams/ams_api_version.h index 2fd987485..dd2b85fc4 100644 --- a/libraries/libvapours/include/vapours/ams/ams_api_version.h +++ b/libraries/libvapours/include/vapours/ams/ams_api_version.h @@ -17,10 +17,10 @@ #define ATMOSPHERE_RELEASE_VERSION_MAJOR 1 #define ATMOSPHERE_RELEASE_VERSION_MINOR 12 -#define ATMOSPHERE_RELEASE_VERSION_MICRO 0 +#define ATMOSPHERE_RELEASE_VERSION_MICRO 1 #define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 23 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0 -#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0 +#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 1 diff --git a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h index 109405664..d16f94e70 100644 --- a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h +++ b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h @@ -102,8 +102,9 @@ #define ATMOSPHERE_TARGET_FIRMWARE_22_1_0 ATMOSPHERE_TARGET_FIRMWARE(22, 1, 0) #define ATMOSPHERE_TARGET_FIRMWARE_22_5_0 ATMOSPHERE_TARGET_FIRMWARE(22, 5, 0) #define ATMOSPHERE_TARGET_FIRMWARE_23_0_0 ATMOSPHERE_TARGET_FIRMWARE(23, 0, 0) +#define ATMOSPHERE_TARGET_FIRMWARE_23_0_1 ATMOSPHERE_TARGET_FIRMWARE(23, 0, 1) -#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_23_0_0 +#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_23_0_1 #define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0) #define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT @@ -198,6 +199,7 @@ namespace ams { TargetFirmware_22_1_0 = ATMOSPHERE_TARGET_FIRMWARE_22_1_0, TargetFirmware_22_5_0 = ATMOSPHERE_TARGET_FIRMWARE_22_5_0, TargetFirmware_23_0_0 = ATMOSPHERE_TARGET_FIRMWARE_23_0_0, + TargetFirmware_23_0_1 = ATMOSPHERE_TARGET_FIRMWARE_23_0_1, TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT, From 6eb8c3e7ce4209c60bac362f1b88804318cccad2 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Fri, 16 Oct 2026 00:14:22 +0100 Subject: [PATCH 13/15] docs: add changelog for 1.12.1 --- docs/changelog.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index a5aa53a87..557afde9f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,26 @@ # Changelog +## 1.12.1 ++ Support was added for the new c-ares resolver in `dns.mitm`. + + Firmware 23.0.0 introduced a new set of commands to the `sfdnsres` service. These commands provide c-ares async support for DNS resolution and are now used by most sysmodules. + + This resulted in host redirection not being applied at all and without warning since we simply were not intercepting the new commands. + + `dns.mitm` now fully reimplements the c-ares serialization code and intercepts the relevant commands. In tandem with the `bsdsocket` sysmodule, we now keep track of resolution queries and redirect their results. ++ Support was added for the new "BatchRead" storage in `fs.mitm`. + + Firmware 23.0.0 introduced a new type of filesystem storage object abstracted by the interface "IStorageForBatchRead". This is a derived class from "IStorage" with the purpose of adding the new "BatchRead" command. + + Anything that uses the filesystem API now attempts to use this type of storage instead of the old one. This is of particular relevance for `qlaunch` and for supporting custom themes which were broken as a consequence of this change. + + `fs.mitm` was updated and extended to not only support the new storage type but also intercept a few newer commands that were previously missing. ++ The patches for the browser applets were redesigned. + + The introduction of these patches in the previous release led to several issues and crashes, notably during media playback in the eshop and within games that use LibAppletOff. + + After extensive analysis and testing it was concluded that the browser applets were running out of memory and still required reducing the memory we reserve for custom sysmodules. + + New patches have been devised and introduced in this release. + + Instead of patching size checks, we now trick the browser applets into thinking there is 1GB of free memory available to them. + + Unused memory is still being reclaimed and discounted from the root heap, but now more realistically. + + The browser engine's heap is now being shrunk with no noticeable downsides to media playback nor web page rendering. + + Thanks to these changes we can now provide 40MB for custom system modules once again. ++ A number of minor issues were fixed and improvements were made, such as: + + Automatic PRODINFO backup was fixed for recent console models that no longer use the ECC device certificate (thanks @chrisbraucker). + + State initialization for SharedMemory and TransferMemory was fixed (thanks @fl0be). + + Compatibility was fixed for Trails in the Sky 2nd Chapter (thanks @masagrator). ++ General system stability improvements to enhance the user's experience. ## 1.12.0 + Support was added for 23.0.0. + Special thanks to @alula for handling the implementation of several kernel changes. From ff43bc566adb33c51a43b9ef45b0d3898ad39040 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Fri, 16 Oct 2026 00:16:30 +0100 Subject: [PATCH 14/15] git subrepo push emummc subrepo: subdir: "emummc" merged: "11a2fd2a2" upstream: origin: "https://github.com/m4xw/emummc" branch: "develop" commit: "11a2fd2a2" git-subrepo: version: "0.4.9" origin: "???" commit: "???" --- emummc/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emummc/.gitrepo b/emummc/.gitrepo index eea652603..4200412ec 100644 --- a/emummc/.gitrepo +++ b/emummc/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/m4xw/emummc branch = develop - commit = 5b966b6f3f83349ec65066a1877f7f948b48d08c - parent = 7442487ace6b79ddddf2cd0007b8dade8150bf5c + commit = 11a2fd2a25a526c9df92506d69da1f679b1bd997 + parent = 6eb8c3e7ce4209c60bac362f1b88804318cccad2 method = merge cmdver = 0.4.9 From 6b0c077b4c608a3a2ee712b8dc35fee87ceb62e8 Mon Sep 17 00:00:00 2001 From: hexkyz Date: Fri, 16 Oct 2026 00:17:09 +0100 Subject: [PATCH 15/15] git subrepo push libraries subrepo: subdir: "libraries" merged: "cf48d2337" upstream: origin: "https://github.com/Atmosphere-NX/Atmosphere-libs" branch: "master" commit: "cf48d2337" git-subrepo: version: "0.4.9" origin: "???" commit: "???" --- libraries/.gitrepo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/.gitrepo b/libraries/.gitrepo index 56e7c065d..e67b1e41e 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = aae2da0155a490abd3bfd17a70ccd16bb73c916a - parent = c68e08a101353a89777eb118d6bd21d1dba98ce5 + commit = cf48d23375cf36742876f4b014971b2ebabc6dc8 + parent = ff43bc566adb33c51a43b9ef45b0d3898ad39040 method = merge cmdver = 0.4.9