diff --git a/include/config.hpp b/include/config.hpp index 08971481..9c2c9517 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -9,6 +9,10 @@ struct EmulatorConfig { bool discordRpcEnabled = false; RendererType rendererType = RendererType::OpenGL; + bool chargerPlugged = true; + // Default to 3% battery to make users suffer + int batteryPercentage = 3; + EmulatorConfig(const std::filesystem::path& path); void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 163b19e1..9fedd161 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -6,6 +6,7 @@ #include #include +#include "config.hpp" #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" @@ -171,7 +172,7 @@ private: void readDirectory(u32 messagePointer, Handle directory); public: - Kernel(CPU& cpu, Memory& mem, GPU& gpu); + Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config); void initializeFS() { return serviceManager.initializeFS(); } void setVersion(u8 major, u8 minor); void serviceSVC(u32 svc); diff --git a/include/memory.hpp b/include/memory.hpp index 2ae4cb28..4ad3d982 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -5,9 +5,11 @@ #include #include #include + +#include "config.hpp" #include "crypto/aes_engine.hpp" -#include "helpers.hpp" #include "handles.hpp" +#include "helpers.hpp" #include "loader/ncsd.hpp" #include "services/region_codes.hpp" #include "services/shared_font.hpp" @@ -154,13 +156,14 @@ private: static constexpr FirmwareInfo firm{.unk = 0, .revision = 0, .minor = 0x34, .major = 2, .syscoreVer = 2, .sdkVer = 0x0000F297}; // Adjusted upon loading a ROM based on the ROM header. Used by CFG::SecureInfoGetArea to get past region locks Regions region = Regions::USA; + const EmulatorConfig& config; public: u16 kernelVersion = 0; u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore) u32 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore) - Memory(u64& cpuTicks); + Memory(u64& cpuTicks, const EmulatorConfig& config); void reset(); void* getReadPointer(u32 address); void* getWritePointer(u32 address); diff --git a/include/services/ptm.hpp b/include/services/ptm.hpp index 73bb5431..62fa6f65 100644 --- a/include/services/ptm.hpp +++ b/include/services/ptm.hpp @@ -1,4 +1,5 @@ #pragma once +#include "config.hpp" #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" @@ -10,6 +11,8 @@ class PTMService { Memory& mem; MAKE_LOG_FUNCTION(log, ptmLogger) + const EmulatorConfig& config; + // Service commands void configureNew3DSCPU(u32 messagePointer); void getAdapterState(u32 messagePointer); @@ -18,7 +21,7 @@ class PTMService { void getTotalStepCount(u32 messagePointer); public: - PTMService(Memory& mem) : mem(mem) {} + PTMService(Memory& mem, const EmulatorConfig& config) : mem(mem), config(config) {} void reset(); void handleSyncRequest(u32 messagePointer); diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 42287bac..823ad5fa 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -33,6 +33,7 @@ #include "services/ssl.hpp" #include "services/y2r.hpp" +struct EmulatorConfig; // More circular dependencies!! class Kernel; @@ -80,7 +81,7 @@ class ServiceManager { void subscribe(u32 messagePointer); public: - ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel); + ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config); void reset(); void initializeFS() { fs.initializeFilesystem(); } void handleSyncRequest(u32 messagePointer); diff --git a/src/config.cpp b/src/config.cpp index 6a961dbe..680ce6a8 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1,5 +1,6 @@ #include "config.hpp" +#include #include #include @@ -57,6 +58,19 @@ void EmulatorConfig::load(const std::filesystem::path& path) { shaderJitEnabled = toml::find_or(gpu, "EnableShaderJIT", true); } } + + if (data.contains("Battery")) { + auto batteryResult = toml::expect(data.at("Battery")); + if (batteryResult.is_ok()) { + auto battery = batteryResult.unwrap(); + + chargerPlugged = toml::find_or(battery, "ChargerPlugged", true); + batteryPercentage = toml::find_or(battery, "BatteryPercentage", 3); + + // Clamp battery % to [0, 100] to make sure it's a valid value + batteryPercentage = std::clamp(batteryPercentage, 0, 100); + } + } } void EmulatorConfig::save(const std::filesystem::path& path) { @@ -81,6 +95,9 @@ void EmulatorConfig::save(const std::filesystem::path& path) { data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType)); + data["Battery"]["ChargerPlugged"] = chargerPlugged; + data["Battery"]["BatteryPercentage"] = batteryPercentage; + std::ofstream file(path, std::ios::out); file << data; file.close(); diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 900ad559..837d20dd 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -3,8 +3,8 @@ #include "kernel_types.hpp" #include "cpu.hpp" -Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu) - : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this) { +Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config) + : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this, config) { objects.reserve(512); // Make room for a few objects to avoid further memory allocs later mutexHandles.reserve(8); portHandles.reserve(32); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 7346feec..0eb76bbc 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -6,10 +6,11 @@ #include "config_mem.hpp" #include "resource_limits.hpp" +#include "services/ptm.hpp" using namespace KernelMemoryTypes; -Memory::Memory(u64& cpuTicks) : cpuTicks(cpuTicks) { +Memory::Memory(u64& cpuTicks, const EmulatorConfig& config) : cpuTicks(cpuTicks), config(config) { fcram = new uint8_t[FCRAM_SIZE](); dspRam = new uint8_t[DSP_RAM_SIZE](); @@ -85,7 +86,18 @@ u8 Memory::read8(u32 vaddr) { return *(u8*)(pointer + offset); } else { switch (vaddr) { - case ConfigMem::BatteryState: return getBatteryState(true, true, BatteryLevel::FourBars); + case ConfigMem::BatteryState: { + // Set by the PTM module + // Charger plugged: Shows whether the charger is plugged + // Charging: Shows whether the charger is plugged and the console is actually charging, ie the battery is not full + // BatteryLevel: A battery level calculated via PTM::GetBatteryLevel + // These are all assembled into a bitfield and returned via config memory + const bool chargerPlugged = config.chargerPlugged; + const bool charging = config.chargerPlugged && (config.batteryPercentage < 100); + const auto batteryLevel = static_cast(PTMService::batteryPercentToLevel(config.batteryPercentage)); + + return getBatteryState(chargerPlugged, charging, batteryLevel); + } case ConfigMem::EnvInfo: return envInfo; case ConfigMem::HardwareType: return ConfigMem::HardwareCodes::Product; case ConfigMem::KernelVersionMinor: return u8(kernelVersion & 0xff); diff --git a/src/core/services/ptm.cpp b/src/core/services/ptm.cpp index a0ec9e96..57fbf638 100644 --- a/src/core/services/ptm.cpp +++ b/src/core/services/ptm.cpp @@ -27,20 +27,18 @@ void PTMService::handleSyncRequest(u32 messagePointer) { void PTMService::getAdapterState(u32 messagePointer) { log("PTM::GetAdapterState\n"); - constexpr bool adapterConnected = true; // Pretend the 3DS is charging cause why not mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write8(messagePointer + 8, adapterConnected ? 1 : 0); + mem.write8(messagePointer + 8, config.chargerPlugged ? 1 : 0); } void PTMService::getBatteryLevel(u32 messagePointer) { log("PTM::GetBatteryLevel"); - constexpr u8 batteryPercent = 3; // 3% battery so users can suffer mem.write32(messagePointer, IPC::responseHeader(0x7, 2, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write8(messagePointer + 8, batteryPercentToLevel(batteryPercent)); + mem.write8(messagePointer + 8, batteryPercentToLevel(config.batteryPercentage)); } void PTMService::getStepHistory(u32 messagePointer) { diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 02f9d569..042d5124 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -5,10 +5,10 @@ #include "ipc.hpp" #include "kernel.hpp" -ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) +ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel), - gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), ptm(mem), soc(mem), + gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16; diff --git a/src/emulator.cpp b/src/emulator.cpp index 9825cd47..c667e13a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -12,8 +12,8 @@ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1; #endif Emulator::Emulator() - : config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config), - memory(cpu.getTicksRef()), cheats(memory, kernel.getServiceManager().getHID()), running(false), programRunning(false) + : config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu, config), cpu(memory, kernel), gpu(memory, config), + memory(cpu.getTicksRef(), config), cheats(memory, kernel.getServiceManager().getHID()), running(false), programRunning(false) #ifdef PANDA3DS_ENABLE_HTTP_SERVER , httpServer(this) #endif