diff --git a/include/services/ac.hpp b/include/services/ac.hpp index 55f46d3ed..a6f686983 100644 --- a/include/services/ac.hpp +++ b/include/services/ac.hpp @@ -16,6 +16,8 @@ class ACService { void cancelConnectAsync(u32 messagePointer); void closeAsync(u32 messagePointer); void createDefaultConfig(u32 messagePointer); + void getConnectingInfraPriority(u32 messagePointer); + void getStatus(u32 messagePointer); void getLastErrorCode(u32 messagePointer); void isConnected(u32 messagePointer); void registerDisconnectEvent(u32 messagePointer); diff --git a/include/services/cfg.hpp b/include/services/cfg.hpp index 3ab0426a8..283275745 100644 --- a/include/services/cfg.hpp +++ b/include/services/cfg.hpp @@ -15,6 +15,7 @@ class CFGService { // Service functions void getConfigInfoBlk2(u32 messagePointer); + void getConfigInfoBlk8(u32 messagePointer); void getCountryCodeID(u32 messagePointer); void getLocalFriendCodeSeed(u32 messagePointer); void getRegionCanadaUSA(u32 messagePointer); @@ -23,6 +24,8 @@ class CFGService { void secureInfoGetByte101(u32 messagePointer); void secureInfoGetRegion(u32 messagePointer); + void getConfigInfo(u32 output, u32 blockID, u32 size, u32 permissionMask); + public: enum class Type { U, // cfg:u diff --git a/include/services/frd.hpp b/include/services/frd.hpp index 46a6e272d..b3891338e 100644 --- a/include/services/frd.hpp +++ b/include/services/frd.hpp @@ -26,16 +26,20 @@ class FRDService { void getFriendKeyList(u32 messagePointer); void getFriendPresence(u32 messagePointer); void getFriendProfile(u32 messagePointer); + void getMyComment(u32 messagePointer); + void getMyFavoriteGame(u32 messagePointer); void getMyFriendKey(u32 messagePointer); void getMyMii(u32 messagePointer); void getMyPresence(u32 messagePointer); void getMyProfile(u32 messagePointer); void getMyScreenName(u32 messsagePointer); void hasLoggedIn(u32 messagePointer); + void isOnline(u32 messagePointer); void logout(u32 messagePointer); void setClientSDKVersion(u32 messagePointer); void setNotificationMask(u32 messagePointer); void updateGameModeDescription(u32 messagePointer); + void updateMii(u32 messagePointer); struct Profile { u8 region; diff --git a/include/services/ndm.hpp b/include/services/ndm.hpp index f8a964c51..6d4e5ad85 100644 --- a/include/services/ndm.hpp +++ b/include/services/ndm.hpp @@ -6,6 +6,8 @@ #include "result/result.hpp" class NDMService { + enum class ExclusiveState : u32 { None = 0, Infrastructure = 1, LocalComms = 2, StreetPass = 3, StreetPassData = 4 }; + Handle handle = KernelHandles::NDM; Memory& mem; MAKE_LOG_FUNCTION(log, ndmLogger) @@ -15,11 +17,14 @@ class NDMService { void enterExclusiveState(u32 messagePointer); void exitExclusiveState(u32 messagePointer); void overrideDefaultDaemons(u32 messagePointer); + void queryExclusiveState(u32 messagePointer); void resumeDaemons(u32 messagePointer); void resumeScheduler(u32 messagePointer); void suspendDaemons(u32 messagePointer); void suspendScheduler(u32 messagePointer); + ExclusiveState exclusiveState = ExclusiveState::None; + public: NDMService(Memory& mem) : mem(mem) {} void reset(); diff --git a/src/core/services/ac.cpp b/src/core/services/ac.cpp index 25efdce62..3feddebeb 100644 --- a/src/core/services/ac.cpp +++ b/src/core/services/ac.cpp @@ -7,6 +7,8 @@ namespace ACCommands { CancelConnectAsync = 0x00070002, CloseAsync = 0x00080004, GetLastErrorCode = 0x000A0000, + GetStatus = 0x000C0000, + GetConnectingInfraPriority = 0x000F0000, RegisterDisconnectEvent = 0x00300004, IsConnected = 0x003E0042, SetClientVersion = 0x00400042, @@ -24,7 +26,9 @@ void ACService::handleSyncRequest(u32 messagePointer) { case ACCommands::CancelConnectAsync: cancelConnectAsync(messagePointer); break; case ACCommands::CloseAsync: closeAsync(messagePointer); break; case ACCommands::CreateDefaultConfig: createDefaultConfig(messagePointer); break; + case ACCommands::GetConnectingInfraPriority: getConnectingInfraPriority(messagePointer); break; case ACCommands::GetLastErrorCode: getLastErrorCode(messagePointer); break; + case ACCommands::GetStatus: getStatus(messagePointer); break; case ACCommands::IsConnected: isConnected(messagePointer); break; case ACCommands::RegisterDisconnectEvent: registerDisconnectEvent(messagePointer); break; case ACCommands::SetClientVersion: setClientVersion(messagePointer); break; @@ -69,6 +73,25 @@ void ACService::getLastErrorCode(u32 messagePointer) { mem.write32(messagePointer + 8, 0); // Hopefully this means no error? } +void ACService::getConnectingInfraPriority(u32 messagePointer) { + log("AC::GetConnectingInfraPriority (stubbed)\n"); + + // TODO: Find out what this is + mem.write32(messagePointer, IPC::responseHeader(0x0F, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0); +} + +void ACService::getStatus(u32 messagePointer) { + log("AC::GetStatus (stubbed)\n"); + + // TODO: Find out what this is + mem.write32(messagePointer, IPC::responseHeader(0x0C, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0); +} + + void ACService::isConnected(u32 messagePointer) { log("AC::IsConnected\n"); // This has parameters according to the command word but it's unknown what they are diff --git a/src/core/services/cfg.cpp b/src/core/services/cfg.cpp index 23c6e262d..5a916ef05 100644 --- a/src/core/services/cfg.cpp +++ b/src/core/services/cfg.cpp @@ -11,6 +11,7 @@ namespace CFGCommands { enum : u32 { GetConfigInfoBlk2 = 0x00010082, + GetConfigInfoBlk8 = 0x04010082, SecureInfoGetRegion = 0x00020000, GenHashConsoleUnique = 0x00030040, GetRegionCanadaUSA = 0x00040000, @@ -61,77 +62,91 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) { u32 output = mem.read32(messagePointer + 16); // Pointer to write the output data to log("CFG::GetConfigInfoBlk2 (size = %X, block ID = %X, output pointer = %08X\n", size, blockID, output); + + getConfigInfo(output, blockID, size, 0x2); + mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 2)); + mem.write32(messagePointer + 4, Result::Success); +} + +void CFGService::getConfigInfoBlk8(u32 messagePointer) { + 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 + 4, Result::Success); +} + +void CFGService::getConfigInfo(u32 output, u32 blockID, u32 size, u32 permissionMask) { // TODO: Make this not bad - if (size == 1 && blockID == 0x70001) { // Sound output mode + if (size == 1 && blockID == 0x70001) { // Sound output mode mem.write8(output, static_cast(DSPService::SoundOutputMode::Stereo)); - } else if (size == 1 && blockID == 0xA0002){ // System language + } else if (size == 1 && blockID == 0xA0002) { // System language mem.write8(output, static_cast(LanguageCodes::English)); - } else if (size == 4 && blockID == 0xB0000) { // Country info - mem.write8(output, 0); // Unknown - mem.write8(output + 1, 0); // Unknown - mem.write8(output + 2, 2); // Province (Temporarily stubbed to Washington DC like Citra) - mem.write8(output + 3, static_cast(country)); // Country code + } else if (size == 4 && blockID == 0xB0000) { // Country info + mem.write8(output, 0); // Unknown + mem.write8(output + 1, 0); // Unknown + mem.write8(output + 2, 2); // Province (Temporarily stubbed to Washington DC like Citra) + mem.write8(output + 3, static_cast(country)); // Country code } else if (size == 0x20 && blockID == 0x50005) { // "Stereo Camera settings" // Implementing this properly fixes NaN uniforms in some games. Values taken from 3dmoo & Citra static constexpr std::array STEREO_CAMERA_SETTINGS = { - 62.0f, 289.0f, 76.80000305175781f, 46.08000183105469f, - 10.0f, 5.0f, 55.58000183105469f, 21.56999969482422f + 62.0f, 289.0f, 76.80000305175781f, 46.08000183105469f, 10.0f, 5.0f, 55.58000183105469f, 21.56999969482422f, }; for (int i = 0; i < 8; i++) { mem.write32(output + i * 4, Helpers::bit_cast(STEREO_CAMERA_SETTINGS[i])); } - } else if (size == 0x1C && blockID == 0xA0000) { // Username + } else if (size == 0x1C && blockID == 0xA0000) { // Username writeStringU16(output, u"Pander"); - } else if (size == 0xC0 && blockID == 0xC0000) { // Parental restrictions info - for (int i = 0; i < 0xC0; i++) - mem.write8(output + i, 0); - } else if (size == 4 && blockID == 0xD0000) { // Agreed EULA version (first 2 bytes) and latest EULA version (next 2 bytes) + } else if (size == 0xC0 && blockID == 0xC0000) { // Parental restrictions info + for (int i = 0; i < 0xC0; i++) mem.write8(output + i, 0); + } else if (size == 4 && blockID == 0xD0000) { // Agreed EULA version (first 2 bytes) and latest EULA version (next 2 bytes) log("Read EULA info\n"); - mem.write16(output, 0x0202); // Agreed EULA version = 2.2 (Random number. TODO: Check) - mem.write16(output + 2, 0x0202); // Latest EULA version = 2.2 - } else if (size == 0x800 && blockID == 0xB0001) { // UTF-16 name for our country in every language at 0x80 byte intervals + mem.write16(output, 0x0202); // Agreed EULA version = 2.2 (Random number. TODO: Check) + mem.write16(output + 2, 0x0202); // Latest EULA version = 2.2 + } else if (size == 0x800 && blockID == 0xB0001) { // UTF-16 name for our country in every language at 0x80 byte intervals constexpr size_t languageCount = 16; - constexpr size_t nameSize = 0x80; // Max size of each name in bytes - std::u16string name = u"PandaLand (Home of PandaSemi LLC) (aka Pandistan)"; // Note: This + the null terminator needs to fit in 0x80 bytes + constexpr size_t nameSize = 0x80; // Max size of each name in bytes + std::u16string name = u"PandaLand (Home of PandaSemi LLC) (aka Pandistan)"; // Note: This + the null terminator needs to fit in 0x80 bytes for (int i = 0; i < languageCount; i++) { u32 pointer = output + i * nameSize; writeStringU16(pointer, name); } - } else if (size == 0x800 && blockID == 0xB0002) { // UTF-16 name for our state in every language at 0x80 byte intervals + } else if (size == 0x800 && blockID == 0xB0002) { // UTF-16 name for our state in every language at 0x80 byte intervals constexpr size_t languageCount = 16; - constexpr size_t nameSize = 0x80; // Max size of each name in bytes - std::u16string name = u"Pandington"; // Note: This + the null terminator needs to fit in 0x80 bytes + constexpr size_t nameSize = 0x80; // Max size of each name in bytes + std::u16string name = u"Pandington"; // Note: This + the null terminator needs to fit in 0x80 bytes for (int i = 0; i < languageCount; i++) { u32 pointer = output + i * nameSize; writeStringU16(pointer, name); } - } else if (size == 4 && blockID == 0xB0003) { // Coordinates (latidude and longtitude) as s16 - mem.write16(output, 0); // Latitude - mem.write16(output + 2, 0); // Longtitude - } else if (size == 2 && blockID == 0xA0001) { // Birthday - mem.write8(output, 5); // Month (May) - mem.write8(output + 1, 5); // Day (Fifth) - } else if (size == 8 && blockID == 0x30001) { // User time offset + } else if (size == 4 && blockID == 0xB0003) { // Coordinates (latidude and longtitude) as s16 + mem.write16(output, 0); // Latitude + mem.write16(output + 2, 0); // Longtitude + } else if (size == 2 && blockID == 0xA0001) { // Birthday + mem.write8(output, 5); // Month (May) + mem.write8(output + 1, 5); // Day (Fifth) + } else if (size == 8 && blockID == 0x30001) { // User time offset printf("Read from user time offset field in NAND. TODO: What is this\n"); mem.write64(output, 0); - } else if (size == 20 && blockID == 0xC0001) { // COPPACS restriction data, used by games when they detect a USA/Canada region for market restriction stuff + } else if (size == 20 && blockID == 0xC0001) { // COPPACS restriction data, used by games when they detect a USA/Canada region for market + // restriction stuff for (u32 i = 0; i < size; i += 4) { mem.write32(output + i, 0); } } else if (size == 4 && blockID == 0x170000) { // Miiverse access key mem.write32(output, 0); } else if (size == 8 && blockID == 0x00090000) { - mem.write64(output, 0); // Some sort of key used with nwm::UDS::InitializeWithVersion + mem.write64(output, 0); // Some sort of key used with nwm::UDS::InitializeWithVersion } else { Helpers::panic("Unhandled GetConfigInfoBlk2 configuration. Size = %d, block = %X", size, blockID); } - - mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 2)); - mem.write32(messagePointer + 4, Result::Success); } void CFGService::secureInfoGetRegion(u32 messagePointer) { diff --git a/src/core/services/frd.cpp b/src/core/services/frd.cpp index fecb33226..19096e8d9 100644 --- a/src/core/services/frd.cpp +++ b/src/core/services/frd.cpp @@ -8,6 +8,7 @@ namespace FRDCommands { enum : u32 { HasLoggedIn = 0x00010000, + IsOnline = 0x00020000, AttachToEventNotification = 0x00200002, SetNotificationMask = 0x00210040, SetClientSdkVersion = 0x00320042, @@ -17,11 +18,15 @@ namespace FRDCommands { GetMyPresence = 0x00080000, GetMyScreenName = 0x00090000, GetMyMii = 0x000A0000, + GetMyFavoriteGame = 0x000D0000, + GetMyComment = 0x000F0000, GetFriendKeyList = 0x00110080, GetFriendPresence = 0x00120042, GetFriendProfile = 0x00150042, GetFriendAttributeFlags = 0x00170042, UpdateGameModeDescription = 0x001D0002, + + UpdateMii = 0x040C0800, }; } @@ -35,17 +40,32 @@ void FRDService::handleSyncRequest(u32 messagePointer, FRDService::Type type) { case FRDCommands::GetFriendKeyList: getFriendKeyList(messagePointer); break; case FRDCommands::GetFriendPresence: getFriendPresence(messagePointer); break; case FRDCommands::GetFriendProfile: getFriendProfile(messagePointer); break; + case FRDCommands::GetMyComment: getMyComment(messagePointer); break; case FRDCommands::GetMyFriendKey: getMyFriendKey(messagePointer); break; case FRDCommands::GetMyMii: getMyMii(messagePointer); break; + case FRDCommands::GetMyFavoriteGame: getMyFavoriteGame(messagePointer); break; case FRDCommands::GetMyPresence: getMyPresence(messagePointer); break; case FRDCommands::GetMyProfile: getMyProfile(messagePointer); break; case FRDCommands::GetMyScreenName: getMyScreenName(messagePointer); break; case FRDCommands::HasLoggedIn: hasLoggedIn(messagePointer); break; + case FRDCommands::IsOnline: isOnline(messagePointer); break; case FRDCommands::Logout: logout(messagePointer); break; case FRDCommands::SetClientSdkVersion: setClientSDKVersion(messagePointer); break; case FRDCommands::SetNotificationMask: setNotificationMask(messagePointer); break; case FRDCommands::UpdateGameModeDescription: updateGameModeDescription(messagePointer); break; - default: Helpers::panic("FRD service requested. Command: %08X\n", command); + + default: + // FRD:A functions + if (type == Type::A) { + switch (command) { + case FRDCommands::UpdateMii: updateMii(messagePointer); break; + default: Helpers::panic("FRD:A service requested. Command: %08X\n", command); break; + } + } else { + Helpers::panic("FRD service requested. Command: %08X\n", command); + } + + break; } } @@ -201,6 +221,23 @@ void FRDService::getMyMii(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void FRDService::getMyFavoriteGame(u32 messagePointer) { + log("FRD::GetMyFavoriteGame (stubbed)\n"); + constexpr u64 titleID = 0; + + mem.write32(messagePointer, IPC::responseHeader(0xD, 3, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write64(messagePointer + 8, titleID); +} + +void FRDService::getMyComment(u32 messagePointer) { + log("FRD::GetMyComment"); + + mem.write32(messagePointer, IPC::responseHeader(0xF, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0); +} + void FRDService::hasLoggedIn(u32 messagePointer) { log("FRD::HasLoggedIn\n"); @@ -209,10 +246,26 @@ void FRDService::hasLoggedIn(u32 messagePointer) { mem.write8(messagePointer + 8, loggedIn ? 1 : 0); } +void FRDService::isOnline(u32 messagePointer) { + log("FRD::IsOnline\n"); + + mem.write32(messagePointer, IPC::responseHeader(0x2, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + // TODO: When is this 0? + mem.write8(messagePointer + 8, 1); +} + void FRDService::logout(u32 messagePointer) { log("FRD::Logout\n"); loggedIn = false; mem.write32(messagePointer, IPC::responseHeader(0x4, 1, 0)); mem.write32(messagePointer + 4, Result::Success); +} + +void FRDService::updateMii(u32 messagePointer) { + log("FRD::UpdateMii (stubbed)\n"); + + mem.write32(messagePointer, IPC::responseHeader(0x40C, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/ndm.cpp b/src/core/services/ndm.cpp index 2c68e81f8..65de1399e 100644 --- a/src/core/services/ndm.cpp +++ b/src/core/services/ndm.cpp @@ -4,7 +4,8 @@ namespace NDMCommands { enum : u32 { EnterExclusiveState = 0x00010042, - ExitExclusiveState = 0x00020002, + ExitExclusiveState = 0x00020002, + QueryExclusiveMode = 0x00030000, OverrideDefaultDaemons = 0x00140040, SuspendDaemons = 0x00060040, ResumeDaemons = 0x00070040, @@ -14,7 +15,7 @@ namespace NDMCommands { }; } -void NDMService::reset() {} +void NDMService::reset() { exclusiveState = ExclusiveState::None; } void NDMService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); @@ -23,6 +24,7 @@ void NDMService::handleSyncRequest(u32 messagePointer) { case NDMCommands::ExitExclusiveState: exitExclusiveState(messagePointer); break; case NDMCommands::ClearHalfAwakeMacFilter: clearHalfAwakeMacFilter(messagePointer); break; case NDMCommands::OverrideDefaultDaemons: overrideDefaultDaemons(messagePointer); break; + case NDMCommands::QueryExclusiveMode: queryExclusiveState(messagePointer); break; case NDMCommands::ResumeDaemons: resumeDaemons(messagePointer); break; case NDMCommands::ResumeScheduler: resumeScheduler(messagePointer); break; case NDMCommands::SuspendDaemons: suspendDaemons(messagePointer); break; @@ -33,16 +35,35 @@ void NDMService::handleSyncRequest(u32 messagePointer) { void NDMService::enterExclusiveState(u32 messagePointer) { log("NDM::EnterExclusiveState (stubbed)\n"); + const u32 state = mem.read32(messagePointer + 4); + + // Check that the exclusive state config is valid + if (state > 4) { + Helpers::warn("NDM::EnterExclusiveState: Invalid state %d", state); + } else { + exclusiveState = static_cast(state); + } + mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } void NDMService::exitExclusiveState(u32 messagePointer) { log("NDM::ExitExclusiveState (stubbed)\n"); + exclusiveState = ExclusiveState::None; + mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } +void NDMService::queryExclusiveState(u32 messagePointer) { + log("NDM::QueryExclusiveState\n"); + + mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, static_cast(exclusiveState)); +} + void NDMService::overrideDefaultDaemons(u32 messagePointer) { log("NDM::OverrideDefaultDaemons (stubbed)\n"); mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 0));