Game list fixes and more structs and stuff

This commit is contained in:
Zach Hilman
2018-06-15 21:46:49 -04:00
parent a081c29541
commit f0128dfcce
5 changed files with 170 additions and 16 deletions

View File

@@ -5,15 +5,20 @@
#include <vector> #include <vector>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h" #include "common/file_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/disk_filesystem.h"
#include "core/file_sys/program_metadata.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
#include "core/loader/nro.h" #include "core/loader/nro.h"
#include "core/memory.h" #include "core/memory.h"
#include "nca.h" #include "nca.h"
#include "nso.h"
namespace Loader { namespace Loader {
@@ -44,9 +49,36 @@ struct NcaHeader {
u8 hash_tables[0x4][0x20]; u8 hash_tables[0x4][0x20];
u8 key_area[0x4][0x10]; u8 key_area[0x4][0x10];
INSERT_PADDING_BYTES(0xC0); INSERT_PADDING_BYTES(0xC0);
INSERT_PADDING_BYTES(0x800);
}; };
static_assert(sizeof(NcaHeader) == 0xC00, "NcaHeader has incorrect size."); static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size.");
struct NcaSectionTableEntry {
u32 media_offset;
u32 media_end_offset;
INSERT_PADDING_BYTES(0x8);
};
static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size.");
struct NcaSectionHeaderBlock {
INSERT_PADDING_BYTES(3);
u8 filesystem_type;
u8 crypto_type;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size.");
struct Pfs0Superblock {
NcaSectionHeaderBlock header_block;
std::array<u8, 0x20> hash;
u32 size;
INSERT_PADDING_BYTES(4);
u64 hash_table_offset;
u64 hash_table_size;
u64 pfs0_header_offset;
u64 pfs0_size;
INSERT_PADDING_BYTES(432);
};
static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size.");
///** ///**
// * Adapted from hactool/aes.c, void aes_xts_decrypt(aes_ctx_t *ctx, void *dst, // * Adapted from hactool/aes.c, void aes_xts_decrypt(aes_ctx_t *ctx, void *dst,
@@ -110,8 +142,8 @@ AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath)
FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) { FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) {
file.Seek(0, SEEK_SET); file.Seek(0, SEEK_SET);
std::array<u8, 0xC00> header_enc_array{}; std::array<u8, 0x400> header_enc_array{};
if (0xC00 != file.ReadBytes(header_enc_array.data(), 0xC00)) if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400))
return FileType::Error; return FileType::Error;
// NcaHeader header = DecryptHeader(header_enc_array); // NcaHeader header = DecryptHeader(header_enc_array);
@@ -125,7 +157,53 @@ FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&)
} }
ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
NGLOG_CRITICAL(Loader, "UNIMPLEMENTED!"); if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
if (!file.IsOpen()) {
return ResultStatus::Error;
}
pfs.Load(filepath, 0x8000);
const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
std::vector<u8> npdm(pfs0.GetFileSize("main.npdm"));
file.Seek(pfs0.GetFileOffset("main.npdm"), SEEK_SET);
if (npdm.size() != file.ReadBytes(npdm.data(), npdm.size()))
return ResultStatus::Error;
ResultStatus result = metadata.Load(npdm);
if (result != ResultStatus::Success) {
return result;
}
metadata.Print();
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
return ResultStatus::ErrorUnsupportedArch;
}
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(module, ) if (next_load_addr) {
NGLOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
}
else {
next_load_addr = load_addr;
}
}
process->program_id = metadata.GetTitleID();
process->svc_access_mask.set();
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
return ResultStatus::Error; return ResultStatus::Error;
} }

View File

@@ -6,12 +6,30 @@
#include <string> #include <string>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/loader/linker.h" #include "core/loader/linker.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
namespace Loader { namespace Loader {
class Nca {
FileSys::PartitionFilesystem GetPFS(u8 id);
u8 GetExeFsPfsId();
u64 GetExeFsFileOffset();
u64 GetExeFsFileSize();
u64 GetRomFSOffset();
u64 GetRomFSSize();
private:
std::vector<FileSys::PartitionFilesystem> pfs;
std::string path;
};
/// Loads an NCA file /// Loads an NCA file
class AppLoader_NCA final : public AppLoader, Linker { class AppLoader_NCA final : public AppLoader, Linker {
public: public:
@@ -33,6 +51,7 @@ public:
private: private:
std::string filepath; std::string filepath;
FileSys::ProgramMetadata metadata;
}; };
} // namespace Loader } // namespace Loader

View File

@@ -66,6 +66,20 @@ FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&)
return FileType::Error; return FileType::Error;
} }
static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
const NsoSegmentHeader& header) {
std::vector<u8> uncompressed_data;
uncompressed_data.resize(header.size);
const int bytes_uncompressed = LZ4_decompress_safe(
reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()), compressed_data.size(), header.size);
ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
}
static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header, static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header,
int compressed_size) { int compressed_size) {
std::vector<u8> compressed_data; std::vector<u8> compressed_data;
@@ -77,22 +91,62 @@ static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeade
return {}; return {};
} }
std::vector<u8> uncompressed_data; return DecompressSegment(compressed_data, header);
uncompressed_data.resize(header.size);
const int bytes_uncompressed = LZ4_decompress_safe(
reinterpret_cast<const char*>(compressed_data.data()),
reinterpret_cast<char*>(uncompressed_data.data()), compressed_size, header.size);
ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(),
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
return uncompressed_data;
} }
static constexpr u32 PageAlignSize(u32 size) { static constexpr u32 PageAlignSize(u32 size) {
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
} }
VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data,
VAddr load_base) {
NsoHeader nso_header{};
memcpy(&nso_header, file_data.data(), sizeof(NsoHeader));
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return {};
// Build program image
Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
std::vector<u8> program_image;
for (int i = 0; i < nso_header.segments.size(); ++i) {
std::vector<u8> compressed_data{};
for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j)
compressed_data[j] = file_data[nso_header.segments[i].offset + j];
std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
program_image.resize(nso_header.segments[i].location);
program_image.insert(program_image.end(), data.begin(), data.end());
codeset->segments[i].addr = nso_header.segments[i].location;
codeset->segments[i].offset = nso_header.segments[i].location;
codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
}
// MOD header pointer is at .text offset + 4
u32 module_offset;
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
// Read MOD header
ModHeader mod_header{};
// Default .bss to size in segment header if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
}
codeset->data.size += bss_size;
const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
program_image.resize(image_size);
// Load codeset for current process
codeset->name = name;
codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
Core::CurrentProcess()->LoadModule(codeset, load_base);
return load_base + image_size;
}
VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
FileUtil::IOFile file(path, "rb"); FileUtil::IOFile file(path, "rb");
if (!file.IsOpen()) { if (!file.IsOpen()) {

View File

@@ -29,6 +29,9 @@ public:
return IdentifyType(file, filepath); return IdentifyType(file, filepath);
} }
static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data,
VAddr load_base);
static VAddr LoadModule(const std::string& path, VAddr load_base); static VAddr LoadModule(const std::string& path, VAddr load_base);
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;

View File

@@ -356,7 +356,7 @@ void GameList::LoadInterfaceLayout() {
item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
} }
const QStringList GameList::supported_file_extensions = {"nso", "nro"}; const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca"};
static bool HasSupportedFileExtension(const std::string& file_name) { static bool HasSupportedFileExtension(const std::string& file_name) {
QFileInfo file = QFileInfo(file_name.c_str()); QFileInfo file = QFileInfo(file_name.c_str());