diff --git a/Makefile b/Makefile index 46276e5..40c7fc0 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ MAKEFILES := sysmod overlay TARGETS := $(foreach dir,$(MAKEFILES),$(CURDIR)/$(dir)) # the below was taken from atmosphere + switch-examples makefile -export VERSION := 1.5.5 +export VERSION := 1.5.6 ifneq ($(strip $(shell git symbolic-ref --short HEAD 2>/dev/null)),) export GIT_BRANCH := $(shell git symbolic-ref --short HEAD) diff --git a/README.md b/README.md index f1940d8..b76960f 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ patch_sysmmc=1 ; 1=(default) patch sysmmc, 0=don't patch sysmmc patch_emummc=1 ; 1=(default) patch emummc, 0=don't patch emummc enable_logging=1 ; 1=(default) output /config/sys-patch/log.ini 0=no log version_skip=1 ; 1=(default) skips out of date patterns, 0=search all patterns +clean_config=1 ; 1=(default) clean the config file 0=don't clean the config file ``` --- @@ -43,7 +44,7 @@ The overlay can be used to change the config options and to see what patches are - Install [devkitpro](https://devkitpro.org/wiki/Getting_Started) - Run the following: ```sh - git clone --recurse-submodules https://github.com/ITotalJustice/sys-patch.git + git clone --recurse-submodules https://github.com/impeeza/sys-patch.git cd ./sys-patch make ``` diff --git a/common/minIni/minGlue.c b/common/minIni/minGlue.c index 631ca1e..4ddd798 100644 --- a/common/minIni/minGlue.c +++ b/common/minIni/minGlue.c @@ -75,6 +75,35 @@ bool ini_read(char* buffer, u64 size, struct NxFile* nxfile) { return true; } +bool ini_read2(char* buffer, u64 size, struct NxFile* nxfile) { + u64 bytes_read = 0; + + if (R_FAILED(fsFileRead(&nxfile->file, nxfile->offset, buffer, size - 1, FsReadOption_None, &bytes_read))) { + return false; + } + + if (bytes_read == 0) { + return false; + } + + buffer[bytes_read] = '\0'; + + char* eol = strchr(buffer, '\n'); + if (!eol) { + eol = strchr(buffer, '\r'); + } + + if (eol) { + *eol = '\0'; + nxfile->offset += (eol - buffer + 1); + } else { + nxfile->offset += bytes_read; + return true; + } + + return true; +} + bool ini_write(const char* buffer, struct NxFile* nxfile) { const size_t size = strlen(buffer); if (R_FAILED(fsFileWrite(&nxfile->file, nxfile->offset, buffer, size, FsWriteOption_None))) { diff --git a/common/minIni/minGlue.h b/common/minIni/minGlue.h index 261f13d..1a40bec 100644 --- a/common/minIni/minGlue.h +++ b/common/minIni/minGlue.h @@ -22,6 +22,7 @@ bool ini_openwrite(const char* filename, struct NxFile* nxfile); bool ini_openrewrite(const char* filename, struct NxFile* nxfile); bool ini_close(struct NxFile* nxfile); bool ini_read(char* buffer, u64 size, struct NxFile* nxfile); +bool ini_read2(char* buffer, u64 size, struct NxFile* nxfile); bool ini_write(const char* buffer, struct NxFile* nxfile); bool ini_tell(struct NxFile* nxfile, s64* pos); bool ini_seek(struct NxFile* nxfile, s64* pos); diff --git a/common/minIni/minIni.c b/common/minIni/minIni.c index 90ee57b..96dd764 100644 --- a/common/minIni/minIni.c +++ b/common/minIni/minIni.c @@ -3,7 +3,7 @@ * These routines are in part based on the article "Multiplatform .INI Files" * by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal. * - * Copyright (c) CompuPhase, 2008-2021 + * Copyright (c) CompuPhase, 2008-2024 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -949,4 +949,18 @@ int ini_putf(const TCHAR *Section, const TCHAR *Key, INI_REAL Value, const TCHAR return ini_puts(Section, Key, LocalBuffer, Filename); } #endif /* INI_REAL */ + +/** ini_putbool() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write; it should be 0 or 1. + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putbool(const TCHAR *Section, const TCHAR *Key, int Value, const TCHAR *Filename) +{ + return ini_puts(Section, Key, Value ? __T("true") : __T("false"), Filename); +} + #endif /* !INI_READONLY */ diff --git a/common/minIni/minIni.h b/common/minIni/minIni.h index b63451c..9a68c87 100644 --- a/common/minIni/minIni.h +++ b/common/minIni/minIni.h @@ -1,6 +1,6 @@ /* minIni - Multi-Platform INI file parser, suitable for embedded systems * - * Copyright (c) CompuPhase, 2008-2021 + * Copyright (c) CompuPhase, 2008-2024 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy @@ -37,24 +37,25 @@ extern "C" { #endif -int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const mTCHAR *Filename); -long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const mTCHAR *Filename); -int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); -int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); -int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const mTCHAR *Filename); +long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const mTCHAR *Filename); +int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); -int ini_hassection(const mTCHAR *Section, const mTCHAR *Filename); -int ini_haskey(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Filename); +int ini_hassection(const mTCHAR *Section, const mTCHAR *Filename); +int ini_haskey(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Filename); #if defined INI_REAL INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const mTCHAR *Filename); #endif #if !defined INI_READONLY -int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const mTCHAR *Filename); -int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const mTCHAR *Filename); +int ini_putbool(const mTCHAR *Section, const mTCHAR *Key, int Value, const mTCHAR *Filename); +int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const mTCHAR *Filename); +int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const mTCHAR *Filename); #if defined INI_REAL -int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const mTCHAR *Filename); +int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const mTCHAR *Filename); #endif #endif /* INI_READONLY */ @@ -124,15 +125,15 @@ int ini_browse(INI_CALLBACK Callback, void *UserData, const mTCHAR *Filename); #endif #if ! defined INI_READONLY + bool put(const std::string& Section, const std::string& Key, bool Value) + { return ini_putbool(Section.c_str(), Key.c_str(), (int)Value, iniFilename.c_str()) != 0; } + bool put(const std::string& Section, const std::string& Key, long Value) { return ini_putl(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()) != 0; } bool put(const std::string& Section, const std::string& Key, int Value) { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } - bool put(const std::string& Section, const std::string& Key, bool Value) - { return ini_putl(Section.c_str(), Key.c_str(), (long)Value, iniFilename.c_str()) != 0; } - bool put(const std::string& Section, const std::string& Key, const std::string& Value) { return ini_puts(Section.c_str(), Key.c_str(), Value.c_str(), iniFilename.c_str()) != 0; } diff --git a/overlay/src/main.cpp b/overlay/src/main.cpp index 2afb6f3..cc5d32d 100644 --- a/overlay/src/main.cpp +++ b/overlay/src/main.cpp @@ -74,11 +74,12 @@ class GuiOptions final : public tsl::Gui { auto frame = new tsl::elm::OverlayFrame("sys-patch", VERSION_WITH_HASH); auto list = new tsl::elm::List(); - list->addItem(new tsl::elm::CategoryHeader("Options")); - list->addItem(config_patch_sysmmc.create_list_item("Patch sysMMC")); - list->addItem(config_patch_emummc.create_list_item("Patch emuMMC")); - list->addItem(config_logging.create_list_item("Logging")); - list->addItem(config_version_skip.create_list_item("Version skip")); + list->addItem(new tsl::elm::CategoryHeader("Optionen")); + list->addItem(config_patch_sysmmc.create_list_item("sysMMC patchen")); + list->addItem(config_patch_emummc.create_list_item("emuMMC patchen")); + list->addItem(config_logging.create_list_item("Protokollieren")); + list->addItem(config_version_skip.create_list_item("Version ueberspringen")); + list->addItem(config_clean_config.create_list_item("Konfiguration reinigen")); frame->setContent(list); return frame; @@ -88,6 +89,7 @@ class GuiOptions final : public tsl::Gui { ConfigEntry config_patch_emummc{"options", "patch_emummc", true}; ConfigEntry config_logging{"options", "enable_logging", true}; ConfigEntry config_version_skip{"options", "version_skip", true}; + ConfigEntry config_clean_config{"options", "clean_config", true}; }; class GuiToggle final : public tsl::Gui { @@ -120,7 +122,7 @@ class GuiToggle final : public tsl::Gui { list->addItem(new tsl::elm::CategoryHeader("NIM - 0100000000000025")); list->addItem(config_nim.create_list_item("nim")); - list->addItem(new tsl::elm::CategoryHeader("Disable CA Verification - apply all")); + list->addItem(new tsl::elm::CategoryHeader("Deaktiviere CA Verifikation - Bei allen")); list->addItem(config_ssl1.create_list_item("disablecaverification1")); list->addItem(config_ssl2.create_list_item("disablecaverification2")); list->addItem(config_ssl3.create_list_item("disablecaverification3")); @@ -164,13 +166,13 @@ class GuiLog final : public tsl::Gui { auto user = (CallbackUser*)UserData; std::string_view value{Value}; - if (value == "Skipped") { + if (value == "Uebersprungen") { return 1; } if (user->last_section != Section) { user->last_section = Section; - user->list->addItem(new tsl::elm::CategoryHeader("Log: " + user->last_section)); + user->list->addItem(new tsl::elm::CategoryHeader("Protokoll: " + user->last_section)); } #define F(x) ((x) >> 4) // 8bit -> 4bit @@ -179,13 +181,13 @@ class GuiLog final : public tsl::Gui { constexpr tsl::Color colour_unpatched{F(250), F(90), F(58), F(255)}; #undef F - if (value.starts_with("Patched")) { + if (value.starts_with("Gepatched")) { if (value.ends_with("(sys-patch)")) { - user->list->addItem(new tsl::elm::ListItem(Key, "Patched", colour_syspatch)); + user->list->addItem(new tsl::elm::ListItem(Key, "Gepatched", colour_syspatch)); } else { - user->list->addItem(new tsl::elm::ListItem(Key, "Patched", colour_file)); + user->list->addItem(new tsl::elm::ListItem(Key, "Gepatched", colour_file)); } - } else if (value.starts_with("Unpatched") || value.starts_with("Disabled")) { + } else if (value.starts_with("Ungepatched") || value.starts_with("Deaktiviert")) { user->list->addItem(new tsl::elm::ListItem(Key, Value, colour_unpatched)); } else if (user->last_section == "stats") { user->list->addItem(new tsl::elm::ListItem(Key, Value, tsl::style::color::ColorDescription)); @@ -196,7 +198,7 @@ class GuiLog final : public tsl::Gui { return 1; }, &callback_userdata, LOG_PATH); } else { - list->addItem(new tsl::elm::ListItem("No log found!")); + list->addItem(new tsl::elm::ListItem("Kein Protokoll gefunden!")); } frame->setContent(list); @@ -212,9 +214,9 @@ class GuiMain final : public tsl::Gui { auto frame = new tsl::elm::OverlayFrame("sys-patch", VERSION_WITH_HASH); auto list = new tsl::elm::List(); - auto options = new tsl::elm::ListItem("Options"); - auto toggle = new tsl::elm::ListItem("Toggle patches"); - auto log = new tsl::elm::ListItem("Log"); + auto options = new tsl::elm::ListItem("Optionen"); + auto toggle = new tsl::elm::ListItem("Patches umschalten"); + auto log = new tsl::elm::ListItem("Protokoll"); options->setClickListener([](u64 keys) -> bool { if (keys & HidNpadButton_A) { @@ -240,7 +242,7 @@ class GuiMain final : public tsl::Gui { return false; }); - list->addItem(new tsl::elm::CategoryHeader("Menu")); + list->addItem(new tsl::elm::CategoryHeader("Menue")); list->addItem(options); list->addItem(toggle); list->addItem(log); diff --git a/sysmod/src/main.cpp b/sysmod/src/main.cpp index 005b2fd..8c62fc5 100644 --- a/sysmod/src/main.cpp +++ b/sysmod/src/main.cpp @@ -18,7 +18,15 @@ u32 AMS_VERSION{}; // set on startup u32 AMS_TARGET_VERSION{}; // set on startup u8 AMS_KEYGEN{}; // set on startup u64 AMS_HASH{}; // set on startup +bool patch_sysmmc; // set on startup +bool patch_emummc; // set on startup +bool enable_logging; // set on startup bool VERSION_SKIP{}; // set on startup +bool CLEAN_CONFIG{}; // set on startup + +constexpr auto ini_path = "/config/sys-patch/config.ini"; +constexpr auto log_path = "/config/sys-patch/log.ini"; +constexpr auto temp_path = "/config/sys-patch/temp.ini"; struct DebugEventInfo { u32 event_type; @@ -511,12 +519,12 @@ auto ini_load_or_write_default(const char* section, const char* key, long _defau auto patch_result_to_str(PatchResult result) -> const char* { switch (result) { - case PatchResult::NOT_FOUND: return "Unpatched"; - case PatchResult::SKIPPED: return "Skipped"; - case PatchResult::DISABLED: return "Disabled"; - case PatchResult::PATCHED_FILE: return "Patched (file)"; - case PatchResult::PATCHED_SYSPATCH: return "Patched (sys-patch)"; - case PatchResult::FAILED_WRITE: return "Failed (svcWriteDebugProcessMemory)"; + case PatchResult::NOT_FOUND: return "Ungepatched"; + case PatchResult::SKIPPED: return "Uebersprungen"; + case PatchResult::DISABLED: return "Deaktiviert"; + case PatchResult::PATCHED_FILE: return "Gepatched (Datei)"; + case PatchResult::PATCHED_SYSPATCH: return "Gepatched (sys-patch)"; + case PatchResult::FAILED_WRITE: return "Fehlgeschlagen (svcWriteDebugProcessMemory)"; } std::unreachable(); @@ -597,21 +605,208 @@ void keygen_to_str(char* s, u8 keygen) { num_2_str(s, keygen); } +char* strdup(const char* str) { + size_t len = strlen(str) + 1; + char* copy = (char*)malloc(len); + if (copy != NULL) { +strncpy(copy, str, len); + } + return copy; +} + +void trim(char* str) { + if (str == NULL) + return; + + char* start = str; + while (*start && isspace((unsigned char)*start)) + ++start; + + size_t len = strlen(start); + char* end = start + len - 1; + while (end > start && isspace((unsigned char)*end)) + --end; + + *(end + 1) = '\0'; + + if (start != str) + memmove(str, start, len - (start - str) + 1); +} + +int clean_config_file() { + ini_remove(temp_path); + NxFile file; + bool rc=ini_openread(ini_path, &file); + char line[128]; + char *line_trim = {}; + char *actual_section = {}; +if (!rc) { + return 1; + } + bool need_rewrite = false; + bool keep_section = false; + bool keep_config = false; + size_t buffer_length=sizeof(line); + size_t line_trim_alloc = buffer_length + 1; + bool first_line_init = false; + int count_buff_line_passed = 0; + int z = 0; + while (ini_read2(line, buffer_length, &file)) { + if (!first_line_init) { + line_trim = (char*) calloc(1, line_trim_alloc); + if (!line_trim) { + ini_close(&file); + return -1; + } + first_line_init = true; + } + strcat(line_trim, line); + if ((line_trim[strlen(line_trim) - 1] != '\r' && line_trim[strlen(line_trim) - 1] != '\n') && strlen(line_trim) > 0) { + z++; + if (z > count_buff_line_passed) { + line_trim_alloc += buffer_length; + line_trim = (char*) realloc(line_trim, line_trim_alloc); + if (!line_trim) { + if (actual_section) free(actual_section); + if (line_trim) free(line_trim); + ini_close(&file); + return -1; + } + count_buff_line_passed++; + } + continue; + } + z = 0; + trim(line_trim); + if (line_trim[0] == '\0' || line_trim[0] == ';' || line_trim[0] == '\n' || line_trim[0] == '\r') { + memset(line_trim, '\0', line_trim_alloc); + continue; + } + if (line_trim[0] == '[' && line_trim[strlen(line_trim) - 1] == ']') { + keep_section = false; + line_trim[strlen(line_trim) - 1] = '\0'; + if (actual_section) { + free(actual_section); + } + actual_section = strdup(line_trim + 1); + if (strcmp(actual_section, "options") == 0) { + keep_section = true; + memset(line_trim, '\0', line_trim_alloc); + continue; + } + for (auto& patch : patches) { + if (strcmp(patch.name, actual_section) == 0) { + keep_section = true; + break; + } + } + if (!keep_section) { + need_rewrite = true; + break; + } + } else { + keep_config = false; + if (!keep_section) { + need_rewrite = true; + break; + } + char *pos = strchr(line_trim, '='); + if (pos != NULL) { + *pos = '\0'; + trim(line_trim); + if ((strcmp(actual_section, "options") == 0) && (strcmp(line_trim, "patch_sysmmc") == 0 || strcmp(line_trim, "patch_emummc") == 0 || strcmp(line_trim, "enable_logging") == 0 || strcmp(line_trim, "version_skip") == 0 || strcmp(line_trim, "clean_config") == 0)) { + memset(line_trim, '\0', line_trim_alloc); + continue; + } + for (auto& patch : patches) { + for (auto& p : patch.patterns) { + if (strcmp(p.patch_name, line_trim) == 0) { + keep_config = true; + break; + } + } + if (keep_config) { + break; + } + } + if (!keep_config) { + need_rewrite = true; + break; + } + } + } + memset(line_trim, '\0', line_trim_alloc); + } + ini_close(&file); + if (line_trim) { + free(line_trim); + } + if (actual_section) { + free(actual_section); + } + + if (!need_rewrite) { + return 0; + } + + bool user_val = ini_getbool("options", "patch_sysmmc", 1, ini_path); + if (ini_putl("options", "patch_sysmmc", user_val, temp_path) == 0) { + return -1; + } + user_val = ini_getbool("options", "patch_emummc", 1, ini_path); + if (ini_putl("options", "patch_emummc", user_val, temp_path) == 0) { + return -1; + } + user_val = ini_getbool("options", "enable_logging", 1, ini_path); + if (ini_putl("options", "enable_logging", user_val, temp_path) == 0) { + return -1; + } + user_val = ini_getbool("options", "version_skip", 1, ini_path); + if (ini_putl("options", "version_skip", user_val, temp_path) == 0) { + return -1; + } + user_val = ini_getbool("options", "clean_config", 1, ini_path); + if (ini_putl("options", "clean_config", user_val, temp_path) == 0) { + return -1; + } + + for (auto& patch : patches) { + for (auto& p : patch.patterns) { + user_val = ini_getbool(patch.name, p.patch_name, p.enabled, ini_path); + if (ini_putl(patch.name, p.patch_name, user_val, temp_path) == 0) { + return -1; + } + } + } + ini_remove(ini_path); +ini_rename(temp_path, ini_path); + return 1; +} + } // namespace int main(int argc, char* argv[]) { - constexpr auto ini_path = "/config/sys-patch/config.ini"; - constexpr auto log_path = "/config/sys-patch/log.ini"; - create_dir("/config/"); create_dir("/config/sys-patch/"); ini_remove(log_path); // load options - const auto patch_sysmmc = ini_load_or_write_default("options", "patch_sysmmc", 1, ini_path); - const auto patch_emummc = ini_load_or_write_default("options", "patch_emummc", 1, ini_path); - const auto enable_logging = ini_load_or_write_default("options", "enable_logging", 1, ini_path); + patch_sysmmc = ini_load_or_write_default("options", "patch_sysmmc", 1, ini_path); + patch_emummc = ini_load_or_write_default("options", "patch_emummc", 1, ini_path); + enable_logging = ini_load_or_write_default("options", "enable_logging", 1, ini_path); VERSION_SKIP = ini_load_or_write_default("options", "version_skip", 1, ini_path); + CLEAN_CONFIG = ini_load_or_write_default("options", "clean_config", 1, ini_path); + + if (CLEAN_CONFIG) { + int rc = clean_config_file(); + if (rc == 0) { + ini_puts("clean_config_file", "result", "Nicht noetig", log_path); + } else if (rc == 1) { + ini_puts("clean_config_file", "result", "Bereinigt", log_path); + } else { + ini_puts("clean_config_file", "result", "Fehler beim bereinigen", log_path); + } + } // load patch toggles for (auto& patch : patches) { @@ -681,17 +876,17 @@ int main(int argc, char* argv[]) { // defined in the Makefile #define DATE (DATE_DAY "." DATE_MONTH "." DATE_YEAR " " DATE_HOUR ":" DATE_MIN ":" DATE_SEC) - ini_puts("stats", "version", VERSION_WITH_HASH, log_path); - ini_puts("stats", "build_date", DATE, log_path); - ini_puts("stats", "fw_version", fw_version, log_path); - ini_puts("stats", "ams_version", ams_version, log_path); - ini_puts("stats", "ams_target_version", ams_target_version, log_path); - ini_puts("stats", "ams_keygen", ams_keygen, log_path); - ini_puts("stats", "ams_hash", ams_hash, log_path); - ini_putl("stats", "is_emummc", emummc, log_path); - ini_putl("stats", "heap_size", INNER_HEAP_SIZE, log_path); - ini_putl("stats", "buffer_size", READ_BUFFER_SIZE, log_path); - ini_puts("stats", "patch_time", patch_time, log_path); + ini_puts("stats", "Version", VERSION_WITH_HASH, log_path); + ini_puts("stats", "Erstellungsdatum", DATE, log_path); + ini_puts("stats", "Firmwareversion", fw_version, log_path); + ini_puts("stats", "SBOS Version", ams_version, log_path); + ini_puts("stats", "SBOS Zielversion", ams_target_version, log_path); + ini_puts("stats", "SBOS Keygen", ams_keygen, log_path); + ini_puts("stats", "SBOS Hash", ams_hash, log_path); + ini_putl("stats", "ist emuMMC", emummc, log_path); + ini_putl("stats", "Heapgroesse", INNER_HEAP_SIZE, log_path); + ini_putl("stats", "Puffergroesse", READ_BUFFER_SIZE, log_path); + ini_puts("stats", "Patchzeit", patch_time, log_path); } // note: sysmod exits here.