- Sys-clk Fix: Now you can completely disable ReverseNX profile syncing by removing "ReverseNX_sync.flag"
535 lines
18 KiB
C++
535 lines
18 KiB
C++
/*
|
|
* --------------------------------------------------------------------------
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
|
* wrote this file. As long as you retain this notice you can do whatever you
|
|
* want with this stuff. If you meet any of us some day, and you think this
|
|
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
|
* --------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define FORCE_ALL_HANDHELD_MODES_TO_USE_DOCK_CLOCK
|
|
#include <nxExt.h>
|
|
#include "errors.h"
|
|
#include "clock_manager.h"
|
|
#include "file_utils.h"
|
|
#include "clocks.h"
|
|
#include "process_management.h"
|
|
#include <cstring>
|
|
|
|
ClockManager* ClockManager::instance = NULL;
|
|
|
|
ClockManager* ClockManager::GetInstance()
|
|
{
|
|
return instance;
|
|
}
|
|
|
|
void ClockManager::Exit()
|
|
{
|
|
if(instance)
|
|
{
|
|
delete instance;
|
|
}
|
|
}
|
|
|
|
void ClockManager::Initialize()
|
|
{
|
|
if(!instance)
|
|
{
|
|
instance = new ClockManager();
|
|
}
|
|
}
|
|
|
|
ClockManager::ClockManager()
|
|
{
|
|
this->config = Config::CreateDefault();
|
|
this->context = new SysClkContext;
|
|
this->context->applicationId = 0;
|
|
this->context->profile = SysClkProfile_Handheld;
|
|
this->context->enabled = false;
|
|
for(unsigned int i = 0; i < SysClkModule_EnumMax; i++)
|
|
{
|
|
this->context->freqs[i] = 0;
|
|
this->context->overrideFreqs[i] = 0;
|
|
}
|
|
this->running = false;
|
|
this->lastTempLogNs = 0;
|
|
this->lastCsvWriteNs = 0;
|
|
}
|
|
|
|
ClockManager::~ClockManager()
|
|
{
|
|
delete this->config;
|
|
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;
|
|
}
|
|
|
|
bool ClockManager::Running()
|
|
{
|
|
return this->running;
|
|
}
|
|
|
|
void ClockManager::Tick()
|
|
{
|
|
std::scoped_lock lock{this->contextMutex};
|
|
|
|
if(!GameStartBoost())
|
|
{
|
|
bool cpuBoost = FileUtils::IsBoostEnabled() ? IsCpuBoostMode() : false;
|
|
if (this->RefreshContext() || this->config->Refresh())
|
|
{
|
|
std::uint32_t hz = 0;
|
|
std::uint32_t hzForceOverride = 0;
|
|
for (unsigned int module = 0; module < SysClkModule_EnumMax - 1; module++)
|
|
{
|
|
hz = this->context->overrideFreqs[module];
|
|
|
|
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(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)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
bool enabled = this->GetConfig()->Enabled();
|
|
if(enabled != this->context->enabled)
|
|
{
|
|
this->context->enabled = enabled;
|
|
FileUtils::LogLine("[mgr] " TARGET " status: %s", enabled ? "enabled" : "disabled");
|
|
hasChanged = true;
|
|
}
|
|
|
|
std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId();
|
|
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::IsReverseNXSyncEnabled() && (FileUtils::IsReverseNXToolExist() || 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 overrides for CPU are 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 (FileUtils::IsReverseNXSyncEnabled() && (!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 && !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();
|
|
}
|
|
|
|
std::uint32_t hz = 0;
|
|
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
|
{
|
|
hz = Clocks::GetCurrentHz((SysClkModule)module);
|
|
|
|
// Skip MEM freq check
|
|
if (module == SysClkModule_MEM)
|
|
{
|
|
this->context->freqs[module] = hz;
|
|
break;
|
|
}
|
|
|
|
// Round to MHz
|
|
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;
|
|
hasChanged = true;
|
|
}
|
|
|
|
hz = this->GetConfig()->GetOverrideHz((SysClkModule)module);
|
|
if (hz != this->context->overrideFreqs[module])
|
|
{
|
|
if(hz)
|
|
{
|
|
FileUtils::LogLine("[mgr] %s override change: %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
|
}
|
|
else
|
|
{
|
|
FileUtils::LogLine("[mgr] %s override disabled", Clocks::GetModuleName((SysClkModule)module, true));
|
|
Clocks::ResetToStock(module);
|
|
}
|
|
this->context->overrideFreqs[module] = hz;
|
|
hasChanged = true;
|
|
}
|
|
}
|
|
|
|
// temperatures do not and should not force a refresh, hasChanged untouched
|
|
std::uint32_t millis = 0;
|
|
std::uint64_t ns = armTicksToNs(armGetSystemTick());
|
|
std::uint64_t tempLogInterval = this->GetConfig()->GetConfigValue(SysClkConfigValue_TempLogIntervalMs) * 1000000ULL;
|
|
bool shouldLogTemp = tempLogInterval && ((ns - this->lastTempLogNs) > tempLogInterval);
|
|
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
|
{
|
|
millis = Clocks::GetTemperatureMilli((SysClkThermalSensor)sensor);
|
|
if(shouldLogTemp)
|
|
{
|
|
FileUtils::LogLine("[mgr] %s temp: %u.%u °C", Clocks::GetThermalSensorName((SysClkThermalSensor)sensor, true), millis/1000, (millis - millis/1000*1000) / 100);
|
|
}
|
|
this->context->temps[sensor] = millis;
|
|
}
|
|
|
|
if(shouldLogTemp)
|
|
{
|
|
this->lastTempLogNs = ns;
|
|
}
|
|
|
|
std::uint64_t csvWriteInterval = this->GetConfig()->GetConfigValue(SysClkConfigValue_CsvWriteIntervalMs) * 1000000ULL;
|
|
|
|
if(csvWriteInterval && ((ns - this->lastCsvWriteNs) > csvWriteInterval))
|
|
{
|
|
FileUtils::WriteContextToCsv(this->context);
|
|
this->lastCsvWriteNs = ns;
|
|
}
|
|
|
|
return hasChanged;
|
|
}
|
|
|
|
SysClkContext ClockManager::GetCurrentContext()
|
|
{
|
|
std::scoped_lock lock{this->contextMutex};
|
|
return *this->context;
|
|
}
|
|
|
|
Config* ClockManager::GetConfig()
|
|
{
|
|
return this->config;
|
|
}
|