mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Add missing FS:USER functions (#7051)
This commit is contained in:
		
							parent
							
								
									b231a22ea5
								
							
						
					
					
						commit
						597a2e8ead
					
				
					 5 changed files with 232 additions and 21 deletions
				
			
		|  | @ -221,6 +221,13 @@ public: | |||
|         return session; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns the client thread that made the service request. | ||||
|      */ | ||||
|     std::shared_ptr<Thread> ClientThread() const { | ||||
|         return thread; | ||||
|     } | ||||
| 
 | ||||
|     class WakeupCallback { | ||||
|     public: | ||||
|         virtual ~WakeupCallback() = default; | ||||
|  |  | |||
|  | @ -670,6 +670,26 @@ void FS_USER::GetFormatInfo(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<bool>(format_info->duplicate_data != 0); | ||||
| } | ||||
| 
 | ||||
| void FS_USER::GetProductInfo(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
| 
 | ||||
|     u32 process_id = rp.Pop<u32>(); | ||||
| 
 | ||||
|     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<ProductInfo>(product_info.value()); | ||||
| } | ||||
| 
 | ||||
| void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     const auto process_id = rp.Pop<u32>(); | ||||
|  | @ -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<ProgramInfo>(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<u64>(); | ||||
|     u32 secure_value_slot = rp.Pop<u32>(); | ||||
|     u32 unique_id = rp.Pop<u32>(); | ||||
|     u8 title_variation = rp.Pop<u8>(); | ||||
|     const u64 value = rp.Pop<u64>(); | ||||
|     const u32 secure_value_slot = rp.Pop<u32>(); | ||||
|     const u32 unique_id = rp.Pop<u32>(); | ||||
|     const u8 title_variation = rp.Pop<u8>(); | ||||
| 
 | ||||
|     // 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>(); | ||||
|     u32 unique_id = rp.Pop<u32>(); | ||||
|     u8 title_variation = rp.Pop<u8>(); | ||||
|     const u32 secure_value_slot = rp.Pop<u32>(); | ||||
|     const u32 unique_id = rp.Pop<u32>(); | ||||
|     const u8 title_variation = rp.Pop<u8>(); | ||||
| 
 | ||||
|     LOG_WARNING( | ||||
|         Service_FS, | ||||
|  | @ -816,7 +847,77 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { | |||
|     rb.Push<u64>(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<u32>(); | ||||
|     const u64 value = rp.Pop<u64>(); | ||||
| 
 | ||||
|     // 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<u32>(); | ||||
| 
 | ||||
|     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<bool>(false); // indicates that the secure value doesn't exist
 | ||||
|     rb.Push<bool>(false); // looks like a boolean value, purpose unknown
 | ||||
|     rb.Push<u64>(0);      // the secure value
 | ||||
| } | ||||
| 
 | ||||
| void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx); | ||||
|     const auto archive_handle = rp.PopRaw<ArchiveHandle>(); | ||||
|     const u32 secure_value_slot = rp.Pop<u32>(); | ||||
|     const u64 value = rp.Pop<u64>(); | ||||
|     const bool flush = rp.Pop<bool>(); | ||||
| 
 | ||||
|     // 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<ArchiveHandle>(); | ||||
|     const u32 secure_value_slot = rp.Pop<u32>(); | ||||
| 
 | ||||
|     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<bool>(false); // indicates that the secure value doesn't exist
 | ||||
|     rb.Push<bool>(false); // looks like a boolean value, purpose unknown
 | ||||
|     rb.Push<u64>(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::ProductInfo> 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<u16> 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"}, | ||||
|  |  | |||
|  | @ -4,10 +4,12 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <optional> | ||||
| #include <unordered_map> | ||||
| #include <boost/serialization/base_object.hpp> | ||||
| #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<FS_USER, ClientSlot> { | |||
| 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<u8, 0x10> 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<ProductInfo> GetProductInfo(u32 process_id); | ||||
| 
 | ||||
|     /// Gets the registered program info of a process.
 | ||||
|     ResultVal<ProgramInfo> 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<u16> GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type); | ||||
|  | @ -624,6 +699,8 @@ private: | |||
|     std::unordered_map<u32, ProgramInfo> program_info_map; | ||||
|     std::string current_gamecard_path; | ||||
| 
 | ||||
|     std::unordered_map<u32, ProductInfo> product_info_map; | ||||
| 
 | ||||
|     u32 priority = -1; ///< For SetPriority and GetPriority service functions
 | ||||
| 
 | ||||
|     Core::System& system; | ||||
|  |  | |||
|  | @ -282,7 +282,7 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr<Kernel::Process>& process) | |||
|     // On real HW this is done with FS:Reg, but we can be lazy
 | ||||
|     auto fs_user = | ||||
|         Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>("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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -174,7 +174,16 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process) | |||
|         auto fs_user = | ||||
|             Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>( | ||||
|                 "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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue