commit 586c36d6f215c85dcba68bf551012cc5a30b0a7b Author: KazushiM <85604869+KazushiMe@users.noreply.github.com> Date: Mon Jul 5 17:02:51 2021 +0800 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5509140f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.DS_Store diff --git a/PatchTools/bspatch.exe b/PatchTools/bspatch.exe new file mode 100644 index 00000000..2be36124 Binary files /dev/null and b/PatchTools/bspatch.exe differ diff --git a/PatchTools/elf2nso.exe b/PatchTools/elf2nso.exe new file mode 100644 index 00000000..ddb7c893 Binary files /dev/null and b/PatchTools/elf2nso.exe differ diff --git a/PatchTools/hacpack.exe b/PatchTools/hacpack.exe new file mode 100644 index 00000000..92eee417 Binary files /dev/null and b/PatchTools/hacpack.exe differ diff --git a/PatchTools/hactool.exe b/PatchTools/hactool.exe new file mode 100644 index 00000000..fa78409b Binary files /dev/null and b/PatchTools/hactool.exe differ diff --git a/PatchTools/nx2elf.exe b/PatchTools/nx2elf.exe new file mode 100644 index 00000000..3b0a8e4e Binary files /dev/null and b/PatchTools/nx2elf.exe differ diff --git a/README.md b/README.md new file mode 100644 index 00000000..74157ab9 --- /dev/null +++ b/README.md @@ -0,0 +1,241 @@ +# Switch OC Suite + +Overclocking suite for Switch(Erista and Mariko) running on Atmosphere CFW. + + + +## Notice + +### Reserved and intended for personal use ONLY. It will NOT be actively maintained. + +### Failure to read this README carefully or Doing CPU/GPU Overclocking on Erista will brick or fry your device (in the short term or long term, who knows). + +- Erista support will be dropped later. + +- nx-ovlloader seems to be unstable in Docked mode only with RAM OC applied. Grab `STACK_SIZE` patch [here](https://github.com/KazushiMe/nx-ovlloader/tree/patch-1) and build it yourself. + + + +## Features + +- CPU/GPU Overclock up to 2397.0/1344.0 MHz for Mariko and up to 2295.0/1075.2 MHz for Erista + +- Auto-Boost CPU when a game starts or is loading (Mariko Only) + +- Optimization for fan control at high load (Mariko Only) + +- RAM Overclock up to 2131.2 MHz for Erista and 1996.8 MHz for Mariko + +- Disable some background services, less power consumption in standby mode (Optional) + +- Sync sys-clk profiles with ReverseNX(-Tools and -RT), no need to change clocks after toggling modes + +- Profile-aware frequency override for all games + +- Game recording and SysDVR streaming @ 60fps with high video bitrate (Optional) + +- Remove copyright watermark in screenshots/recordings and bypass connection test, courtesy of [HookedBehemoth](https://github.com/HookedBehemoth/exefs_patches) + +### Details + +- Bump CPU/GPU frequencies up to 2397.0/1344.0 MHz for Mariko and 2295.0/1075.2 MHz for Erista, bypassing Horizon OS limit. + + - Some SoCs may not reach MAX clock, or be unstable at/near MAX clock. + - CPU/GPU Overclocking on Erista will consume more power than what the charger provides, and generates much more heat. **You have been WARNED AGAIN!** + - Mariko is still functioning w/o charger under MAX OC(Your Mileage May Vary), therefore limit posed by sys-clk is lifted only for Mariko, but don't overdo it on battery. + +- Auto-Boost CPU when a game starts or is in loading screen (Mariko Only, Optional). + + - 1963.5 MHz w/o charger and 2295.0 MHz with charger + + - Some games don't utilize SetCpuBoostMode, e.g. Overcooked 2, so Auto-Boost would be invalid in loading screens. + +- RAM Overclock for both Erista and Mariko consoles. For now, Erista can reach up to 2131.2 MHz with overvolting, and up to 1996.8 MHz for Mariko without overvolting. + + - RAM frequencies other than the only one you've chosen can NOT be used, but the impact of power consumption is negligible. So the ability to set RAM frequencies is removed in favor of ptm RAM patches, which could set RAM at specific clock permanently. + + - For Mariko: + + - Recommended frequency for Hynix RAM is 1731.2/1862.4 MHz(fk Hynix), but for Samsung and Micron ones you may use higher frequencies like 1996.8 MHz. + - Use Hekate to check out the brand of your RAM chips. + - Choose RAM clock with care, or your eMMC filesystem will be **corrupted**. + + - For Erista: + + - If you boot via Hekate, Minerva module(`/bootloader/sys/libsys_minerva.bso`) should be removed or recompiled with [changes](https://github.com/CTCaer/hekate/blob/master/modules/hekate_libsys_minerva/sys_sdrammtc.c#L31) applied. + - RAM voltage is set at 1125mV @ 1731.2 MHz, 1150 mV @ 1862.4 MHz, 1175 mV @ 1996.8 MHz and 1200 mV @ 2131.2 MHz. + +- Game recording and SysDVR streaming @ 60fps with high video bitrate (Optional). + + - Video duration shown in album will be 2x than the actual value, but playback speed is not affected. + + - Recordings may be less than 30sec if higher bitrate is used. + + - It has noticeable performance impacts in demanding games. + + + +## Installation + +### Method A: AIO Package + +**Contains:** + +- Patches for pcv and ptm modules + +- Precompiled patch tools for pcv module (only for amd64 Windows, build yourself otherwise): + + [hactool](https://github.com/SciresM/hactool), [nx2elf](https://github.com/shuffle2/nx2elf), elf2nso from [switch-tools](https://github.com/switchbrew/switch-tools/), [hacPack](https://github.com/The-4n/hacPack), [bsdiff-win](https://github.com/cnSchwarzer/bsdiff-win/) ([bsdiff](http://www.daemonology.net/bsdiff/)) + +- Prebuilt sys-clk and ReverseNX-RT modified for OC + +- `system-settings.ini` for Horizon OS + +⚠️**Warnings**:⚠️ + +- Since system files are altered, you could **NOT** boot to stock(OFW) with patch applied to SysNAND until you revert the patch, and ban risks exist (?). Therefore, patching SysNAND is **NOT encouraged**. + +- Restoring pcv module backup is required before updating Horizon OS and booting OFW. Launch the `patcher.te` script to restore your backup. + +**Steps:** + +1. Make sure you are running targeted HOS (12.0.x), and have `prod.keys` dumped by [Lockpick_RCM](https://github.com/shchmue/Lockpick_RCM). + +2. Loader patches for Atmosphere: Grab from the web and apply. I won't provide them here. (Or build AMS with `ValidateAcidSignature()` stubbed.) + +3. Place all the files in `SdOut` into SD card. + See [Usage and customization](#usage and customization) and [Details](#details) sections for more info. + + - Be careful of `/atmosphere/config/system_settings.ini`, you may want to edit it manually. + + - Remove all the files in previous OC Suite version before updating to avoid conflicts. + +4. Dump your pcv module. + + If you already have the pcv backup of targeted HOS version, jump to Step 4. Otherwise, redump is required. + + - Load [TegraExplorer](https://github.com/suchmememanyskill/TegraExplorer/releases/latest) payload in hekate. + + - Choose `Browse SD` -> `patcher.te` -> `Launch Script`. + + - Select the MMC you'd like to mount and `Dump PCV Module Backup` + + - Wait for `Done!` showing up and transfer the backup `/atmosphere/oc_patches/pcv-backup` to your PC. + +5. Extract `PatchTools` folder from the AIO package, put `pcv-backup` and `prod.keys` in. + +6. Select RAM frequency and prepare the patches: + + - Copy the `/atmosphere/oc_patches/xx-xxxx.x/ptm-patch` folder ->`/atmosphere/exefs_patches/` on your SD card. + + - Copy `/atmosphere/oc_patches/xx-xxxx.x/pcv-bspatch` -> `PatchTools` on your PC. + +7. Open `cmd.exe`, change working directory to the `PatchTools` folder and type in the following commands. + + Assuming that you put the folder on your Desktop: + + ```cmd + cd %USERPROFILE%\Desktop\PatchTools + mkdir .\temp + mkdir .\temp\pcv_exefs + hactool -k prod.keys --disablekeywarns -t -nca .\pcv-backup --exefsdir .\temp + nx2elf .\temp\main + bspatch .\temp\main.elf .\temp\main-mod.elf .\pcv-bspatch + elf2nso .\temp\main-mod.elf .\temp\pcv_exefs\main + copy .\temp\main.npdm .\temp\pcv_exefs\ + hacpack -k prod.keys -o .\ --type nca --ncatype program --titleid 010000000000001A --exefsdir .\temp\pcv_exefs\ + ren *.nca pcv-module + rd /S /Q .\temp\ + rd /S /Q .\hacpack_backup\ + + ``` + +8. Move the patched `pcv-module` to `/atmosphere/oc_patches/`. + +9. In TegraExplorer, `Browse SD` -> `patcher.te` -> `Launch Script` and then `Apply Patched PCV Module`. + +10. Wait for `Done!` and then reboot to enjoy. + + + +### Method B: For Pro-users + +```bash +git clone https://github.com/KazushiMe/Switch-OC-Suite.git ~/Switch-OC-Suite + +cd $YOUR_ATMOSPHERE_REPO +cp -R ~/Switch-OC-Suite/Atmosphere/*pp stratosphere/loader/source/ +patch < ~/Switch-OC-Suite/Atmosphere/ldr_patcher.diff + +cd $YOUR_SYS_CLK_REPO +git apply ~/Switch-OC-Suite/sys-clk.diff + +cd $YOUR_REVERSENX_RT_REPO +git apply ~/Switch-OC-Suite/ReverseNX-RT.diff +``` + +Then compile sys-clk, ReverseNX-RT and Atmosphere with devkitpro, and don't forget to grab necessary patches in the repo. + +Simply build `loader.kip` from Atmosphere and load it with hekate if you don't feel like wasting time. + + + +## Usage and customization + +**system_settings.ini** in `/atmosphere/config/` + +- **For Erista:** + - Remove the "Fan Control for Mariko" section. + +- Remove the "disable services" part if you use Nintendo Online services like friends, cloud saving and notifications. + +- For "Game Recording FPS and Bitrate", if you play demanding games or don't care about streaming/framerate/bitrate, comment out this section. + +**sys-clk** + +- **For Mariko:** + + - Remove `/config/sys-clk/boost.flag` if you like longer waiting time in loading screens. + + - Remove `/config/sys-clk/boost_start.flag` if you don't want games to boot faster. + +- Add `/config/sys-clk/downclock_dock.flag` to use handheld clocks in Docked mode when Handheld flag is set in ReverseNX. + +- Add `[A111111111111111]` title config in `/config/sys-clk/config.ini` to set frequency override globally: + + ```ini + [A111111111111111] + docked_cpu= + docked_gpu= + handheld_charging_cpu= + handheld_charging_gpu= + handheld_charging_usb_cpu= + handheld_charging_usb_gpu= + handheld_charging_official_cpu= + handheld_charging_official_gpu= + handheld_cpu= + handheld_gpu= + ``` + + + +## Credit + +- CTCaer for modded Hekate bootloader, RE and hardware research + +- HookedBehemoth for nifm_ctest and am_no_copyright [patches](https://github.com/HookedBehemoth/exefs_patches) + +- masagrator for [ReverseNX-RT](https://github.com/masagrator/ReverseNX-RT) + +- RetroNX team for [sys-clk](https://github.com/retronx-team/sys-clk) and a [place](https://*discord*.*gg*/erNC4FB) to discuss + +- SciresM and Reswitched Team for the state-of-the-art [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere) CFW of Switch + +- suchmememanyskill for [TegraExplorer](https://github.com/suchmememanyskill/TegraExplorer) and [TegraScript](https://github.com/suchmememanyskill/TegraScript) + +- Switchbrew [wiki](http://switchbrew.org/wiki/) for Switch in-depth info + +- ZatchyCatGames for RE and original OC patches + + + diff --git a/SdOut/atmosphere/config/system_settings.ini b/SdOut/atmosphere/config/system_settings.ini new file mode 100644 index 00000000..473160c8 --- /dev/null +++ b/SdOut/atmosphere/config/system_settings.ini @@ -0,0 +1,104 @@ +;Don't apply all cheats automatically +[atmosphere] +dmnt_cheats_enabled_by_default = u8!0x0 + +;Fan Control for Mariko +[tc] +tskin_rate_table_console = str!"[[-1000000, 40000, 0, 0], [36000, 43000, 51, 51], [43000, 50000, 51, 153], [50000, 56000, 153, 255], [56000, 1000000, 255, 255]]" +tskin_rate_table_handheld = str!"[[-1000000, 40000, 0, 0], [36000, 43000, 51, 51], [43000, 48000, 51, 102], [48000, 56000, 102, 204], [56000, 1000000, 255, 255], [48000, 1000000, 255, 255]]" +holdable_tskin = u32!0xDAC0 + +;Game Recording FPS and Bitrate +[am.debug] +;30 or 60 +continuous_recording_fps=u32!60 +;~7.5Mbps, default is ~5Mbps, VBR(Variable Bitrate) +continuous_recording_video_bit_rate=u32!0x780000 + +;Disable services for power saving in standby mode +;Delete following part if you use Nintendo Online Services +[bgtc] +enable_halfawake = u32!0x0 +minimum_interval_normal = u32!0x7FFFFFFF +minimum_interval_save = u32!0x7FFFFFFF +battery_threshold_save = u32!0x64 +battery_threshold_stop = u32!0x0 + +[npns] +background_processing = u8!0x0 +sleep_periodic_interval = u32!0x7FFFFFFF +sleep_processing_timeout = u32!0x0 +sleep_max_try_count = u32!0x0 + +[ns.notification] +enable_download_task_list = u8!0x0 +enable_download_ticket = u8!0x0 +enable_network_update = u8!0x0 +enable_random_wait = u8!0x0 +enable_request_on_cold_boot = u8!0x0 +enable_send_rights_usage_status_request = u8!0x0 +enable_sync_elicense_request = u8!0x0 +enable_version_list = u8!0x0 +retry_interval_min = u32!0x7FFFFFFF +retry_interval_max = u32!0x7FFFFFFF + +[ns.rights] +register_na_ids_as_valid_accounts_forcibly_even_if_not_exist = u8!0x1 + +[account] +na_required_for_network_service = u8!0x0 +na_license_verification_enabled = u8!0x0 + +[account.daemon] +background_awaking_periodicity = u32!0x7FFFFFFF +initial_schedule_delay = u32!0x7FFFFFFF +profile_sync_interval = u32!0x7FFFFFFF +na_info_refresh_interval = u32!0x7FFFFFFF + +[capsrv] +enable_album_screenshot_filedata_verification = u8!0x0 +enable_album_movie_filehash_verification = u8!0x0 +enable_album_movie_filesign_verification = u8!0x0 + +[friends] +background_processing = u8!0x0 + +[notification.presenter] +snooze_interval_in_seconds = u32!0x7FFFFFFF +connection_retry_count = u32!0x0 +alarm_pattern_total_repeat_count = u32!0x0 +alarm_pattern_with_vibration_repeat_count = u32!0x0 + +[prepo] +;background_processing = u8!0x0 (shutdown directly when entering sleep mode) +transmission_interval_min = u32!0x7FFFFFFF +transmission_retry_interval_min = u32!0x7FFFFFFF +transmission_retry_interval_max = u32!0x7FFFFFFF +transmission_interval_in_sleep = u32!0x7FFFFFFF +statistics_save_interval_min = u32!0x7FFFFFFF +statistics_post_interval = u32!0x7FFFFFFF +save_system_report = u8!0x0 + +[olsc] +default_auto_upload_global_setting = u8!0x0 +default_auto_download_global_setting = u8!0x0 +autonomy_registration_interval_seconds = u32!0x7FFFFFFF +network_service_license_info_cache_expiration_seconds = u32!0x7FFFFFFF +postponed_transfer_task_processing_interval_seconds = u32!0x7FFFFFFF +retry_offset_seconds = u32!0x7FFFFFFF +network_trouble_detection_span_seconds = u32!0x7FFFFFFF +network_connection_polling_interval_seconds = u32!0x7FFFFFFF +is_save_data_backup_policy_check_required = u8!0x0 +is_global_transfer_task_autonomy_registration_enabled = u8!0x0 +is_on_event_transfer_task_registration_enabled = u8!0x0 +is_periodic_transfer_task_registration_enabled = u8!0x0 + +[ntc] +is_autonomic_correction_enabled = u8!0x0 +autonomic_correction_interval_seconds = u32!0x7FFFFFFF +autonomic_correction_failed_retry_interval_seconds = u32!0x7FFFFFFF +autonomic_correction_immediate_try_count_max = u32!0x0 +autonomic_correction_immediate_try_interval_milliseconds = u32!0x7FFFFFFF + +[systemupdate] +bgnup_retry_seconds = u32!0x7FFFFFFF \ No newline at end of file diff --git a/SdOut/atmosphere/contents/00FF0000636C6BFF/exefs.nsp b/SdOut/atmosphere/contents/00FF0000636C6BFF/exefs.nsp new file mode 100644 index 00000000..87c063bd Binary files /dev/null and b/SdOut/atmosphere/contents/00FF0000636C6BFF/exefs.nsp differ diff --git a/SdOut/atmosphere/contents/00FF0000636C6BFF/flags/boot2.flag b/SdOut/atmosphere/contents/00FF0000636C6BFF/flags/boot2.flag new file mode 100644 index 00000000..e69de29b diff --git a/SdOut/atmosphere/exefs_patches/am_no_copyright/5E5F1CC24D374591AFC2A2336C9453CCFA3961C1.ips b/SdOut/atmosphere/exefs_patches/am_no_copyright/5E5F1CC24D374591AFC2A2336C9453CCFA3961C1.ips new file mode 100644 index 00000000..dae12a93 Binary files /dev/null and b/SdOut/atmosphere/exefs_patches/am_no_copyright/5E5F1CC24D374591AFC2A2336C9453CCFA3961C1.ips differ diff --git a/SdOut/atmosphere/exefs_patches/nifm_ctest/A85F50FBA10E06A3EBA3D3FACB9E075B218C7D6D.ips b/SdOut/atmosphere/exefs_patches/nifm_ctest/A85F50FBA10E06A3EBA3D3FACB9E075B218C7D6D.ips new file mode 100644 index 00000000..a26b00db Binary files /dev/null and b/SdOut/atmosphere/exefs_patches/nifm_ctest/A85F50FBA10E06A3EBA3D3FACB9E075B218C7D6D.ips differ diff --git a/SdOut/atmosphere/oc_patches/12-1731.2/pcv-bspatch b/SdOut/atmosphere/oc_patches/12-1731.2/pcv-bspatch new file mode 100644 index 00000000..0dfebb3b Binary files /dev/null and b/SdOut/atmosphere/oc_patches/12-1731.2/pcv-bspatch differ diff --git a/SdOut/atmosphere/oc_patches/12-1731.2/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips b/SdOut/atmosphere/oc_patches/12-1731.2/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips new file mode 100644 index 00000000..e76baf57 Binary files /dev/null and b/SdOut/atmosphere/oc_patches/12-1731.2/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips differ diff --git a/SdOut/atmosphere/oc_patches/12-1862.4/pcv-bspatch b/SdOut/atmosphere/oc_patches/12-1862.4/pcv-bspatch new file mode 100644 index 00000000..544aa545 Binary files /dev/null and b/SdOut/atmosphere/oc_patches/12-1862.4/pcv-bspatch differ diff --git a/SdOut/atmosphere/oc_patches/12-1862.4/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips b/SdOut/atmosphere/oc_patches/12-1862.4/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips new file mode 100644 index 00000000..060423ca Binary files /dev/null and b/SdOut/atmosphere/oc_patches/12-1862.4/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips differ diff --git a/SdOut/atmosphere/oc_patches/12-1996.8/pcv-bspatch b/SdOut/atmosphere/oc_patches/12-1996.8/pcv-bspatch new file mode 100644 index 00000000..bf27533a Binary files /dev/null and b/SdOut/atmosphere/oc_patches/12-1996.8/pcv-bspatch differ diff --git a/SdOut/atmosphere/oc_patches/12-1996.8/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips b/SdOut/atmosphere/oc_patches/12-1996.8/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips new file mode 100644 index 00000000..a1880544 Binary files /dev/null and b/SdOut/atmosphere/oc_patches/12-1996.8/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips differ diff --git a/SdOut/atmosphere/oc_patches/12-2131.2/pcv-bspatch b/SdOut/atmosphere/oc_patches/12-2131.2/pcv-bspatch new file mode 100644 index 00000000..7245bb1b Binary files /dev/null and b/SdOut/atmosphere/oc_patches/12-2131.2/pcv-bspatch differ diff --git a/SdOut/atmosphere/oc_patches/12-2131.2/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips b/SdOut/atmosphere/oc_patches/12-2131.2/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips new file mode 100644 index 00000000..6082fea1 Binary files /dev/null and b/SdOut/atmosphere/oc_patches/12-2131.2/ptm-patch/A79706954C6C45568B0FFE610627E2E89D8FB0D4.ips differ diff --git a/SdOut/config/sys-clk/boost.flag b/SdOut/config/sys-clk/boost.flag new file mode 100644 index 00000000..e69de29b diff --git a/SdOut/config/sys-clk/boost_start.flag b/SdOut/config/sys-clk/boost_start.flag new file mode 100644 index 00000000..e69de29b diff --git a/SdOut/patcher.te b/SdOut/patcher.te new file mode 100644 index 00000000..ec152c8b --- /dev/null +++ b/SdOut/patcher.te @@ -0,0 +1,149 @@ +pcvModulePath = "bis:/Contents/registered/875acbb5a5b6526159ac68a3c91884c7.nca/00" +OldBackupPath = "sd:/atmosphere/oc_patches/12-patch.bak" +BackupPath = "sd:/atmosphere/oc_patches/pcv-backup" +PatchPath = "sd:/atmosphere/oc_patches/pcv-module" + +POWER = 0x8 + +pauseexit = { + println("Press any button to exit.\n") + pause() + exit() +} + +pausecont = { + println("Press A/POWER button to continue.") + println("Press any other button to exit.") + if ( ! ( pause() & POWER ) ) { + exit() + } +} + +header = { + println("\n-- Switch OC Suite Patcher --\n\n") + println(" OC Suite is provided 'as is' without warranty of any kind,\n USE AT YOUR OWN RISKS!") + println(" If you don't have Joy-Cons connected or are using Switch Lite,") + println(" Press VOL+/- to navigate and POWER button to confirm.\n") +} + +header() + +if (_EMU) { + menuOptions = ["Exit", "Mount EmuMMC", "Mount SysMMC"] +} +else() { + menuOptions = ["Exit", "Mount SysMMC"] +} + +res = menu(menuOptions, 0) + +clearscreen() + +if ( res == 0 ) { + exit() +} + +if ( res == 1 && _EMU ) { + println("EmuMMC is selected!") + wait(1000) + if ( mmcConnect("EMUMMC") ) { + println("An error occured during mmc connect!") + pauseexit() + } +} + +if ( res == 2 || ( res == 1 && ! _EMU ) ) { + println("SysMMC is selected!") + wait(1000) + if ( mmcConnect("SYSMMC") ) { + println("An error occured during mmc connect!") + pauseexit() + } +} + +println("Mounting SYSTEM Partition and Checking...") + +if ( mmcMount("SYSTEM") ) { + println("An error occured during SYSTEM mount!") + pauseexit() +} + +if ( ! fileExists(pcvModulePath) ) { + println("You're NOT using targeted Horizon OS version!") + println("Targeted version: 12.0.x\n") + pauseexit() +} + +if ( fileExists(OldBackupPath) ) { + println("Old Backup found! Renaming...") + if ( fileMove(OldBackupPath, BackupPath) ) { + println("An error occured during renaming backup!") + pauseexit() + } +} + +clearscreen() +header() + +menuOptions = ["Exit", "Dump PCV Module Backup", "Apply Patched PCV Module", "Restore PCV Module Backup"] + +res = menu(menuOptions, 0) + +clearscreen() + +if ( res == 0 ) { + exit() +} + +if ( res == 1 ) { + if ( fileExists(BackupPath) ) { + color("RED") + println("You have the backup already. Do you want to DELETE it and redump?") + println("Make sure you DO NOT have the patched one in the MMC!") + color("WHITE") + wait(3000) + pausecont() + if ( fileDel(BackupPath) ) { + println("An error occured during removing old backup!") + pauseexit() + } + } + println("Dumping...") + if ( fileCopy(pcvModulePath, BackupPath) ) { + println("An error occured during dumping!") + pauseexit() + } + println("Done! ") + pauseexit() +} + +if ( res == 2 ) { + if ( ! fileExists(PatchPath) ) { + color("RED") + println("Patched PCV Module NOT FOUND!") + println("Make sure you have followed the instructions in README.") + color("WHITE") + pauseexit() + } + println("Applying Patched PCV Module...") + if ( fileDel(pcvModulePath) && fileCopy(PatchPath, pcvModulePath) ) { + println("An error occured!") + pauseexit() + } + println("Done! ") + pauseexit() +} + +if ( res == 3 ) { + if ( ! fileExists(BackupPath) ) { + println("PCV Module Backup NOT FOUND!") + pauseexit() + } + println("Restoring PCV Module...") + if ( fileDel(pcvModulePath) && fileCopy(BackupPath, pcvModulePath) ) { + println("An error occured!") + pauseexit() + } + println("Done! ") + pauseexit() +} diff --git a/SdOut/switch/.overlays/ReverseNX-RT-ovl.ovl b/SdOut/switch/.overlays/ReverseNX-RT-ovl.ovl new file mode 100644 index 00000000..22a058f2 Binary files /dev/null and b/SdOut/switch/.overlays/ReverseNX-RT-ovl.ovl differ diff --git a/SdOut/switch/.overlays/sys-clk-overlay.ovl b/SdOut/switch/.overlays/sys-clk-overlay.ovl new file mode 100644 index 00000000..5055bd19 Binary files /dev/null and b/SdOut/switch/.overlays/sys-clk-overlay.ovl differ diff --git a/SdOut/switch/sys-clk-manager.nro b/SdOut/switch/sys-clk-manager.nro new file mode 100644 index 00000000..c565cc52 Binary files /dev/null and b/SdOut/switch/sys-clk-manager.nro differ diff --git a/Source/Atmosphere/ldr_patcher.diff b/Source/Atmosphere/ldr_patcher.diff new file mode 100644 index 00000000..4d2e9aed --- /dev/null +++ b/Source/Atmosphere/ldr_patcher.diff @@ -0,0 +1,51 @@ +--- stratosphere/loader/source/ldr_patcher.cpp @a672917 ++++ stratosphere/loader/source/ldr_patcher.cpp +@@ -15,11 +15,27 @@ + */ + #include + #include "ldr_patcher.hpp" ++#include "ldr_pcv_patch.hpp" + + namespace ams::ldr { + + namespace { + ++ constexpr u8 PcvModuleId[2][20] = { ++ { 0x91, 0xD6, 0x1D, 0x59, 0xD7, 0x00, 0x23, 0x78, 0xE3, 0x55, 0x84, 0xFC, 0x0B, 0x38, 0xC7, 0x69, 0x3A, 0x3A, 0xBA, 0xB5, }, //11.0.0 ++ { 0xC5, 0x03, 0xE9, 0x65, 0x50, 0xF3, 0x02, 0xE1, 0x21, 0x87, 0x31, 0x36, 0xB8, 0x14, 0xA5, 0x29, 0x86, 0x3D, 0x94, 0x9B, }, //12.0.0 ++ }; ++ ++ constexpr u8 NifmModuleId[2][20] = { ++ { 0x7A, 0x43, 0xF8, 0x40, 0x33, 0x7C, 0x28, 0xD4, 0x53, 0x71, 0x88, 0x43, 0x60, 0x8E, 0xEF, 0xF7, 0x8A, 0xFD, 0x46, 0x0B, }, //11.0.0 ++ { 0xA8, 0x5F, 0x50, 0xFB, 0xA1, 0x0E, 0x06, 0xA3, 0xEB, 0xA3, 0xD3, 0xFA, 0xCB, 0x9E, 0x07, 0x5B, 0x21, 0x8C, 0x7D, 0x6D, }, ++ }; ++ ++ constexpr u8 AmModuleId[2][20] = { ++ { 0x25, 0x50, 0x97, 0xEF, 0x11, 0xC0, 0x75, 0x9B, 0x1F, 0x36, 0x13, 0x2F, 0x73, 0xBD, 0xDB, 0x04, 0xC3, 0xEF, 0x9A, 0xBE, }, //11.0.0 ++ { 0x5E, 0x5F, 0x1C, 0xC2, 0x4D, 0x37, 0x45, 0x91, 0xAF, 0xC2, 0xA2, 0x33, 0x6C, 0x94, 0x53, 0xCC, 0xFA, 0x39, 0x61, 0xC1, }, ++ }; ++ + constexpr const char *NsoPatchesDirectory = "exefs_patches"; + + /* Exefs patches want to prevent modification of header, */ +@@ -113,6 +129,20 @@ namespace ams::ldr { + + /* Apply IPS patches. */ + void LocateAndApplyIpsPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size) { ++ for(int i = 0; i < 2; i++) ++ { ++ if(memcmp(PcvModuleId[i], build_id, sizeof(PcvModuleId[i])) == 0) { ++ ApplyPcvPatch(reinterpret_cast(mapped_nso), mapped_size, i); ++ return; // Return here since pcv module loads before sd card can be mounted ++ } ++ else if(memcmp(NifmModuleId[i], build_id, sizeof(NifmModuleId[i])) == 0) { ++ ApplyCtestPatch(reinterpret_cast(mapped_nso), mapped_size, i); ++ } ++ else if(memcmp(AmModuleId[i], build_id, sizeof(AmModuleId[i])) == 0) { ++ ApplyCopyrightPatch(reinterpret_cast(mapped_nso), mapped_size, i); ++ } ++ } ++ + if (!EnsureSdCardMounted()) { + return; + } diff --git a/Source/Atmosphere/ldr_pcv_patch.cpp b/Source/Atmosphere/ldr_pcv_patch.cpp new file mode 100644 index 00000000..ba7b426f --- /dev/null +++ b/Source/Atmosphere/ldr_pcv_patch.cpp @@ -0,0 +1,268 @@ +// placed in Atmosphere/stratosphere/loader/source/ + +#include + +#define EMC_OVERCLOCK 1 +#define EMC_OVERVOLT 1 + +namespace ams::ldr { + + namespace { + + constexpr u32 CtestOffset[2] = { 0xF400, 0x1079C }; + + constexpr u32 CopyrightOffset[2] = { 0xC6128, 0xCA414 }; //am_no_copyright port + + constexpr u8 BehemothPatch[8] = { 0xE0, 0x03, 0x1F, 0xAA, 0xC0, 0x03, 0x5F, 0xD6 }; //🤔 + + typedef struct { + u32 hz = 0; + u32 volt = 0; + u32 unk[6] = {0}; + s32 coeffs[6] = {0}; + } gpu_clock_table_t; + + constexpr u32 EmcFreqOffsets[2][30] = { + { 0xD7C60, 0xD7C68, 0xD7C70, 0xD7C78, 0xD7C80, 0xD7C88, 0xD7C90, 0xD7C98, 0xD7CA0, 0xD7CA8, 0xE1800, 0xEEFA0, 0xF2478, 0xFE284, 0x10A304, 0x10D7DC, 0x110A40, 0x113CA4, 0x116F08, 0x11A16C, 0x11D3D0, 0x120634, 0x123898, 0x126AFC, 0x129D60, 0x12CFC4, 0x130228, 0x13BFE0, 0x140D00, 0x140D50, }, + { 0xE1810, 0xE6530, 0xE6580, 0xE6AB0, 0xE6AB8, 0xE6AC0, 0xE6AC8, 0xE6AD0, 0xE6AD8, 0xE6AE0, 0xE6AE8, 0xE6AF0, 0xE6AF8, 0xF0650, 0xFDDF0, 0x1012C8, 0x10D0D4, 0x119154, 0x11C62C, 0x11F890, 0x122AF4, 0x125D58, 0x128FBC, 0x12C220, 0x12F484, 0x1326E8, 0x13594C, 0x138BB0, 0x13BE14, 0x13F078, } + }; + // RAM freqs to choose: 1731200, 1862400, 1996800, 2131200 + constexpr u32 NewEmcFreq = 1862400; + // RAM overclock could be UNSTABLE on some RAM without bumping up voltage, + // and therefore show graphical glitches, hang randomly or even worse, corrupt your NAND + + namespace Erista { + + typedef struct { + u32 hz = 0; + u32 unk = 0; + u32 volt = 0; + u32 unk2[5] = {0}; + s32 coeffs[6] = {0}; + } cpu_clock_table_t; + + /* CPU */ + constexpr u32 CpuVoltageLimitOffsets[2][3] = { + { 0xE1AC8, 0xE1AD4, 0xE37E4 }, + { 0xF0918, 0xF0924, 0xF2634 }, + }; + constexpr u32 NewCpuVoltageLimit = 1358; + static_assert(NewCpuVoltageLimit <= 1400); + + constexpr u32 CpuTablesFreeSpace[2] = { 0xE3B78, 0xF29C8 }; + constexpr cpu_clock_table_t NewCpuTables[] = { + { 1887000, 0, 1240000, {}, { 5100873, -279186, 4747 } }, + { 1963500, 0, 1262000, {}, { 5100873, -279186, 4747 } }, + { 2091000, 0, 1298000, {}, { 5100873, -279186, 4747 } }, + { 2193000, 0, 1328000, {}, { 5100873, -279186, 4747 } }, + { 2295000, 0, 1358000, {}, { 5100873, -279186, 4747 } }, + }; + static_assert(sizeof(NewCpuTables) <= sizeof(cpu_clock_table_t)*16); + + /* GPU */ + constexpr u32 GpuTablesFreeSpace[2] = { 0xE2B58, 0xF19A8 }; + constexpr gpu_clock_table_t NewGpuTables[] = { + { 998400, 0, {}, { 1316991, 8144, -940, 808, -21583, 226 } }, + { 1075200, 0, {}, { 1358883, 8144, -940, 808, -21583, 226 } }, + }; + static_assert(sizeof(NewGpuTables) <= sizeof(gpu_clock_table_t)*20); + + /* EMC */ + constexpr u32 EmcVolatageOffsets[2][2] = { + { 0x143998, 0x14399C }, + { 0x142878, 0x14287C }, + }; + constexpr u32 NewEmcVoltage = 1150000; + static_assert(NewEmcVoltage <= 1250000); + // 1150mV for 1862.4 MHz and 1200 mV for 2131.2 MHz, according to the feedback in RetroNX Discord + // 1125mV(HOS default) for 1731.2 MHz and 1175mV for 1996.8 MHz + }; + + namespace Mariko { + + typedef struct { + u32 hz = 0; + u32 unk1 = 0; + s32 coeffs[6] = {0}; + u32 volt = 0; + u32 unk3[5] = {0}; + } cpu_clock_table_t; + + /* CPU */ + constexpr u32 CpuVoltageLimitOffsets[2][11] = { + { 0xE1A8C, 0xE1A98, 0xE1AA4, 0xE1AB0, 0xE1AF8, 0xE1B04, 0xE1B10, 0xE1B1C, 0xE1B28, 0xE1B34, 0xE1F4C }, + { 0xF08DC, 0xF08E8, 0xF08F4, 0xF0900, 0xF0948, 0xF0954, 0xF0960, 0xF096C, 0xF0978, 0xF0984, 0xF0D9C }, + }; + constexpr u32 NewCpuVoltageLimit = 1220; + static_assert(NewCpuVoltageLimit <= 1300); //1300mV hangs for me + + constexpr u32 CpuVoltageCoeffTable[2][10] = { + { 0xE2140, 0xE2178, 0xE21B0, 0xE21E8, 0xE2220, 0xE2258, 0xE2290, 0xE22C8, 0xE2300, 0xE2338 }, + { 0xF0F90, 0xF0FC8, 0xF1000, 0xF1038, 0xF1070, 0xF10A8, 0xF10E0, 0xF1118, 0xF1150, 0xF1188 }, + }; + constexpr u32 NewCpuVoltageCoeff = NewCpuVoltageLimit * 1000; + + constexpr u32 CpuTablesFreeSpace[2] = { 0xE2350, 0xF11A0 }; + constexpr cpu_clock_table_t NewCpuTables[] = { + {2091000, 0, {1719782, -40440, 27}, NewCpuVoltageCoeff, {}}, + {2193000, 0, {1809766, -41939, 27}, NewCpuVoltageCoeff, {}}, + {2295000, 0, {1904458, -43439, 27}, NewCpuVoltageCoeff, {}}, + {2397000, 0, {2004105, -44938, 27}, NewCpuVoltageCoeff, {}}, + /*{2499000, 0, {2108966, -46437, 27}, NewCpuVoltageCoeff, {}}, + {2601000, 0, {2219313, -47937, 27}, NewCpuVoltageCoeff, {}}, + {2703000, 0, {2335434, -49436, 27}, NewCpuVoltageCoeff, {}}, + {2805000, 0, {2457630, -50936, 27}, NewCpuVoltageCoeff, {}}, + {2907000, 0, {2586221, -52435, 27}, NewCpuVoltageCoeff, {}}, + {3009000, 0, {2721539, -53934, 27}, NewCpuVoltageCoeff, {}},*/ + // calculated using linear regression: + // coeffs[0]=604531*exp(0.0000005*hz) + // coeffs[1]=-0.0147*hz-9702.1 + }; + static_assert(sizeof(NewCpuTables) <= sizeof(cpu_clock_table_t)*14); + + constexpr u32 MaxCpuClockOffset[2] = { 0xE2740, 0xF1590 }; + constexpr u32 NewMaxCpuClock = 2397000; + + /* GPU */ + constexpr u32 GpuTablesFreeSpace[2] = { 0xE3410, 0xF2260 }; + constexpr gpu_clock_table_t NewGpuTables[] = { + { 1305600, 0, {}, {1380113, -13465, -874, 0, 2580, 648} }, + { 1344000, 0, {}, {1420000, -14000, -870, 0, 2193, 824} }, + /*{ 1382400, 0, {}, {1423200, -14188, -868, 0, 2150, 830} }, + { 1420800, 0, {}, {1426290, -14368, -860, 0, 2100, 850} }, + { 1305600, 0, {}, {1380113, -13465, -874, 0, 2580, 648} }, + { 1344000, 0, {}, {1420000, -14100, -870, 0, 2193, 824} }, + { 1382400, 0, {}, {1426290, -14368, -860, 0, 2100, 850} }, + { 1420800, 0, {}, {1450000, -14600, -860, 0, 2000, 850} }, + { 1382400, 0, {}, {1454061, -14500, -868, 0, 2000, 900} }, + { 1382400, 0, {}, {1474061, -15277, -866, 0, 1710, 1024} }, + { 1382400, 0, {}, {1500000, -15500, -880, 0, 1000, 548} }, + { 1420800, 0, {}, {1550000, -16500, -885, 0, 1500, 300} },*/ + // some arbitrary coeffs I guessed and tested, YMMV + }; + static_assert(sizeof(NewGpuTables) <= sizeof(gpu_clock_table_t)*15); + + constexpr u32 Reg1MaxGpuOffset[2] = { 0x2E0AC, 0x3F6CC }; + constexpr u8 Reg1NewMaxGpuClock[2][0xC] = { + // Original: 1267MHz + /* + MOV W13,#0x5600 + MOVK W13,#0x13,LSL #16 + NOP + */ + // Bump to 1536MHz + /* + MOV W13,#0x7000 + MOVK W13,#0x17,LSL #16 + NOP + */ + //0x0D, 0xC0, 0x8A, 0x52, 0x6D, 0x02, 0xA0, 0x72, 0x1F, 0x20, 0x03, 0xD5 + { 0x0D, 0x00, 0x8E, 0x52, 0xED, 0x02, 0xA0, 0x72, 0x1F, 0x20, 0x03, 0xD5 }, + { 0x0B, 0x00, 0x8E, 0x52, 0xEB, 0x02, 0xA0, 0x72, 0x1F, 0x20, 0x03, 0xD5 }, + }; + + constexpr u32 Reg2MaxGpuOffset[2] = { 0x2E110, 0x3F730 }; + constexpr u8 Reg2NewMaxGpuClock[2][0x8] = { + // Original: 1267MHz + /* + MOV W13,#0x5600 + MOVK W13,#0x13,LSL #16 + */ + // Bump to 1536MHz + /* + MOV W13,#0x7000 + MOVK W13,#0x17,LSL #16 + */ + //0x0D, 0xC0, 0x8A, 0x52, 0x6D, 0x02, 0xA0, 0x72 + { 0x0D, 0x00, 0x8E, 0x52, 0xED, 0x02, 0xA0, 0x72, }, + { 0x0B, 0x00, 0x8E, 0x52, 0xEB, 0x02, 0xA0, 0x72, }, + }; + + /* EMC */ + // Not available on Mariko + /*constexpr u32 EmcVolatageOffsets[2][2] = { + { + 0x145FE4, //max77812_dram + 0x144BA4, //max77812_dram + }, + { + 0x143A84, + 0x144EC4, + } + }; + constexpr u32 NewEmcVoltage = 650000; + static_assert(NewEmcVoltage <= 750000);*/ + }; + + } + + void ApplyPcvPatch(u8 *mapped_module, size_t mapped_size, int i) { + + /* Add new CPU and GPU clock tables for Erista */ + AMS_ABORT_UNLESS(Erista::CpuTablesFreeSpace[i] <= mapped_size && Erista::GpuTablesFreeSpace[i] <= mapped_size); + std::memcpy(mapped_module + Erista::CpuTablesFreeSpace[i], Erista::NewCpuTables, sizeof(Erista::NewCpuTables)); + std::memcpy(mapped_module + Erista::GpuTablesFreeSpace[i], Erista::NewGpuTables, sizeof(Erista::NewGpuTables)); + + /* Patch max CPU voltage on Erista */ + for(int j = 0; j < 3; j++) { + std::memcpy(mapped_module + Erista::CpuVoltageLimitOffsets[i][j], &Erista::NewCpuVoltageLimit, sizeof(Erista::NewCpuVoltageLimit)); + } + + /* Add new CPU and GPU clock tables for Mariko */ + AMS_ABORT_UNLESS(Mariko::CpuTablesFreeSpace[i] <= mapped_size && Mariko::GpuTablesFreeSpace[i] <= mapped_size); + std::memcpy(mapped_module + Mariko::CpuTablesFreeSpace[i], Mariko::NewCpuTables, sizeof(Mariko::NewCpuTables)); + std::memcpy(mapped_module + Mariko::GpuTablesFreeSpace[i], Mariko::NewGpuTables, sizeof(Mariko::NewGpuTables)); + + /* Patch Mariko max CPU and GPU clockrates */ + AMS_ABORT_UNLESS(Mariko::MaxCpuClockOffset[i] <= mapped_size && Mariko::Reg1MaxGpuOffset[i] <= mapped_size && Mariko::Reg2MaxGpuOffset[i] <= mapped_size); + std::memcpy(mapped_module + Mariko::MaxCpuClockOffset[i], &Mariko::NewMaxCpuClock, sizeof(Mariko::NewMaxCpuClock)); + std::memcpy(mapped_module + Mariko::Reg1MaxGpuOffset[i], Mariko::Reg1NewMaxGpuClock, sizeof(Mariko::Reg1NewMaxGpuClock[i])); + std::memcpy(mapped_module + Mariko::Reg2MaxGpuOffset[i], Mariko::Reg2NewMaxGpuClock, sizeof(Mariko::Reg2NewMaxGpuClock[i])); + + /* Patch max cpu voltage on Mariko */ + for(int j = 0; j < 11; j++) { + std::memcpy(mapped_module + Mariko::CpuVoltageLimitOffsets[i][j], &Mariko::NewCpuVoltageLimit, sizeof(Mariko::NewCpuVoltageLimit)); + } + for(int j = 0; j < 10; j++) { + std::memcpy(mapped_module + Mariko::CpuVoltageCoeffTable[i][j], &Mariko::NewCpuVoltageCoeff, sizeof(Mariko::NewCpuVoltageCoeff)); + } + + /* Patch EMC clocks and voltage if enabled. + Note: On Erista, this requires removing or modifiying minerva */ + if constexpr(EMC_OVERCLOCK) { + for(u32 j = 0; j < sizeof(EmcFreqOffsets[i])/sizeof(u32); j++) { + AMS_ABORT_UNLESS(EmcFreqOffsets[i][j] <= mapped_size); + std::memcpy(mapped_module + EmcFreqOffsets[i][j], &NewEmcFreq, sizeof(NewEmcFreq)); + } + } + + if constexpr(EMC_OVERVOLT) { + if(spl::GetSocType() == spl::SocType_Erista) { + for(u32 j = 0; j < sizeof(Erista::EmcVolatageOffsets[i])/sizeof(u32); j++) { + AMS_ABORT_UNLESS(Erista::EmcVolatageOffsets[i][j] <= mapped_size); + std::memcpy(mapped_module + Erista::EmcVolatageOffsets[i][j], &Erista::NewEmcVoltage, sizeof(Erista::NewEmcVoltage)); + } + } + // Not available on Mariko + /*else if(spl::GetSocType() == spl::SocType_Mariko) { + for(u32 j = 0; j < sizeof(Mariko::EmcVolatageOffsets[i])/sizeof(u32); j++) { + AMS_ABORT_UNLESS(Mariko::EmcVolatageOffsets[i][j] <= mapped_size); + std::memcpy(mapped_module + Mariko::EmcVolatageOffsets[i][j], &Mariko::NewEmcVoltage, sizeof(Mariko::NewEmcVoltage)); + } + }*/ + } + + return; + } + + void ApplyCtestPatch(u8 *mapped_module, size_t mapped_size, int i) { + AMS_ABORT_UNLESS(CtestOffset[i] - 0x100 <= mapped_size); + std::memcpy(mapped_module + CtestOffset[i] - 0x100, BehemothPatch, sizeof(BehemothPatch)); + } + + void ApplyCopyrightPatch(u8 *mapped_module, size_t mapped_size, int i) { + AMS_ABORT_UNLESS(CopyrightOffset[i] - 0x100 <= mapped_size); + std::memcpy(mapped_module + CopyrightOffset[i] - 0x100, BehemothPatch, sizeof(BehemothPatch)); + } + +} diff --git a/Source/Atmosphere/ldr_pcv_patch.hpp b/Source/Atmosphere/ldr_pcv_patch.hpp new file mode 100644 index 00000000..6af17518 --- /dev/null +++ b/Source/Atmosphere/ldr_pcv_patch.hpp @@ -0,0 +1,14 @@ +// placed in Atmosphere/stratosphere/loader/source/ + +#pragma once +#include + +namespace ams::ldr { + + void ApplyPcvPatch(u8 *mapped_module, size_t mapped_size, int i); + + void ApplyCtestPatch(u8 *mapped_module, size_t mapped_size, int i); + + void ApplyCopyrightPatch(u8 *mapped_module, size_t mapped_size, int i); + +} diff --git a/Source/Patch/12-ptm.pchtxt b/Source/Patch/12-ptm.pchtxt new file mode 100644 index 00000000..ca01abfd --- /dev/null +++ b/Source/Patch/12-ptm.pchtxt @@ -0,0 +1,160 @@ +@nsobid-A79706954C6C45568B0FFE610627E2E89D8FB0D4 +@flag offset_shift 0x100 + +// PTM PerformanceConfiguration Patch for 12.0.x +// Change all RAM freqs to 1862.4 MHz and Boost CPU freqs to 1963.5 MHz by default +// RAM +// 1731.2 MHz - 00043067 +// 1862.4 MHz - 00F8016F +// 1996.8 MHz - 00C00477 +// 2131.2 MHz - 0088077F +// CPU +// 1963.5 MHz - E0A10875 +// 2295.0 MHz - C0EBCA88 +// All values are Little-Endian +// See https://switchbrew.org/wiki/PTM_services#PerformanceConfiguration for details +// Use IPSwitch(https://github.com/3096/ipswitch/) to convert into ips patch + +//0x00010000: 1020, 384.0, 1600.0 +@enabled +000C5E14 00F7CB3C +000C5E18 00F7CB3C +000C5E1C 0060E316 +000C5E20 0060E316 +000C5E24 00F8016F +000C5E28 00F8016F + +//0x00010001: 1020, 768.0, 1600.0 +@enabled +000C5E34 00F7CB3C +000C5E38 00F7CB3C +000C5E3C 00C0C62D +000C5E40 00C0C62D +000C5E44 00F8016F +000C5E48 00F8016F + +//0x00010002: 1224, 691.0, 1600.0 +@enabled +000C5E54 00C2F448 +000C5E58 00C2F448 +000C5E5C 00E03229 +000C5E60 00E03229 +000C5E64 00F8016F +000C5E68 00F8016F + +//0x00020000: 1020, 230.4, 1600.0 +@enabled +000C5E74 00F7CB3C +000C5E78 00F7CB3C +000C5E7C 00A0BB0D +000C5E80 00A0BB0D +000C5E84 00F8016F +000C5E88 00F8016F + +//0x00020001: 1020, 307.2, 1600.0 +@enabled +000C5E94 00F7CB3C +000C5E98 00F7CB3C +000C5E9C 00804F12 +000C5EA0 00804F12 +000C5EA4 00F8016F +000C5EA8 00F8016F + +//0x00020002: 1224, 230.4, 1600.0 +@enabled +000C5EB4 00C2F448 +000C5EB8 00C2F448 +000C5EBC 00A0BB0D +000C5EC0 00A0BB0D +000C5EC4 00F8016F +000C5EC8 00F8016F + +//0x00020003: 1020, 307.2, 1331.2 +@enabled +000C5ED4 00F7CB3C +000C5ED8 00F7CB3C +000C5EDC 00804F12 +000C5EE0 00804F12 +000C5EE4 00F8016F +000C5EE8 00F8016F + +//0x00020004: 1020, 384.0, 1331.2 +@enabled +000C5EF4 00F7CB3C +000C5EF8 00F7CB3C +000C5EFC 0060E316 +000C5F00 0060E316 +000C5F04 00F8016F +000C5F08 00F8016F + +//0x00020005: 1020, 307.0, 1065.6 +@enabled +000C5F14 00F7CB3C +000C5F18 00F7CB3C +000C5F1C 00804F12 +000C5F20 00804F12 +000C5F24 00F8016F +000C5F28 00F8016F + +//0x00020006: 1020, 384.0, 1065.6 +@enabled +000C5F34 00F7CB3C +000C5F38 00F7CB3C +000C5F3C 0060E316 +000C5F40 0060E316 +000C5F44 00F8016F +000C5F48 00F8016F + +//0x92220007: 1020, 460.0, 1600.0 +@enabled +000C5F54 00F7CB3C +000C5F58 00F7CB3C +000C5F5C 0040771B +000C5F60 0040771B +000C5F64 00F8016F +000C5F68 00F8016F + +//0x92220008: 1020, 460.0, 1331.2 +@enabled +000C5F74 00F7CB3C +000C5F78 00F7CB3C +000C5F7C 0040771B +000C5F80 0040771B +000C5F84 00F8016F +000C5F88 00F8016F + +//0x92220009: 1785, 76.8, 1600.0 +@enabled +000C5F94 E0A10875 +000C5F98 E0A10875 +000C5F9C 00E09304 +000C5FA0 00E09304 +000C5FA4 00F8016F +000C5FA8 00F8016F + +//0x9222000A: 1785, 76.8, 1331.2 +@enabled +000C5FB4 E0A10875 +000C5FB8 E0A10875 +000C5FBC 00E09304 +000C5FC0 00E09304 +000C5FC4 00F8016F +000C5FC8 00F8016F + +//0x9222000B: 1020, 76.8, 1600.0 +@enabled +000C5FD4 00F7CB3C +000C5FD8 00F7CB3C +000C5FDC 00E09304 +000C5FE0 00E09304 +000C5FE4 00F8016F +000C5FE8 00F8016F + +//0x9222000C: 1020, 76.8, 1331.2 +@enabled +000C5FF4 00F7CB3C +000C5FF8 00F7CB3C +000C5FFC 00E09304 +000C6000 00E09304 +000C6004 00F8016F +000C6008 00F8016F diff --git a/Source/Patch/ReverseNX-RT.diff b/Source/Patch/ReverseNX-RT.diff new file mode 100644 index 00000000..616a9326 --- /dev/null +++ b/Source/Patch/ReverseNX-RT.diff @@ -0,0 +1,190 @@ +diff --git a/.gitignore b/.gitignore +index 3e35c18..6b42bc5 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -8,3 +8,4 @@ build/ + *.nacp + + *.nro ++*.DS_Store +diff --git a/Overlay/libs/Atmosphere-libs b/Overlay/libs/Atmosphere-libs +index 2d522dc..bc08912 160000 +--- a/Overlay/libs/Atmosphere-libs ++++ b/Overlay/libs/Atmosphere-libs +@@ -1 +1 @@ +-Subproject commit 2d522dc6a12b2eb5eb3f103a8c5b5126ca301b1a ++Subproject commit bc08912dd31bb172467add8e24b4f0adac431939 +diff --git a/Overlay/libs/libtesla b/Overlay/libs/libtesla +index 6628524..a5ce77d 160000 +--- a/Overlay/libs/libtesla ++++ b/Overlay/libs/libtesla +@@ -1 +1 @@ +-Subproject commit 66285245361a02e5480c7bb7dac9ef6449ae6181 ++Subproject commit a5ce77d14f144a24c21684128ae79ef1e808734c +diff --git a/Overlay/source/main.cpp b/Overlay/source/main.cpp +index cec060c..7b7d8b1 100644 +--- a/Overlay/source/main.cpp ++++ b/Overlay/source/main.cpp +@@ -2,8 +2,12 @@ + #include // The Tesla Header + #include "dmntcht.h" + ++#define PROCESS_MANAGEMENT_QLAUNCH_TID 0x0100000000001000ULL ++ + bool def = true; ++bool defChanged = false; + bool isDocked = false; ++bool isDockedChanged = false; + bool PluginRunning = false; + bool closed = false; + Handle debug; +@@ -17,6 +21,7 @@ bool dmntcht = false; + bool SaltySD = false; + bool bak = false; + bool plugin = false; ++bool sysclkComm = false; + char DockedChar[32]; + char SystemChar[32]; + char PluginChar[36]; +@@ -54,6 +59,29 @@ bool isServiceRunning(const char *serviceName) { + } + } + ++u64 GetCurrentApplicationId() ++{ ++ // Copied from sys-clk ++ Result rc = 0; ++ std::uint64_t pid = 0; ++ std::uint64_t tid = 0; ++ rc = pmdmntGetApplicationProcessId(&pid); ++ ++ if (rc == 0x20f) ++ { ++ return PROCESS_MANAGEMENT_QLAUNCH_TID; ++ } ++ ++ rc = pminfoGetProgramId(&tid, pid); ++ ++ if (rc == 0x20f) ++ { ++ return PROCESS_MANAGEMENT_QLAUNCH_TID; ++ } ++ ++ return tid; ++} ++ + class GuiTest : public tsl::Gui { + public: + GuiTest(u8 arg1, u8 arg2, bool arg3) { } +@@ -95,9 +123,10 @@ class GuiTest : public tsl::Gui { + if (MAGIC == 0x06BA7E39) { + auto *clickableListItem = new tsl::elm::ListItem("Change system control"); + clickableListItem->setClickListener([](u64 keys) { +- if (keys & KEY_A) { ++ if (keys & HidNpadButton_A) { + if (PluginRunning == true) { + def = !def; ++ defChanged = true; + if (dmntcht == true) { + dmntchtWriteCheatProcessMemory(def_address, &def, 0x1); + dmntchtReadCheatProcessMemory(def_address, &def, 0x1); +@@ -120,9 +149,10 @@ class GuiTest : public tsl::Gui { + + auto *clickableListItem2 = new tsl::elm::ListItem("Change mode"); + clickableListItem2->setClickListener([](u64 keys) { +- if (keys & KEY_A) { ++ if (keys & HidNpadButton_A) { + if (PluginRunning == true && def == false) { + isDocked =! isDocked; ++ isDockedChanged = true; + if (dmntcht == true) { + dmntchtWriteCheatProcessMemory(docked_address, &isDocked, 0x1); + dmntchtReadCheatProcessMemory(docked_address, &isDocked, 0x1); +@@ -145,7 +175,7 @@ class GuiTest : public tsl::Gui { + else if (SaltySD == true && plugin == true && check == false) { + auto *clickableListItem = new tsl::elm::ListItem("(De)activate plugin"); + clickableListItem->setClickListener([](u64 keys) { +- if (keys & KEY_A) { ++ if (keys & HidNpadButton_A) { + if (bak == false) { + rename("sdmc:/SaltySD/plugins/ReverseNX-RT.elf", "sdmc:/SaltySD/plugins/ReverseNX-RT.elf.bak"); + bak = true; +@@ -185,6 +215,38 @@ class GuiTest : public tsl::Gui { + svcReadDebugProcessMemory(&isDocked, debug, docked_address, 0x1); + svcCloseHandle(debug); + } ++ if (sysclkComm && isDockedChanged && def == false && bak == false) ++ { ++ uint8_t sysclkConfArr[9]; ++ u64 currentTID = GetCurrentApplicationId(); ++ if (currentTID != PROCESS_MANAGEMENT_QLAUNCH_TID) ++ { ++ for(int i = 0; i < 8; i++) // TID to hex ++ sysclkConfArr[i] = currentTID >> 8*(7-i); ++ sysclkConfArr[8] = isDocked + 1; ++ ++ FILE* sysclkConf = fopen("/config/sys-clk/ReverseNX-RT.conf", "wb+"); ++ fwrite(sysclkConfArr, sizeof(sysclkConfArr), 1, sysclkConf); ++ fclose(sysclkConf); ++ } ++ isDockedChanged = false; ++ } ++ if (sysclkComm && defChanged && def == true && bak == false) // change from force mode to system-controlled mode ++ { ++ uint8_t sysclkConfArr[9]; ++ u64 currentTID = GetCurrentApplicationId(); ++ if (currentTID != PROCESS_MANAGEMENT_QLAUNCH_TID) ++ { ++ for(int i = 0; i < 8; i++) // TID to hex ++ sysclkConfArr[i] = currentTID >> 8*(7-i); ++ sysclkConfArr[8] = 3; ++ ++ FILE* sysclkConf = fopen("/config/sys-clk/ReverseNX-RT.conf", "wb+"); ++ fwrite(sysclkConfArr, sizeof(sysclkConfArr), 1, sysclkConf); ++ fclose(sysclkConf); ++ } ++ defChanged = false; ++ } + i = 0; + } + else i++; +@@ -202,7 +264,7 @@ class GuiTest : public tsl::Gui { + } + + // Called once every frame to handle inputs not handled by other UI elements +- virtual bool handleInput(u64 keysDown, u64 keysHeld, touchPosition touchInput, JoystickPosition leftJoyStick, JoystickPosition rightJoyStick) override { ++ virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override { + return false; // Return true here to singal the inputs have been consumed + } + }; +@@ -213,6 +275,7 @@ class OverlayTest : public tsl::Overlay { + virtual void initServices() override { + smInitialize(); + fsdevMountSdmc(); ++ pminfoInitialize(); + + SaltySD = CheckPort(); + if (SaltySD == false) return; +@@ -234,6 +297,14 @@ class OverlayTest : public tsl::Overlay { + else return; + } + ++ // Check if sys-clk exist ++ temp = fopen("/atmosphere/contents/00FF0000636C6BFF/flags/boot2.flag", "r"); ++ if (temp != NULL) ++ { ++ sysclkComm = true; ++ fclose(temp); ++ } ++ + if (R_FAILED(pmdmntGetApplicationProcessId(&PID))) remove("sdmc:/SaltySD/ReverseNX-RT.hex"); + else { + check = true; +@@ -284,6 +355,7 @@ class OverlayTest : public tsl::Overlay { + virtual void exitServices() override { + dmntchtExit(); + if (dmntcht == false) svcCloseHandle(debug); ++ pminfoExit(); + fsdevUnmountDevice("sdmc"); + smExit(); + } // Callet at the end to clean up all services previously initialized diff --git a/Source/Patch/sys-clk.diff b/Source/Patch/sys-clk.diff new file mode 100644 index 00000000..bac9c105 --- /dev/null +++ b/Source/Patch/sys-clk.diff @@ -0,0 +1,997 @@ +diff --git a/common/src/apm_profile_table.c b/common/src/apm_profile_table.c +index 7439ff1..19f5b68 100644 +--- a/common/src/apm_profile_table.c ++++ b/common/src/apm_profile_table.c +@@ -23,8 +23,8 @@ SysClkApmConfiguration sysclk_g_apm_configurations[] = { + {0x00020006, 1020000000, 384000000, 1065600000}, + {0x92220007, 1020000000, 460800000, 1600000000}, + {0x92220008, 1020000000, 460800000, 1331200000}, +- {0x92220009, 1785000000, 76800000, 1600000000}, +- {0x9222000A, 1785000000, 76800000, 1331200000}, ++ {0x92220009, 1963500000, 76800000, 1600000000}, ++ {0x9222000A, 1963500000, 76800000, 1331200000}, + {0x9222000B, 1020000000, 76800000, 1600000000}, + {0x9222000C, 1020000000, 76800000, 1331200000}, + {0, 0, 0, 0}, +diff --git a/common/src/clock_table.c b/common/src/clock_table.c +index a8164dc..2765962 100644 +--- a/common/src/clock_table.c ++++ b/common/src/clock_table.c +@@ -11,11 +11,15 @@ + #include + + uint32_t sysclk_g_freq_table_mem_hz[] = { +- 665600000, +- 800000000, +- 1065600000, ++ //665600000, ++ //800000000, ++ //1065600000, + 1331200000, +- 1600000000, ++ //1600000000, ++ 1731200000, ++ 1862400000, ++ 1996800000, ++ 2131200000, + 0, + }; + +@@ -32,6 +36,12 @@ uint32_t sysclk_g_freq_table_cpu_hz[] = { + 1581000000, + 1683000000, + 1785000000, ++ 1887000000, ++ 1963500000, ++ 2091000000, ++ 2193000000, ++ 2295000000, ++ 2397000000, + 0, + }; + +@@ -48,5 +58,14 @@ uint32_t sysclk_g_freq_table_gpu_hz[] = { + 768000000, + 844800000, + 921600000, ++ 998400000, ++ 1075200000, ++ 1152000000, ++ 1228800000, ++ 1267200000, ++ 1305600000, ++ 1344000000, ++ //1382400000, ++ //1420800000, + 0, + }; +diff --git a/manager/src/advanced_settings_tab.cpp b/manager/src/advanced_settings_tab.cpp +index 325d228..6d9725e 100644 +--- a/manager/src/advanced_settings_tab.cpp ++++ b/manager/src/advanced_settings_tab.cpp +@@ -77,7 +77,7 @@ AdvancedSettingsTab::AdvancedSettingsTab() + }); + + // MEM +- brls::SelectListItem *memFreqListItem = createFreqListItem(SysClkModule_MEM, context.overrideFreqs[SysClkModule_MEM] / 1000000); ++ /*brls::SelectListItem *memFreqListItem = createFreqListItem(SysClkModule_MEM, context.overrideFreqs[SysClkModule_MEM] / 1000000); + memFreqListItem->getValueSelectedEvent()->subscribe([](int result) + { + Result rc = result == 0 ? +@@ -90,11 +90,11 @@ AdvancedSettingsTab::AdvancedSettingsTab() + errorResult(result == 0 ? "sysclkIpcRemoveOverride" : "sysclkIpcSetOverride", rc); + // TODO: Reset selected value + } +- }); ++ });*/ + + this->addView(cpuFreqListItem); + this->addView(gpuFreqListItem); +- this->addView(memFreqListItem); ++ //this->addView(memFreqListItem); + + // Config + this->addView(new brls::Header("Configuration")); +diff --git a/manager/src/app_profile_frame.cpp b/manager/src/app_profile_frame.cpp +index 4ad9545..0ea6af3 100644 +--- a/manager/src/app_profile_frame.cpp ++++ b/manager/src/app_profile_frame.cpp +@@ -116,7 +116,7 @@ void AppProfileFrame::addFreqs(brls::List* list, SysClkProfile profile) + list->addView(gpuListItem); + + // MEM +- brls::SelectListItem* memListItem = createFreqListItem(SysClkModule_MEM, this->profiles.mhzMap[profile][SysClkModule_MEM]); ++ /*brls::SelectListItem* memListItem = createFreqListItem(SysClkModule_MEM, this->profiles.mhzMap[profile][SysClkModule_MEM]); + + this->profiles.mhzMap[profile][SysClkModule_MEM] *= 1000000; + +@@ -126,7 +126,7 @@ void AppProfileFrame::addFreqs(brls::List* list, SysClkProfile profile) + + brls::Logger::debug("Caching freq for module %d and profile %d to %" PRIu32, SysClkModule_MEM, profile, this->profiles.mhzMap[profile][SysClkModule_MEM]); + }); +- list->addView(memListItem); ++ list->addView(memListItem);*/ + } + + void AppProfileFrame::onProfileChanged() +diff --git a/manager/src/cheat_sheet_tab.cpp b/manager/src/cheat_sheet_tab.cpp +index 6823e0e..08a91bf 100644 +--- a/manager/src/cheat_sheet_tab.cpp ++++ b/manager/src/cheat_sheet_tab.cpp +@@ -19,6 +19,8 @@ + */ + + #include "cheat_sheet_tab.h" ++#include "ipc/client.h" ++#include "utils.h" + + #include + +@@ -28,8 +30,9 @@ CheatSheetTab::CheatSheetTab() + this->addView(new brls::Header("CPU Clocks")); + brls::Table *cpuTable = new brls::Table(); + +- cpuTable->addRow(brls::TableRowType::BODY, "Maximum", "1785 MHz"); +- cpuTable->addRow(brls::TableRowType::BODY, "Official Docked and Handheld", "1020 MHz"); ++ cpuTable->addRow(brls::TableRowType::BODY, "OC Suite Maximum", "2397.0 MHz"); ++ cpuTable->addRow(brls::TableRowType::BODY, "Official Boost", "1785.0 MHz"); ++ cpuTable->addRow(brls::TableRowType::BODY, "Official Docked and Handheld", "1020.0 MHz"); + + this->addView(cpuTable); + +@@ -37,10 +40,10 @@ CheatSheetTab::CheatSheetTab() + this->addView(new brls::Header("GPU Clocks")); + brls::Table *gpuTable = new brls::Table(); + +- gpuTable->addRow(brls::TableRowType::BODY, "Maximum", "921 MHz"); +- gpuTable->addRow(brls::TableRowType::BODY, "Official Docked", "768 MHz"); +- gpuTable->addRow(brls::TableRowType::BODY, "Maximum Handheld", "460 MHz"); +- gpuTable->addRow(brls::TableRowType::BODY, "Official Handheld", "384 MHz"); ++ gpuTable->addRow(brls::TableRowType::BODY, "OC Suite Maximum", "1344.0 MHz"); ++ gpuTable->addRow(brls::TableRowType::BODY, "Official Maximum", "921.6 MHz"); ++ gpuTable->addRow(brls::TableRowType::BODY, "Official Docked", "768.0 MHz"); ++ gpuTable->addRow(brls::TableRowType::BODY, "Official Handheld", "384.0/460.8 MHz"); + + this->addView(gpuTable); + +@@ -48,8 +51,20 @@ CheatSheetTab::CheatSheetTab() + this->addView(new brls::Header("MEM Clocks")); + brls::Table *memTable = new brls::Table(); + +- memTable->addRow(brls::TableRowType::BODY, "Maximum, Official Docked", "1600 MHz"); +- memTable->addRow(brls::TableRowType::BODY, "Official Handheld", "1331 MHz"); ++ // Get context ++ SysClkContext context; ++ Result rc = sysclkIpcGetCurrentContext(&context); ++ ++ if (R_FAILED(rc)) ++ { ++ brls::Logger::error("Unable to get context"); ++ errorResult("sysclkIpcGetCurrentContext", rc); ++ brls::Application::crash("Could not get the current sys-clk context, please check that it is correctly installed and enabled."); ++ return; ++ } ++ ++ memTable->addRow(brls::TableRowType::BODY, "OC Suite", formatFreq(context.freqs[SysClkModule_MEM])); ++ memTable->addRow(brls::TableRowType::BODY, "Official", "1331.2/1600.0 MHz"); + + this->addView(memTable); + } +diff --git a/overlay/src/ui/gui/app_profile_gui.cpp b/overlay/src/ui/gui/app_profile_gui.cpp +index 29adfe5..116c803 100644 +--- a/overlay/src/ui/gui/app_profile_gui.cpp ++++ b/overlay/src/ui/gui/app_profile_gui.cpp +@@ -62,7 +62,7 @@ void AppProfileGui::addProfileUI(SysClkProfile profile) + this->listElement->addItem(new tsl::elm::CategoryHeader(sysclkFormatProfile(profile, true))); + this->addModuleListItem(profile, SysClkModule_CPU, &sysclk_g_freq_table_cpu_hz[0]); + this->addModuleListItem(profile, SysClkModule_GPU, &sysclk_g_freq_table_gpu_hz[0]); +- this->addModuleListItem(profile, SysClkModule_MEM, &sysclk_g_freq_table_mem_hz[0]); ++ //this->addModuleListItem(profile, SysClkModule_MEM, &sysclk_g_freq_table_mem_hz[0]); + } + + void AppProfileGui::listUI() +diff --git a/overlay/src/ui/gui/global_override_gui.cpp b/overlay/src/ui/gui/global_override_gui.cpp +index 3ea98e4..91742b7 100644 +--- a/overlay/src/ui/gui/global_override_gui.cpp ++++ b/overlay/src/ui/gui/global_override_gui.cpp +@@ -62,7 +62,7 @@ void GlobalOverrideGui::listUI() + { + this->addModuleListItem(SysClkModule_CPU, &sysclk_g_freq_table_cpu_hz[0]); + this->addModuleListItem(SysClkModule_GPU, &sysclk_g_freq_table_gpu_hz[0]); +- this->addModuleListItem(SysClkModule_MEM, &sysclk_g_freq_table_mem_hz[0]); ++ //this->addModuleListItem(SysClkModule_MEM, &sysclk_g_freq_table_mem_hz[0]); + } + + void GlobalOverrideGui::refresh() +diff --git a/sysmodule/src/clock_manager.cpp b/sysmodule/src/clock_manager.cpp +index 46d7207..4139f34 100644 +--- a/sysmodule/src/clock_manager.cpp ++++ b/sysmodule/src/clock_manager.cpp +@@ -8,10 +8,14 @@ + * -------------------------------------------------------------------------- + */ + ++#define FORCE_ALL_HANDHELD_MODES_TO_USE_DOCK_CLOCK ++#include ++#include "errors.h" + #include "clock_manager.h" + #include "file_utils.h" + #include "clocks.h" + #include "process_management.h" ++#include + + ClockManager* ClockManager::instance = NULL; + +@@ -59,6 +63,136 @@ ClockManager::~ClockManager() + delete this->context; + } + ++bool ClockManager::IsCpuBoostMode() ++{ ++ std::uint32_t confId = 0; ++ Result rc = 0; ++ rc = apmExtGetCurrentPerformanceConfiguration(&confId); ++ ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); ++ if(confId == 0x92220009 || confId == 0x9222000A) ++ return true; ++ else ++ return false; ++} ++ ++SysClkProfile ClockManager::ReverseNXProfile(bool ForceDock) ++{ ++ RealProfile = Clocks::GetCurrentProfile(); ++ switch(RealProfile) ++ { ++ case SysClkProfile_HandheldChargingOfficial: ++#ifdef FORCE_ALL_HANDHELD_MODES_TO_USE_DOCK_CLOCK ++ case SysClkProfile_HandheldChargingUSB: ++ case SysClkProfile_HandheldCharging: ++ case SysClkProfile_Handheld: ++#endif ++ if (ForceDock) ++ return SysClkProfile_Docked; ++ else ++ return RealProfile; ++ case SysClkProfile_Docked: ++ if (ForceDock) ++ return SysClkProfile_Docked; ++ else ++ return FileUtils::IsDownclockDockEnabled() ? SysClkProfile_HandheldChargingOfficial : SysClkProfile_Docked; ++ default: ++ return RealProfile; ++ } ++} ++ ++void ClockManager::checkReverseNXTool() ++{ ++ char ReverseNXToolAsm[] = "_ZN2nn2oe18GetPerformanceModeEv.asm64"; // Checking one asm64 file is enough ++ char ReverseNXToolAsmPath[128]; ++ uint8_t flag = 0; ++ snprintf(ReverseNXToolAsmPath, sizeof ReverseNXToolAsmPath, "/SaltySD/patches/%s", ReverseNXToolAsm); ++ ++ FILE *readReverseNXToolAsm; ++ readReverseNXToolAsm = fopen(ReverseNXToolAsmPath, "rb"); ++ ++ // Enforce mode globally: Enabled ++ if(readReverseNXToolAsm != NULL) ++ { ++ checkReverseNXToolAsm(readReverseNXToolAsm, &flag); ++ switch(flag) ++ { ++ case 1: ++ FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Enforce Handheld globally"); ++ this->context->profile = ReverseNXProfile(false); ++ isDockedReverseNX = false; ++ isEnabledReverseNX = true; ++ isEnabledReverseNXTool = true; ++ break; ++ case 2: ++ FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Enforce Docked globally"); ++ this->context->profile = ReverseNXProfile(true); ++ isDockedReverseNX = true; ++ isEnabledReverseNX = true; ++ isEnabledReverseNXTool = true; ++ break; ++ } ++ } ++ else ++ { ++ snprintf(ReverseNXToolAsmPath, sizeof ReverseNXToolAsmPath, "/SaltySD/patches/%016lX/%s", this->context->applicationId, ReverseNXToolAsm); ++ readReverseNXToolAsm = fopen(ReverseNXToolAsmPath, "rb"); ++ // Found game-specific setting ++ if(readReverseNXToolAsm != NULL) ++ { ++ checkReverseNXToolAsm(readReverseNXToolAsm, &flag); ++ switch(flag) ++ { ++ case 1: ++ FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Force Handheld in %016lX", this->context->applicationId); ++ this->context->profile = ReverseNXProfile(false); ++ isDockedReverseNX = false; ++ isEnabledReverseNX = true; ++ isEnabledReverseNXTool = true; ++ break; ++ case 2: ++ FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Force Docked in %016lX", this->context->applicationId); ++ this->context->profile = ReverseNXProfile(true); ++ isDockedReverseNX = true; ++ isEnabledReverseNX = true; ++ isEnabledReverseNXTool = true; ++ break; ++ default: ++ isEnabledReverseNXTool = false; ++ } ++ } ++ } ++} ++ ++bool ClockManager::GameStartBoost() ++{ ++ if (tickStartBoost && this->GetConfig()->Enabled()) ++ { ++ if (Clocks::GetCurrentHz(SysClkModule_CPU) != MAX_CPU) ++ { ++ Clocks::SetHz(SysClkModule_CPU, MAX_CPU); ++ this->context->freqs[SysClkModule_CPU] = MAX_CPU; ++ } ++ ++ std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId(); ++ // If user exit the game ++ if (applicationId != this->context->applicationId) ++ { ++ tickStartBoost = 0; ++ return false; ++ } ++ ++ if (tickStartBoost == 1) ++ { ++ FileUtils::LogLine("[mgr] Boost done, reset to stock"); ++ Clocks::ResetToStock(); ++ } ++ tickStartBoost--; ++ return true; ++ } ++ ++ return false; ++} ++ + void ClockManager::SetRunning(bool running) + { + this->running = running; +@@ -72,27 +206,75 @@ bool ClockManager::Running() + void ClockManager::Tick() + { + std::scoped_lock lock{this->contextMutex}; +- if (this->RefreshContext() || this->config->Refresh()) ++ ++ if(!GameStartBoost()) + { +- std::uint32_t hz = 0; +- for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) ++ bool cpuBoost = FileUtils::IsBoostEnabled() ? IsCpuBoostMode() : false; ++ if (this->RefreshContext() || this->config->Refresh()) + { +- hz = this->context->overrideFreqs[module]; +- +- if(!hz) ++ std::uint32_t hz = 0; ++ std::uint32_t hzForceOverride = 0; ++ for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { +- hz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile); +- } ++ hz = this->context->overrideFreqs[module]; + +- if (hz) +- { +- hz = Clocks::GetNearestHz((SysClkModule)module, this->context->profile, hz); ++ if(!hz) ++ { ++ hz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile); ++ hzForceOverride = this->config->GetAutoClockHz(0xA111111111111111, (SysClkModule)module, this->context->profile); ++ if (!hz && hzForceOverride) ++ hz = hzForceOverride; + +- if (hz != this->context->freqs[module] && this->context->enabled) ++ if(isEnabledReverseNX && !hz) ++ { ++ switch(module) ++ { ++ case SysClkModule_CPU: ++ hz = 1020'000'000; ++ break; ++ case SysClkModule_GPU: ++ if (!isDockedReverseNX && ((FileUtils::IsDownclockDockEnabled() && RealProfile == SysClkProfile_Docked) ++ || RealProfile != SysClkProfile_Docked)) ++ hz = 460'800'000; ++ else ++ hz = 768'000'000; ++ break; ++ } ++ } ++ ++ } ++ ++ if (hz) + { +- FileUtils::LogLine("[mgr] %s clock set : %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10); +- Clocks::SetHz((SysClkModule)module, hz); +- this->context->freqs[module] = hz; ++ hz = Clocks::GetNearestHz((SysClkModule)module, isEnabledReverseNX ? RealProfile : this->context->profile, hz); ++ ++ if (hz != this->context->freqs[module] && this->context->enabled) ++ { ++ if (cpuBoost) ++ { ++ if (module == SysClkModule_CPU && hz < MAX_CPU) ++ { ++ hz = MAX_CPU; ++ FileUtils::LogLine("[mgr] CpuBoostMode detected, bump CPU to max"); ++ } ++ } ++ FileUtils::LogLine("[mgr] %s clock set : %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10); ++ Clocks::SetHz((SysClkModule)module, hz); ++ this->context->freqs[module] = hz; ++ } ++ } ++ } ++ } ++ else if (FileUtils::IsBoostEnabled()) ++ { ++ // If user doesn't set any freq but with sys-clk enabled, then boost CPU in CpuBoostMode ++ if(cpuBoost && this->GetConfig()->Enabled()) ++ { ++ if(this->context->freqs[SysClkModule_CPU] != MAX_CPU) ++ { ++ FileUtils::LogLine("[mgr] CpuBoostMode detected, bump CPU to max"); ++ Clocks::SetHz(SysClkModule_CPU, MAX_CPU); ++ this->context->freqs[SysClkModule_CPU] = MAX_CPU; + } + } + } +@@ -104,6 +286,56 @@ void ClockManager::WaitForNextTick() + svcSleepThread(this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) * 1000000ULL); + } + ++void ClockManager::checkReverseNXToolAsm(FILE* readFile, uint8_t* flag) ++{ ++ // Copied from ReverseNXTool ++ uint8_t Docked[0x10] = {0xE0, 0x03, 0x00, 0x32, 0xC0, 0x03, 0x5F, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ++ uint8_t Handheld[0x10] = {0x00, 0x00, 0xA0, 0x52, 0xC0, 0x03, 0x5F, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ++ uint8_t filebuffer[0x10] = {0}; ++ uint8_t cmpresult = 0; ++ fread(&filebuffer, 1, 16, readFile); ++ cmpresult = memcmp(filebuffer, Docked, sizeof(Docked)); ++ if (cmpresult != 0) ++ { ++ cmpresult = memcmp(filebuffer, Handheld, sizeof(Handheld)); ++ if (cmpresult != 0) ++ *flag = 0; // Set to default ++ else ++ *flag = 1; // Handheld ++ } ++ else ++ *flag = 2; // Docked ++ ++ fclose(readFile); ++} ++ ++void ClockManager::checkReverseNXRT(bool recheckReverseNX, uint8_t* flag) ++{ ++ FILE* readReverseNXRTConf = fopen(FILE_REVERSENX_RT_CONF_PATH, "rb"); ++ if (readReverseNXRTConf != NULL) ++ { ++ uint8_t ReverseNXRTConfArr[9]; ++ fread(ReverseNXRTConfArr, 9, 1, readReverseNXRTConf); ++ fclose(readReverseNXRTConf); ++ remove(FILE_REVERSENX_RT_CONF_PATH); ++ ++ uint8_t currentTid[8]; ++ for(int i = 0; i < 8; i++) ++ currentTid[i] = this->context->applicationId >> 8*(7-i); ++ ++ uint8_t cmpresult = memcmp(currentTid, ReverseNXRTConfArr, sizeof(currentTid)); ++ ++ if (cmpresult == 0) ++ *flag = ReverseNXRTConfArr[8]; // 1: Handheld, 2: Docked, 3: Reset ++ else ++ *flag = 0; // 0: Not applicable ++ } ++ else if (recheckReverseNX) ++ *flag = prevReverseNXRT; // Use previous state when profile changes ++ else ++ *flag = 0; ++} ++ + bool ClockManager::RefreshContext() + { + bool hasChanged = false; +@@ -120,21 +352,103 @@ bool ClockManager::RefreshContext() + if (applicationId != this->context->applicationId) + { + FileUtils::LogLine("[mgr] TitleID change: %016lX", applicationId); ++ prevReverseNXRT = 0; // Reset ReverseNX-RT previous state when Title ID changes + this->context->applicationId = applicationId; + hasChanged = true; ++ ++ if (FileUtils::IsReverseNXEnabled() || recheckReverseNX) ++ { ++ // A new game starts or the real profile changes, then we need to check if ReverseNXTool patches are applied ++ isEnabledReverseNX = false; ++ ++ // Check if ReverseNXTool patches are applied ++ if (applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID) ++ this->checkReverseNXTool(); ++ } ++ ++ if (FileUtils::IsBoostStartEnabled() && this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID) ++ { ++ // If a game starts and override for CPU clock is not enabled, then set MAX_CPU for 10 sec ++ std::uint32_t overcpu = this->context->overrideFreqs[SysClkModule_CPU]; ++ if (!overcpu) ++ { ++ tickStartBoost = (std::uint32_t)( 10'000 / this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) ) + 1; ++ FileUtils::LogLine("[mgr] A game starts, bump CPU to max for 10 sec"); ++ return true; ++ } ++ } + } + ++ if (!tickCheckReverseNXRT || recheckReverseNX) ++ { ++ uint8_t flag = 0; ++ checkReverseNXRT(recheckReverseNX, &flag); ++ ++ switch(flag) ++ { ++ case 1: ++ FileUtils::LogLine("[mgr] ReverseNX-RT detected: Enforce Handheld Mode"); ++ this->context->profile = ReverseNXProfile(false); ++ prevReverseNXRT = flag; ++ isEnabledReverseNX = true; ++ isDockedReverseNX = false; ++ hasChanged = true; ++ break; ++ case 2: ++ FileUtils::LogLine("[mgr] ReverseNX-RT detected: Enforce Docked Mode"); ++ this->context->profile = ReverseNXProfile(true); ++ prevReverseNXRT = flag; ++ isEnabledReverseNX = true; ++ isDockedReverseNX = true; ++ hasChanged = true; ++ break; ++ case 3: ++ FileUtils::LogLine("[mgr] ReverseNX-RT disabled: Reset to System-controlled Mode and recheck ReverseNX-Tool"); ++ RealProfile = Clocks::GetCurrentProfile(); ++ this->context->profile = RealProfile; ++ prevReverseNXRT = 0; ++ isEnabledReverseNX = false; ++ isDockedReverseNX = false; ++ hasChanged = true; ++ if (this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID) ++ this->checkReverseNXTool(); ++ break; ++ case 0: ++ if (recheckReverseNX && isEnabledReverseNXTool && this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID) ++ this->checkReverseNXTool(); ++ break; ++ } ++ // Check once per sec ++ tickCheckReverseNXRT = (std::uint32_t)( 1'000 / this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) ) + 1; ++ } ++ tickCheckReverseNXRT--; ++ ++ if (recheckReverseNX) ++ recheckReverseNX = false; ++ + SysClkProfile profile = Clocks::GetCurrentProfile(); +- if (profile != this->context->profile) ++ if (profile != this->context->profile && !isEnabledReverseNX) + { + FileUtils::LogLine("[mgr] Profile change: %s", Clocks::GetProfileName(profile, true)); + this->context->profile = profile; + hasChanged = true; + } ++ if (profile != RealProfile && isEnabledReverseNX) ++ { ++ FileUtils::LogLine("[mgr] Profile change: %s, recheck ReverseNX", Clocks::GetProfileName(profile, true)); ++ this->context->profile = profile; ++ RealProfile = profile; ++ hasChanged = true; ++ recheckReverseNX = true; ++ } + + // restore clocks to stock values on app or profile change + if(hasChanged) + { ++ if (profile == SysClkProfile_Handheld) ++ MAX_CPU = 1963'500'000; ++ else ++ MAX_CPU = 2295'000'000; + Clocks::ResetToStock(); + } + +@@ -142,7 +456,9 @@ bool ClockManager::RefreshContext() + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + hz = Clocks::GetCurrentHz((SysClkModule)module); +- if (hz != 0 && hz != this->context->freqs[module]) ++ uint32_t cur_mhz = hz/1000'000; ++ uint32_t be4_mhz = this->context->freqs[module]/1000'000; ++ if (hz != 0 && cur_mhz != be4_mhz) + { + FileUtils::LogLine("[mgr] %s clock change: %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10); + this->context->freqs[module] = hz; +diff --git a/sysmodule/src/clock_manager.h b/sysmodule/src/clock_manager.h +index 3eecb21..fd5dce2 100644 +--- a/sysmodule/src/clock_manager.h ++++ b/sysmodule/src/clock_manager.h +@@ -20,14 +20,32 @@ + class ClockManager + { + public: ++ std::uint32_t MAX_CPU = 1963500000; ++ + static ClockManager* GetInstance(); + static void Initialize(); + static void Exit(); + ++ bool recheckReverseNX = false; ++ bool isEnabledReverseNX = false; ++ bool isEnabledReverseNXTool = false; ++ bool isDockedReverseNX = false; ++ std::uint16_t tickCheckReverseNXRT = 0; ++ std::uint16_t tickStartBoost = 0; ++ char prevReverseNXRT = 0; ++ SysClkProfile RealProfile; ++ ++ bool IsCpuBoostMode(); ++ SysClkProfile ReverseNXProfile(bool); ++ void checkReverseNXTool(); ++ bool GameStartBoost(); ++ + void SetRunning(bool running); + bool Running(); + void Tick(); + void WaitForNextTick(); ++ void checkReverseNXToolAsm(FILE*, uint8_t*); ++ void checkReverseNXRT(bool, uint8_t*); + SysClkContext GetCurrentContext(); + Config* GetConfig(); + +diff --git a/sysmodule/src/clocks.cpp b/sysmodule/src/clocks.cpp +index 62ef40f..3ef7e15 100644 +--- a/sysmodule/src/clocks.cpp ++++ b/sysmodule/src/clocks.cpp +@@ -12,6 +12,8 @@ + #include "clocks.h" + #include "errors.h" + ++bool Clocks::isMariko = false; ++ + void Clocks::GetList(SysClkModule module, std::uint32_t **outClocks) + { + switch(module) +@@ -60,6 +62,26 @@ void Clocks::Initialize() + rc = tcInitialize(); + ASSERT_RESULT_OK(rc, "tcInitialize"); + } ++ ++ // Check if it's Mariko ++ u64 hardware_type = 0; ++ splInitialize(); ++ splGetConfig(SplConfigItem_HardwareType, &hardware_type); ++ splExit(); ++ ++ switch(hardware_type) { ++ case 0: //Icosa ++ case 1: //Copper ++ break; ++ case 2: //Hoag ++ case 3: //Iowa ++ case 4: //Calcio ++ case 5: //Aula ++ isMariko = true; ++ break; ++ default: ++ break; ++ } + } + + void Clocks::Exit() +@@ -171,7 +193,8 @@ void Clocks::ResetToStock() + + Clocks::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz); + Clocks::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz); +- Clocks::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz); ++ // We don't need to set MEM freqs any more ++ //Clocks::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz); + } + else + { +@@ -195,16 +218,16 @@ SysClkProfile Clocks::GetCurrentProfile() + return SysClkProfile_Docked; + } + +- ChargerType chargerType; ++ PsmChargerType chargerType; + + rc = psmGetChargerType(&chargerType); + ASSERT_RESULT_OK(rc, "psmGetChargerType"); + +- if(chargerType == ChargerType_Charger) ++ if(chargerType == PsmChargerType_EnoughPower) + { + return SysClkProfile_HandheldChargingOfficial; + } +- else if(chargerType == ChargerType_Usb) ++ else if(chargerType == PsmChargerType_LowPower || chargerType == PsmChargerType_NotSupported) + { + return SysClkProfile_HandheldChargingUSB; + } +@@ -214,6 +237,10 @@ SysClkProfile Clocks::GetCurrentProfile() + + void Clocks::SetHz(SysClkModule module, std::uint32_t hz) + { ++ // We don't need to set MEM freqs any more ++ if (module == SysClkModule_MEM) ++ return; ++ + Result rc = 0; + + if(hosversionAtLeast(8,0,0)) +@@ -222,7 +249,6 @@ void Clocks::SetHz(SysClkModule module, std::uint32_t hz) + + rc = clkrstOpenSession(&session, Clocks::GetPcvModuleId(module), 3); + ASSERT_RESULT_OK(rc, "clkrstOpenSession"); +- + rc = clkrstSetClockRate(&session, hz); + ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); + +@@ -248,7 +274,7 @@ std::uint32_t Clocks::GetCurrentHz(SysClkModule module) + ASSERT_RESULT_OK(rc, "clkrstOpenSession"); + + rc = clkrstGetClockRate(&session, &hz); +- ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); ++ ASSERT_RESULT_OK(rc, "clkrstGetClockRate"); + + clkrstCloseSession(&session); + } +@@ -280,11 +306,11 @@ std::uint32_t Clocks::GetMaxAllowedHz(SysClkModule module, SysClkProfile profile + { + if(profile < SysClkProfile_HandheldCharging) + { +- return SYSCLK_GPU_HANDHELD_MAX_HZ; ++ return isMariko ? 1536000000 : SYSCLK_GPU_HANDHELD_MAX_HZ; + } + else if(profile <= SysClkProfile_HandheldChargingUSB) + { +- return SYSCLK_GPU_UNOFFICIAL_CHARGER_MAX_HZ; ++ return isMariko ? 1536000000 : SYSCLK_GPU_UNOFFICIAL_CHARGER_MAX_HZ; + } + } + +@@ -293,6 +319,79 @@ std::uint32_t Clocks::GetMaxAllowedHz(SysClkModule module, SysClkProfile profile + + std::uint32_t Clocks::GetNearestHz(SysClkModule module, std::uint32_t inHz) + { ++ // Hardcoded values to return, I don't know why it will bump to max when excessive OC ++ if(module == SysClkModule_MEM) ++ { ++ switch(inHz) ++ { ++ case 1331000000: ++ return 1331200000; ++ case 1731000000: ++ return 1731200000; ++ case 1862000000: ++ return 1862400000; ++ case 1996000000: ++ return 1996800000; ++ default: ++ return inHz; ++ } ++ } ++ ++ if(module == SysClkModule_CPU) ++ { ++ switch(inHz) ++ { ++ case 1963000000: ++ return 1963500000; ++ default: ++ return inHz; ++ } ++ } ++ ++ if(module == SysClkModule_GPU) ++ { ++ switch(inHz) ++ { ++ case 76000000: ++ return 76800000; ++ case 153000000: ++ return 153600000; ++ case 230000000: ++ return 230400000; ++ case 307000000: ++ return 307200000; ++ case 460000000: ++ return 460800000; ++ case 537000000: ++ return 537600000; ++ case 614000000: ++ return 614400000; ++ case 691000000: ++ return 691200000; ++ case 844000000: ++ return 844800000; ++ case 921000000: ++ return 921600000; ++ case 998000000: ++ return 998400000; ++ case 1075000000: ++ return 1075200000; ++ case 1228000000: ++ return 1228800000; ++ case 1267000000: ++ return 1267200000; ++ case 1305000000: ++ return 1305600000; ++ case 1382000000: ++ return 1382400000; ++ case 1420000000: ++ return 1420800000; ++ default: ++ return inHz; ++ } ++ } ++ ++ return inHz; + std::uint32_t *clockTable = NULL; + GetList(module, &clockTable); + +diff --git a/sysmodule/src/clocks.h b/sysmodule/src/clocks.h +index 7f86be8..5ff180b 100644 +--- a/sysmodule/src/clocks.h ++++ b/sysmodule/src/clocks.h +@@ -16,6 +16,7 @@ + class Clocks + { + public: ++ static bool isMariko; + static void Exit(); + static void Initialize(); + static void ResetToStock(); +diff --git a/sysmodule/src/file_utils.cpp b/sysmodule/src/file_utils.cpp +index 5503f44..8f91a1a 100644 +--- a/sysmodule/src/file_utils.cpp ++++ b/sysmodule/src/file_utils.cpp +@@ -9,12 +9,19 @@ + */ + + #include "file_utils.h" ++#include "clocks.h" ++#include ++#include + #include + + static LockableMutex g_log_mutex; + static LockableMutex g_csv_mutex; + static std::atomic_bool g_has_initialized = false; + static bool g_log_enabled = false; ++static bool g_boost_enabled = false; ++static bool g_boost_start_enabled = false; ++static bool g_downclock_dock_enabled = false; ++static bool g_reversenx_enabled = false; + static std::uint64_t g_last_flag_check = 0; + + extern "C" void __libnx_init_time(void); +@@ -124,9 +131,55 @@ void FileUtils::RefreshFlags(bool force) + g_log_enabled = false; + } + ++ // Only Enable Boost for Mariko ++ if (Clocks::isMariko) ++ { ++ file = fopen(FILE_BOOST_FLAG_PATH, "r"); ++ if (file) ++ { ++ g_boost_enabled = true; ++ fclose(file); ++ } else { ++ g_boost_enabled = false; ++ } ++ ++ file = fopen(FILE_BOOST_START_FLAG_PATH, "r"); ++ if (file) ++ { ++ g_boost_start_enabled = true; ++ fclose(file); ++ } else { ++ g_boost_start_enabled = false; ++ } ++ } ++ ++ file = fopen(FILE_DOWNCLOCK_DOCK_FLAG_PATH, "r"); ++ if (file) ++ { ++ g_downclock_dock_enabled = true; ++ fclose(file); ++ } else { ++ g_downclock_dock_enabled = false; ++ } ++ + g_last_flag_check = now; + } + ++bool FileUtils::IsBoostEnabled() ++{ ++ return g_boost_enabled; ++} ++ ++bool FileUtils::IsBoostStartEnabled() ++{ ++ return g_boost_start_enabled; ++} ++ ++bool FileUtils::IsDownclockDockEnabled() ++{ ++ return g_downclock_dock_enabled; ++} ++ + void FileUtils::InitializeAsync() + { + Thread initThread = {0}; +@@ -163,9 +216,23 @@ Result FileUtils::Initialize() + FileUtils::LogLine("=== " TARGET " " TARGET_VERSION " ==="); + } + ++ FILE *file = fopen(FILE_SALTYNX_PATH, "r"); ++ if (file) ++ { ++ g_reversenx_enabled = true; ++ fclose(file); ++ } else { ++ g_reversenx_enabled = false; ++ } ++ + return rc; + } + ++bool FileUtils::IsReverseNXEnabled() ++{ ++ return g_reversenx_enabled; ++} ++ + void FileUtils::Exit() + { + if (!g_has_initialized) +diff --git a/sysmodule/src/file_utils.h b/sysmodule/src/file_utils.h +index 4f1642e..336e63b 100644 +--- a/sysmodule/src/file_utils.h ++++ b/sysmodule/src/file_utils.h +@@ -22,6 +22,11 @@ + #define FILE_CONTEXT_CSV_PATH FILE_CONFIG_DIR "/context.csv" + #define FILE_LOG_FLAG_PATH FILE_CONFIG_DIR "/log.flag" + #define FILE_LOG_FILE_PATH FILE_CONFIG_DIR "/log.txt" ++#define FILE_BOOST_FLAG_PATH FILE_CONFIG_DIR "/boost.flag" ++#define FILE_BOOST_START_FLAG_PATH FILE_CONFIG_DIR "/boost_start.flag" ++#define FILE_DOWNCLOCK_DOCK_FLAG_PATH FILE_CONFIG_DIR "/downclock_dock.flag" ++#define FILE_SALTYNX_PATH "/atmosphere/contents/0000000000534C56/flags/boot2.flag" // Just check for SaltyNX boot flag ++#define FILE_REVERSENX_RT_CONF_PATH FILE_CONFIG_DIR "/ReverseNX-RT.conf" + + class FileUtils + { +@@ -30,6 +35,10 @@ class FileUtils + static Result Initialize(); + static bool IsInitialized(); + static bool IsLogEnabled(); ++ static bool IsBoostEnabled(); ++ static bool IsBoostStartEnabled(); ++ static bool IsDownclockDockEnabled(); ++ static bool IsReverseNXEnabled(); + static void InitializeAsync(); + static void LogLine(const char *format, ...); + static void WriteContextToCsv(const SysClkContext* context);