Filesystem/Archives: Implemented the SaveData archive
The savedata for each game is stored in /savedata/<ProgramID> for NCCH files. ELF files and 3DSX files use the folder 0 because they have no ID information Got rid of the code duplication in File and Directory Files that deal with the host machine's file system now live in DiskFile, similarly for directories and DiskDirectory and archives with DiskArchive. FS_U: Use the correct error code when a file wasn't found
This commit is contained in:
parent
e6f440ea7f
commit
ea9ce0fba7
25 changed files with 458 additions and 490 deletions
|
@ -14,6 +14,7 @@ namespace Kernel {
|
|||
|
||||
Handle g_main_thread = 0;
|
||||
ObjectPool g_object_pool;
|
||||
u64 g_program_id = 0;
|
||||
|
||||
ObjectPool::ObjectPool() {
|
||||
next_id = INITIAL_NEXT_ID;
|
||||
|
|
|
@ -151,6 +151,12 @@ private:
|
|||
extern ObjectPool g_object_pool;
|
||||
extern Handle g_main_thread;
|
||||
|
||||
/// The ID code of the currently running game
|
||||
/// TODO(Subv): This variable should not be here,
|
||||
/// we need a way to store information about the currently loaded application
|
||||
/// for later query during runtime, maybe using the LDR service?
|
||||
extern u64 g_program_id;
|
||||
|
||||
/// Initialize the kernel
|
||||
void Init();
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
/// Detailed description of the error. This listing is likely incomplete.
|
||||
enum class ErrorDescription : u32 {
|
||||
Success = 0,
|
||||
FS_NotFound = 100,
|
||||
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
|
||||
InvalidSection = 1000,
|
||||
TooLarge = 1001,
|
||||
NotAuthorized = 1002,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "common/file_util.h"
|
||||
#include "common/math_util.h"
|
||||
|
||||
#include "core/file_sys/archive_savedata.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/archive_sdmc.h"
|
||||
#include "core/file_sys/directory_backend.h"
|
||||
|
@ -135,6 +136,13 @@ public:
|
|||
break;
|
||||
}
|
||||
|
||||
case FileCommand::Flush:
|
||||
{
|
||||
LOG_TRACE(Service_FS, "Flush");
|
||||
backend->Flush();
|
||||
break;
|
||||
}
|
||||
|
||||
// Unknown command...
|
||||
default:
|
||||
LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
|
||||
|
@ -220,9 +228,18 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
|
|||
|
||||
auto itr = id_code_map.find(id_code);
|
||||
if (itr == id_code_map.end()) {
|
||||
if (id_code == ArchiveIdCode::SaveData) {
|
||||
// When a SaveData archive is created for the first time, it is not yet formatted
|
||||
// and the save file/directory structure expected by the game has not yet been initialized.
|
||||
// Returning the NotFormatted error code will signal the game to provision the SaveData archive
|
||||
// with the files and folders that it expects.
|
||||
// The FormatSaveData service call will create the SaveData archive when it is called.
|
||||
return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
|
||||
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||
}
|
||||
// TODO: Verify error against hardware
|
||||
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
|
||||
ErrorSummary::NotFound, ErrorLevel::Permanent);
|
||||
ErrorSummary::NotFound, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
// This should never even happen in the first place with 64-bit handles,
|
||||
|
@ -260,8 +277,8 @@ ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSy
|
|||
|
||||
std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
|
||||
if (backend == nullptr) {
|
||||
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
|
||||
ErrorSummary::NotFound, ErrorLevel::Permanent);
|
||||
return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
|
||||
ErrorSummary::NotFound, ErrorLevel::Status);
|
||||
}
|
||||
|
||||
auto file = std::make_unique<File>(std::move(backend), path);
|
||||
|
@ -366,6 +383,28 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F
|
|||
return MakeResult<Handle>(handle);
|
||||
}
|
||||
|
||||
ResultCode FormatSaveData() {
|
||||
// TODO(Subv): Actually wipe the savedata folder after creating or opening it
|
||||
|
||||
// Do not create the archive again if it already exists
|
||||
if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
|
||||
return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
|
||||
|
||||
// Create the SaveData archive
|
||||
std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
|
||||
auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory,
|
||||
Kernel::g_program_id);
|
||||
|
||||
if (savedata_archive->Initialize()) {
|
||||
CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
|
||||
return RESULT_SUCCESS;
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
|
||||
savedata_archive->GetMountPoint().c_str());
|
||||
return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize archives
|
||||
void ArchiveInit() {
|
||||
next_handle = 1;
|
||||
|
@ -375,9 +414,9 @@ void ArchiveInit() {
|
|||
// archive type is SDMC, so it is the only one getting exposed.
|
||||
|
||||
std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
|
||||
auto archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
|
||||
if (archive->Initialize())
|
||||
CreateArchive(std::move(archive), ArchiveIdCode::SDMC);
|
||||
auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
|
||||
if (sdmc_archive->Initialize())
|
||||
CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
|
||||
else
|
||||
LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
|
||||
}
|
||||
|
|
|
@ -109,6 +109,12 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons
|
|||
*/
|
||||
ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
|
||||
|
||||
/**
|
||||
* Creates a blank SaveData archive.
|
||||
* @return ResultCode 0 on success or the corresponding code on error
|
||||
*/
|
||||
ResultCode FormatSaveData();
|
||||
|
||||
/// Initialize archives
|
||||
void ArchiveInit();
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/scope_exit.h"
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/fs/fs_user.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
|
@ -50,9 +50,7 @@ static void Initialize(Service::Interface* self) {
|
|||
static void OpenFile(Service::Interface* self) {
|
||||
u32* cmd_buff = Kernel::GetCommandBuffer();
|
||||
|
||||
// TODO(Link Mauve): cmd_buff[2], aka archive handle lower word, isn't used according to
|
||||
// 3dmoo's or ctrulib's implementations. Triple check if it's really the case.
|
||||
Handle archive_handle = static_cast<Handle>(cmd_buff[3]);
|
||||
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];
|
||||
FileSys::Mode mode; mode.hex = cmd_buff[6];
|
||||
|
@ -398,6 +396,36 @@ static void IsSdmcDetected(Service::Interface* self) {
|
|||
LOG_DEBUG(Service_FS, "called");
|
||||
}
|
||||
|
||||
/**
|
||||
* FS_User::FormatSaveData service function
|
||||
* Inputs:
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
static void FormatSaveData(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
|
||||
|
||||
cmd_buff[1] = FormatSaveData().raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* FS_User::FormatThisUserSaveData service function
|
||||
* Inputs:
|
||||
* 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
|
||||
|
||||
cmd_buff[1] = FormatSaveData().raw;
|
||||
}
|
||||
|
||||
const FSUserInterface::FunctionInfo FunctionTable[] = {
|
||||
{0x000100C6, nullptr, "Dummy1"},
|
||||
{0x040100C4, nullptr, "Control"},
|
||||
|
@ -415,7 +443,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
|
|||
{0x080C00C2, OpenArchive, "OpenArchive"},
|
||||
{0x080D0144, nullptr, "ControlArchive"},
|
||||
{0x080E0080, CloseArchive, "CloseArchive"},
|
||||
{0x080F0180, nullptr, "FormatThisUserSaveData"},
|
||||
{0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
|
||||
{0x08100200, nullptr, "CreateSystemSaveData"},
|
||||
{0x08110040, nullptr, "DeleteSystemSaveData"},
|
||||
{0x08120080, nullptr, "GetFreeBytes"},
|
||||
|
@ -476,7 +504,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = {
|
|||
{0x08490040, nullptr, "GetArchiveResource"},
|
||||
{0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
|
||||
{0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
|
||||
{0x084C0242, nullptr, "FormatSaveData"},
|
||||
{0x084C0242, FormatSaveData, "FormatSaveData"},
|
||||
{0x084D0102, nullptr, "GetLegacySubBannerData"},
|
||||
{0x084E0342, nullptr, "UpdateSha256Context"},
|
||||
{0x084F0102, nullptr, "ReadSpecialFile"},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue