#pragma once #define ALWAYS_INLINE inline __attribute__((always_inline)) #include "SaltyNX.h" #include "Battery.hpp" #include "audsnoop.h" #include "Misc.hpp" #include "max17050.h" #include "tmp451.h" #include "pwm.h" #include #include #include // rgltr_services.cpp (no changes needed here—just compile it once) #include #include "rgltr.h" #include "rgltr_services.h" // for extern Service g_rgltrSrv, etc. #if defined(__cplusplus) extern "C" { #endif #include #if defined(__cplusplus) } #endif #define NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD 0x80044715 #define FieldDescriptor uint32_t #define BASE_SNS_UOHM 5000 static bool fixHiding = false; static bool fixForeground = false; //Common bool isMariko = false; Thread t0; Thread t1; Thread t2; Thread t3; Thread t4; Thread t5; Thread t6; Thread t7; uint64_t systemtickfrequency = 19200000; LEvent threadexit; PwmChannelSession g_ICon; const std::string folderpath = "sdmc:/switch/.overlays/"; // Use const string_view for paths to avoid string copying constexpr const char* directoryPath = "sdmc:/config/status-monitor/"; constexpr const char* configIniPath = "sdmc:/config/status-monitor/config.ini"; constexpr const char* ultrahandConfigIniPath = "sdmc:/config/ultrahand/config.ini"; constexpr const char* teslaConfigIniPath = "sdmc:/config/tesla/config.ini"; std::string filename; std::string filepath; std::string keyCombo = "ZL+ZR+DDOWN"; // default Ultrahand Menu combo //Misc2 MmuRequest nvdecRequest; MmuRequest nvencRequest; MmuRequest nvjpgRequest; //Checks Result clkrstCheck = 1; Result nvCheck = 1; Result pcvCheck = 1; Result i2cCheck = 1; Result pwmCheck = 1; Result tcCheck = 1; Result Hinted = 1; Result pmdmntCheck = 1; Result psmCheck = 1; Result audsnoopCheck = 1; Result nvdecCheck = 1; Result nvencCheck = 1; Result nvjpgCheck = 1; Result nifmCheck = 1; Result sysclkCheck = 0; Result pwmDutyCycleCheck = 1; //Wi-Fi NifmInternetConnectionType NifmConnectionType = (NifmInternetConnectionType)-1; NifmInternetConnectionStatus NifmConnectionStatus = (NifmInternetConnectionStatus)-1; bool Nifm_showpass = false; Result Nifm_internet_rc = -1; Result Nifm_profile_rc = -1; NifmNetworkProfileData_new Nifm_profile = {0}; //Multimedia engines uint32_t NVDEC_Hz = 0; uint32_t NVENC_Hz = 0; uint32_t NVJPG_Hz = 0; //DSP uint32_t DSP_Load_u = -1; //Battery Service* psmService = 0; BatteryChargeInfoFields _batteryChargeInfoFields = {0}; float batCurrentAvg = 0; float batVoltageAvg = 0; float PowerConsumption = 0; int16_t batTimeEstimate = -1; float actualFullBatCapacity = 0; float designedFullBatCapacity = 0; bool batteryFiltered = false; uint8_t batteryTimeLeftRefreshRate = 60; int32_t BatteryTimeCache[120]; //Temperatures float SOC_temperatureF = 0; float PCB_temperatureF = 0; int32_t skin_temperaturemiliC = 0; //CPU Usage //uint64_t idletick0 = systemtickfrequency; //uint64_t idletick1 = systemtickfrequency; //uint64_t idletick2 = systemtickfrequency; //uint64_t idletick3 = systemtickfrequency; std::atomic idletick0{systemtickfrequency}; std::atomic idletick1{systemtickfrequency}; std::atomic idletick2{systemtickfrequency}; std::atomic idletick3{systemtickfrequency}; //Frequency uint32_t CPU_Hz = 0; uint32_t GPU_Hz = 0; uint32_t RAM_Hz = 0; //RAM Size uint64_t RAM_Total_all_u = 0; uint64_t RAM_Total_application_u = 0; uint64_t RAM_Total_applet_u = 0; uint64_t RAM_Total_system_u = 0; uint64_t RAM_Total_systemunsafe_u = 0; uint64_t RAM_Used_all_u = 0; uint64_t RAM_Used_application_u = 0; uint64_t RAM_Used_applet_u = 0; uint64_t RAM_Used_system_u = 0; uint64_t RAM_Used_systemunsafe_u = 0; //Fan double Rotation_Duty = 0; //GPU Usage FieldDescriptor fd = 0; uint32_t GPU_Load_u = 0; bool GPULoadPerFrame = true; //NX-FPS struct resolutionCalls { uint16_t width; uint16_t height; uint16_t calls; }; struct NxFpsSharedBlock { uint32_t MAGIC; uint8_t FPS; float FPSavg; bool pluginActive; uint8_t FPSlocked; uint8_t FPSmode; uint8_t ZeroSync; uint8_t patchApplied; uint8_t API; uint32_t FPSticks[10]; uint8_t Buffers; uint8_t SetBuffers; uint8_t ActiveBuffers; uint8_t SetActiveBuffers; union { struct { bool handheld: 1; bool docked: 1; unsigned int reserved: 6; } NX_PACKED ds; uint8_t general; } displaySync; resolutionCalls renderCalls[8]; resolutionCalls viewportCalls[8]; bool forceOriginalRefreshRate; bool dontForce60InDocked; bool forceSuspend; uint8_t currentRefreshRate; float readSpeedPerSecond; uint8_t FPSlockedDocked; uint64_t frameNumber; } NX_PACKED; NxFpsSharedBlock* NxFps = 0; std::atomic GameRunning{false}; std::atomic check{true}; std::atomic SaltySD{false}; std::atomic realVoltsPolling{false}; uintptr_t FPSaddress = 0; uintptr_t FPSavgaddress = 0; uint64_t PID = 0; uint32_t FPS = 0xFE; float FPSmin = 254; float FPSmax = 0; float FPSavg = 254; float FPSavg_old = 254; bool useOldFPSavg = false; SharedMemory _sharedmemory = {}; std::atomic SharedMemoryUsed{false}; Handle remoteSharedMemory = 1; uint64_t lastFrameNumber = 0; //Read real freqs from sys-clk sysmodule uint32_t realCPU_Hz = 0; uint32_t realGPU_Hz = 0; uint32_t realRAM_Hz = 0; uint32_t partLoad[SysClkPartLoad_EnumMax]; uint32_t realCPU_mV = 0; uint32_t realGPU_mV = 0; uint32_t realVDD2_mV = 0; uint32_t realVDDQ_mV = 0; uint32_t realSOC_mV = 0; uint8_t refreshRate = 0; int compare (const void* elem1, const void* elem2) { if ((((resolutionCalls*)(elem1))->calls) > (((resolutionCalls*)(elem2))->calls)) return -1; else return 1; } void LoadSharedMemoryAndRefreshRate() { if (SaltySD_Connect()) return; SaltySD_GetSharedMemoryHandle(&remoteSharedMemory); SaltySD_GetDisplayRefreshRate(&refreshRate); SaltySD_Term(); shmemLoadRemote(&_sharedmemory, remoteSharedMemory, 0x1000, Perm_Rw); if (!shmemMap(&_sharedmemory)) SharedMemoryUsed = true; else FPS = 1234; } void LoadSharedMemory() { if (SaltySD_Connect()) return; SaltySD_GetSharedMemoryHandle(&remoteSharedMemory); SaltySD_Term(); shmemLoadRemote(&_sharedmemory, remoteSharedMemory, 0x1000, Perm_Rw); if (!shmemMap(&_sharedmemory)) SharedMemoryUsed = true; else FPS = 1234; } void searchSharedMemoryBlock(uintptr_t base) { if (!base || !SharedMemoryUsed) { NxFps = 0; return; } ptrdiff_t search_offset = 0; const uintptr_t memory_end = base + 0x1000; while (search_offset < 0x1000) { const uintptr_t current_addr = base + search_offset; // Ensure we don't read past the end of shared memory if (current_addr + sizeof(NxFpsSharedBlock) > memory_end) { break; } NxFps = (NxFpsSharedBlock*)current_addr; // Add bounds checking and magic validation if (NxFps && current_addr >= base && NxFps->MAGIC == 0x465053) { return; } search_offset += 4; } NxFps = 0; } //Check if SaltyNX is working bool CheckPort() { Handle saltysd; // Try up to 67 times with exponential backoff for better responsiveness for (int i = 0; i < 50; i++) { if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) { svcCloseHandle(saltysd); return true; } // Progressive sleep - start fast, then slow down //if (i < 10) { // svcSleepThread(100'000); // 0.1ms for first 10 attempts //} else if (i < 30) { // svcSleepThread(500'000); // 0.5ms for next 20 attempts //} else { // svcSleepThread(1'000'000); // 1ms for remaining attempts //} } return false; } Mutex mutex_Misc = {0}; void CheckIfGameRunning(void*) { do { mutexLock(&mutex_Misc); if (!check && R_FAILED(pmdmntGetApplicationProcessId(&PID))) { GameRunning = false; check = true; } else if (!GameRunning && SharedMemoryUsed) { const uintptr_t base = (uintptr_t)shmemGetAddr(&_sharedmemory); searchSharedMemoryBlock(base); if (NxFps) { (NxFps->pluginActive) = false; mutexUnlock(&mutex_Misc); // ← Fix: Unlock before return if (leventWait(&threadexit, 100'000'000)) { return; } mutexLock(&mutex_Misc); if ((NxFps->pluginActive)) { GameRunning = true; check = false; } } } mutexUnlock(&mutex_Misc); } while (!leventWait(&threadexit, 1'000'000'000)); } // Utils.hpp or your relevant header static constexpr size_t CACHE_ELEMENTS = sizeof(BatteryTimeCache) / sizeof(BatteryTimeCache[0]); Mutex mutex_BatteryChecker = {0}; void BatteryChecker(void*) { if (R_FAILED(psmCheck) || R_FAILED(i2cCheck)){ return; } uint16_t data = 0; float tempV = 0.0; float tempA = 0.0; size_t ArraySize = 10; if (batteryFiltered) { ArraySize = 1; } float* readingsAmp = new float[ArraySize]; float* readingsVolt = new float[ArraySize]; Max17050ReadReg(MAX17050_AvgCurrent, &data); tempA = (1.5625 / (max17050SenseResistor * max17050CGain)) * (s16)data; for (size_t i = 0; i < ArraySize; i++) { readingsAmp[i] = tempA; } Max17050ReadReg(MAX17050_AvgVCELL, &data); tempV = 0.625 * (data >> 3); for (size_t i = 0; i < ArraySize; i++) { readingsVolt[i] = tempV; } if (!actualFullBatCapacity) { Max17050ReadReg(MAX17050_FullCAP, &data); actualFullBatCapacity = data * (BASE_SNS_UOHM / MAX17050_BOARD_SNS_RESISTOR_UOHM) / MAX17050_BOARD_CGAIN; } if (!designedFullBatCapacity) { Max17050ReadReg(MAX17050_DesignCap, &data); designedFullBatCapacity = data * (BASE_SNS_UOHM / MAX17050_BOARD_SNS_RESISTOR_UOHM) / MAX17050_BOARD_CGAIN; } if (readingsAmp[0] >= 0) { batTimeEstimate = -1; } else { Max17050ReadReg(MAX17050_TTE, &data); float batteryTimeEstimateInMinutes = (5.625 * data) / 60; if (batteryTimeEstimateInMinutes > (99.0*60.0)+59.0) { batTimeEstimate = (99*60)+59; } else batTimeEstimate = (int16_t)batteryTimeEstimateInMinutes; } size_t counter = 0; uint64_t tick_TTE = svcGetSystemTick(); uint64_t nanoseconds = 1000; do { mutexLock(&mutex_BatteryChecker); const uint64_t startTick = svcGetSystemTick(); psmGetBatteryChargeInfoFields(psmService, &_batteryChargeInfoFields); // Calculation is based on Hekate's max17050.c // Source: https://github.com/CTCaer/hekate/blob/master/bdk/power/max17050.c if (!batteryFiltered) { Max17050ReadReg(MAX17050_Current, &data); tempA = (1.5625 / (max17050SenseResistor * max17050CGain)) * (s16)data; Max17050ReadReg(MAX17050_VCELL, &data); tempV = 0.625 * (data >> 3); } else { Max17050ReadReg(MAX17050_AvgCurrent, &data); tempA = (1.5625 / (max17050SenseResistor * max17050CGain)) * (s16)data; Max17050ReadReg(MAX17050_AvgVCELL, &data); tempV = 0.625 * (data >> 3); } if (tempA && tempV) { readingsAmp[counter % ArraySize] = tempA; readingsVolt[counter % ArraySize] = tempV; counter++; } float batCurrent = 0.0; float batVoltage = 0.0; float batPowerAvg = 0.0; for (size_t x = 0; x < ArraySize; x++) { batCurrent += readingsAmp[x]; batVoltage += readingsVolt[x]; batPowerAvg += (readingsAmp[x] * readingsVolt[x]) / 1'000; } batCurrent /= ArraySize; batVoltage /= ArraySize; batCurrentAvg = batCurrent; batVoltageAvg = batVoltage; batPowerAvg /= ArraySize * 1000; PowerConsumption = batPowerAvg; if (batCurrentAvg >= 0) { batTimeEstimate = -1; } else { static float batteryTimeEstimateInMinutes = 0; Max17050ReadReg(MAX17050_TTE, &data); batteryTimeEstimateInMinutes = (5.625 * data) / 60; if (batteryTimeEstimateInMinutes > (99.0*60.0)+59.0) { batteryTimeEstimateInMinutes = (99.0*60.0)+59.0; } static int itr = 0; const int cacheElements = (sizeof(BatteryTimeCache) / sizeof(BatteryTimeCache[0])); BatteryTimeCache[itr++ % cacheElements] = (int32_t)batteryTimeEstimateInMinutes; const uint64_t new_tick_TTE = svcGetSystemTick(); if (armTicksToNs(new_tick_TTE - tick_TTE) / 1'000'000'000 >= batteryTimeLeftRefreshRate) { const size_t to_divide = itr < cacheElements ? itr : cacheElements; batTimeEstimate = (int16_t)(std::accumulate(&BatteryTimeCache[0], &BatteryTimeCache[to_divide], 0) / to_divide); tick_TTE = new_tick_TTE; } } mutexUnlock(&mutex_BatteryChecker); nanoseconds = armTicksToNs(svcGetSystemTick() - startTick); if (nanoseconds < 1'000'000'000 / 2) { nanoseconds = (1'000'000'000 / 2) - nanoseconds; } else { nanoseconds = 1000; } } while(!leventWait(&threadexit, nanoseconds)); batTimeEstimate = -1; _batteryChargeInfoFields = {0}; memset(BatteryTimeCache, 0, sizeof(BatteryTimeCache)); delete[] readingsAmp; delete[] readingsVolt; } void StartBatteryThread() { //if (!skip) { // threadWaitForExit(&t7); // threadClose(&t7); // leventClear(&threadexit); //} leventClear(&threadexit); threadCreate(&t7, BatteryChecker, NULL, NULL, 0x2000, 0x3F, 3); threadStart(&t7); } void CloseBatteryThread() { leventSignal(&threadexit); threadWaitForExit(&t7); threadClose(&t7); } void gpuLoadThread(void*) { #define gpu_samples_average 8 uint32_t gpu_load_array[gpu_samples_average] = {0}; size_t i = 0; if (!GPULoadPerFrame && R_SUCCEEDED(nvCheck)) do { u32 temp; if (R_SUCCEEDED(nvIoctl(fd, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &temp))) { gpu_load_array[i++ % gpu_samples_average] = temp; GPU_Load_u = std::accumulate(&gpu_load_array[0], &gpu_load_array[gpu_samples_average], 0) / gpu_samples_average; } } while(!leventWait(&threadexit, 16'666'000)); } std::string getVersionString() { char buf[0x100] = ""; // 256 bytes — safe for any expected version string Result rc = sysclkIpcGetVersionString(buf, sizeof(buf)); if (R_FAILED(rc) || buf[0] == '\0') { return "unknown"; } return std::string(buf); } bool usingEOS() { const std::string versionString = getVersionString(); return versionString.find("eos") != std::string::npos; } // === ULTRA-FAST VOLTAGE READING === static constexpr PowerDomainId domains[] = { PcvPowerDomainId_Max77621_Cpu, // [0] CPU PcvPowerDomainId_Max77621_Gpu, // [1] GPU PcvPowerDomainId_Max77812_Dram, // [2] VDD2 (EMC/DRAM) PcvPowerDomainId_Max77620_Sd0, // [3] SOC PcvPowerDomainId_Max77620_Sd1 // [4] VDDQ }; // Stuff that doesn't need multithreading void Misc(void*) { const uint64_t timeout_ns = TeslaFPS < 10 ? (1'000'000'000 / TeslaFPS) : 100'000'000; const bool isUsingEOS = usingEOS(); // Initialize voltage reading if needed bool canReadVoltages = false; if (!isUsingEOS && realVoltsPolling) { canReadVoltages = R_SUCCEEDED(rgltrInitialize()); if (!canReadVoltages) { realVoltsPolling = false; } } do { mutexLock(&mutex_Misc); // CPU, GPU and RAM Frequency if (R_SUCCEEDED(clkrstCheck)) { ClkrstSession clkSession; if (R_SUCCEEDED(clkrstOpenSession(&clkSession, PcvModuleId_CpuBus, 3))) { clkrstGetClockRate(&clkSession, &CPU_Hz); clkrstCloseSession(&clkSession); } if (R_SUCCEEDED(clkrstOpenSession(&clkSession, PcvModuleId_GPU, 3))) { clkrstGetClockRate(&clkSession, &GPU_Hz); clkrstCloseSession(&clkSession); } if (R_SUCCEEDED(clkrstOpenSession(&clkSession, PcvModuleId_EMC, 3))) { clkrstGetClockRate(&clkSession, &RAM_Hz); clkrstCloseSession(&clkSession); } } else if (R_SUCCEEDED(pcvCheck)) { pcvGetClockRate(PcvModule_CpuBus, &CPU_Hz); pcvGetClockRate(PcvModule_GPU, &GPU_Hz); pcvGetClockRate(PcvModule_EMC, &RAM_Hz); } // Get sys-clk data SysClkContext sysclkCTX; if (R_SUCCEEDED(sysclkIpcGetCurrentContext(&sysclkCTX))) { realCPU_Hz = sysclkCTX.realFreqs[SysClkModule_CPU]; realGPU_Hz = sysclkCTX.realFreqs[SysClkModule_GPU]; realRAM_Hz = sysclkCTX.realFreqs[SysClkModule_MEM]; partLoad[SysClkPartLoad_EMC] = sysclkCTX.partLoad[SysClkPartLoad_EMC]; partLoad[SysClkPartLoad_EMCCpu] = sysclkCTX.partLoad[SysClkPartLoad_EMCCpu]; realCPU_mV = sysclkCTX.voltages[HocClkVoltage_CPU]; realGPU_mV = sysclkCTX.voltages[HocClkVoltage_GPU]; realVDD2_mV = sysclkCTX.voltages[HocClkVoltage_EMCVDD2]; realVDDQ_mV = sysclkCTX.voltages[HocClkVoltage_EMCVDDQ_MarikoOnly]; realSOC_mV = sysclkCTX.voltages[HocClkVoltage_SOC]; } // Temperatures if (R_SUCCEEDED(i2cCheck)) { Tmp451GetSocTemp(&SOC_temperatureF); Tmp451GetPcbTemp(&PCB_temperatureF); } if (R_SUCCEEDED(tcCheck)) { tcGetSkinTemperatureMilliC(&skin_temperaturemiliC); } // RAM Memory Used if (R_SUCCEEDED(Hinted)) { svcGetSystemInfo(&RAM_Total_application_u, 0, INVALID_HANDLE, 0); svcGetSystemInfo(&RAM_Total_applet_u, 0, INVALID_HANDLE, 1); svcGetSystemInfo(&RAM_Total_system_u, 0, INVALID_HANDLE, 2); svcGetSystemInfo(&RAM_Total_systemunsafe_u, 0, INVALID_HANDLE, 3); svcGetSystemInfo(&RAM_Used_application_u, 1, INVALID_HANDLE, 0); svcGetSystemInfo(&RAM_Used_applet_u, 1, INVALID_HANDLE, 1); svcGetSystemInfo(&RAM_Used_system_u, 1, INVALID_HANDLE, 2); svcGetSystemInfo(&RAM_Used_systemunsafe_u, 1, INVALID_HANDLE, 3); } // Fan if (R_SUCCEEDED(pwmCheck)) { double temp = 0; if (R_SUCCEEDED(pwmChannelSessionGetDutyCycle(&g_ICon, &temp))) { temp *= 10; temp = trunc(temp); temp /= 10; Rotation_Duty = 100.0 - temp; if (Rotation_Duty <= 0) { Rotation_Duty = 0.0000001; } } } // GPU Load if (R_SUCCEEDED(nvCheck) && GPULoadPerFrame) { nvIoctl(fd, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &GPU_Load_u); } // FPS - with proper null checks if (GameRunning) { if (NxFps && SharedMemoryUsed) { FPS = NxFps->FPS; const size_t element_count = sizeof(NxFps->FPSticks) / sizeof(NxFps->FPSticks[0]); FPSavg_old = static_cast(systemtickfrequency) / (std::accumulate(&NxFps->FPSticks[0], &NxFps->FPSticks[element_count], 0.0f) / element_count); const float FPS_in = static_cast(FPS); if (FPSavg_old >= (FPS_in - 0.25f) && FPSavg_old <= (FPS_in + 0.25f)) { FPSavg = FPS_in; } else { FPSavg = FPSavg_old; } lastFrameNumber = NxFps->frameNumber; if (FPSavg > FPSmax) FPSmax = FPSavg; if (FPSavg < FPSmin) FPSmin = FPSavg; } } else { FPSavg = 254; FPSmin = 254; FPSmax = 0; } mutexUnlock(&mutex_Misc); } while (!leventWait(&threadexit, timeout_ns)); // Cleanup voltage reading if initialized if (canReadVoltages) { rgltrExit(); } } void Misc2(void*) { u32 dummy = 0; do { //DSP if (R_SUCCEEDED(audsnoopCheck)) audsnoopGetDspUsage(&DSP_Load_u); //Multimedia clock rates if (R_SUCCEEDED(nvdecCheck)) mmuRequestGet(&nvdecRequest, &NVDEC_Hz); if (R_SUCCEEDED(nvencCheck)) mmuRequestGet(&nvencRequest, &NVENC_Hz); if (R_SUCCEEDED(nvjpgCheck)) mmuRequestGet(&nvjpgRequest, &NVJPG_Hz); if (R_SUCCEEDED(nifmCheck)) { //u32 dummy = 0; Nifm_internet_rc = nifmGetInternetConnectionStatus(&NifmConnectionType, &dummy, &NifmConnectionStatus); if (!Nifm_internet_rc && (NifmConnectionType == NifmInternetConnectionType_WiFi)) Nifm_profile_rc = nifmGetCurrentNetworkProfile((NifmNetworkProfileData*)&Nifm_profile); } } while (!leventWait(&threadexit, 100'000'000)); } void Misc3(void*) { const bool isUsingEOS = usingEOS(); // Initialize voltage reading if needed bool canReadVoltages = false; if (!isUsingEOS && realVoltsPolling) { canReadVoltages = R_SUCCEEDED(rgltrInitialize()); if (!canReadVoltages) { realVoltsPolling = false; } } do { mutexLock(&mutex_Misc); // Temperatures if (R_SUCCEEDED(i2cCheck)) { Tmp451GetSocTemp(&SOC_temperatureF); Tmp451GetPcbTemp(&PCB_temperatureF); } if (R_SUCCEEDED(tcCheck)) { tcGetSkinTemperatureMilliC(&skin_temperaturemiliC); } // Fan if (R_SUCCEEDED(pwmCheck)) { double temp = 0; if (R_SUCCEEDED(pwmChannelSessionGetDutyCycle(&g_ICon, &temp))) { temp *= 10; temp = trunc(temp); temp /= 10; Rotation_Duty = 100.0 - temp; if (Rotation_Duty <= 0) { Rotation_Duty = 0.0000001; } } } // GPU Load if (R_SUCCEEDED(nvCheck)) { nvIoctl(fd, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &GPU_Load_u); } SysClkContext sysclkCTX; if (R_SUCCEEDED(sysclkIpcGetCurrentContext(&sysclkCTX))) { partLoad[SysClkPartLoad_EMC] = sysclkCTX.partLoad[SysClkPartLoad_EMC]; } mutexUnlock(&mutex_Misc); } while (!leventWait(&threadexit, 1'000'000'000)); // 1 second timeout // Cleanup voltage reading if initialized if (canReadVoltages) { rgltrExit(); } } //Check each core for idled ticks in intervals, they cannot read info about other core than they are assigned //In case of getting more than systemtickfrequency in idle, make it equal to systemtickfrequency to get 0% as output and nothing less //This is because making each loop also takes time, which is not considered because this will take also additional time //Check each core for idled ticks in intervals, they cannot read info about other core than they are assigned //In case of getting more than systemtickfrequency in idle, make it equal to systemtickfrequency to get 0% as output and nothing less //This is because making each loop also takes time, which is not considered because this will take also additional time //void CheckCore0(void*) { // uint64_t timeout_ns = 1'000'000'000 / TeslaFPS; // while(true) { // uint64_t idletick_a0 = 0; // uint64_t idletick_b0 = 0; // svcGetInfo(&idletick_b0, InfoType_IdleTickCount, INVALID_HANDLE, 0); // if (leventWait(&threadexit, timeout_ns)) // return; // svcGetInfo(&idletick_a0, InfoType_IdleTickCount, INVALID_HANDLE, 0); // idletick0 = idletick_a0 - idletick_b0; // } //} // //void CheckCore1(void*) { // uint64_t timeout_ns = 1'000'000'000 / TeslaFPS; // while(true) { // uint64_t idletick_a1 = 0; // uint64_t idletick_b1 = 0; // svcGetInfo(&idletick_b1, InfoType_IdleTickCount, INVALID_HANDLE, 1); // if (leventWait(&threadexit, timeout_ns)) // return; // svcGetInfo(&idletick_a1, InfoType_IdleTickCount, INVALID_HANDLE, 1); // idletick1 = idletick_a1 - idletick_b1; // } //} // //void CheckCore2(void*) { // uint64_t timeout_ns = 1'000'000'000 / TeslaFPS; // while(true) { // uint64_t idletick_a2 = 0; // uint64_t idletick_b2 = 0; // svcGetInfo(&idletick_b2, InfoType_IdleTickCount, INVALID_HANDLE, 2); // if (leventWait(&threadexit, timeout_ns)) // return; // svcGetInfo(&idletick_a2, InfoType_IdleTickCount, INVALID_HANDLE, 2); // idletick2 = idletick_a2 - idletick_b2; // } //} // //void CheckCore3(void*) { // uint64_t timeout_ns = 1'000'000'000 / TeslaFPS; // while(true) { // uint64_t idletick_a3 = 0; // uint64_t idletick_b3 = 0; // svcGetInfo(&idletick_b3, InfoType_IdleTickCount, INVALID_HANDLE, 3); // if (leventWait(&threadexit, timeout_ns)) // return; // svcGetInfo(&idletick_a3, InfoType_IdleTickCount, INVALID_HANDLE, 3); // idletick3 = idletick_a3 - idletick_b3; // } //} void CheckCore(void* idletick_ptr) { const uint64_t timeout_ns = 1'000'000'000ULL / TeslaFPS; std::atomic* idletick = (std::atomic*)idletick_ptr; while (true) { uint64_t idletick_a; uint64_t idletick_b; svcGetInfo(&idletick_b, InfoType_IdleTickCount, INVALID_HANDLE, -1); Result rc_break = leventWait(&threadexit, timeout_ns); svcGetInfo(&idletick_a, InfoType_IdleTickCount, INVALID_HANDLE, -1); if (rc_break) return; idletick->store(idletick_a - idletick_b, std::memory_order_release); } } //Start reading all stats void StartThreads() { // Clear the thread exit event for new threads leventClear(&threadexit); threadCreate(&t0, CheckCore, &idletick0, NULL, 0x1000, 0x10, 0); threadCreate(&t1, CheckCore, &idletick1, NULL, 0x1000, 0x10, 1); threadCreate(&t2, CheckCore, &idletick2, NULL, 0x1000, 0x10, 2); threadCreate(&t3, CheckCore, &idletick3, NULL, 0x1000, 0x10, 3); //threadCreate(&t0, CheckCore, &coreIds[0], NULL, 0x1000, 0x10, 0); //threadCreate(&t1, CheckCore, &coreIds[1], NULL, 0x1000, 0x10, 1); //threadCreate(&t2, CheckCore, &coreIds[2], NULL, 0x1000, 0x10, 2); //threadCreate(&t3, CheckCore, &coreIds[3], NULL, 0x1000, 0x10, 3); threadCreate(&t4, Misc, NULL, NULL, 0x4000, 0x3F, -2); threadCreate(&t5, gpuLoadThread, NULL, NULL, 0x1000, 0x3F, -2); threadCreate(&t7, BatteryChecker, NULL, NULL, 0x4000, 0x3F, -2); threadStart(&t0); threadStart(&t1); threadStart(&t2); threadStart(&t3); threadStart(&t4); threadStart(&t5); threadStart(&t7); if (SaltySD) { //Assign NX-FPS to default core threadCreate(&t6, CheckIfGameRunning, NULL, NULL, 0x1000, 0x38, -2); threadStart(&t6); } } //End reading all stats void CloseThreads() { leventSignal(&threadexit); threadWaitForExit(&t0); threadWaitForExit(&t1); threadWaitForExit(&t2); threadWaitForExit(&t3); threadWaitForExit(&t4); threadWaitForExit(&t5); threadWaitForExit(&t6); threadWaitForExit(&t7); threadClose(&t0); threadClose(&t1); threadClose(&t2); threadClose(&t3); threadClose(&t4); threadClose(&t5); threadClose(&t6); threadClose(&t7); } //Separate functions dedicated to "FPS Counter" mode void FPSCounter(void*) { const uint64_t timeout_ns = 1'000'000'000 / TeslaFPS; do { if (GameRunning) { if (SharedMemoryUsed && NxFps) { FPS = (NxFps -> FPS); const size_t element_count = sizeof(NxFps -> FPSticks) / sizeof(NxFps -> FPSticks[0]); FPSavg_old = (float)systemtickfrequency / (std::accumulate(&NxFps->FPSticks[0], &NxFps->FPSticks[element_count], 0) / element_count); const float FPS_in = (float)FPS; if (FPSavg_old >= (FPS_in-0.25) && FPSavg_old <= (FPS_in+0.25)) FPSavg = FPS_in; else FPSavg = FPSavg_old; lastFrameNumber = NxFps -> frameNumber; } } else FPSavg = 254; } while (!leventWait(&threadexit, timeout_ns)); } void StartFPSCounterThread() { leventClear(&threadexit); threadCreate(&t6, CheckIfGameRunning, NULL, NULL, 0x1000, 0x38, -2); threadStart(&t6); threadCreate(&t4, FPSCounter, NULL, NULL, 0x1000, 0x3F, 3); threadStart(&t4); } void EndFPSCounterThread() { leventSignal(&threadexit); threadWaitForExit(&t6); threadWaitForExit(&t4); threadClose(&t6); threadClose(&t4); } void StartInfoThread() { // Clear the thread exit event for new threads leventClear(&threadexit); threadCreate(&t0, CheckCore, &idletick0, NULL, 0x1000, 0x10, 0); threadCreate(&t1, CheckCore, &idletick1, NULL, 0x1000, 0x10, 1); threadCreate(&t2, CheckCore, &idletick2, NULL, 0x1000, 0x10, 2); threadCreate(&t3, CheckCore, &idletick3, NULL, 0x1000, 0x10, 3); //threadCreate(&t1, CheckCore, &coreIds[0], NULL, 0x1000, 0x10, 0); //threadCreate(&t2, CheckCore, &coreIds[1], NULL, 0x1000, 0x10, 1); //threadCreate(&t3, CheckCore, &coreIds[2], NULL, 0x1000, 0x10, 2); //threadCreate(&t4, CheckCore, &coreIds[3], NULL, 0x1000, 0x10, 3); threadCreate(&t7, Misc3, NULL, NULL, 0x1000, 0x3F, -2); threadStart(&t0); threadStart(&t1); threadStart(&t2); threadStart(&t3); threadStart(&t7); } void EndInfoThread() { // Signal the thread exit event leventSignal(&threadexit); // Wait for all threads to exit threadWaitForExit(&t0); threadWaitForExit(&t1); threadWaitForExit(&t2); threadWaitForExit(&t3); threadWaitForExit(&t7); // Close thread handles threadClose(&t0); threadClose(&t1); threadClose(&t2); threadClose(&t3); threadClose(&t7); } // String formatting functions void removeSpaces(std::string& str) { str.erase(std::remove(str.begin(), str.end(), ' '), str.end()); } void convertToUpper(std::string& str) { std::transform(str.begin(), str.end(), str.begin(), ::toupper); } void convertToLower(std::string& str) { std::transform(str.begin(), str.end(), str.begin(), ::tolower); } std::map replaces{ {"A", "\uE0E0"}, {"B", "\uE0E1"}, {"X", "\uE0E2"}, {"Y", "\uE0E3"}, {"L", "\uE0E4"}, {"R", "\uE0E5"}, {"ZL", "\uE0E6"}, {"ZR", "\uE0E7"}, {"SL", "\uE0E8"}, {"SR", "\uE0E9"}, {"DUP", "\uE0EB"}, {"DDOWN", "\uE0EC"}, {"DLEFT", "\uE0ED"}, {"DRIGHT", "\uE0EE"}, {"PLUS", "\uE0EF"}, {"MINUS", "\uE0F0"}, {"LSTICK", "\uE104"}, {"RSTICK", "\uE105"}, {"RS", "\uE105"}, {"LS", "\uE104"} }; void formatButtonCombination(std::string& line) { // Remove all spaces from the line line.erase(std::remove(line.begin(), line.end(), ' '), line.end()); // Replace '+' with ' + ' size_t pos = 0; size_t max_pluses = 3; while ((pos = line.find('+', pos)) != std::string::npos) { if (!max_pluses) { line = line.substr(0, pos); return; } if (pos > 0 && pos < line.size() - 1) { if (std::isalnum(line[pos - 1]) && std::isalnum(line[pos + 1])) { line.replace(pos, 1, " + "); pos += 3; } } ++pos; max_pluses--; } pos = 0; size_t old_pos = 0; static std::string button; while ((pos = line.find(" + ", pos)) != std::string::npos) { button = line.substr(old_pos, pos - old_pos); if (replaces.find(button) != replaces.end()) { line.replace(old_pos, button.length(), replaces[button]); pos = 0; old_pos = 0; } else pos += 3; old_pos = pos; } button = line.substr(old_pos); if (replaces.find(button) != replaces.end()) { line.replace(old_pos, button.length(), replaces[button]); } } //uint64_t comboBitmask = 0; // //constexpr uint64_t MapButtons(const std::string& buttonCombo) { // static std::map buttonMap = { // {"A", HidNpadButton_A}, // {"B", HidNpadButton_B}, // {"X", HidNpadButton_X}, // {"Y", HidNpadButton_Y}, // {"L", HidNpadButton_L}, // {"R", HidNpadButton_R}, // {"ZL", HidNpadButton_ZL}, // {"ZR", HidNpadButton_ZR}, // {"PLUS", HidNpadButton_Plus}, // {"MINUS", HidNpadButton_Minus}, // {"DUP", HidNpadButton_Up}, // {"DDOWN", HidNpadButton_Down}, // {"DLEFT", HidNpadButton_Left}, // {"DRIGHT", HidNpadButton_Right}, // {"SL", HidNpadButton_AnySL}, // {"SR", HidNpadButton_AnySR}, // {"LSTICK", HidNpadButton_StickL}, // {"RSTICK", HidNpadButton_StickR}, // {"LS", HidNpadButton_StickL}, // {"RS", HidNpadButton_StickR}, // {"UP", HidNpadButton_AnyUp}, // {"DOWN", HidNpadButton_AnyDown}, // {"LEFT", HidNpadButton_AnyLeft}, // {"RIGHT", HidNpadButton_AnyRight} // }; // // // std::string comboCopy = buttonCombo; // Make a copy of buttonCombo // // static const std::string delimiter = "+"; // size_t pos = 0; // static std::string button; // size_t max_delimiters = 4; // while ((pos = comboCopy.find(delimiter)) != std::string::npos) { // button = comboCopy.substr(0, pos); // if (buttonMap.find(button) != buttonMap.end()) { // comboBitmask |= buttonMap[button]; // } // comboCopy.erase(0, pos + delimiter.length()); // if (!--max_delimiters) { // return comboBitmask; // } // } // if (buttonMap.find(comboCopy) != buttonMap.end()) { // comboBitmask |= buttonMap[comboCopy]; // } // return comboBitmask; //} ALWAYS_INLINE bool isKeyComboPressed(uint64_t keysHeld, uint64_t keysDown) { // Check if any of the combo buttons are pressed down this frame // while the rest of the combo buttons are being held const uint64_t comboButtonsDown = keysDown & tsl::cfg::launchCombo; const uint64_t comboButtonsHeld = keysHeld & tsl::cfg::launchCombo; // If any combo buttons are pressed down this frame if (comboButtonsDown != 0) { // Check if the remaining combo buttons are being held // (the full combo should be active when combining held + down) const uint64_t totalComboActive = comboButtonsHeld | comboButtonsDown; if (totalComboActive == tsl::cfg::launchCombo) { fixHiding = true; // for fixing hiding when returning //triggerRumbleDoubleClick.store(true, std::memory_order_release); //triggerExitSound.store(true, std::memory_order_release); return true; } } return false; } inline int safeFanDuty(int raw) { if (raw < 0) return 0; if (raw > 100) return 100; return raw; } // Helper function to check if comboBitmask is satisfied with at least one key in keysDown and the rest in keysHeld bool isKeyComboPressed2(uint64_t keysDown, uint64_t keysHeld) { uint64_t requiredKeys = tsl::cfg::launchCombo; bool hasKeyDown = false; // Tracks if at least one key is in keysDown static uint64_t keyBit; // Iterate over each bit in the comboBitmask while (requiredKeys) { keyBit = requiredKeys & ~(requiredKeys - 1); // Get the lowest bit set in requiredKeys // Check if the key is in keysDown or keysHeld if (keysDown & keyBit) { hasKeyDown = true; // Found at least one key in keysDown } else if (!(keysHeld & keyBit)) { return false; // If the key is neither in keysDown nor keysHeld, the combo is incomplete } // Remove the lowest bit and continue to check other keys requiredKeys &= ~keyBit; } // Ensure that at least one key was in keysDown and the rest were in keysHeld return hasKeyDown; } // Custom utility function for parsing an ini file void ParseIniFile() { // Check and create directory if needed //struct stat st; //if (stat(directoryPath, &st) != 0) { // mkdir(directoryPath, 0777); //} ult::createSingleDirectory(directoryPath); // Load main config INI once auto configData = ult::getParsedDataFromIniFile(configIniPath); auto statusIt = configData.find("status-monitor"); if (statusIt != configData.end()) { const auto& statusSection = statusIt->second; std::string key; // Process all settings with direct lookups auto batteryFilterIt = statusSection.find("battery_avg_iir_filter"); if (batteryFilterIt != statusSection.end()) { key = batteryFilterIt->second; convertToUpper(key); batteryFiltered = (key == "TRUE"); } auto refreshRateIt = statusSection.find("battery_time_left_refreshrate"); if (refreshRateIt != statusSection.end()) { batteryTimeLeftRefreshRate = std::clamp(atol(refreshRateIt->second.c_str()), 1L, 60L); } auto gpuLoadIt = statusSection.find("average_gpu_load"); if (gpuLoadIt != statusSection.end()) { key = gpuLoadIt->second; convertToUpper(key); GPULoadPerFrame = (key != "TRUE"); } auto fpsAvgIt = statusSection.find("use_old_fps_average"); if (fpsAvgIt != statusSection.end()) { key = fpsAvgIt->second; convertToUpper(key); useOldFPSavg = (key == "TRUE"); } } // Handle external combo - load each file once const struct { const char* path; const char* section; } externalConfigs[] = { {ultrahandConfigIniPath, "ultrahand"}, {teslaConfigIniPath, "tesla"} }; for (const auto& config : externalConfigs) { auto extConfigData = ult::getParsedDataFromIniFile(config.path); auto sectionIt = extConfigData.find(config.section); if (sectionIt != extConfigData.end()) { auto keyComboIt = sectionIt->second.find("key_combo"); if (keyComboIt != sectionIt->second.end() && !keyComboIt->second.empty()) { keyCombo = keyComboIt->second; removeSpaces(keyCombo); convertToUpper(keyCombo); break; } } } //comboBitmask = MapButtons(keyCombo); } ALWAYS_INLINE bool isValidRGBA4Color(const std::string& hexColor) { const char* data = hexColor.data(); const size_t size = hexColor.size(); static unsigned char c; for (size_t i = 0; i < size; ++i) { c = data[i]; // Branchless hex digit check: (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') if (!((c - '0') <= 9 || (c - 'A') <= 5 || (c - 'a') <= 5)) { return false; } } return true; } bool convertStrToRGBA4444(std::string hexColor, uint16_t* returnValue) { // Check if # is present if (hexColor.size() != 5 || hexColor[0] != '#') return false; hexColor = hexColor.substr(1); if (isValidRGBA4Color(hexColor)) { *returnValue = std::stoi(std::string(hexColor.rbegin(), hexColor.rend()), nullptr, 16); return true; } return false; } struct FullSettings { uint8_t refreshRate; bool setPosRight; bool showRealFreqs; bool realVolts; bool showDeltas; bool showTargetFreqs; bool showFPS; bool showRES; bool showRDSD; bool useDynamicColors; bool disableScreenshots; uint16_t separatorColor; uint16_t catColor1; uint16_t catColor2; uint16_t textColor; }; struct MiniSettings { uint8_t refreshRate; bool realFrequencies; bool realVolts; bool showFullCPU; bool showFullResolution; bool showFanPercentage; bool showSOCVoltage; bool useDynamicColors; bool showVDDQ; bool showVDD2; bool decimalVDD2; bool showDTC; bool useDTCSymbol; std::string dtcFormat; size_t handheldFontSize; size_t dockedFontSize; size_t spacing; uint16_t backgroundColor; uint16_t focusBackgroundColor; uint16_t separatorColor; uint16_t catColor; uint16_t textColor; std::string show; bool showRAMLoad; bool showRAMLoadCPUGPU; bool invertBatteryDisplay; bool disableScreenshots; bool sleepExit; //int setPos; int frameOffsetX; int frameOffsetY; size_t framePadding; }; struct MicroSettings { uint8_t refreshRate; bool realFrequencies; bool realVolts; bool showFullCPU; bool showFullResolution; bool showSOCVoltage; bool useDynamicColors; bool showVDDQ; bool showVDD2; bool decimalVDD2; bool showDTC; bool useDTCSymbol; std::string dtcFormat; bool invertBatteryDisplay; size_t handheldFontSize; size_t dockedFontSize; uint8_t alignTo; uint16_t backgroundColor; uint16_t separatorColor; uint16_t catColor; uint16_t textColor; std::string show; bool showRAMLoad; bool setPosBottom; bool disableScreenshots; bool sleepExit; }; struct FpsCounterSettings { uint8_t refreshRate; size_t handheldFontSize; size_t dockedFontSize; uint16_t backgroundColor; uint16_t focusBackgroundColor; uint16_t textColor; //int setPos; bool useIntegerCounter; bool disableScreenshots; int frameOffsetX; int frameOffsetY; size_t framePadding; }; struct FpsGraphSettings { bool showInfo; uint8_t refreshRate; uint16_t backgroundColor; uint16_t focusBackgroundColor; uint16_t fpsColor; uint16_t mainLineColor; uint16_t roundedLineColor; uint16_t perfectLineColor; uint16_t dashedLineColor; uint16_t borderColor; uint16_t maxFPSTextColor; uint16_t minFPSTextColor; uint16_t textColor; uint16_t catColor; //int setPos; bool useDynamicColors; bool disableScreenshots; int frameOffsetX; int frameOffsetY; size_t framePadding; }; struct ResolutionSettings { uint8_t refreshRate; uint16_t backgroundColor; uint16_t focusBackgroundColor; uint16_t catColor; //uint16_t catColor2; uint16_t textColor; //int setPos; bool disableScreenshots; int frameOffsetX; int frameOffsetY; size_t framePadding; }; ALWAYS_INLINE void GetConfigSettings(MiniSettings* settings) { // Initialize defaults settings->realFrequencies = true; settings->realVolts = true; settings->showFullCPU = false; settings->showFullResolution = true; settings->showFanPercentage = true; settings->useDynamicColors = true; settings->showFullCPU = false; settings->showSOCVoltage = false; settings->showVDDQ = false; settings->showVDD2 = true; settings->decimalVDD2 = false; settings->showDTC = true; settings->useDTCSymbol = true; settings->dtcFormat = "%m-%d-%Y%H:%M:%S";//"%Y-%m-%d %I:%M:%S %p"; settings->handheldFontSize = 15; settings->dockedFontSize = 15; settings->spacing = 8; convertStrToRGBA4444("#0009", &(settings->backgroundColor)); convertStrToRGBA4444("#000F", &(settings->focusBackgroundColor)); convertStrToRGBA4444("#888F", &(settings->separatorColor)); convertStrToRGBA4444("#2DFF", &(settings->catColor)); convertStrToRGBA4444("#FFFF", &(settings->textColor)); settings->show = "DTC+BAT+CPU+GPU+RAM+TMP+FPS+RES"; settings->showRAMLoad = true; settings->showRAMLoadCPUGPU = false; settings->invertBatteryDisplay = true; settings->refreshRate = 1; settings->disableScreenshots = false; settings->sleepExit = false; //settings->setPos = 0; settings->frameOffsetX = 10; settings->frameOffsetY = 10; settings->framePadding = 10; // Open and read file efficiently FILE* configFile = fopen(configIniPath, "r"); if (!configFile) return; fseek(configFile, 0, SEEK_END); const long fileSize = ftell(configFile); fseek(configFile, 0, SEEK_SET); std::string fileData; fileData.resize(fileSize); fread(fileData.data(), 1, fileSize, configFile); fclose(configFile); auto parsedData = ult::parseIni(fileData); // Cache section lookup auto sectionIt = parsedData.find("mini"); if (sectionIt == parsedData.end()) return; std::string key; uint16_t temp; const auto& section = sectionIt->second; // Process refresh_rate auto it = section.find("refresh_rate"); if (it != section.end()) { settings->refreshRate = std::clamp(atol(it->second.c_str()), 1L, 60L); } // Process boolean flags it = section.find("real_freqs"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->realFrequencies = (key == "TRUE"); } it = section.find("real_volts"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->realVolts = (key == "TRUE"); } // Process font sizes with shared bounds static constexpr long minFontSize = 8; static constexpr long maxFontSize = 22; it = section.find("handheld_font_size"); if (it != section.end()) { settings->handheldFontSize = std::clamp(atol(it->second.c_str()), minFontSize, maxFontSize); } it = section.find("docked_font_size"); if (it != section.end()) { settings->dockedFontSize = std::clamp(atol(it->second.c_str()), minFontSize, maxFontSize); } it = section.find("spacing"); if (it != section.end()) { settings->spacing = atol(it->second.c_str()); } // Process colors it = section.find("background_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->backgroundColor = temp; } it = section.find("focus_background_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->focusBackgroundColor = temp; } it = section.find("separator_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->separatorColor = temp; } it = section.find("cat_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->catColor = temp; } it = section.find("text_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->textColor = temp; } // Process RAM load flag it = section.find("show_full_cpu"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showFullCPU = !(key == "FALSE"); } it = section.find("show_full_res"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showFullResolution = !(key == "FALSE"); } it = section.find("show_soc_voltage"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showSOCVoltage = !(key == "FALSE"); } it = section.find("use_dynamic_colors"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->useDynamicColors = (key == "TRUE"); } it = section.find("show_vddq"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showVDDQ = !(key == "FALSE"); } it = section.find("show_vdd2"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showVDD2 = !(key == "FALSE"); } it = section.find("decimal_vdd2"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->decimalVDD2 = !(key == "FALSE"); } it = section.find("show_dtc"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showDTC = !(key == "FALSE"); } it = section.find("use_dtc_symbol"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->useDTCSymbol = !(key == "FALSE"); } it = section.find("dtc_format"); if (it != section.end()) { key = it->second; settings->dtcFormat = std::move(key); } // Process show string it = section.find("show"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->show = std::move(key); } // Process RAM load flag it = section.find("replace_MB_with_RAM_load"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showRAMLoad = (key != "FALSE"); } // Process CPU/GPU RAM load flag it = section.find("show_RAM_load_CPU_GPU"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showRAMLoadCPUGPU = (key != "FALSE"); } // Invert the battery display value it = section.find("invert_battery_display"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->invertBatteryDisplay = (key != "FALSE"); } // Process disable screenshots it = section.find("disable_screenshots"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->disableScreenshots = (key != "FALSE"); } // Process exit on sleep it = section.find("sleep_exit"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->sleepExit = (key != "FALSE"); } // Process alignment settings //it = section.find("layer_width_align"); //if (it != section.end()) { // key = it->second; // convertToUpper(key); // if (key == "CENTER") { // settings->setPos = 1; // } else if (key == "RIGHT") { // settings->setPos = 2; // } //} // //it = section.find("layer_height_align"); //if (it != section.end()) { // key = it->second; // convertToUpper(key); // if (key == "CENTER") { // settings->setPos += 3; // } else if (key == "BOTTOM") { // settings->setPos += 6; // } //} it = section.find("frame_offset_x"); if (it != section.end()) { settings->frameOffsetX = atol(it->second.c_str()); } it = section.find("frame_offset_y"); if (it != section.end()) { settings->frameOffsetY = atol(it->second.c_str()); } it = section.find("frame_padding"); if (it != section.end()) { settings->framePadding = atol(it->second.c_str()); } } ALWAYS_INLINE void GetConfigSettings(MicroSettings* settings) { // Initialize defaults settings->realFrequencies = true; settings->realVolts = true; settings->showFullCPU = false; settings->showFullResolution = false; settings->showSOCVoltage = true; settings->useDynamicColors = true; settings->showVDDQ = false; settings->showVDD2 = true; settings->decimalVDD2 = false; settings->showDTC = true; settings->useDTCSymbol = true; settings->dtcFormat = "%H:%M:%S";//"%Y-%m-%d %I:%M:%S %p"; settings->invertBatteryDisplay = false; settings->handheldFontSize = 15; settings->dockedFontSize = 15; settings->alignTo = 1; // CENTER convertStrToRGBA4444("#0009", &(settings->backgroundColor)); convertStrToRGBA4444("#888F", &(settings->separatorColor)); convertStrToRGBA4444("#2DFF", &(settings->catColor)); convertStrToRGBA4444("#FFFF", &(settings->textColor)); settings->show = "FPS+CPU+GPU+RAM+SOC+BAT+DTC"; settings->showRAMLoad = true; settings->setPosBottom = false; settings->disableScreenshots = false; settings->sleepExit = false; settings->refreshRate = 1; // Open and read file efficiently FILE* configFile = fopen(configIniPath, "r"); if (!configFile) return; fseek(configFile, 0, SEEK_END); const long fileSize = ftell(configFile); fseek(configFile, 0, SEEK_SET); std::string fileData; fileData.resize(fileSize); fread(fileData.data(), 1, fileSize, configFile); fclose(configFile); auto parsedData = ult::parseIni(fileData); // Cache section lookup auto sectionIt = parsedData.find("micro"); if (sectionIt == parsedData.end()) return; std::string key; uint16_t temp; const auto& section = sectionIt->second; // Process refresh_rate auto it = section.find("refresh_rate"); if (it != section.end()) { settings->refreshRate = std::clamp(atol(it->second.c_str()), 1L, 60L); } // Process boolean flags it = section.find("real_freqs"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->realFrequencies = (key == "TRUE"); } it = section.find("real_volts"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->realVolts = (key == "TRUE"); } it = section.find("show_full_cpu"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showFullCPU = (key == "TRUE"); } it = section.find("show_full_res"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showFullResolution = (key == "TRUE"); } it = section.find("show_soc_voltage"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showSOCVoltage = !(key == "FALSE"); } it = section.find("use_dynamic_colors"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->useDynamicColors = (key == "TRUE"); } it = section.find("show_vddq"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showVDDQ = !(key == "FALSE"); } it = section.find("show_vdd2"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showVDD2 = !(key == "FALSE"); } it = section.find("decimal_vdd2"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->decimalVDD2 = !(key == "FALSE"); } it = section.find("show_dtc"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showDTC = !(key == "FALSE"); } it = section.find("use_dtc_symbol"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->useDTCSymbol = !(key == "FALSE"); } it = section.find("dtc_format"); if (it != section.end()) { key = it->second; settings->dtcFormat = std::move(key); } // Invert the battery display value it = section.find("invert_battery_display"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->invertBatteryDisplay = (key != "FALSE"); } // Process font sizes with shared bounds static constexpr long minFontSize = 8; static constexpr long maxFontSize = 18; it = section.find("handheld_font_size"); if (it != section.end()) { settings->handheldFontSize = std::clamp(atol(it->second.c_str()), minFontSize, maxFontSize); } it = section.find("docked_font_size"); if (it != section.end()) { settings->dockedFontSize = std::clamp(atol(it->second.c_str()), minFontSize, maxFontSize); } // Process colors it = section.find("background_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->backgroundColor = temp; } it = section.find("separator_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->separatorColor = temp; } it = section.find("cat_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->catColor = temp; } it = section.find("text_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->textColor = temp; } // Process text alignment it = section.find("text_align"); if (it != section.end()) { key = it->second; convertToUpper(key); if (key == "LEFT") { settings->alignTo = 0; } else if (key == "CENTER") { settings->alignTo = 1; } else if (key == "RIGHT") { settings->alignTo = 2; } } // Process RAM load flag it = section.find("replace_GB_with_RAM_load"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showRAMLoad = (key != "FALSE"); } // Process show string it = section.find("show"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->show = std::move(key); } // Process layer height alignment it = section.find("layer_height_align"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->setPosBottom = (key == "BOTTOM"); } // Process disable screenshots it = section.find("disable_screenshots"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->disableScreenshots = (key != "FALSE"); } // Process exit on sleep it = section.find("sleep_exit"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->sleepExit = (key != "FALSE"); } } ALWAYS_INLINE void GetConfigSettings(FpsCounterSettings* settings) { // Initialize defaults settings->handheldFontSize = 40; settings->dockedFontSize = 40; convertStrToRGBA4444("#0009", &(settings->backgroundColor)); convertStrToRGBA4444("#000F", &(settings->focusBackgroundColor)); convertStrToRGBA4444("#8CFF", &(settings->textColor)); //settings->setPos = 0; settings->refreshRate = 30; settings->useIntegerCounter = false; settings->disableScreenshots = false; settings->frameOffsetX = 10; settings->frameOffsetY = 10; settings->framePadding = 10; // Open and read file efficiently FILE* configFile = fopen(configIniPath, "r"); if (!configFile) return; fseek(configFile, 0, SEEK_END); const long fileSize = ftell(configFile); fseek(configFile, 0, SEEK_SET); std::string fileData; fileData.resize(fileSize); fread(fileData.data(), 1, fileSize, configFile); fclose(configFile); auto parsedData = ult::parseIni(fileData); // Cache section lookup auto sectionIt = parsedData.find("fps-counter"); if (sectionIt == parsedData.end()) return; std::string key; uint16_t temp; const auto& section = sectionIt->second; // Process font sizes with shared bounds static constexpr long minFontSize = 8; static constexpr long maxFontSize = 150; auto it = section.find("handheld_font_size"); if (it != section.end()) { settings->handheldFontSize = std::clamp(atol(it->second.c_str()), minFontSize, maxFontSize); } it = section.find("docked_font_size"); if (it != section.end()) { settings->dockedFontSize = std::clamp(atol(it->second.c_str()), minFontSize, maxFontSize); } // Process colors it = section.find("background_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->backgroundColor = temp; } it = section.find("focus_background_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->focusBackgroundColor = temp; } it = section.find("text_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->textColor = temp; } // Process alignment settings //it = section.find("layer_width_align"); //if (it != section.end()) { // key = it->second; // convertToUpper(key); // if (key == "CENTER") { // settings->setPos = 1; // } else if (key == "RIGHT") { // settings->setPos = 2; // } //} //it = section.find("layer_height_align"); //if (it != section.end()) { // key = it->second; // convertToUpper(key); // if (key == "CENTER") { // settings->setPos += 3; // } else if (key == "BOTTOM") { // settings->setPos += 6; // } //} it = section.find("use_integer_counter"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->useIntegerCounter = (key != "FALSE"); } // Process disable screenshots it = section.find("disable_screenshots"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->disableScreenshots = (key != "FALSE"); } it = section.find("frame_offset_x"); if (it != section.end()) { settings->frameOffsetX = atol(it->second.c_str()); } it = section.find("frame_offset_y"); if (it != section.end()) { settings->frameOffsetY = atol(it->second.c_str()); } it = section.find("frame_padding"); if (it != section.end()) { settings->framePadding = atol(it->second.c_str()); } } ALWAYS_INLINE void GetConfigSettings(FpsGraphSettings* settings) { // Initialize defaults settings->showInfo = true; //settings->setPos = 0; convertStrToRGBA4444("#0009", &(settings->backgroundColor)); convertStrToRGBA4444("#000F", &(settings->focusBackgroundColor)); convertStrToRGBA4444("#888C", &(settings->fpsColor)); convertStrToRGBA4444("#2DFF", &(settings->borderColor)); convertStrToRGBA4444("#8888", &(settings->dashedLineColor)); convertStrToRGBA4444("#FFFF", &(settings->maxFPSTextColor)); convertStrToRGBA4444("#FFFF", &(settings->minFPSTextColor)); convertStrToRGBA4444("#FFFF", &(settings->mainLineColor)); convertStrToRGBA4444("#F0FF", &(settings->roundedLineColor)); convertStrToRGBA4444("#0C0F", &(settings->perfectLineColor)); convertStrToRGBA4444("#FFFF", &(settings->textColor)); convertStrToRGBA4444("#0F0F", &(settings->catColor)); settings->refreshRate = 30; settings->useDynamicColors = true; settings->disableScreenshots = false; settings->frameOffsetX = 10; settings->frameOffsetY = 10; settings->framePadding = 10; // Open and read file efficiently FILE* configFile = fopen(configIniPath, "r"); if (!configFile) return; fseek(configFile, 0, SEEK_END); const long fileSize = ftell(configFile); fseek(configFile, 0, SEEK_SET); std::string fileData; fileData.resize(fileSize); fread(fileData.data(), 1, fileSize, configFile); fclose(configFile); auto parsedData = ult::parseIni(fileData); // Cache section lookup auto sectionIt = parsedData.find("fps-graph"); if (sectionIt == parsedData.end()) return; std::string key; uint16_t temp; const auto& section = sectionIt->second; // Process alignment settings //auto it = section.find("layer_width_align"); //if (it != section.end()) { // key = it->second; // convertToUpper(key); // if (key == "CENTER") { // settings->setPos = 1; // } else if (key == "RIGHT") { // settings->setPos = 2; // } //} // //it = section.find("layer_height_align"); //if (it != section.end()) { // key = it->second; // convertToUpper(key); // if (key == "CENTER") { // settings->setPos += 3; // } else if (key == "BOTTOM") { // settings->setPos += 6; // } //} // Process show_info boolean auto it = section.find("show_info"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showInfo = (key == "TRUE"); } it = section.find("use_dynamic_colors"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->useDynamicColors = (key == "TRUE"); } // Process disable screenshots it = section.find("disable_screenshots"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->disableScreenshots = (key != "FALSE"); } it = section.find("frame_offset_x"); if (it != section.end()) { settings->frameOffsetX = atol(it->second.c_str()); } it = section.find("frame_offset_y"); if (it != section.end()) { settings->frameOffsetY = atol(it->second.c_str()); } it = section.find("frame_padding"); if (it != section.end()) { settings->framePadding = atol(it->second.c_str()); } // Process colors - using a struct for cleaner code struct ColorMapping { const char* key; uint16_t* target; }; const ColorMapping colorMappings[] = { {"min_fps_text_color", &settings->minFPSTextColor}, {"max_fps_text_color", &settings->maxFPSTextColor}, {"background_color", &settings->backgroundColor}, {"focus_background_color", &settings->focusBackgroundColor}, {"fps_counter_color", &settings->fpsColor}, {"border_color", &settings->borderColor}, {"dashed_line_color", &settings->dashedLineColor}, {"main_line_color", &settings->mainLineColor}, {"rounded_line_color", &settings->roundedLineColor}, {"perfect_line_color", &settings->perfectLineColor}, {"text_color", &settings->textColor}, {"cat_color", &settings->catColor} }; for (const auto& mapping : colorMappings) { it = section.find(mapping.key); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) *(mapping.target) = temp; } } } ALWAYS_INLINE void GetConfigSettings(FullSettings* settings) { // Initialize defaults settings->setPosRight = false; settings->refreshRate = 1; settings->showRealFreqs = true; settings->showDeltas = true; settings->showTargetFreqs = true; settings->showFPS = true; settings->showRES = true; settings->showRDSD = true; settings->useDynamicColors = true; settings->disableScreenshots = false; convertStrToRGBA4444("#888F", &(settings->separatorColor)); convertStrToRGBA4444("#8FFF", &(settings->catColor1)); convertStrToRGBA4444("#8CFF", &(settings->catColor2)); convertStrToRGBA4444("#FFFF", &(settings->textColor)); // Open and read file efficiently FILE* configFile = fopen(configIniPath, "r"); if (!configFile) return; fseek(configFile, 0, SEEK_END); const long fileSize = ftell(configFile); fseek(configFile, 0, SEEK_SET); std::string fileData; fileData.resize(fileSize); fread(fileData.data(), 1, fileSize, configFile); fclose(configFile); auto parsedData = ult::parseIni(fileData); // Cache section lookup auto sectionIt = parsedData.find("full"); if (sectionIt == parsedData.end()) return; std::string key; uint16_t temp; const auto& section = sectionIt->second; // Process refresh_rate auto it = section.find("refresh_rate"); if (it != section.end()) { settings->refreshRate = std::clamp(atol(it->second.c_str()), 1L, 60L); } // Process layer position it = section.find("layer_width_align"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->setPosRight = (key == "RIGHT"); } // Process boolean flags it = section.find("show_real_freqs"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showRealFreqs = !(key == "FALSE"); } it = section.find("show_deltas"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showDeltas = !(key == "FALSE"); } it = section.find("show_target_freqs"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showTargetFreqs = !(key == "FALSE"); } it = section.find("show_fps"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showFPS = !(key == "FALSE"); } it = section.find("show_res"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showRES = !(key == "FALSE"); } it = section.find("show_read_speed"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->showRDSD = !(key == "FALSE"); } it = section.find("use_dynamic_colors"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->useDynamicColors = (key == "TRUE"); } // Process disable screenshots it = section.find("disable_screenshots"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->disableScreenshots = (key != "FALSE"); } it = section.find("separator_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->separatorColor = temp; } it = section.find("cat_color_1"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->catColor1 = temp; } it = section.find("cat_color_2"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->catColor2 = temp; } it = section.find("text_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->textColor = temp; } } ALWAYS_INLINE void GetConfigSettings(ResolutionSettings* settings) { // Initialize defaults convertStrToRGBA4444("#0009", &(settings->backgroundColor)); convertStrToRGBA4444("#000F", &(settings->focusBackgroundColor)); convertStrToRGBA4444("#8FFF", &(settings->catColor)); //convertStrToRGBA4444("#8CFF", &(settings->catColor2)); convertStrToRGBA4444("#FFFF", &(settings->textColor)); settings->refreshRate = 10; //ettings->setPos = 0; settings->disableScreenshots = false; settings->frameOffsetX = 10; settings->frameOffsetY = 10; settings->framePadding = 10; // Open and read file efficiently FILE* configFile = fopen(configIniPath, "r"); if (!configFile) return; fseek(configFile, 0, SEEK_END); const long fileSize = ftell(configFile); fseek(configFile, 0, SEEK_SET); std::string fileData; fileData.resize(fileSize); fread(fileData.data(), 1, fileSize, configFile); fclose(configFile); auto parsedData = ult::parseIni(fileData); // Cache section lookup auto sectionIt = parsedData.find("game_resolutions"); if (sectionIt == parsedData.end()) return; std::string key; const auto& section = sectionIt->second; // Process refresh_rate auto it = section.find("refresh_rate"); if (it != section.end()) { settings->refreshRate = std::clamp(atol(it->second.c_str()), 1L, 60L); } uint16_t temp; // Process colors it = section.find("background_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->backgroundColor = temp; } it = section.find("focus_background_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->focusBackgroundColor = temp; } it = section.find("cat_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->catColor = temp; } //it = section.find("cat_color_2"); //if (it != section.end()) { // temp = 0; // if (convertStrToRGBA4444(it->second, &temp)) // settings->catColor2 = temp; //} it = section.find("text_color"); if (it != section.end()) { temp = 0; if (convertStrToRGBA4444(it->second, &temp)) settings->textColor = temp; } it = section.find("frame_offset_x"); if (it != section.end()) { settings->frameOffsetX = atol(it->second.c_str()); } it = section.find("frame_offset_y"); if (it != section.end()) { settings->frameOffsetY = atol(it->second.c_str()); } it = section.find("frame_padding"); if (it != section.end()) { settings->framePadding = atol(it->second.c_str()); } // Process alignment settings //it = section.find("layer_width_align"); //if (it != section.end()) { // key = it->second; // convertToUpper(key); // if (key == "CENTER") { // settings->setPos = 1; // } else if (key == "RIGHT") { // settings->setPos = 2; // } //} // //it = section.find("layer_height_align"); //if (it != section.end()) { // key = it->second; // convertToUpper(key); // if (key == "CENTER") { // settings->setPos += 3; // } else if (key == "BOTTOM") { // settings->setPos += 6; // } //} // Process disable screenshots it = section.find("disable_screenshots"); if (it != section.end()) { key = it->second; convertToUpper(key); settings->disableScreenshots = (key != "FALSE"); } }