Files
threeSD/src/core/extdata.cpp
T
2021-07-06 16:42:22 +08:00

131 lines
3.6 KiB
C++

// 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;
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;
}
return FileUtil::WriteBytesToFile(path, data[0].data(), data[0].size());
}
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