mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-03 16:49:04 +00:00
Refactor InnerFAT into Savegame and Extdata
And derive TitleDB from it too.
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
// Copyright 2021 threeSD Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/data_container.h"
|
||||
#include "core/decryptor.h"
|
||||
#include "core/extdata.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
Extdata::Extdata(std::string data_path_, const SDMCDecryptor& decryptor_)
|
||||
: data_path(std::move(data_path_)), decryptor(&decryptor_) {
|
||||
|
||||
if (data_path.back() != '/' && data_path.back() != '\\') {
|
||||
data_path += '/';
|
||||
}
|
||||
|
||||
use_decryptor = true;
|
||||
is_good = Init();
|
||||
}
|
||||
|
||||
Extdata::Extdata(std::string data_path_) : data_path(std::move(data_path_)) {
|
||||
if (data_path.back() != '/' && data_path.back() != '\\') {
|
||||
data_path += '/';
|
||||
}
|
||||
|
||||
use_decryptor = false;
|
||||
is_good = Init();
|
||||
}
|
||||
|
||||
Extdata::~Extdata() = default;
|
||||
|
||||
bool Extdata::CheckMagic() const {
|
||||
if (header.fat_header.magic != MakeMagic('V', 'S', 'X', 'E') ||
|
||||
header.fat_header.version != 0x30000) {
|
||||
|
||||
LOG_ERROR(Core, "File is invalid, decryption errors may have happened.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Extdata::IsGood() const {
|
||||
return is_good;
|
||||
}
|
||||
|
||||
bool Extdata::Extract(std::string path) const {
|
||||
if (path.back() != '/' && path.back() != '\\') {
|
||||
path += '/';
|
||||
}
|
||||
|
||||
if (!ExtractDirectory(path, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write format info
|
||||
const auto format_info = GetFormatInfo();
|
||||
return FileUtil::WriteBytesToFile(path + "metadata", &format_info, sizeof(format_info));
|
||||
}
|
||||
|
||||
std::vector<u8> Extdata::ReadFile(const std::string& path) const {
|
||||
if (use_decryptor) {
|
||||
return decryptor->DecryptFile(path);
|
||||
} else {
|
||||
FileUtil::IOFile file(path, "rb");
|
||||
return file.GetData();
|
||||
}
|
||||
}
|
||||
|
||||
bool Extdata::Init() {
|
||||
// Read VSXE file
|
||||
auto vsxe_raw = ReadFile(data_path + "00000000/00000001");
|
||||
if (vsxe_raw.empty()) {
|
||||
LOG_ERROR(Core, "Failed to load or decrypt VSXE");
|
||||
return false;
|
||||
}
|
||||
|
||||
DataContainer vsxe_container(std::move(vsxe_raw));
|
||||
if (!vsxe_container.IsGood()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::vector<u8>> data;
|
||||
if (!vsxe_container.GetIVFCLevel4Data(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Archive<Extdata>::Init(std::move(data));
|
||||
}
|
||||
|
||||
bool Extdata::ExtractFile(const std::string& path, std::size_t index) const {
|
||||
/// Maximum amount of device files a device directory can hold.
|
||||
constexpr u32 DeviceDirCapacity = 126;
|
||||
|
||||
FileUtil::IOFile file(path, "wb");
|
||||
if (!file) {
|
||||
LOG_ERROR(Core, "Could not open file {}", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 file_index = index + 1;
|
||||
u32 sub_directory_id = file_index / DeviceDirCapacity;
|
||||
u32 sub_file_id = file_index % DeviceDirCapacity;
|
||||
std::string device_file_path =
|
||||
fmt::format("{}{:08x}/{:08x}", data_path, sub_directory_id, sub_file_id);
|
||||
|
||||
auto container_data = ReadFile(device_file_path);
|
||||
if (container_data.empty()) { // File does not exist?
|
||||
LOG_WARNING(Core, "Ignoring file {}", device_file_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
DataContainer container(std::move(container_data));
|
||||
if (!container.IsGood()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::vector<u8>> data;
|
||||
if (!container.GetIVFCLevel4Data(data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.WriteBytes(data[0].data(), data[0].size()) != data[0].size()) {
|
||||
LOG_ERROR(Core, "Write data failed (file: {})", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArchiveFormatInfo Extdata::GetFormatInfo() const {
|
||||
// This information is based on how Citra created the metadata in FS
|
||||
ArchiveFormatInfo format_info = {/* total_size */ 0,
|
||||
/* number_directories */ fs_info.maximum_directory_count,
|
||||
/* number_files */ fs_info.maximum_file_count,
|
||||
/* duplicate_data */ false};
|
||||
|
||||
return format_info;
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
Reference in New Issue
Block a user