implemented basic cia loader

This commit is contained in:
noah the goodra 2017-03-15 15:24:22 -05:00
parent 26979cd6ef
commit 8b4d0c5d8d
4 changed files with 66 additions and 2 deletions

View File

@ -141,7 +141,7 @@ void GameList::LoadInterfaceLayout() {
}
const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf",
"cci", "cxi", "app"};
"cci", "cxi", "app", "cia"};
static bool HasSupportedFileExtension(const std::string& file_name) {
QFileInfo file = QFileInfo(file_name.c_str());

View File

@ -113,6 +113,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp
// NCCH/NCSD container formats.
case FileType::CXI:
case FileType::CCI:
case FileType::CIA:
return std::make_unique<AppLoader_NCCH>(std::move(file), filepath);
default:

View File

@ -26,6 +26,44 @@ namespace Loader {
static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
static u32 FindNCCHOffsetInCIA(FileUtil::IOFile& file) {
// Reset read pointer in case this file has been read before.
file.Seek(0, SEEK_SET);
CIAHeader cia_header;
file.ReadBytes(&cia_header, sizeof(CIAHeader));
u32 certs_offset = Common::AlignUp(cia_header.header_size, 64);
u32 tik_offset = Common::AlignUp(certs_offset + cia_header.cert_size, 64);
u32 tmd_offset = Common::AlignUp(tik_offset + cia_header.ticket_size, 64);
u32 content_offset = Common::AlignUp(tmd_offset + cia_header.tmd_size, 64);
return content_offset;
}
static bool IsValidCia(FileUtil::IOFile& file) {
// Reset read pointer in case this file has been read before.
file.Seek(0, SEEK_SET);
bool is_valid = false;
u32 magic;
u32 offset = FindNCCHOffsetInCIA(file);
file.Seek(offset, SEEK_SET);
file.ReadArray<u32>(&magic, 1);
if (MakeMagic('N', 'C', 'C', 'H') != magic) {
// this additional if check was made to account for the possible
// padding which always seems to exist as 256 extra bytes
// TODO: see if the padding can be other sizes as well as 256
file.Seek(offset + 256, SEEK_SET);
file.ReadArray<u32>(&magic, 1);
if (MakeMagic('N', 'C', 'C', 'H') == magic) {
is_valid = true;
}
} else {
is_valid = true;
}
return is_valid;
}
/**
* Get the decompressed size of an LZSS compressed ExeFS file
* @param buffer Buffer of compressed file
@ -117,6 +155,9 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
if (MakeMagic('N', 'C', 'C', 'H') == magic)
return FileType::CXI;
if (IsValidCia(file))
return FileType::CIA;
return FileType::Error;
}
@ -260,6 +301,17 @@ ResultStatus AppLoader_NCCH::LoadExeFS() {
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
}
if (IsValidCia(file)) {
LOG_DEBUG(Loader, "Loading CIA");
ncch_offset = FindNCCHOffsetInCIA(file);
file.Seek(ncch_offset, SEEK_SET);
file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
// the padding check
if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) {
ncch_offset += 256;
}
}
// Verify we are loading the correct file type...
if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
return ResultStatus::ErrorInvalidFormat;

View File

@ -5,10 +5,22 @@
#pragma once
#include <memory>
#include "common/alignment.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/loader/loader.h"
struct CIAHeader {
u32_le header_size;
u8 type[2];
u8 version[2];
u32_le cert_size;
u32_le ticket_size;
u32_le tmd_size;
u32_le meta_size;
u64_le content_size;
u8 content_index[0x2000];
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// NCCH header (Note: "NCCH" appears to be a publicly unknown acronym)
@ -251,5 +263,4 @@ private:
std::string filepath;
};
} // namespace Loader