#pragma once #include "fake_process.hpp" #include "../platform/fs.hpp" #include #include namespace HLE { using Platform::FS::ArchiveHandle; class FakeFS; using OS::FakeProcess; using OS::FakeThread; using OS::RESULT_OK; using Result = OS::OS::Result; using OS::HandleTable; using OS::Handle; using OS::Event; using OS::ProcessId; struct FileContext { spdlog::logger& logger; }; /** * Interface for an internal buffer used as the data source/target for file * read/write operations. * * Usually, files operate on emulated memory, but it's beneficial for code * reuse in frontend code to enable file implementations to work with * host memory, too. This class enables this by abstracting away the notion * of emulated memory. */ class FileBuffer { public: /// Read the specified number of bytes from the internal buffer to dest // virtual void Read(char* dest, uint32_t num_bytes) = 0; /// Write the specified number of bytes from the source to the internal buffer virtual void Write(char* source, uint32_t num_bytes) = 0; }; class File { friend class SubFile; void Fail(); // True if this file is the reference file of a SubFile. While a sub file // is open, no operations may be requested on the parent file. bool has_subfile = false; public: virtual ~File() = default; // Returns result code and number of bytes read (0 on error) virtual OS::OS::ResultAnd Read(FileContext&, FakeFS&, uint64_t offset, uint32_t num_bytes, FileBuffer& target)/* = 0*/; // Returns result code and number of bytes written (0 on error) virtual OS::OS::ResultAnd Write(FakeThread& thread, FakeFS&, uint64_t offset, uint32_t num_bytes, uint32_t options, uint32_t buffer_addr)/* = 0*/; // Returns result code and file size in bytes (0 on error) virtual OS::OS::ResultAnd GetSize(FakeThread& thread, FakeFS&)/* = 0*/ { Fail(); return std::make_tuple(RESULT_OK, 0);} virtual OS::OS::ResultAnd<> SetSize(FakeThread& thread, FakeFS&, uint64_t size) { Fail(); return std::make_tuple(RESULT_OK);} virtual const char* GetFileName() { static std::string buffer; buffer = std::string("(unknown ") + typeid(*this).name() + ")"; return buffer.c_str(); } bool HasOpenSubFile() const noexcept; }; class Directory; class Archive { public: virtual ~Archive() = default; virtual Platform::FS::ArchiveFormatInfo GetFormatInfo(FakeThread&, FakeFS&); virtual std::pair> OpenFile(FakeThread&, FakeFS&, uint32_t transaction, uint32_t open_flags, uint32_t attributes, uint32_t path_type, IPC::StaticBuffer path) = 0; virtual Result CreateDirectory(FakeThread&, FakeFS&, uint32_t path_type, IPC::StaticBuffer path) = 0; virtual std::pair> OpenDirectory(FakeThread&, FakeFS&, uint32_t path_type, IPC::StaticBuffer path) = 0; virtual Result CreateFile(FakeThread&, FakeFS&, uint32_t transaction, uint32_t attributes, uint64_t size, uint32_t path_type, IPC::StaticBuffer path); virtual Result DeleteFile(FakeThread&, FakeFS&, uint32_t transaction, uint32_t path_type, IPC::StaticBuffer path) = 0; virtual Result RenameFile(FakeThread&, FakeFS&, uint32_t transaction, uint32_t source_path_type, IPC::StaticBuffer source_path, uint32_t target_path_type, IPC::StaticBuffer target_path) = 0; virtual Result DeleteDirectory(FakeThread&, FakeFS&, uint32_t transaction, bool recursive, uint32_t path_type, IPC::StaticBuffer path) = 0; }; struct FSServiceHelper : HLE::OS::ServiceHelper { // nullptr for ports; uint32_t (port handle index) for fs:USER/LDR sessions; others are self-explanatory using HandleData = std::variant, std::shared_ptr>; template uint32_t Append(HandleTable::Entry entry, FSServiceHelper::HandleData data = nullptr); void Erase(int32_t index) override; void OnNewSession(int32_t port_index, HandleTable::Entry session) override; /// Replaces the File object in the indexed HandleData with a new one. The previous object will be destroyed. void ReplaceFileServer(int32_t index, std::unique_ptr new_file); std::vector handle_data; }; class FakeFS { struct ProcessInfo { Platform::FS::ProgramInfo program; Platform::FS::StorageInfo storage; }; public: // TODO: Un-public-ize FSServiceHelper service; // Map from session handles to the ProcessId assigned via FSUser::Initialize std::unordered_map fsuser_process_ids; std::unordered_map process_infos; std::unordered_map> archives; uint64_t next_archive_handle = 0; // TODO: Clarify what thread these belong to, and make sure they are *NOT* shared between threads! uint32_t static_buffer_size = 0x1000; uint32_t static_buffer_addr[3]; Handle pxifs_session; // handle table indices uint32_t fsuser_handle_index; uint32_t fsldr_handle_index; void FSRegThread(FakeThread& thread); void FSUserThread(FakeThread& thread); decltype(HLE::OS::ServiceHelper::SendReply) RegCommandHandler(FakeThread& thread, IPC::CommandHeader header); decltype(HLE::OS::ServiceHelper::SendReply) UserCommandHandler(FakeThread& thread, Handle sender, const IPC::CommandHeader& header); OS::OS::ResultAnd<> OnReg_Register(FakeThread& thread, ProcessId pid, Platform::PXI::PM::ProgramHandle program_handle, Platform::PXI::PM::ProgramInfo program_info, Platform::FS::StorageInfo); OS::OS::ResultAnd<> OnReg_Unregister(FakeThread& thread, ProcessId pid); OS::OS::ResultAnd<> OnReg_CheckHostLoadId(FakeThread& thread, Platform::PXI::PM::ProgramHandle program_handle); std::tuple HandleInitialize(FakeThread& thread, Handle session_handle, ProcessId id); std::tuple HandleInitializeWithSdkVersion(FakeThread& thread, Handle session_handle, uint32_t version, ProcessId id); std::tuple HandleOpenFile(FakeThread& thread, ProcessId session_id, uint32_t transaction, uint64_t archive_handle, uint32_t path_type, uint32_t path_size, uint32_t open_flags, uint32_t attributes, IPC::StaticBuffer path); std::tuple HandleOpenFileDirectly(FakeThread& thread, ProcessId session_id, uint32_t transaction, uint32_t archive_id, uint32_t archive_path_type, uint32_t archive_path_size, uint32_t file_path_type, uint32_t file_path_size, uint32_t open_flags, uint32_t attributes, const IPC::StaticBuffer& archive_path, const IPC::StaticBuffer& file_path); std::tuple HandleDeleteFile(FakeThread& thread, ProcessId session_id, uint32_t transaction, uint64_t archive_handle, uint32_t file_path_type, uint32_t file_path_size, IPC::StaticBuffer file_path); std::tuple HandleRenameFile(FakeThread& thread, uint32_t transaction, uint64_t source_archive_handle, uint32_t source_file_path_type, uint32_t source_file_path_size, uint64_t target_archive_handle, uint32_t target_file_path_type, uint32_t target_file_path_size, IPC::StaticBuffer source_file_path, IPC::StaticBuffer target_file_path); std::tuple HandleDeleteDirectory(FakeThread& thread, ProcessId session_id, bool recursive, uint32_t transaction, ArchiveHandle archive_handle, uint32_t dir_path_type, uint32_t dir_path_size, IPC::StaticBuffer dir_path); std::tuple HandleCreateFile(FakeThread& thread, ProcessId session_id, uint32_t transaction, uint64_t archive_handle, uint32_t file_path_type, uint32_t file_path_size, uint32_t attributes, uint64_t initial_file_size, IPC::StaticBuffer file_path); std::tuple HandleCreateDirectory(FakeThread& thread, ProcessId session_id, uint32_t transaction, uint64_t archive_handle, uint32_t dir_path_type, uint32_t dir_path_size, uint32_t attributes, IPC::StaticBuffer dir_path); std::tuple HandleOpenDirectory(FakeThread& thread, ProcessId session_id, uint64_t archive_handle, uint32_t path_type, uint32_t path_size, IPC::StaticBuffer path); std::tuple HandleOpenArchive(FakeThread& thread, ProcessId session_id, uint32_t archive_id, uint32_t path_type, uint32_t path_size, IPC::StaticBuffer path); std::tuple HandleControlArchive(FakeThread& thread, ProcessId session_id, uint64_t archive_handle, uint32_t action, uint32_t input_size, uint32_t output_size, IPC::MappedBuffer input, IPC::MappedBuffer output); std::tuple HandleCloseArchive(FakeThread& thread, ProcessId session_id, uint64_t archive_handle); std::tuple HandleFormatOwnSaveData(FakeThread& thread, ProcessId session_id, uint32_t block_size, uint32_t num_dirs, uint32_t num_files, uint32_t num_dir_buckets, uint32_t num_file_buckets, uint32_t unknown); std::tuple HandleIsSdmcDetected(FakeThread& thread); std::tuple HandleGetPriority(FakeThread& thread); std::tuple HandleGetProgramLaunchInfo(FakeThread& thread, ProcessId id); std::tuple HandleGetFormatInfo(FakeThread& thread, ProcessId session_id, Platform::FS::ArchiveId archive_id, uint32_t archive_path_type, uint32_t archive_path_size, IPC::StaticBuffer archive_path); std::tuple HandleFormatSaveData(FakeThread& thread, ProcessId session_id, Platform::FS::ArchiveId archive_id, uint32_t path_type, uint32_t path_size, uint32_t num_blocks, uint32_t num_dirs, uint32_t num_files, uint32_t num_dir_buckets, uint32_t num_file_buckets, uint32_t unknown, IPC::StaticBuffer archive_path); std::tuple HandleCreateExtSaveData(FakeThread& thread, ProcessId session_id, const Platform::FS::ExtSaveDataInfo& info, uint32_t num_directories, uint32_t num_files, uint64_t size_limit, uint32_t smdh_size, IPC::MappedBuffer smdh); std::tuple HandleCreateExtSaveDataLegacy(FakeThread& thread, ProcessId session_id, uint32_t media_type, uint64_t save_id, uint32_t smdh_size, uint32_t num_directories, uint32_t num_files, IPC::MappedBuffer smdh); std::tuple HandleDeleteExtSaveData(FakeThread& thread, ProcessId session_id, const Platform::FS::ExtSaveDataInfo& info); std::tuple HandleCreateSystemSaveData(FakeThread& thread, ProcessId session_id, uint32_t media_type, uint32_t save_id, uint32_t total_size, uint32_t block_size, uint32_t num_directories, uint32_t num_files, uint32_t unk1, uint32_t unk2, uint32_t unk3); std::tuple HandleUpdateSha256Context( FakeThread& thread, ProcessId session_id, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, IPC::MappedBuffer); FakeProcess& process; spdlog::logger& logger; FileContext file_context { logger }; public: FakeFS(FakeThread& thread); ~FakeFS(); }; } // namespace HLE