|
|
@ -4,8 +4,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
|
|
#include "common/file_util.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "core/loader/ncch.h"
|
|
|
|
#include "core/loader/ncch.h"
|
|
|
|
#include "core/hle/kernel/kernel.h"
|
|
|
|
#include "core/hle/kernel/kernel.h"
|
|
|
|
#include "core/mem_map.h"
|
|
|
|
#include "core/mem_map.h"
|
|
|
@ -99,15 +97,6 @@ static bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompresse
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// AppLoader_NCCH class
|
|
|
|
// AppLoader_NCCH class
|
|
|
|
|
|
|
|
|
|
|
|
/// AppLoader_NCCH constructor
|
|
|
|
|
|
|
|
AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) {
|
|
|
|
|
|
|
|
this->filename = filename;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// AppLoader_NCCH destructor
|
|
|
|
|
|
|
|
AppLoader_NCCH::~AppLoader_NCCH() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ResultStatus AppLoader_NCCH::LoadExec() const {
|
|
|
|
ResultStatus AppLoader_NCCH::LoadExec() const {
|
|
|
|
if (!is_loaded)
|
|
|
|
if (!is_loaded)
|
|
|
|
return ResultStatus::ErrorNotLoaded;
|
|
|
|
return ResultStatus::ErrorNotLoaded;
|
|
|
@ -123,8 +112,9 @@ ResultStatus AppLoader_NCCH::LoadExec() const {
|
|
|
|
|
|
|
|
|
|
|
|
ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const {
|
|
|
|
ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) const {
|
|
|
|
// Iterate through the ExeFs archive until we find the .code file...
|
|
|
|
// Iterate through the ExeFs archive until we find the .code file...
|
|
|
|
FileUtil::IOFile file(filename, "rb");
|
|
|
|
if (!file->IsOpen())
|
|
|
|
if (file.IsOpen()) {
|
|
|
|
return ResultStatus::Error;
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG(Loader, "%d sections:", kMaxSections);
|
|
|
|
LOG_DEBUG(Loader, "%d sections:", kMaxSections);
|
|
|
|
for (int i = 0; i < kMaxSections; i++) {
|
|
|
|
for (int i = 0; i < kMaxSections; i++) {
|
|
|
|
// Load the specified section...
|
|
|
|
// Load the specified section...
|
|
|
@ -135,7 +125,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
|
|
|
|
|
|
|
|
|
|
|
|
s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
|
|
|
|
s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
|
|
|
|
sizeof(ExeFs_Header)+ncch_offset);
|
|
|
|
sizeof(ExeFs_Header)+ncch_offset);
|
|
|
|
file.Seek(section_offset, 0);
|
|
|
|
file->Seek(section_offset, 0);
|
|
|
|
|
|
|
|
|
|
|
|
// Section is compressed...
|
|
|
|
// Section is compressed...
|
|
|
|
if (i == 0 && is_compressed) {
|
|
|
|
if (i == 0 && is_compressed) {
|
|
|
@ -146,7 +136,7 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
|
|
|
|
} catch (std::bad_alloc&) {
|
|
|
|
} catch (std::bad_alloc&) {
|
|
|
|
return ResultStatus::ErrorMemoryAllocationFailed;
|
|
|
|
return ResultStatus::ErrorMemoryAllocationFailed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size);
|
|
|
|
file->ReadBytes(&temp_buffer[0], exefs_header.section[i].size);
|
|
|
|
|
|
|
|
|
|
|
|
// Decompress .code section...
|
|
|
|
// Decompress .code section...
|
|
|
|
u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0],
|
|
|
|
u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0],
|
|
|
@ -160,34 +150,29 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>&
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
else {
|
|
|
|
buffer.resize(exefs_header.section[i].size);
|
|
|
|
buffer.resize(exefs_header.section[i].size);
|
|
|
|
file.ReadBytes(&buffer[0], exefs_header.section[i].size);
|
|
|
|
file->ReadBytes(&buffer[0], exefs_header.section[i].size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ResultStatus::Success;
|
|
|
|
return ResultStatus::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
|
|
LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
|
|
|
|
|
|
|
|
return ResultStatus::Error;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ResultStatus::ErrorNotUsed;
|
|
|
|
return ResultStatus::ErrorNotUsed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ResultStatus AppLoader_NCCH::Load() {
|
|
|
|
ResultStatus AppLoader_NCCH::Load() {
|
|
|
|
LOG_INFO(Loader, "Loading NCCH file %s...", filename.c_str());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (is_loaded)
|
|
|
|
if (is_loaded)
|
|
|
|
return ResultStatus::ErrorAlreadyLoaded;
|
|
|
|
return ResultStatus::ErrorAlreadyLoaded;
|
|
|
|
|
|
|
|
|
|
|
|
FileUtil::IOFile file(filename, "rb");
|
|
|
|
if (!file->IsOpen())
|
|
|
|
if (file.IsOpen()) {
|
|
|
|
return ResultStatus::Error;
|
|
|
|
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
|
|
|
|
|
|
|
|
|
|
|
file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
|
|
|
|
|
|
|
|
|
|
|
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
|
|
|
// Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
|
|
|
|
if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
|
|
|
|
if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
|
|
|
|
LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
|
|
|
|
LOG_WARNING(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
|
|
|
|
ncch_offset = 0x4000;
|
|
|
|
ncch_offset = 0x4000;
|
|
|
|
file.Seek(ncch_offset, 0);
|
|
|
|
file->Seek(ncch_offset, 0);
|
|
|
|
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
|
|
|
file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Verify we are loading the correct file type...
|
|
|
|
// Verify we are loading the correct file type...
|
|
|
@ -196,7 +181,7 @@ ResultStatus AppLoader_NCCH::Load() {
|
|
|
|
|
|
|
|
|
|
|
|
// Read ExHeader...
|
|
|
|
// Read ExHeader...
|
|
|
|
|
|
|
|
|
|
|
|
file.ReadBytes(&exheader_header, sizeof(ExHeader_Header));
|
|
|
|
file->ReadBytes(&exheader_header, sizeof(ExHeader_Header));
|
|
|
|
|
|
|
|
|
|
|
|
is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
|
|
|
|
is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
|
|
|
|
entry_point = exheader_header.codeset_info.text.address;
|
|
|
|
entry_point = exheader_header.codeset_info.text.address;
|
|
|
@ -213,18 +198,14 @@ ResultStatus AppLoader_NCCH::Load() {
|
|
|
|
LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
|
|
|
|
LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
|
|
|
|
LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
|
|
|
|
LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
|
|
|
|
|
|
|
|
|
|
|
|
file.Seek(exefs_offset + ncch_offset, 0);
|
|
|
|
file->Seek(exefs_offset + ncch_offset, 0);
|
|
|
|
file.ReadBytes(&exefs_header, sizeof(ExeFs_Header));
|
|
|
|
file->ReadBytes(&exefs_header, sizeof(ExeFs_Header));
|
|
|
|
|
|
|
|
|
|
|
|
LoadExec(); // Load the executable into memory for booting
|
|
|
|
LoadExec(); // Load the executable into memory for booting
|
|
|
|
|
|
|
|
|
|
|
|
is_loaded = true; // Set state to loaded
|
|
|
|
is_loaded = true; // Set state to loaded
|
|
|
|
|
|
|
|
|
|
|
|
return ResultStatus::Success;
|
|
|
|
return ResultStatus::Success;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ResultStatus::Error;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const {
|
|
|
|
ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) const {
|
|
|
@ -244,8 +225,9 @@ ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) const {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
|
|
|
|
ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
|
|
|
|
FileUtil::IOFile file(filename, "rb");
|
|
|
|
if (!file->IsOpen())
|
|
|
|
if (file.IsOpen()) {
|
|
|
|
return ResultStatus::Error;
|
|
|
|
|
|
|
|
|
|
|
|
// Check if the NCCH has a RomFS...
|
|
|
|
// Check if the NCCH has a RomFS...
|
|
|
|
if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
|
|
|
|
if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) {
|
|
|
|
u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
|
|
|
|
u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000;
|
|
|
@ -256,17 +238,13 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::vector<u8>& buffer) const {
|
|
|
|
|
|
|
|
|
|
|
|
buffer.resize(romfs_size);
|
|
|
|
buffer.resize(romfs_size);
|
|
|
|
|
|
|
|
|
|
|
|
file.Seek(romfs_offset, 0);
|
|
|
|
file->Seek(romfs_offset, 0);
|
|
|
|
file.ReadBytes(&buffer[0], romfs_size);
|
|
|
|
file->ReadBytes(&buffer[0], romfs_size);
|
|
|
|
|
|
|
|
|
|
|
|
return ResultStatus::Success;
|
|
|
|
return ResultStatus::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG_DEBUG(Loader, "NCCH has no RomFS");
|
|
|
|
LOG_DEBUG(Loader, "NCCH has no RomFS");
|
|
|
|
return ResultStatus::ErrorNotUsed;
|
|
|
|
return ResultStatus::ErrorNotUsed;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
LOG_ERROR(Loader, "Unable to read file %s!", filename.c_str());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ResultStatus::Error;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
u64 AppLoader_NCCH::GetProgramId() const {
|
|
|
|
u64 AppLoader_NCCH::GetProgramId() const {
|
|
|
|