From 597a2e8ead27561f9402bec3ec966f5b66c8f926 Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Tue, 31 Oct 2023 22:01:25 +0100 Subject: [PATCH] Add missing FS:USER functions (#7051) --- src/core/hle/kernel/hle_ipc.h | 7 ++ src/core/hle/service/fs/fs_user.cpp | 148 +++++++++++++++++++++++++--- src/core/hle/service/fs/fs_user.h | 85 +++++++++++++++- src/core/loader/3dsx.cpp | 2 +- src/core/loader/ncch.cpp | 11 ++- 5 files changed, 232 insertions(+), 21 deletions(-) diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index f8c4a31a7..c0702cdb2 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -221,6 +221,13 @@ public: return session; } + /** + * Returns the client thread that made the service request. + */ + std::shared_ptr ClientThread() const { + return thread; + } + class WakeupCallback { public: virtual ~WakeupCallback() = default; diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index cf01f78a2..8dcdb2d77 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -670,6 +670,26 @@ void FS_USER::GetFormatInfo(Kernel::HLERequestContext& ctx) { rb.Push(format_info->duplicate_data != 0); } +void FS_USER::GetProductInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + + u32 process_id = rp.Pop(); + + LOG_DEBUG(Service_FS, "called, process_id={}", process_id); + + IPC::RequestBuilder rb = rp.MakeBuilder(6, 0); + + const auto product_info = GetProductInfo(process_id); + if (!product_info.has_value()) { + rb.Push(ResultCode(FileSys::ErrCodes::ArchiveNotMounted, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status)); + return; + } + + rb.Push(RESULT_SUCCESS); + rb.PushRaw(product_info.value()); +} + void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const auto process_id = rp.Pop(); @@ -687,8 +707,20 @@ void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) { return; } + ProgramInfo program_info = program_info_result.Unwrap(); + + // Always report the launched program mediatype is SD if the friends module is requesting this + // information and the media type is game card. Otherwise, friends will append a "romid" field + // to the NASC request with a cartridge unique identifier. Using a dump of a game card and the + // game card itself at the same time online is known to have caused issues in the past. + auto process = ctx.ClientThread()->owner_process.lock(); + if (process && process->codeset->name == "friends" && + program_info.media_type == MediaType::GameCard) { + program_info.media_type = MediaType::SDMC; + } + rb.Push(RESULT_SUCCESS); - rb.PushRaw(program_info_result.Unwrap()); + rb.PushRaw(program_info); } void FS_USER::ObsoletedCreateExtSaveData(Kernel::HLERequestContext& ctx) { @@ -775,12 +807,12 @@ void FS_USER::AddSeed(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { +void FS_USER::ObsoletedSetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - u64 value = rp.Pop(); - u32 secure_value_slot = rp.Pop(); - u32 unique_id = rp.Pop(); - u8 title_variation = rp.Pop(); + const u64 value = rp.Pop(); + const u32 secure_value_slot = rp.Pop(); + const u32 unique_id = rp.Pop(); + const u8 title_variation = rp.Pop(); // TODO: Generate and Save the Secure Value @@ -794,12 +826,11 @@ void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { +void FS_USER::ObsoletedGetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); - - u32 secure_value_slot = rp.Pop(); - u32 unique_id = rp.Pop(); - u8 title_variation = rp.Pop(); + const u32 secure_value_slot = rp.Pop(); + const u32 unique_id = rp.Pop(); + const u8 title_variation = rp.Pop(); LOG_WARNING( Service_FS, @@ -816,7 +847,77 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { rb.Push(0); // the secure value } -void FS_USER::Register(u32 process_id, u64 program_id, const std::string& filepath) { +void FS_USER::SetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const u32 secure_value_slot = rp.Pop(); + const u64 value = rp.Pop(); + + // TODO: Generate and Save the Secure Value + + LOG_WARNING(Service_FS, "(STUBBED) called, value=0x{:016x} secure_value_slot=0x{:08X}", value, + secure_value_slot); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + rb.Push(RESULT_SUCCESS); +} + +void FS_USER::GetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const u32 secure_value_slot = rp.Pop(); + + LOG_WARNING(Service_FS, "(STUBBED) called secure_value_slot=0x{:08X}", secure_value_slot); + + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); + + rb.Push(RESULT_SUCCESS); + + // TODO: Implement Secure Value Lookup & Generation + + rb.Push(false); // indicates that the secure value doesn't exist + rb.Push(false); // looks like a boolean value, purpose unknown + rb.Push(0); // the secure value +} + +void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto archive_handle = rp.PopRaw(); + const u32 secure_value_slot = rp.Pop(); + const u64 value = rp.Pop(); + const bool flush = rp.Pop(); + + // TODO: Generate and Save the Secure Value + + LOG_WARNING(Service_FS, + "(STUBBED) called, value=0x{:016x} secure_value_slot=0x{:04X} " + "archive_handle=0x{:08X} flush={}", + value, secure_value_slot, archive_handle, flush); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + rb.Push(RESULT_SUCCESS); +} + +void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + const auto archive_handle = rp.PopRaw(); + const u32 secure_value_slot = rp.Pop(); + + LOG_WARNING(Service_FS, "(STUBBED) called secure_value_slot=0x{:08X} archive_handle=0x{:08X}", + secure_value_slot, archive_handle); + + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); + + rb.Push(RESULT_SUCCESS); + + // TODO: Implement Secure Value Lookup & Generation + + rb.Push(false); // indicates that the secure value doesn't exist + rb.Push(false); // looks like a boolean value, purpose unknown + rb.Push(0); // the secure value +} + +void FS_USER::RegisterProgramInfo(u32 process_id, u64 program_id, const std::string& filepath) { const MediaType media_type = GetMediaTypeFromPath(filepath); program_info_map.insert_or_assign(process_id, ProgramInfo{program_id, media_type}); if (media_type == MediaType::GameCard) { @@ -828,6 +929,19 @@ std::string FS_USER::GetCurrentGamecardPath() const { return current_gamecard_path; } +void FS_USER::RegisterProductInfo(u32 process_id, const ProductInfo& product_info) { + product_info_map.insert_or_assign(process_id, product_info); +} + +std::optional FS_USER::GetProductInfo(u32 process_id) { + auto it = product_info_map.find(process_id); + if (it != product_info_map.end()) { + return it->second; + } else { + return {}; + } +} + ResultVal FS_USER::GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type) { // TODO(B3N30) check if on real 3DS NCSD is checked if partition exists @@ -929,7 +1043,7 @@ FS_USER::FS_USER(Core::System& system) {0x082B, nullptr, "CardNorDirectRead_4xIO"}, {0x082C, nullptr, "CardNorDirectCpuWriteWithoutVerify"}, {0x082D, nullptr, "CardNorDirectSectorEraseWithoutVerify"}, - {0x082E, nullptr, "GetProductInfo"}, + {0x082E, &FS_USER::GetProductInfo, "GetProductInfo"}, {0x082F, &FS_USER::GetProgramLaunchInfo, "GetProgramLaunchInfo"}, {0x0830, &FS_USER::ObsoletedCreateExtSaveData, "Obsoleted_3_0_CreateExtSaveData"}, {0x0831, nullptr, "CreateSharedExtSaveData"}, @@ -984,12 +1098,16 @@ FS_USER::FS_USER(Core::System& system) {0x0862, &FS_USER::SetPriority, "SetPriority"}, {0x0863, &FS_USER::GetPriority, "GetPriority"}, {0x0864, nullptr, "GetNandInfo"}, - {0x0865, &FS_USER::SetSaveDataSecureValue, "SetSaveDataSecureValue"}, - {0x0866, &FS_USER::GetSaveDataSecureValue, "GetSaveDataSecureValue"}, + {0x0865, &FS_USER::ObsoletedSetSaveDataSecureValue, "SetSaveDataSecureValue"}, + {0x0866, &FS_USER::ObsoletedGetSaveDataSecureValue, "GetSaveDataSecureValue"}, {0x0867, nullptr, "ControlSecureSave"}, {0x0868, nullptr, "GetMediaType"}, {0x0869, nullptr, "GetNandEraseCount"}, {0x086A, nullptr, "ReadNandReport"}, + {0x086E, &FS_USER::SetThisSaveDataSecureValue, "SetThisSaveDataSecureValue" }, + {0x086F, &FS_USER::GetThisSaveDataSecureValue, "GetThisSaveDataSecureValue" }, + {0x0875, &FS_USER::SetSaveDataSecureValue, "SetSaveDataSecureValue" }, + {0x0876, &FS_USER::GetSaveDataSecureValue, "GetSaveDataSecureValue" }, {0x087A, &FS_USER::AddSeed, "AddSeed"}, {0x087D, &FS_USER::GetNumSeeds, "GetNumSeeds"}, {0x0886, nullptr, "CheckUpdatedDat"}, diff --git a/src/core/hle/service/fs/fs_user.h b/src/core/hle/service/fs/fs_user.h index 37c7ba880..52f3b9a3e 100644 --- a/src/core/hle/service/fs/fs_user.h +++ b/src/core/hle/service/fs/fs_user.h @@ -4,10 +4,12 @@ #pragma once +#include #include #include #include "common/common_types.h" #include "core/file_sys/errors.h" +#include "core/hle/service/fs/archive.h" #include "core/hle/service/service.h" namespace Core { @@ -47,12 +49,23 @@ class FS_USER final : public ServiceFramework { public: explicit FS_USER(Core::System& system); - // On real HW this is part of FS:Reg. But since that module is only used by loader and pm, which - // we HLEed, we can just directly use it here - void Register(u32 process_id, u64 program_id, const std::string& filepath); + // On real HW this is part of FSReg (FSReg:Register). But since that module is only used by + // loader and pm, which we HLEed, we can just directly use it here + void RegisterProgramInfo(u32 process_id, u64 program_id, const std::string& filepath); std::string GetCurrentGamecardPath() const; + struct ProductInfo { + std::array product_code; + u16_le maker_code; + u16_le remaster_version; + }; + static_assert(sizeof(ProductInfo) == 0x14); + + void RegisterProductInfo(u32 process_id, const ProductInfo& product_info); + + std::optional GetProductInfo(u32 process_id); + /// Gets the registered program info of a process. ResultVal GetProgramLaunchInfo(u32 process_id) const { auto info = program_info_map.find(process_id); @@ -509,6 +522,17 @@ private: */ void GetFormatInfo(Kernel::HLERequestContext& ctx); + /** + * FS_User::GetProductInfo service function. + * Inputs: + * 0 : 0x082E0040 + * 1 : Process ID + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-6 : Product info + */ + void GetProductInfo(Kernel::HLERequestContext& ctx); + /** * FS_User::GetProgramLaunchInfo service function. * Inputs: @@ -600,7 +624,7 @@ private: * 0 : 0x08650140 * 1 : Result of function, 0 on success, otherwise error code */ - void SetSaveDataSecureValue(Kernel::HLERequestContext& ctx); + void ObsoletedSetSaveDataSecureValue(Kernel::HLERequestContext& ctx); /** * FS_User::GetSaveDataSecureValue service function. @@ -615,6 +639,57 @@ private: * 2 : If Secure Value doesn't exist, 0, if it exists, 1 * 3-4 : Secure Value */ + void ObsoletedGetSaveDataSecureValue(Kernel::HLERequestContext& ctx); + + /** + * FS_User::SetThisSaveDataSecureValue service function. + * Inputs: + * 1 : Secure Value Slot + * 2-3 : Secure Value + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void SetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx); + + /** + * FS_User::GetSaveDataSecureValue service function. + * Inputs: + * 1 : Secure Value Slot + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : If Secure Value doesn't exist, 0, if it exists, 1 + * 3 : Unknown + * 4-5 : Secure Value + */ + void GetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx); + + /** + * FS_User::SetSaveDataSecureValue service function. + * Inputs: + * 0 : 0x08750180 + * 1-2 : Archive + * 3 : Secure Value Slot + * 4 : value + * 5 : flush + * Outputs: + * 0 : header + * 1 : Result of function, 0 on success, otherwise error code + */ + void SetSaveDataSecureValue(Kernel::HLERequestContext& ctx); + + /** + * FS_User::GetSaveDataSecureValue service function. + * Inputs: + * 0 : 0x087600C0 + * 1-2 : Archive + * 2 : Secure Value slot + * Outputs: + * 0 : Header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : If Secure Value doesn't exist, 0, if it exists, 1 + * 3 : unknown + * 4-5 : Secure Value + */ void GetSaveDataSecureValue(Kernel::HLERequestContext& ctx); static ResultVal GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type); @@ -624,6 +699,8 @@ private: std::unordered_map program_info_map; std::string current_gamecard_path; + std::unordered_map product_info_map; + u32 priority = -1; ///< For SetPriority and GetPriority service functions Core::System& system; diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 1a7bd4494..b100533f9 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -282,7 +282,7 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr& process) // On real HW this is done with FS:Reg, but we can be lazy auto fs_user = Core::System::GetInstance().ServiceManager().GetService("fs:USER"); - fs_user->Register(process->GetObjectId(), process->codeset->program_id, filepath); + fs_user->RegisterProgramInfo(process->GetObjectId(), process->codeset->program_id, filepath); process->Run(48, Kernel::DEFAULT_STACK_SIZE); diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index bb6829d5b..9392c1fb8 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -174,7 +174,16 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr& process) auto fs_user = Core::System::GetInstance().ServiceManager().GetService( "fs:USER"); - fs_user->Register(process->process_id, process->codeset->program_id, filepath); + fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id, filepath); + + Service::FS::FS_USER::ProductInfo product_info{}; + std::memcpy(product_info.product_code.data(), overlay_ncch->ncch_header.product_code, + product_info.product_code.size()); + std::memcpy(&product_info.remaster_version, + overlay_ncch->exheader_header.codeset_info.flags.remaster_version, + sizeof(product_info.remaster_version)); + product_info.maker_code = overlay_ncch->ncch_header.maker_code; + fs_user->RegisterProductInfo(process->process_id, product_info); process->Run(priority, stack_size); return ResultStatus::Success;