Files
Horizon-OC/Source/sys-clk-OC/sysmodule/src/clock_manager.cpp
KazushiM de9f7b7478 - Sys-clk Fix: "Do not override" in overlay now works as intended
- Sys-clk Fix: Now you can completely disable ReverseNX profile syncing by removing "ReverseNX_sync.flag"
2021-08-31 23:24:17 +08:00

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;
}