mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu.git
				synced 2025-05-12 00:45:25 +00:00 
			
		
		
		
	Merge pull request #1302 from Subv/save_fix
HLE/FS: Fixed many corner cases in our file handling
This commit is contained in:
		
						commit
						b83e95727f
					
				@ -11,6 +11,7 @@
 | 
			
		||||
 | 
			
		||||
#include "common/bit_field.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
 | 
			
		||||
#include "core/hle/result.h"
 | 
			
		||||
 | 
			
		||||
@ -62,6 +63,14 @@ private:
 | 
			
		||||
    std::u16string u16str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ArchiveFormatInfo {
 | 
			
		||||
    u32_le total_size; ///< The pre-defined size of the archive, as specified in the Create or Format call
 | 
			
		||||
    u32_le number_directories; ///< The pre-defined number of directories in the archive, as specified in the Create or Format call
 | 
			
		||||
    u32_le number_files; ///< The pre-defined number of files in the archive, as specified in the Create or Format call
 | 
			
		||||
    u8 duplicate_data; ///< Whether the archive should duplicate the data, as specified in the Create or Format call
 | 
			
		||||
};
 | 
			
		||||
static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
 | 
			
		||||
 | 
			
		||||
class ArchiveBackend : NonCopyable {
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~ArchiveBackend() {
 | 
			
		||||
@ -76,16 +85,16 @@ public:
 | 
			
		||||
     * Open a file specified by its path, using the specified mode
 | 
			
		||||
     * @param path Path relative to the archive
 | 
			
		||||
     * @param mode Mode to open the file with
 | 
			
		||||
     * @return Opened file, or nullptr
 | 
			
		||||
     * @return Opened file, or error code
 | 
			
		||||
     */
 | 
			
		||||
    virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0;
 | 
			
		||||
    virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete a file specified by its path
 | 
			
		||||
     * @param path Path relative to the archive
 | 
			
		||||
     * @return Whether the file could be deleted
 | 
			
		||||
     * @return Result of the operation
 | 
			
		||||
     */
 | 
			
		||||
    virtual bool DeleteFile(const Path& path) const = 0;
 | 
			
		||||
    virtual ResultCode DeleteFile(const Path& path) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rename a File specified by its path
 | 
			
		||||
@ -108,7 +117,7 @@ public:
 | 
			
		||||
     * @param size The size of the new file, filled with zeroes
 | 
			
		||||
     * @return File creation result code
 | 
			
		||||
     */
 | 
			
		||||
    virtual ResultCode CreateFile(const Path& path, u32 size) const = 0;
 | 
			
		||||
    virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a directory specified by its path
 | 
			
		||||
@ -159,9 +168,17 @@ public:
 | 
			
		||||
    /**
 | 
			
		||||
     * Deletes the archive contents and then re-creates the base folder
 | 
			
		||||
     * @param path Path to the archive
 | 
			
		||||
     * @param format_info Format information for the new archive
 | 
			
		||||
     * @return ResultCode of the operation, 0 on success
 | 
			
		||||
     */
 | 
			
		||||
    virtual ResultCode Format(const Path& path) = 0;
 | 
			
		||||
    virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the format info about the archive with the specified path
 | 
			
		||||
     * @param path Path to the archive
 | 
			
		||||
     * @return Format information about the archive or error code
 | 
			
		||||
     */
 | 
			
		||||
    virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
 | 
			
		||||
@ -58,7 +58,7 @@ Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location, bool shared)
 | 
			
