mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-17 17:11:23 +00:00
Partial commit: Loader
This commit is contained in:
parent
250ce63527
commit
2553e45d76
348
rpcs3/Loader/ELF.h
Normal file
348
rpcs3/Loader/ELF.h
Normal file
@ -0,0 +1,348 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../Utilities/File.h"
|
||||
|
||||
enum class elf_os : u8
|
||||
{
|
||||
none = 0,
|
||||
|
||||
lv2 = 0x66,
|
||||
};
|
||||
|
||||
enum class elf_type : u16
|
||||
{
|
||||
none = 0,
|
||||
rel = 1,
|
||||
exec = 2,
|
||||
dyn = 3,
|
||||
core = 4,
|
||||
|
||||
prx = 0xffa4,
|
||||
|
||||
psv1 = 0xfe00, // ET_SCE_EXEC
|
||||
psv2 = 0xfe04, // ET_SCE_RELEXEC (vitasdk)
|
||||
};
|
||||
|
||||
enum class elf_machine : u16
|
||||
{
|
||||
ppc64 = 0x15,
|
||||
spu = 0x17,
|
||||
arm = 0x28,
|
||||
mips = 0x08,
|
||||
};
|
||||
|
||||
template<template<typename T> class en_t, typename sz_t>
|
||||
struct elf_ehdr
|
||||
{
|
||||
nse_t<u32> e_magic;
|
||||
u8 e_class;
|
||||
u8 e_data;
|
||||
u8 e_curver;
|
||||
elf_os e_os_abi;
|
||||
u8 e_abi_ver;
|
||||
u8 e_pad[7];
|
||||
en_t<elf_type> e_type;
|
||||
en_t<elf_machine> e_machine;
|
||||
en_t<u32> e_version;
|
||||
en_t<sz_t> e_entry;
|
||||
en_t<sz_t> e_phoff;
|
||||
en_t<sz_t> e_shoff;
|
||||
en_t<u32> e_flags;
|
||||
en_t<u16> e_ehsize;
|
||||
en_t<u16> e_phentsize;
|
||||
en_t<u16> e_phnum;
|
||||
en_t<u16> e_shentsize;
|
||||
en_t<u16> e_shnum;
|
||||
en_t<u16> e_shstrndx;
|
||||
};
|
||||
|
||||
template<template<typename T> class en_t, typename sz_t>
|
||||
struct elf_phdr
|
||||
{
|
||||
static_assert(!sizeof(sz_t), "Invalid elf size type (must be u32 or u64)");
|
||||
};
|
||||
|
||||
template<template<typename T> class en_t>
|
||||
struct elf_phdr<en_t, u64>
|
||||
{
|
||||
en_t<u32> p_type;
|
||||
en_t<u32> p_flags;
|
||||
en_t<u64> p_offset;
|
||||
en_t<u64> p_vaddr;
|
||||
en_t<u64> p_paddr;
|
||||
en_t<u64> p_filesz;
|
||||
en_t<u64> p_memsz;
|
||||
en_t<u64> p_align;
|
||||
};
|
||||
|
||||
template<template<typename T> class en_t>
|
||||
struct elf_phdr<en_t, u32>
|
||||
{
|
||||
en_t<u32> p_type;
|
||||
en_t<u32> p_offset;
|
||||
en_t<u32> p_vaddr;
|
||||
en_t<u32> p_paddr;
|
||||
en_t<u32> p_filesz;
|
||||
en_t<u32> p_memsz;
|
||||
en_t<u32> p_flags;
|
||||
en_t<u32> p_align;
|
||||
};
|
||||
|
||||
template<template<typename T> class en_t, typename sz_t>
|
||||
struct elf_prog final : elf_phdr<en_t, sz_t>
|
||||
{
|
||||
std::vector<char> bin;
|
||||
|
||||
using base = elf_phdr<en_t, sz_t>;
|
||||
|
||||
elf_prog() = default;
|
||||
|
||||
elf_prog(u32 type, u32 flags, sz_t vaddr, sz_t memsz, sz_t align, std::vector<char>&& bin)
|
||||
: bin(std::move(bin))
|
||||
{
|
||||
base::p_type = type;
|
||||
base::p_flags = flags;
|
||||
base::p_vaddr = vaddr;
|
||||
base::p_memsz = memsz;
|
||||
base::p_align = align;
|
||||
base::p_filesz = static_cast<sz_t>(bin.size());
|
||||
base::p_paddr = 0;
|
||||
base::p_offset = -1;
|
||||
}
|
||||
};
|
||||
|
||||
template<template<typename T> class en_t, typename sz_t>
|
||||
struct elf_shdr
|
||||
{
|
||||
en_t<u32> sh_name;
|
||||
en_t<u32> sh_type;
|
||||
en_t<sz_t> sh_flags;
|
||||
en_t<sz_t> sh_addr;
|
||||
en_t<sz_t> sh_offset;
|
||||
en_t<sz_t> sh_size;
|
||||
en_t<u32> sh_link;
|
||||
en_t<u32> sh_info;
|
||||
en_t<sz_t> sh_addralign;
|
||||
en_t<sz_t> sh_entsize;
|
||||
};
|
||||
|
||||
// Default elf_loader::load() return type, specialized to change.
|
||||
template<typename T>
|
||||
struct elf_load_result
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
// ELF loader errors
|
||||
enum class elf_error
|
||||
{
|
||||
ok = 0,
|
||||
|
||||
stream,
|
||||
stream_header,
|
||||
stream_phdrs,
|
||||
stream_shdrs,
|
||||
stream_data,
|
||||
|
||||
header_magic,
|
||||
header_version,
|
||||
header_class,
|
||||
header_machine,
|
||||
header_endianness,
|
||||
header_type,
|
||||
header_os,
|
||||
};
|
||||
|
||||
// ELF loader error information
|
||||
template<>
|
||||
struct bijective<elf_error, const char*>
|
||||
{
|
||||
static constexpr std::pair<elf_error, const char*> map[]
|
||||
{
|
||||
{ elf_error::ok, "" },
|
||||
|
||||
{ elf_error::stream, "Invalid stream" },
|
||||
{ elf_error::stream_header, "Failed to read ELF header" },
|
||||
{ elf_error::stream_phdrs, "Failed to read ELF program headers" },
|
||||
{ elf_error::stream_shdrs, "Failed to read ELF section headers" },
|
||||
{ elf_error::stream_data, "Failed to read ELF program data" },
|
||||
|
||||
{ elf_error::header_magic, "Not an ELF" },
|
||||
{ elf_error::header_version, "Invalid or unsupported ELF format" },
|
||||
{ elf_error::header_class, "Invalid ELF class" },
|
||||
{ elf_error::header_machine, "Invalid ELF machine" },
|
||||
{ elf_error::header_endianness, "Invalid ELF data (endianness)" },
|
||||
{ elf_error::header_type, "Invalid ELF type" },
|
||||
{ elf_error::header_os, "Invalid ELF OS ABI" },
|
||||
};
|
||||
};
|
||||
|
||||
// ELF loader with specified parameters.
|
||||
// en_t: endianness (specify le_t or be_t)
|
||||
// sz_t: size (specify u32 for ELF32, u64 for ELF64)
|
||||
template<template<typename T> class en_t, typename sz_t, elf_machine Machine, elf_os OS, elf_type Type>
|
||||
class elf_loader
|
||||
{
|
||||
elf_error m_error{};
|
||||
|
||||
elf_error error(elf_error e)
|
||||
{
|
||||
return m_error = e;
|
||||
}
|
||||
|
||||
public:
|
||||
using ehdr_t = elf_ehdr<en_t, sz_t>;
|
||||
using phdr_t = elf_phdr<en_t, sz_t>;
|
||||
using shdr_t = elf_shdr<en_t, sz_t>;
|
||||
using prog_t = elf_prog<en_t, sz_t>;
|
||||
|
||||
ehdr_t header{};
|
||||
|
||||
std::vector<prog_t> progs;
|
||||
std::vector<shdr_t> shdrs;
|
||||
|
||||
public:
|
||||
elf_loader() = default;
|
||||
|
||||
elf_loader(const fs::file& stream, u64 offset = 0)
|
||||
{
|
||||
open(stream, offset);
|
||||
}
|
||||
|
||||
elf_error open(const fs::file& stream, u64 offset = 0)
|
||||
{
|
||||
// Check stream
|
||||
if (!stream)
|
||||
return error(elf_error::stream);
|
||||
|
||||
// Read ELF header
|
||||
stream.seek(offset);
|
||||
if (!stream.read(header))
|
||||
return error(elf_error::stream_header);
|
||||
|
||||
// Check magic
|
||||
if (header.e_magic != "\177ELF"_u32)
|
||||
return error(elf_error::header_magic);
|
||||
|
||||
// Check class
|
||||
if (header.e_class != (std::is_same<sz_t, u32>::value ? 1 : 2))
|
||||
return error(elf_error::header_class);
|
||||
|
||||
// Check endianness
|
||||
if (header.e_data != (std::is_same<en_t<u32>, le_t<u32>>::value ? 1 : 2))
|
||||
return error(elf_error::header_endianness);
|
||||
|
||||
// Check machine
|
||||
if (header.e_machine != Machine)
|
||||
return error(elf_error::header_machine);
|
||||
|
||||
// Check OS only if specified (hack)
|
||||
if (OS != elf_os::none && header.e_os_abi != OS)
|
||||
return error(elf_error::header_os);
|
||||
|
||||
// Check type only if specified (hack)
|
||||
if (Type != elf_type::none && header.e_type != Type)
|
||||
return error(elf_error::header_type);
|
||||
|
||||
// Check version and other params
|
||||
if (header.e_curver != 1 || header.e_version != 1 || header.e_ehsize != sizeof(ehdr_t))
|
||||
return error(elf_error::header_version);
|
||||
|
||||
if (header.e_phnum && header.e_phentsize != sizeof(phdr_t))
|
||||
return error(elf_error::header_version);
|
||||
|
||||
if (header.e_shnum && header.e_shentsize != sizeof(shdr_t))
|
||||
return error(elf_error::header_version);
|
||||
|
||||
// Load program headers
|
||||
std::vector<phdr_t> _phdrs(header.e_phnum);
|
||||
stream.seek(offset + header.e_phoff);
|
||||
if (!stream.read(_phdrs))
|
||||
return error(elf_error::stream_phdrs);
|
||||
|
||||
shdrs.resize(header.e_shnum);
|
||||
stream.seek(offset + header.e_shoff);
|
||||
if (!stream.read(shdrs))
|
||||
return error(elf_error::stream_shdrs);
|
||||
|
||||
progs.clear();
|
||||
progs.reserve(_phdrs.size());
|
||||
for (const auto& hdr : _phdrs)
|
||||
{
|
||||
progs.emplace_back();
|
||||
|
||||
static_cast<phdr_t&>(progs.back()) = hdr;
|
||||
progs.back().bin.resize(hdr.p_filesz);
|
||||
stream.seek(offset + hdr.p_offset);
|
||||
if (!stream.read(progs.back().bin))
|
||||
return error(elf_error::stream_data);
|
||||
}
|
||||
|
||||
shdrs.shrink_to_fit();
|
||||
progs.shrink_to_fit();
|
||||
|
||||
return m_error = elf_error::ok;
|
||||
}
|
||||
|
||||
void save(const fs::file& stream) const
|
||||
{
|
||||
// Write header
|
||||
ehdr_t header{};
|
||||
header.e_magic = "\177ELF"_u32;
|
||||
header.e_class = std::is_same<sz_t, u32>::value ? 1 : 2;
|
||||
header.e_data = std::is_same<en_t<u32>, le_t<u32>>::value ? 1 : 2;
|
||||
header.e_curver = 1;
|
||||
header.e_os_abi = OS != elf_os::none ? OS : this->header.e_os_abi;
|
||||
header.e_abi_ver = this->header.e_abi_ver;
|
||||
header.e_type = Type != elf_type::none ? Type : this->header.e_type;
|
||||
header.e_machine = Machine;
|
||||
header.e_version = 1;
|
||||
header.e_entry = this->header.e_entry;
|
||||
header.e_phoff = SIZE_32(ehdr_t);
|
||||
header.e_shoff = SIZE_32(ehdr_t) + SIZE_32(phdr_t) * ::size32(progs);
|
||||
header.e_flags = this->header.e_flags;
|
||||
header.e_ehsize = SIZE_32(ehdr_t);
|
||||
header.e_phentsize = SIZE_32(phdr_t);
|
||||
header.e_phnum = ::size32(progs);
|
||||
header.e_shentsize = SIZE_32(shdr_t);
|
||||
header.e_shnum = ::size32(shdrs);
|
||||
header.e_shstrndx = this->header.e_shstrndx;
|
||||
stream.write(header);
|
||||
|
||||
sz_t off = header.e_shoff + SIZE_32(shdr_t) * ::size32(shdrs);
|
||||
|
||||
for (phdr_t phdr : progs)
|
||||
{
|
||||
phdr.p_offset = std::exchange(off, off + phdr.p_filesz);
|
||||
stream.write(phdr);
|
||||
}
|
||||
|
||||
for (shdr_t shdr : shdrs)
|
||||
{
|
||||
// TODO?
|
||||
stream.write(shdr);
|
||||
}
|
||||
|
||||
// Write data
|
||||
for (const auto& prog : progs)
|
||||
{
|
||||
stream.write(prog.bin);
|
||||
}
|
||||
}
|
||||
|
||||
// Return error code
|
||||
operator elf_error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
// Format-specific loader function (must be specialized)
|
||||
typename elf_load_result<elf_loader>::type load() const;
|
||||
};
|
||||
|
||||
using ppu_exec_loader = elf_loader<be_t, u64, elf_machine::ppc64, elf_os::none, elf_type::exec>;
|
||||
using ppu_prx_loader = elf_loader<be_t, u64, elf_machine::ppc64, elf_os::lv2, elf_type::prx>;
|
||||
using spu_exec_loader = elf_loader<be_t, u32, elf_machine::spu, elf_os::none, elf_type::exec>;
|
||||
using arm_exec_loader = elf_loader<le_t, u32, elf_machine::arm, elf_os::none, elf_type::none>;
|
||||
|
||||
template<> struct elf_load_result<ppu_prx_loader> { using type = std::shared_ptr<struct lv2_prx_t>; };
|
@ -1,453 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "ELF32.h"
|
||||
#include "Emu/FS/vfsStream.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/ARMv7/ARMv7Thread.h"
|
||||
#include "Emu/ARMv7/ARMv7Decoder.h"
|
||||
#include "Emu/ARMv7/PSVFuncList.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/state.h"
|
||||
|
||||
extern void armv7_init_tls();
|
||||
|
||||
namespace loader
|
||||
{
|
||||
namespace handlers
|
||||
{
|
||||
handler::error_code elf32::init(vfsStream& stream)
|
||||
{
|
||||
m_ehdr = {};
|
||||
m_phdrs.clear();
|
||||
m_shdrs.clear();
|
||||
|
||||
error_code res = handler::init(stream);
|
||||
|
||||
if (res != ok)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
m_stream->Read(&m_ehdr, sizeof(ehdr));
|
||||
|
||||
if (!m_ehdr.check())
|
||||
{
|
||||
return bad_file;
|
||||
}
|
||||
|
||||
if (m_ehdr.data_le.e_phnum && (m_ehdr.is_le() ? m_ehdr.data_le.e_phentsize != sizeof(phdr) : m_ehdr.data_be.e_phentsize != sizeof(phdr)))
|
||||
{
|
||||
return broken_file;
|
||||
}
|
||||
|
||||
if (m_ehdr.data_le.e_shnum && (m_ehdr.is_le() ? m_ehdr.data_le.e_shentsize != sizeof(shdr) : m_ehdr.data_be.e_shentsize != sizeof(shdr)))
|
||||
{
|
||||
return broken_file;
|
||||
}
|
||||
|
||||
LOG_WARNING(LOADER, "m_ehdr.e_type = 0x%x", m_ehdr.is_le() ? m_ehdr.data_le.e_type : m_ehdr.data_be.e_type.value());
|
||||
|
||||
if (m_ehdr.data_le.e_phnum)
|
||||
{
|
||||
m_phdrs.resize(m_ehdr.is_le() ? m_ehdr.data_le.e_phnum : m_ehdr.data_be.e_phnum.value());
|
||||
m_stream->Seek(handler::get_stream_offset() + (m_ehdr.is_le() ? m_ehdr.data_le.e_phoff : m_ehdr.data_be.e_phoff.value()));
|
||||
size_t size = (m_ehdr.is_le() ? m_ehdr.data_le.e_phnum : m_ehdr.data_be.e_phnum.value()) * sizeof(phdr);
|
||||
|
||||
if (m_stream->Read(m_phdrs.data(), size) != size)
|
||||
return broken_file;
|
||||
}
|
||||
|
||||
if (m_ehdr.data_le.e_shnum)
|
||||
{
|
||||
m_shdrs.resize(m_ehdr.is_le() ? m_ehdr.data_le.e_shnum : m_ehdr.data_be.e_shnum.value());
|
||||
m_stream->Seek(handler::get_stream_offset() + (m_ehdr.is_le() ? m_ehdr.data_le.e_shoff : m_ehdr.data_be.e_shoff.value()));
|
||||
size_t size = (m_ehdr.is_le() ? m_ehdr.data_le.e_shnum : m_ehdr.data_be.e_shnum.value()) * sizeof(shdr);
|
||||
|
||||
if (m_stream->Read(m_shdrs.data(), size) != size)
|
||||
return broken_file;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
handler::error_code elf32::load()
|
||||
{
|
||||
Elf_Machine machine;
|
||||
switch (machine = (Elf_Machine)(u16)(m_ehdr.is_le() ? m_ehdr.data_le.e_machine : m_ehdr.data_be.e_machine.value()))
|
||||
{
|
||||
case MACHINE_MIPS: vm::psp::init(); break;
|
||||
case MACHINE_ARM: vm::psv::init(); break;
|
||||
case MACHINE_SPU: vm::ps3::init(); break;
|
||||
|
||||
default:
|
||||
return bad_version;
|
||||
}
|
||||
|
||||
error_code res = load_data(0);
|
||||
|
||||
if (res != ok)
|
||||
return res;
|
||||
|
||||
switch (machine)
|
||||
{
|
||||
case MACHINE_MIPS: break;
|
||||
case MACHINE_ARM:
|
||||
{
|
||||
struct psv_libc_param_t
|
||||
{
|
||||
u32 size; // 0x0000001c
|
||||
u32 unk1; // 0x00000000
|
||||
|
||||
vm::lptr<u32> sceLibcHeapSize;
|
||||
vm::lptr<u32> sceLibcHeapSizeDefault;
|
||||
vm::lptr<u32> sceLibcHeapExtendedAlloc;
|
||||
vm::lptr<u32> sceLibcHeapDelayedAlloc;
|
||||
|
||||
u32 unk2;
|
||||
u32 unk3;
|
||||
|
||||
vm::lptr<u32> __sce_libcmallocreplace;
|
||||
vm::lptr<u32> __sce_libcnewreplace;
|
||||
};
|
||||
|
||||
struct psv_process_param_t
|
||||
{
|
||||
u32 size; // 0x00000030
|
||||
u32 unk1; // 'PSP2'
|
||||
u32 unk2; // 0x00000005
|
||||
u32 unk3;
|
||||
|
||||
vm::lcptr<char> sceUserMainThreadName;
|
||||
vm::lptr<s32> sceUserMainThreadPriority;
|
||||
vm::lptr<u32> sceUserMainThreadStackSize;
|
||||
vm::lptr<u32> sceUserMainThreadAttribute;
|
||||
vm::lcptr<char> sceProcessName;
|
||||
vm::lptr<u32> sce_process_preload_disabled;
|
||||
vm::lptr<u32> sceUserMainThreadCpuAffinityMask;
|
||||
|
||||
vm::lptr<psv_libc_param_t> __sce_libcparam;
|
||||
};
|
||||
|
||||
initialize_psv_modules();
|
||||
|
||||
auto armv7_thr_stop_data = vm::ptr<u32>::make(vm::alloc(3 * 4, vm::main));
|
||||
armv7_thr_stop_data[0] = 0xf870; // HACK instruction (Thumb)
|
||||
armv7_thr_stop_data[1] = SFI_HLE_RETURN;
|
||||
Emu.SetCPUThreadStop(armv7_thr_stop_data.addr());
|
||||
|
||||
u32 entry = 0; // actual entry point (ELFs entry point is ignored)
|
||||
u32 fnid_addr = 0;
|
||||
u32 code_start = 0;
|
||||
u32 code_end = 0;
|
||||
u32 vnid_addr = 0;
|
||||
std::unordered_map<u32, u32> vnid_list;
|
||||
|
||||
vm::ptr<psv_process_param_t> proc_param = vm::null;
|
||||
|
||||
for (auto& shdr : m_shdrs)
|
||||
{
|
||||
// get secton name
|
||||
//auto name = vm::cptr<char>::make(sname_base + shdr.data_le.sh_name);
|
||||
|
||||
m_stream->Seek(handler::get_stream_offset() + m_shdrs[m_ehdr.data_le.e_shstrndx].data_le.sh_offset + shdr.data_le.sh_name);
|
||||
std::string name;
|
||||
char c;
|
||||
while (m_stream->SRead(c) && c)
|
||||
{
|
||||
name.push_back(c);
|
||||
}
|
||||
|
||||
if (!strcmp(name.c_str(), ".text"))
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".text analysis...");
|
||||
|
||||
code_start = shdr.data_le.sh_addr;
|
||||
code_end = shdr.data_le.sh_size + code_start;
|
||||
}
|
||||
else if (!strcmp(name.c_str(), ".sceExport.rodata"))
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".sceExport.rodata analysis...");
|
||||
|
||||
auto enid = vm::cptr<u32>::make(shdr.data_le.sh_addr);
|
||||
auto edata = vm::cptr<u32>::make(enid.addr() + shdr.data_le.sh_size / 2);
|
||||
|
||||
for (u32 j = 0; j < shdr.data_le.sh_size / 8; j++)
|
||||
{
|
||||
switch (const u32 nid = enid[j])
|
||||
{
|
||||
case 0x935cd196: // set entry point
|
||||
{
|
||||
entry = edata[j];
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x6c2224ba: // __sce_moduleinfo
|
||||
{
|
||||
// currently nothing, but it should theoretically be the root of analysis instead of section name comparison
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x70fba1e7: // __sce_process_param
|
||||
{
|
||||
proc_param.set(edata[j]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown export 0x%08x (addr=0x%08x)", nid, edata[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp(name.c_str(), ".sceFNID.rodata"))
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".sceFNID.rodata analysis...");
|
||||
|
||||
fnid_addr = shdr.data_le.sh_addr;
|
||||
}
|
||||
else if (!strcmp(name.c_str(), ".sceFStub.rodata"))
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".sceFStub.rodata analysis...");
|
||||
|
||||
if (!fnid_addr)
|
||||
{
|
||||
LOG_ERROR(LOADER, ".sceFNID.rodata address not found, unable to process imports");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto fnid = vm::cptr<u32>::make(fnid_addr);
|
||||
auto fstub = vm::cptr<u32>::make(shdr.data_le.sh_addr);
|
||||
|
||||
for (u32 j = 0; j < shdr.data_le.sh_size / 4; j++)
|
||||
{
|
||||
const u32 nid = fnid[j];
|
||||
const u32 addr = fstub[j];
|
||||
|
||||
u32 index;
|
||||
|
||||
if (auto func = get_psv_func_by_nid(nid, &index))
|
||||
{
|
||||
if (func->module)
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Imported function '%s' in module '%s' (nid=0x%08x, addr=0x%x)", func->name, func->module->name, nid, addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Imported function '%s' (nid=0x%08x, addr=0x%x)", func->name, nid, addr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown function 0x%08x (addr=0x%x)", nid, addr);
|
||||
|
||||
// TODO: set correct name if possible
|
||||
index = add_psv_func(psv_func(nid, 0, nullptr, "UNKNOWN", nullptr));
|
||||
}
|
||||
|
||||
vm::psv::write32(addr, 0xe0700090 | (index & 0xfff0) << 4 | (index & 0xf)); // HACK instruction (ARM)
|
||||
|
||||
code_end = std::min<u32>(addr, code_end);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(name.c_str(), ".sceVNID.rodata"))
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".sceVNID.rodata analysis...");
|
||||
|
||||
vnid_addr = shdr.data_le.sh_addr;
|
||||
}
|
||||
else if (!strcmp(name.c_str(), ".sceVStub.rodata"))
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".sceVStub.rodata analysis...");
|
||||
|
||||
if (!vnid_addr)
|
||||
{
|
||||
if (shdr.data_le.sh_size)
|
||||
{
|
||||
LOG_ERROR(LOADER, ".sceVNID.rodata address not found, unable to process imports");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto vnid = vm::cptr<u32>::make(vnid_addr);
|
||||
auto vstub = vm::cptr<u32>::make(shdr.data_le.sh_addr);
|
||||
|
||||
for (u32 j = 0; j < shdr.data_le.sh_size / 4; j++)
|
||||
{
|
||||
const u32 nid = vnid[j];
|
||||
const u32 addr = vstub[j];
|
||||
|
||||
LOG_ERROR(LOADER, "Unknown object 0x%08x (ref_addr=0x%x)", nid, addr);
|
||||
|
||||
// TODO: find imported object (vtable, typeinfo or something), assign it to vnid_list[addr]
|
||||
}
|
||||
}
|
||||
else if (!strcmp(name.c_str(), ".tbss"))
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".tbss analysis...");
|
||||
const u32 img_addr = shdr.data_le.sh_addr; // start address of TLS initialization image
|
||||
const u32 img_size = (&shdr)[1].data_le.sh_addr - img_addr; // calculate its size as the difference between sections
|
||||
const u32 tls_size = shdr.data_le.sh_size; // full size of TLS
|
||||
|
||||
LOG_WARNING(LOADER, "TLS: img_addr=0x%08x, img_size=0x%x, tls_size=0x%x", img_addr, img_size, tls_size);
|
||||
Emu.SetTLSData(img_addr, img_size, tls_size);
|
||||
}
|
||||
else if (!strcmp(name.c_str(), ".sceRefs.rodata"))
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".sceRefs.rodata analysis...");
|
||||
|
||||
u32 data = 0;
|
||||
|
||||
for (auto code = vm::cptr<u32>::make(shdr.data_le.sh_addr); code.addr() < shdr.data_le.sh_addr + shdr.data_le.sh_size; code++)
|
||||
{
|
||||
switch (*code)
|
||||
{
|
||||
case 0x000000ff: // save address for future use
|
||||
{
|
||||
data = *++code;
|
||||
break;
|
||||
}
|
||||
case 0x0000002f: // movw r*,# instruction is replaced
|
||||
{
|
||||
if (!data) // probably, imported object
|
||||
{
|
||||
auto found = vnid_list.find(code.addr());
|
||||
if (found != vnid_list.end())
|
||||
{
|
||||
data = found->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data)
|
||||
{
|
||||
LOG_ERROR(LOADER, ".sceRefs: movw writing failed (ref_addr=0x%x, addr=0x%x)", code, code[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".sceRefs: movw written at 0x%x (ref_addr=0x%x, data=0x%x)", code[1], code, data);
|
||||
}
|
||||
|
||||
const u32 addr = *++code;
|
||||
vm::psv::write16(addr + 0, vm::psv::read16(addr + 0) | (data & 0x800) >> 1 | (data & 0xf000) >> 12);
|
||||
vm::psv::write16(addr + 2, vm::psv::read16(addr + 2) | (data & 0x700) << 4 | (data & 0xff));
|
||||
break;
|
||||
}
|
||||
case 0x00000030: // movt r*,# instruction is replaced
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
LOG_ERROR(LOADER, ".sceRefs: movt writing failed (ref_addr=0x%x, addr=0x%x)", code, code[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_NOTICE(LOADER, ".sceRefs: movt written at 0x%x (ref_addr=0x%x, data=0x%x)", code[1], code, data);
|
||||
}
|
||||
|
||||
const u32 addr = *++code;
|
||||
vm::psv::write16(addr + 0, vm::psv::read16(addr + 0) | (data & 0x8000000) >> 17 | (data & 0xf0000000) >> 28);
|
||||
vm::psv::write16(addr + 2, vm::psv::read16(addr + 2) | (data & 0x7000000) >> 12 | (data & 0xff0000) >> 16);
|
||||
break;
|
||||
}
|
||||
case 0x00000000:
|
||||
{
|
||||
data = 0;
|
||||
|
||||
LOG_TRACE(LOADER, ".sceRefs: zero code found");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown code in .sceRefs section (0x%08x)", *code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_NOTICE(LOADER, "__sce_process_param(addr=0x%x) analysis...", proc_param);
|
||||
|
||||
if (proc_param->size != 0x30 || proc_param->unk1 != *(u32*)"PSP2" || proc_param->unk2 != 5)
|
||||
{
|
||||
LOG_ERROR(LOADER, "__sce_process_param: unexpected data found (size=0x%x, 0x%x, 0x%x, 0x%x)", proc_param->size, proc_param->unk1, proc_param->unk2, proc_param->unk3);
|
||||
}
|
||||
|
||||
LOG_NOTICE(LOADER, "*** &sceUserMainThreadName = 0x%x", proc_param->sceUserMainThreadName);
|
||||
LOG_NOTICE(LOADER, "*** &sceUserMainThreadPriority = 0x%x", proc_param->sceUserMainThreadPriority);
|
||||
LOG_NOTICE(LOADER, "*** &sceUserMainThreadStackSize = 0x%x", proc_param->sceUserMainThreadStackSize);
|
||||
LOG_NOTICE(LOADER, "*** &sceUserMainThreadAttribute = 0x%x", proc_param->sceUserMainThreadAttribute);
|
||||
LOG_NOTICE(LOADER, "*** &sceProcessName = 0x%x", proc_param->sceProcessName);
|
||||
LOG_NOTICE(LOADER, "*** &sce_process_preload_disabled = 0x%x", proc_param->sce_process_preload_disabled);
|
||||
LOG_NOTICE(LOADER, "*** &sceUserMainThreadCpuAffinityMask = 0x%x", proc_param->sceUserMainThreadCpuAffinityMask);
|
||||
|
||||
auto libc_param = proc_param->__sce_libcparam;
|
||||
|
||||
LOG_NOTICE(LOADER, "__sce_libcparam(addr=0x%x) analysis...", libc_param);
|
||||
|
||||
if (libc_param->size != 0x1c || libc_param->unk1)
|
||||
{
|
||||
LOG_ERROR(LOADER, "__sce_libcparam: unexpected data found (size=0x%x, 0x%x, 0x%x)", libc_param->size, libc_param->unk1, libc_param->unk2);
|
||||
}
|
||||
|
||||
LOG_NOTICE(LOADER, "*** &sceLibcHeapSize = 0x%x", libc_param->sceLibcHeapSize);
|
||||
LOG_NOTICE(LOADER, "*** &sceLibcHeapSizeDefault = 0x%x", libc_param->sceLibcHeapSizeDefault);
|
||||
LOG_NOTICE(LOADER, "*** &sceLibcHeapExtendedAlloc = 0x%x", libc_param->sceLibcHeapExtendedAlloc);
|
||||
LOG_NOTICE(LOADER, "*** &sceLibcHeapDelayedAlloc = 0x%x", libc_param->sceLibcHeapDelayedAlloc);
|
||||
|
||||
armv7_init_tls();
|
||||
armv7_decoder_initialize(code_start, code_end);
|
||||
|
||||
const std::string& thread_name = proc_param->sceUserMainThreadName ? proc_param->sceUserMainThreadName.get_ptr() : "main_thread";
|
||||
const u32 stack_size = proc_param->sceUserMainThreadStackSize ? proc_param->sceUserMainThreadStackSize->value() : 256 * 1024;
|
||||
const u32 priority = proc_param->sceUserMainThreadPriority ? proc_param->sceUserMainThreadPriority->value() : 160;
|
||||
|
||||
armv7_thread(entry, thread_name, stack_size, priority).args({ Emu.GetPath(), "-emu" }).run();
|
||||
break;
|
||||
}
|
||||
case MACHINE_SPU: spu_thread(m_ehdr.is_le() ? m_ehdr.data_le.e_entry : m_ehdr.data_be.e_entry.value(), "main_thread").args({ Emu.GetPath()/*, "-emu"*/ }).run(); break;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
handler::error_code elf32::load_data(u32 offset, bool skip_writeable)
|
||||
{
|
||||
Elf_Machine machine = (Elf_Machine)(u16)(m_ehdr.is_le() ? m_ehdr.data_le.e_machine : m_ehdr.data_be.e_machine.value());
|
||||
|
||||
for (auto &phdr : m_phdrs)
|
||||
{
|
||||
u32 memsz = m_ehdr.is_le() ? phdr.data_le.p_memsz : phdr.data_be.p_memsz.value();
|
||||
u32 filesz = m_ehdr.is_le() ? phdr.data_le.p_filesz : phdr.data_be.p_filesz.value();
|
||||
u32 vaddr = offset + (m_ehdr.is_le() ? phdr.data_le.p_vaddr : phdr.data_be.p_vaddr.value());
|
||||
u32 offset = m_ehdr.is_le() ? phdr.data_le.p_offset : phdr.data_be.p_offset.value();
|
||||
|
||||
switch (m_ehdr.is_le() ? phdr.data_le.p_type : phdr.data_be.p_type.value())
|
||||
{
|
||||
case 0x00000001: //LOAD
|
||||
if (phdr.data_le.p_memsz)
|
||||
{
|
||||
if (machine == MACHINE_ARM && !vm::falloc(vaddr, memsz, vm::main))
|
||||
{
|
||||
LOG_ERROR(LOADER, "%s(): AllocFixed(0x%llx, 0x%x) failed", __FUNCTION__, vaddr, memsz);
|
||||
|
||||
return loading_error;
|
||||
}
|
||||
|
||||
if (skip_writeable == true && (phdr.data_be.p_flags & 2/*PF_W*/) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filesz)
|
||||
{
|
||||
m_stream->Seek(handler::get_stream_offset() + offset);
|
||||
m_stream->Read(vm::base(vaddr), filesz);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
#pragma once
|
||||
#include "Loader.h"
|
||||
|
||||
struct vfsStream;
|
||||
|
||||
namespace loader
|
||||
{
|
||||
namespace handlers
|
||||
{
|
||||
class elf32 : public handler
|
||||
{
|
||||
public:
|
||||
struct ehdr
|
||||
{
|
||||
u32 e_magic;
|
||||
u8 e_class;
|
||||
u8 e_data;
|
||||
u8 e_curver;
|
||||
u8 e_os_abi;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u64 e_abi_ver;
|
||||
u16 e_type;
|
||||
u16 e_machine;
|
||||
u32 e_version;
|
||||
u32 e_entry;
|
||||
u32 e_phoff;
|
||||
u32 e_shoff;
|
||||
u32 e_flags;
|
||||
u16 e_ehsize;
|
||||
u16 e_phentsize;
|
||||
u16 e_phnum;
|
||||
u16 e_shentsize;
|
||||
u16 e_shnum;
|
||||
u16 e_shstrndx;
|
||||
} data_le;
|
||||
|
||||
struct
|
||||
{
|
||||
be_t<u64> e_abi_ver;
|
||||
be_t<u16> e_type;
|
||||
be_t<u16> e_machine;
|
||||
be_t<u32> e_version;
|
||||
be_t<u32> e_entry;
|
||||
be_t<u32> e_phoff;
|
||||
be_t<u32> e_shoff;
|
||||
be_t<u32> e_flags;
|
||||
be_t<u16> e_ehsize;
|
||||
be_t<u16> e_phentsize;
|
||||
be_t<u16> e_phnum;
|
||||
be_t<u16> e_shentsize;
|
||||
be_t<u16> e_shnum;
|
||||
be_t<u16> e_shstrndx;
|
||||
} data_be;
|
||||
};
|
||||
|
||||
bool is_le() const { return e_data == 1; }
|
||||
bool check() const { return e_magic == 0x464C457F; }
|
||||
};
|
||||
|
||||
struct shdr
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 sh_name;
|
||||
u32 sh_type;
|
||||
u32 sh_flags;
|
||||
u32 sh_addr;
|
||||
u32 sh_offset;
|
||||
u32 sh_size;
|
||||
u32 sh_link;
|
||||
u32 sh_info;
|
||||
u32 sh_addralign;
|
||||
u32 sh_entsize;
|
||||
} data_le;
|
||||
|
||||
struct
|
||||
{
|
||||
be_t<u32> sh_name;
|
||||
be_t<u32> sh_type;
|
||||
be_t<u32> sh_flags;
|
||||
be_t<u32> sh_addr;
|
||||
be_t<u32> sh_offset;
|
||||
be_t<u32> sh_size;
|
||||
be_t<u32> sh_link;
|
||||
be_t<u32> sh_info;
|
||||
be_t<u32> sh_addralign;
|
||||
be_t<u32> sh_entsize;
|
||||
} data_be;
|
||||
};
|
||||
};
|
||||
|
||||
struct phdr
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 p_type;
|
||||
u32 p_offset;
|
||||
u32 p_vaddr;
|
||||
u32 p_paddr;
|
||||
u32 p_filesz;
|
||||
u32 p_memsz;
|
||||
u32 p_flags;
|
||||
u32 p_align;
|
||||
} data_le;
|
||||
|
||||
struct
|
||||
{
|
||||
be_t<u32> p_type;
|
||||
be_t<u32> p_offset;
|
||||
be_t<u32> p_vaddr;
|
||||
be_t<u32> p_paddr;
|
||||
be_t<u32> p_filesz;
|
||||
be_t<u32> p_memsz;
|
||||
be_t<u32> p_flags;
|
||||
be_t<u32> p_align;
|
||||
} data_be;
|
||||
};
|
||||
};
|
||||
|
||||
ehdr m_ehdr;
|
||||
std::vector<phdr> m_phdrs;
|
||||
std::vector<shdr> m_shdrs;
|
||||
|
||||
error_code init(vfsStream& stream) override;
|
||||
error_code load() override;
|
||||
error_code load_data(u32 offset, bool skip_writeable = false);
|
||||
|
||||
virtual ~elf32() = default;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,737 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/FS/vfsStream.h"
|
||||
#include "Emu/FS/vfsFile.h"
|
||||
#include "Emu/FS/vfsDir.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/state.h"
|
||||
#include "Emu/SysCalls/SysCalls.h"
|
||||
#include "Emu/SysCalls/Modules.h"
|
||||
#include "Emu/SysCalls/ModuleManager.h"
|
||||
#include "Emu/SysCalls/lv2/sys_prx.h"
|
||||
#include "Emu/Cell/PPUInstrTable.h"
|
||||
#include "ELF64.h"
|
||||
|
||||
using namespace PPU_instr;
|
||||
|
||||
namespace loader
|
||||
{
|
||||
namespace handlers
|
||||
{
|
||||
handler::error_code elf64::init(vfsStream& stream)
|
||||
{
|
||||
m_ehdr = {};
|
||||
m_sprx_module_info = {};
|
||||
m_sprx_function_info = {};
|
||||
|
||||
m_phdrs.clear();
|
||||
m_shdrs.clear();
|
||||
|
||||
m_sprx_segments_info.clear();
|
||||
m_sprx_import_info.clear();
|
||||
m_sprx_export_info.clear();
|
||||
|
||||
error_code res = handler::init(stream);
|
||||
|
||||
if (res != ok)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
m_stream->Read(&m_ehdr, sizeof(ehdr));
|
||||
|
||||
if (!m_ehdr.check())
|
||||
{
|
||||
return bad_file;
|
||||
}
|
||||
|
||||
if (m_ehdr.e_phnum && m_ehdr.e_phentsize != sizeof(phdr))
|
||||
{
|
||||
return broken_file;
|
||||
}
|
||||
|
||||
if (m_ehdr.e_shnum && m_ehdr.e_shentsize != sizeof(shdr))
|
||||
{
|
||||
return broken_file;
|
||||
}
|
||||
|
||||
if (m_ehdr.e_machine != MACHINE_PPC64 && m_ehdr.e_machine != MACHINE_SPU)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown elf64 machine type: 0x%x", m_ehdr.e_machine);
|
||||
return bad_version;
|
||||
}
|
||||
|
||||
if (m_ehdr.e_phnum)
|
||||
{
|
||||
m_phdrs.resize(m_ehdr.e_phnum);
|
||||
m_stream->Seek(handler::get_stream_offset() + m_ehdr.e_phoff);
|
||||
if (m_stream->Read(m_phdrs.data(), m_ehdr.e_phnum * sizeof(phdr)) != m_ehdr.e_phnum * sizeof(phdr))
|
||||
return broken_file;
|
||||
}
|
||||
|
||||
if (m_ehdr.e_shnum)
|
||||
{
|
||||
m_shdrs.resize(m_ehdr.e_shnum);
|
||||
m_stream->Seek(handler::get_stream_offset() + m_ehdr.e_shoff);
|
||||
if (m_stream->Read(m_shdrs.data(), m_ehdr.e_shnum * sizeof(shdr)) != m_ehdr.e_shnum * sizeof(shdr))
|
||||
return broken_file;
|
||||
}
|
||||
|
||||
if (is_sprx())
|
||||
{
|
||||
m_stream->Seek(handler::get_stream_offset() + m_phdrs[0].p_paddr.addr());
|
||||
m_stream->Read(&m_sprx_module_info, sizeof(sprx_module_info));
|
||||
|
||||
//m_stream->Seek(handler::get_stream_offset() + m_phdrs[1].p_vaddr.addr());
|
||||
//m_stream->Read(&m_sprx_function_info, sizeof(sprx_function_info));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
handler::error_code elf64::load_sprx(sprx_info& info)
|
||||
{
|
||||
for (auto &phdr : m_phdrs)
|
||||
{
|
||||
switch ((u32)phdr.p_type)
|
||||
{
|
||||
case 0x1: //load
|
||||
{
|
||||
if (phdr.p_memsz)
|
||||
{
|
||||
sprx_segment_info segment;
|
||||
segment.size = phdr.p_memsz;
|
||||
segment.size_file = phdr.p_filesz;
|
||||
|
||||
segment.begin.set(vm::alloc(segment.size, vm::main));
|
||||
|
||||
if (!segment.begin)
|
||||
{
|
||||
LOG_ERROR(LOADER, "%s() sprx: vm::alloc(0x%x) failed", __FUNCTION__, segment.size);
|
||||
|
||||
return loading_error;
|
||||
}
|
||||
|
||||
segment.initial_addr = phdr.p_vaddr;
|
||||
LOG_WARNING(LOADER, "segment addr=0x%x, initial addr = 0x%x", segment.begin.addr(), segment.initial_addr.addr());
|
||||
|
||||
if (phdr.p_filesz)
|
||||
{
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset);
|
||||
m_stream->Read(segment.begin.get_ptr(), phdr.p_filesz);
|
||||
}
|
||||
|
||||
if (phdr.p_paddr)
|
||||
{
|
||||
sys_prx_module_info_t module_info;
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_paddr.addr());
|
||||
m_stream->Read(&module_info, sizeof(module_info));
|
||||
|
||||
info.name = std::string(module_info.name, 28);
|
||||
info.rtoc = module_info.toc + segment.begin.addr();
|
||||
|
||||
LOG_WARNING(LOADER, "%s (rtoc=0x%x):", info.name, info.rtoc);
|
||||
|
||||
sys_prx_library_info_t lib;
|
||||
for (u32 e = module_info.exports_start.addr();
|
||||
e < module_info.exports_end.addr();
|
||||
e += lib.size ? lib.size : sizeof(sys_prx_library_info_t))
|
||||
{
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset + e);
|
||||
m_stream->Read(&lib, sizeof(lib));
|
||||
|
||||
std::string modulename;
|
||||
if (lib.name_addr)
|
||||
{
|
||||
char name[27];
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset + lib.name_addr);
|
||||
m_stream->Read(name, sizeof(name));
|
||||
modulename = name;
|
||||
LOG_WARNING(LOADER, "**** Exported: %s", name);
|
||||
}
|
||||
|
||||
auto &module = info.modules[modulename];
|
||||
|
||||
LOG_WARNING(LOADER, "**** 0x%x - 0x%x - 0x%x", (u32)lib.unk4, (u32)lib.unk5, (u32)lib.unk6);
|
||||
|
||||
for (u16 i = 0, end = lib.num_func; i < end; ++i)
|
||||
{
|
||||
be_t<u32> fnid, fstub;
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset + lib.fnid_addr + i * sizeof(fnid));
|
||||
m_stream->Read(&fnid, sizeof(fnid));
|
||||
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset + lib.fstub_addr + i * sizeof(fstub));
|
||||
m_stream->Read(&fstub, sizeof(fstub));
|
||||
|
||||
module.exports[fnid] = fstub;
|
||||
|
||||
//LOG_NOTICE(LOADER, "Exported function '%s' in '%s' module (LLE)", get_ps3_function_name(fnid), module_name);
|
||||
LOG_WARNING(LOADER, "**** %s: [%s] -> 0x%x", modulename, get_ps3_function_name(fnid), (u32)fstub);
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = module_info.imports_start;
|
||||
i < module_info.imports_end;
|
||||
i += lib.size ? lib.size : sizeof(sys_prx_library_info_t))
|
||||
{
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset + i);
|
||||
m_stream->Read(&lib, sizeof(lib));
|
||||
|
||||
std::string modulename;
|
||||
if (lib.name_addr)
|
||||
{
|
||||
char name[27];
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset + lib.name_addr);
|
||||
m_stream->Read(name, sizeof(name));
|
||||
modulename = name;
|
||||
LOG_WARNING(LOADER, "**** Imported: %s", name);
|
||||
}
|
||||
|
||||
auto &module = info.modules[modulename];
|
||||
|
||||
LOG_WARNING(LOADER, "**** 0x%x - 0x%x - 0x%x", (u32)lib.unk4, (u32)lib.unk5, (u32)lib.unk6);
|
||||
|
||||
for (u16 i = 0, end = lib.num_func; i < end; ++i)
|
||||
{
|
||||
be_t<u32> fnid, fstub;
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset + lib.fnid_addr + i * sizeof(fnid));
|
||||
m_stream->Read(&fnid, sizeof(fnid));
|
||||
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset + lib.fstub_addr + i * sizeof(fstub));
|
||||
m_stream->Read(&fstub, sizeof(fstub));
|
||||
|
||||
module.imports[fnid] = fstub;
|
||||
|
||||
LOG_WARNING(LOADER, "**** %s: [%s] -> 0x%x", modulename, get_ps3_function_name(fnid), (u32)fstub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info.segments.push_back(segment);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x700000a4: //relocation
|
||||
{
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset);
|
||||
|
||||
for (uint i = 0; i < phdr.p_filesz; i += sizeof(sys_prx_relocation_info_t))
|
||||
{
|
||||
sys_prx_relocation_info_t rel;
|
||||
m_stream->Read(&rel, sizeof(rel));
|
||||
|
||||
u32 ADDR = info.segments[rel.index_addr].begin.addr() + rel.offset;
|
||||
|
||||
switch ((u32)rel.type)
|
||||
{
|
||||
case 1:
|
||||
LOG_NOTICE(LOADER, "**** RELOCATION(1): 0x%x <- 0x%x", ADDR, (u32)(info.segments[rel.index_value].begin.addr() + rel.ptr.addr()));
|
||||
*vm::ptr<u32>::make(ADDR) = info.segments[rel.index_value].begin.addr() + rel.ptr.addr();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
LOG_NOTICE(LOADER, "**** RELOCATION(4): 0x%x <- 0x%x", ADDR, (u16)(rel.ptr.addr()));
|
||||
*vm::ptr<u16>::make(ADDR) = (u16)(u64)rel.ptr.addr();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
LOG_NOTICE(LOADER, "**** RELOCATION(5): 0x%x <- 0x%x", ADDR, (u16)(info.segments[rel.index_value].begin.addr() >> 16));
|
||||
*vm::ptr<u16>::make(ADDR) = info.segments[rel.index_value].begin.addr() >> 16;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
LOG_WARNING(LOADER, "**** RELOCATION(6): 0x%x <- 0x%x", ADDR, (u16)(info.segments[1].begin.addr() >> 16));
|
||||
*vm::ptr<u16>::make(ADDR) = info.segments[1].begin.addr() >> 16;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(LOADER, "unknown prx relocation type (0x%x)", (u32)rel.type);
|
||||
return bad_relocation_type;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &m : info.modules)
|
||||
{
|
||||
for (auto &e : m.second.exports)
|
||||
{
|
||||
u32 stub = e.second;
|
||||
|
||||
for (auto &s : info.segments)
|
||||
{
|
||||
if (stub >= s.initial_addr.addr() && stub < s.initial_addr.addr() + s.size_file)
|
||||
{
|
||||
stub += s.begin.addr() - s.initial_addr.addr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(e.second != stub);
|
||||
e.second = stub;
|
||||
}
|
||||
|
||||
for (auto &i : m.second.imports)
|
||||
{
|
||||
u32 stub = i.second;
|
||||
|
||||
for (auto &s : info.segments)
|
||||
{
|
||||
if (stub >= s.initial_addr.addr() && stub < s.initial_addr.addr() + s.size_file)
|
||||
{
|
||||
stub += s.begin.addr() - s.initial_addr.addr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(i.second != stub);
|
||||
i.second = stub;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
handler::error_code elf64::load()
|
||||
{
|
||||
if (is_sprx())
|
||||
{
|
||||
sprx_info info;
|
||||
return load_sprx(info);
|
||||
}
|
||||
|
||||
//store elf to memory
|
||||
vm::ps3::init();
|
||||
Emu.GetModuleManager().Init();
|
||||
|
||||
error_code res = alloc_memory(0);
|
||||
if (res != ok)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<u32> start_funcs;
|
||||
std::vector<u32> stop_funcs;
|
||||
std::vector<u32> exit_funcs;
|
||||
|
||||
//load modules
|
||||
vfsDir lle_dir("/dev_flash/sys/external");
|
||||
|
||||
for (const auto module : lle_dir)
|
||||
{
|
||||
if (module->flags & DirEntry_TypeDir)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rpcs3::state.config.core.load_liblv2.value())
|
||||
{
|
||||
if (module->name != "liblv2.sprx")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
elf64 sprx_handler;
|
||||
|
||||
vfsFile fsprx(lle_dir.GetPath() + "/" + module->name);
|
||||
|
||||
if (fsprx.IsOpened())
|
||||
{
|
||||
sprx_handler.init(fsprx);
|
||||
|
||||
if (sprx_handler.is_sprx())
|
||||
{
|
||||
if (!rpcs3::state.config.core.load_liblv2.value())
|
||||
{
|
||||
if (rpcs3::config.lle.get_entry_value<bool>(sprx_handler.sprx_get_module_name(), false) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_WARNING(LOADER, "Loading LLE library '%s'", sprx_handler.sprx_get_module_name().c_str());
|
||||
|
||||
sprx_info info;
|
||||
sprx_handler.load_sprx(info);
|
||||
|
||||
for (auto &m : info.modules)
|
||||
{
|
||||
if (m.first == "")
|
||||
{
|
||||
for (auto &e : m.second.exports)
|
||||
{
|
||||
auto code = vm::cptr<u32>::make(vm::check_addr(e.second, 8) ? vm::read32(e.second).value() : 0);
|
||||
|
||||
bool is_empty = !code || (code[0] == 0x38600000 && code[1] == BLR());
|
||||
|
||||
if (!code)
|
||||
{
|
||||
LOG_ERROR(LOADER, "bad OPD of special function 0x%08x in '%s' library (0x%x)", e.first, info.name.c_str(), code);
|
||||
}
|
||||
|
||||
switch (e.first)
|
||||
{
|
||||
case 0xbc9a0086:
|
||||
{
|
||||
if (!is_empty)
|
||||
{
|
||||
LOG_ERROR(LOADER, "start func found in '%s' library (0x%x)", info.name.c_str(), code);
|
||||
start_funcs.push_back(e.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xab779874:
|
||||
{
|
||||
if (!is_empty)
|
||||
{
|
||||
LOG_ERROR(LOADER, "stop func found in '%s' library (0x%x)", info.name.c_str(), code);
|
||||
stop_funcs.push_back(e.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x3ab9a95e:
|
||||
{
|
||||
if (!is_empty)
|
||||
{
|
||||
LOG_ERROR(LOADER, "exit func found in '%s' library (0x%x)", info.name.c_str(), code);
|
||||
exit_funcs.push_back(e.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: LOG_ERROR(LOADER, "unknown special func 0x%08x in '%s' library (0x%x)", e.first, info.name.c_str(), code); break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Module<>* module = Emu.GetModuleManager().GetModuleByName(m.first.c_str());
|
||||
|
||||
if (!module)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown module '%s' in '%s' library", m.first.c_str(), info.name.c_str());
|
||||
}
|
||||
|
||||
for (auto& f : m.second.exports)
|
||||
{
|
||||
const u32 nid = f.first;
|
||||
const u32 addr = f.second;
|
||||
|
||||
u32 index;
|
||||
|
||||
auto func = get_ppu_func_by_nid(nid, &index);
|
||||
|
||||
if (!func)
|
||||
{
|
||||
index = add_ppu_func(ModuleFunc(nid, 0, module, nullptr, nullptr, vm::ptr<void()>::make(addr)));
|
||||
}
|
||||
else
|
||||
{
|
||||
func->lle_func.set(addr);
|
||||
|
||||
if (func->flags & MFF_FORCED_HLE)
|
||||
{
|
||||
u32 i_addr = 0;
|
||||
|
||||
if (!vm::check_addr(addr, 8) || !vm::check_addr(i_addr = vm::read32(addr), 4))
|
||||
{
|
||||
LOG_ERROR(LOADER, "Failed to inject code for exported function '%s' (opd=0x%x, 0x%x)", get_ps3_function_name(nid), addr, i_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
vm::write32(i_addr, HACK(index | EIF_PERFORM_BLR));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& f : m.second.imports)
|
||||
{
|
||||
const u32 nid = f.first;
|
||||
const u32 addr = f.second;
|
||||
|
||||
u32 index;
|
||||
|
||||
auto func = get_ppu_func_by_nid(nid, &index);
|
||||
|
||||
if (!func)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown function '%s' (0x%x)", get_ps3_function_name(nid), addr);
|
||||
|
||||
index = add_ppu_func(ModuleFunc(nid, 0, module, nullptr, nullptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Imported function '%s' (0x%x)", get_ps3_function_name(nid), addr);
|
||||
}
|
||||
|
||||
if (!patch_ppu_import(addr, index))
|
||||
{
|
||||
LOG_ERROR(LOADER, "Failed to inject code for function '%s' (0x%x)", get_ps3_function_name(nid), addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res = load_data(0);
|
||||
if (res != ok)
|
||||
return res;
|
||||
|
||||
//initialize process
|
||||
auto rsx_callback_data = vm::ptr<u32>::make(vm::alloc(4 * 4, vm::main));
|
||||
*rsx_callback_data++ = (rsx_callback_data + 1).addr();
|
||||
Emu.SetRSXCallback(rsx_callback_data.addr());
|
||||
|
||||
rsx_callback_data[0] = ADDI(r11, 0, 0x3ff);
|
||||
rsx_callback_data[1] = SC(0);
|
||||
rsx_callback_data[2] = BLR();
|
||||
|
||||
auto ppu_thr_stop_data = vm::ptr<u32>::make(vm::alloc(2 * 4, vm::main));
|
||||
ppu_thr_stop_data[0] = SC(3);
|
||||
ppu_thr_stop_data[1] = BLR();
|
||||
Emu.SetCPUThreadStop(ppu_thr_stop_data.addr());
|
||||
|
||||
Emu.GetModuleManager().Alloc();
|
||||
|
||||
static const int branch_size = 8 * 4;
|
||||
|
||||
auto make_branch = [](vm::ptr<u32>& ptr, u32 addr)
|
||||
{
|
||||
u32 stub = vm::read32(addr);
|
||||
u32 rtoc = vm::read32(addr + 4);
|
||||
|
||||
*ptr++ = LI_(r0, 0);
|
||||
*ptr++ = ORI(r0, r0, stub & 0xffff);
|
||||
*ptr++ = ORIS(r0, r0, stub >> 16);
|
||||
*ptr++ = LI_(r2, 0);
|
||||
*ptr++ = ORI(r2, r2, rtoc & 0xffff);
|
||||
*ptr++ = ORIS(r2, r2, rtoc >> 16);
|
||||
*ptr++ = MTCTR(r0);
|
||||
*ptr++ = BCTRL();
|
||||
};
|
||||
|
||||
auto entry = vm::ptr<u32>::make(vm::alloc(56 + branch_size * (start_funcs.size() + 1), vm::main));
|
||||
|
||||
const auto OPD = entry;
|
||||
|
||||
// make initial OPD
|
||||
*entry++ = OPD.addr() + 8;
|
||||
*entry++ = 0xdeadbeef;
|
||||
|
||||
// save initialization args
|
||||
*entry++ = MR(r14, r3);
|
||||
*entry++ = MR(r15, r4);
|
||||
*entry++ = MR(r16, r5);
|
||||
*entry++ = MR(r17, r6);
|
||||
*entry++ = MR(r18, r11);
|
||||
*entry++ = MR(r19, r12);
|
||||
|
||||
for (auto &f : start_funcs)
|
||||
{
|
||||
make_branch(entry, f);
|
||||
}
|
||||
|
||||
// restore initialization args
|
||||
*entry++ = MR(r3, r14);
|
||||
*entry++ = MR(r4, r15);
|
||||
*entry++ = MR(r5, r16);
|
||||
*entry++ = MR(r6, r17);
|
||||
*entry++ = MR(r11, r18);
|
||||
*entry++ = MR(r12, r19);
|
||||
|
||||
// branch to initialization
|
||||
make_branch(entry, m_ehdr.e_entry);
|
||||
|
||||
const auto decoder_cache = fxm::make<ppu_decoder_cache_t>();
|
||||
|
||||
for (u32 page = 0; page < 0x20000000; page += 4096)
|
||||
{
|
||||
// TODO: scan only executable areas
|
||||
if (vm::check_addr(page, 4096))
|
||||
{
|
||||
decoder_cache->initialize(page, 4096);
|
||||
}
|
||||
}
|
||||
|
||||
ppu_thread main_thread(OPD.addr(), "main_thread");
|
||||
|
||||
main_thread.args({ Emu.GetPath()/*, "-emu"*/ }).run();
|
||||
main_thread.gpr(11, OPD.addr()).gpr(12, Emu.GetMallocPageSize());
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
handler::error_code elf64::alloc_memory(u64 offset)
|
||||
{
|
||||
for (auto &phdr : m_phdrs)
|
||||
{
|
||||
switch (phdr.p_type.value())
|
||||
{
|
||||
case 0x00000001: //LOAD
|
||||
{
|
||||
if (phdr.p_memsz)
|
||||
{
|
||||
if (!vm::falloc(phdr.p_vaddr.addr(), phdr.p_memsz, vm::main))
|
||||
{
|
||||
LOG_ERROR(LOADER, "%s(): AllocFixed(0x%llx, 0x%llx) failed", __FUNCTION__, phdr.p_vaddr.addr(), phdr.p_memsz);
|
||||
|
||||
return loading_error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
handler::error_code elf64::load_data(u64 offset)
|
||||
{
|
||||
for (auto &phdr : m_phdrs)
|
||||
{
|
||||
switch (phdr.p_type.value())
|
||||
{
|
||||
case 0x00000001: //LOAD
|
||||
{
|
||||
if (phdr.p_memsz)
|
||||
{
|
||||
if (phdr.p_filesz)
|
||||
{
|
||||
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset);
|
||||
m_stream->Read(phdr.p_vaddr.get_ptr(), phdr.p_filesz);
|
||||
|
||||
if (rpcs3::state.config.core.hook_st_func.value())
|
||||
{
|
||||
hook_ppu_funcs(vm::static_ptr_cast<u32>(phdr.p_vaddr), phdr.p_filesz / 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x00000007: //TLS
|
||||
{
|
||||
Emu.SetTLSData(phdr.p_vaddr.addr(), phdr.p_filesz, phdr.p_memsz);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x60000001: //LOOS+1
|
||||
{
|
||||
if (phdr.p_filesz)
|
||||
{
|
||||
struct process_param_t
|
||||
{
|
||||
be_t<u32> size;
|
||||
be_t<u32> magic;
|
||||
be_t<u32> version;
|
||||
be_t<u32> sdk_version;
|
||||
be_t<s32> primary_prio;
|
||||
be_t<u32> primary_stacksize;
|
||||
be_t<u32> malloc_pagesize;
|
||||
be_t<u32> ppc_seg;
|
||||
//be_t<u32> crash_dump_param_addr;
|
||||
};
|
||||
|
||||
const auto& info = *(process_param_t*)phdr.p_vaddr.get_ptr();
|
||||
|
||||
if (info.size < sizeof(process_param_t))
|
||||
{
|
||||
LOG_WARNING(LOADER, "Bad process_param size! [0x%x : 0x%x]", info.size, SIZE_32(process_param_t));
|
||||
}
|
||||
if (info.magic != 0x13bcc5f6)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Bad process_param magic! [0x%x]", info.magic);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_NOTICE(LOADER, "*** sdk version: 0x%x", info.sdk_version);
|
||||
LOG_NOTICE(LOADER, "*** primary prio: %d", info.primary_prio);
|
||||
LOG_NOTICE(LOADER, "*** primary stacksize: 0x%x", info.primary_stacksize);
|
||||
LOG_NOTICE(LOADER, "*** malloc pagesize: 0x%x", info.malloc_pagesize);
|
||||
LOG_NOTICE(LOADER, "*** ppc seg: 0x%x", info.ppc_seg);
|
||||
//LOG_NOTICE(LOADER, "*** crash dump param addr: 0x%x", info.crash_dump_param_addr);
|
||||
|
||||
Emu.SetParams(info.sdk_version, info.malloc_pagesize, std::max<u32>(info.primary_stacksize, 0x4000), info.primary_prio);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x60000002: //LOOS+2
|
||||
{
|
||||
if (phdr.p_filesz)
|
||||
{
|
||||
const sys_proc_prx_param& proc_prx_param = *(sys_proc_prx_param*)phdr.p_vaddr.get_ptr();
|
||||
|
||||
if (proc_prx_param.magic != 0x1b434cec)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Bad magic! (0x%x)", proc_prx_param.magic);
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto stub = proc_prx_param.libstubstart; stub < proc_prx_param.libstubend; ++stub)
|
||||
{
|
||||
const std::string module_name = stub->s_modulename.get_ptr();
|
||||
|
||||
Module<>* module = Emu.GetModuleManager().GetModuleByName(module_name.c_str());
|
||||
|
||||
if (!module)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown module '%s'", module_name.c_str());
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < stub->s_imports; ++i)
|
||||
{
|
||||
const u32 nid = stub->s_nid[i];
|
||||
const u32 addr = stub->s_text[i];
|
||||
|
||||
u32 index;
|
||||
|
||||
auto func = get_ppu_func_by_nid(nid, &index);
|
||||
|
||||
if (!func)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown function '%s' in '%s' module (0x%x)", get_ps3_function_name(nid), module_name, addr);
|
||||
|
||||
index = add_ppu_func(ModuleFunc(nid, 0, module, nullptr, nullptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool is_lle = func->lle_func && !(func->flags & MFF_FORCED_HLE);
|
||||
|
||||
LOG_NOTICE(LOADER, "Imported %sfunction '%s' in '%s' module (0x%x)", is_lle ? "LLE " : "", get_ps3_function_name(nid), module_name, addr);
|
||||
}
|
||||
|
||||
if (!patch_ppu_import(addr, index))
|
||||
{
|
||||
LOG_ERROR(LOADER, "Failed to inject code at address 0x%x", addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOG_ERROR(LOADER, "Unknown phdr type (0x%08x)", phdr.p_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
#pragma once
|
||||
#include "Loader.h"
|
||||
|
||||
struct vfsStream;
|
||||
|
||||
namespace loader
|
||||
{
|
||||
namespace handlers
|
||||
{
|
||||
class elf64 : public handler
|
||||
{
|
||||
public:
|
||||
struct ehdr
|
||||
{
|
||||
be_t<u32> e_magic;
|
||||
u8 e_class;
|
||||
u8 e_data;
|
||||
u8 e_curver;
|
||||
u8 e_os_abi;
|
||||
be_t<u64> e_abi_ver;
|
||||
be_t<u16> e_type;
|
||||
be_t<u16> e_machine;
|
||||
be_t<u32> e_version;
|
||||
be_t<u64> e_entry;
|
||||
be_t<u64> e_phoff;
|
||||
be_t<u64> e_shoff;
|
||||
be_t<u32> e_flags;
|
||||
be_t<u16> e_ehsize;
|
||||
be_t<u16> e_phentsize;
|
||||
be_t<u16> e_phnum;
|
||||
be_t<u16> e_shentsize;
|
||||
be_t<u16> e_shnum;
|
||||
be_t<u16> e_shstrndx;
|
||||
|
||||
bool check() const { return e_magic == 0x7F454C46; }
|
||||
} m_ehdr;
|
||||
|
||||
struct phdr
|
||||
{
|
||||
be_t<u32> p_type;
|
||||
be_t<u32> p_flags;
|
||||
be_t<u64> p_offset;
|
||||
_ptr_base<void, be_t<u64>> p_vaddr;
|
||||
_ptr_base<void, be_t<u64>> p_paddr;
|
||||
be_t<u64> p_filesz;
|
||||
be_t<u64> p_memsz;
|
||||
be_t<u64> p_align;
|
||||
};
|
||||
|
||||
struct shdr
|
||||
{
|
||||
be_t<u32> sh_name;
|
||||
be_t<u32> sh_type;
|
||||
be_t<u64> sh_flags;
|
||||
_ptr_base<void, be_t<u64>> sh_addr;
|
||||
be_t<u64> sh_offset;
|
||||
be_t<u64> sh_size;
|
||||
be_t<u32> sh_link;
|
||||
be_t<u32> sh_info;
|
||||
be_t<u64> sh_addralign;
|
||||
be_t<u64> sh_entsize;
|
||||
};
|
||||
|
||||
struct sprx_module_info
|
||||
{
|
||||
be_t<u16> attr;
|
||||
u8 version[2];
|
||||
char name[28];
|
||||
be_t<u32> toc_addr;
|
||||
be_t<u32> export_start;
|
||||
be_t<u32> export_end;
|
||||
be_t<u32> import_start;
|
||||
be_t<u32> import_end;
|
||||
} m_sprx_module_info;
|
||||
|
||||
struct sprx_export_info
|
||||
{
|
||||
u8 size;
|
||||
u8 padding;
|
||||
be_t<u16> version;
|
||||
be_t<u16> attr;
|
||||
be_t<u16> func_count;
|
||||
be_t<u16> vars_count;
|
||||
be_t<u16> tls_vars_count;
|
||||
be_t<u16> hash_info;
|
||||
be_t<u16> tls_hash_info;
|
||||
u8 reserved[2];
|
||||
be_t<u32> lib_name_offset;
|
||||
be_t<u32> nid_offset;
|
||||
be_t<u32> stub_offset;
|
||||
};
|
||||
|
||||
struct sprx_import_info
|
||||
{
|
||||
u8 size;
|
||||
u8 unused;
|
||||
be_t<u16> version;
|
||||
be_t<u16> attr;
|
||||
be_t<u16> func_count;
|
||||
be_t<u16> vars_count;
|
||||
be_t<u16> tls_vars_count;
|
||||
u8 reserved[4];
|
||||
be_t<u32> lib_name_offset;
|
||||
be_t<u32> nid_offset;
|
||||
be_t<u32> stub_offset;
|
||||
//...
|
||||
};
|
||||
|
||||
struct sprx_function_info
|
||||
{
|
||||
be_t<u32> name_table_offset;
|
||||
be_t<u32> entry_table_offset;
|
||||
be_t<u32> padding;
|
||||
} m_sprx_function_info;
|
||||
|
||||
struct sprx_lib_info
|
||||
{
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct sprx_segment_info
|
||||
{
|
||||
_ptr_base<void> begin;
|
||||
u32 size;
|
||||
u32 size_file;
|
||||
_ptr_base<void> initial_addr;
|
||||
std::vector<sprx_module_info> modules;
|
||||
};
|
||||
|
||||
struct sprx_info
|
||||
{
|
||||
std::string name;
|
||||
u32 rtoc;
|
||||
|
||||
struct module_info
|
||||
{
|
||||
std::unordered_map<u32, u32> exports;
|
||||
std::unordered_map<u32, u32> imports;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, module_info> modules;
|
||||
std::vector<sprx_segment_info> segments;
|
||||
};
|
||||
|
||||
std::vector<phdr> m_phdrs;
|
||||
std::vector<shdr> m_shdrs;
|
||||
|
||||
std::vector<sprx_segment_info> m_sprx_segments_info;
|
||||
std::vector<sprx_import_info> m_sprx_import_info;
|
||||
std::vector<sprx_export_info> m_sprx_export_info;
|
||||
|
||||
public:
|
||||
virtual ~elf64() = default;
|
||||
|
||||
error_code init(vfsStream& stream) override;
|
||||
error_code load() override;
|
||||
error_code alloc_memory(u64 offset);
|
||||
error_code load_data(u64 offset);
|
||||
error_code load_sprx(sprx_info& info);
|
||||
bool is_sprx() const { return m_ehdr.e_type == 0xffa4; }
|
||||
std::string sprx_get_module_name() const { return m_sprx_module_info.name; }
|
||||
};
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "Loader.h"
|
||||
#include "PSF.h"
|
||||
#include "Emu/FS/vfsLocalFile.h"
|
||||
|
||||
namespace loader
|
||||
{
|
||||
bool loader::load(vfsStream& stream)
|
||||
{
|
||||
for (auto i : m_handlers)
|
||||
{
|
||||
i->set_status(i->init(stream));
|
||||
if (i->get_status() == handler::ok)
|
||||
{
|
||||
i->set_status(i->load());
|
||||
if (i->get_status() == handler::ok)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_NOTICE(LOADER, "loader::load() failed: %s", i->get_error_code().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_NOTICE(LOADER, "loader::init() failed: %s", i->get_error_code().c_str());
|
||||
stream.Seek(i->get_stream_offset());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handler::error_code handler::init(vfsStream& stream)
|
||||
{
|
||||
m_stream_offset = stream.Tell();
|
||||
m_stream = &stream;
|
||||
|
||||
return ok;
|
||||
}
|
||||
};
|
||||
|
||||
static const u64 g_spu_offset = 0x10000;
|
||||
|
||||
const std::string Ehdr_DataToString(const u8 data)
|
||||
{
|
||||
if(data > 1) return fmt::format("%d's complement, big endian", data);
|
||||
if(data < 1) return "Data is not found";
|
||||
|
||||
return fmt::format("%d's complement, little endian", data);
|
||||
}
|
||||
|
||||
const std::string Ehdr_TypeToString(const u16 type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case 0: return "NULL";
|
||||
case 2: return "EXEC (Executable file)";
|
||||
};
|
||||
|
||||
return fmt::format("Unknown (%d)", type);
|
||||
}
|
||||
|
||||
const std::string Ehdr_OS_ABIToString(const u8 os_abi)
|
||||
{
|
||||
switch(os_abi)
|
||||
{
|
||||
case 0x0 : return "UNIX System V";
|
||||
case 0x66: return "Cell OS LV-2";
|
||||
};
|
||||
|
||||
return fmt::format("Unknown (0x%x)", os_abi);
|
||||
}
|
||||
|
||||
const std::string Ehdr_MachineToString(const u16 machine)
|
||||
{
|
||||
switch(machine)
|
||||
{
|
||||
case MACHINE_MIPS: return "MIPS";
|
||||
case MACHINE_PPC64: return "PowerPC64";
|
||||
case MACHINE_SPU: return "SPU";
|
||||
case MACHINE_ARM: return "ARM";
|
||||
};
|
||||
|
||||
return fmt::format("Unknown (0x%x)", machine);
|
||||
}
|
||||
|
||||
const std::string Phdr_FlagsToString(u32 flags)
|
||||
{
|
||||
enum {ppu_R = 0x1, ppu_W = 0x2, ppu_E = 0x4};
|
||||
enum {spu_E = 0x1, spu_W = 0x2, spu_R = 0x4};
|
||||
enum {rsx_R = 0x1, rsx_W = 0x2, rsx_E = 0x4};
|
||||
|
||||
#define FLAGS_TO_STRING(f) \
|
||||
std::string(f & f##_R ? "R" : "-") + \
|
||||
std::string(f & f##_W ? "W" : "-") + \
|
||||
std::string(f & f##_E ? "E" : "-")
|
||||
|
||||
const u8 ppu = flags & 0xf;
|
||||
const u8 spu = (flags >> 0x14) & 0xf;
|
||||
const u8 rsx = (flags >> 0x18) & 0xf;
|
||||
|
||||
std::string ret;
|
||||
ret += fmt::format("[0x%x] ", flags);
|
||||
|
||||
flags &= ~ppu;
|
||||
flags &= ~spu << 0x14;
|
||||
flags &= ~rsx << 0x18;
|
||||
|
||||
if(flags != 0) return fmt::format("Unknown %s PPU[0x%x] SPU[0x%x] RSX[0x%x]", ret.c_str(), ppu, spu, rsx);
|
||||
|
||||
ret += "PPU[" + FLAGS_TO_STRING(ppu) + "] ";
|
||||
ret += "SPU[" + FLAGS_TO_STRING(spu) + "] ";
|
||||
ret += "RSX[" + FLAGS_TO_STRING(rsx) + "]";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::string Phdr_TypeToString(const u32 type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case 0x00000001: return "LOAD";
|
||||
case 0x00000004: return "NOTE";
|
||||
case 0x00000007: return "TLS";
|
||||
case 0x60000001: return "LOOS+1";
|
||||
case 0x60000002: return "LOOS+2";
|
||||
};
|
||||
|
||||
return fmt::format("Unknown (0x%x)", type);
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
#pragma once
|
||||
#include "Emu/Memory/vm.h"
|
||||
|
||||
struct vfsFileBase;
|
||||
struct vfsStream;
|
||||
|
||||
#ifdef _DEBUG
|
||||
//#define LOADER_DEBUG
|
||||
#endif
|
||||
|
||||
enum Elf_Machine
|
||||
{
|
||||
MACHINE_Unknown,
|
||||
MACHINE_MIPS = 0x08,
|
||||
MACHINE_PPC64 = 0x15,
|
||||
MACHINE_SPU = 0x17,
|
||||
MACHINE_ARM = 0x28
|
||||
};
|
||||
|
||||
enum ShdrType
|
||||
{
|
||||
SHT_NULL,
|
||||
SHT_PROGBITS,
|
||||
SHT_SYMTAB,
|
||||
SHT_STRTAB,
|
||||
SHT_RELA,
|
||||
SHT_HASH,
|
||||
SHT_DYNAMIC,
|
||||
SHT_NOTE,
|
||||
SHT_NOBITS,
|
||||
SHT_REL,
|
||||
SHT_SHLIB,
|
||||
SHT_DYNSYM
|
||||
};
|
||||
|
||||
enum ShdrFlag
|
||||
{
|
||||
SHF_WRITE = 0x1,
|
||||
SHF_ALLOC = 0x2,
|
||||
SHF_EXECINSTR = 0x4,
|
||||
SHF_MASKPROC = 0xf0000000
|
||||
};
|
||||
|
||||
const std::string Ehdr_DataToString(const u8 data);
|
||||
const std::string Ehdr_TypeToString(const u16 type);
|
||||
const std::string Ehdr_OS_ABIToString(const u8 os_abi);
|
||||
const std::string Ehdr_MachineToString(const u16 machine);
|
||||
const std::string Phdr_FlagsToString(u32 flags);
|
||||
const std::string Phdr_TypeToString(const u32 type);
|
||||
|
||||
namespace loader
|
||||
{
|
||||
class handler
|
||||
{
|
||||
u64 m_stream_offset;
|
||||
|
||||
protected:
|
||||
vfsStream* m_stream;
|
||||
|
||||
public:
|
||||
enum error_code
|
||||
{
|
||||
bad_version = -1,
|
||||
bad_file = -2,
|
||||
broken_file = -3,
|
||||
loading_error = -4,
|
||||
bad_relocation_type = -5,
|
||||
ok = 0
|
||||
};
|
||||
|
||||
virtual ~handler() = default;
|
||||
|
||||
virtual error_code init(vfsStream& stream);
|
||||
virtual error_code load() = 0;
|
||||
u64 get_stream_offset() const
|
||||
{
|
||||
return m_stream_offset;
|
||||
}
|
||||
|
||||
void set_status(const error_code& code)
|
||||
{
|
||||
m_status = code;
|
||||
}
|
||||
|
||||
error_code get_status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
const std::string get_error_code() const
|
||||
{
|
||||
switch (m_status)
|
||||
{
|
||||
case bad_version: return "Bad version";
|
||||
case bad_file: return "Bad file";
|
||||
case broken_file: return "Broken file";
|
||||
case loading_error: return "Loading error";
|
||||
case bad_relocation_type: return "Bad relocation type";
|
||||
case ok: return "Ok";
|
||||
|
||||
default: return "Unknown error code";
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
error_code m_status;
|
||||
};
|
||||
|
||||
class loader
|
||||
{
|
||||
std::vector<handler*> m_handlers;
|
||||
|
||||
public:
|
||||
~loader()
|
||||
{
|
||||
for (auto &h : m_handlers)
|
||||
{
|
||||
delete h;
|
||||
}
|
||||
}
|
||||
|
||||
void register_handler(handler* handler)
|
||||
{
|
||||
m_handlers.push_back(handler);
|
||||
}
|
||||
|
||||
bool load(vfsStream& stream);
|
||||
};
|
||||
|
||||
using namespace vm;
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace psf
|
||||
{
|
||||
_log::channel log("PSF");
|
||||
_log::channel log("PSF", _log::level::notice);
|
||||
|
||||
struct header_t
|
||||
{
|
||||
@ -25,26 +25,26 @@ namespace psf
|
||||
|
||||
const std::string& entry::as_string() const
|
||||
{
|
||||
CHECK_ASSERTION(m_type == format::string || m_type == format::array);
|
||||
Expects(m_type == format::string || m_type == format::array);
|
||||
return m_value_string;
|
||||
}
|
||||
|
||||
u32 entry::as_integer() const
|
||||
{
|
||||
CHECK_ASSERTION(m_type == format::integer);
|
||||
Expects(m_type == format::integer);
|
||||
return m_value_integer;
|
||||
}
|
||||
|
||||
entry& entry::operator =(const std::string& value)
|
||||
{
|
||||
CHECK_ASSERTION(m_type == format::string || m_type == format::array);
|
||||
Expects(m_type == format::string || m_type == format::array);
|
||||
m_value_string = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
entry& entry::operator =(u32 value)
|
||||
{
|
||||
CHECK_ASSERTION(m_type == format::integer);
|
||||
Expects(m_type == format::integer);
|
||||
m_value_integer = value;
|
||||
return *this;
|
||||
}
|
||||
@ -61,10 +61,10 @@ namespace psf
|
||||
return SIZE_32(u32);
|
||||
}
|
||||
|
||||
throw EXCEPTION("Invalid format (0x%x)", m_type);
|
||||
throw fmt::exception("Invalid format (0x%x)" HERE, m_type);
|
||||
}
|
||||
|
||||
registry load(const std::vector<char>& data)
|
||||
registry load_object(const std::vector<char>& data)
|
||||
{
|
||||
registry result;
|
||||
|
||||
@ -75,18 +75,18 @@ namespace psf
|
||||
}
|
||||
|
||||
// Check size
|
||||
CHECK_ASSERTION(data.size() >= sizeof(header_t));
|
||||
CHECK_ASSERTION((std::uintptr_t)data.data() % 8 == 0);
|
||||
Expects(data.size() >= sizeof(header_t));
|
||||
Expects((std::uintptr_t)data.data() % 8 == 0);
|
||||
|
||||
// Get header
|
||||
const header_t& header = reinterpret_cast<const header_t&>(data[0]);
|
||||
|
||||
// Check magic and version
|
||||
CHECK_ASSERTION(header.magic == *(u32*)"\0PSF");
|
||||
CHECK_ASSERTION(header.version == 0x101);
|
||||
CHECK_ASSERTION(sizeof(header_t) + header.entries_num * sizeof(def_table_t) <= header.off_key_table);
|
||||
CHECK_ASSERTION(header.off_key_table <= header.off_data_table);
|
||||
CHECK_ASSERTION(header.off_data_table <= data.size());
|
||||
Expects(header.magic == "\0PSF"_u32);
|
||||
Expects(header.version == 0x101);
|
||||
Expects(sizeof(header_t) + header.entries_num * sizeof(def_table_t) <= header.off_key_table);
|
||||
Expects(header.off_key_table <= header.off_data_table);
|
||||
Expects(header.off_data_table <= data.size());
|
||||
|
||||
// Get indices (alignment should be fine)
|
||||
const def_table_t* indices = reinterpret_cast<const def_table_t*>(data.data() + sizeof(header_t));
|
||||
@ -94,7 +94,7 @@ namespace psf
|
||||
// Load entries
|
||||
for (u32 i = 0; i < header.entries_num; ++i)
|
||||
{
|
||||
CHECK_ASSERTION(indices[i].key_off < header.off_data_table - header.off_key_table);
|
||||
Expects(indices[i].key_off < header.off_data_table - header.off_key_table);
|
||||
|
||||
// Get key name range
|
||||
const auto name_ptr = data.begin() + header.off_key_table + indices[i].key_off;
|
||||
@ -103,10 +103,10 @@ namespace psf
|
||||
// Get name (must be unique)
|
||||
std::string key(name_ptr, name_end);
|
||||
|
||||
CHECK_ASSERTION(result.count(key) == 0);
|
||||
CHECK_ASSERTION(indices[i].param_len <= indices[i].param_max);
|
||||
CHECK_ASSERTION(indices[i].data_off < data.size() - header.off_data_table);
|
||||
CHECK_ASSERTION(indices[i].param_max < data.size() - indices[i].data_off);
|
||||
Expects(result.count(key) == 0);
|
||||
Expects(indices[i].param_len <= indices[i].param_max);
|
||||
Expects(indices[i].data_off < data.size() - header.off_data_table);
|
||||
Expects(indices[i].param_max < data.size() - indices[i].data_off);
|
||||
|
||||
// Get data pointer
|
||||
const auto value_ptr = data.begin() + header.off_data_table + indices[i].data_off;
|
||||
@ -147,7 +147,7 @@ namespace psf
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<char> save(const registry& psf)
|
||||
std::vector<char> save_object(const registry& psf)
|
||||
{
|
||||
std::vector<def_table_t> indices; indices.reserve(psf.size());
|
||||
|
||||
@ -175,7 +175,7 @@ namespace psf
|
||||
|
||||
// Generate header
|
||||
header_t header;
|
||||
header.magic = *(u32*)"\0PSF";
|
||||
header.magic = "\0PSF"_u32;
|
||||
header.version = 0x101;
|
||||
header.off_key_table = gsl::narrow<u32>(sizeof(header_t) + sizeof(def_table_t) * psf.size());
|
||||
header.off_data_table = gsl::narrow<u32>(header.off_key_table + key_offset);
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace psf
|
||||
{
|
||||
enum class format : u16
|
||||
@ -23,8 +25,8 @@ namespace psf
|
||||
, m_max_size(max_size)
|
||||
, m_value_string(value)
|
||||
{
|
||||
CHECK_ASSERTION(type == format::string || type == format::array);
|
||||
CHECK_ASSERTION(max_size);
|
||||
Expects(type == format::string || type == format::array);
|
||||
Expects(max_size);
|
||||
}
|
||||
|
||||
// Construct integer entry, assign the value
|
||||
@ -49,11 +51,24 @@ namespace psf
|
||||
// Define PSF registry as a sorted map of entries:
|
||||
using registry = std::map<std::string, entry>;
|
||||
|
||||
// Load PSF registry from binary data
|
||||
registry load(const std::vector<char>&);
|
||||
// Load PSF registry from SFO binary data
|
||||
registry load_object(const std::vector<char>&);
|
||||
|
||||
// Convert PSF registry to binary format
|
||||
std::vector<char> save(const registry&);
|
||||
// Load PSF registry from SFO file, if opened
|
||||
inline registry load_object(const fs::file& f)
|
||||
{
|
||||
if (f)
|
||||
{
|
||||
return load_object(f.to_vector<char>());
|
||||
}
|
||||
else
|
||||
{
|
||||
return registry{};
|
||||
}
|
||||
}
|
||||
|
||||
// Convert PSF registry to SFO binary format
|
||||
std::vector<char> save_object(const registry&);
|
||||
|
||||
// Get string value or default value
|
||||
std::string get_string(const registry& psf, const std::string& key, const std::string& def = {});
|
||||
|
@ -1,52 +1,41 @@
|
||||
#include "stdafx.h"
|
||||
#include "Utilities/rXml.h"
|
||||
#include "Emu/FS/VFS.h"
|
||||
#include "Emu/FS/vfsFileBase.h"
|
||||
#include "Emu/System.h"
|
||||
#include "TROPUSR.h"
|
||||
|
||||
TROPUSRLoader::TROPUSRLoader()
|
||||
{
|
||||
m_file = NULL;
|
||||
memset(&m_header, 0, sizeof(m_header));
|
||||
}
|
||||
|
||||
TROPUSRLoader::~TROPUSRLoader()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::Load(const std::string& filepath, const std::string& configpath)
|
||||
{
|
||||
if (m_file)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
const std::string& path = vfs::get(filepath);
|
||||
|
||||
if (!Emu.GetVFS().ExistsFile(filepath))
|
||||
if (!fs::is_file(path))
|
||||
{
|
||||
Generate(filepath, configpath);
|
||||
}
|
||||
|
||||
m_file = Emu.GetVFS().OpenFile(filepath, fom::read);
|
||||
LoadHeader();
|
||||
LoadTableHeaders();
|
||||
LoadTables();
|
||||
if (!m_file.open(path, fs::read))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Close();
|
||||
if (!LoadHeader() || !LoadTableHeaders() || !LoadTables())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_file.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::LoadHeader()
|
||||
{
|
||||
if (!m_file->IsOpened())
|
||||
if (!m_file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_file->Seek(0);
|
||||
m_file.seek(0);
|
||||
|
||||
if (m_file->Read(&m_header, sizeof(TROPUSRHeader)) != sizeof(TROPUSRHeader))
|
||||
if (!m_file.read(m_header))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -56,18 +45,18 @@ bool TROPUSRLoader::LoadHeader()
|
||||
|
||||
bool TROPUSRLoader::LoadTableHeaders()
|
||||
{
|
||||
if (!m_file->IsOpened())
|
||||
if (!m_file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_file->Seek(0x30);
|
||||
m_file.seek(0x30);
|
||||
m_tableHeaders.clear();
|
||||
m_tableHeaders.resize(m_header.tables_count);
|
||||
|
||||
for (TROPUSRTableHeader& tableHeader : m_tableHeaders)
|
||||
{
|
||||
if (m_file->Read(&tableHeader, sizeof(TROPUSRTableHeader)) != sizeof(TROPUSRTableHeader))
|
||||
if (!m_file.read(tableHeader))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -76,14 +65,14 @@ bool TROPUSRLoader::LoadTableHeaders()
|
||||
|
||||
bool TROPUSRLoader::LoadTables()
|
||||
{
|
||||
if (!m_file->IsOpened())
|
||||
if (!m_file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const TROPUSRTableHeader& tableHeader : m_tableHeaders)
|
||||
{
|
||||
m_file->Seek(tableHeader.offset);
|
||||
m_file.seek(tableHeader.offset);
|
||||
|
||||
if (tableHeader.type == 4)
|
||||
{
|
||||
@ -92,7 +81,7 @@ bool TROPUSRLoader::LoadTables()
|
||||
|
||||
for (auto& entry : m_table4)
|
||||
{
|
||||
if (m_file->Read(&entry, sizeof(TROPUSREntry4)) != sizeof(TROPUSREntry4))
|
||||
if (!m_file.read(entry))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -104,7 +93,7 @@ bool TROPUSRLoader::LoadTables()
|
||||
|
||||
for (auto& entry : m_table6)
|
||||
{
|
||||
if (m_file->Read(&entry, sizeof(TROPUSREntry6)) != sizeof(TROPUSREntry6))
|
||||
if (!m_file.read(entry))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -118,39 +107,39 @@ bool TROPUSRLoader::LoadTables()
|
||||
// TODO: TROPUSRLoader::Save deletes the TROPUSR and creates it again. This is probably very slow.
|
||||
bool TROPUSRLoader::Save(const std::string& filepath)
|
||||
{
|
||||
if (m_file)
|
||||
if (!m_file.open(vfs::get(filepath), fs::rewrite))
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_file = Emu.GetVFS().OpenFile(filepath, fom::rewrite);
|
||||
m_file->Write(&m_header, sizeof(TROPUSRHeader));
|
||||
m_file.write(m_header);
|
||||
|
||||
for (const TROPUSRTableHeader& tableHeader : m_tableHeaders)
|
||||
{
|
||||
m_file->Write(&tableHeader, sizeof(TROPUSRTableHeader));
|
||||
m_file.write(tableHeader);
|
||||
}
|
||||
|
||||
for (const auto& entry : m_table4)
|
||||
{
|
||||
m_file->Write(&entry, sizeof(TROPUSREntry4));
|
||||
m_file.write(entry);
|
||||
}
|
||||
|
||||
for (const auto& entry : m_table6)
|
||||
{
|
||||
m_file->Write(&entry, sizeof(TROPUSREntry6));
|
||||
m_file.write(entry);
|
||||
}
|
||||
|
||||
m_file->Close();
|
||||
|
||||
m_file.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TROPUSRLoader::Generate(const std::string& filepath, const std::string& configpath)
|
||||
{
|
||||
std::string path;
|
||||
const std::string& path = vfs::get(configpath);
|
||||
|
||||
// TODO: rXmlDocument can open only real file
|
||||
ASSERT(!fs::get_virtual_device(path));
|
||||
rXmlDocument doc;
|
||||
Emu.GetVFS().GetDevice(configpath.c_str(), path);
|
||||
doc.Load(path);
|
||||
|
||||
m_table4.clear();
|
||||
@ -238,13 +227,3 @@ bool TROPUSRLoader::UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TROPUSRLoader::Close()
|
||||
{
|
||||
if (m_file)
|
||||
{
|
||||
m_file->Close();
|
||||
delete m_file;
|
||||
m_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
struct vfsStream;
|
||||
|
||||
struct TROPUSRHeader
|
||||
{
|
||||
be_t<u32> magic; // 81 8F 54 AD
|
||||
@ -56,8 +54,8 @@ struct TROPUSREntry6
|
||||
|
||||
class TROPUSRLoader
|
||||
{
|
||||
vfsStream* m_file;
|
||||
TROPUSRHeader m_header;
|
||||
fs::file m_file;
|
||||
TROPUSRHeader m_header{};
|
||||
std::vector<TROPUSRTableHeader> m_tableHeaders;
|
||||
|
||||
std::vector<TROPUSREntry4> m_table4;
|
||||
@ -69,12 +67,8 @@ class TROPUSRLoader
|
||||
virtual bool LoadTables();
|
||||
|
||||
public:
|
||||
TROPUSRLoader();
|
||||
~TROPUSRLoader();
|
||||
|
||||
virtual bool Load(const std::string& filepath, const std::string& configpath);
|
||||
virtual bool Save(const std::string& filepath);
|
||||
virtual void Close();
|
||||
|
||||
virtual u32 GetTrophiesCount();
|
||||
|
||||
|
@ -1,42 +1,34 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/FS/VFS.h"
|
||||
#include "Emu/FS/vfsFile.h"
|
||||
#include "TRP.h"
|
||||
|
||||
TRPLoader::TRPLoader(vfsStream& f) : trp_f(f)
|
||||
TRPLoader::TRPLoader(const fs::file& f)
|
||||
: trp_f(f)
|
||||
{
|
||||
}
|
||||
|
||||
TRPLoader::~TRPLoader()
|
||||
bool TRPLoader::Install(const std::string& dest, bool show)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool TRPLoader::Install(std::string dest, bool show)
|
||||
{
|
||||
if (!trp_f.IsOpened())
|
||||
if (!trp_f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dest.empty() && dest.back() != '/')
|
||||
const std::string& local_path = vfs::get(dest);
|
||||
|
||||
if (!fs::create_dir(local_path) && errno != EEXIST)
|
||||
{
|
||||
dest += '/';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Emu.GetVFS().ExistsDir(dest))
|
||||
{
|
||||
Emu.GetVFS().CreateDir(dest);
|
||||
}
|
||||
std::vector<char> buffer; buffer.reserve(65536);
|
||||
|
||||
for (const TRPEntry& entry : m_entries)
|
||||
{
|
||||
char* buffer = new char [(u32)entry.size];
|
||||
trp_f.Seek(entry.offset);
|
||||
trp_f.Read(buffer, entry.size);
|
||||
vfsFile(dest + entry.name, fom::rewrite).Write(buffer, entry.size);
|
||||
delete[] buffer;
|
||||
trp_f.seek(entry.offset);
|
||||
buffer.resize(entry.size);
|
||||
if (!trp_f.read(buffer)) continue; // ???
|
||||
fs::file(local_path + '/' + entry.name, fs::rewrite).write(buffer);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -44,14 +36,14 @@ bool TRPLoader::Install(std::string dest, bool show)
|
||||
|
||||
bool TRPLoader::LoadHeader(bool show)
|
||||
{
|
||||
if (!trp_f.IsOpened())
|
||||
if (!trp_f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
trp_f.Seek(0);
|
||||
trp_f.seek(0);
|
||||
|
||||
if (trp_f.Read(&m_header, sizeof(TRPHeader)) != sizeof(TRPHeader))
|
||||
if (!trp_f.read(m_header))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -71,7 +63,7 @@ bool TRPLoader::LoadHeader(bool show)
|
||||
|
||||
for (u32 i = 0; i < m_header.trp_files_count; i++)
|
||||
{
|
||||
if (trp_f.Read(&m_entries[i], sizeof(TRPEntry)) != sizeof(TRPEntry))
|
||||
if (!trp_f.read(m_entries[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -123,8 +115,3 @@ void TRPLoader::RenameEntry(const char *oldname, const char *newname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TRPLoader::Close()
|
||||
{
|
||||
trp_f.Close();
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
struct vfsStream;
|
||||
|
||||
struct TRPHeader
|
||||
{
|
||||
be_t<u32> trp_magic;
|
||||
@ -23,21 +21,19 @@ struct TRPEntry
|
||||
char padding[12];
|
||||
};
|
||||
|
||||
class TRPLoader
|
||||
class TRPLoader final
|
||||
{
|
||||
vfsStream& trp_f;
|
||||
const fs::file& trp_f;
|
||||
TRPHeader m_header;
|
||||
std::vector<TRPEntry> m_entries;
|
||||
|
||||
public:
|
||||
TRPLoader(vfsStream& f);
|
||||
~TRPLoader();
|
||||
virtual bool Install(std::string dest, bool show = false);
|
||||
virtual bool LoadHeader(bool show = false);
|
||||
TRPLoader(const fs::file& f);
|
||||
|
||||
virtual bool ContainsEntry(const char *filename);
|
||||
virtual void RemoveEntry(const char *filename);
|
||||
virtual void RenameEntry(const char *oldname, const char *newname);
|
||||
bool Install(const std::string& dest, bool show = false);
|
||||
bool LoadHeader(bool show = false);
|
||||
|
||||
virtual void Close();
|
||||
bool ContainsEntry(const char *filename);
|
||||
void RemoveEntry(const char *filename);
|
||||
void RenameEntry(const char *oldname, const char *newname);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user