Browse Source
Merge pull request #2546 from DarkLordZach/kips
Merge pull request #2546 from DarkLordZach/kips
loader, file_sys: Add support for parsing and loading KIP (Kernel Internal Process) filesnce_cpp
committed by
GitHub
11 changed files with 522 additions and 121 deletions
-
4src/core/CMakeLists.txt
-
131src/core/crypto/partition_data_manager.cpp
-
228src/core/file_sys/kernel_executable.cpp
-
99src/core/file_sys/kernel_executable.h
-
15src/core/file_sys/program_metadata.cpp
-
5src/core/file_sys/program_metadata.h
-
102src/core/loader/kip.cpp
-
35src/core/loader/kip.h
-
16src/core/loader/loader.cpp
-
5src/core/loader/loader.h
-
3src/yuzu/game_list.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
|
||||
@ -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 |
||||
@ -0,0 +1,102 @@ |
|||||
|
// Copyright 2019 yuzu emulator team
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include "core/file_sys/kernel_executable.h"
|
||||
|
#include "core/file_sys/program_metadata.h"
|
||||
|
#include "core/gdbstub/gdbstub.h"
|
||||
|
#include "core/hle/kernel/code_set.h"
|
||||
|
#include "core/hle/kernel/process.h"
|
||||
|
#include "core/loader/kip.h"
|
||||
|
|
||||
|
namespace Loader { |
||||
|
|
||||
|
namespace { |
||||
|
constexpr u32 PageAlignSize(u32 size) { |
||||
|
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
||||
|
} |
||||
|
} // Anonymous namespace
|
||||
|
|
||||
|
AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_) |
||||
|
: AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {} |
||||
|
|
||||
|
AppLoader_KIP::~AppLoader_KIP() = default; |
||||
|
|
||||
|
FileType AppLoader_KIP::IdentifyType(const FileSys::VirtualFile& file) { |
||||
|
u32_le magic{}; |
||||
|
if (file->GetSize() < sizeof(u32) || file->ReadObject(&magic) != sizeof(u32)) { |
||||
|
return FileType::Error; |
||||
|
} |
||||
|
|
||||
|
if (magic == Common::MakeMagic('K', 'I', 'P', '1')) { |
||||
|
return FileType::KIP; |
||||
|
} |
||||
|
|
||||
|
return FileType::Error; |
||||
|
} |
||||
|
|
||||
|
FileType AppLoader_KIP::GetFileType() const { |
||||
|
return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP |
||||
|
: FileType::Error; |
||||
|
} |
||||
|
|
||||
|
AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) { |
||||
|
if (is_loaded) { |
||||
|
return {ResultStatus::ErrorAlreadyLoaded, {}}; |
||||
|
} |
||||
|
|
||||
|
if (kip == nullptr) { |
||||
|
return {ResultStatus::ErrorNullFile, {}}; |
||||
|
} |
||||
|
|
||||
|
if (kip->GetStatus() != ResultStatus::Success) { |
||||
|
return {kip->GetStatus(), {}}; |
||||
|
} |
||||
|
|
||||
|
const auto get_kip_address_space_type = [](const auto& kip) { |
||||
|
return kip.Is64Bit() |
||||
|
? (kip.Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit |
||||
|
: FileSys::ProgramAddressSpaceType::Is36Bit) |
||||
|
: FileSys::ProgramAddressSpaceType::Is32Bit; |
||||
|
}; |
||||
|
|
||||
|
const auto address_space = get_kip_address_space_type(*kip); |
||||
|
|
||||
|
FileSys::ProgramMetadata metadata; |
||||
|
metadata.LoadManual(kip->Is64Bit(), address_space, kip->GetMainThreadPriority(), |
||||
|
kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(), |
||||
|
kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, kip->GetKernelCapabilities()); |
||||
|
|
||||
|
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
||||
|
Kernel::CodeSet codeset; |
||||
|
std::vector<u8> program_image; |
||||
|
|
||||
|
const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment, |
||||
|
const std::vector<u8>& data, u32 offset) { |
||||
|
segment.addr = offset; |
||||
|
segment.offset = offset; |
||||
|
segment.size = PageAlignSize(static_cast<u32>(data.size())); |
||||
|
program_image.resize(offset); |
||||
|
program_image.insert(program_image.end(), data.begin(), data.end()); |
||||
|
}; |
||||
|
|
||||
|
load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset()); |
||||
|
load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset()); |
||||
|
load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset()); |
||||
|
|
||||
|
program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize()); |
||||
|
codeset.DataSegment().size += kip->GetBSSSize(); |
||||
|
|
||||
|
GDBStub::RegisterModule(kip->GetName(), base_address, base_address + program_image.size()); |
||||
|
|
||||
|
codeset.memory = std::move(program_image); |
||||
|
process.LoadModule(std::move(codeset), base_address); |
||||
|
|
||||
|
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address); |
||||
|
|
||||
|
is_loaded = true; |
||||
|
return {ResultStatus::Success, |
||||
|
LoadParameters{kip->GetMainThreadPriority(), kip->GetMainThreadStackSize()}}; |
||||
|
} |
||||
|
|
||||
|
} // namespace Loader
|
||||
@ -0,0 +1,35 @@ |
|||||
|
// Copyright 2019 yuzu emulator team |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "core/loader/loader.h" |
||||
|
|
||||
|
namespace FileSys { |
||||
|
class KIP; |
||||
|
} |
||||
|
|
||||
|
namespace Loader { |
||||
|
|
||||
|
class AppLoader_KIP final : public AppLoader { |
||||
|
public: |
||||
|
explicit AppLoader_KIP(FileSys::VirtualFile file); |
||||
|
~AppLoader_KIP() override; |
||||
|
|
||||
|
/** |
||||
|
* Returns the type of the file |
||||
|
* @param file std::shared_ptr<VfsFile> open file |
||||
|
* @return FileType found, or FileType::Error if this loader doesn't know it |
||||
|
*/ |
||||
|
static FileType IdentifyType(const FileSys::VirtualFile& file); |
||||
|
|
||||
|
FileType GetFileType() const override; |
||||
|
|
||||
|
LoadResult Load(Kernel::Process& process) override; |
||||
|
|
||||
|
private: |
||||
|
std::unique_ptr<FileSys::KIP> kip; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Loader |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue