diff --git a/CMakeLists.txt b/CMakeLists.txt index 02617f157..c38a6af2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,6 +326,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services src/core/services/ir_user.cpp src/core/services/http.cpp src/core/services/soc.cpp src/core/services/ssl.cpp src/core/services/news_u.cpp src/core/services/amiibo_device.cpp src/core/services/csnd.cpp src/core/services/nwm_uds.cpp src/core/services/fonts.cpp + src/core/services/ns.cpp ) set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp src/core/PICA/shader_interpreter.cpp src/core/PICA/dynapica/shader_rec.cpp @@ -389,7 +390,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp - include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp + include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp ) cmrc_add_resource_library( diff --git a/include/ipc.hpp b/include/ipc.hpp index 67a8897ed..07a77f029 100644 --- a/include/ipc.hpp +++ b/include/ipc.hpp @@ -2,8 +2,19 @@ #include namespace IPC { + namespace BufferType { + enum : std::uint32_t { + Send = 1, + Receive = 2, + }; + } + constexpr std::uint32_t responseHeader(std::uint32_t commandID, std::uint32_t normalResponses, std::uint32_t translateResponses) { // TODO: Maybe validate the response count stuff fits in 6 bits return (commandID << 16) | (normalResponses << 6) | translateResponses; } -} \ No newline at end of file + + constexpr std::uint32_t pointerHeader(std::uint32_t index, std::uint32_t size, std::uint32_t type) { + return (size << 14) | (index << 10) | (type << 1); + } +} // namespace IPC \ No newline at end of file diff --git a/include/kernel/config_mem.hpp b/include/kernel/config_mem.hpp index 81f0fef10..7d1890a0f 100644 --- a/include/kernel/config_mem.hpp +++ b/include/kernel/config_mem.hpp @@ -8,6 +8,7 @@ namespace ConfigMem { KernelVersionMajor = 0x1FF80003, SyscoreVer = 0x1FF80010, EnvInfo = 0x1FF80014, + PrevFirm = 0x1FF80016, AppMemAlloc = 0x1FF80040, FirmUnknown = 0x1FF80060, FirmRevision = 0x1FF80061, @@ -30,6 +31,11 @@ namespace ConfigMem { // Shows what type of hardware we're running on namespace HardwareCodes { - enum : u8 { Product = 1, Devboard = 2, Debugger = 3, Capture = 4 }; + enum : u8 { + Product = 1, + Devboard = 2, + Debugger = 3, + Capture = 4, + }; } } // namespace ConfigMem diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index 45400837f..158c75016 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -20,6 +20,7 @@ namespace KernelHandles { CFG_U, // CFG service (Console & region info) CFG_I, CFG_S, // Used by most system apps in lieu of cfg:u + CFG_NOR, // Used by system settings app CSND, // Plays audio directly from PCM samples DLP_SRVR, // Download Play: Server. Used for network play. DSP, // DSP service (Used for audio decoding and output) @@ -38,11 +39,14 @@ namespace KernelHandles { NIM, // Updates, DLC, etc NDM, // ????? NS_S, // Nintendo Shell service + NWM_EXT, // ????? NWM_UDS, // Local multiplayer - NEWS_U, // This service literally has 1 command (AddNotification) and I don't even understand what it does + NEWS_S, // news:u on steroids + NEWS_U, // This service literally has 1 command (AddNotification) PTM_U, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state) PTM_SYSM, // PTM system service PTM_PLAY, // PTM Play service, used for retrieving play history + PTM_GETS, // PTM RTC service (GetSystemTime) SOC, // Socket service SSL, // SSL service (Totally didn't expect that) Y2R, // Also does camera stuff @@ -82,6 +86,8 @@ namespace KernelHandles { case CECD: return "CECD"; case CFG_U: return "CFG:U"; case CFG_I: return "CFG:I"; + case CFG_S: return "CFG:S"; + case CFG_NOR: return "CFG:NOR"; case CSND: return "CSND"; case DSP: return "DSP"; case DLP_SRVR: return "DLP::SRVR"; @@ -97,13 +103,16 @@ namespace KernelHandles { case MCU_HWC: return "MCU::HWC"; case MIC: return "MIC"; case NDM: return "NDM"; + case NEWS_S: return "NEWS_S"; case NEWS_U: return "NEWS_U"; + case NWM_EXT: return "nwm::EXT"; case NWM_UDS: return "nwm::UDS"; case NFC: return "NFC"; case NIM: return "NIM"; case PTM_U: return "PTM:U"; case PTM_SYSM: return "PTM:SYSM"; case PTM_PLAY: return "PTM:PLAY"; + case PTM_GETS: return "PTM:GETS"; case SOC: return "SOC"; case SSL: return "SSL"; case Y2R: return "Y2R"; diff --git a/include/logger.hpp b/include/logger.hpp index 4fc521b6c..626025fa4 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -65,6 +65,7 @@ namespace Log { static Logger nwmUdsLogger; static Logger nimLogger; static Logger ndmLogger; + static Logger nsLogger; static Logger ptmLogger; static Logger socLogger; static Logger sslLogger; diff --git a/include/services/ac.hpp b/include/services/ac.hpp index 56acd4362..02775e185 100644 --- a/include/services/ac.hpp +++ b/include/services/ac.hpp @@ -19,6 +19,7 @@ class ACService { void closeAsync(u32 messagePointer); void createDefaultConfig(u32 messagePointer); void getConnectingInfraPriority(u32 messagePointer); + void getNZoneBeaconNotFoundEvent(u32 messagePointer); void getStatus(u32 messagePointer); void getLastErrorCode(u32 messagePointer); void getWifiStatus(u32 messagePointer); diff --git a/include/services/boss.hpp b/include/services/boss.hpp index edc50dee4..bf5cd88de 100644 --- a/include/services/boss.hpp +++ b/include/services/boss.hpp @@ -14,10 +14,14 @@ class BOSSService { // Service commands void cancelTask(u32 messagePointer); + void deleteNsData(u32 messagePointer); void initializeSession(u32 messagePointer); + void getAppNewFlag(u32 messagePointer); void getErrorCode(u32 messagePointer); + void getNsDataHeaderInfo(u32 messagePointer); void getNewArrivalFlag(u32 messagePointer); void getNsDataIdList(u32 messagePointer, u32 commandWord); + void getNsDataLastUpdated(u32 messagePointer); void getOptoutFlag(u32 messagePointer); void getStorageEntryInfo(u32 messagePointer); // Unknown what this is, name taken from Citra void getTaskIdList(u32 messagePointer); @@ -26,12 +30,15 @@ class BOSSService { void getTaskState(u32 messagePointer); void getTaskStatus(u32 messagePointer); void getTaskStorageInfo(u32 messagePointer); + void readNsData(u32 messagePointer); void receiveProperty(u32 messagePointer); void registerNewArrivalEvent(u32 messagePointer); void registerStorageEntry(u32 messagePointer); void registerTask(u32 messagePointer); void sendProperty(u32 messagePointer); + void setAppNewFlag(u32 messagePointer); void setOptoutFlag(u32 messagePointer); + void startBgImmediate(u32 messagePointer); void startTask(u32 messagePointer); void unregisterStorage(u32 messagePointer); void unregisterTask(u32 messagePointer); diff --git a/include/services/cfg.hpp b/include/services/cfg.hpp index e2ddffa81..82c8153a0 100644 --- a/include/services/cfg.hpp +++ b/include/services/cfg.hpp @@ -18,7 +18,7 @@ class CFGService { // Service functions void getConfigInfoBlk2(u32 messagePointer); - void getConfigInfoBlk8(u32 messagePointer); + void getConfigInfoBlk8(u32 messagePointer, u32 commandWord); void getCountryCodeID(u32 messagePointer); void getLocalFriendCodeSeed(u32 messagePointer); void getRegionCanadaUSA(u32 messagePointer); @@ -26,7 +26,14 @@ class CFGService { void genUniqueConsoleHash(u32 messagePointer); void secureInfoGetByte101(u32 messagePointer); void secureInfoGetRegion(u32 messagePointer); + void setConfigInfoBlk4(u32 messagePointer); + void updateConfigNANDSavegame(u32 messagePointer); void translateCountryInfo(u32 messagePointer); + void isFangateSupported(u32 messagePointer); + + // cfg:nor functions + void norInitialize(u32 messagePointer); + void norReadData(u32 messagePointer); void getConfigInfo(u32 output, u32 blockID, u32 size, u32 permissionMask); diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index 8294565b1..4ac8e7472 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -121,4 +121,4 @@ class GPUService { std::memset(ptr, 0, 0x1000); } } -}; +}; \ No newline at end of file diff --git a/include/services/gsp_lcd.hpp b/include/services/gsp_lcd.hpp index f34f59aba..7dbdae8fe 100644 --- a/include/services/gsp_lcd.hpp +++ b/include/services/gsp_lcd.hpp @@ -6,15 +6,13 @@ #include "result/result.hpp" class LCDService { - using Handle = HorizonHandle; - - Handle handle = KernelHandles::LCD; Memory& mem; MAKE_LOG_FUNCTION(log, gspLCDLogger) // Service commands + void setLedForceOff(u32 messagePointer); -public: + public: LCDService(Memory& mem) : mem(mem) {} void reset(); void handleSyncRequest(u32 messagePointer); diff --git a/include/services/ns.hpp b/include/services/ns.hpp new file mode 100644 index 000000000..cb00e49b0 --- /dev/null +++ b/include/services/ns.hpp @@ -0,0 +1,25 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" +#include "result/result.hpp" + +class NSService { + Memory& mem; + MAKE_LOG_FUNCTION(log, nsLogger) + + // Service commands + void launchTitle(u32 messagePointer); + + public: + enum class Type { + S, // ns:s + P, // ns:p + C, // ns:c + }; + + NSService(Memory& mem) : mem(mem) {} + void reset(); + void handleSyncRequest(u32 messagePointer, Type type); +}; diff --git a/include/services/ptm.hpp b/include/services/ptm.hpp index 5b797a1df..331fba378 100644 --- a/include/services/ptm.hpp +++ b/include/services/ptm.hpp @@ -24,6 +24,7 @@ class PTMService { public: enum class Type { + GETS, // ptm:gets U, // ptm:u SYSM, // ptm:sysm PLAY, // ptm:play diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 4fa1e6654..c777408c6 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -28,10 +28,11 @@ #include "services/mcu/mcu_hwc.hpp" #include "services/mic.hpp" #include "services/ndm.hpp" -#include "services/nwm_uds.hpp" #include "services/news_u.hpp" #include "services/nfc.hpp" #include "services/nim.hpp" +#include "services/ns.hpp" +#include "services/nwm_uds.hpp" #include "services/ptm.hpp" #include "services/soc.hpp" #include "services/ssl.hpp" @@ -52,11 +53,11 @@ class ServiceManager { MAKE_LOG_FUNCTION(log, srvLogger) - ACService ac; + ACService ac; ACTService act; - AMService am; + AMService am; APTService apt; - BOSSService boss; + BOSSService boss; CAMService cam; CECDService cecd; CFGService cfg; @@ -76,7 +77,8 @@ class ServiceManager { NewsUService news_u; NFCService nfc; NwmUdsService nwm_uds; - NIMService nim; + NIMService nim; + NSService ns; PTMService ptm; SOCService soc; SSLService ssl; diff --git a/src/core/services/ac.cpp b/src/core/services/ac.cpp index 8f5545fed..f3cb5e835 100644 --- a/src/core/services/ac.cpp +++ b/src/core/services/ac.cpp @@ -1,4 +1,5 @@ #include "services/ac.hpp" + #include "ipc.hpp" namespace ACCommands { @@ -10,6 +11,7 @@ namespace ACCommands { GetStatus = 0x000C0000, GetWifiStatus = 0x000D0000, GetConnectingInfraPriority = 0x000F0000, + GetNZoneBeaconNotFoundEvent = 0x002F0004, RegisterDisconnectEvent = 0x00300004, IsConnected = 0x003E0042, SetClientVersion = 0x00400042, @@ -29,12 +31,17 @@ void ACService::handleSyncRequest(u32 messagePointer) { case ACCommands::CreateDefaultConfig: createDefaultConfig(messagePointer); break; case ACCommands::GetConnectingInfraPriority: getConnectingInfraPriority(messagePointer); break; case ACCommands::GetLastErrorCode: getLastErrorCode(messagePointer); break; + case ACCommands::GetNZoneBeaconNotFoundEvent: getNZoneBeaconNotFoundEvent(messagePointer); break; case ACCommands::GetStatus: getStatus(messagePointer); break; case ACCommands::GetWifiStatus: getWifiStatus(messagePointer); break; case ACCommands::IsConnected: isConnected(messagePointer); break; case ACCommands::RegisterDisconnectEvent: registerDisconnectEvent(messagePointer); break; case ACCommands::SetClientVersion: setClientVersion(messagePointer); break; - default: Helpers::panic("AC service requested. Command: %08X\n", command); + + default: + mem.write32(messagePointer + 4, Result::Success); + Helpers::warn("AC service requested. Command: %08X\n", command); + break; } } @@ -72,7 +79,7 @@ void ACService::getLastErrorCode(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x0A, 2, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, 0); // Hopefully this means no error? + mem.write32(messagePointer + 8, 0); // Hopefully this means no error? } void ACService::getConnectingInfraPriority(u32 messagePointer) { @@ -136,4 +143,13 @@ void ACService::registerDisconnectEvent(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x30, 1, 0)); mem.write32(messagePointer + 4, Result::Success); +} + +void ACService::getNZoneBeaconNotFoundEvent(u32 messagePointer) { + const u32 processID = mem.read32(messagePointer + 8); + const Handle event = mem.read32(messagePointer + 16); + log("AC::GetNZoneBeaconNotFoundEvent (process ID = %X, event = %X) (stubbed)\n", processID, event); + + mem.write32(messagePointer, IPC::responseHeader(0x2F, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/boss.cpp b/src/core/services/boss.cpp index cb8fdf818..cf9cbe369 100644 --- a/src/core/services/boss.cpp +++ b/src/core/services/boss.cpp @@ -1,4 +1,5 @@ #include "services/boss.hpp" + #include "ipc.hpp" namespace BOSSCommands { @@ -25,27 +26,36 @@ namespace BOSSCommands { GetTaskState = 0x00200082, GetTaskStatus = 0x002300C2, GetTaskInfo = 0x00250082, + DeleteNsData = 0x00260040, + GetNsDataHeaderInfo = 0x002700C2, + ReadNsData = 0x00280102, + GetNsDataLastUpdated = 0x002D0040, GetErrorCode = 0x002E0040, RegisterStorageEntry = 0x002F0140, GetStorageEntryInfo = 0x00300000, + StartBgImmediate = 0x00330042, + InitializeSessionPrivileged = 0x04010082, + GetAppNewFlag = 0x04040080, + SetAppNewFlag = 0x040500C0, // Probably }; } -void BOSSService::reset() { - optoutFlag = 0; -} +void BOSSService::reset() { optoutFlag = 0; } void BOSSService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { case BOSSCommands::CancelTask: cancelTask(messagePointer); break; + case BOSSCommands::DeleteNsData: deleteNsData(messagePointer); break; + case BOSSCommands::GetAppNewFlag: getAppNewFlag(messagePointer); break; case BOSSCommands::GetErrorCode: getErrorCode(messagePointer); break; + case BOSSCommands::GetNsDataHeaderInfo: getNsDataHeaderInfo(messagePointer); break; case BOSSCommands::GetNewArrivalFlag: getNewArrivalFlag(messagePointer); break; case BOSSCommands::GetNsDataIdList: case BOSSCommands::GetNsDataIdList1: case BOSSCommands::GetNsDataIdList2: - case BOSSCommands::GetNsDataIdList3: - getNsDataIdList(messagePointer, command); break; + case BOSSCommands::GetNsDataIdList3: getNsDataIdList(messagePointer, command); break; + case BOSSCommands::GetNsDataLastUpdated: getNsDataLastUpdated(messagePointer); break; case BOSSCommands::GetOptoutFlag: getOptoutFlag(messagePointer); break; case BOSSCommands::GetStorageEntryInfo: getStorageEntryInfo(messagePointer); break; case BOSSCommands::GetTaskIdList: getTaskIdList(messagePointer); break; @@ -54,17 +64,31 @@ void BOSSService::handleSyncRequest(u32 messagePointer) { case BOSSCommands::GetTaskState: getTaskState(messagePointer); break; case BOSSCommands::GetTaskStatus: getTaskStatus(messagePointer); break; case BOSSCommands::GetTaskStorageInfo: getTaskStorageInfo(messagePointer); break; - case BOSSCommands::InitializeSession: initializeSession(messagePointer); break; + case BOSSCommands::InitializeSession: + case BOSSCommands::InitializeSessionPrivileged: initializeSession(messagePointer); break; + case BOSSCommands::ReadNsData: readNsData(messagePointer); break; case BOSSCommands::ReceiveProperty: receiveProperty(messagePointer); break; case BOSSCommands::RegisterNewArrivalEvent: registerNewArrivalEvent(messagePointer); break; case BOSSCommands::RegisterStorageEntry: registerStorageEntry(messagePointer); break; case BOSSCommands::RegisterTask: registerTask(messagePointer); break; case BOSSCommands::SendProperty: sendProperty(messagePointer); break; + case BOSSCommands::SetAppNewFlag: setAppNewFlag(messagePointer); break; case BOSSCommands::SetOptoutFlag: setOptoutFlag(messagePointer); break; + case BOSSCommands::StartBgImmediate: startBgImmediate(messagePointer); break; case BOSSCommands::StartTask: startTask(messagePointer); break; case BOSSCommands::UnregisterStorage: unregisterStorage(messagePointer); break; case BOSSCommands::UnregisterTask: unregisterTask(messagePointer); break; - default: Helpers::panic("BOSS service requested. Command: %08X\n", command); + + case 0x04500102: // Home Menu uses this command, what is this? + Helpers::warn("BOSS command 0x04500102"); + mem.write32(messagePointer, IPC::responseHeader(0x450, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); + break; + + default: + mem.write32(messagePointer + 4, Result::Success); + Helpers::warn("BOSS service requested. Command: %08X\n", command); + break; } } @@ -99,7 +123,7 @@ void BOSSService::getTaskState(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); mem.write8(messagePointer + 8, 0); // TaskStatus: Report the task finished successfully mem.write32(messagePointer + 12, 0); // Current state value for task PropertyID 0x4 - mem.write8(messagePointer + 16, 0); // TODO: Figure out what this should be + mem.write8(messagePointer + 16, 0); // TODO: Figure out what this should be } void BOSSService::getTaskStatus(u32 messagePointer) { @@ -150,15 +174,15 @@ void BOSSService::getErrorCode(u32 messagePointer) { log("BOSS::GetErrorCode (stubbed)\n"); mem.write32(messagePointer, IPC::responseHeader(0x2E, 2, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, Result::Success); // No error code + mem.write32(messagePointer + 8, Result::Success); // No error code } void BOSSService::getStorageEntryInfo(u32 messagePointer) { log("BOSS::GetStorageEntryInfo (undocumented)\n"); mem.write32(messagePointer, IPC::responseHeader(0x30, 3, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, 0); // u32, unknown meaning - mem.write16(messagePointer + 12, 0); // s16, unknown meaning + mem.write32(messagePointer + 8, 0); // u32, unknown meaning + mem.write16(messagePointer + 12, 0); // s16, unknown meaning } void BOSSService::sendProperty(u32 messagePointer) { @@ -173,7 +197,6 @@ void BOSSService::sendProperty(u32 messagePointer) { // TODO: Should this do anything else? } - void BOSSService::receiveProperty(u32 messagePointer) { const u32 id = mem.read32(messagePointer + 4); const u32 size = mem.read32(messagePointer + 8); @@ -182,13 +205,13 @@ void BOSSService::receiveProperty(u32 messagePointer) { log("BOSS::ReceiveProperty (id = %d, size = %08X, ptr = %08X) (stubbed)\n", id, size, ptr); mem.write32(messagePointer, IPC::responseHeader(0x16, 2, 2)); mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, 0); // Read size + mem.write32(messagePointer + 8, 0); // Read size } // This seems to accept a KEvent as a parameter and register it for something Spotpass related // I need to update the 3DBrew page when it's known what it does properly void BOSSService::registerNewArrivalEvent(u32 messagePointer) { - const Handle eventHandle = mem.read32(messagePointer + 4); // Kernel event handle to register + const Handle eventHandle = mem.read32(messagePointer + 4); // Kernel event handle to register log("BOSS::RegisterNewArrivalEvent (handle = %X)\n", eventHandle); mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0)); @@ -252,5 +275,92 @@ void BOSSService::getNewArrivalFlag(u32 messagePointer) { log("BOSS::GetNewArrivalFlag (stubbed)\n"); mem.write32(messagePointer, IPC::responseHeader(0x7, 2, 0)); mem.write32(messagePointer + 4, Result::Success); - mem.write8(messagePointer + 8, 0); // Flag + mem.write8(messagePointer + 8, 0); // Flag +} + +void BOSSService::startBgImmediate(u32 messagePointer) { + const u32 size = mem.read32(messagePointer + 8); + const u32 taskIDs = mem.read32(messagePointer + 12); + log("BOSS::StartBgImmediate (size = %X, task ID pointer = %X) (stubbed)\n", size, taskIDs); + + mem.write32(messagePointer, IPC::responseHeader(0x33, 1, 2)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, IPC::pointerHeader(0, size, IPC::BufferType::Send)); + mem.write32(messagePointer + 12, taskIDs); +} + +void BOSSService::getAppNewFlag(u32 messagePointer) { + const u64 appID = mem.read64(messagePointer + 4); + log("BOSS::GetAppNewFlag (app ID = %llX)\n", appID); + + mem.write32(messagePointer, IPC::responseHeader(0x404, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write8(messagePointer + 8, 0); // No new content +} + +void BOSSService::getNsDataHeaderInfo(u32 messagePointer) { + const u32 nsDataID = mem.read32(messagePointer + 4); + const u8 type = mem.read8(messagePointer + 8); + const u32 size = mem.read32(messagePointer + 12); + const u32 nsDataHeaderInfo = mem.read32(messagePointer + 20); + log("BOSS::GetNsDataHeaderInfo (NS data ID = %X, type = %X, size = %X, NS data header info pointer = %X) (stubbed)\n", nsDataID, type, size, + nsDataHeaderInfo); + + switch (type) { + case 3: + case 5: mem.write32(nsDataHeaderInfo, 0); break; // ?? + + default: Helpers::panic("Unimplemented NS data header info type %X", type); + } + + mem.write32(messagePointer, IPC::responseHeader(0x27, 1, 2)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, IPC::pointerHeader(0, size, IPC::BufferType::Receive)); + mem.write32(messagePointer + 12, nsDataHeaderInfo); +} + +void BOSSService::getNsDataLastUpdated(u32 messagePointer) { + const u32 nsDataID = mem.read32(messagePointer + 4); + log("BOSS::GetNsDataLastUpdated (NS data ID = %X) (stubbed)\n", nsDataID); + + mem.write32(messagePointer, IPC::responseHeader(0x2D, 3, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write64(messagePointer + 8, 0); // Milliseconds since last update? +} + +void BOSSService::readNsData(u32 messagePointer) { + const u32 nsDataID = mem.read32(messagePointer + 4); + const s64 offset = mem.read64(messagePointer + 8); + const u32 size = mem.read32(messagePointer + 20); + const u32 data = mem.read32(messagePointer + 24); + log("BOSS::ReadNsData (NS data ID = %X, offset = %llX, size = %X, data pointer = %X) (stubbed)\n", nsDataID, offset, size, data); + + for (u32 i = 0; i < size; i++) { + mem.write8(data + i, 0); + } + + mem.write32(messagePointer, IPC::responseHeader(0x28, 3, 2)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, size); // Technically how many bytes have been read + mem.write32(messagePointer + 12, 0); // ?? + mem.write32(messagePointer + 16, IPC::pointerHeader(0, size, IPC::BufferType::Receive)); + mem.write32(messagePointer + 20, data); +} + +void BOSSService::deleteNsData(u32 messagePointer) { + const u32 nsDataID = mem.read32(messagePointer + 4); + log("BOSS::DeleteNsData (NS data ID = %X) (stubbed)\n", nsDataID); + + mem.write32(messagePointer, IPC::responseHeader(0x26, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +// Judging by the inputs and command number, this could very well be a "SetAppNewFlag" +void BOSSService::setAppNewFlag(u32 messagePointer) { + const u64 appID = mem.read64(messagePointer + 4); + const u8 flag = mem.read32(messagePointer + 12); + log("BOSS::SetAppNewFlag (app ID = %llX, flag = %X)\n", appID, flag); + + mem.write32(messagePointer, IPC::responseHeader(0x405, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/cfg.cpp b/src/core/services/cfg.cpp index 0b5f6437b..68d76f7a5 100644 --- a/src/core/services/cfg.cpp +++ b/src/core/services/cfg.cpp @@ -17,40 +17,80 @@ namespace CFGCommands { GetRegionCanadaUSA = 0x00040000, GetSystemModel = 0x00050000, TranslateCountryInfo = 0x00080080, - GetCountryCodeID = 0x000A0040, + GetCountryCodeID = 0x000A0040, + IsFangateSupported = 0x000B0000, + SetConfigInfoBlk4 = 0x04020082, + UpdateConfigNANDSavegame = 0x04030000, GetLocalFriendCodeSeed = 0x04050000, SecureInfoGetByte101 = 0x04070000, }; } +// cfg:i commands +namespace CFGICommands { + enum : u32 { + GetConfigInfoBlk8 = 0x08010082, + }; +} + +// cfg:nor commands +namespace NORCommands { + enum : u32 { + Initialize = 0x00010040, + ReadData = 0x00050082, + }; +} + void CFGService::reset() {} void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) { const u32 command = mem.read32(messagePointer); - switch (command) { - case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break; - case CFGCommands::GetCountryCodeID: getCountryCodeID(messagePointer); break; - case CFGCommands::GetRegionCanadaUSA: getRegionCanadaUSA(messagePointer); break; - case CFGCommands::GetSystemModel: getSystemModel(messagePointer); break; - case CFGCommands::GenHashConsoleUnique: genUniqueConsoleHash(messagePointer); break; - case CFGCommands::SecureInfoGetRegion: secureInfoGetRegion(messagePointer); break; - case CFGCommands::TranslateCountryInfo: translateCountryInfo(messagePointer); break; - - default: - if (type == Type::S) { - // cfg:s-only functions - switch (command) { - case CFGCommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer); break; - case CFGCommands::GetLocalFriendCodeSeed: getLocalFriendCodeSeed(messagePointer); break; - case CFGCommands::SecureInfoGetByte101: secureInfoGetByte101(messagePointer); break; - default: Helpers::panic("CFG:S service requested. Command: %08X\n", command); + + if (type != Type::NOR) { + switch (command) { + case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break; + case CFGCommands::GetCountryCodeID: getCountryCodeID(messagePointer); break; + case CFGCommands::GetRegionCanadaUSA: getRegionCanadaUSA(messagePointer); break; + case CFGCommands::GetSystemModel: getSystemModel(messagePointer); break; + case CFGCommands::GenHashConsoleUnique: genUniqueConsoleHash(messagePointer); break; + case CFGCommands::IsFangateSupported: isFangateSupported(messagePointer); break; + case CFGCommands::SecureInfoGetRegion: secureInfoGetRegion(messagePointer); break; + case CFGCommands::TranslateCountryInfo: translateCountryInfo(messagePointer); break; + + default: + if (type == Type::S) { + // cfg:s (and cfg:i) functions only functions + switch (command) { + case CFGCommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer, command); break; + case CFGCommands::GetLocalFriendCodeSeed: getLocalFriendCodeSeed(messagePointer); break; + case CFGCommands::SecureInfoGetByte101: secureInfoGetByte101(messagePointer); break; + case CFGCommands::SetConfigInfoBlk4: setConfigInfoBlk4(messagePointer); break; + case CFGCommands::UpdateConfigNANDSavegame: updateConfigNANDSavegame(messagePointer); break; + + default: Helpers::panic("CFG:S service requested. Command: %08X\n", command); + } + } else if (type == Type::I) { + switch (command) { + case CFGCommands::GetConfigInfoBlk8: + case CFGICommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer, command); break; + + default: Helpers::panic("CFG:I service requested. Command: %08X\n", command); + } + } else { + Helpers::panic("CFG service requested. Command: %08X\n", command); } - } else { - Helpers::panic("CFG service requested. Command: %08X\n", command); - } - break; + break; + } + } else { + // cfg:nor functions + switch (command) { + case NORCommands::Initialize: norInitialize(messagePointer); break; + case NORCommands::ReadData: norReadData(messagePointer); break; + + default: Helpers::panic("CFG:NOR service requested. Command: %08X\n", command); + } } } @@ -84,14 +124,14 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } -void CFGService::getConfigInfoBlk8(u32 messagePointer) { +void CFGService::getConfigInfoBlk8(u32 messagePointer, u32 commandWord) { u32 size = mem.read32(messagePointer + 4); u32 blockID = mem.read32(messagePointer + 8); u32 output = mem.read32(messagePointer + 16); // Pointer to write the output data to log("CFG::GetConfigInfoBlk8 (size = %X, block ID = %X, output pointer = %08X\n", size, blockID, output); getConfigInfo(output, blockID, size, 0x8); - mem.write32(messagePointer, IPC::responseHeader(0x401, 1, 2)); + mem.write32(messagePointer, IPC::responseHeader(commandWord >> 16, 1, 2)); mem.write32(messagePointer + 4, Result::Success); } @@ -160,6 +200,37 @@ void CFGService::getConfigInfo(u32 output, u32 blockID, u32 size, u32 permission mem.write32(output, 0); } else if (size == 8 && blockID == 0x00090000) { mem.write64(output, 0); // Some sort of key used with nwm::UDS::InitializeWithVersion + } else if (size == 4 && blockID == 0x110000) { + mem.write32(output, 1); // According to 3Dbrew, 0 means system setup is required + } else if (size == 2 && blockID == 0x50001) { + // Backlight controls. Values taken from Citra + mem.write8(output, 0); + mem.write8(output + 1, 2); + } else if (size == 8 && blockID == 0x50009) { + // N3DS Backlight controls? + mem.write64(output, 0); + } else if (size == 4 && blockID == 0x180000) { + // Infrared LED related? + mem.write32(output, 0); + } else if (size == 1 && blockID == 0xE0000) { + mem.write8(output, 0); + } else if ((size == 512 && blockID == 0xC0002) || (size == 148 && blockID == 0x100001)) { + // CTR parental controls block (0xC0002) and TWL parental controls block (0x100001) + for (u32 i = 0; i < size; i++) { + mem.write8(output + i, 0); + } + } else if (size == 2 && blockID == 0x100000) { + // EULA agreed + mem.write8(output, 1); // We have agreed to the EULA + mem.write8(output + 1, 1); // EULA version = 1 + } else if (size == 1 && blockID == 0x100002) { + Helpers::warn("Unimplemented TWL country code access"); + mem.write8(output, 0); + } else if (size == 24 && blockID == 0x180001) { + // QTM calibration data + for (u32 i = 0; i < size; i++) { + mem.write8(output + i, 0); + } } else { Helpers::panic("Unhandled GetConfigInfoBlk2 configuration. Size = %d, block = %X", size, blockID); } @@ -260,6 +331,25 @@ void CFGService::getLocalFriendCodeSeed(u32 messagePointer) { mem.write64(messagePointer + 8, 0); } +void CFGService::setConfigInfoBlk4(u32 messagePointer) { + u32 blockID = mem.read32(messagePointer + 4); + u32 size = mem.read32(messagePointer + 8); + u32 input = mem.read32(messagePointer + 16); + log("CFG::SetConfigInfoBlk4 (block ID = %X, size = %X, input pointer = %08X)\n", blockID, size, input); + + mem.write32(messagePointer, IPC::responseHeader(0x401, 1, 2)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, IPC::pointerHeader(0, size, IPC::BufferType::Receive)); + mem.write32(messagePointer + 12, input); +} + +void CFGService::updateConfigNANDSavegame(u32 messagePointer) { + log("CFG::UpdateConfigNANDSavegame\n"); + + mem.write32(messagePointer, IPC::responseHeader(0x403, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + // https://www.3dbrew.org/wiki/Cfg:TranslateCountryInfo void CFGService::translateCountryInfo(u32 messagePointer) { const u32 country = mem.read32(messagePointer + 4); @@ -292,4 +382,28 @@ void CFGService::translateCountryInfo(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x8, 2, 0)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, result); +} + +void CFGService::isFangateSupported(u32 messagePointer) { + log("CFG::IsFangateSupported\n"); + + // TODO: What even is fangate? + mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 1); +} + +void CFGService::norInitialize(u32 messagePointer) { + log("CFG::NOR::Initialize\n"); + + mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void CFGService::norReadData(u32 messagePointer) { + log("CFG::NOR::ReadData\n"); + Helpers::warn("Unimplemented CFG::NOR::ReadData"); + + mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 96364e736..5c6ab3d69 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -1,4 +1,5 @@ #include "services/gsp_gpu.hpp" + #include "PICA/regs.hpp" #include "ipc.hpp" #include "kernel.hpp" @@ -39,7 +40,7 @@ namespace GXCommands { } void GPUService::reset() { - privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle + privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle interruptEvent = std::nullopt; gspThreadCount = 0; sharedMem = nullptr; @@ -113,38 +114,38 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) { log("GSP::GPU::RegisterInterruptRelayQueue (flags = %X, event handle = %X)\n", flags, eventHandle); const auto event = kernel.getObject(eventHandle, KernelObjectType::Event); - if (event == nullptr) { // Check if interrupt event is invalid + if (event == nullptr) { // Check if interrupt event is invalid Helpers::panic("Invalid event passed to GSP::GPU::RegisterInterruptRelayQueue"); } else { interruptEvent = eventHandle; } mem.write32(messagePointer, IPC::responseHeader(0x13, 2, 2)); - mem.write32(messagePointer + 4, Result::GSP::SuccessRegisterIRQ); // First init returns a unique result - mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index - mem.write32(messagePointer + 12, 0); // Translation descriptor + mem.write32(messagePointer + 4, Result::GSP::SuccessRegisterIRQ); // First init returns a unique result + mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index + mem.write32(messagePointer + 12, 0); // Translation descriptor mem.write32(messagePointer + 16, KernelHandles::GSPSharedMemHandle); } void GPUService::requestInterrupt(GPUInterrupt type) { - if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet + if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet return; } // TODO: Add support for multiple GSP threads - u8 index = sharedMem[0]; // The interrupt block is normally located at sharedMem + processGSPIndex*0x40 + u8 index = sharedMem[0]; // The interrupt block is normally located at sharedMem + processGSPIndex*0x40 u8& interruptCount = sharedMem[1]; u8 flagIndex = (index + interruptCount) % 0x34; interruptCount++; - sharedMem[2] = 0; // Set error code to 0 - sharedMem[0xC + flagIndex] = static_cast(type); // Write interrupt type to queue + sharedMem[2] = 0; // Set error code to 0 + sharedMem[0xC + flagIndex] = static_cast(type); // Write interrupt type to queue // Update framebuffer info in shared memory // Most new games check to make sure that the "flag" byte of the framebuffer info header is set to 0 // Not emulating this causes Yoshi's Wooly World, Captain Toad, Metroid 2 et al to hang if (type == GPUInterrupt::VBlank0 || type == GPUInterrupt::VBlank1) { - int screen = static_cast(type) - static_cast(GPUInterrupt::VBlank0); // 0 for top screen, 1 for bottom + int screen = static_cast(type) - static_cast(GPUInterrupt::VBlank0); // 0 for top screen, 1 for bottom FramebufferUpdate* update = getFramebufferInfo(screen); if (update->dirtyFlag & 1) { @@ -165,7 +166,6 @@ void GPUService::readHwRegs(u32 messagePointer) { const u32 initialDataPointer = mem.read32(messagePointer + 0x104); u32 dataPointer = initialDataPointer; log("GSP::GPU::ReadHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer); - // Check for alignment if ((size & 3) || (ioAddr & 3) || (dataPointer & 3)) { @@ -197,8 +197,8 @@ void GPUService::readHwRegs(u32 messagePointer) { } void GPUService::writeHwRegs(u32 messagePointer) { - u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned - const u32 size = mem.read32(messagePointer + 8); // Size in bytes + u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned + const u32 size = mem.read32(messagePointer + 8); // Size in bytes u32 dataPointer = mem.read32(messagePointer + 16); log("GSP::GPU::writeHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer); @@ -230,14 +230,14 @@ void GPUService::writeHwRegs(u32 messagePointer) { // Update sequential GPU registers using an array of data and mask values using this formula // GPU register = (register & ~mask) | (data & mask). void GPUService::writeHwRegsWithMask(u32 messagePointer) { - u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned - const u32 size = mem.read32(messagePointer + 8); // Size in bytes + u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned + const u32 size = mem.read32(messagePointer + 8); // Size in bytes - u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer - u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer + u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer + u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer - log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n", - ioAddr, size, dataPointer, maskPointer); + log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n", ioAddr, size, dataPointer, + maskPointer); // Check for alignment if ((size & 3) || (ioAddr & 3) || (dataPointer & 3) || (maskPointer & 3)) { @@ -351,11 +351,11 @@ void GPUService::setInternalPriorities(u32 messagePointer) { } void GPUService::processCommandBuffer() { - if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet + if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet return; } - constexpr int threadCount = 1; // TODO: More than 1 thread can have GSP commands at a time + constexpr int threadCount = 1; // TODO: More than 1 thread can have GSP commands at a time for (int t = 0; t < threadCount; t++) { u8* cmdBuffer = &sharedMem[0x800 + t * 0x200]; u8& commandsLeft = cmdBuffer[1]; @@ -408,9 +408,9 @@ void GPUService::memoryFill(u32* cmd) { u32 control = cmd[7]; // buf0 parameters - u32 start0 = cmd[1]; // Start address for the fill. If 0, don't fill anything - u32 value0 = cmd[2]; // Value to fill the framebuffer with - u32 end0 = cmd[3]; // End address for the fill + u32 start0 = cmd[1]; // Start address for the fill. If 0, don't fill anything + u32 value0 = cmd[2]; // Value to fill the framebuffer with + u32 end0 = cmd[3]; // End address for the fill u32 control0 = control & 0xffff; // buf1 parameters @@ -439,7 +439,7 @@ void GPUService::triggerDisplayTransfer(u32* cmd) { log("GSP::GPU::TriggerDisplayTransfer (Stubbed)\n"); gpu.displayTransfer(inputAddr, outputAddr, inputSize, outputSize, flags); - requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt + requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt } void GPUService::triggerDMARequest(u32* cmd) { @@ -453,22 +453,14 @@ void GPUService::triggerDMARequest(u32* cmd) { requestInterrupt(GPUInterrupt::DMA); } -void GPUService::flushCacheRegions(u32* cmd) { - log("GSP::GPU::FlushCacheRegions (Stubbed)\n"); -} +void GPUService::flushCacheRegions(u32* cmd) { log("GSP::GPU::FlushCacheRegions (Stubbed)\n"); } void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) { using namespace PICA::ExternalRegs; static constexpr std::array fbAddresses = { - Framebuffer0AFirstAddr, - Framebuffer0BFirstAddr, - Framebuffer1AFirstAddr, - Framebuffer1BFirstAddr, - Framebuffer0ASecondAddr, - Framebuffer0BSecondAddr, - Framebuffer1ASecondAddr, - Framebuffer1BSecondAddr, + Framebuffer0AFirstAddr, Framebuffer0BFirstAddr, Framebuffer1AFirstAddr, Framebuffer1BFirstAddr, + Framebuffer0ASecondAddr, Framebuffer0BSecondAddr, Framebuffer1ASecondAddr, Framebuffer1BSecondAddr, }; auto& regs = gpu.getExtRegisters(); @@ -478,12 +470,7 @@ void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) { regs[fbAddresses[fbIndex + 1]] = VaddrToPaddr(info.rightFramebufferVaddr); static constexpr std::array configAddresses = { - Framebuffer0Config, - Framebuffer0Select, - Framebuffer0Stride, - Framebuffer1Config, - Framebuffer1Select, - Framebuffer1Stride, + Framebuffer0Config, Framebuffer0Select, Framebuffer0Stride, Framebuffer1Config, Framebuffer1Select, Framebuffer1Stride, }; const u32 configIndex = screenId * 3; @@ -494,14 +481,14 @@ void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) { // Actually send command list (aka display list) to GPU void GPUService::processCommandList(u32* cmd) { - const u32 address = cmd[1] & ~7; // Buffer address - const u32 size = cmd[2] & ~3; // Buffer size in bytes - [[maybe_unused]] const bool updateGas = cmd[3] == 1; // Update gas additive blend results (0 = don't update, 1 = update) - [[maybe_unused]] const bool flushBuffer = cmd[7] == 1; // Flush buffer (0 = don't flush, 1 = flush) + const u32 address = cmd[1] & ~7; // Buffer address + const u32 size = cmd[2] & ~3; // Buffer size in bytes + [[maybe_unused]] const bool updateGas = cmd[3] == 1; // Update gas additive blend results (0 = don't update, 1 = update) + [[maybe_unused]] const bool flushBuffer = cmd[7] == 1; // Flush buffer (0 = don't flush, 1 = flush) log("GPU::GSP::processCommandList. Address: %08X, size in bytes: %08X\n", address, size); gpu.startCommandList(address, size); - requestInterrupt(GPUInterrupt::P3D); // Send an IRQ when command list processing is over + requestInterrupt(GPUInterrupt::P3D); // Send an IRQ when command list processing is over } // TODO: Emulate the transfer engine & its registers @@ -576,4 +563,4 @@ void GPUService::importDisplayCaptureInfo(u32 messagePointer) { mem.write32(messagePointer + 28, bottomScreenCapture.rightFramebuffer); mem.write32(messagePointer + 32, bottomScreenCapture.format); mem.write32(messagePointer + 36, bottomScreenCapture.stride); -} +} \ No newline at end of file diff --git a/src/core/services/gsp_lcd.cpp b/src/core/services/gsp_lcd.cpp index d018166bb..d2e0ac211 100644 --- a/src/core/services/gsp_lcd.cpp +++ b/src/core/services/gsp_lcd.cpp @@ -1,8 +1,10 @@ #include "services/gsp_lcd.hpp" + #include "ipc.hpp" namespace LCDCommands { enum : u32 { + SetLedForceOff = 0x00130040, }; } @@ -11,6 +13,16 @@ void LCDService::reset() {} void LCDService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { + case LCDCommands::SetLedForceOff: setLedForceOff(messagePointer); break; + default: Helpers::panic("LCD service requested. Command: %08X\n", command); } +} + +void LCDService::setLedForceOff(u32 messagePointer) { + const u8 state = mem.read8(messagePointer + 4); + log("LCD::SetLedForceOff (state = %X)\n", state); + + mem.write32(messagePointer, IPC::responseHeader(0x13, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/ns.cpp b/src/core/services/ns.cpp new file mode 100644 index 000000000..f26e72d38 --- /dev/null +++ b/src/core/services/ns.cpp @@ -0,0 +1,32 @@ +#include "services/ns.hpp" + +#include "ipc.hpp" + +namespace NSCommands { + enum : u32 { + LaunchTitle = 0x000200C0, + }; +} + +void NSService::reset() {} + +void NSService::handleSyncRequest(u32 messagePointer, Type type) { + const u32 command = mem.read32(messagePointer); + + // ns:s commands + switch (command) { + case NSCommands::LaunchTitle: launchTitle(messagePointer); break; + + default: Helpers::panic("NS service requested. Command: %08X\n", command); + } +} + +void NSService::launchTitle(u32 messagePointer) { + const u64 titleID = mem.read64(messagePointer + 4); + const u32 launchFlags = mem.read32(messagePointer + 12); + Helpers::warn("NS::LaunchTitle (title ID = %llX, launch flags = %X) (stubbed)", titleID, launchFlags); + + mem.write32(messagePointer, IPC::responseHeader(0x2, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0); // Process ID +} diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index e276cd23e..b8d68789c 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -7,9 +7,9 @@ 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), - csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel, config), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), - gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), - news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} + csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel, config), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), + fs(mem, kernel, config), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), + nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), ns(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16; @@ -40,6 +40,7 @@ void ServiceManager::reset() { news_u.reset(); nfc.reset(); nim.reset(); + ns.reset(); ptm.reset(); soc.reset(); ssl.reset(); @@ -99,19 +100,23 @@ static std::map serviceMap = { { "act:a", KernelHandles::ACT }, { "act:u", KernelHandles::ACT }, { "am:app", KernelHandles::AM }, + { "am:sys", KernelHandles::AM }, { "APT:S", KernelHandles::APT }, // TODO: APT:A, APT:S and APT:U are slightly different { "APT:A", KernelHandles::APT }, { "APT:U", KernelHandles::APT }, { "boss:U", KernelHandles::BOSS }, + { "boss:P", KernelHandles::BOSS }, { "cam:u", KernelHandles::CAM }, { "cecd:u", KernelHandles::CECD }, { "cfg:u", KernelHandles::CFG_U }, { "cfg:i", KernelHandles::CFG_I }, { "cfg:s", KernelHandles::CFG_S }, + { "cfg:nor", KernelHandles::CFG_NOR }, { "csnd:SND", KernelHandles::CSND }, { "dlp:SRVR", KernelHandles::DLP_SRVR }, { "dsp::DSP", KernelHandles::DSP }, { "hid:USER", KernelHandles::HID }, + { "hid:SPVR", KernelHandles::HID }, { "http:C", KernelHandles::HTTP }, { "ir:USER", KernelHandles::IR_USER }, { "frd:a", KernelHandles::FRD_A }, @@ -126,11 +131,13 @@ static std::map serviceMap = { { "news:u", KernelHandles::NEWS_U }, { "nfc:u", KernelHandles::NFC }, { "ns:s", KernelHandles::NS_S }, + { "nwm::EXT", KernelHandles::NWM_EXT }, { "nwm::UDS", KernelHandles::NWM_UDS }, { "nim:aoc", KernelHandles::NIM }, { "ptm:u", KernelHandles::PTM_U }, // TODO: ptm:u and ptm:sysm have very different command sets { "ptm:sysm", KernelHandles::PTM_SYSM }, { "ptm:play", KernelHandles::PTM_PLAY }, + { "ptm:gets", KernelHandles::PTM_GETS }, { "soc:U", KernelHandles::SOC }, { "ssl:C", KernelHandles::SSL }, { "y2r:u", KernelHandles::Y2R }, @@ -213,6 +220,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer, CFGService::Type::U); break; case KernelHandles::CFG_I: cfg.handleSyncRequest(messagePointer, CFGService::Type::I); break; case KernelHandles::CFG_S: cfg.handleSyncRequest(messagePointer, CFGService::Type::S); break; + case KernelHandles::CFG_NOR: cfg.handleSyncRequest(messagePointer, CFGService::Type::NOR); break; case KernelHandles::CSND: csnd.handleSyncRequest(messagePointer); break; case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break; case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break; @@ -228,11 +236,12 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break; case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break; case KernelHandles::NEWS_U: news_u.handleSyncRequest(messagePointer); break; - case KernelHandles::NS_S: Helpers::panic("Unimplemented SendSyncRequest to ns:s"); break; + case KernelHandles::NS_S: ns.handleSyncRequest(messagePointer, NSService::Type::S); break; case KernelHandles::NWM_UDS: nwm_uds.handleSyncRequest(messagePointer); break; case KernelHandles::PTM_PLAY: ptm.handleSyncRequest(messagePointer, PTMService::Type::PLAY); break; case KernelHandles::PTM_SYSM: ptm.handleSyncRequest(messagePointer, PTMService::Type::SYSM); break; case KernelHandles::PTM_U: ptm.handleSyncRequest(messagePointer, PTMService::Type::U); break; + case KernelHandles::PTM_GETS: ptm.handleSyncRequest(messagePointer, PTMService::Type::GETS); break; case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break; case KernelHandles::SSL: ssl.handleSyncRequest(messagePointer); break; case KernelHandles::Y2R: y2r.handleSyncRequest(messagePointer); break;