diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index a8302af14..9f0560c56 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -103,12 +103,12 @@ bool Exists(const std::string& filename) { u64 GetFileModificationTimestamp(const std::string& filepath) { if (FileUtil::Exists(filepath)) { - struct stat64 file_info; + struct stat file_info; #ifdef _WIN32 if (0 == _wstat64(Common::UTF8ToUTF16W(filepath).c_str(), &file_info)) { #else - if (0 == stat64(filepath.c_str(), &file_info)) { + if (0 == stat(filepath.c_str(), &file_info)) { #endif return static_cast(file_info.st_mtime); } diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 4e094faa7..80950a0d7 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -157,4 +157,34 @@ inline DescriptorType GetDescriptorType(u32 descriptor) { return StaticBuffer; } +/** + * CheckBufferMappingTranslation function + * If the translation's buffer_mapping_permission match the @param(mapping_type) type, + * this function will return a true + */ +inline bool CheckBufferMappingTranslation(MappedBufferPermissions mapping_type, u32 size, + u32 translation) { + if (0x8 == translation) { + return false; + } + switch (mapping_type) { + case IPC::MappedBufferPermissions::R: + if (((size << 4) | 0xA) == translation) { + return true; + } + break; + case IPC::MappedBufferPermissions::W: + if (((size << 4) | 0xC) == translation) { + return true; + } + break; + case IPC::MappedBufferPermissions::RW: + if (((size << 4) | 0xE) == translation) { + return true; + } + break; + } + return false; +} + } // namespace IPC diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 09205e4b2..49145fa2d 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -13,6 +13,7 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/archive_ncch.h" @@ -285,6 +286,53 @@ ResultVal OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi return MakeResult(next_handle++); } +bool CheckArchiveHandle(ArchiveHandle handle) { + const auto handle_map_end_itr = handle_map.end(); + for (auto itr = handle_map.begin(); itr != handle_map_end_itr; ++itr) { + if (itr->first == handle) { + return true; + } + } + return false; +} + +ResultCode CommitSavedata(ArchiveHandle handle) { + ResultCode result = RESULT_SUCCESS; + + if (CheckArchiveHandle(handle)) { + result = RESULT_SUCCESS; + } else { + result = ResultCode(ErrorDescription::FS_ArchiveNotMounted, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); // 0xC8804465 + } + + u32 level = result.raw >> 27; + if (result.raw & 0x80000000) { + level -= 32; + } + if (level == 0xFFFFFFFF) { + LOG_ERROR(Service_FS, "Fatal Error : Invalid operation"); + } + return result; +} + +ResultCode GetTimeStamp(u32& input_buffer, u32& input_size, u32& output_buffer, u32& output_size) { + const std::string filepath = Common::UTF16ToUTF8( + std::u16string(reinterpret_cast(Memory::GetPointer(input_buffer), input_size))); + + u64 timestamp = FileUtil::GetFileModificationTimestamp(filepath); + + if (timestamp != 0) { + Memory::WriteBlock(output_buffer, ×tamp, output_size); + LOG_DEBUG(Service_FS, "timestamp=0x016llX", timestamp); + return RESULT_SUCCESS; + } else { + LOG_ERROR(Service_FS, "File was not exist, filepath :%s", filepath.c_str()); + return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, + ErrorLevel::Permanent); + } +} + ResultCode CloseArchive(ArchiveHandle handle) { if (handle_map.erase(handle) == 0) return ERR_INVALID_ARCHIVE_HANDLE; diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 7ba62ede0..674db5be9 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -84,6 +84,19 @@ protected: */ ResultVal OpenArchive(ArchiveIdCode id_code, FileSys::Path& archive_path); +/** + * ControlArchive action 0 : Commits save data changes + * @param handle Handle to the archive to control + */ +ResultCode CommitSavedata(ArchiveHandle handle); + +/** + * ControlArchive action 1 : Retrieves a file's last-modified timestamp + * @param input_buffer and @param input_size will get a pathname + * @param output_buffer and @param output_size : if file exist, return the file timestamp + */ +ResultCode GetTimeStamp(u32& input_buffer, u32& input_size, u32& output_buffer, u32& output_size); + /** * Closes an archive * @param handle Handle to the archive to close diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 337da1387..bc44bf7d1 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -420,12 +420,14 @@ static void OpenDirectory(Service::Interface* self) { /** * FS_User::OpenArchive service function * Inputs: + * 0 : Header Code [0x080C00C2] * 1 : Archive ID * 2 : Archive low path type * 3 : Archive low path size * 4 : (LowPathSize << 14) | 2 * 5 : Archive low path * Outputs: + * 0 : Header code * 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) @@ -443,6 +445,8 @@ static void OpenArchive(Service::Interface* self) { archive_path.DebugStr().c_str()); ResultVal handle = OpenArchive(archive_id, archive_path); + + cmd_buff[0] = IPC::MakeHeader(0x80C, 0x3, 0); // 0x080C00C0 cmd_buff[1] = handle.Code().raw; if (handle.Succeeded()) { cmd_buff[2] = *handle & 0xFFFFFFFF; @@ -455,6 +459,96 @@ static void OpenArchive(Service::Interface* self) { } } +/** + * FS_User::ControlArchive service function + * Inputs: + * 0 : Header code [0x080D0144] + * 1-2 : u64, Archive Handle + * 3 : Action + * 4 : Input Size + * 5 : Output Size + * 6 : (inputSize << 4) | 0xA + * 7 : void* Input + * 8 : (outputSize << 4) | 0xC + * 9 : void* Output + * Outputs: + * 0 : Header code + * 1 : Result code + * Notes: + * Action: + * 0 : Commits save data changes. + * Input : None + * Output : None + * 1 : Retrieves a file's last-modified timestamp + * Input : u16*, UTF-16 Path + * Output : u64, Time Stamp + * 30877 : Calls FSPXI command 0x00560102(unknown) + * Input : u16*, 12 bytes + * Output : 16 bytes + */ +static void ControlArchive(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); + u32 action = cmd_buff[3]; + u32 input_size = cmd_buff[4]; + u32 output_size = cmd_buff[5]; + u32 input_translation = cmd_buff[6]; + u32 input_buffer = cmd_buff[7]; + u32 output_translation = cmd_buff[8]; + u32 output_buffer = cmd_buff[9]; + + if (!IPC::CheckBufferMappingTranslation(IPC::MappedBufferPermissions::R, input_size, + input_translation)) { + cmd_buff[0] = IPC::MakeHeader(0x0, 1, 0); // 0x40 + cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent) + .raw; // 0xD9001830 + LOG_ERROR(Service_FS, "Check input_buffer_mapping_translation failed"); + return; + } + + if (!IPC::CheckBufferMappingTranslation(IPC::MappedBufferPermissions::W, output_size, + output_translation)) { + cmd_buff[0] = IPC::MakeHeader(0x0, 1, 0); // 0x40 + cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent) + .raw; // 0xD9001830 + LOG_ERROR(Service_FS, "Check output_buffer_mapping_translation failed"); + return; + } + + switch (action) { + case 0: // Action 0 : Commits save data changes. + cmd_buff[0] = IPC::MakeHeader(0x80D, 1, 4); // 0x080d0044 + cmd_buff[1] = CommitSavedata(archive_handle).raw; + cmd_buff[2] = input_translation; + cmd_buff[3] = input_buffer; + cmd_buff[4] = output_translation; + cmd_buff[5] = output_buffer; + LOG_WARNING(Service_FS, "(STUBBED) Commits save data changes, archive_handle=0x%08X", + archive_handle); + break; + case 1: // Action 1 : Retrieves a file's last-modified timestamp. + cmd_buff[0] = IPC::MakeHeader(0x80D, 1, 4); // 0x080d0044 + cmd_buff[1] = GetTimeStamp(input_buffer, input_size, output_buffer, output_size).raw; + cmd_buff[2] = input_translation; + cmd_buff[3] = input_buffer; + cmd_buff[4] = output_translation; + cmd_buff[5] = output_buffer; + LOG_WARNING(Service_FS, "Retrieves a file's last-modified timestamp"); + break; + case 30877: + default: + cmd_buff[0] = IPC::MakeHeader(0x80D, 1, 0); + cmd_buff[1] = ResultCode(ErrorDescription::NotImplemented, ErrorModule::FS, + ErrorSummary::NotSupported, ErrorLevel::Permanent) + .raw; + UNIMPLEMENTED(); + return; + } +} + /** * FS_User::CloseArchive service function * Inputs: @@ -462,13 +556,15 @@ static void OpenArchive(Service::Interface* self) { * 1 : Archive handle low word * 2 : Archive handle high word * Outputs: - * 0 : ??? TODO(yuriks): Verify return header + * 0 : Header code * 1 : Result of function, 0 on success, otherwise error code */ static void CloseArchive(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]); + + cmd_buff[0] = IPC::MakeHeader(0x80E, 0x1, 0); // 0x080E0040 cmd_buff[1] = CloseArchive(archive_handle).raw; } @@ -928,7 +1024,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x080A0244, RenameDirectory, "RenameDirectory"}, {0x080B0102, OpenDirectory, "OpenDirectory"}, {0x080C00C2, OpenArchive, "OpenArchive"}, - {0x080D0144, nullptr, "ControlArchive"}, + {0x080D0144, ControlArchive, "ControlArchive"}, {0x080E0080, CloseArchive, "CloseArchive"}, {0x080F0180, FormatThisUserSaveData, "FormatThisUserSaveData"}, {0x08100200, CreateLegacySystemSaveData, "CreateLegacySystemSaveData"},