diff --git a/Makefile b/Makefile index 8ea46f1..3536911 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,34 @@ MAKEFILES := sysmod overlay TARGETS := $(foreach dir,$(MAKEFILES),$(CURDIR)/$(dir)) +# the below was taken from atmosphere + switch-examples makefile +export VERSION := 1.3.0 +export GIT_BRANCH := $(shell git symbolic-ref --short HEAD) + +ifeq ($(strip $(shell git status --porcelain 2>/dev/null)),) +export GIT_REVISION := $(GIT_BRANCH)-$(shell git rev-parse --short HEAD) +export VERSION_DIRTY := $(VERSION) +export VERSION_WITH_HASH := $(VERSION)-$(shell git rev-parse --short HEAD) +else +export GIT_REVISION := $(GIT_BRANCH)-$(shell git rev-parse --short HEAD)-dirty +export VERSION_DIRTY := $(VERSION)-dirty +export VERSION_WITH_HASH := $(VERSION)-$(shell git rev-parse --short HEAD)-dirty +endif + +export BUILD_DATE := -DDATE_YEAR=\"$(shell date +%Y)\" \ + -DDATE_MONTH=\"$(shell date +%m)\" \ + -DDATE_DAY=\"$(shell date +%d)\" \ + -DDATE_HOUR=\"$(shell date +%H)\" \ + -DDATE_MIN=\"$(shell date +%M)\" \ + -DDATE_SEC=\"$(shell date +%S)\" \ + +export CUSTOM_DEFINES := -DVERSION=\"v$(VERSION)\" \ + -DGIT_BRANCH=\"$(GIT_BRANCH)\" \ + -DGIT_REVISION=\"$(GIT_REVISION)\" \ + -DVERSION_DIRTY=\"$(VERSION_DIRTY)\" \ + -DVERSION_WITH_HASH=\"$(VERSION_WITH_HASH)\" \ + $(BUILD_DATE) + all: $(TARGETS) @mkdir -p out/ @cp -R sysmod/out/* out/ diff --git a/README.md b/README.md index 8e9f30d..1b6eeef 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ the config file can be found in `/config/sys-patch/config.ini`, if the file does patch_sysmmc=1 ; 1=(default) patch sysmmc, 0=don't patch sysmmc patch_emummc=1 ; 1=(default) patch emummc, 0=don't patch emummc logging=1 ; 1=(default) output /config/sys-patch/log.inim 0=no log +version_skip=1 ; 1=(default) skips out of date patterns, 0=search all patterns ``` --- diff --git a/overlay/Makefile b/overlay/Makefile index 1f8f277..382a67d 100644 --- a/overlay/Makefile +++ b/overlay/Makefile @@ -39,7 +39,7 @@ include $(DEVKITPRO)/libnx/switch_rules #--------------------------------------------------------------------------------- APP_TITLE := sys-patch APP_AUTHOR := TotalJustice -APP_VERSION := 1.2.0 +APP_VERSION := $(VERSION_DIRTY) TARGET := sys-patch-overlay BUILD := build @@ -55,7 +55,7 @@ NO_ICON := 1 ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE CFLAGS := -g -Wall -O2 -ffunction-sections \ - $(ARCH) $(DEFINES) + $(ARCH) $(DEFINES) $(CUSTOM_DEFINES) CFLAGS += $(INCLUDE) -D__SWITCH__ diff --git a/overlay/src/main.cpp b/overlay/src/main.cpp index 23a4439..402742c 100644 --- a/overlay/src/main.cpp +++ b/overlay/src/main.cpp @@ -1,4 +1,5 @@ #define TESLA_INIT_IMPL // If you have more than one file using the tesla header, only define this in the main one +#define STBTT_STATIC #include // The Tesla Header #include #include "minIni/minIni.h" @@ -76,14 +77,16 @@ public: config_patch_sysmmc.load_value_from_ini(); config_patch_emummc.load_value_from_ini(); config_logging.load_value_from_ini(); + config_version_skip.load_value_from_ini(); - auto frame = new tsl::elm::OverlayFrame("sys-patch", "v1.2.0"); + 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_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")); if (does_file_exist(LOG_PATH)) { struct CallbackUser { @@ -93,7 +96,9 @@ public: ini_browse([](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData){ auto user = (CallbackUser*)UserData; - if (!std::strcmp("Skipped", Value)) { + std::string_view value{Value}; + + if (value == "Skipped") { return 1; } @@ -108,7 +113,6 @@ public: constexpr tsl::Color colour_unpatched{F(250), F(90), F(58), F(255)}; #undef F - std::string_view value{Value}; if (value.starts_with("Patched")) { if (value.ends_with("(sys-patch)")) { user->list->addItem(new tsl::elm::ListItem(Key, "Patched", colour_syspatch)); @@ -117,6 +121,10 @@ public: } } else if (value.starts_with("Unpatched")) { 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)); + } else { + user->list->addItem(new tsl::elm::ListItem(Key, Value, tsl::style::color::ColorText)); } return 1; @@ -132,6 +140,7 @@ public: ConfigEntry config_patch_sysmmc{"options", "patch_sysmmc", true}; ConfigEntry config_patch_emummc{"options", "patch_emummc", true}; ConfigEntry config_logging{"options", "patch_logging", true}; + ConfigEntry config_version_skip{"options", "version_skip", true}; }; // libtesla already initialized fs, hid, pl, pmdmnt, hid:sys and set:sys diff --git a/sysmod/Makefile b/sysmod/Makefile index bcd7eb4..41098a7 100644 --- a/sysmod/Makefile +++ b/sysmod/Makefile @@ -51,7 +51,7 @@ INCLUDES := include ../common ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE CFLAGS := -g -Wall -O2 -ffunction-sections \ - $(ARCH) $(DEFINES) + $(ARCH) $(DEFINES) $(CUSTOM_DEFINES) CFLAGS += $(INCLUDE) -D__SWITCH__ diff --git a/sysmod/src/main.cpp b/sysmod/src/main.cpp index 982bfab..f405f8d 100644 --- a/sysmod/src/main.cpp +++ b/sysmod/src/main.cpp @@ -1,20 +1,24 @@ #include #include -#include // for min -#include // for byteswap +#include // for std::min +#include // for std::byteswap #include // std::unreachable #include #include "minIni/minIni.h" namespace { -constexpr u64 INNER_HEAP_SIZE = 0x4000; // Size of the inner heap (adjust as necessary). -constexpr u64 READ_BUFFER_SIZE = 0x1000; // size of buffer which memory is read into +constexpr u64 INNER_HEAP_SIZE = 0x1000; // Size of the inner heap (adjust as necessary). +constexpr u64 READ_BUFFER_SIZE = 0x1000; // size of static buffer which memory is read into constexpr u32 FW_VER_ANY = 0x0; constexpr u16 REGEX_SKIP = 0x100; u32 FW_VERSION{}; // set on startup 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 VERSION_SKIP{}; // set on startup struct DebugEventInfo { u32 event_type; @@ -201,12 +205,12 @@ constexpr auto mov0_applied(u32 inst) -> bool { } constinit Patterns fs_patterns[] = { - { "noacidsigchk1", "0xC8FE4739", -24, 0, bl_cond, ret0_patch, ret0_applied }, - { "noacidsigchk2", "0x0210911F000072", -5, 0, bl_cond, ret0_patch, ret0_applied }, - { "noncasigchk_old", "0x1E42B9", -5, 0, tbz_cond, nop_patch, nop_applied }, - { "noncasigchk_new", "0x3E4479", -5, 0, tbz_cond, nop_patch, nop_applied }, - { "nocntchk_old", "0x081C00121F05007181000054", -4, 0, bl_cond, ret0_patch, ret0_applied }, - { "nocntchk_new", "0x081C00121F05007141010054", -4, 0, bl_cond, ret0_patch, ret0_applied }, + { "noacidsigchk1", "0xC8FE4739", -24, 0, bl_cond, ret0_patch, ret0_applied, FW_VER_ANY, MAKEHOSVERSION(9,2,0) }, + { "noacidsigchk2", "0x0210911F000072", -5, 0, bl_cond, ret0_patch, ret0_applied, FW_VER_ANY, MAKEHOSVERSION(9,2,0) }, + { "noncasigchk_old", "0x1E42B9", -5, 0, tbz_cond, nop_patch, nop_applied, MAKEHOSVERSION(10,0,0), MAKEHOSVERSION(14,2,1) }, + { "noncasigchk_new", "0x3E4479", -5, 0, tbz_cond, nop_patch, nop_applied, MAKEHOSVERSION(15,0,0) }, + { "nocntchk_old", "0x081C00121F05007181000054", -4, 0, bl_cond, ret0_patch, ret0_applied, MAKEHOSVERSION(10,0,0), MAKEHOSVERSION(14,2,1) }, + { "nocntchk_new", "0x081C00121F05007141010054", -4, 0, bl_cond, ret0_patch, ret0_applied, MAKEHOSVERSION(15,0,0) }, }; constinit Patterns ldr_patterns[] = { @@ -251,13 +255,14 @@ auto is_emummc() -> bool { return (paths.unk[0] != '\0') || (paths.nintendo[0] != '\0'); } -auto patcher(Handle handle, std::span data, u64 addr, std::span patterns) -> bool { +void patcher(Handle handle, std::span data, u64 addr, std::span patterns) { for (auto& p : patterns) { // skip if version isn't valid - if ((p.min_fw_ver && p.min_fw_ver > FW_VERSION) || + if (VERSION_SKIP && + ((p.min_fw_ver && p.min_fw_ver > FW_VERSION) || (p.max_fw_ver && p.max_fw_ver < FW_VERSION) || (p.min_ams_ver && p.min_ams_ver > AMS_VERSION) || - (p.max_ams_ver && p.max_ams_ver < AMS_VERSION)) { + (p.max_ams_ver && p.max_ams_ver < AMS_VERSION))) { p.result = PatchedResult::SKIPPED; continue; } @@ -267,10 +272,6 @@ auto patcher(Handle handle, std::span data, u64 addr, std::span= data.size()) { - continue; - } - for (u32 i = 0; i < data.size(); i++) { if (i + p.byte_pattern.size >= data.size()) { break; @@ -314,8 +315,6 @@ auto patcher(Handle handle, std::span data, u64 addr, std::span bool { @@ -327,8 +326,9 @@ auto apply_patch(PatchEntry& patch) -> bool { static u8 buffer[READ_BUFFER_SIZE]; // skip if version isn't valid - if ((patch.min_fw_ver && patch.min_fw_ver > FW_VERSION) || - (patch.max_fw_ver && patch.max_fw_ver < FW_VERSION)) { + if (VERSION_SKIP && + ((patch.min_fw_ver && patch.min_fw_ver > FW_VERSION) || + (patch.max_fw_ver && patch.max_fw_ver < FW_VERSION))) { for (auto& p : patch.patterns) { p.result = PatchedResult::SKIPPED; } @@ -422,6 +422,81 @@ auto patch_result_to_str(PatchedResult result) -> const char* { std::unreachable(); } +void num_2_str(char*& s, u16 num) { + u16 max_v = 1000; + if (num > 9) { + while (max_v >= 10) { + if (num >= max_v) { + while (max_v != 1) { + *s++ = '0' + (num / max_v); + num -= (num / max_v) * max_v; + max_v /= 10; + } + } else { + max_v /= 10; + } + } + } + *s++ = '0' + (num); // always add 0 or 1's +} + +void ms_2_str(char* s, u32 num) { + u32 max_v = 100; + *s++ = '0' + (num / 1000); // add seconds + num -= (num / 1000) * 1000; + *s++ = '.'; + + while (max_v >= 10) { + if (num >= max_v) { + while (max_v != 1) { + *s++ = '0' + (num / max_v); + num -= (num / max_v) * max_v; + max_v /= 10; + } + } + else { + *s++ = '0'; // append 0 + max_v /= 10; + } + } + *s++ = '0' + (num); // always add 0 or 1's + *s++ = 's'; // in seconds +} + +// eg, 852481 -> 13.2.1 +void version_to_str(char* s, u32 ver) { + for (int i = 0; i < 3; i++) { + num_2_str(s, (ver >> 16) & 0xFF); + if (i != 2) { + *s++ = '.'; + } + ver <<= 8; + } +} + +// eg, 0xAF66FF99 -> AF66FF99 +void hash_to_str(char* s, u32 hash) { + for (int i = 0; i < 4; i++) { + const auto num = (hash >> 24) & 0xFF; + const auto top = (num >> 4) & 0xF; + const auto bottom = (num >> 0) & 0xF; + + constexpr auto a = [](u8 nib) -> char { + if (nib >= 0 && nib <= 9) { return '0' + nib; } + return 'a' + nib - 10; + }; + + *s++ = a(top); + *s++ = a(bottom); + + hash <<= 8; + } +} + +void keygen_to_str(char* s, u8 keygen) { + num_2_str(s, keygen); +} + } // namespace int main(int argc, char* argv[]) { @@ -435,6 +510,7 @@ int main(int argc, char* argv[]) { 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); + VERSION_SKIP = ini_load_or_write_default("options", "version_skip", 1, ini_path); const auto emummc = is_emummc(); bool enable_patching = true; @@ -448,12 +524,18 @@ int main(int argc, char* argv[]) { enable_patching = false; } + // speedtest + const auto ticks_start = armGetSystemTick(); + if (enable_patching) { for (auto& patch : patches) { apply_patch(patch); } } + const auto ticks_end = armGetSystemTick(); + const auto diff_ns = armTicksToNs(ticks_end) - armTicksToNs(ticks_start); + if (enable_logging) { for (auto& patch : patches) { for (auto& p : patch.patterns) { @@ -463,6 +545,41 @@ int main(int argc, char* argv[]) { ini_puts(patch.name, p.patch_name, patch_result_to_str(p.result), log_path); } } + + // fw of the system + char fw_version[12]{}; + // atmosphere version + char ams_version[12]{}; + // lowest fw supported by atmosphere + char ams_target_version[12]{}; + // ??? + char ams_keygen[3]{}; + // git commit hash + char ams_hash[9]{}; + // how long it took to patch + char patch_time[20]{}; + + version_to_str(fw_version, FW_VERSION); + version_to_str(ams_version, AMS_VERSION); + version_to_str(ams_target_version, AMS_TARGET_VERSION); + keygen_to_str(ams_keygen, AMS_KEYGEN); + hash_to_str(ams_hash, AMS_HASH >> 32); + ms_2_str(patch_time, diff_ns/1000ULL/1000ULL); + + // 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); } // note: sysmod exits here. @@ -511,9 +628,16 @@ void __appInit(void) { // get ams version if (R_SUCCEEDED(rc = splInitialize())) { u64 v{}; + u64 hash{}; if (R_SUCCEEDED(rc = splGetConfig((SplConfigItem)65000, &v))) { - AMS_VERSION = (v >> 16) & 0xFFFFFF; + AMS_VERSION = (v >> 40) & 0xFFFFFF; + AMS_KEYGEN = (v >> 32) & 0xFF; + AMS_TARGET_VERSION = v & 0xFFFFFF; } + if (R_SUCCEEDED(rc = splGetConfig((SplConfigItem)65003, &hash))) { + AMS_HASH = hash; + } + splExit(); }