// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #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 { class System; } namespace Service::FS { class ArchiveManager; struct ProgramInfo { u64 program_id; MediaType media_type; INSERT_PADDING_BYTES(4); }; static_assert(sizeof(ProgramInfo) == 0x10, "ProgramInfo struct has incorrect size"); struct ClientSlot : public Kernel::SessionRequestHandler::SessionDataBase { // We retrieves program ID for client process on FS::Initialize(WithSDKVersion) // Real 3DS matches program ID and process ID based on data registered by loader via fs:REG, so // theoretically the program ID for FS client and for process codeset can mismatch if the loader // behaviour is modified. Since we don't emulate fs:REG mechanism, we assume the program ID is // the same as codeset ID and fetch from there directly. u64 program_id = 0; private: template void serialize(Archive& ar, const unsigned int) { ar& boost::serialization::base_object( *this); ar& program_id; } friend class boost::serialization::access; }; class FS_USER final : public ServiceFramework { public: explicit FS_USER(Core::System& system); // 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); if (info != program_info_map.end()) { return info->second; } else { return Result(FileSys::ErrCodes::ArchiveNotMounted, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status); } } private: void Initialize(Kernel::HLERequestContext& ctx); /** * FS_User::OpenFile service function * Inputs: * 1 : Transaction * 2 : Archive handle lower word * 3 : Archive handle upper word * 4 : Low path type * 5 : Low path size * 6 : Open flags * 7 : Attributes * 8 : (LowPathSize << 14) | 2 * 9 : Low path data pointer * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 3 : File handle */ void OpenFile(Kernel::HLERequestContext& ctx); /** * FS_User::OpenFileDirectly service function * Inputs: * 1 : Transaction * 2 : Archive ID * 3 : Archive low path type * 4 : Archive low path size * 5 : File low path type * 6 : File low path size * 7 : Flags * 8 : Attributes * 9 : (ArchiveLowPathSize << 14) | 0x802 * 10 : Archive low path * 11 : (FileLowPathSize << 14) | 2 * 12 : File low path * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 3 : File handle */ void OpenFileDirectly(Kernel::HLERequestContext& ctx); /* * FS_User::DeleteFile service function * Inputs: * 1 : Transaction * 2 : Archive handle lower word * 3 : Archive handle upper word * 4 : File path string type * 5 : File path string size * 7 : File path string data * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void DeleteFile(Kernel::HLERequestContext& ctx); /* * FS_User::RenameFile service function * Inputs: * 1 : Transaction * 2 : Source archive handle lower word * 3 : Source archive handle upper word * 4 : Source file path type * 5 : Source file path size * 6 : Dest archive handle lower word * 7 : Dest archive handle upper word * 8 : Dest file path type * 9 : Dest file path size * 11: Source file path string data * 13: Dest file path string * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void RenameFile(Kernel::HLERequestContext& ctx); /* * FS_User::DeleteDirectory service function * Inputs: * 1 : Transaction * 2 : Archive handle lower word * 3 : Archive handle upper word * 4 : Directory path string type * 5 : Directory path string size * 7 : Directory path string data * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void DeleteDirectory(Kernel::HLERequestContext& ctx); /* * FS_User::DeleteDirectoryRecursively service function * Inputs: * 0 : Command header 0x08070142 * 1 : Transaction * 2 : Archive handle lower word * 3 : Archive handle upper word * 4 : Directory path string type * 5 : Directory path string size * 7 : Directory path string data * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx); /* * FS_User::CreateFile service function * Inputs: * 0 : Command header 0x08080202 * 1 : Transaction * 2 : Archive handle lower word * 3 : Archive handle upper word * 4 : File path string type * 5 : File path string size * 6 : File attributes * 7-8 : File size * 10: File path string data * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void CreateFile(Kernel::HLERequestContext& ctx); /* * FS_User::CreateDirectory service function * Inputs: * 1 : Transaction * 2 : Archive handle lower word * 3 : Archive handle upper word * 4 : Directory path string type * 5 : Directory path string size * 6 : Directory attributes * 8 : Directory path string data * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void CreateDirectory(Kernel::HLERequestContext& ctx); /* * FS_User::RenameDirectory service function * Inputs: * 1 : Transaction * 2 : Source archive handle lower word * 3 : Source archive handle upper word * 4 : Source dir path type * 5 : Source dir path size * 6 : Dest archive handle lower word * 7 : Dest archive handle upper word * 8 : Dest dir path type * 9 : Dest dir path size * 11: Source dir path string data * 13: Dest dir path string * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void RenameDirectory(Kernel::HLERequestContext& ctx); /** * FS_User::OpenDirectory service function * Inputs: * 1 : Archive handle low word * 2 : Archive handle high word * 3 : Low path type * 4 : Low path size * 7 : (LowPathSize << 14) | 2 * 8 : Low path data pointer * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 3 : Directory handle */ void OpenDirectory(Kernel::HLERequestContext& ctx); /** * FS_User::OpenArchive service function * Inputs: * 1 : Archive ID * 2 : Archive low path type * 3 : Archive low path size * 4 : (LowPathSize << 14) | 2 * 5 : Archive low path * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Archive handle lower word (unused) * 3 : Archive handle upper word (same as file handle) */ void OpenArchive(Kernel::HLERequestContext& ctx); /** * FS_User::ControlArchive service function * Inputs: * 1-2 : Archive Handle * 3 : Action * 4 : Input Size * 5 : Output Size * 6 : (Input Size << 4) | 0xA * 7 : Input Pointer * 8 : (Output Size << 4) | 0xC * 9 : Output Pointer * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void ControlArchive(Kernel::HLERequestContext& ctx); /** * FS_User::CloseArchive service function * Inputs: * 0 : 0x080E0080 * 1 : Archive handle low word * 2 : Archive handle high word * Outputs: * 0 : 0x080E0040 * 1 : Result of function, 0 on success, otherwise error code */ void CloseArchive(Kernel::HLERequestContext& ctx); /* * FS_User::IsSdmcDetected service function * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Whether the Sdmc could be detected */ void IsSdmcDetected(Kernel::HLERequestContext& ctx); /** * FS_User::IsSdmcWriteable service function * Outputs: * 0 : Command header 0x08180080 * 1 : Result of function, 0 on success, otherwise error code * 2 : Whether the Sdmc is currently writeable */ void IsSdmcWriteable(Kernel::HLERequestContext& ctx); /** * FS_User::FormatSaveData service function, * formats the SaveData specified by the input path. * Inputs: * 0 : 0x084C0242 * 1 : Archive ID * 2 : Archive path type * 3 : Archive path size * 4 : Size in Blocks (1 block = 512 bytes) * 5 : Number of directories * 6 : Number of files * 7 : Directory bucket count * 8 : File bucket count * 9 : Duplicate data * 10 : (PathSize << 14) | 2 * 11 : Archive low path * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void FormatSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::FormatThisUserSaveData service function * Inputs: * 0 : 0x080F0180 * 1 : Size in Blocks (1 block = 512 bytes) * 2 : Number of directories * 3 : Number of files * 4 : Directory bucket count * 5 : File bucket count * 6 : Duplicate data * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void FormatThisUserSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::GetFreeBytes service function * Inputs: * 0: 0x08120080 * 1: Archive handle low word * 2: Archive handle high word * Outputs: * 1: Result of function, 0 on success, otherwise error code * 2: Free byte count low word * 3: Free byte count high word */ void GetFreeBytes(Kernel::HLERequestContext& ctx); /** * FS_User::GetSdmcArchiveResource service function. * Inputs: * 0 : 0x08140000 * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Sector byte-size * 3 : Cluster byte-size * 4 : Partition capacity in clusters * 5 : Available free space in clusters */ void GetSdmcArchiveResource(Kernel::HLERequestContext& ctx); /** * FS_User::GetNandArchiveResource service function. * Inputs: * 0 : 0x08150000 * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Sector byte-size * 3 : Cluster byte-size * 4 : Partition capacity in clusters * 5 : Available free space in clusters */ void GetNandArchiveResource(Kernel::HLERequestContext& ctx); /** * FS_User::CreateExtSaveData service function * Inputs: * 0 : 0x08510242 * 1 : Media type (NAND / SDMC) * 2 : Low word of the saveid to create * 3 : High word of the saveid to create * 4 : Unknown * 5 : Number of directories * 6 : Number of files * 7-8 : Size limit * 9 : Size of the SMDH icon * 10: (SMDH Size << 4) | 0x0000000A * 11: Pointer to the SMDH icon for the new ExtSaveData * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void CreateExtSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::DeleteExtSaveData service function * Inputs: * 0 : 0x08520100 * 1 : Media type (NAND / SDMC) * 2 : Low word of the saveid to create * 3 : High word of the saveid to create * 4 : Unknown * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void DeleteExtSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::CardSlotIsInserted service function. * Inputs: * 0 : 0x08210000 * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Whether there is a game card inserted into the slot or not. */ void CardSlotIsInserted(Kernel::HLERequestContext& ctx); /** * FS_User::DeleteSystemSaveData service function. * Inputs: * 0 : 0x08570080 * 1 : High word of the SystemSaveData id to delete * 2 : Low word of the SystemSaveData id to delete * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void DeleteSystemSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::CreateSystemSaveData service function. * Inputs: * 0 : 0x08560240 * 1 : u8 MediaType of the system save data * 2 : SystemSaveData id to create * 3 : Total size * 4 : Block size * 5 : Number of directories * 6 : Number of files * 7 : Directory bucket count * 8 : File bucket count * 9 : u8 Whether to duplicate data or not * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void CreateSystemSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::CreateLegacySystemSaveData service function. * This function appears to be obsolete and seems to have been replaced by * command 0x08560240 (CreateSystemSaveData). * * Inputs: * 0 : 0x08100200 * 1 : SystemSaveData id to create * 2 : Total size * 3 : Block size * 4 : Number of directories * 5 : Number of files * 6 : Directory bucket count * 7 : File bucket count * 8 : u8 Duplicate data * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void CreateLegacySystemSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::InitializeWithSdkVersion service function. * Inputs: * 0 : 0x08610042 * 1 : Used SDK Version * 2 : ProcessId Header * 3 : placeholder for ProcessId * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void InitializeWithSdkVersion(Kernel::HLERequestContext& ctx); /** * FS_User::SetPriority service function. * Inputs: * 0 : 0x08620040 * 1 : priority * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void SetPriority(Kernel::HLERequestContext& ctx); /** * FS_User::GetPriority service function. * Inputs: * 0 : 0x08630000 * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : priority */ void GetPriority(Kernel::HLERequestContext& ctx); /** * FS_User::GetArchiveResource service function. * Inputs: * 0 : 0x08490040 * 1 : Media type * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Sector byte-size * 3 : Cluster byte-size * 4 : Partition capacity in clusters * 5 : Available free space in clusters */ void GetArchiveResource(Kernel::HLERequestContext& ctx); /** * FS_User::GetFormatInfo service function. * Inputs: * 0 : 0x084500C2 * 1 : Archive ID * 2 : Archive path type * 3 : Archive path size * 4 : (PathSize << 14) | 2 * 5 : Archive low path * Outputs: * 0 : 0x08450140 * 1 : Result of function, 0 on success, otherwise error code * 2 : Total size * 3 : Number of directories * 4 : Number of files * 5 : Duplicate data */ 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: * 0 : 0x082F0040 * 1 : Process ID * Outputs: * 0 : 0x082F0140 * 1 : Result of function, 0 on success, otherwise error code * 2-3 : Program ID * 4 : Media type * 5 : Unknown */ void GetProgramLaunchInfo(Kernel::HLERequestContext& ctx); /** * FS_User::Obsoleted_3_0_CreateExtSaveData service function. * Inputs: * 0 : 0x08300182 * 1 : Media type * 2 : Low word of the saveid to create * 3 : High word of the saveid to create * 4 : Size of the SMDH icon * 5 : Number of directories * 6 : Number of files * 7 : (SMDH Size << 4) | 0x0000000A * 8 : Pointer to the SMDH icon for the new ExtSaveData * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void ObsoletedCreateExtSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::Obsoleted_3_0_DeleteExtSaveData service function. * Inputs: * 0 : 0x08350080 * 1 : Media type * 2 : Save ID Low (high is always 0x00000000) * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void ObsoletedDeleteExtSaveData(Kernel::HLERequestContext& ctx); /** * FS_User::GetSpecialContentIndex service function. * Inputs: * 0 : 0x083A0100 * 1 : Media type * 2-3 : Program ID * 4 : Special content type * Outputs: * 0 : 0x083A0080 * 1 : Result of function, 0 on success, otherwise error code * 2 : Special content index */ void GetSpecialContentIndex(Kernel::HLERequestContext& ctx); /** * FS_User::GetNumSeeds service function. * Inputs: * 0 : 0x087D0000 * Outputs: * 0 : 0x087D0080 * 1 : Result of function, 0 on success, otherwise error code * 2 : Number of seeds in the SEEDDB */ void GetNumSeeds(Kernel::HLERequestContext& ctx); /** * FS_User::AddSeed service function. * Inputs: * 0 : 0x087A0180 * 1-2 : u64, Title ID * 3-6 : Seed * Outputs: * 0 : 0x087A0040 * 1 : Result of function, 0 on success, otherwise error code */ void AddSeed(Kernel::HLERequestContext& ctx); /** * FS_User::SetSaveDataSecureValue service function. * Inputs: * 0 : 0x08650140 * 1-2 : Secure Value * 3 : Secure Value Slot * 4 : Title Id * 5 : Title Variation * Outputs: * 0 : 0x08650140 * 1 : Result of function, 0 on success, otherwise error code */ void ObsoletedSetSaveDataSecureValue(Kernel::HLERequestContext& ctx); /** * FS_User::GetSaveDataSecureValue service function. * Inputs: * 0 : 0x086600C0 * 1 : Secure Value Slot * 2 : Title Id * 3 : Title Variation * Outputs: * 0 : 0x086600C0 * 1 : Result of function, 0 on success, otherwise error code * 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); static ResultVal GetSpecialContentIndexFromTMD(MediaType media_type, u64 title_id, SpecialContentType type); 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; ArchiveManager& archives; template void serialize(Archive& ar, const unsigned int) { ar& boost::serialization::base_object(*this); ar& priority; } friend class boost::serialization::access; }; void InstallInterfaces(Core::System& system); } // namespace Service::FS SERVICE_CONSTRUCT(Service::FS::FS_USER) BOOST_CLASS_EXPORT_KEY(Service::FS::FS_USER) BOOST_CLASS_EXPORT_KEY(Service::FS::ClientSlot)