- nca::ParseControl now gets the nacp base on the language. - xci returns a more descriptive error and logs info during install. - replace volatile with atmoic, label atmoic methods with volatile as per the standard. - s2s usb install skips verifying the content as its being installed from another switch. - fix game / save menu bug where it would only load the nacp if it has started yet, now it will force load even if its in progress. this fixes save file zips having empty names. - game menu saves the application type, to be later used for displaying if its a gamecard (inserted or not), launched etc.
94 lines
2.7 KiB
C++
94 lines
2.7 KiB
C++
#include "yati/container/xci.hpp"
|
|
#include "defines.hpp"
|
|
#include "log.hpp"
|
|
|
|
namespace sphaira::yati::container {
|
|
namespace {
|
|
|
|
#define XCI_MAGIC std::byteswap(0x48454144)
|
|
#define HFS0_MAGIC 0x30534648
|
|
#define HFS0_HEADER_OFFSET 0xF000
|
|
|
|
struct Hfs0Header {
|
|
u32 magic;
|
|
u32 total_files;
|
|
u32 string_table_size;
|
|
u32 padding;
|
|
};
|
|
|
|
struct Hfs0FileTableEntry {
|
|
u64 data_offset;
|
|
u64 data_size;
|
|
u32 name_offset;
|
|
u32 hash_size;
|
|
u64 padding;
|
|
u8 hash[0x20];
|
|
};
|
|
|
|
struct Hfs0 {
|
|
Hfs0Header header{};
|
|
std::vector<Hfs0FileTableEntry> file_table{};
|
|
std::vector<std::string> string_table{};
|
|
s64 data_offset{};
|
|
};
|
|
|
|
Result Hfs0GetPartition(source::Base* source, s64 off, Hfs0& out) {
|
|
u64 bytes_read;
|
|
|
|
// get header
|
|
R_TRY(source->Read(std::addressof(out.header), off, sizeof(out.header), std::addressof(bytes_read)));
|
|
R_UNLESS(out.header.magic == HFS0_MAGIC, Result_XciBadMagic);
|
|
off += bytes_read;
|
|
|
|
// get file table
|
|
out.file_table.resize(out.header.total_files);
|
|
R_TRY(source->Read(out.file_table.data(), off, out.file_table.size() * sizeof(Hfs0FileTableEntry), std::addressof(bytes_read)))
|
|
off += bytes_read;
|
|
|
|
// get string table
|
|
std::vector<char> string_table(out.header.string_table_size);
|
|
R_TRY(source->Read(string_table.data(), off, string_table.size(), std::addressof(bytes_read)))
|
|
off += bytes_read;
|
|
|
|
for (u32 i = 0; i < out.header.total_files; i++) {
|
|
out.string_table.emplace_back(string_table.data() + out.file_table[i].name_offset);
|
|
}
|
|
|
|
out.data_offset = off;
|
|
R_SUCCEED();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Result Xci::GetCollections(Collections& out) {
|
|
Hfs0 root{};
|
|
R_TRY(Hfs0GetPartition(m_source, HFS0_HEADER_OFFSET, root));
|
|
log_write("[XCI] got root partition\n");
|
|
|
|
for (u32 i = 0; i < root.header.total_files; i++) {
|
|
if (root.string_table[i] == "secure") {
|
|
log_write("[XCI] found secure partition\n");
|
|
|
|
Hfs0 secure{};
|
|
R_TRY(Hfs0GetPartition(m_source, root.data_offset + root.file_table[i].data_offset, secure));
|
|
log_write("[XCI] got secure partition\n");
|
|
|
|
for (u32 i = 0; i < secure.header.total_files; i++) {
|
|
CollectionEntry entry;
|
|
entry.name = secure.string_table[i];
|
|
entry.offset = secure.data_offset + secure.file_table[i].data_offset;
|
|
entry.size = secure.file_table[i].data_size;
|
|
out.emplace_back(entry);
|
|
}
|
|
|
|
R_SUCCEED();
|
|
} else {
|
|
log_write("[XCI] skipping partition %u | %s\n", i, root.string_table[i].c_str());
|
|
}
|
|
}
|
|
|
|
return Result_XciSecurePartitionNotFound;
|
|
}
|
|
|
|
} // namespace sphaira::yati::container
|