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:
Subv 2014-12-16 00:33:41 -05:00
parent e6f440ea7f
commit ea9ce0fba7
25 changed files with 458 additions and 490 deletions

View file

@ -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;

View file

@ -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();

View file

@ -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,

View file

@ -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());
}

View file

@ -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();

View file

@ -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"},