mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-22 12:50:05 +00:00
file_sys/title_metadata: Allow loading from both files, FileBackends, and data
This commit is contained in:
parent
b3e87d01fb
commit
ce806dcdf6
@ -29,47 +29,70 @@ static u32 GetSignatureSize(u32 signature_type) {
|
|||||||
case EcdsaSha256:
|
case EcdsaSha256:
|
||||||
return 0x3C;
|
return 0x3C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader::ResultStatus TitleMetadata::Load() {
|
Loader::ResultStatus TitleMetadata::Load(const std::string& file_path) {
|
||||||
FileUtil::IOFile file(filepath, "rb");
|
FileUtil::IOFile file(file_path, "rb");
|
||||||
if (!file.IsOpen())
|
if (!file.IsOpen())
|
||||||
return Loader::ResultStatus::Error;
|
return Loader::ResultStatus::Error;
|
||||||
|
|
||||||
if (!file.ReadBytes(&signature_type, sizeof(u32_be)))
|
std::vector<u8> file_data(file.GetSize());
|
||||||
|
|
||||||
|
if (!file.ReadBytes(file_data.data(), file.GetSize()))
|
||||||
return Loader::ResultStatus::Error;
|
return Loader::ResultStatus::Error;
|
||||||
|
|
||||||
|
Loader::ResultStatus result = Load(file_data);
|
||||||
|
if (result != Loader::ResultStatus::Success)
|
||||||
|
LOG_ERROR(Service_FS, "Failed to load TMD from file %s!", file_path.c_str());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader::ResultStatus TitleMetadata::Load(const std::vector<u8> file_data, size_t offset) {
|
||||||
|
size_t total_size = static_cast<size_t>(file_data.size() - offset);
|
||||||
|
if (total_size < sizeof(u32_be))
|
||||||
|
return Loader::ResultStatus::Error;
|
||||||
|
|
||||||
|
memcpy(&signature_type, &file_data[offset], sizeof(u32_be));
|
||||||
|
|
||||||
// Signature lengths are variable, and the body follows the signature
|
// Signature lengths are variable, and the body follows the signature
|
||||||
u32 signature_size = GetSignatureSize(signature_type);
|
u32 signature_size = GetSignatureSize(signature_type);
|
||||||
|
|
||||||
tmd_signature.resize(signature_size);
|
|
||||||
if (!file.ReadBytes(&tmd_signature[0], signature_size))
|
|
||||||
return Loader::ResultStatus::Error;
|
|
||||||
|
|
||||||
// The TMD body start position is rounded to the nearest 0x40 after the signature
|
// The TMD body start position is rounded to the nearest 0x40 after the signature
|
||||||
size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
|
size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
|
||||||
file.Seek(body_start, SEEK_SET);
|
size_t body_end = body_start + sizeof(Body);
|
||||||
|
|
||||||
// Read our TMD body, then load the amount of ContentChunks specified
|
if (total_size < body_end)
|
||||||
if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body))
|
|
||||||
return Loader::ResultStatus::Error;
|
return Loader::ResultStatus::Error;
|
||||||
|
|
||||||
|
// Read signature + TMD body, then load the amount of ContentChunks specified
|
||||||
|
tmd_signature.resize(signature_size);
|
||||||
|
memcpy(tmd_signature.data(), &file_data[offset + sizeof(u32_be)], signature_size);
|
||||||
|
memcpy(&tmd_body, &file_data[offset + body_start], sizeof(TitleMetadata::Body));
|
||||||
|
|
||||||
|
size_t expected_size =
|
||||||
|
body_start + sizeof(Body) + tmd_body.content_count * sizeof(ContentChunk);
|
||||||
|
if (total_size < expected_size) {
|
||||||
|
LOG_ERROR(Service_FS, "Malformed TMD, expected size 0x%zx, got 0x%zx!", expected_size,
|
||||||
|
total_size);
|
||||||
|
return Loader::ResultStatus::ErrorInvalidFormat;
|
||||||
|
}
|
||||||
|
|
||||||
for (u16 i = 0; i < tmd_body.content_count; i++) {
|
for (u16 i = 0; i < tmd_body.content_count; i++) {
|
||||||
ContentChunk chunk;
|
ContentChunk chunk;
|
||||||
if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) {
|
|
||||||
tmd_chunks.push_back(chunk);
|
memcpy(&chunk, &file_data[offset + body_end + (i * sizeof(ContentChunk))],
|
||||||
} else {
|
sizeof(ContentChunk));
|
||||||
LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!",
|
tmd_chunks.push_back(chunk);
|
||||||
filepath.c_str(), i);
|
|
||||||
return Loader::ResultStatus::ErrorInvalidFormat;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Loader::ResultStatus::Success;
|
return Loader::ResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader::ResultStatus TitleMetadata::Save() {
|
Loader::ResultStatus TitleMetadata::Save(const std::string& file_path) {
|
||||||
FileUtil::IOFile file(filepath, "wb");
|
FileUtil::IOFile file(file_path, "wb");
|
||||||
if (!file.IsOpen())
|
if (!file.IsOpen())
|
||||||
return Loader::ResultStatus::Error;
|
return Loader::ResultStatus::Error;
|
||||||
|
|
||||||
@ -186,8 +209,7 @@ void TitleMetadata::AddContentChunk(const ContentChunk& chunk) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TitleMetadata::Print() const {
|
void TitleMetadata::Print() const {
|
||||||
LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(),
|
LOG_DEBUG(Service_FS, "%u chunks", static_cast<u32>(tmd_body.content_count));
|
||||||
static_cast<u32>(tmd_body.content_count));
|
|
||||||
|
|
||||||
// Content info describes ranges of content chunks
|
// Content info describes ranges of content chunks
|
||||||
LOG_DEBUG(Service_FS, "Content info:");
|
LOG_DEBUG(Service_FS, "Content info:");
|
||||||
|
@ -92,9 +92,9 @@ public:
|
|||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {}
|
Loader::ResultStatus Load(const std::string& file_path);
|
||||||
Loader::ResultStatus Load();
|
Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0);
|
||||||
Loader::ResultStatus Save();
|
Loader::ResultStatus Save(const std::string& file_path);
|
||||||
|
|
||||||
u64 GetTitleID() const;
|
u64 GetTitleID() const;
|
||||||
u32 GetTitleType() const;
|
u32 GetTitleType() const;
|
||||||
@ -121,8 +121,6 @@ private:
|
|||||||
u32_be signature_type;
|
u32_be signature_type;
|
||||||
std::vector<u8> tmd_signature;
|
std::vector<u8> tmd_signature;
|
||||||
std::vector<ContentChunk> tmd_chunks;
|
std::vector<ContentChunk> tmd_chunks;
|
||||||
|
|
||||||
std::string filepath;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
@ -98,8 +98,8 @@ std::string GetTitleContentPath(Service::FS::MediaType media_type, u64 tid, u16
|
|||||||
std::string tmd_path = GetTitleMetadataPath(media_type, tid);
|
std::string tmd_path = GetTitleMetadataPath(media_type, tid);
|
||||||
|
|
||||||
u32 content_id = 0;
|
u32 content_id = 0;
|
||||||
FileSys::TitleMetadata tmd(tmd_path);
|
FileSys::TitleMetadata tmd;
|
||||||
if (tmd.Load() == Loader::ResultStatus::Success) {
|
if (tmd.LoadFromFile(tmd_path) == Loader::ResultStatus::Success) {
|
||||||
content_id = tmd.GetContentIDByIndex(index);
|
content_id = tmd.GetContentIDByIndex(index);
|
||||||
|
|
||||||
// TODO(shinyquagsire23): how does DLC actually get this folder on hardware?
|
// TODO(shinyquagsire23): how does DLC actually get this folder on hardware?
|
||||||
@ -199,8 +199,8 @@ void FindContentInfos(Service::Interface* self) {
|
|||||||
std::string tmd_path = GetTitleMetadataPath(media_type, title_id);
|
std::string tmd_path = GetTitleMetadataPath(media_type, title_id);
|
||||||
|
|
||||||
u32 content_read = 0;
|
u32 content_read = 0;
|
||||||
FileSys::TitleMetadata tmd(tmd_path);
|
FileSys::TitleMetadata tmd;
|
||||||
if (tmd.Load() == Loader::ResultStatus::Success) {
|
if (tmd.LoadFromFile(tmd_path) == Loader::ResultStatus::Success) {
|
||||||
// Get info for each content index requested
|
// Get info for each content index requested
|
||||||
for (size_t i = 0; i < content_count; i++) {
|
for (size_t i = 0; i < content_count; i++) {
|
||||||
std::shared_ptr<FileUtil::IOFile> romfs_file;
|
std::shared_ptr<FileUtil::IOFile> romfs_file;
|
||||||
@ -238,8 +238,8 @@ void ListContentInfos(Service::Interface* self) {
|
|||||||
std::string tmd_path = GetTitleMetadataPath(media_type, title_id);
|
std::string tmd_path = GetTitleMetadataPath(media_type, title_id);
|
||||||
|
|
||||||
u32 copied = 0;
|
u32 copied = 0;
|
||||||
FileSys::TitleMetadata tmd(tmd_path);
|
FileSys::TitleMetadata tmd;
|
||||||
if (tmd.Load() == Loader::ResultStatus::Success) {
|
if (tmd.LoadFromFile(tmd_path) == Loader::ResultStatus::Success) {
|
||||||
copied = std::min(content_count, static_cast<u32>(tmd.GetContentCount()));
|
copied = std::min(content_count, static_cast<u32>(tmd.GetContentCount()));
|
||||||
for (u32 i = start_index; i < copied; i++) {
|
for (u32 i = start_index; i < copied; i++) {
|
||||||
std::shared_ptr<FileUtil::IOFile> romfs_file;
|
std::shared_ptr<FileUtil::IOFile> romfs_file;
|
||||||
@ -313,8 +313,8 @@ ResultCode GetTitleInfoFromList(const std::vector<u64>& title_id_list,
|
|||||||
TitleInfo title_info = {};
|
TitleInfo title_info = {};
|
||||||
title_info.tid = title_id_list[i];
|
title_info.tid = title_id_list[i];
|
||||||
|
|
||||||
FileSys::TitleMetadata tmd(tmd_path);
|
FileSys::TitleMetadata tmd;
|
||||||
if (tmd.Load() == Loader::ResultStatus::Success) {
|
if (tmd.LoadFromFile(tmd_path) == Loader::ResultStatus::Success) {
|
||||||
// TODO(shinyquagsire23): This is the total size of all files this process owns,
|
// TODO(shinyquagsire23): This is the total size of all files this process owns,
|
||||||
// including savefiles and other content. This comes close but is off.
|
// including savefiles and other content. This comes close but is off.
|
||||||
title_info.size = tmd.GetContentSizeByIndex(FileSys::TMDContentIndex::Main);
|
title_info.size = tmd.GetContentSizeByIndex(FileSys::TMDContentIndex::Main);
|
||||||
@ -462,8 +462,8 @@ void GetNumContentInfos(Service::Interface* self) {
|
|||||||
|
|
||||||
std::string tmd_path = GetTitleMetadataPath(media_type, title_id);
|
std::string tmd_path = GetTitleMetadataPath(media_type, title_id);
|
||||||
|
|
||||||
FileSys::TitleMetadata tmd(tmd_path);
|
FileSys::TitleMetadata tmd;
|
||||||
if (tmd.Load() == Loader::ResultStatus::Success) {
|
if (tmd.LoadFromFile(tmd_path) == Loader::ResultStatus::Success)
|
||||||
rb.Push<u32>(tmd.GetContentCount());
|
rb.Push<u32>(tmd.GetContentCount());
|
||||||
} else {
|
} else {
|
||||||
rb.Push<u32>(1); // Number of content infos plus one
|
rb.Push<u32>(1); // Number of content infos plus one
|
||||||
|
Loading…
Reference in New Issue
Block a user