Merge pull request #695 from DarkLordZach/nro-asset
NRO Assets and NACP File Format
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
							
								
								
									
										42
									
								
								src/core/file_sys/control_metadata.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/core/file_sys/control_metadata.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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<RawNACP>()) { | ||||
|     file->ReadObject(raw.get()); | ||||
| } | ||||
|  | ||||
| const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | ||||
|     return raw->language_entries.at(static_cast<u8>(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 | ||||
							
								
								
									
										81
									
								
								src/core/file_sys/control_metadata.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/core/file_sys/control_metadata.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <array> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #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<char, 0x200> application_name; | ||||
|     std::array<char, 0x100> 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<LanguageEntry, 16> language_entries; | ||||
|     INSERT_PADDING_BYTES(0x38); | ||||
|     u64_le title_id; | ||||
|     INSERT_PADDING_BYTES(0x20); | ||||
|     std::array<char, 0x10> 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<u64_le, 0x7> title_id_array; | ||||
|     INSERT_PADDING_BYTES(0x8); | ||||
|     u64_le title_id_update; | ||||
|     std::array<u8, 0x40> 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<RawNACP> raw; | ||||
| }; | ||||
|  | ||||
| } // namespace FileSys | ||||
| @@ -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<FileSys::NACP>(std::make_shared<FileSys::OffsetVfsFile>( | ||||
|                 file, asset_header.nacp.size, offset + asset_header.nacp.offset, "Control.nacp")); | ||||
|         } | ||||
|  | ||||
|         if (asset_header.romfs.size > 0) { | ||||
|             romfs = std::make_shared<FileSys::OffsetVfsFile>( | ||||
|                 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<Kernel::Process>& process) { | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|  | ||||
| ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& 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 | ||||
|   | ||||
| @@ -6,12 +6,15 @@ | ||||
|  | ||||
| #include <string> | ||||
| #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<Kernel::Process>& process) override; | ||||
|  | ||||
|     ResultStatus ReadIcon(std::vector<u8>& 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<u8> icon_data; | ||||
|     std::unique_ptr<FileSys::NACP> nacp; | ||||
|     FileSys::VirtualFile romfs; | ||||
| }; | ||||
|  | ||||
| } // namespace Loader | ||||
|   | ||||
		Reference in New Issue
	
	Block a user