Skip to content
Snippets Groups Projects
Commit e8f641a5 authored by Zach Hilman's avatar Zach Hilman
Browse files

NRO Assets and NACP file format

Cleanup

Review fixes
parent e85a528b
No related branches found
No related tags found
No related merge requests found
......@@ -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
......
// 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
// 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment