mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-05 18:23:39 -04:00
ExtData support
This commit is contained in:
parent
78e6aef690
commit
8d71f4afa3
4 changed files with 348 additions and 4 deletions
|
|
@ -290,6 +290,117 @@ Result ArchiveManager::DeleteExtSaveData(MediaType media_type, u8 unknown, u32 h
|
|||
return ext_savedata->DeleteExtData(media_type, unknown, high, low);
|
||||
}
|
||||
|
||||
ResultVal<u32> ArchiveManager::EnumerateExtSaveData(MediaType media_type, bool shared,
|
||||
u32 entry_size, u8* output, u32 max_count) {
|
||||
std::string media_dir;
|
||||
bool use_shared = shared || (media_type == MediaType::NAND);
|
||||
if (media_type == MediaType::SDMC || media_type == MediaType::NAND) {
|
||||
media_dir = media_type == MediaType::NAND
|
||||
? FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)
|
||||
: FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||
} else {
|
||||
return ResultUnknown;
|
||||
}
|
||||
|
||||
auto parse_hex = [](const std::string& s, u32& out) -> bool {
|
||||
char* end = nullptr;
|
||||
unsigned long val = std::strtoul(s.c_str(), &end, 16);
|
||||
if (end == s.c_str() || *end != '\0')
|
||||
return false;
|
||||
out = static_cast<u32>(val);
|
||||
return true;
|
||||
};
|
||||
|
||||
std::string container = FileSys::GetExtDataContainerPath(media_dir, use_shared);
|
||||
u32 written = 0;
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(
|
||||
nullptr, container,
|
||||
[&](u64*, const std::string& dir, const std::string& high_name) {
|
||||
if (written >= max_count)
|
||||
return true;
|
||||
u32 high;
|
||||
if (!parse_hex(high_name, high))
|
||||
return true;
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(
|
||||
nullptr, dir + high_name + "/",
|
||||
[&](u64*, const std::string&, const std::string& low_name) {
|
||||
if (written >= max_count)
|
||||
return true;
|
||||
u32 low;
|
||||
if (!parse_hex(low_name, low))
|
||||
return true;
|
||||
|
||||
if (entry_size == sizeof(u64)) {
|
||||
u64 id = (static_cast<u64>(high) << 32) | low;
|
||||
std::memcpy(output + written * sizeof(u64), &id, sizeof(u64));
|
||||
} else if (entry_size == sizeof(u32)) {
|
||||
std::memcpy(output + written * sizeof(u32), &low, sizeof(u32));
|
||||
}
|
||||
++written;
|
||||
return true;
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
ResultVal<u32> ArchiveManager::ReadExtSaveDataIcon(const ExtSaveDataInfo& info, u32 buffer_size,
|
||||
u8* buffer) {
|
||||
auto archive = id_code_map.find(
|
||||
static_cast<MediaType>(info.media_type) == MediaType::NAND
|
||||
? ArchiveIdCode::SharedExtSaveData
|
||||
: ArchiveIdCode::ExtSaveData);
|
||||
if (archive == id_code_map.end()) {
|
||||
return UnimplementedFunction(ErrorModule::FS);
|
||||
}
|
||||
|
||||
auto* ext_savedata =
|
||||
static_cast<FileSys::ArchiveFactory_ExtSaveData*>(archive->second.get());
|
||||
FileSys::Path path = FileSys::ConstructExtDataBinaryPath(
|
||||
static_cast<u32>(info.media_type), info.save_id_high, info.save_id_low);
|
||||
std::string icon_path =
|
||||
FileSys::GetExtSaveDataPath(ext_savedata->GetMountPoint(), path) + "icon";
|
||||
|
||||
FileUtil::IOFile file(icon_path, "rb");
|
||||
if (!file.IsOpen()) {
|
||||
return FileSys::ResultFileNotFound;
|
||||
}
|
||||
|
||||
u32 bytes_to_read =
|
||||
static_cast<u32>(std::min(static_cast<u64>(buffer_size), file.GetSize()));
|
||||
if (file.ReadBytes(buffer, bytes_to_read) != bytes_to_read) {
|
||||
return ResultUnknown;
|
||||
}
|
||||
return bytes_to_read;
|
||||
}
|
||||
|
||||
ResultVal<ArchiveManager::ExtDataBlockInfo> ArchiveManager::GetExtDataBlockSize(
|
||||
const ExtSaveDataInfo& info) {
|
||||
auto archive = id_code_map.find(
|
||||
static_cast<MediaType>(info.media_type) == MediaType::NAND
|
||||
? ArchiveIdCode::SharedExtSaveData
|
||||
: ArchiveIdCode::ExtSaveData);
|
||||
if (archive == id_code_map.end()) {
|
||||
return UnimplementedFunction(ErrorModule::FS);
|
||||
}
|
||||
|
||||
auto* ext_savedata =
|
||||
static_cast<FileSys::ArchiveFactory_ExtSaveData*>(archive->second.get());
|
||||
FileSys::Path path = FileSys::ConstructExtDataBinaryPath(
|
||||
static_cast<u32>(info.media_type), info.save_id_high, info.save_id_low);
|
||||
|
||||
CASCADE_RESULT(FileSys::ArchiveFormatInfo format_info, ext_savedata->GetFormatInfo(path, 0));
|
||||
|
||||
constexpr u32 block_size = 512;
|
||||
u64 total_blocks =
|
||||
(static_cast<u64>(format_info.total_size) + block_size - 1) / block_size;
|
||||
return ExtDataBlockInfo{total_blocks, total_blocks, block_size};
|
||||
}
|
||||
|
||||
Result ArchiveManager::DeleteSystemSaveData(u32 high, u32 low) {
|
||||
// Construct the binary path to the archive first
|
||||
const FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low);
|
||||
|
|
|
|||
|
|
@ -258,6 +258,39 @@ public:
|
|||
*/
|
||||
Result DeleteExtSaveData(MediaType media_type, u8 unknown, u32 high, u32 low);
|
||||
|
||||
/**
|
||||
* Enumerates ExtSaveData IDs for the specified media type
|
||||
* @param media_type The media type to enumerate (NAND / SDMC)
|
||||
* @param shared Whether to enumerate shared (NAND) ext save data
|
||||
* @param entry_size Size of each ID entry in bytes (4 or 8)
|
||||
* @param output Output buffer to write IDs into
|
||||
* @param max_count Maximum number of IDs to write
|
||||
* @return Number of IDs written on success, or error code
|
||||
*/
|
||||
ResultVal<u32> EnumerateExtSaveData(MediaType media_type, bool shared, u32 entry_size,
|
||||
u8* output, u32 max_count);
|
||||
|
||||
/**
|
||||
* Reads the SMDH icon from the specified ExtSaveData archive
|
||||
* @param info ExtSaveData archive identifier
|
||||
* @param buffer_size Maximum bytes to read into buffer
|
||||
* @param buffer Output buffer for icon data
|
||||
* @return Number of bytes read on success, or error code
|
||||
*/
|
||||
ResultVal<u32> ReadExtSaveDataIcon(const ExtSaveDataInfo& info, u32 buffer_size, u8* buffer);
|
||||
|
||||
/**
|
||||
* Returns block usage information for the specified ExtSaveData archive
|
||||
* @param info ExtSaveData archive identifier
|
||||
* @return Block info on success, or error code
|
||||
*/
|
||||
struct ExtDataBlockInfo {
|
||||
u64 total_blocks;
|
||||
u64 free_blocks;
|
||||
u32 block_size;
|
||||
};
|
||||
ResultVal<ExtDataBlockInfo> GetExtDataBlockSize(const ExtSaveDataInfo& info);
|
||||
|
||||
/**
|
||||
* Deletes the SystemSaveData archive folder for the specified save data id
|
||||
* @param high The high word of the SystemSaveData archive to delete
|
||||
|
|
|
|||
|
|
@ -1016,6 +1016,138 @@ void FS_USER::DeleteExtSaveData(Kernel::HLERequestContext& ctx) {
|
|||
info.save_id_low, info.save_id_high, info.media_type, info.unknown);
|
||||
}
|
||||
|
||||
void FS_USER::EnumerateExtSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 ids_size = rp.Pop<u32>();
|
||||
u8 media_type = rp.Pop<u8>();
|
||||
u32 id_size = rp.Pop<u32>();
|
||||
bool shared = rp.Pop<bool>();
|
||||
auto& output_buffer = rp.PopMappedBuffer();
|
||||
|
||||
u32 max_count = id_size > 0 ? ids_size / id_size : 0;
|
||||
std::vector<u8> output(ids_size);
|
||||
auto result = archives.EnumerateExtSaveData(static_cast<MediaType>(media_type), shared,
|
||||
id_size, output.data(), max_count);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
if (result.Failed()) {
|
||||
rb.Push(result.Code());
|
||||
rb.Push<u32>(0);
|
||||
} else {
|
||||
u32 count = result.Unwrap();
|
||||
output_buffer.Write(output.data(), 0, count * id_size);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(count);
|
||||
}
|
||||
rb.PushMappedBuffer(output_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, media_type={:02X} shared={} id_size={} ids_size={:08X}",
|
||||
media_type, shared, id_size, ids_size);
|
||||
}
|
||||
|
||||
void FS_USER::ObsoletedEnumerateExtSaveData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 ids_size = rp.Pop<u32>();
|
||||
u8 media_type = rp.Pop<u8>();
|
||||
auto& output_buffer = rp.PopMappedBuffer();
|
||||
|
||||
constexpr u32 id_size = sizeof(u64);
|
||||
u32 max_count = ids_size / id_size;
|
||||
std::vector<u8> output(ids_size);
|
||||
auto result = archives.EnumerateExtSaveData(static_cast<MediaType>(media_type), false,
|
||||
id_size, output.data(), max_count);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
if (result.Failed()) {
|
||||
rb.Push(result.Code());
|
||||
rb.Push<u32>(0);
|
||||
} else {
|
||||
u32 count = result.Unwrap();
|
||||
output_buffer.Write(output.data(), 0, count * id_size);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(count);
|
||||
}
|
||||
rb.PushMappedBuffer(output_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, media_type={:02X} ids_size={:08X}", media_type, ids_size);
|
||||
}
|
||||
|
||||
void FS_USER::ReadExtSaveDataIcon(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
ExtSaveDataInfo info = rp.PopRaw<ExtSaveDataInfo>();
|
||||
u32 smdh_size = rp.Pop<u32>();
|
||||
auto& output_buffer = rp.PopMappedBuffer();
|
||||
|
||||
std::vector<u8> icon_data(smdh_size);
|
||||
auto result = archives.ReadExtSaveDataIcon(info, smdh_size, icon_data.data());
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
if (result.Failed()) {
|
||||
rb.Push(result.Code());
|
||||
rb.Push<u32>(0);
|
||||
} else {
|
||||
output_buffer.Write(icon_data.data(), 0, *result);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(*result);
|
||||
}
|
||||
rb.PushMappedBuffer(output_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, save_low={:08X} save_high={:08X} smdh_size={:08X}",
|
||||
info.save_id_low, info.save_id_high, smdh_size);
|
||||
}
|
||||
|
||||
void FS_USER::GetExtDataBlockSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
ExtSaveDataInfo info = rp.PopRaw<ExtSaveDataInfo>();
|
||||
|
||||
auto result = archives.GetExtDataBlockSize(info);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(6, 0);
|
||||
if (result.Failed()) {
|
||||
rb.Push(result.Code());
|
||||
rb.Push<u32>(0);
|
||||
rb.Push<u32>(0);
|
||||
rb.Push<u32>(0);
|
||||
rb.Push<u32>(0);
|
||||
rb.Push<u32>(0);
|
||||
} else {
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(result->total_blocks & 0xFFFFFFFF));
|
||||
rb.Push(static_cast<u32>(result->total_blocks >> 32));
|
||||
rb.Push(static_cast<u32>(result->free_blocks & 0xFFFFFFFF));
|
||||
rb.Push(static_cast<u32>(result->free_blocks >> 32));
|
||||
rb.Push(result->block_size);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, save_low={:08X} save_high={:08X}", info.save_id_low,
|
||||
info.save_id_high);
|
||||
}
|
||||
|
||||
void FS_USER::CheckArchive(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto archive_id = rp.PopEnum<FS::ArchiveIdCode>();
|
||||
const auto archivename_type = rp.PopEnum<FileSys::LowPathType>();
|
||||
const auto archivename_size = rp.Pop<u32>();
|
||||
std::vector<u8> archivename = rp.PopStaticBuffer();
|
||||
ASSERT(archivename.size() == archivename_size);
|
||||
const FileSys::Path archive_path(archivename_type, std::move(archivename));
|
||||
|
||||
ClientSlot* slot = GetSessionData(ctx.Session());
|
||||
const ResultVal<ArchiveHandle> handle =
|
||||
archives.OpenArchive(archive_id, archive_path, slot->program_id);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
if (handle.Succeeded()) {
|
||||
archives.CloseArchive(*handle);
|
||||
rb.Push(ResultSuccess);
|
||||
} else {
|
||||
rb.Push(handle.Code());
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, archive_id=0x{:08X} archive_path={}", archive_id,
|
||||
archive_path.DebugStr());
|
||||
}
|
||||
|
||||
void FS_USER::CardSlotIsInserted(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
|
|
@ -1844,7 +1976,7 @@ FS_USER::FS_USER(Core::System& system)
|
|||
{0x0830, &FS_USER::ObsoletedCreateExtSaveData, "Obsoleted_3_0_CreateExtSaveData"},
|
||||
{0x0831, nullptr, "CreateSharedExtSaveData"},
|
||||
{0x0832, nullptr, "ReadExtSaveDataIcon"},
|
||||
{0x0833, nullptr, "EnumerateExtSaveData"},
|
||||
{0x0833, &FS_USER::ObsoletedEnumerateExtSaveData, "Obsoleted_3_0_EnumerateExtSaveData"},
|
||||
{0x0834, nullptr, "EnumerateSharedExtSaveData"},
|
||||
{0x0835, &FS_USER::ObsoletedDeleteExtSaveData, "Obsoleted_3_0_DeleteExtSaveData"},
|
||||
{0x0836, nullptr, "DeleteSharedExtSaveData"},
|
||||
|
|
@ -1876,9 +2008,9 @@ FS_USER::FS_USER(Core::System& system)
|
|||
{0x0850, nullptr, "GetSpecialFileSize"},
|
||||
{0x0851, &FS_USER::CreateExtSaveData, "CreateExtSaveData"},
|
||||
{0x0852, &FS_USER::DeleteExtSaveData, "DeleteExtSaveData"},
|
||||
{0x0853, nullptr, "ReadExtSaveDataIcon"},
|
||||
{0x0854, nullptr, "GetExtDataBlockSize"},
|
||||
{0x0855, nullptr, "EnumerateExtSaveData"},
|
||||
{0x0853, &FS_USER::ReadExtSaveDataIcon, "ReadExtSaveDataIcon"},
|
||||
{0x0854, &FS_USER::GetExtDataBlockSize, "GetExtDataBlockSize"},
|
||||
{0x0855, &FS_USER::EnumerateExtSaveData, "EnumerateExtSaveData"},
|
||||
{0x0856, &FS_USER::CreateSystemSaveData, "CreateSystemSaveData"},
|
||||
{0x0857, &FS_USER::DeleteSystemSaveData, "DeleteSystemSaveData"},
|
||||
{0x0858, nullptr, "StartDeviceMoveAsSource"},
|
||||
|
|
@ -1902,6 +2034,7 @@ FS_USER::FS_USER(Core::System& system)
|
|||
{0x086A, nullptr, "ReadNandReport"},
|
||||
{0x086E, &FS_USER::SetThisSaveDataSecureValue, "SetThisSaveDataSecureValue" },
|
||||
{0x086F, &FS_USER::GetThisSaveDataSecureValue, "GetThisSaveDataSecureValue" },
|
||||
{0x0870, &FS_USER::CheckArchive, "CheckArchive"},
|
||||
{0x0875, &FS_USER::SetSaveDataSecureValue, "SetSaveDataSecureValue" },
|
||||
{0x0876, &FS_USER::GetSaveDataSecureValue, "GetSaveDataSecureValue" },
|
||||
{0x087A, &FS_USER::AddSeed, "AddSeed"},
|
||||
|
|
|
|||
|
|
@ -428,6 +428,73 @@ private:
|
|||
*/
|
||||
void DeleteExtSaveData(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::EnumerateExtSaveData service function (0x08550102)
|
||||
* Inputs:
|
||||
* 1 : Output IDs buffer size in bytes
|
||||
* 2 : Media type (NAND / SDMC)
|
||||
* 3 : ID entry size (4 or 8)
|
||||
* 4 : Shared flag (0 = normal, 1 = shared)
|
||||
* 5 : (IDs buffer size << 4) | 0xC
|
||||
* 6 : Pointer to output IDs buffer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Number of IDs written
|
||||
*/
|
||||
void EnumerateExtSaveData(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::Obsoleted_3_0_EnumerateExtSaveData service function (0x08330082)
|
||||
* Inputs:
|
||||
* 1 : Output IDs buffer size in bytes
|
||||
* 2 : Media type (NAND / SDMC)
|
||||
* 3 : (IDs buffer size << 4) | 0xC
|
||||
* 4 : Pointer to output IDs buffer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Number of IDs written
|
||||
*/
|
||||
void ObsoletedEnumerateExtSaveData(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::ReadExtSaveDataIcon service function (0x08530142)
|
||||
* Inputs:
|
||||
* 1-4 : ExtSaveDataInfo
|
||||
* 5 : SMDH buffer size
|
||||
* 6 : (SMDH size << 4) | 0xC
|
||||
* 7 : Pointer to output SMDH buffer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : Bytes read
|
||||
*/
|
||||
void ReadExtSaveDataIcon(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetExtDataBlockSize service function (0x08540100)
|
||||
* Inputs:
|
||||
* 1-4 : ExtSaveDataInfo
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2-3 : Total blocks (u64)
|
||||
* 4-5 : Free blocks (u64)
|
||||
* 6 : Block size (u32)
|
||||
*/
|
||||
void GetExtDataBlockSize(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::CheckArchive service function (0x087000C2)
|
||||
* Checks whether the specified archive exists and is accessible.
|
||||
* Inputs:
|
||||
* 1 : Archive ID code
|
||||
* 2 : Archive path type (LowPathType)
|
||||
* 3 : Archive path size
|
||||
* 4 : Static buffer descriptor
|
||||
* 5 : Archive path pointer
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void CheckArchive(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::CardSlotIsInserted service function.
|
||||
* Inputs:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue