mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 03:25:16 +00:00
PPU Analyzer: Firmware/import caller analysis and KLIC finding pass
This commit is contained in:
parent
e97bfecb7f
commit
e9648967e8
14 changed files with 380 additions and 96 deletions
|
@ -96,7 +96,7 @@ usz decrypt_binaries_t::decrypt(std::string_view klic_input)
|
|||
case "SCE\0"_u32:
|
||||
{
|
||||
// First KLIC is no KLIC
|
||||
elf_file = decrypt_self(std::move(elf_file), key_it != 0 ? reinterpret_cast<u8*>(&m_klics[key_it]) : nullptr);
|
||||
elf_file = decrypt_self(elf_file, key_it != 0 ? reinterpret_cast<u8*>(&m_klics[key_it]) : nullptr);
|
||||
|
||||
if (!elf_file)
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@ SELF_KEY::SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::str
|
|||
|
||||
KeyVault::KeyVault()
|
||||
{
|
||||
std::memcpy(klicensee_key, NP_KLIC_FREE, sizeof(klicensee_key));
|
||||
}
|
||||
|
||||
void KeyVault::LoadSelfLV0Keys()
|
||||
|
@ -751,15 +752,14 @@ SELF_KEY KeyVault::FindSelfKey(u32 type, u16 revision, u64 version)
|
|||
return key;
|
||||
}
|
||||
|
||||
void KeyVault::SetKlicenseeKey(u8* key)
|
||||
void KeyVault::SetKlicenseeKey(const u8* key)
|
||||
{
|
||||
klicensee_key = std::make_unique<u8[]>(0x10);
|
||||
memcpy(klicensee_key.get(), key, 0x10);
|
||||
std::memcpy(klicensee_key, key, 0x10);
|
||||
}
|
||||
|
||||
u8* KeyVault::GetKlicenseeKey() const
|
||||
const u8* KeyVault::GetKlicenseeKey() const
|
||||
{
|
||||
return klicensee_key.get();
|
||||
return klicensee_key;
|
||||
}
|
||||
|
||||
void rap_to_rif(unsigned char* rap, unsigned char* rif)
|
||||
|
|
|
@ -319,13 +319,13 @@ class KeyVault
|
|||
std::vector<SELF_KEY> sk_LDR_arr{};
|
||||
std::vector<SELF_KEY> sk_UNK7_arr{};
|
||||
std::vector<SELF_KEY> sk_NPDRM_arr{};
|
||||
std::unique_ptr<u8[]> klicensee_key{};
|
||||
u8 klicensee_key[16]{};
|
||||
|
||||
public:
|
||||
KeyVault();
|
||||
SELF_KEY FindSelfKey(u32 type, u16 revision, u64 version);
|
||||
void SetKlicenseeKey(u8* key);
|
||||
u8* GetKlicenseeKey() const;
|
||||
void SetKlicenseeKey(const u8* key);
|
||||
const u8* GetKlicenseeKey() const;
|
||||
|
||||
private:
|
||||
void LoadSelfLV0Keys();
|
||||
|
|
|
@ -1045,11 +1045,8 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
|
|||
}
|
||||
else if (npd->license == 3) // Free license.
|
||||
{
|
||||
// Use klicensee if available.
|
||||
if (key_v.GetKlicenseeKey())
|
||||
memcpy(npdrm_key, key_v.GetKlicenseeKey(), 0x10);
|
||||
else
|
||||
memcpy(npdrm_key, NP_KLIC_FREE, 0x10);
|
||||
// Use klicensee if available. (may be set to NP_KLIC_FREE if none is set)
|
||||
std::memcpy(npdrm_key, key_v.GetKlicenseeKey(), 0x10);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1085,7 +1082,7 @@ const NPD_HEADER* SELFDecrypter::GetNPDHeader() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool SELFDecrypter::LoadMetadata(u8* klic_key)
|
||||
bool SELFDecrypter::LoadMetadata(const u8* klic_key)
|
||||
{
|
||||
aes_context aes;
|
||||
const auto metadata_info = std::make_unique<u8[]>(sizeof(meta_info));
|
||||
|
@ -1319,11 +1316,11 @@ static bool IsDebugSelf(const fs::file& f)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool CheckDebugSelf(fs::file& s)
|
||||
static fs::file CheckDebugSelf(const fs::file& s)
|
||||
{
|
||||
if (s.size() < 0x18)
|
||||
{
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the key version.
|
||||
|
@ -1352,15 +1349,14 @@ static bool CheckDebugSelf(fs::file& s)
|
|||
e.write(buf, size);
|
||||
}
|
||||
|
||||
s = std::move(e);
|
||||
return true;
|
||||
return e;
|
||||
}
|
||||
|
||||
// Leave the file untouched.
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* out_info, bool require_encrypted)
|
||||
fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key, SelfAdditionalInfo* out_info)
|
||||
{
|
||||
if (out_info)
|
||||
{
|
||||
|
@ -1377,10 +1373,10 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou
|
|||
// Check SELF header first. Check for a debug SELF.
|
||||
if (elf_or_self.size() >= 4 && elf_or_self.read<u32>() == "SCE\0"_u32)
|
||||
{
|
||||
if (CheckDebugSelf(elf_or_self))
|
||||
if (fs::file res = CheckDebugSelf(elf_or_self))
|
||||
{
|
||||
// TODO: Decrypt
|
||||
return elf_or_self;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Check the ELF file class (32 or 64 bit).
|
||||
|
@ -1399,14 +1395,14 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou
|
|||
// Load and decrypt the SELF file metadata.
|
||||
if (!self_dec.LoadMetadata(klic_key))
|
||||
{
|
||||
self_log.error("Failed to load SELF file metadata!");
|
||||
(klic_key ? self_log.notice : self_log.error)("Failed to load SELF file metadata!");
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
// Decrypt the SELF file data.
|
||||
if (!self_dec.DecryptData())
|
||||
{
|
||||
self_log.error("Failed to decrypt SELF file data!");
|
||||
(klic_key ? self_log.notice : self_log.error)("Failed to decrypt SELF file data!");
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
|
@ -1414,12 +1410,7 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou
|
|||
return self_dec.MakeElf(isElf32);
|
||||
}
|
||||
|
||||
if (require_encrypted)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return elf_or_self;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key, NPD_HEADER* npd_out)
|
||||
|
|
|
@ -476,7 +476,7 @@ public:
|
|||
fs::file MakeElf(bool isElf32);
|
||||
bool LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info = nullptr);
|
||||
void ShowHeaders(bool isElf32);
|
||||
bool LoadMetadata(u8* klic_key);
|
||||
bool LoadMetadata(const u8* klic_key);
|
||||
bool DecryptData();
|
||||
bool DecryptNPDRM(u8 *metadata, u32 metadata_size);
|
||||
const NPD_HEADER* GetNPDHeader() const;
|
||||
|
@ -559,7 +559,7 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr, bool require_encrypted = false);
|
||||
fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr);
|
||||
bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr, NPD_HEADER* npd_out = nullptr);
|
||||
bool get_npdrm_self_header(const fs::file& self, NPD_HEADER& npd);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
LOG_CHANNEL(ppu_validator);
|
||||
|
||||
const ppu_decoder<ppu_itype> s_ppu_itype;
|
||||
extern const ppu_decoder<ppu_itype> g_ppu_itype;
|
||||
|
||||
template<>
|
||||
void fmt_class_string<ppu_attr>::format(std::string& out, u64 arg)
|
||||
|
@ -535,6 +535,7 @@ static constexpr struct const_tag{} is_const;
|
|||
static constexpr struct range_tag{} is_range;
|
||||
static constexpr struct min_value_tag{} minv;
|
||||
static constexpr struct max_value_tag{} maxv;
|
||||
static constexpr struct sign_bit_tag{} sign_bitv;
|
||||
static constexpr struct load_addr_tag{} load_addrv;
|
||||
|
||||
struct reg_state_t
|
||||
|
@ -548,13 +549,13 @@ struct reg_state_t
|
|||
// Check if state is a constant value
|
||||
bool operator()(const_tag) const
|
||||
{
|
||||
return value_range == 1 && bit_range == 0;
|
||||
return !is_loaded && value_range == 1 && bit_range == 0;
|
||||
}
|
||||
|
||||
// Check if state is a ranged value
|
||||
bool operator()(range_tag) const
|
||||
{
|
||||
return bit_range == 0;
|
||||
return !is_loaded && bit_range == 0;
|
||||
}
|
||||
|
||||
// Get minimum bound
|
||||
|
@ -569,6 +570,11 @@ struct reg_state_t
|
|||
return value_range ? (ge_than | bit_range) + value_range : 0;
|
||||
}
|
||||
|
||||
u64 operator()(sign_bit_tag) const
|
||||
{
|
||||
return value_range == 0 || (bit_range >> 63) || (ge_than + value_range - 1) >> 63 != (ge_than >> 63) ? u64{umax} : (ge_than >> 63);
|
||||
}
|
||||
|
||||
u64 operator()(load_addr_tag) const
|
||||
{
|
||||
return is_loaded ? ge_than : 0;
|
||||
|
@ -922,7 +928,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
{
|
||||
const ppu_opcode_t op{+range[index]};
|
||||
|
||||
switch (s_ppu_itype.decode(op.opcode))
|
||||
switch (g_ppu_itype.decode(op.opcode))
|
||||
{
|
||||
case ppu_itype::UNK:
|
||||
{
|
||||
|
@ -962,7 +968,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
// Register new function
|
||||
auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function_ext&
|
||||
{
|
||||
if (addr < start || addr >= end || s_ppu_itype.decode(*get_ptr<u32>(addr)) == ppu_itype::UNK)
|
||||
if (addr < start || addr >= end || g_ppu_itype.decode(*get_ptr<u32>(addr)) == ppu_itype::UNK)
|
||||
{
|
||||
if (!fmap.contains(addr))
|
||||
{
|
||||
|
@ -1337,7 +1343,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
const u32 iaddr = _ptr.addr();
|
||||
|
||||
const ppu_opcode_t op{*ptr};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
const ppu_itype::type type = g_ppu_itype.decode(op.opcode);
|
||||
|
||||
if ((type == ppu_itype::B || type == ppu_itype::BC) && op.lk && (!op.aa || verify_ref(iaddr)))
|
||||
{
|
||||
|
@ -1365,25 +1371,68 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
u32 addr = 0;
|
||||
u32 size = 0;
|
||||
u32 parent_block_idx = umax;
|
||||
u64 mapped_registers_mask = 0;
|
||||
u64 moved_registers_mask = 0;
|
||||
ppua_reg_mask_t mapped_registers_mask{0};
|
||||
ppua_reg_mask_t moved_registers_mask{0};
|
||||
};
|
||||
|
||||
// Block analysis workload
|
||||
std::vector<block_local_info_t> block_queue_storage;
|
||||
|
||||
bool is_function_caller_analysis = false;
|
||||
|
||||
// Main loop (func_queue may grow)
|
||||
for (usz i = 0; i < func_queue.size(); i++)
|
||||
for (usz i = 0; i <= func_queue.size(); i++)
|
||||
{
|
||||
if (i == func_queue.size())
|
||||
{
|
||||
if (is_function_caller_analysis)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Add callers of imported functions to be analyzed
|
||||
std::set<u32> added;
|
||||
|
||||
for (const auto& [stub_addr, _] : stub_addr_to_constant_state_of_registers)
|
||||
{
|
||||
auto it = fmap.upper_bound(stub_addr);
|
||||
|
||||
if (it == fmap.begin())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto stub_func = std::prev(it);
|
||||
|
||||
for (u32 caller : stub_func->second.callers)
|
||||
{
|
||||
ppu_function_ext& func = ::at32(fmap, caller);
|
||||
|
||||
if (func.attr.none_of(ppu_attr::no_size) && !func.blocks.empty() && !added.contains(caller))
|
||||
{
|
||||
added.emplace(caller);
|
||||
func_queue.emplace_back(::at32(fmap, caller));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (added.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
is_function_caller_analysis = true;
|
||||
}
|
||||
|
||||
if (check_aborted && check_aborted())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ppu_function_ext& func = func_queue[i].get();
|
||||
ppu_function_ext& func = func_queue[i];
|
||||
|
||||
// Fixup TOCs
|
||||
if (func.toc && func.toc != umax)
|
||||
if (!is_function_caller_analysis && func.toc && func.toc != umax)
|
||||
{
|
||||
// Fixup callers
|
||||
for (u32 addr : func.callers)
|
||||
|
@ -1407,7 +1456,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
const u32 iaddr = addr;
|
||||
|
||||
const ppu_opcode_t op{get_ref<u32>(iaddr)};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
const ppu_itype::type type = g_ppu_itype.decode(op.opcode);
|
||||
|
||||
if (type == ppu_itype::B || type == ppu_itype::BC)
|
||||
{
|
||||
|
@ -1453,7 +1502,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
}
|
||||
}
|
||||
|
||||
if (func.blocks.empty())
|
||||
if (!is_function_caller_analysis && func.blocks.empty())
|
||||
{
|
||||
// Special function analysis
|
||||
const vm::cptr<u32> _ptr = vm::cast(func.addr);
|
||||
|
@ -1760,7 +1809,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
if (parent_block != umax)
|
||||
{
|
||||
// Inherit loaded registers mask (lazily)
|
||||
block.mapped_registers_mask = ::at32(block_queue, parent_block).mapped_registers_mask;
|
||||
block.mapped_registers_mask.mask = ::at32(block_queue, parent_block).mapped_registers_mask.mask;
|
||||
}
|
||||
|
||||
return static_cast<u32>(block_queue.size() - 1);
|
||||
|
@ -1769,6 +1818,15 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
return umax;
|
||||
};
|
||||
|
||||
std::map<u32, u32> preserve_blocks;
|
||||
|
||||
if (is_function_caller_analysis)
|
||||
{
|
||||
preserve_blocks = std::move(func.blocks);
|
||||
func.blocks.clear();
|
||||
func.blocks.emplace(preserve_blocks.begin()->first, 0);
|
||||
}
|
||||
|
||||
for (auto& block : func.blocks)
|
||||
{
|
||||
if (!block.second && block.first < func_end)
|
||||
|
@ -1813,7 +1871,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
|
||||
auto is_reg_mapped = [&](u32 index)
|
||||
{
|
||||
return !!(block_queue[j].mapped_registers_mask & (u64{1} << index));
|
||||
return !!(block_queue[j].mapped_registers_mask.mask & (u64{1} << index));
|
||||
};
|
||||
|
||||
reg_state_t dummy_state{};
|
||||
|
@ -1824,7 +1882,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
|
||||
const usz reg_mask = u64{1} << index;
|
||||
|
||||
if (~block->moved_registers_mask & reg_mask)
|
||||
if (~block->moved_registers_mask.mask & reg_mask)
|
||||
{
|
||||
if ((j + 1) * 64 >= reg_state_storage.size())
|
||||
{
|
||||
|
@ -1836,11 +1894,11 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
usz begin_block = umax;
|
||||
|
||||
// Try searching for register origin
|
||||
if (block->mapped_registers_mask & reg_mask)
|
||||
if (block->mapped_registers_mask.mask & reg_mask)
|
||||
{
|
||||
for (u32 i = block->parent_block_idx; i != umax; i = block_queue[i].parent_block_idx)
|
||||
{
|
||||
if (~block_queue[i].moved_registers_mask & reg_mask)
|
||||
if (~block_queue[i].moved_registers_mask.mask & reg_mask)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -1860,8 +1918,8 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
reg_state_storage[64 * j + index] = make_unknown_reg_state();
|
||||
}
|
||||
|
||||
block->mapped_registers_mask |= reg_mask;
|
||||
block->moved_registers_mask |= reg_mask;
|
||||
block->mapped_registers_mask.mask |= reg_mask;
|
||||
block->moved_registers_mask.mask |= reg_mask;
|
||||
}
|
||||
|
||||
return reg_state_storage[64 * j + index];
|
||||
|
@ -1877,8 +1935,8 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
reg_state_storage[64 * block_index + index] = rhs;
|
||||
|
||||
const usz reg_mask = u64{1} << index;
|
||||
block_queue[block_index].mapped_registers_mask |= reg_mask;
|
||||
block_queue[block_index].moved_registers_mask |= reg_mask;
|
||||
block_queue[block_index].mapped_registers_mask.mask |= reg_mask;
|
||||
block_queue[block_index].moved_registers_mask.mask |= reg_mask;
|
||||
};
|
||||
|
||||
const auto unmap_reg = [&](u32 index)
|
||||
|
@ -1887,8 +1945,8 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
|
||||
const usz reg_mask = u64{1} << index;
|
||||
|
||||
block->mapped_registers_mask &= ~reg_mask;
|
||||
block->moved_registers_mask &= ~reg_mask;
|
||||
block->mapped_registers_mask.mask &= ~reg_mask;
|
||||
block->moved_registers_mask.mask &= ~reg_mask;
|
||||
};
|
||||
|
||||
enum : u32
|
||||
|
@ -1907,7 +1965,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
{
|
||||
const u32 iaddr = _ptr.addr();
|
||||
const ppu_opcode_t op{*advance(_ptr, ptr, 1)};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
const ppu_itype::type type = g_ppu_itype.decode(op.opcode);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
@ -1935,13 +1993,55 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
const bool is_call = op.lk && target != iaddr && target != _ptr.addr() && _ptr.addr() < func_end;
|
||||
const auto pfunc = is_call ? &add_func(target, 0, 0) : nullptr;
|
||||
|
||||
if (pfunc && pfunc->blocks.empty())
|
||||
if (pfunc && pfunc->blocks.empty() && !is_function_caller_analysis)
|
||||
{
|
||||
// Postpone analysis (no info)
|
||||
postpone_analysis = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_function_caller_analysis && is_call && !(pfunc->attr & ppu_attr::no_return))
|
||||
{
|
||||
while (is_function_caller_analysis)
|
||||
{
|
||||
// Verify that it is the call to the imported function (may be more than one)
|
||||
const auto it = stub_addr_to_constant_state_of_registers.lower_bound(target);
|
||||
|
||||
if (it == stub_addr_to_constant_state_of_registers.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto next_func = fmap.upper_bound(it->first);
|
||||
|
||||
if (next_func == fmap.begin())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto stub_func = std::prev(next_func);
|
||||
|
||||
if (stub_func->first == target)
|
||||
{
|
||||
// It is
|
||||
// Now, mine register state
|
||||
// Currently only of R3
|
||||
|
||||
if (is_reg_mapped(3))
|
||||
{
|
||||
const reg_state_t& value = get_reg(3);
|
||||
|
||||
if (value(is_const))
|
||||
{
|
||||
it->second.emplace_back(ppua_reg_mask_t{ 1u << 3 }, value(minv) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add next block if necessary
|
||||
if ((is_call && !(pfunc->attr & ppu_attr::no_return)) || (type == ppu_itype::BC && (op.bo & 0x14) != 0x14))
|
||||
{
|
||||
|
@ -1993,7 +2093,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
store_block_reg(next_idx, lhs_cr_state, lhs_state);
|
||||
store_block_reg(next_idx, rhs_cr_state, rhs_state);
|
||||
|
||||
const u64 reg_mask = block_queue[j].mapped_registers_mask;
|
||||
const u64 reg_mask = block_queue[j].mapped_registers_mask.mask;
|
||||
|
||||
for (u32 bit = std::countr_zero(reg_mask); bit < 64 && reg_mask & (u64{1} << bit);
|
||||
bit += 1, bit = std::countr_zero(reg_mask >> (bit % 64)) + bit)
|
||||
|
@ -2024,7 +2124,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
else if (is_call || target < func.addr || target >= func_end)
|
||||
{
|
||||
// Add function call (including obvious tail call)
|
||||
add_func(target, 0, 0);
|
||||
add_func(target, 0, func.addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2291,6 +2391,65 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
|
||||
continue;
|
||||
}
|
||||
case ppu_itype::LWZ:
|
||||
{
|
||||
const bool is_load_from_toc = (is_function_caller_analysis && op.ra == 2u && func.toc && func.toc != umax);
|
||||
|
||||
if (is_load_from_toc || is_reg_mapped(op.rd) || is_reg_mapped(op.ra))
|
||||
{
|
||||
const reg_state_t ra = get_reg(op.ra);
|
||||
auto& rd = get_reg(op.rd);
|
||||
|
||||
rd = {};
|
||||
rd.tag = reg_tag_allocator++;
|
||||
rd.is_loaded = true;
|
||||
|
||||
reg_state_t const_offs{};
|
||||
const_offs.load_const(op.simm16);
|
||||
|
||||
reg_state_t toc_offset{};
|
||||
toc_offset.load_const(func.toc);
|
||||
|
||||
const reg_state_t& off_ra = is_load_from_toc ? toc_offset : ra;
|
||||
|
||||
rd.ge_than = const_offs(minv);
|
||||
|
||||
const bool is_negative = const_offs(sign_bitv) == 1u;
|
||||
|
||||
const bool is_offset_test_ok = is_negative
|
||||
? (0 - const_offs(minv) <= off_ra(minv) && off_ra(minv) + const_offs(minv) < segs_end)
|
||||
: (off_ra(minv) < segs_end && const_offs(minv) < segs_end - off_ra(minv));
|
||||
|
||||
if (off_ra(minv) < off_ra(maxv) && is_offset_test_ok)
|
||||
{
|
||||
rd.ge_than += off_ra(minv);
|
||||
|
||||
const bool is_range_end_test_ok = is_negative
|
||||
? (off_ra(maxv) + const_offs(minv) <= segs_end)
|
||||
: (off_ra(maxv) - 1 < segs_end - 1 && const_offs(minv) <= segs_end - off_ra(maxv));
|
||||
|
||||
if (is_range_end_test_ok)
|
||||
{
|
||||
rd.value_range = off_ra.value_range;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_load_from_toc)
|
||||
{
|
||||
if (rd.value_range == 1)
|
||||
{
|
||||
// Try to load a constant value from data segment
|
||||
if (auto val_ptr = get_ptr<u32>(static_cast<u32>(rd.ge_than)))
|
||||
{
|
||||
rd = {};
|
||||
rd.load_const(*val_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
case ppu_itype::LWZX:
|
||||
case ppu_itype::LDX: // TODO: Confirm if LDX can appear in jumptable branching (probably in LV1 applications such as ps2_emu)
|
||||
{
|
||||
|
@ -2311,6 +2470,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
// Register possible jumptable offset
|
||||
auto& rd = get_reg(op.rd);
|
||||
rd = {};
|
||||
rd.tag = reg_tag_allocator++;
|
||||
rd.is_loaded = true;
|
||||
|
||||
const reg_state_t& const_reg = is_ra ? ra : rb;
|
||||
|
@ -2451,6 +2611,19 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
}
|
||||
}
|
||||
|
||||
if (!preserve_blocks.empty())
|
||||
{
|
||||
ensure(func.blocks.size() == preserve_blocks.size());
|
||||
|
||||
for (auto fit = func.blocks.begin(), pit = preserve_blocks.begin(); fit != func.blocks.end(); fit++, pit++)
|
||||
{
|
||||
// Ensure block addresses match
|
||||
ensure(fit->first == pit->first);
|
||||
}
|
||||
|
||||
func.blocks = std::move(preserve_blocks);
|
||||
}
|
||||
|
||||
if (postpone_analysis)
|
||||
{
|
||||
// Block aborted: abort function, postpone
|
||||
|
@ -2501,7 +2674,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
{
|
||||
const u32 iaddr = _ptr.addr();
|
||||
const ppu_opcode_t op{get_ref<u32>(_ptr++)};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
const ppu_itype::type type = g_ppu_itype.decode(op.opcode);
|
||||
|
||||
if (type == ppu_itype::B || type == ppu_itype::BC)
|
||||
{
|
||||
|
@ -2574,7 +2747,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
{
|
||||
const u32 addr = _ptr.addr();
|
||||
const ppu_opcode_t op{get_ref<u32>(_ptr++)};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
const ppu_itype::type type = g_ppu_itype.decode(op.opcode);
|
||||
|
||||
if (type == ppu_itype::UNK)
|
||||
{
|
||||
|
@ -2813,7 +2986,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
{
|
||||
const ppu_opcode_t op{get_ref<u32>(i_pos)};
|
||||
|
||||
switch (auto type = s_ppu_itype.decode(op.opcode))
|
||||
switch (auto type = g_ppu_itype.decode(op.opcode))
|
||||
{
|
||||
case ppu_itype::UNK:
|
||||
case ppu_itype::ECIWX:
|
||||
|
@ -2884,7 +3057,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
}
|
||||
|
||||
const ppu_opcode_t test_op{get_ref<u32>(target)};
|
||||
const auto type0 = s_ppu_itype.decode(test_op.opcode);
|
||||
const auto type0 = g_ppu_itype.decode(test_op.opcode);
|
||||
|
||||
if (type0 == ppu_itype::UNK)
|
||||
{
|
||||
|
@ -2906,7 +3079,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
|
|||
break;
|
||||
}
|
||||
|
||||
const auto type1 = s_ppu_itype.decode(get_ref<u32>(target + 4));
|
||||
const auto type1 = g_ppu_itype.decode(get_ref<u32>(target + 4));
|
||||
|
||||
if (type1 == ppu_itype::UNK)
|
||||
{
|
||||
|
|
|
@ -106,6 +106,11 @@ struct ppu_segment
|
|||
void* ptr{};
|
||||
};
|
||||
|
||||
struct ppua_reg_mask_t
|
||||
{
|
||||
u64 mask;
|
||||
};
|
||||
|
||||
// PPU Module Information
|
||||
template <typename Type>
|
||||
struct ppu_module : public Type
|
||||
|
@ -138,6 +143,8 @@ struct ppu_module : public Type
|
|||
ppu_module* parent = nullptr; // For compilation: refers to original structure (is whole, not partitioned)
|
||||
std::pair<u32, u32> local_bounds{0, u32{umax}}; // Module addresses range
|
||||
std::shared_ptr<std::pair<u32, u32>> jit_bounds; // JIT instance modules addresses range
|
||||
std::unordered_map<u32, void*> imports; // Imports information for release upon unload (TODO: OVL implementation!)
|
||||
std::map<u32, std::vector<std::pair<ppua_reg_mask_t, u64>>> stub_addr_to_constant_state_of_registers; // Tells possible constant states of registers of functions
|
||||
bool is_relocatable = false; // Is code relocatable(?)
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -71,6 +71,25 @@ extern u32 ppu_generate_id(std::string_view name)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void select_from_nids_scenpdrm_addrs(std::map<u32, std::vector<std::pair<ppua_reg_mask_t, u64>>>& result, const std::unordered_map<u32, u32>& fnid_to_use_addr)
|
||||
{
|
||||
static const u32 fnids_list[] =
|
||||
{
|
||||
ppu_generate_id("sceNpDrmProcessExitSpawn"),
|
||||
ppu_generate_id("sceNpDrmProcessExitSpawn2"),
|
||||
ppu_generate_id("sceNpDrmIsAvailable"),
|
||||
ppu_generate_id("sceNpDrmIsAvailable2"),
|
||||
};
|
||||
|
||||
for (const auto& [nid, use] : fnid_to_use_addr)
|
||||
{
|
||||
if (std::count(std::begin(fnids_list), std::end(fnids_list), nid))
|
||||
{
|
||||
result.emplace(use, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ppu_static_module::ppu_static_module(const char* name)
|
||||
: name(name)
|
||||
{
|
||||
|
@ -157,9 +176,6 @@ struct ppu_linkage_info
|
|||
// FNID -> (export; [imports...])
|
||||
std::map<u32, info> functions{};
|
||||
std::map<u32, info> variables{};
|
||||
|
||||
// Obsolete
|
||||
bool imported = false;
|
||||
};
|
||||
|
||||
// Module map
|
||||
|
@ -940,9 +956,12 @@ static auto ppu_load_exports(const ppu_module<lv2_obj>& _module, ppu_linkage_inf
|
|||
return result;
|
||||
}
|
||||
|
||||
static auto ppu_load_imports(const ppu_module<lv2_obj>& _module, std::vector<ppu_reloc>& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end)
|
||||
using import_result_t = std::pair<std::unordered_map<u32, void*>, std::unordered_map<u32, u32>>;
|
||||
|
||||
static import_result_t ppu_load_imports(const ppu_module<lv2_obj>& _module, std::vector<ppu_reloc>& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end)
|
||||
{
|
||||
std::unordered_map<u32, void*> result;
|
||||
import_result_t result;
|
||||
auto& [import_table, nid_to_use_addr] = result;
|
||||
|
||||
std::lock_guard lock(link->mutex);
|
||||
|
||||
|
@ -976,12 +995,18 @@ static auto ppu_load_imports(const ppu_module<lv2_obj>& _module, std::vector<ppu
|
|||
ppu_loader.notice("**** %s import: [%s] (0x%08x) -> 0x%x", module_name, ppu_get_function_name(module_name, fnid), fnid, fstub);
|
||||
|
||||
// Function linkage info
|
||||
auto& flink = link->modules[module_name].functions[fnid];
|
||||
auto& flink = mlink.functions[fnid];
|
||||
|
||||
// Add new import
|
||||
result.emplace(faddr, &flink);
|
||||
import_table.emplace(faddr, &flink);
|
||||
flink.imports.emplace(faddr);
|
||||
mlink.imported = true;
|
||||
|
||||
// Check address
|
||||
// TODO: The address of use should be extracted from analyser instead
|
||||
if (fstub && fstub >= _module.segs[0].addr && fstub <= _module.segs[0].addr + _module.segs[0].size)
|
||||
{
|
||||
nid_to_use_addr.emplace(fnid, fstub);
|
||||
}
|
||||
|
||||
// Link address (special HLE function by default)
|
||||
const u32 link_addr = flink.export_addr ? flink.export_addr : g_fxo->get<ppu_function_manager>().addr;
|
||||
|
@ -992,7 +1017,7 @@ static auto ppu_load_imports(const ppu_module<lv2_obj>& _module, std::vector<ppu
|
|||
// Patch refs if necessary (0x2000 seems to be correct flag indicating the presence of additional info)
|
||||
if (const u32 frefs = (lib.attributes & 0x2000) ? +_module.get_ref<u32>(fnids, i + lib.num_func) : 0)
|
||||
{
|
||||
result.emplace(frefs, &flink);
|
||||
import_table.emplace(frefs, &flink);
|
||||
flink.frefss.emplace(frefs);
|
||||
ppu_patch_refs(_module, &relocs, frefs, link_addr);
|
||||
}
|
||||
|
@ -1010,12 +1035,11 @@ static auto ppu_load_imports(const ppu_module<lv2_obj>& _module, std::vector<ppu
|
|||
ppu_loader.notice("**** %s import: &[%s] (ref=*0x%x)", module_name, ppu_get_variable_name(module_name, vnid), vref);
|
||||
|
||||
// Variable linkage info
|
||||
auto& vlink = link->modules[module_name].variables[vnid];
|
||||
auto& vlink = mlink.variables[vnid];
|
||||
|
||||
// Add new import
|
||||
result.emplace(vref, &vlink);
|
||||
import_table.emplace(vref, &vlink);
|
||||
vlink.imports.emplace(vref);
|
||||
mlink.imported = true;
|
||||
|
||||
// Link if available
|
||||
ppu_patch_refs(_module, &relocs, vref, vlink.export_addr);
|
||||
|
@ -1838,10 +1862,13 @@ shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, c
|
|||
|
||||
ppu_loader.warning("Library %s (rtoc=0x%x):", lib_name, lib_info->toc);
|
||||
|
||||
std::unordered_map<u32, u32> nid_to_use_addr;
|
||||
ppu_linkage_info dummy{};
|
||||
|
||||
prx->specials = ppu_load_exports(*prx, virtual_load ? &dummy : &link, prx->exports_start, prx->exports_end, true, &exported_funcs);
|
||||
prx->imports = ppu_load_imports(*prx, prx->relocs, virtual_load ? &dummy : &link, lib_info->imports_start, lib_info->imports_end);
|
||||
|
||||
std::tie(prx->imports, nid_to_use_addr) = ppu_load_imports(*prx, prx->relocs, virtual_load ? &dummy : &link, lib_info->imports_start, lib_info->imports_end);
|
||||
select_from_nids_scenpdrm_addrs(prx->stub_addr_to_constant_state_of_registers, nid_to_use_addr);
|
||||
|
||||
if (virtual_load)
|
||||
{
|
||||
|
@ -2450,10 +2477,13 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str
|
|||
return false;
|
||||
}
|
||||
|
||||
std::unordered_map<u32, u32> nid_to_use_addr;
|
||||
ppu_linkage_info dummy{};
|
||||
|
||||
ppu_load_exports(_main, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end);
|
||||
ppu_load_imports(_main, _main.relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
|
||||
|
||||
std::tie(std::ignore, nid_to_use_addr) = ppu_load_imports(_main, _main.relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
|
||||
select_from_nids_scenpdrm_addrs(_main.stub_addr_to_constant_state_of_registers, nid_to_use_addr);
|
||||
|
||||
std::stable_sort(_main.relocs.begin(), _main.relocs.end());
|
||||
}
|
||||
|
@ -3061,10 +3091,14 @@ std::pair<shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_ob
|
|||
fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic);
|
||||
}
|
||||
|
||||
std::unordered_map<u32, u32> nid_to_use_addr;
|
||||
ppu_linkage_info dummy{};
|
||||
|
||||
ppu_load_exports(*ovlm, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end);
|
||||
ppu_load_imports(*ovlm, ovlm->relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
|
||||
|
||||
std::tie(std::ignore, nid_to_use_addr) = ppu_load_imports(*ovlm, ovlm->relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
|
||||
select_from_nids_scenpdrm_addrs(ovlm->stub_addr_to_constant_state_of_registers, nid_to_use_addr);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -4162,6 +4162,52 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
|||
const u32 software_thread_limit = std::min<u32>(g_cfg.core.llvm_threads ? g_cfg.core.llvm_threads : u32{umax}, ::size32(file_queue));
|
||||
const u32 cpu_thread_limit = utils::get_thread_count() > 8u ? std::max<u32>(utils::get_thread_count(), 2) - 1 : utils::get_thread_count(); // One LLVM thread less
|
||||
|
||||
std::vector<u128> decrypt_klics;
|
||||
|
||||
if (loaded_modules)
|
||||
{
|
||||
for (auto mod : *loaded_modules)
|
||||
{
|
||||
for (const auto& [stub, data_vec] : mod->stub_addr_to_constant_state_of_registers)
|
||||
{
|
||||
if (decrypt_klics.size() >= 4u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (const auto& [reg_mask, constant_value] : data_vec)
|
||||
{
|
||||
if (decrypt_klics.size() >= 4u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (constant_value > u32{umax})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// R3 - first argument
|
||||
if (reg_mask.mask & (1u << 3))
|
||||
{
|
||||
// Sizeof KLIC
|
||||
if (auto klic_ptr = mod->get_ptr<const u8>(static_cast<u32>(constant_value), 16))
|
||||
{
|
||||
// Try to read from that address
|
||||
if (const u128 klic_value = read_from_ptr<u128>(klic_ptr))
|
||||
{
|
||||
if (!std::count_if(decrypt_klics.begin(), decrypt_klics.end(), FN(std::memcmp(&x, &klic_value, 16) == 0)))
|
||||
{
|
||||
decrypt_klics.emplace_back(klic_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
named_thread_group workers("SPRX Worker ", std::min<u32>(software_thread_limit, cpu_thread_limit), [&]
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
|
@ -4211,8 +4257,23 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
|||
fmt::append(path, "_x%x", off);
|
||||
}
|
||||
|
||||
// Some files may fail to decrypt due to the lack of klic
|
||||
src = decrypt_self(std::move(src));
|
||||
for (usz i = 0;; i++)
|
||||
{
|
||||
if (i > decrypt_klics.size())
|
||||
{
|
||||
src.close();
|
||||
break;
|
||||
}
|
||||
|
||||
// Some files may fail to decrypt due to the lack of klic
|
||||
u128 key = i == decrypt_klics.size() ? u128{} : decrypt_klics[i];
|
||||
|
||||
if (auto result = decrypt_self(src, i == decrypt_klics.size() ? nullptr : reinterpret_cast<const u8*>(&key)))
|
||||
{
|
||||
src = std::move(result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src)
|
||||
{
|
||||
|
@ -4380,8 +4441,23 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
|||
continue;
|
||||
}
|
||||
|
||||
// Some files may fail to decrypt due to the lack of klic
|
||||
src = decrypt_self(std::move(src), nullptr, nullptr, true);
|
||||
for (usz i = 0;; i++)
|
||||
{
|
||||
if (i > decrypt_klics.size())
|
||||
{
|
||||
src.close();
|
||||
break;
|
||||
}
|
||||
|
||||
// Some files may fail to decrypt due to the lack of klic
|
||||
u128 key = i == decrypt_klics.size() ? u128{} : decrypt_klics[i];
|
||||
|
||||
if (auto result = decrypt_self(src, i == decrypt_klics.size() ? nullptr : reinterpret_cast<const u8*>(&key)))
|
||||
{
|
||||
src = std::move(result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!src)
|
||||
{
|
||||
|
@ -4484,6 +4560,7 @@ extern void ppu_initialize()
|
|||
}
|
||||
|
||||
std::vector<ppu_module<lv2_obj>*> module_list;
|
||||
module_list.emplace_back(&g_fxo->get<main_ppu_module<lv2_obj>>());
|
||||
|
||||
const std::string firmware_sprx_path = vfs::get("/dev_flash/sys/external/");
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ static error_code overlay_load_module(vm::ptr<u32> ovlmid, const std::string& vp
|
|||
|
||||
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
|
||||
|
||||
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
|
||||
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic));
|
||||
|
||||
if (!src)
|
||||
{
|
||||
|
|
|
@ -265,7 +265,7 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
|
|||
|
||||
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
|
||||
|
||||
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
|
||||
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic));
|
||||
|
||||
if (!src)
|
||||
{
|
||||
|
|
|
@ -192,7 +192,6 @@ struct lv2_prx final : ppu_module<lv2_obj>
|
|||
shared_mutex mutex;
|
||||
|
||||
std::unordered_map<u32, u32> specials;
|
||||
std::unordered_map<u32, void*> imports;
|
||||
|
||||
vm::ptr<s32(u32 argc, vm::ptr<void> argv)> start = vm::null;
|
||||
vm::ptr<s32(u32 argc, vm::ptr<void> argv)> stop = vm::null;
|
||||
|
|
|
@ -1747,7 +1747,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
|
||||
fs::file src{path};
|
||||
|
||||
src = decrypt_self(std::move(src));
|
||||
src = decrypt_self(src);
|
||||
|
||||
const ppu_exec_object obj = src;
|
||||
|
||||
|
@ -1764,6 +1764,8 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
|
||||
g_fxo->init<named_thread>("SPRX Loader"sv, [this, dir_queue]() mutable
|
||||
{
|
||||
std::vector<ppu_module<lv2_obj>*> mod_list;
|
||||
|
||||
if (auto& _main = *ensure(g_fxo->try_get<main_ppu_module<lv2_obj>>()); !_main.path.empty())
|
||||
{
|
||||
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, std::vector<u32>{}, [](){ return Emu.IsStopped(); }))
|
||||
|
@ -1773,6 +1775,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
|
||||
Emu.ConfigurePPUCache();
|
||||
ppu_initialize(_main);
|
||||
mod_list.emplace_back(&_main);
|
||||
}
|
||||
|
||||
if (Emu.IsStopped())
|
||||
|
@ -1780,7 +1783,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
return;
|
||||
}
|
||||
|
||||
ppu_precompile(dir_queue, nullptr);
|
||||
ppu_precompile(dir_queue, mod_list.empty() ? nullptr : &mod_list);
|
||||
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
|
@ -2238,7 +2241,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
|||
{
|
||||
// Decrypt SELF
|
||||
had_been_decrypted = true;
|
||||
elf_file = decrypt_self(std::move(elf_file), klic.empty() ? nullptr : reinterpret_cast<u8*>(&klic[0]), &g_ps3_process_info.self_info);
|
||||
elf_file = decrypt_self(elf_file, klic.empty() ? nullptr : reinterpret_cast<u8*>(&klic[0]), &g_ps3_process_info.self_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -566,14 +566,14 @@ public:
|
|||
}
|
||||
else if constexpr (TupleAlike<T>)
|
||||
{
|
||||
constexpr usz tup_size = c_tup_size<type>;
|
||||
constexpr int tup_size = c_tup_size<type>;
|
||||
|
||||
static_assert(tup_size == 2 || tup_size == 4, "Unimplemented tuple serialization!");
|
||||
|
||||
using first_t = std::remove_cvref_t<decltype(std::get<std::min<usz>(0, tup_size - 1)>(std::declval<type&>()))>;
|
||||
using second_t = std::remove_cvref_t<decltype(std::get<std::min<usz>(1, tup_size - 1)>(std::declval<type&>()))>;
|
||||
using third_t = std::remove_cvref_t<decltype(std::get<std::min<usz>(2, tup_size - 1)>(std::declval<type&>()))>;
|
||||
using fourth_t = std::remove_cvref_t<decltype(std::get<std::min<usz>(3, tup_size - 1)>(std::declval<type&>()))>;
|
||||
using first_t = typename std::tuple_element<std::min(0, tup_size - 1), type>::type;
|
||||
using second_t = typename std::tuple_element<std::min(1, tup_size - 1), type>::type;
|
||||
using third_t = typename std::tuple_element<std::min(2, tup_size - 1), type>::type;
|
||||
using fourth_t = typename std::tuple_element<std::min(3, tup_size - 1), type>::type;
|
||||
|
||||
first_t first = this->operator first_t();
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue