From e8f641a52de33b33e0d6c73ee7ca07f06dcd8aeb Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 23 Jul 2018 12:33:24 -0400 Subject: [PATCH] NRO Assets and NACP file format Cleanup Review fixes --- src/core/CMakeLists.txt | 2 + src/core/file_sys/control_metadata.cpp | 42 +++++++++++++ src/core/file_sys/control_metadata.h | 81 ++++++++++++++++++++++++++ src/core/loader/nro.cpp | 79 ++++++++++++++++++++++++- src/core/loader/nro.h | 12 ++++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/core/file_sys/control_metadata.cpp create mode 100644 src/core/file_sys/control_metadata.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 27a5de7fd0..fd75854b05 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -12,6 +12,8 @@ add_library(core STATIC core_timing.h file_sys/content_archive.cpp file_sys/content_archive.h + file_sys/control_metadata.cpp + file_sys/control_metadata.h file_sys/directory.h file_sys/errors.h file_sys/mode.h diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp new file mode 100644 index 0000000000..3ddc9f162d --- /dev/null +++ b/src/core/file_sys/control_metadata.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/string_util.h" +#include "common/swap.h" +#include "core/file_sys/control_metadata.h" + +namespace FileSys { + +std::string LanguageEntry::GetApplicationName() const { + return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); +} + +std::string LanguageEntry::GetDeveloperName() const { + return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); +} + +NACP::NACP(VirtualFile file_) : file(std::move(file_)), raw(std::make_unique()) { + file->ReadObject(raw.get()); +} + +const LanguageEntry& NACP::GetLanguageEntry(Language language) const { + return raw->language_entries.at(static_cast(language)); +} + +std::string NACP::GetApplicationName(Language language) const { + return GetLanguageEntry(language).GetApplicationName(); +} + +std::string NACP::GetDeveloperName(Language language) const { + return GetLanguageEntry(language).GetDeveloperName(); +} + +u64 NACP::GetTitleId() const { + return raw->title_id; +} + +std::string NACP::GetVersionString() const { + return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); +} +} // namespace FileSys diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h new file mode 100644 index 0000000000..cc3b745f7e --- /dev/null +++ b/src/core/file_sys/control_metadata.h @@ -0,0 +1,81 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "common/common_funcs.h" +#include "core/file_sys/vfs.h" + +namespace FileSys { + +// A localized entry containing strings within the NACP. +// One for each language of type Language. +struct LanguageEntry { + std::array application_name; + std::array developer_name; + + std::string GetApplicationName() const; + std::string GetDeveloperName() const; +}; +static_assert(sizeof(LanguageEntry) == 0x300, "LanguageEntry has incorrect size."); + +// The raw file format of a NACP file. +struct RawNACP { + std::array language_entries; + INSERT_PADDING_BYTES(0x38); + u64_le title_id; + INSERT_PADDING_BYTES(0x20); + std::array version_string; + u64_le dlc_base_title_id; + u64_le title_id_2; + INSERT_PADDING_BYTES(0x28); + u64_le product_code; + u64_le title_id_3; + std::array title_id_array; + INSERT_PADDING_BYTES(0x8); + u64_le title_id_update; + std::array bcat_passphrase; + INSERT_PADDING_BYTES(0xEC0); +}; +static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size."); + +// A language on the NX. These are for names and icons. +enum class Language : u8 { + AmericanEnglish = 0, + BritishEnglish = 1, + Japanese = 2, + French = 3, + German = 4, + LatinAmericanSpanish = 5, + Spanish = 6, + Italian = 7, + Dutch = 8, + CanadianFrench = 9, + Portugese = 10, + Russian = 11, + Korean = 12, + Taiwanese = 13, + Chinese = 14, +}; + +// A class representing the format used by NX metadata files, typically named Control.nacp. +// These store application name, dev name, title id, and other miscellaneous data. +class NACP { +public: + explicit NACP(VirtualFile file); + const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const; + std::string GetApplicationName(Language language = Language::AmericanEnglish) const; + std::string GetDeveloperName(Language language = Language::AmericanEnglish) const; + u64 GetTitleId() const; + std::string GetVersionString() const; + +private: + VirtualFile file; + std::unique_ptr raw; +}; + +} // namespace FileSys diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index c020399f27..44158655cb 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -10,6 +10,8 @@ #include "common/logging/log.h" #include "common/swap.h" #include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/vfs_offset.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" @@ -49,7 +51,55 @@ struct ModHeader { }; static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); -AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} +struct AssetSection { + u64_le offset; + u64_le size; +}; +static_assert(sizeof(AssetSection) == 0x10, "AssetSection has incorrect size."); + +struct AssetHeader { + u32_le magic; + u32_le format_version; + AssetSection icon; + AssetSection nacp; + AssetSection romfs; +}; +static_assert(sizeof(AssetHeader) == 0x38, "AssetHeader has incorrect size."); + +AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) { + NroHeader nro_header{}; + if (file->ReadObject(&nro_header) != sizeof(NroHeader)) + return; + + if (file->GetSize() >= nro_header.file_size + sizeof(AssetHeader)) { + u64 offset = nro_header.file_size; + AssetHeader asset_header{}; + if (file->ReadObject(&asset_header, offset) != sizeof(AssetHeader)) + return; + + if (asset_header.format_version != 0) + LOG_WARNING(Loader, + "NRO Asset Header has format {}, currently supported format is 0. If " + "strange glitches occur with metadata, check NRO assets.", + asset_header.format_version); + if (asset_header.magic != Common::MakeMagic('A', 'S', 'E', 'T')) + return; + + if (asset_header.nacp.size > 0) { + nacp = std::make_unique(std::make_shared( + file, asset_header.nacp.size, offset + asset_header.nacp.offset, "Control.nacp")); + } + + if (asset_header.romfs.size > 0) { + romfs = std::make_shared( + file, asset_header.romfs.size, offset + asset_header.romfs.offset, "game.romfs"); + } + + if (asset_header.icon.size > 0) { + icon_data = file->ReadBytes(asset_header.icon.size, offset + asset_header.icon.offset); + } + } +} FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) { // Read NSO header @@ -136,4 +186,31 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr& process) { return ResultStatus::Success; } +ResultStatus AppLoader_NRO::ReadIcon(std::vector& buffer) { + if (icon_data.empty()) + return ResultStatus::ErrorNotUsed; + buffer = icon_data; + return ResultStatus::Success; +} + +ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) { + if (nacp == nullptr) + return ResultStatus::ErrorNotUsed; + out_program_id = nacp->GetTitleId(); + return ResultStatus::Success; +} + +ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) { + if (romfs == nullptr) + return ResultStatus::ErrorNotUsed; + dir = romfs; + return ResultStatus::Success; +} + +ResultStatus AppLoader_NRO::ReadTitle(std::string& title) { + if (nacp == nullptr) + return ResultStatus::ErrorNotUsed; + title = nacp->GetApplicationName(); + return ResultStatus::Success; +} } // namespace Loader diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 2c03d06bb1..5f3fc40d26 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -6,12 +6,15 @@ #include #include "common/common_types.h" +#include "core/file_sys/control_metadata.h" #include "core/hle/kernel/kernel.h" #include "core/loader/linker.h" #include "core/loader/loader.h" namespace Loader { +struct AssetHeader; + /// Loads an NRO file class AppLoader_NRO final : public AppLoader, Linker { public: @@ -30,8 +33,17 @@ public: ResultStatus Load(Kernel::SharedPtr& process) override; + ResultStatus ReadIcon(std::vector& buffer) override; + ResultStatus ReadProgramId(u64& out_program_id) override; + ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + ResultStatus ReadTitle(std::string& title) override; + private: bool LoadNro(FileSys::VirtualFile file, VAddr load_base); + + std::vector icon_data; + std::unique_ptr nacp; + FileSys::VirtualFile romfs; }; } // namespace Loader