summaryrefslogtreecommitdiffstats
path: root/src/core/file_sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/kernel_executable.cpp228
-rw-r--r--src/core/file_sys/kernel_executable.h99
-rw-r--r--src/core/file_sys/nca_metadata.h6
-rw-r--r--src/core/file_sys/patch_manager.cpp10
-rw-r--r--src/core/file_sys/patch_manager.h9
-rw-r--r--src/core/file_sys/program_metadata.cpp15
-rw-r--r--src/core/file_sys/program_metadata.h5
-rw-r--r--src/core/file_sys/registered_cache.cpp23
-rw-r--r--src/core/file_sys/registered_cache.h3
-rw-r--r--src/core/file_sys/submission_package.cpp11
10 files changed, 397 insertions, 12 deletions
diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
new file mode 100644
index 000000000..371300684
--- /dev/null
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -0,0 +1,228 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/string_util.h"
+#include "core/file_sys/kernel_executable.h"
+#include "core/file_sys/vfs_offset.h"
+
+namespace FileSys {
+
+constexpr u32 INI_MAX_KIPS = 0x50;
+
+namespace {
+bool DecompressBLZ(std::vector<u8>& data) {
+ if (data.size() < 0xC)
+ return {};
+
+ const auto data_size = data.size() - 0xC;
+
+ u32 compressed_size{};
+ u32 init_index{};
+ u32 additional_size{};
+ std::memcpy(&compressed_size, data.data() + data_size, sizeof(u32));
+ std::memcpy(&init_index, data.data() + data_size + 0x4, sizeof(u32));
+ std::memcpy(&additional_size, data.data() + data_size + 0x8, sizeof(u32));
+
+ const auto start_offset = data.size() - compressed_size;
+ data.resize(compressed_size + additional_size + start_offset);
+
+ std::size_t index = compressed_size - init_index;
+ std::size_t out_index = compressed_size + additional_size;
+
+ while (out_index > 0) {
+ --index;
+ auto control = data[index + start_offset];
+ for (size_t i = 0; i < 8; ++i) {
+ if (((control << i) & 0x80) > 0) {
+ if (index < 2) {
+ return false;
+ }
+ index -= 2;
+ std::size_t segment_offset =
+ data[index + start_offset] | data[index + start_offset + 1] << 8;
+ std::size_t segment_size = ((segment_offset >> 12) & 0xF) + 3;
+ segment_offset &= 0xFFF;
+ segment_offset += 3;
+
+ if (out_index < segment_size)
+ segment_size = out_index;
+
+ if (out_index < segment_size) {
+ return false;
+ }
+
+ out_index -= segment_size;
+
+ for (size_t j = 0; j < segment_size; ++j) {
+ if (out_index + j + segment_offset + start_offset >= data.size()) {
+ return false;
+ }
+ data[out_index + j + start_offset] =
+ data[out_index + j + segment_offset + start_offset];
+ }
+ } else {
+ if (out_index < 1) {
+ return false;
+ }
+ --out_index;
+ --index;
+ data[out_index + start_offset] = data[index + start_offset];
+ }
+
+ if (out_index == 0)
+ break;
+ }
+ }
+
+ return true;
+}
+} // Anonymous namespace
+
+KIP::KIP(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
+ if (file == nullptr) {
+ status = Loader::ResultStatus::ErrorNullFile;
+ return;
+ }
+
+ if (file->GetSize() < sizeof(KIPHeader) || file->ReadObject(&header) != sizeof(KIPHeader)) {
+ status = Loader::ResultStatus::ErrorBadKIPHeader;
+ return;
+ }
+
+ if (header.magic != Common::MakeMagic('K', 'I', 'P', '1')) {
+ status = Loader::ResultStatus::ErrorBadKIPHeader;
+ return;
+ }
+
+ u64 offset = sizeof(KIPHeader);
+ for (std::size_t i = 0; i < header.sections.size(); ++i) {
+ auto compressed = file->ReadBytes(header.sections[i].compressed_size, offset);
+ offset += header.sections[i].compressed_size;
+
+ if (header.sections[i].compressed_size == 0 && header.sections[i].decompressed_size != 0) {
+ decompressed_sections[i] = std::vector<u8>(header.sections[i].decompressed_size);
+ } else if (header.sections[i].compressed_size == header.sections[i].decompressed_size) {
+ decompressed_sections[i] = std::move(compressed);
+ } else {
+ decompressed_sections[i] = compressed;
+ if (!DecompressBLZ(decompressed_sections[i])) {
+ status = Loader::ResultStatus::ErrorBLZDecompressionFailed;
+ return;
+ }
+ }
+ }
+}
+
+Loader::ResultStatus KIP::GetStatus() const {
+ return status;
+}
+
+std::string KIP::GetName() const {
+ return Common::StringFromFixedZeroTerminatedBuffer(header.name.data(), header.name.size());
+}
+
+u64 KIP::GetTitleID() const {
+ return header.title_id;
+}
+
+std::vector<u8> KIP::GetSectionDecompressed(u8 index) const {
+ return decompressed_sections[index];
+}
+
+bool KIP::Is64Bit() const {
+ return (header.flags & 0x8) != 0;
+}
+
+bool KIP::Is39BitAddressSpace() const {
+ return (header.flags & 0x10) != 0;
+}
+
+bool KIP::IsService() const {
+ return (header.flags & 0x20) != 0;
+}
+
+std::vector<u32> KIP::GetKernelCapabilities() const {
+ return std::vector<u32>(header.capabilities.begin(), header.capabilities.end());
+}
+
+s32 KIP::GetMainThreadPriority() const {
+ return header.main_thread_priority;
+}
+
+u32 KIP::GetMainThreadStackSize() const {
+ return header.sections[1].attribute;
+}
+
+u32 KIP::GetMainThreadCpuCore() const {
+ return header.default_core;
+}
+
+const std::vector<u8>& KIP::GetTextSection() const {
+ return decompressed_sections[0];
+}
+
+const std::vector<u8>& KIP::GetRODataSection() const {
+ return decompressed_sections[1];
+}
+
+const std::vector<u8>& KIP::GetDataSection() const {
+ return decompressed_sections[2];
+}
+
+u32 KIP::GetTextOffset() const {
+ return header.sections[0].offset;
+}
+
+u32 KIP::GetRODataOffset() const {
+ return header.sections[1].offset;
+}
+
+u32 KIP::GetDataOffset() const {
+ return header.sections[2].offset;
+}
+
+u32 KIP::GetBSSSize() const {
+ return header.sections[3].decompressed_size;
+}
+
+u32 KIP::GetBSSOffset() const {
+ return header.sections[3].offset;
+}
+
+INI::INI(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
+ if (file->GetSize() < sizeof(INIHeader) || file->ReadObject(&header) != sizeof(INIHeader)) {
+ status = Loader::ResultStatus::ErrorBadINIHeader;
+ return;
+ }
+
+ if (header.magic != Common::MakeMagic('I', 'N', 'I', '1')) {
+ status = Loader::ResultStatus::ErrorBadINIHeader;
+ return;
+ }
+
+ if (header.kip_count > INI_MAX_KIPS) {
+ status = Loader::ResultStatus::ErrorINITooManyKIPs;
+ return;
+ }
+
+ u64 offset = sizeof(INIHeader);
+ for (std::size_t i = 0; i < header.kip_count; ++i) {
+ const auto kip_file =
+ std::make_shared<OffsetVfsFile>(file, file->GetSize() - offset, offset);
+ KIP kip(kip_file);
+ if (kip.GetStatus() == Loader::ResultStatus::Success) {
+ kips.push_back(std::move(kip));
+ }
+ }
+}
+
+Loader::ResultStatus INI::GetStatus() const {
+ return status;
+}
+
+const std::vector<KIP>& INI::GetKIPs() const {
+ return kips;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h
new file mode 100644
index 000000000..324a57384
--- /dev/null
+++ b/src/core/file_sys/kernel_executable.h
@@ -0,0 +1,99 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/swap.h"
+#include "core/file_sys/vfs_types.h"
+#include "core/loader/loader.h"
+
+namespace FileSys {
+
+struct KIPSectionHeader {
+ u32_le offset;
+ u32_le decompressed_size;
+ u32_le compressed_size;
+ u32_le attribute;
+};
+static_assert(sizeof(KIPSectionHeader) == 0x10, "KIPSectionHeader has incorrect size.");
+
+struct KIPHeader {
+ u32_le magic;
+ std::array<char, 0xC> name;
+ u64_le title_id;
+ u32_le process_category;
+ u8 main_thread_priority;
+ u8 default_core;
+ INSERT_PADDING_BYTES(1);
+ u8 flags;
+ std::array<KIPSectionHeader, 6> sections;
+ std::array<u32, 0x20> capabilities;
+};
+static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
+
+struct INIHeader {
+ u32_le magic;
+ u32_le size;
+ u32_le kip_count;
+ INSERT_PADDING_BYTES(0x4);
+};
+static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
+
+// Kernel Internal Process
+class KIP {
+public:
+ explicit KIP(const VirtualFile& file);
+
+ Loader::ResultStatus GetStatus() const;
+
+ std::string GetName() const;
+ u64 GetTitleID() const;
+ std::vector<u8> GetSectionDecompressed(u8 index) const;
+
+ // Executable Flags
+ bool Is64Bit() const;
+ bool Is39BitAddressSpace() const;
+ bool IsService() const;
+
+ std::vector<u32> GetKernelCapabilities() const;
+
+ s32 GetMainThreadPriority() const;
+ u32 GetMainThreadStackSize() const;
+ u32 GetMainThreadCpuCore() const;
+
+ const std::vector<u8>& GetTextSection() const;
+ const std::vector<u8>& GetRODataSection() const;
+ const std::vector<u8>& GetDataSection() const;
+
+ u32 GetTextOffset() const;
+ u32 GetRODataOffset() const;
+ u32 GetDataOffset() const;
+
+ u32 GetBSSSize() const;
+ u32 GetBSSOffset() const;
+
+private:
+ Loader::ResultStatus status;
+
+ KIPHeader header{};
+ std::array<std::vector<u8>, 6> decompressed_sections;
+};
+
+class INI {
+public:
+ explicit INI(const VirtualFile& file);
+
+ Loader::ResultStatus GetStatus() const;
+
+ const std::vector<KIP>& GetKIPs() const;
+
+private:
+ Loader::ResultStatus status;
+
+ INIHeader header{};
+ std::vector<KIP> kips;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index 84d5cd1e0..1f82fff0a 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -35,9 +35,9 @@ enum class ContentRecordType : u8 {
Program = 1,
Data = 2,
Control = 3,
- Manual = 4,
- Legal = 5,
- Patch = 6,
+ HtmlDocument = 4,
+ LegalInformation = 5,
+ DeltaFragment = 6,
};
struct ContentRecord {
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index da823c37b..a8f80e2c6 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -493,6 +493,16 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
return out;
}
+std::optional<u32> PatchManager::GetGameVersion() const {
+ const auto& installed = Core::System::GetInstance().GetContentProvider();
+ const auto update_tid = GetUpdateTitleID(title_id);
+ if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
+ return installed.GetEntryVersion(update_tid);
+ }
+
+ return installed.GetEntryVersion(title_id);
+}
+
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed = Core::System::GetInstance().GetContentProvider();
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 769f8c6f0..a363c6577 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -66,8 +66,13 @@ public:
std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
VirtualFile update_raw = nullptr) const;
- // Given title_id of the program, attempts to get the control data of the update and parse it,
- // falling back to the base control data.
+ // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
+ // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
+ // std::nullopt
+ std::optional<u32> GetGameVersion() const;
+
+ // Given title_id of the program, attempts to get the control data of the update and parse
+ // it, falling back to the base control data.
std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
// Version of GetControlMetadata that takes an arbitrary NCA
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index d863253f8..eb76174c5 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -51,6 +51,21 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
return Loader::ResultStatus::Success;
}
+void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
+ u8 main_thread_prio, u8 main_thread_core,
+ u32 main_thread_stack_size, u64 title_id,
+ u64 filesystem_permissions,
+ KernelCapabilityDescriptors capabilities) {
+ npdm_header.has_64_bit_instructions.Assign(is_64_bit);
+ npdm_header.address_space_type.Assign(address_space);
+ npdm_header.main_thread_priority = main_thread_prio;
+ npdm_header.main_thread_cpu = main_thread_core;
+ npdm_header.main_stack_size = main_thread_stack_size;
+ aci_header.title_id = title_id;
+ aci_file_access.permissions = filesystem_permissions;
+ aci_kernel_capabilities = std ::move(capabilities);
+}
+
bool ProgramMetadata::Is64BitProgram() const {
return npdm_header.has_64_bit_instructions;
}
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 7de5b9cf9..43bf2820a 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -46,6 +46,11 @@ public:
Loader::ResultStatus Load(VirtualFile file);
+ // Load from parameters instead of NPDM file, used for KIP
+ void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, u8 main_thread_prio,
+ u8 main_thread_core, u32 main_thread_stack_size, u64 title_id,
+ u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
+
bool Is64BitProgram() const;
ProgramAddressSpaceType GetAddressSpaceType() const;
u8 GetMainThreadPriority() const;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 58917e094..3725b10f7 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -99,7 +99,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
return ContentRecordType::Data;
case NCAContentType::Manual:
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
- return ContentRecordType::Manual;
+ return ContentRecordType::HtmlDocument;
default:
UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
}
@@ -397,8 +397,8 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
});
if (meta_iter == ncas.end()) {
- LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and "
- "is therefore malformed. Double check your encryption keys.");
+ LOG_ERROR(Loader, "The file you are attempting to install does not have a metadata NCA and "
+ "is therefore malformed. Check your encryption keys.");
return InstallResult::ErrorMetaFailed;
}
@@ -415,6 +415,9 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
const auto cnmt_file = section0->GetFiles()[0];
const CNMT cnmt(cnmt_file);
for (const auto& record : cnmt.GetContentRecords()) {
+ // Ignore DeltaFragments, they are not useful to us
+ if (record.type == ContentRecordType::DeltaFragment)
+ continue;
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
@@ -645,6 +648,20 @@ ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnion
return out;
}
+std::optional<ContentProviderUnionSlot> ContentProviderUnion::GetSlotForEntry(
+ u64 title_id, ContentRecordType type) const {
+ const auto iter =
+ std::find_if(providers.begin(), providers.end(), [title_id, type](const auto& provider) {
+ return provider.second != nullptr && provider.second->HasEntry(title_id, type);
+ });
+
+ if (iter == providers.end()) {
+ return std::nullopt;
+ }
+
+ return iter->first;
+}
+
ManualContentProvider::~ManualContentProvider() = default;
void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index ec9052653..4398d63e1 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -199,6 +199,9 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
+ std::optional<ContentProviderUnionSlot> GetSlotForEntry(u64 title_id,
+ ContentRecordType type) const;
+
private:
std::map<ContentProviderUnionSlot, ContentProvider*> providers;
};
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index d0428a457..8b3b14e25 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -248,10 +248,13 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
if (next_file == nullptr) {
- LOG_WARNING(Service_FS,
- "NCA with ID {}.nca is listed in content metadata, but cannot "
- "be found in PFS. NSP appears to be corrupted.",
- id_string);
+ if (rec.type != ContentRecordType::DeltaFragment) {
+ LOG_WARNING(Service_FS,
+ "NCA with ID {}.nca is listed in content metadata, but cannot "
+ "be found in PFS. NSP appears to be corrupted.",
+ id_string);
+ }
+
continue;
}