From 8b4d0c5d8da1260c06e5edc6bf3afc116c36ea2c Mon Sep 17 00:00:00 2001 From: noah the goodra Date: Wed, 15 Mar 2017 15:24:22 -0500 Subject: [PATCH] implemented basic cia loader --- src/citra_qt/game_list.cpp | 2 +- src/core/loader/loader.cpp | 1 + src/core/loader/ncch.cpp | 52 ++++++++++++++++++++++++++++++++++++++ src/core/loader/ncch.h | 13 +++++++++- 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index a9ec9e830..96da4579f 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -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()); diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index be719d74c..382f5f127 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -113,6 +113,7 @@ static std::unique_ptr GetFileLoader(FileUtil::IOFile&& file, FileTyp // NCCH/NCSD container formats. case FileType::CXI: case FileType::CCI: + case FileType::CIA: return std::make_unique(std::move(file), filepath); default: diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 1a4e3efa8..270182a61 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -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(&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(&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; diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 4ef95b5c6..3b17080c6 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -5,10 +5,22 @@ #pragma once #include +#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