From e9cdbf02b81d7d6f3003eafe666c54ad0bbc85d4 Mon Sep 17 00:00:00 2001 From: do-kiss Date: Fri, 26 Jun 2026 15:31:45 +0800 Subject: [PATCH 1/6] feat: add firmware version condition support - Add VersionOp enum and ProgramEntry struct with optional version check - Parse version conditions from config.ini (e.g. '0100000000001000 = >=16.0.0') - CheckVersion() blocks program only when firmware doesn't match condition - Backward compatible: entries without version condition behave as before - Add SysEnvResult_InvalidVersionFormat error code for malformed versions - Update README with firmware version condition documentation --- README.MD | 25 ++++++ sysmodule/source/fs.cpp | 169 ++++++++++++++++++++++++++++++++++-- sysmodule/source/fs.hpp | 19 +++- sysmodule/source/main.cpp | 5 +- sysmodule/source/result.hpp | 1 + 5 files changed, 207 insertions(+), 12 deletions(-) diff --git a/README.MD b/README.MD index 320bf66..90d07b4 100644 --- a/README.MD +++ b/README.MD @@ -31,6 +31,23 @@ You have two sections:
Order doesn’t matter.
Just add the program ID(s) under whichever environment you want them blocked in.
+### Firmware Version Conditions (Optional) + +Each program ID can optionally include a firmware version condition.
+When a condition is set, the program is **only blocked if the current firmware does NOT match the condition**.
+ +Supported operators: `=` (equal), `>=`, `<=`, `>`, `<`
+ +Format: ` = `
+ +Examples: +``` +[SysNand] +0100000000001000 ; No condition: always blocked in SysNAND +010000000000XXXX = 16.0.0 ; Blocked unless firmware == 16.0.0 +010000000000YYYY = >=17.0.0 ; Blocked unless firmware >= 17.0.0 +``` + ## Example For instance this config will block themes on ``SysNand``.
@@ -41,5 +58,13 @@ For instance this config will block themes on ``SysNand``.
[EmuNand] ``` +With firmware version conditions — only block a theme if firmware is not 16.0.0:
+``` +[SysNand] +0100000000001000 = 16.0.0 + +[EmuNand] +``` + ## Thanks to - Arcdelta for motivating me to actually finish this lmao. diff --git a/sysmodule/source/fs.cpp b/sysmodule/source/fs.cpp index 032d3d9..e90c0b6 100644 --- a/sysmodule/source/fs.cpp +++ b/sysmodule/source/fs.cpp @@ -8,6 +8,7 @@ #include #include #include "result.hpp" +#include "fs.hpp" namespace fs { @@ -36,7 +37,22 @@ namespace fs { return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0; } - Result EditContent(std::vector &matchList, std::string &env, std::string &del) { + bool CheckVersion(u32 currentVer, const ProgramEntry &entry) { + if (!entry.hasVersionCheck) { + return true; + } + + switch (entry.op) { + case OP_EQ: return currentVer == entry.version; + case OP_GE: return currentVer >= entry.version; + case OP_LE: return currentVer <= entry.version; + case OP_GT: return currentVer > entry.version; + case OP_LT: return currentVer < entry.version; + default: return false; + } + } + + Result EditContent(std::vector &matchList, std::string &env, std::string &del, u32 currentVer) { DIR *dir = opendir(CONTENTS); if (!dir) { return SYSENV_RC(SysEnvResult_OpenContentsFailed); @@ -60,11 +76,40 @@ namespace fs { modified.erase(modified.length() - del.length()); } - bool inList = std::find(matchList.begin(), matchList.end(), name) != matchList.end() || std::find(matchList.begin(), matchList.end(), modified) != matchList.end(); + // 查找匹配的 ProgramEntry + // 同时尝试去掉本侧环境后缀再匹配,以恢复已被阻止的目录 + std::string matchName = modified; + if (EndsWith(matchName, env)) { + matchName.erase(matchName.length() - env.length()); + } + + ProgramEntry *matched = nullptr; + for (auto &pe : matchList) { + if (name == pe.id || modified == pe.id || matchName == pe.id) { + matched = &pe; + break; + } + } + + if (matched != nullptr) { + bool shouldBlock; + if (matched->hasVersionCheck) { + // 有版本条件:固件不符合条件时才阻止 + shouldBlock = !CheckVersion(currentVer, *matched); + } else { + // 无版本条件:保持原有行为,始终阻止 + shouldBlock = true; + } - if (inList) { - if (!EndsWith(modified, env)) { - modified += env; + if (shouldBlock) { + if (!EndsWith(modified, env)) { + modified += env; + } + } else { + // 不应阻止:去掉环境后缀以恢复加载 + if (EndsWith(modified, env)) { + modified.erase(modified.length() - env.length()); + } } } @@ -96,7 +141,88 @@ namespace fs { return SYSENV_RC(SysEnvResult_HeaderMissing); } - void ReadConfigEntries(std::ifstream &file, std::vector &entries) { + // 解析一行中的版本条件部分,格式: " = >=16.0.0" 或 " = 16.0.0" + Result ParseVersionCondition(const std::string &condition, ProgramEntry &entry) { + std::string cond = condition; + + // 去掉前导空格 + size_t pos = 0; + while (pos < cond.size() && cond[pos] == ' ') { + pos++; + } + cond = cond.substr(pos); + + if (cond.empty()) { + return SYSENV_RC(SysEnvResult_InvalidVersionFormat); + } + + // 检测运算符 + u8 op; + size_t verStart; + if (cond.compare(0, 2, ">=") == 0) { + op = OP_GE; + verStart = 2; + } else if (cond.compare(0, 2, "<=") == 0) { + op = OP_LE; + verStart = 2; + } else if (cond[0] == '>') { + op = OP_GT; + verStart = 1; + } else if (cond[0] == '<') { + op = OP_LT; + verStart = 1; + } else if (cond[0] == '=') { + op = OP_EQ; + verStart = 1; + } else { + // 无运算符,默认精确匹配(兼容 "16.0.0" 直接写的情况) + op = OP_EQ; + verStart = 0; + } + + std::string verStr = cond.substr(verStart); + // 去掉版本号前的空格 + pos = 0; + while (pos < verStr.size() && verStr[pos] == ' ') { + pos++; + } + verStr = verStr.substr(pos); + + // 解析 MAJOR.MINOR.MICRO + u32 major = 0, minor = 0, micro = 0; + int dots = 0; + std::string part; + for (char c : verStr) { + if (c >= '0' && c <= '9') { + part += c; + } else if (c == '.') { + if (dots == 0) { + major = std::stoul(part); + } else if (dots == 1) { + minor = std::stoul(part); + } + part.clear(); + dots++; + } else { + return SYSENV_RC(SysEnvResult_InvalidVersionFormat); + } + } + if (!part.empty()) { + micro = std::stoul(part); + } + + if (dots > 2) { + return SYSENV_RC(SysEnvResult_InvalidVersionFormat); + } + + entry.hasVersionCheck = true; + entry.op = op; + entry.version = MAKEHOSVERSION(major, minor, micro); + + R_SUCCEED(); + } + + void ReadConfigEntries(std::ifstream &file, std::vector &entries) { std::string line; while (std::getline(file, line)) { if (!line.empty() && line.back() == '\r') { @@ -111,7 +237,34 @@ namespace fs { continue; } - entries.push_back(line); + ProgramEntry entry; + entry.hasVersionCheck = false; + entry.op = OP_EQ; + entry.version = 0; + + // 查找 '=' 分隔符,拆分程序 ID 和版本条件 + size_t eqPos = line.find('='); + if (eqPos != std::string::npos) { + entry.id = line.substr(0, eqPos); + // 去掉程序 ID 尾部空格 + while (!entry.id.empty() && entry.id.back() == ' ') { + entry.id.pop_back(); + } + + std::string cond = line.substr(eqPos + 1); + Result rc = ParseVersionCondition(cond, entry); + if (R_FAILED(rc)) { + // 解析失败:跳过此行,记录日志 + Log("Failed to parse version condition: %s", line.c_str()); + continue; + } + } else { + entry.id = line; + } + + if (!entry.id.empty()) { + entries.push_back(entry); + } } } @@ -141,7 +294,7 @@ namespace fs { return SYSENV_RC(SysEnvResult_EmptyConfig); } - Result ParseConfig(std::vector &entries, bool emuNand) { + Result ParseConfig(std::vector &entries, bool emuNand) { R_TRY(EnsureConfigExists()); std::ifstream file(PATH); diff --git a/sysmodule/source/fs.hpp b/sysmodule/source/fs.hpp index 5610fd6..dd9eb3b 100644 --- a/sysmodule/source/fs.hpp +++ b/sysmodule/source/fs.hpp @@ -4,8 +4,23 @@ namespace fs { + enum VersionOp { + OP_EQ = 0, // = + OP_GE, // >= + OP_LE, // <= + OP_GT, // > + OP_LT, // < + }; + + struct ProgramEntry { + std::string id; + bool hasVersionCheck; // 是否设置了固件版本条件 + u8 op; // VersionOp 枚举值 + u32 version; // MAKEHOSVERSION(major, minor, micro) + }; + void Log(const char *log, ...); - void EditContent(std::vector &matchList, std::string &env, std::string &del); - Result ParseConfig(std::vector &entries, bool emuNand); + Result EditContent(std::vector &matchList, std::string &env, std::string &del, u32 currentVer); + Result ParseConfig(std::vector &entries, bool emuNand); } diff --git a/sysmodule/source/main.cpp b/sysmodule/source/main.cpp index 84a7729..bba6165 100644 --- a/sysmodule/source/main.cpp +++ b/sysmodule/source/main.cpp @@ -59,8 +59,9 @@ bool IsEmuNand() { } int main() { - std::vector entries; + std::vector entries; bool isEmunand = IsEmuNand(); + u32 currentVer = hosversionGet(); Result rc = fs::ParseConfig(entries, isEmunand); if (R_FAILED(rc)) { @@ -77,7 +78,7 @@ int main() { del = ".emu.bak"; } - fs::EditContent(entries, env, del); + fs::EditContent(entries, env, del, currentVer); return 0; } diff --git a/sysmodule/source/result.hpp b/sysmodule/source/result.hpp index 0feac01..59501b1 100644 --- a/sysmodule/source/result.hpp +++ b/sysmodule/source/result.hpp @@ -12,6 +12,7 @@ enum SysEnvResult { SysEnvResult_CreateFileFailed, SysEnvResult_OpenContentsFailed, SysEnvResult_RenameFailed, + SysEnvResult_InvalidVersionFormat, }; #define SYSENV_RC(x) MAKERESULT(SysEnvModule, x) From 436d28082c19751736071e374f0b61d0dc97c58c Mon Sep 17 00:00:00 2001 From: do-kiss Date: Fri, 26 Jun 2026 15:44:40 +0800 Subject: [PATCH 2/6] fix: remove dead includes, fix stoul on empty version part, cleanup Makefile --- .DS_Store | Bin 0 -> 6148 bytes .gitignore | 4 +++- sysmodule/.DS_Store | Bin 0 -> 6148 bytes sysmodule/Makefile | 20 +++++++++++++++++--- sysmodule/config.ini.template | 31 +++++++++++++++++++++++++++++++ sysmodule/source/fs.cpp | 5 +++-- 6 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 .DS_Store create mode 100644 sysmodule/.DS_Store create mode 100644 sysmodule/config.ini.template diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..33fee36be5cf7f603625a480e95aea1ae597a61b GIT binary patch literal 6148 zcmeHKy-ve05I&a-sVbo^NQ@qsfxJPe!V~lbP#RQ_61h_8z!vcWeE@bA9)#EFhM3BC zKC8rP=!gK_N$1bGpZN0GvST7LlU0=wjfp6QG7g4ldW8L~dolXy1a+kAvldxmS=SJ8(fqw?1qE?-3%PzIEN zKg$4bvXFEnsI4-f3@8IT26%t4P{ur9E9kclH2w+zOknnczW=uaS~dXlfUO`b5F@ET zNj2e$VI&=P8{+bSt)Qfn@gw6sVPq3-D8`QtyY=B@azSmC0c9X&;7A_FeEwg5fB%n@ z^hp^|2L2TTCMl-H1fLYn*1eD8v(`dype!8M3hq+S@kcRY`6xbxdV$??2bc$J1z~~c OkASB^8)e`}8TbS$+gWM= literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index 96a91c3..25b9674 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ build *.nsp *.nso -*.npdm \ No newline at end of file +*.npdm +*.zip +sdout/ \ No newline at end of file diff --git a/sysmodule/.DS_Store b/sysmodule/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..df525c757c276bfbd8930fb3d8f5faf647ae0ed6 GIT binary patch literal 6148 zcmeHKOHRWu5FMAc3POl3S^5Z6VhajYI6*l;l?DM)B3G2|a|t*AJ1zne;sOY^9Dr4~ zys@p+?nm7rgl44ib0#zP%ge_x5vjqf=o9sbD1kG!cG0XcKF_{lEgv}mDmz9|Ovhzz zOu3RPfxoE$-@6XwG@~0TDY}0_xV-8Q$EKRtYdHDBgA*22Cd=~DuNR=leY_3Ta(Dd@g7;!&YWNYkfvUtIKZ*bwP_s z-O`vYLt@d1h#DO~E*FO6p6=hCkDO-F7$Ln)IT1k`F+nj=Kok%Kwkm)%n=RRMD6J?U z3Wx%Y0{nf@aK_YQ?a+QY(AX^iu#K=a^zB~~P;&s7daNBH0#mLO=t`B}VklRRxb}Ie z$J(JQCuL{GIDTg3cPPrvj<~kzq*8~{iUOiQtiX;t?D6`4zIp#27s-(*APW2|1ynm5 zW&>Q3Ut3o$j@McZAHmr;uXfm!pktR}[ = <条件><主,次,微>] +; +; 无版本条件: 始终在当前环境阻止 +; 有版本条件: 仅当固件版本不满足条件时才阻止 +; +; ========== 版本条件运算符 ========== +; = 16.0.0 精确匹配 (等于) +; >= 16.0.0 大于等于 +; <= 16.0.0 小于等于 +; > 16.0.0 大于 +; < 16.0.0 小于 +; +; ========== 示例 ========== + +[SysNand] +;0100000000001000 ; 始终在 SysNAND 阻止主题 +;010000000000XXXX = 16.0.0 ; 仅固件不是 16.0.0 时阻止 +;010000000000YYYY = >=17.0.0 ; 仅固件低于 17.0.0 时阻止 + +[EmuNand] +;0100000000001000 ; 始终在 EmuNAND 阻止主题 +;010000000000XXXX = 16.0.0 ; 仅固件不是 16.0.0 时阻止 diff --git a/sysmodule/source/fs.cpp b/sysmodule/source/fs.cpp index e90c0b6..462311b 100644 --- a/sysmodule/source/fs.cpp +++ b/sysmodule/source/fs.cpp @@ -1,10 +1,8 @@ #include #include #include -#include #include #include -#include #include #include #include "result.hpp" @@ -196,6 +194,9 @@ namespace fs { if (c >= '0' && c <= '9') { part += c; } else if (c == '.') { + if (part.empty()) { + return SYSENV_RC(SysEnvResult_InvalidVersionFormat); + } if (dots == 0) { major = std::stoul(part); } else if (dots == 1) { From 7f6c33e2bb77da941928666423d1de36f17027e2 Mon Sep 17 00:00:00 2001 From: do-kiss Date: Fri, 26 Jun 2026 15:44:51 +0800 Subject: [PATCH 3/6] chore: gitignore .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes sysmodule/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store delete mode 100644 sysmodule/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 33fee36be5cf7f603625a480e95aea1ae597a61b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKy-ve05I&a-sVbo^NQ@qsfxJPe!V~lbP#RQ_61h_8z!vcWeE@bA9)#EFhM3BC zKC8rP=!gK_N$1bGpZN0GvST7LlU0=wjfp6QG7g4ldW8L~dolXy1a+kAvldxmS=SJ8(fqw?1qE?-3%PzIEN zKg$4bvXFEnsI4-f3@8IT26%t4P{ur9E9kclH2w+zOknnczW=uaS~dXlfUO`b5F@ET zNj2e$VI&=P8{+bSt)Qfn@gw6sVPq3-D8`QtyY=B@azSmC0c9X&;7A_FeEwg5fB%n@ z^hp^|2L2TTCMl-H1fLYn*1eD8v(`dype!8M3hq+S@kcRY`6xbxdV$??2bc$J1z~~c OkASB^8)e`}8TbS$+gWM= diff --git a/sysmodule/.DS_Store b/sysmodule/.DS_Store deleted file mode 100644 index df525c757c276bfbd8930fb3d8f5faf647ae0ed6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOHRWu5FMAc3POl3S^5Z6VhajYI6*l;l?DM)B3G2|a|t*AJ1zne;sOY^9Dr4~ zys@p+?nm7rgl44ib0#zP%ge_x5vjqf=o9sbD1kG!cG0XcKF_{lEgv}mDmz9|Ovhzz zOu3RPfxoE$-@6XwG@~0TDY}0_xV-8Q$EKRtYdHDBgA*22Cd=~DuNR=leY_3Ta(Dd@g7;!&YWNYkfvUtIKZ*bwP_s z-O`vYLt@d1h#DO~E*FO6p6=hCkDO-F7$Ln)IT1k`F+nj=Kok%Kwkm)%n=RRMD6J?U z3Wx%Y0{nf@aK_YQ?a+QY(AX^iu#K=a^zB~~P;&s7daNBH0#mLO=t`B}VklRRxb}Ie z$J(JQCuL{GIDTg3cPPrvj<~kzq*8~{iUOiQtiX;t?D6`4zIp#27s-(*APW2|1ynm5 zW&>Q3Ut3o$j@McZAHmr;uXfm!pktR} Date: Fri, 26 Jun 2026 19:24:04 +0800 Subject: [PATCH 4/6] fix: trim trailing spaces in version condition to prevent parse failure --- .gitignore | 3 ++- sysmodule/source/fs.cpp | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 25b9674..3156dc4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ build *.nso *.npdm *.zip -sdout/ \ No newline at end of file +sdout/ +.DS_Store \ No newline at end of file diff --git a/sysmodule/source/fs.cpp b/sysmodule/source/fs.cpp index 462311b..6deb6c3 100644 --- a/sysmodule/source/fs.cpp +++ b/sysmodule/source/fs.cpp @@ -143,12 +143,15 @@ namespace fs { Result ParseVersionCondition(const std::string &condition, ProgramEntry &entry) { std::string cond = condition; - // 去掉前导空格 + // 去掉前导和尾部空格 size_t pos = 0; while (pos < cond.size() && cond[pos] == ' ') { pos++; } cond = cond.substr(pos); + while (!cond.empty() && cond.back() == ' ') { + cond.pop_back(); + } if (cond.empty()) { return SYSENV_RC(SysEnvResult_InvalidVersionFormat); From b80b36f4c68f5e212c1e2424437062d4189259da Mon Sep 17 00:00:00 2001 From: do-kiss Date: Fri, 26 Jun 2026 19:38:06 +0800 Subject: [PATCH 5/6] chore: translate all Chinese comments and config template to English --- sysmodule/config.ini.template | 40 +++++++++++++++++------------------ sysmodule/source/fs.cpp | 28 ++++++++++++------------ sysmodule/source/fs.hpp | 4 ++-- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/sysmodule/config.ini.template b/sysmodule/config.ini.template index d2eb8ff..4edaf1d 100644 --- a/sysmodule/config.ini.template +++ b/sysmodule/config.ini.template @@ -1,31 +1,31 @@ ; -; sys-env 配置文件 -; 路径: sdmc:/config/sys-env/config.ini +; sys-env configuration +; Path: sdmc:/config/sys-env/config.ini ; -; ========== 格式说明 ========== +; ========== Format ========== ; -; [SysNand] 下配置在真实系统中阻止的程序 -; [EmuNand] 下配置在虚拟系统中阻止的程序 +; [SysNand] programs to block in SysNAND +; [EmuNand] programs to block in EmuNAND ; -; 每个条目一行: <16位程序ID>[ = <条件><主,次,微>] +; Each entry: <16-char program ID>[ = ] ; -; 无版本条件: 始终在当前环境阻止 -; 有版本条件: 仅当固件版本不满足条件时才阻止 +; Without version condition: always blocked in this environment +; With version condition: blocked only when firmware does NOT match the condition ; -; ========== 版本条件运算符 ========== -; = 16.0.0 精确匹配 (等于) -; >= 16.0.0 大于等于 -; <= 16.0.0 小于等于 -; > 16.0.0 大于 -; < 16.0.0 小于 +; ========== Version Operators ========== +; = 16.0.0 exact match (equal) +; >= 16.0.0 greater than or equal +; <= 16.0.0 less than or equal +; > 16.0.0 greater than +; < 16.0.0 less than ; -; ========== 示例 ========== +; ========== Examples ========== [SysNand] -;0100000000001000 ; 始终在 SysNAND 阻止主题 -;010000000000XXXX = 16.0.0 ; 仅固件不是 16.0.0 时阻止 -;010000000000YYYY = >=17.0.0 ; 仅固件低于 17.0.0 时阻止 +;0100000000001000 ; Always block themes in SysNAND +;010000000000XXXX = 16.0.0 ; Block unless firmware is 16.0.0 +;010000000000YYYY = >=17.0.0 ; Block unless firmware >= 17.0.0 [EmuNand] -;0100000000001000 ; 始终在 EmuNAND 阻止主题 -;010000000000XXXX = 16.0.0 ; 仅固件不是 16.0.0 时阻止 +;0100000000001000 ; Always block themes in EmuNAND +;010000000000XXXX = 16.0.0 ; Block unless firmware is 16.0.0 diff --git a/sysmodule/source/fs.cpp b/sysmodule/source/fs.cpp index 6deb6c3..c2e3255 100644 --- a/sysmodule/source/fs.cpp +++ b/sysmodule/source/fs.cpp @@ -74,8 +74,8 @@ namespace fs { modified.erase(modified.length() - del.length()); } - // 查找匹配的 ProgramEntry - // 同时尝试去掉本侧环境后缀再匹配,以恢复已被阻止的目录 + // Find matching ProgramEntry + // Also try stripping the current env suffix to match previously blocked directories std::string matchName = modified; if (EndsWith(matchName, env)) { matchName.erase(matchName.length() - env.length()); @@ -92,10 +92,10 @@ namespace fs { if (matched != nullptr) { bool shouldBlock; if (matched->hasVersionCheck) { - // 有版本条件:固件不符合条件时才阻止 + // Has version condition: block only when firmware doesn't match shouldBlock = !CheckVersion(currentVer, *matched); } else { - // 无版本条件:保持原有行为,始终阻止 + // No version condition: always block (original behavior) shouldBlock = true; } @@ -104,7 +104,7 @@ namespace fs { modified += env; } } else { - // 不应阻止:去掉环境后缀以恢复加载 + // Should not block: remove env suffix to restore loading if (EndsWith(modified, env)) { modified.erase(modified.length() - env.length()); } @@ -139,11 +139,11 @@ namespace fs { return SYSENV_RC(SysEnvResult_HeaderMissing); } - // 解析一行中的版本条件部分,格式: " = >=16.0.0" 或 " = 16.0.0" + // Parse the version condition part of a config line, e.g. "= >=16.0.0" or "= 16.0.0" Result ParseVersionCondition(const std::string &condition, ProgramEntry &entry) { std::string cond = condition; - // 去掉前导和尾部空格 + // Strip leading and trailing spaces size_t pos = 0; while (pos < cond.size() && cond[pos] == ' ') { pos++; @@ -157,7 +157,7 @@ namespace fs { return SYSENV_RC(SysEnvResult_InvalidVersionFormat); } - // 检测运算符 + // Detect operator u8 op; size_t verStart; if (cond.compare(0, 2, ">=") == 0) { @@ -176,20 +176,20 @@ namespace fs { op = OP_EQ; verStart = 1; } else { - // 无运算符,默认精确匹配(兼容 "16.0.0" 直接写的情况) + // No operator: default to exact match (supports bare "16.0.0") op = OP_EQ; verStart = 0; } std::string verStr = cond.substr(verStart); - // 去掉版本号前的空格 + // Strip spaces before version number pos = 0; while (pos < verStr.size() && verStr[pos] == ' ') { pos++; } verStr = verStr.substr(pos); - // 解析 MAJOR.MINOR.MICRO + // Parse MAJOR.MINOR.MICRO u32 major = 0, minor = 0, micro = 0; int dots = 0; std::string part; @@ -246,11 +246,11 @@ namespace fs { entry.op = OP_EQ; entry.version = 0; - // 查找 '=' 分隔符,拆分程序 ID 和版本条件 + // Find '=' delimiter to split program ID and version condition size_t eqPos = line.find('='); if (eqPos != std::string::npos) { entry.id = line.substr(0, eqPos); - // 去掉程序 ID 尾部空格 + // Trim trailing spaces from program ID while (!entry.id.empty() && entry.id.back() == ' ') { entry.id.pop_back(); } @@ -258,7 +258,7 @@ namespace fs { std::string cond = line.substr(eqPos + 1); Result rc = ParseVersionCondition(cond, entry); if (R_FAILED(rc)) { - // 解析失败:跳过此行,记录日志 + // Parse failed: skip this line and log Log("Failed to parse version condition: %s", line.c_str()); continue; } diff --git a/sysmodule/source/fs.hpp b/sysmodule/source/fs.hpp index dd9eb3b..08ad8e8 100644 --- a/sysmodule/source/fs.hpp +++ b/sysmodule/source/fs.hpp @@ -14,8 +14,8 @@ namespace fs { struct ProgramEntry { std::string id; - bool hasVersionCheck; // 是否设置了固件版本条件 - u8 op; // VersionOp 枚举值 + bool hasVersionCheck; // true if firmware version condition is set + u8 op; // VersionOp value u32 version; // MAKEHOSVERSION(major, minor, micro) }; From 0f9e97758d7515a8b4e6d260878b113de6edeaa9 Mon Sep 17 00:00:00 2001 From: do-kiss Date: Sun, 28 Jun 2026 09:47:53 +0800 Subject: [PATCH 6/6] fix: single version part assigned to major, auto-unblock orphaned entries - Fix ParseVersionCondition: when only one version part is given (e.g. '= 16'), assign it to major instead of micro (16.0.0 vs 0.0.16) - Fix EditContent: directories with env suffix that are no longer in the config get their .bak stripped automatically, preventing permanently blocked entries --- sysmodule/source/fs.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sysmodule/source/fs.cpp b/sysmodule/source/fs.cpp index c2e3255..3daa750 100644 --- a/sysmodule/source/fs.cpp +++ b/sysmodule/source/fs.cpp @@ -109,6 +109,11 @@ namespace fs { modified.erase(modified.length() - env.length()); } } + } else { + // Not in matchList: clean up orphaned .bak from removed entries + if (EndsWith(modified, env)) { + modified.erase(modified.length() - env.length()); + } } std::string oldPath = std::string(CONTENTS) + name; @@ -212,7 +217,11 @@ namespace fs { } } if (!part.empty()) { - micro = std::stoul(part); + if (dots == 0) { + major = std::stoul(part); + } else { + micro = std::stoul(part); + } } if (dots > 2) {