From 421c3e831ac76dbcd9ab0e1c3302572b5908caef Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Wed, 5 Jun 2019 00:18:25 -0400 Subject: file_sys: Add classes to parse KIP1 and INI1 files --- src/core/file_sys/kernel_executable.cpp | 229 ++++++++++++++++++++++++++++++++ src/core/file_sys/kernel_executable.h | 99 ++++++++++++++ 2 files changed, 328 insertions(+) create mode 100644 src/core/file_sys/kernel_executable.cpp create mode 100644 src/core/file_sys/kernel_executable.h (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp new file mode 100644 index 000000000..0ddb9a60b --- /dev/null +++ b/src/core/file_sys/kernel_executable.cpp @@ -0,0 +1,229 @@ +// 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& 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 & 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]; + } + + control <<= 1; + if (out_index == 0) + return true; + } + } + + 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(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 KIP::GetSectionDecompressed(u8 index) const { + return decompressed_sections[index]; +} + +bool KIP::Is64Bit() const { + return header.flags & 0x8; +} + +bool KIP::Is39BitAddressSpace() const { + return header.flags & 0x10; +} + +bool KIP::IsService() const { + return header.flags & 0x20; +} + +std::vector KIP::GetKernelCapabilities() const { + return std::vector(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& KIP::GetTextSection() const { + return decompressed_sections[0]; +} + +const std::vector& KIP::GetRODataSection() const { + return decompressed_sections[1]; +} + +const std::vector& 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(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& 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 name; + u64_le title_id; + u32_le process_category; + u8 main_thread_priority; + u8 default_core; + INSERT_PADDING_BYTES(1); + u8 flags; + std::array sections; + std::array 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 GetSectionDecompressed(u8 index) const; + + // Executable Flags + bool Is64Bit() const; + bool Is39BitAddressSpace() const; + bool IsService() const; + + std::vector GetKernelCapabilities() const; + + s32 GetMainThreadPriority() const; + u32 GetMainThreadStackSize() const; + u32 GetMainThreadCpuCore() const; + + const std::vector& GetTextSection() const; + const std::vector& GetRODataSection() const; + const std::vector& 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, 6> decompressed_sections; +}; + +class INI { +public: + explicit INI(const VirtualFile& file); + + Loader::ResultStatus GetStatus() const; + + const std::vector& GetKIPs() const; + +private: + Loader::ResultStatus status; + + INIHeader header{}; + std::vector kips; +}; + +} // namespace FileSys -- cgit v1.2.3 From b550a01f743c6324ce3174093d1314eaf2c8ef54 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Wed, 5 Jun 2019 00:20:26 -0400 Subject: program_metadata: Add function to load meta from raw parameters Needed for KIP loading as KIPs do not have an NPDM but do have the essential parts of the data within. --- src/core/file_sys/program_metadata.cpp | 15 +++++++++++++++ src/core/file_sys/program_metadata.h | 5 +++++ 2 files changed, 20 insertions(+) (limited to 'src/core/file_sys') 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; -- cgit v1.2.3 From c417b4fe28861062e799ddf5d010e1c4a1abe01e Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Wed, 5 Jun 2019 00:33:00 -0400 Subject: game_list: Accept *.kip as a file extension of executables --- src/core/file_sys/kernel_executable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp index 0ddb9a60b..45cbde4c9 100644 --- a/src/core/file_sys/kernel_executable.cpp +++ b/src/core/file_sys/kernel_executable.cpp @@ -144,7 +144,7 @@ bool KIP::IsService() const { } std::vector KIP::GetKernelCapabilities() const { - return std::vector(header.capabilities.begin(), header.capabilities.end()); + return std::vector(header.capabilities.begin(), header.capabilities.end()); } s32 KIP::GetMainThreadPriority() const { -- cgit v1.2.3 From 9db119f8a2e5a4d877f00b9efb40e4a109c95ef7 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 6 Jun 2019 19:20:15 -0400 Subject: kernel_executable: Optimize BLZ decompression --- src/core/file_sys/kernel_executable.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/core/file_sys') diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp index 45cbde4c9..371300684 100644 --- a/src/core/file_sys/kernel_executable.cpp +++ b/src/core/file_sys/kernel_executable.cpp @@ -34,7 +34,7 @@ bool DecompressBLZ(std::vector& data) { --index; auto control = data[index + start_offset]; for (size_t i = 0; i < 8; ++i) { - if ((control & 0x80) > 0) { + if (((control << i) & 0x80) > 0) { if (index < 2) { return false; } @@ -70,9 +70,8 @@ bool DecompressBLZ(std::vector& data) { data[out_index + start_offset] = data[index + start_offset]; } - control <<= 1; if (out_index == 0) - return true; + break; } } @@ -132,15 +131,15 @@ std::vector KIP::GetSectionDecompressed(u8 index) const { } bool KIP::Is64Bit() const { - return header.flags & 0x8; + return (header.flags & 0x8) != 0; } bool KIP::Is39BitAddressSpace() const { - return header.flags & 0x10; + return (header.flags & 0x10) != 0; } bool KIP::IsService() const { - return header.flags & 0x20; + return (header.flags & 0x20) != 0; } std::vector KIP::GetKernelCapabilities() const { -- cgit v1.2.3