		||||
        : mount_point(GetExtDataContainerPath(mount_location, shared)) {
 | 
			
		||||
        : shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) {
 | 
			
		||||
    LOG_INFO(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -74,21 +74,59 @@ bool ArchiveFactory_ExtSaveData::Initialize() {
 | 
			
		||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) {
 | 
			
		||||
    std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/";
 | 
			
		||||
    if (!FileUtil::Exists(fullpath)) {
 | 
			
		||||
        // TODO(Subv): Check error code, this one is probably wrong
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
 | 
			
		||||
            ErrorSummary::InvalidState, ErrorLevel::Status);
 | 
			
		||||
        // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
 | 
			
		||||
        // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
 | 
			
		||||
        if (!shared) {
 | 
			
		||||
            return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
 | 
			
		||||
                              ErrorSummary::InvalidState, ErrorLevel::Status);
 | 
			
		||||
        } else {
 | 
			
		||||
            return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
 | 
			
		||||
                              ErrorSummary::InvalidState, ErrorLevel::Status);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    auto archive = Common::make_unique<DiskArchive>(fullpath);
 | 
			
		||||
    return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path) {
 | 
			
		||||
ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
 | 
			
		||||
    // These folders are always created with the ExtSaveData
 | 
			
		||||
    std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/";
 | 
			
		||||
    std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/";
 | 
			
		||||
    FileUtil::CreateFullPath(user_path);
 | 
			
		||||
    FileUtil::CreateFullPath(boss_path);
 | 
			
		||||
 | 
			
		||||
    // Write the format metadata
 | 
			
		||||
    std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
 | 
			
		||||
    FileUtil::IOFile file(metadata_path, "wb");
 | 
			
		||||
 | 
			
		||||
    if (!file.IsOpen()) {
 | 
			
		||||
        // TODO(Subv): Find the correct error code
 | 
			
		||||
        return ResultCode(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    file.WriteBytes(&format_info, sizeof(format_info));
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Path& path) const {
 | 
			
		||||
    std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
 | 
			
		||||
    FileUtil::IOFile file(metadata_path, "rb");
 | 
			
		||||
 | 
			
		||||
    if (!file.IsOpen()) {
 | 
			
		||||
        LOG_ERROR(Service_FS, "Could not open metadata information for archive");
 | 
			
		||||
        // TODO(Subv): Verify error code
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ArchiveFormatInfo info = {};
 | 
			
		||||
    file.ReadBytes(&info, sizeof(info));
 | 
			
		||||
    return MakeResult<ArchiveFormatInfo>(info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data, size_t icon_size) {
 | 
			
		||||
    std::string game_path = FileSys::GetExtSaveDataPath(GetMountPoint(), path);
 | 
			
		||||
    FileUtil::IOFile icon_file(game_path + "icon", "wb");
 | 
			
		||||
    icon_file.WriteBytes(icon_data, icon_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
 | 
			
		||||
@ -31,10 +31,19 @@ public:
 | 
			
		||||
    std::string GetName() const override { return "ExtSaveData"; }
 | 
			
		||||
 | 
			
		||||
    ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
 | 
			
		||||
    ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
 | 
			
		||||
 | 
			
		||||
    const std::string& GetMountPoint() const { return mount_point; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Writes the SMDH icon of the ExtSaveData to file
 | 
			
		||||
     * @param path Path of this ExtSaveData
 | 
			
		||||
     * @param icon_data Binary data of the icon
 | 
			
		||||
     * @param icon_size Size of the icon data
 | 
			
		||||
     */
 | 
			
		||||
    void WriteIcon(const Path& path, const u8* icon_data, size_t icon_size);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    /**
 | 
			
		||||
     * This holds the full directory path for this archive, it is only set after a successful call
 | 
			
		||||
@ -42,6 +51,7 @@ private:
 | 
			
		||||
     * See GetExtSaveDataPath for the code that extracts this data from an archive path.
 | 
			
		||||
     */
 | 
			
		||||
    std::string mount_point;
 | 
			
		||||
    bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData archive
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -29,11 +29,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_RomFS::Open(const Path
 | 
			
		||||
    return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode ArchiveFactory_RomFS::Format(const Path& path) {
 | 
			
		||||
ResultCode ArchiveFactory_RomFS::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
 | 
			
		||||
    LOG_ERROR(Service_FS, "Attempted to format a RomFS archive.");
 | 
			
		||||
    // TODO: Verify error code
 | 
			
		||||
    return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS,
 | 
			
		||||
            ErrorSummary::NotSupported, ErrorLevel::Permanent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_RomFS::GetFormatInfo(const Path& path) const {
 | 
			
		||||
    // TODO(Subv): Implement
 | 
			
		||||
    LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
 | 
			
		||||
    return ResultCode(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,8 @@ public:
 | 
			
		||||
 | 
			
		||||
    std::string GetName() const override { return "RomFS"; }
 | 
			
		||||
    ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
 | 
			
		||||
    ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::shared_ptr<FileUtil::IOFile> romfs_file;
 | 
			
		||||
 | 
			
		||||
@ -26,11 +26,17 @@ static std::string GetSaveDataContainerPath(const std::string& sdmc_directory) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
 | 
			
		||||
    u32 high = program_id >> 32;
 | 
			
		||||
    u32 low = program_id & 0xFFFFFFFF;
 | 
			
		||||
    u32 high = (u32)(program_id >> 32);
 | 
			
		||||
    u32 low = (u32)(program_id & 0xFFFFFFFF);
 | 
			
		||||
    return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high, low);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 program_id) {
 | 
			
		||||
    u32 high = (u32)(program_id >> 32);
 | 
			
		||||
    u32 low = (u32)(program_id & 0xFFFFFFFF);
 | 
			
		||||
    return Common::StringFromFormat("%s%08x/%08x/data/00000001.metadata", mount_location.c_str(), high, low);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ArchiveFactory_SaveData::ArchiveFactory_SaveData(const std::string& sdmc_directory)
 | 
			
		||||
        : mount_point(GetSaveDataContainerPath(sdmc_directory)) {
 | 
			
		||||
    LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
 | 
			
		||||
@ -51,11 +57,35 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P
 | 
			
		||||
    return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode ArchiveFactory_SaveData::Format(const Path& path) {
 | 
			
		||||
ResultCode ArchiveFactory_SaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
 | 
			
		||||
    std::string concrete_mount_point = GetSaveDataPath(mount_point, Kernel::g_current_process->codeset->program_id);
 | 
			
		||||
    FileUtil::DeleteDirRecursively(concrete_mount_point);
 | 
			
		||||
    FileUtil::CreateFullPath(concrete_mount_point);
 | 
			
		||||
 | 
			
		||||
    // Write the format metadata
 | 
			
		||||
    std::string metadata_path = GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id);
 | 
			
		||||
    FileUtil::IOFile file(metadata_path, "wb");
 | 
			
		||||
 | 
			
		||||
    if (file.IsOpen()) {
 | 
			
		||||
        file.WriteBytes(&format_info, sizeof(format_info));
 | 
			
		||||
        return RESULT_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const {
 | 
			
		||||
    std::string metadata_path = GetSaveDataMetadataPath(mount_point, Kernel::g_current_process->codeset->program_id);
 | 
			
		||||
    FileUtil::IOFile file(metadata_path, "rb");
 | 
			
		||||
 | 
			
		||||
    if (!file.IsOpen()) {
 | 
			
		||||
        LOG_ERROR(Service_FS, "Could not open metadata information for archive");
 | 
			
		||||
        // TODO(Subv): Verify error code
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ArchiveFormatInfo info = {};
 | 
			
		||||
    file.ReadBytes(&info, sizeof(info));
 | 
			
		||||
    return MakeResult<ArchiveFormatInfo>(info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,9 @@ public:
 | 
			
		||||
    std::string GetName() const override { return "SaveData"; }
 | 
			
		||||
 | 
			
		||||
    ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
 | 
			
		||||
 | 
			
		||||
    ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::string mount_point;
 | 
			
		||||
 | 
			
		||||
@ -48,11 +48,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co
 | 
			
		||||
    return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path) {
 | 
			
		||||
ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
 | 
			
		||||
    LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive.");
 | 
			
		||||
    // TODO: Verify error code
 | 
			
		||||
    return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS,
 | 
			
		||||
        ErrorSummary::NotSupported, ErrorLevel::Permanent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const {
 | 
			
		||||
    // TODO(Subv): Implement
 | 
			
		||||
    LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
 | 
			
		||||
    return ResultCode(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,8 @@ public:
 | 
			
		||||
    std::string GetName() const override { return "SaveDataCheck"; }
 | 
			
		||||
 | 
			
		||||
    ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
 | 
			
		||||
    ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::string mount_point;
 | 
			
		||||
 | 
			
		||||
@ -40,9 +40,14 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path&
 | 
			
		||||
    return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode ArchiveFactory_SDMC::Format(const Path& path) {
 | 
			
		||||
ResultCode ArchiveFactory_SDMC::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
 | 
			
		||||
    // This is kind of an undesirable operation, so let's just ignore it. :)
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path) const {
 | 
			
		||||
    // TODO(Subv): Implement
 | 
			
		||||
    LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
 | 
			
		||||
    return ResultCode(-1);
 | 
			
		||||
}
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,8 @@ public:
 | 
			
		||||
    std::string GetName() const override { return "SDMC"; }
 | 
			
		||||
 | 
			
		||||
    ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
 | 
			
		||||
    ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::string sdmc_directory;
 | 
			
		||||
 | 
			
		||||
@ -63,11 +63,17 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c
 | 
			
		||||
    return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path) {
 | 
			
		||||
ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
 | 
			
		||||
    std::string fullpath = GetSystemSaveDataPath(base_path, path);
 | 
			
		||||
    FileUtil::DeleteDirRecursively(fullpath);
 | 
			
		||||
    FileUtil::CreateFullPath(fullpath);
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path) const {
 | 
			
		||||
    // TODO(Subv): Implement
 | 
			
		||||
    LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
 | 
			
		||||
    return ResultCode(-1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,8 @@ public:
 | 
			
		||||
    ArchiveFactory_SystemSaveData(const std::string& mount_point);
 | 
			
		||||
 | 
			
		||||
    ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path) override;
 | 
			
		||||
    ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
 | 
			
		||||
    ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
 | 
			
		||||
 | 
			
		||||
    std::string GetName() const override { return "SystemSaveData"; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,16 +17,28 @@
 | 
			
		||||
 | 
			
		||||
namespace FileSys {
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
 | 
			
		||||
ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
 | 
			
		||||
    LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
 | 
			
		||||
    auto file = Common::make_unique<DiskFile>(*this, path, mode);
 | 
			
		||||
    if (!file->Open())
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    return std::move(file);
 | 
			
		||||
    ResultCode result = file->Open();
 | 
			
		||||
    if (result.IsError())
 | 
			
		||||
        return result;
 | 
			
		||||
    return MakeResult<std::unique_ptr<FileBackend>>(std::move(file));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DiskArchive::DeleteFile(const Path& path) const {
 | 
			
		||||
    return FileUtil::Delete(mount_point + path.AsString());
 | 
			
		||||
ResultCode DiskArchive::DeleteFile(const Path& path) const {
 | 
			
		||||
    std::string file_path = mount_point + path.AsString();
 | 
			
		||||
 | 
			
		||||
    if (FileUtil::IsDirectory(file_path))
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
 | 
			
		||||
    if (!FileUtil::Exists(file_path))
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
 | 
			
		||||
 | 
			
		||||
    if (FileUtil::Delete(file_path))
 | 
			
		||||
        return RESULT_SUCCESS;
 | 
			
		||||
 | 
			
		||||
    return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
 | 
			
		||||
@ -37,11 +49,14 @@ bool DiskArchive::DeleteDirectory(const Path& path) const {
 | 
			
		||||
    return FileUtil::DeleteDir(mount_point + path.AsString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const {
 | 
			
		||||
ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u64 size) const {
 | 
			
		||||
    std::string full_path = mount_point + path.AsString();
 | 
			
		||||
 | 
			
		||||
    if (FileUtil::IsDirectory(full_path))
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
 | 
			
		||||
    if (FileUtil::Exists(full_path))
 | 
			
		||||
        return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info);
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Status);
 | 
			
		||||
 | 
			
		||||
    if (size == 0) {
 | 
			
		||||
        FileUtil::CreateEmptyFile(full_path);
 | 
			
		||||
@ -89,38 +104,57 @@ DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode
 | 
			
		||||
    this->mode.hex = mode.hex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool DiskFile::Open() {
 | 
			
		||||
    if (!mode.create_flag && !FileUtil::Exists(path)) {
 | 
			
		||||
        LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str());
 | 
			
		||||
        return false;
 | 
			
		||||
ResultCode DiskFile::Open() {
 | 
			
		||||
    if (FileUtil::IsDirectory(path))
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
 | 
			
		||||
    // Specifying only the Create flag is invalid
 | 
			
		||||
    if (mode.create_flag && !mode.read_flag && !mode.write_flag) {
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string mode_string;
 | 
			
		||||
    if (mode.create_flag)
 | 
			
		||||
        mode_string = "w+";
 | 
			
		||||
    else if (mode.write_flag)
 | 
			
		||||
        mode_string = "r+"; // Files opened with Write access can be read from
 | 
			
		||||
    if (!FileUtil::Exists(path)) {
 | 
			
		||||
        if (!mode.create_flag) {
 | 
			
		||||
            LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str());
 | 
			
		||||
            return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Create the file
 | 
			
		||||
            FileUtil::CreateEmptyFile(path);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string mode_string = "";
 | 
			
		||||
    if (mode.write_flag)
 | 
			
		||||
        mode_string += "r+"; // Files opened with Write access can be read from
 | 
			
		||||
    else if (mode.read_flag)
 | 
			
		||||
        mode_string = "r";
 | 
			
		||||
        mode_string += "r";
 | 
			
		||||
 | 
			
		||||
    // Open the file in binary mode, to avoid problems with CR/LF on Windows systems
 | 
			
		||||
    mode_string += "b";
 | 
			
		||||
 | 
			
		||||
    file = Common::make_unique<FileUtil::IOFile>(path, mode_string.c_str());
 | 
			
		||||
    return file->IsOpen();
 | 
			
		||||
    if (file->IsOpen())
 | 
			
		||||
        return RESULT_SUCCESS;
 | 
			
		||||
    return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, ErrorLevel::Status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
 | 
			
		||||
ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
 | 
			
		||||
    if (!mode.read_flag && !mode.write_flag)
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
 | 
			
		||||
    file->Seek(offset, SEEK_SET);
 | 
			
		||||
    return file->ReadBytes(buffer, length);
 | 
			
		||||
    return MakeResult<size_t>(file->ReadBytes(buffer, length));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
 | 
			
		||||
ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
 | 
			
		||||
    if (!mode.write_flag)
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
 | 
			
		||||
    file->Seek(offset, SEEK_SET);
 | 
			
		||||
    size_t written = file->WriteBytes(buffer, length);
 | 
			
		||||
    if (flush)
 | 
			
		||||
        file->Flush();
 | 
			
		||||
    return written;
 | 
			
		||||
    return MakeResult<size_t>(written);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u64 DiskFile::GetSize() const {
 | 
			
		||||
 | 
			
		||||
@ -33,11 +33,11 @@ public:
 | 
			
		||||
 | 
			
		||||
    virtual std::string GetName() const override { return "DiskArchive: " + mount_point; }
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
 | 
			
		||||
    bool DeleteFile(const Path& path) const override;
 | 
			
		||||
    ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override;
 | 
			
		||||
    ResultCode DeleteFile(const Path& path) const override;
 | 
			
		||||
    bool RenameFile(const Path& src_path, const Path& dest_path) const override;
 | 
			
		||||
    bool DeleteDirectory(const Path& path) const override;
 | 
			
		||||
    ResultCode CreateFile(const Path& path, u32 size) const override;
 | 
			
		||||
    ResultCode CreateFile(const Path& path, u64 size) const override;
 | 
			
		||||
    bool CreateDirectory(const Path& path) const override;
 | 
			
		||||
    bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
 | 
			
		||||
    std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
 | 
			
		||||
@ -54,9 +54,9 @@ class DiskFile : public FileBackend {
 | 
			
		||||
public:
 | 
			
		||||
    DiskFile(const DiskArchive& archive, const Path& path, const Mode mode);
 | 
			
		||||
 | 
			
		||||
    bool Open() override;
 | 
			
		||||
    size_t Read(u64 offset, size_t length, u8* buffer) const override;
 | 
			
		||||
    size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
 | 
			
		||||
    ResultCode Open() override;
 | 
			
		||||
    ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
 | 
			
		||||
    ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
 | 
			
		||||
    u64 GetSize() const override;
 | 
			
		||||
    bool SetSize(u64 size) const override;
 | 
			
		||||
    bool Close() const override;
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "core/hle/result.h"
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
// FileSys namespace
 | 
			
		||||
@ -20,18 +21,18 @@ public:
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open the file
 | 
			
		||||
     * @return true if the file opened correctly
 | 
			
		||||
     * @return Result of the file operation
 | 
			
		||||
     */
 | 
			
		||||
    virtual bool Open() = 0;
 | 
			
		||||
    virtual ResultCode Open() = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Read data from the file
 | 
			
		||||
     * @param offset Offset in bytes to start reading data from
 | 
			
		||||
     * @param length Length in bytes of data to read from file
 | 
			
		||||
     * @param buffer Buffer to read data into
 | 
			
		||||
     * @return Number of bytes read
 | 
			
		||||
     * @return Number of bytes read, or error code
 | 
			
		||||
     */
 | 
			
		||||
    virtual size_t Read(u64 offset, size_t length, u8* buffer) const = 0;
 | 
			
		||||
    virtual ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Write data to the file
 | 
			
		||||
@ -39,9 +40,9 @@ public:
 | 
			
		||||
     * @param length Length in bytes of data to write to file
 | 
			
		||||
     * @param flush The flush parameters (0 == do not flush)
 | 
			
		||||
     * @param buffer Buffer to read data from
 | 
			
		||||
     * @return Number of bytes written
 | 
			
		||||
     * @return Number of bytes written, or error code
 | 
			
		||||
     */
 | 
			
		||||
    virtual size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0;
 | 
			
		||||
    virtual ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the size of the file in bytes
 | 
			
		||||
 | 
			
		||||
@ -20,13 +20,15 @@ std::string IVFCArchive::GetName() const {
 | 
			
		||||
    return "IVFC";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<FileBackend> IVFCArchive::OpenFile(const Path& path, const Mode mode) const {
 | 
			
		||||
    return Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size);
 | 
			
		||||
ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, const Mode mode) const {
 | 
			
		||||
    return MakeResult<std::unique_ptr<FileBackend>>(Common::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool IVFCArchive::DeleteFile(const Path& path) const {
 | 
			
		||||
ResultCode IVFCArchive::DeleteFile(const Path& path) const {
 | 
			
		||||
    LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", GetName().c_str());
 | 
			
		||||
    return false;
 | 
			
		||||
    // TODO(Subv): Verify error code
 | 
			
		||||
    return ResultCode(ErrorDescription::NoData, ErrorModule::FS,
 | 
			
		||||
                      ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
 | 
			
		||||
@ -39,7 +41,7 @@ bool IVFCArchive::DeleteDirectory(const Path& path) const {
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode IVFCArchive::CreateFile(const Path& path, u32 size) const {
 | 
			
		||||
ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
 | 
			
		||||
    LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", GetName().c_str());
 | 
			
		||||
    // TODO: Verify error code
 | 
			
		||||
    return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent);
 | 
			
		||||
@ -66,17 +68,18 @@ u64 IVFCArchive::GetFreeBytes() const {
 | 
			
		||||
 | 
			
		||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
 | 
			
		||||
ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
 | 
			
		||||
    LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
 | 
			
		||||
    romfs_file->Seek(data_offset + offset, SEEK_SET);
 | 
			
		||||
    size_t read_length = (size_t)std::min((u64)length, data_size - offset);
 | 
			
		||||
 | 
			
		||||
    return romfs_file->ReadBytes(buffer, read_length);
 | 
			
		||||
    return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
 | 
			
		||||
ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush, const u8* buffer) const {
 | 
			
		||||
    LOG_ERROR(Service_FS, "Attempted to write to IVFC file");
 | 
			
		||||
    return 0;
 | 
			
		||||
    // TODO(Subv): Find error code
 | 
			
		||||
    return MakeResult<size_t>(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u64 IVFCFile::GetSize() const {
 | 
			
		||||
 | 
			
		||||
@ -34,11 +34,11 @@ public:
 | 
			
		||||
 | 
			
		||||
    std::string GetName() const override;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
 | 
			
		||||
    bool DeleteFile(const Path& path) const override;
 | 
			
		||||
    ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode mode) const override;
 | 
			
		||||
    ResultCode DeleteFile(const Path& path) const override;
 | 
			
		||||
    bool RenameFile(const Path& src_path, const Path& dest_path) const override;
 | 
			
		||||
    bool DeleteDirectory(const Path& path) const override;
 | 
			
		||||
    ResultCode CreateFile(const Path& path, u32 size) const override;
 | 
			
		||||
    ResultCode CreateFile(const Path& path, u64 size) const override;
 | 
			
		||||
    bool CreateDirectory(const Path& path) const override;
 | 
			
		||||
    bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
 | 
			
		||||
    std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
 | 
			
		||||
@ -55,9 +55,9 @@ public:
 | 
			
		||||
    IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
 | 
			
		||||
        : romfs_file(file), data_offset(offset), data_size(size) {}
 | 
			
		||||
 | 
			
		||||
    bool Open() override { return true; }
 | 
			
		||||
    size_t Read(u64 offset, size_t length, u8* buffer) const override;
 | 
			
		||||
    size_t Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
 | 
			
		||||
    ResultCode Open() override { return RESULT_SUCCESS; }
 | 
			
		||||
    ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
 | 
			
		||||
    ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
 | 
			
		||||
    u64 GetSize() const override;
 | 
			
		||||
    bool SetSize(u64 size) const override;
 | 
			
		||||
    bool Close() const override { return false; }
 | 
			
		||||
 | 
			
		||||
@ -19,8 +19,12 @@
 | 
			
		||||
enum class ErrorDescription : u32 {
 | 
			
		||||
    Success = 0,
 | 
			
		||||
    WrongAddress = 53,
 | 
			
		||||
    FS_NotFound = 100,
 | 
			
		||||
    FS_NotFound = 120,
 | 
			
		||||
    FS_AlreadyExists = 190,
 | 
			
		||||
    FS_InvalidOpenFlags = 230,
 | 
			
		||||
    FS_NotAFile = 250,
 | 
			
		||||
    FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
 | 
			
		||||
    FS_InvalidPath = 702,
 | 
			
		||||
    InvalidSection = 1000,
 | 
			
		||||
    TooLarge = 1001,
 | 
			
		||||
    NotAuthorized = 1002,
 | 
			
		||||
 | 
			
		||||
@ -310,7 +310,8 @@ ResultCode UpdateConfigNANDSavegame() {
 | 
			
		||||
 | 
			
		||||
ResultCode FormatConfig() {
 | 
			
		||||
    ResultCode res = DeleteConfigNANDSaveFile();
 | 
			
		||||
    if (!res.IsSuccess())
 | 
			
		||||
    // The delete command fails if the file doesn't exist, so we have to check that too
 | 
			
		||||
    if (!res.IsSuccess() && res.description != ErrorDescription::FS_NotFound)
 | 
			
		||||
        return res;
 | 
			
		||||
    // Delete the old data
 | 
			
		||||
    cfg_config_file_buffer.fill(0);
 | 
			
		||||
@ -407,7 +408,7 @@ void Init() {
 | 
			
		||||
    // If the archive didn't exist, create the files inside
 | 
			
		||||
    if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
 | 
			
		||||
        // Format the archive to create the directories
 | 
			
		||||
        Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
 | 
			
		||||
        Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SystemSaveData, FileSys::ArchiveFormatInfo(), archive_path);
 | 
			
		||||
 | 
			
		||||
        // Open it again to get a valid archive now that the folder exists
 | 
			
		||||
        archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SystemSaveData, archive_path);
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,18 @@ ResultVal<bool> File::SyncRequest() {
 | 
			
		||||
            u32 address = cmd_buff[5];
 | 
			
		||||
            LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
 | 
			
		||||
                      GetTypeName().c_str(), GetName().c_str(), offset, length, address);
 | 
			
		||||
            cmd_buff[2] = static_cast<u32>(backend->Read(offset, length, Memory::GetPointer(address)));
 | 
			
		||||
 | 
			
		||||
            if (offset + length > backend->GetSize()) {
 | 
			
		||||
                LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%llX length=0x%08X file_size=0x%llX",
 | 
			
		||||
                          offset, length, backend->GetSize());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ResultVal<size_t> read = backend->Read(offset, length, Memory::GetPointer(address));
 | 
			
		||||
            if (read.Failed()) {
 | 
			
		||||
                cmd_buff[1] = read.Code().raw;
 | 
			
		||||
                return read.Code();
 | 
			
		||||
            }
 | 
			
		||||
            cmd_buff[2] = static_cast<u32>(*read);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -116,7 +127,13 @@ ResultVal<bool> File::SyncRequest() {
 | 
			
		||||
            u32 address = cmd_buff[6];
 | 
			
		||||
            LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
 | 
			
		||||
                      GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
 | 
			
		||||
            cmd_buff[2] = static_cast<u32>(backend->Write(offset, length, flush != 0, Memory::GetPointer(address)));
 | 
			
		||||
 | 
			
		||||
            ResultVal<size_t> written = backend->Write(offset, length, flush != 0, Memory::GetPointer(address));
 | 
			
		||||
            if (written.Failed()) {
 | 
			
		||||
                cmd_buff[1] = written.Code().raw;
 | 
			
		||||
                return written.Code();
 | 
			
		||||
            }
 | 
			
		||||
            cmd_buff[2] = static_cast<u32>(*written);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -294,13 +311,11 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han
 | 
			
		||||
    if (archive == nullptr)
 | 
			
		||||
        return ERR_INVALID_HANDLE;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<FileSys::FileBackend> backend = archive->OpenFile(path, mode);
 | 
			
		||||
    if (backend == nullptr) {
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
 | 
			
		||||
                          ErrorSummary::NotFound, ErrorLevel::Status);
 | 
			
		||||
    }
 | 
			
		||||
    auto backend = archive->OpenFile(path, mode);
 | 
			
		||||
    if (backend.Failed())
 | 
			
		||||
        return backend.Code();
 | 
			
		||||
 | 
			
		||||
    auto file = Kernel::SharedPtr<File>(new File(std::move(backend), path));
 | 
			
		||||
    auto file = Kernel::SharedPtr<File>(new File(backend.MoveFrom(), path));
 | 
			
		||||
    return MakeResult<Kernel::SharedPtr<File>>(std::move(file));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -309,10 +324,7 @@ ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Pa
 | 
			
		||||
    if (archive == nullptr)
 | 
			
		||||
        return ERR_INVALID_HANDLE;
 | 
			
		||||
 | 
			
		||||
    if (archive->DeleteFile(path))
 | 
			
		||||
        return RESULT_SUCCESS;
 | 
			
		||||
    return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
 | 
			
		||||
                      ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
    return archive->DeleteFile(path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
 | 
			
		||||
@ -347,7 +359,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
 | 
			
		||||
                      ErrorSummary::Canceled, ErrorLevel::Status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) {
 | 
			
		||||
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size) {
 | 
			
		||||
    ArchiveBackend* archive = GetArchive(archive_handle);
 | 
			
		||||
    if (archive == nullptr)
 | 
			
		||||
        return ERR_INVALID_HANDLE;
 | 
			
		||||
@ -395,7 +407,7 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path);
 | 
			
		||||
    if (backend == nullptr) {
 | 
			
		||||
        return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
 | 
			
		||||
        return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
 | 
			
		||||
                          ErrorSummary::NotFound, ErrorLevel::Permanent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -410,49 +422,45 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
 | 
			
		||||
    return MakeResult<u64>(archive->GetFreeBytes());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) {
 | 
			
		||||
ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info, const FileSys::Path& path) {
 | 
			
		||||
    auto archive_itr = id_code_map.find(id_code);
 | 
			
		||||
    if (archive_itr == id_code_map.end()) {
 | 
			
		||||
        return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return archive_itr->second->Format(path);
 | 
			
		||||
    return archive_itr->second->Format(path, format_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size) {
 | 
			
		||||
ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code, FileSys::Path& archive_path) {
 | 
			
		||||
    auto archive = id_code_map.find(id_code);
 | 
			
		||||
    if (archive == id_code_map.end()) {
 | 
			
		||||
        return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return archive->second->GetFormatInfo(archive_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size, const FileSys::ArchiveFormatInfo& format_info) {
 | 
			
		||||
    // Construct the binary path to the archive first
 | 
			
		||||
    FileSys::Path path = FileSys::ConstructExtDataBinaryPath(static_cast<u32>(media_type), high, low);
 | 
			
		||||
 | 
			
		||||
    std::string media_type_directory;
 | 
			
		||||
    if (media_type == MediaType::NAND) {
 | 
			
		||||
        media_type_directory = FileUtil::GetUserPath(D_NAND_IDX);
 | 
			
		||||
    } else if (media_type == MediaType::SDMC) {
 | 
			
		||||
        media_type_directory = FileUtil::GetUserPath(D_SDMC_IDX);
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_ERROR(Service_FS, "Unsupported media type %u", media_type);
 | 
			
		||||
        return ResultCode(-1); // TODO(Subv): Find the right error code
 | 
			
		||||
    auto archive = id_code_map.find(media_type == MediaType::NAND ? ArchiveIdCode::SharedExtSaveData : ArchiveIdCode::ExtSaveData);
 | 
			
		||||
 | 
			
		||||
    if (archive == id_code_map.end()) {
 | 
			
		||||
        return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the right error
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND);
 | 
			
		||||
    std::string game_path = FileSys::GetExtSaveDataPath(base_path, path);
 | 
			
		||||
    // These two folders are always created with the ExtSaveData
 | 
			
		||||
    std::string user_path = game_path + "user/";
 | 
			
		||||
    std::string boss_path = game_path + "boss/";
 | 
			
		||||
    if (!FileUtil::CreateFullPath(user_path))
 | 
			
		||||
        return ResultCode(-1); // TODO(Subv): Find the right error code
 | 
			
		||||
    if (!FileUtil::CreateFullPath(boss_path))
 | 
			
		||||
        return ResultCode(-1); // TODO(Subv): Find the right error code
 | 
			
		||||
    auto ext_savedata = static_cast<FileSys::ArchiveFactory_ExtSaveData*>(archive->second.get());
 | 
			
		||||
 | 
			
		||||
    ResultCode result = ext_savedata->Format(path, format_info);
 | 
			
		||||
    if (result.IsError())
 | 
			
		||||
        return result;
 | 
			
		||||
 | 
			
		||||
    u8* smdh_icon = Memory::GetPointer(icon_buffer);
 | 
			
		||||
    if (!smdh_icon)
 | 
			
		||||
        return ResultCode(-1); // TODO(Subv): Find the right error code
 | 
			
		||||
 | 
			
		||||
    // Create the icon
 | 
			
		||||
    FileUtil::IOFile icon_file(game_path + "icon", "wb+");
 | 
			
		||||
    if (!icon_file.IsGood())
 | 
			
		||||
        return ResultCode(-1); // TODO(Subv): Find the right error code
 | 
			
		||||
 | 
			
		||||
    icon_file.WriteBytes(smdh_icon, icon_size);
 | 
			
		||||
    ext_savedata->WriteIcon(path, smdh_icon, icon_size);
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -473,7 +481,7 @@ ResultCode DeleteExtSaveData(MediaType media_type, u32 high, u32 low) {
 | 
			
		||||
    // Delete all directories (/user, /boss) and the icon file.
 | 
			
		||||
    std::string base_path = FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND);
 | 
			
		||||
    std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path);
 | 
			
		||||
    if (!FileUtil::DeleteDirRecursively(extsavedata_path))
 | 
			
		||||
    if (FileUtil::Exists(extsavedata_path) && !FileUtil::DeleteDirRecursively(extsavedata_path))
 | 
			
		||||
        return ResultCode(-1); // TODO(Subv): Find the right error code
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -136,7 +136,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy
 | 
			
		||||
 * @param file_size The size of the new file, filled with zeroes
 | 
			
		||||
 * @return File creation result code
 | 
			
		||||
 */
 | 
			
		||||
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size);
 | 
			
		||||
ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u64 file_size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a Directory from an Archive
 | 
			
		||||
@ -177,10 +177,20 @@ ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle);
 | 
			
		||||
 * Erases the contents of the physical folder that contains the archive
 | 
			
		||||
 * identified by the specified id code and path
 | 
			
		||||
 * @param id_code The id of the archive to format
 | 
			
		||||
 * @param format_info Format information about the new archive
 | 
			
		||||
 * @param path The path to the archive, if relevant.
 | 
			
		||||
 * @return ResultCode 0 on success or the corresponding code on error
 | 
			
		||||
 */
 | 
			
		||||
ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = FileSys::Path());
 | 
			
		||||
ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info, const FileSys::Path& path = FileSys::Path());
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Retrieves the format info about the archive of the specified type and path.
 | 
			
		||||
 * The format info is supplied by the client code when creating archives.
 | 
			
		||||
 * @param id_code The id of the archive
 | 
			
		||||
 * @param archive_path The path of the archive, if relevant
 | 
			
		||||
 * @return The format info of the archive, or the corresponding error code if failed.
 | 
			
		||||
 */
 | 
			
		||||
ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code, FileSys::Path& archive_path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates a blank SharedExtSaveData archive for the specified extdata ID
 | 
			
		||||
@ -189,9 +199,10 @@ ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path = File
 | 
			
		||||
 * @param low The low word of the extdata id to create
 | 
			
		||||
 * @param icon_buffer VAddr of the SMDH icon for this ExtSaveData
 | 
			
		||||
 * @param icon_size Size of the SMDH icon
 | 
			
		||||
 * @param format_info Format information about the new archive
 | 
			
		||||
 * @return ResultCode 0 on success or the corresponding code on error
 | 
			
		||||
 */
 | 
			
		||||
ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size);
 | 
			
		||||
ResultCode CreateExtSaveData(MediaType media_type, u32 high, u32 low, VAddr icon_buffer, u32 icon_size, const FileSys::ArchiveFormatInfo& format_info);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deletes the SharedExtSaveData archive for the specified extdata ID
 | 
			
		||||
 | 
			
		||||
@ -234,7 +234,7 @@ static void DeleteDirectory(Service::Interface* self) {
 | 
			
		||||
 *      3 : Archive handle upper word
 | 
			
		||||
 *      4 : File path string type
 | 
			
		||||
 *      5 : File path string size
 | 
			
		||||
 *      7 : File size (filled with zeroes)
 | 
			
		||||
 *      7-8 : File size
 | 
			
		||||
 *      10: File path string data
 | 
			
		||||
 *  Outputs:
 | 
			
		||||
 *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
@ -245,12 +245,12 @@ static void CreateFile(Service::Interface* self) {
 | 
			
		||||
    ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]);
 | 
			
		||||
    auto filename_type    = static_cast<FileSys::LowPathType>(cmd_buff[4]);
 | 
			
		||||
    u32 filename_size     = cmd_buff[5];
 | 
			
		||||
    u32 file_size         = cmd_buff[7];
 | 
			
		||||
    u64 file_size         = ((u64)cmd_buff[8] << 32) | cmd_buff[7];
 | 
			
		||||
    u32 filename_ptr      = cmd_buff[10];
 | 
			
		||||
 | 
			
		||||
    FileSys::Path file_path(filename_type, filename_size, filename_ptr);
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", filename_type, filename_size, file_path.DebugStr().c_str());
 | 
			
		||||
    LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, filename_size, file_path.DebugStr().c_str());
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw;
 | 
			
		||||
}
 | 
			
		||||
@ -443,17 +443,22 @@ static void IsSdmcWriteable(Service::Interface* self) {
 | 
			
		||||
 *  Inputs:
 | 
			
		||||
 *      0  : 0x084C0242
 | 
			
		||||
 *      1  : Archive ID
 | 
			
		||||
 *      2  : Archive low path type
 | 
			
		||||
 *      3  : Archive low path size
 | 
			
		||||
 *      10 : (LowPathSize << 14) | 2
 | 
			
		||||
 *      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
 | 
			
		||||
 */
 | 
			
		||||
static void FormatSaveData(Service::Interface* self) {
 | 
			
		||||
    // TODO(Subv): Find out what the other inputs and outputs of this function are
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    LOG_DEBUG(Service_FS, "(STUBBED)");
 | 
			
		||||
    LOG_WARNING(Service_FS, "(STUBBED)");
 | 
			
		||||
 | 
			
		||||
    auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
 | 
			
		||||
    auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
 | 
			
		||||
@ -464,9 +469,9 @@ static void FormatSaveData(Service::Interface* self) {
 | 
			
		||||
    LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
 | 
			
		||||
 | 
			
		||||
    if (archive_id != FS::ArchiveIdCode::SaveData) {
 | 
			
		||||
        // TODO(Subv): What should happen if somebody attempts to format a different archive?
 | 
			
		||||
        LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", cmd_buff[1]);
 | 
			
		||||
        cmd_buff[1] = UnimplementedFunction(ErrorModule::FS).raw;
 | 
			
		||||
        LOG_ERROR(Service_FS, "tried to format an archive different than SaveData, %u", archive_id);
 | 
			
		||||
        cmd_buff[1] = ResultCode(ErrorDescription::FS_InvalidPath, ErrorModule::FS,
 | 
			
		||||
                                 ErrorSummary::InvalidArgument, ErrorLevel::Usage).raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -477,23 +482,40 @@ static void FormatSaveData(Service::Interface* self) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw;
 | 
			
		||||
    FileSys::ArchiveFormatInfo format_info;
 | 
			
		||||
    format_info.duplicate_data = cmd_buff[9] & 0xFF;
 | 
			
		||||
    format_info.number_directories = cmd_buff[5];
 | 
			
		||||
    format_info.number_files = cmd_buff[6];
 | 
			
		||||
    format_info.total_size = cmd_buff[4] * 512;
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData, format_info).raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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
 | 
			
		||||
 */
 | 
			
		||||
static void FormatThisUserSaveData(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
    LOG_DEBUG(Service_FS, "(STUBBED)");
 | 
			
		||||
 | 
			
		||||
    // TODO(Subv): Find out what the inputs and outputs of this function are
 | 
			
		||||
    FileSys::ArchiveFormatInfo format_info;
 | 
			
		||||
    format_info.duplicate_data = cmd_buff[6] & 0xFF;
 | 
			
		||||
    format_info.number_directories = cmd_buff[2];
 | 
			
		||||
    format_info.number_files = cmd_buff[3];
 | 
			
		||||
    format_info.total_size = cmd_buff[1] * 512;
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData).raw;
 | 
			
		||||
    cmd_buff[1] = FormatArchive(ArchiveIdCode::SaveData, format_info).raw;
 | 
			
		||||
 | 
			
		||||
    LOG_TRACE(Service_FS, "called");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -531,10 +553,9 @@ static void GetFreeBytes(Service::Interface* self) {
 | 
			
		||||
 *      2 : Low word of the saveid to create
 | 
			
		||||
 *      3 : High word of the saveid to create
 | 
			
		||||
 *      4 : Unknown
 | 
			
		||||
 *      5 : Unknown
 | 
			
		||||
 *      6 : Unknown
 | 
			
		||||
 *      7 : Unknown
 | 
			
		||||
 *      8 : 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
 | 
			
		||||
@ -556,7 +577,12 @@ static void CreateExtSaveData(Service::Interface* self) {
 | 
			
		||||
            cmd_buff[3], cmd_buff[4], cmd_buff[5], cmd_buff[6], cmd_buff[7], cmd_buff[8], icon_size,
 | 
			
		||||
            cmd_buff[10], icon_buffer);
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low, icon_buffer, icon_size).raw;
 | 
			
		||||
    FileSys::ArchiveFormatInfo format_info;
 | 
			
		||||
    format_info.number_directories = cmd_buff[5];
 | 
			
		||||
    format_info.number_files = cmd_buff[6];
 | 
			
		||||
    format_info.duplicate_data = false;
 | 
			
		||||
    format_info.total_size = 0;
 | 
			
		||||
    cmd_buff[1] = CreateExtSaveData(media_type, save_high, save_low, icon_buffer, icon_size, format_info).raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -731,6 +757,51 @@ static void GetArchiveResource(Service::Interface* self) {
 | 
			
		||||
    cmd_buff[5] = 0x80000; // 8GiB free
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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
 | 
			
		||||
 */
 | 
			
		||||
static void GetFormatInfo(Service::Interface* self) {
 | 
			
		||||
    u32* cmd_buff = Kernel::GetCommandBuffer();
 | 
			
		||||
 | 
			
		||||
    auto archive_id = static_cast<FS::ArchiveIdCode>(cmd_buff[1]);
 | 
			
		||||
    auto archivename_type = static_cast<FileSys::LowPathType>(cmd_buff[2]);
 | 
			
		||||
    u32 archivename_size = cmd_buff[3];
 | 
			
		||||
    u32 archivename_ptr = cmd_buff[5];
 | 
			
		||||
    FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
 | 
			
		||||
 | 
			
		||||
    cmd_buff[0] = IPC::MakeHeader(0x0845, 5, 0);
 | 
			
		||||
 | 
			
		||||
    auto format_info = GetArchiveFormatInfo(archive_id, archive_path);
 | 
			
		||||
 | 
			
		||||
    if (format_info.Failed()) {
 | 
			
		||||
        LOG_ERROR(Service_FS, "Failed to retrieve the format info");
 | 
			
		||||
        cmd_buff[1] = format_info.Code().raw;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cmd_buff[1] = RESULT_SUCCESS.raw;
 | 
			
		||||
    cmd_buff[2] = format_info->total_size;
 | 
			
		||||
    cmd_buff[3] = format_info->number_directories;
 | 
			
		||||
    cmd_buff[4] = format_info->number_files;
 | 
			
		||||
    cmd_buff[5] = format_info->duplicate_data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		||||
    {0x000100C6, nullptr,                  "Dummy1"},
 | 
			
		||||
    {0x040100C4, nullptr,                  "Control"},
 | 
			
		||||
@ -802,7 +873,7 @@ const Interface::FunctionInfo FunctionTable[] = {
 | 
			
		||||
    {0x08420040, nullptr,                  "DeleteAllExtSaveDataOnNand"},
 | 
			
		||||
    {0x08430000, nullptr,                  "InitializeCtrFileSystem"},
 | 
			
		||||
    {0x08440000, nullptr,                  "CreateSeed"},
 | 
			
		||||
    {0x084500C2, nullptr,                  "GetFormatInfo"},
 | 
			
		||||
    {0x084500C2, GetFormatInfo,            "GetFormatInfo"},
 | 
			
		||||
    {0x08460102, nullptr,                  "GetLegacyRomHeader2"},
 | 
			
		||||
    {0x08470180, nullptr,                  "FormatCtrCardUserSaveData"},
 | 
			
		||||
    {0x08480042, nullptr,                  "GetSdmcCtrRootPath"},
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,7 @@ void Init() {
 | 
			
		||||
    // If the archive didn't exist, create the files inside
 | 
			
		||||
    if (archive_result.Code().description == ErrorDescription::FS_NotFormatted) {
 | 
			
		||||
        // Format the archive to create the directories
 | 
			
		||||
        Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
 | 
			
		||||
        Service::FS::FormatArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, FileSys::ArchiveFormatInfo(), archive_path);
 | 
			
		||||
        // Open it again to get a valid archive now that the folder exists
 | 
			
		||||
        archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path);
 | 
			
		||||
        ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!");
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user