mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 05:45:38 +00:00
System: Rewrite EXE override/loading
Relies on POST=7 as a kernel initialization indicator, instead of patching the BIOS. Fixes EXE loading with OpenBIOS and PS2 BIOS, and fast boot getting baked into save states.
This commit is contained in:
parent
7b99fcbbf3
commit
6fe0c986fa
|
@ -278,7 +278,7 @@ std::string Achievements::GetGameHash(CDImage* image)
|
|||
BIOS::PSEXEHeader header = {};
|
||||
if (executable_data.size() >= sizeof(header))
|
||||
std::memcpy(&header, executable_data.data(), sizeof(header));
|
||||
if (!BIOS::IsValidPSExeHeader(header, static_cast<u32>(executable_data.size())))
|
||||
if (!BIOS::IsValidPSExeHeader(header, executable_data.size()))
|
||||
{
|
||||
ERROR_LOG("PS-EXE header is invalid in '{}' ({} bytes)", executable_name, executable_data.size());
|
||||
return {};
|
||||
|
|
|
@ -19,6 +19,7 @@ Log_SetChannel(BIOS);
|
|||
|
||||
namespace BIOS {
|
||||
static const ImageInfo* GetInfoForHash(const std::span<u8> image, const ImageInfo::Hash& hash);
|
||||
static void PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
|
||||
|
||||
static constexpr ImageInfo::Hash MakeHashFromString(const char str[])
|
||||
{
|
||||
|
@ -255,54 +256,16 @@ bool BIOS::PatchBIOSFastBoot(u8* image, u32 image_size)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BIOS::PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp)
|
||||
{
|
||||
#define PATCH(offset, value) PatchBIOS(image, image_size, (offset), (value))
|
||||
|
||||
// pc has to be done first because we can't load it in the delay slot
|
||||
PATCH(0xBFC06FF0, UINT32_C(0x3C080000) | r_pc >> 16); // lui $t0, (r_pc >> 16)
|
||||
PATCH(0xBFC06FF4, UINT32_C(0x35080000) | (r_pc & UINT32_C(0xFFFF))); // ori $t0, $t0, (r_pc & 0xFFFF)
|
||||
PATCH(0xBFC06FF8, UINT32_C(0x3C1C0000) | r_gp >> 16); // lui $gp, (r_gp >> 16)
|
||||
PATCH(0xBFC06FFC, UINT32_C(0x379C0000) | (r_gp & UINT32_C(0xFFFF))); // ori $gp, $gp, (r_gp & 0xFFFF)
|
||||
|
||||
if (r_sp != 0)
|
||||
{
|
||||
PATCH(0xBFC07000, UINT32_C(0x3C1D0000) | r_sp >> 16); // lui $sp, (r_sp >> 16)
|
||||
PATCH(0xBFC07004, UINT32_C(0x37BD0000) | (r_sp & UINT32_C(0xFFFF))); // ori $sp, $sp, (r_sp & 0xFFFF)
|
||||
}
|
||||
else
|
||||
{
|
||||
PATCH(0xBFC07000, UINT32_C(0x00000000)); // nop
|
||||
PATCH(0xBFC07004, UINT32_C(0x00000000)); // nop
|
||||
}
|
||||
if (r_fp != 0)
|
||||
{
|
||||
PATCH(0xBFC07008, UINT32_C(0x3C1E0000) | r_fp >> 16); // lui $fp, (r_fp >> 16)
|
||||
PATCH(0xBFC0700C, UINT32_C(0x01000008)); // jr $t0
|
||||
PATCH(0xBFC07010, UINT32_C(0x37DE0000) | (r_fp & UINT32_C(0xFFFF))); // ori $fp, $fp, (r_fp & 0xFFFF)
|
||||
}
|
||||
else
|
||||
{
|
||||
PATCH(0xBFC07008, UINT32_C(0x00000000)); // nop
|
||||
PATCH(0xBFC0700C, UINT32_C(0x01000008)); // jr $t0
|
||||
PATCH(0xBFC07010, UINT32_C(0x00000000)); // nop
|
||||
}
|
||||
|
||||
#undef PATCH
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BIOS::IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size)
|
||||
bool BIOS::IsValidPSExeHeader(const PSEXEHeader& header, size_t file_size)
|
||||
{
|
||||
static constexpr char expected_id[] = {'P', 'S', '-', 'X', ' ', 'E', 'X', 'E'};
|
||||
if (std::memcmp(header.id, expected_id, sizeof(expected_id)) != 0)
|
||||
if (file_size < sizeof(expected_id) || std::memcmp(header.id, expected_id, sizeof(expected_id)) != 0)
|
||||
return false;
|
||||
|
||||
if ((header.file_size + sizeof(PSEXEHeader)) > file_size)
|
||||
{
|
||||
WARNING_LOG("Incorrect file size in PS-EXE header: {} bytes should not be greater than {} bytes", header.file_size,
|
||||
static_cast<unsigned>(file_size - sizeof(PSEXEHeader)));
|
||||
file_size - sizeof(PSEXEHeader));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -70,12 +70,9 @@ std::optional<Image> LoadImageFromFile(const char* filename, Error* error);
|
|||
|
||||
bool IsValidBIOSForRegion(ConsoleRegion console_region, ConsoleRegion bios_region);
|
||||
|
||||
void PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
|
||||
|
||||
bool PatchBIOSFastBoot(u8* image, u32 image_size);
|
||||
bool PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp);
|
||||
|
||||
bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size);
|
||||
bool IsValidPSExeHeader(const PSEXEHeader& header, size_t file_size);
|
||||
DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header);
|
||||
|
||||
/// Loads the BIOS image for the specified region.
|
||||
|
|
200
src/core/bus.cpp
200
src/core/bus.cpp
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "bus.h"
|
||||
#include "bios.h"
|
||||
#include "cdrom.h"
|
||||
#include "cpu_code_cache.h"
|
||||
#include "cpu_core.h"
|
||||
|
@ -13,6 +14,7 @@
|
|||
#include "interrupt_controller.h"
|
||||
#include "mdec.h"
|
||||
#include "pad.h"
|
||||
#include "psf_loader.h"
|
||||
#include "settings.h"
|
||||
#include "sio.h"
|
||||
#include "spu.h"
|
||||
|
@ -20,14 +22,17 @@
|
|||
#include "timers.h"
|
||||
#include "timing_event.h"
|
||||
|
||||
#include "util/cd_image.h"
|
||||
#include "util/state_wrapper.h"
|
||||
|
||||
#include "common/align.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/intrin.h"
|
||||
#include "common/log.h"
|
||||
#include "common/memmap.h"
|
||||
#include "common/path.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <tuple>
|
||||
|
@ -146,6 +151,8 @@ static std::vector<std::pair<u8*, size_t>> s_fastmem_ram_views;
|
|||
|
||||
static u8** s_fastmem_lut = nullptr;
|
||||
|
||||
static bool s_kernel_initialize_hook_run = false;
|
||||
|
||||
static void SetRAMSize(bool enable_8mb_ram);
|
||||
|
||||
static std::tuple<TickCount, TickCount, TickCount> CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
|
||||
|
@ -155,6 +162,9 @@ static u8* GetLUTFastmemPointer(u32 address, u8* ram_ptr);
|
|||
|
||||
static void SetRAMPageWritable(u32 page_index, bool writable);
|
||||
|
||||
static void KernelInitializedHook();
|
||||
static bool SideloadEXE(const std::string& path, Error* error);
|
||||
|
||||
static void SetHandlers();
|
||||
static void UpdateMappedRAMSize();
|
||||
|
||||
|
@ -348,6 +358,7 @@ void Bus::Reset()
|
|||
s_MEMCTRL.exp2_delay_size.bits = 0x00070777;
|
||||
s_MEMCTRL.common_delay.bits = 0x00031125;
|
||||
g_ram_code_bits = {};
|
||||
s_kernel_initialize_hook_run = false;
|
||||
RecalculateMemoryTimings();
|
||||
|
||||
// Avoid remapping if unchanged.
|
||||
|
@ -358,35 +369,6 @@ void Bus::Reset()
|
|||
}
|
||||
}
|
||||
|
||||
void Bus::AddTTYCharacter(char ch)
|
||||
{
|
||||
if (ch == '\r')
|
||||
{
|
||||
}
|
||||
else if (ch == '\n')
|
||||
{
|
||||
if (!s_tty_line_buffer.empty())
|
||||
{
|
||||
Log::FastWrite("TTY", "", LOGLEVEL_INFO, "\033[1;34m{}\033[0m", s_tty_line_buffer);
|
||||
#ifdef _DEBUG
|
||||
if (CPU::IsTraceEnabled())
|
||||
CPU::WriteToExecutionLog("TTY: %s\n", s_tty_line_buffer.c_str());
|
||||
#endif
|
||||
}
|
||||
s_tty_line_buffer.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_tty_line_buffer += ch;
|
||||
}
|
||||
}
|
||||
|
||||
void Bus::AddTTYString(std::string_view str)
|
||||
{
|
||||
for (char ch : str)
|
||||
AddTTYCharacter(ch);
|
||||
}
|
||||
|
||||
bool Bus::DoState(StateWrapper& sw)
|
||||
{
|
||||
u32 ram_size = g_ram_size;
|
||||
|
@ -420,12 +402,10 @@ bool Bus::DoState(StateWrapper& sw)
|
|||
UpdateMappedRAMSize();
|
||||
|
||||
sw.Do(&s_tty_line_buffer);
|
||||
return !sw.HasError();
|
||||
}
|
||||
|
||||
void Bus::SetExpansionROM(std::vector<u8> data)
|
||||
{
|
||||
s_exp1_rom = std::move(data);
|
||||
sw.DoEx(&s_kernel_initialize_hook_run, 68, true);
|
||||
|
||||
return !sw.HasError();
|
||||
}
|
||||
|
||||
std::tuple<TickCount, TickCount, TickCount> Bus::CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay)
|
||||
|
@ -863,6 +843,146 @@ std::optional<PhysicalMemoryAddress> Bus::SearchMemory(PhysicalMemoryAddress sta
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Bus::SetExpansionROM(std::vector<u8> data)
|
||||
{
|
||||
s_exp1_rom = std::move(data);
|
||||
}
|
||||
|
||||
void Bus::AddTTYCharacter(char ch)
|
||||
{
|
||||
if (ch == '\r')
|
||||
{
|
||||
}
|
||||
else if (ch == '\n')
|
||||
{
|
||||
if (!s_tty_line_buffer.empty())
|
||||
{
|
||||
Log::FastWrite("TTY", "", LOGLEVEL_INFO, "\033[1;34m{}\033[0m", s_tty_line_buffer);
|
||||
#ifdef _DEBUG
|
||||
if (CPU::IsTraceEnabled())
|
||||
CPU::WriteToExecutionLog("TTY: %s\n", s_tty_line_buffer.c_str());
|
||||
#endif
|
||||
}
|
||||
s_tty_line_buffer.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_tty_line_buffer += ch;
|
||||
}
|
||||
}
|
||||
|
||||
void Bus::AddTTYString(std::string_view str)
|
||||
{
|
||||
for (char ch : str)
|
||||
AddTTYCharacter(ch);
|
||||
}
|
||||
|
||||
bool Bus::InjectExecutable(std::span<const u8> buffer, bool set_pc, Error* error)
|
||||
{
|
||||
BIOS::PSEXEHeader header;
|
||||
if (buffer.size() < sizeof(header))
|
||||
{
|
||||
Error::SetStringView(error, "Executable does not contain a header.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memcpy(&header, buffer.data(), sizeof(header));
|
||||
if (!BIOS::IsValidPSExeHeader(header, buffer.size()))
|
||||
{
|
||||
Error::SetStringView(error, "Executable does not contain a valid header.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.memfill_size > 0)
|
||||
{
|
||||
const u32 words_to_write = header.memfill_size / 4;
|
||||
u32 address = header.memfill_start & ~UINT32_C(3);
|
||||
for (u32 i = 0; i < words_to_write; i++)
|
||||
{
|
||||
CPU::SafeWriteMemoryWord(address, 0);
|
||||
address += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 data_load_size =
|
||||
std::min(static_cast<u32>(static_cast<u32>(buffer.size() - sizeof(BIOS::PSEXEHeader))), header.file_size);
|
||||
if (data_load_size > 0)
|
||||
{
|
||||
if (!CPU::SafeWriteMemoryBytes(header.load_address, &buffer[sizeof(header)], data_load_size))
|
||||
{
|
||||
Error::SetStringFmt(error, "Failed to upload {} bytes to memory at address 0x{:08X}.", data_load_size,
|
||||
header.load_address);
|
||||
}
|
||||
}
|
||||
|
||||
// patch the BIOS to jump to the executable directly
|
||||
if (set_pc)
|
||||
{
|
||||
const u32 r_pc = header.initial_pc;
|
||||
CPU::g_state.regs.gp = header.initial_gp;
|
||||
CPU::g_state.regs.sp = header.initial_sp_base + header.initial_sp_offset;
|
||||
CPU::g_state.regs.fp = header.initial_sp_base + header.initial_sp_offset;
|
||||
CPU::SetPC(r_pc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bus::KernelInitializedHook()
|
||||
{
|
||||
if (s_kernel_initialize_hook_run)
|
||||
return;
|
||||
|
||||
INFO_LOG("Kernel initialized.");
|
||||
s_kernel_initialize_hook_run = true;
|
||||
|
||||
const System::BootMode boot_mode = System::GetBootMode();
|
||||
if (boot_mode == System::BootMode::BootEXE || boot_mode == System::BootMode::BootPSF)
|
||||
{
|
||||
Error error;
|
||||
if (((boot_mode == System::BootMode::BootEXE) ? SideloadEXE(System::GetExeOverride(), &error) :
|
||||
PSFLoader::Load(System::GetExeOverride(), &error)))
|
||||
{
|
||||
// Clear all state, since we're blatently overwriting memory.
|
||||
CPU::CodeCache::Reset();
|
||||
CPU::ClearICache();
|
||||
|
||||
// Stop executing the current block and shell init, and jump straight to the new code.
|
||||
DebugAssert(!TimingEvents::IsRunningEvents());
|
||||
CPU::ExitExecution();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shut down system on load failure.
|
||||
Host::ReportErrorAsync("EXE/PSF Load Failed", error.GetDescription());
|
||||
System::ShutdownSystem(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Bus::SideloadEXE(const std::string& path, Error* error)
|
||||
{
|
||||
// look for a libps.exe next to the exe, if it exists, load it
|
||||
bool okay = true;
|
||||
if (const std::string libps_path = Path::BuildRelativePath(path, "libps.exe");
|
||||
FileSystem::FileExists(libps_path.c_str()))
|
||||
{
|
||||
const std::optional<std::vector<u8>> exe_data = FileSystem::ReadBinaryFile(libps_path.c_str(), error);
|
||||
okay = (exe_data.has_value() && InjectExecutable(exe_data.value(), false, error));
|
||||
if (!okay)
|
||||
Error::AddPrefix(error, "Failed to load libps.exe: ");
|
||||
}
|
||||
if (okay)
|
||||
{
|
||||
const std::optional<std::vector<u8>> exe_data = FileSystem::ReadBinaryFile(System::GetExeOverride().c_str(), error);
|
||||
okay = (exe_data.has_value() && InjectExecutable(exe_data.value(), true, error));
|
||||
if (!okay)
|
||||
Error::AddPrefixFmt(error, "Failed to load {}: ", Path::GetFileName(path));
|
||||
}
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
#define BUS_CYCLES(n) CPU::g_state.pending_ticks += n
|
||||
|
||||
// TODO: Move handlers to own files for better inlining.
|
||||
|
@ -1192,7 +1312,10 @@ void Bus::EXP2WriteHandler(VirtualMemoryAddress address, u32 value)
|
|||
}
|
||||
else if (offset == 0x41 || offset == 0x42)
|
||||
{
|
||||
DEV_LOG("BIOS POST status: {:02X}", value & UINT32_C(0x0F));
|
||||
const u32 post_code = value & UINT32_C(0x0F);
|
||||
DEV_LOG("BIOS POST status: {:02X}", post_code);
|
||||
if (post_code == 0x07)
|
||||
KernelInitializedHook();
|
||||
}
|
||||
else if (offset == 0x70)
|
||||
{
|
||||
|
@ -1233,7 +1356,12 @@ void Bus::EXP3WriteHandler(VirtualMemoryAddress address, u32 value)
|
|||
{
|
||||
const u32 offset = address & EXP3_MASK;
|
||||
if (offset == 0)
|
||||
WARNING_LOG("BIOS POST3 status: {:02X}", value & UINT32_C(0x0F));
|
||||
{
|
||||
const u32 post_code = value & UINT32_C(0x0F);
|
||||
WARNING_LOG("BIOS POST3 status: {:02X}", post_code);
|
||||
if (post_code == 0x07)
|
||||
KernelInitializedHook();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <array>
|
||||
#include <bitset>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
@ -218,4 +219,7 @@ std::optional<PhysicalMemoryAddress> SearchMemory(PhysicalMemoryAddress start_ad
|
|||
void AddTTYCharacter(char ch);
|
||||
void AddTTYString(std::string_view str);
|
||||
|
||||
/// Injects a PS-EXE into memory at its specified load location. If set_pc is set, execution will be redirected.
|
||||
bool InjectExecutable(std::span<const u8> buffer, bool set_pc, Error* error);
|
||||
|
||||
} // namespace Bus
|
||||
|
|
|
@ -28,7 +28,6 @@ Log_SetChannel(CPU::Core);
|
|||
|
||||
namespace CPU {
|
||||
static bool ShouldUseInterpreter();
|
||||
static void SetPC(u32 new_pc);
|
||||
static void UpdateLoadDelay();
|
||||
static void Branch(u32 target);
|
||||
static void FlushLoadDelay();
|
||||
|
@ -306,7 +305,7 @@ ALWAYS_INLINE_RELEASE bool CPU::ShouldUseInterpreter()
|
|||
return (g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter || g_state.using_debug_dispatcher);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE_RELEASE void CPU::SetPC(u32 new_pc)
|
||||
void CPU::SetPC(u32 new_pc)
|
||||
{
|
||||
DebugAssert(Common::IsAlignedPow2(new_pc, 4));
|
||||
g_state.npc = new_pc;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace CPU {
|
||||
|
||||
void SetPC(u32 new_pc);
|
||||
|
||||
// exceptions
|
||||
void RaiseException(Exception excode);
|
||||
void RaiseException(u32 CAUSE_bits, u32 EPC);
|
||||
|
|
|
@ -202,7 +202,7 @@ bool GameList::GetPsfListEntry(const std::string& path, Entry* entry)
|
|||
{
|
||||
// we don't need to walk the library chain here - the top file is enough
|
||||
PSFLoader::File file;
|
||||
if (!file.Load(path.c_str()))
|
||||
if (!file.Load(path.c_str(), nullptr))
|
||||
return false;
|
||||
|
||||
entry->serial.clear();
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "psf_loader.h"
|
||||
#include "bios.h"
|
||||
#include "bus.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
Log_SetChannel(PSFLoader);
|
||||
|
||||
namespace PSFLoader {
|
||||
static bool LoadLibraryPSF(const std::string& path, bool use_pc_sp, Error* error, u32 depth = 0);
|
||||
}
|
||||
|
||||
std::optional<std::string> File::GetTagString(const char* tag_name) const
|
||||
std::optional<std::string> PSFLoader::File::GetTagString(const char* tag_name) const
|
||||
{
|
||||
auto it = m_tags.find(tag_name);
|
||||
if (it == m_tags.end())
|
||||
|
@ -24,7 +32,7 @@ std::optional<std::string> File::GetTagString(const char* tag_name) const
|
|||
return it->second;
|
||||
}
|
||||
|
||||
std::optional<int> File::GetTagInt(const char* tag_name) const
|
||||
std::optional<int> PSFLoader::File::GetTagInt(const char* tag_name) const
|
||||
{
|
||||
auto it = m_tags.find(tag_name);
|
||||
if (it == m_tags.end())
|
||||
|
@ -33,7 +41,7 @@ std::optional<int> File::GetTagInt(const char* tag_name) const
|
|||
return std::atoi(it->second.c_str());
|
||||
}
|
||||
|
||||
std::optional<float> File::GetTagFloat(const char* tag_name) const
|
||||
std::optional<float> PSFLoader::File::GetTagFloat(const char* tag_name) const
|
||||
{
|
||||
auto it = m_tags.find(tag_name);
|
||||
if (it == m_tags.end())
|
||||
|
@ -42,7 +50,7 @@ std::optional<float> File::GetTagFloat(const char* tag_name) const
|
|||
return static_cast<float>(std::atof(it->second.c_str()));
|
||||
}
|
||||
|
||||
std::string File::GetTagString(const char* tag_name, const char* default_value) const
|
||||
std::string PSFLoader::File::GetTagString(const char* tag_name, const char* default_value) const
|
||||
{
|
||||
std::optional<std::string> value(GetTagString(tag_name));
|
||||
if (value.has_value())
|
||||
|
@ -51,24 +59,21 @@ std::string File::GetTagString(const char* tag_name, const char* default_value)
|
|||
return default_value;
|
||||
}
|
||||
|
||||
int File::GetTagInt(const char* tag_name, int default_value) const
|
||||
int PSFLoader::File::GetTagInt(const char* tag_name, int default_value) const
|
||||
{
|
||||
return GetTagInt(tag_name).value_or(default_value);
|
||||
}
|
||||
|
||||
float File::GetTagFloat(const char* tag_name, float default_value) const
|
||||
float PSFLoader::File::GetTagFloat(const char* tag_name, float default_value) const
|
||||
{
|
||||
return GetTagFloat(tag_name).value_or(default_value);
|
||||
}
|
||||
|
||||
bool File::Load(const char* path)
|
||||
bool PSFLoader::File::Load(const char* path, Error* error)
|
||||
{
|
||||
std::optional<std::vector<u8>> file_data(FileSystem::ReadBinaryFile(path));
|
||||
std::optional<std::vector<u8>> file_data(FileSystem::ReadBinaryFile(path, error));
|
||||
if (!file_data.has_value() || file_data->empty())
|
||||
{
|
||||
ERROR_LOG("Failed to open/read PSF file '{}'", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const u8* file_pointer = file_data->data();
|
||||
const u8* file_pointer_end = file_data->data() + file_data->size();
|
||||
|
@ -81,7 +86,7 @@ bool File::Load(const char* path)
|
|||
header.compressed_program_size == 0 ||
|
||||
(sizeof(header) + header.reserved_area_size + header.compressed_program_size) > file_size)
|
||||
{
|
||||
ERROR_LOG("Invalid or incompatible header in PSF '{}'", path);
|
||||
Error::SetStringView(error, "Invalid or incompatible PSF header.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -98,7 +103,7 @@ bool File::Load(const char* path)
|
|||
int err = inflateInit(&strm);
|
||||
if (err != Z_OK)
|
||||
{
|
||||
ERROR_LOG("inflateInit() failed: {}", err);
|
||||
Error::SetStringFmt(error, "inflateInit() failed: {}", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -106,7 +111,7 @@ bool File::Load(const char* path)
|
|||
err = inflate(&strm, Z_NO_FLUSH);
|
||||
if (err != Z_STREAM_END)
|
||||
{
|
||||
ERROR_LOG("inflate() failed: {}", err);
|
||||
Error::SetStringFmt(error, "inflate() failed: {}", err);
|
||||
inflateEnd(&strm);
|
||||
return false;
|
||||
}
|
||||
|
@ -166,19 +171,20 @@ bool File::Load(const char* path)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool LoadLibraryPSF(const char* path, bool use_pc_sp, u32 depth = 0)
|
||||
bool PSFLoader::LoadLibraryPSF(const std::string& path, bool use_pc_sp, Error* error, u32 depth)
|
||||
{
|
||||
// don't recurse past 10 levels just in case of broken files
|
||||
if (depth >= 10)
|
||||
{
|
||||
ERROR_LOG("Recursion depth exceeded when loading PSF '{}'", path);
|
||||
Error::SetStringFmt(error, "Recursion depth exceeded when loading PSF '{}'", Path::GetFileName(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
File file;
|
||||
if (!file.Load(path))
|
||||
if (!file.Load(path.c_str(), error))
|
||||
{
|
||||
ERROR_LOG("Failed to load main PSF '{}'", path);
|
||||
Error::AddPrefixFmt(error, "Failed to load {} PSF '{}': ", (depth == 0) ? "main" : "parent",
|
||||
Path::GetFileName(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -186,16 +192,13 @@ static bool LoadLibraryPSF(const char* path, bool use_pc_sp, u32 depth = 0)
|
|||
std::optional<std::string> lib_name(file.GetTagString("_lib"));
|
||||
if (lib_name.has_value())
|
||||
{
|
||||
const std::string lib_path(Path::BuildRelativePath(path, lib_name.value()));
|
||||
INFO_LOG("Loading main parent PSF '{}'", lib_path);
|
||||
const std::string lib_path = Path::BuildRelativePath(path, lib_name.value());
|
||||
INFO_LOG("Loading parent PSF '{}'", Path::GetFileName(lib_path));
|
||||
|
||||
// We should use the initial SP/PC from the **first** parent lib.
|
||||
const bool lib_use_pc_sp = (depth == 0);
|
||||
if (!LoadLibraryPSF(lib_path.c_str(), lib_use_pc_sp, depth + 1))
|
||||
{
|
||||
ERROR_LOG("Failed to load main parent PSF '{}'", lib_path);
|
||||
if (!LoadLibraryPSF(lib_path.c_str(), lib_use_pc_sp, error, depth + 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't apply the PC/SP from the minipsf file.
|
||||
if (lib_use_pc_sp)
|
||||
|
@ -203,10 +206,10 @@ static bool LoadLibraryPSF(const char* path, bool use_pc_sp, u32 depth = 0)
|
|||
}
|
||||
|
||||
// apply the main psf
|
||||
if (!System::InjectEXEFromBuffer(file.GetProgramData().data(), static_cast<u32>(file.GetProgramData().size()),
|
||||
use_pc_sp))
|
||||
if (!Bus::InjectExecutable(file.GetProgramData(), use_pc_sp, error))
|
||||
{
|
||||
ERROR_LOG("Failed to parse EXE from PSF '{}'", path);
|
||||
Error::AddPrefixFmt(error, "Failed to inject {} PSF '{}': ", (depth == 0) ? "main" : "parent",
|
||||
Path::GetFileName(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -218,22 +221,17 @@ static bool LoadLibraryPSF(const char* path, bool use_pc_sp, u32 depth = 0)
|
|||
if (!lib_name.has_value())
|
||||
break;
|
||||
|
||||
const std::string lib_path(Path::BuildRelativePath(path, lib_name.value()));
|
||||
INFO_LOG("Loading parent PSF '{}'", lib_path);
|
||||
if (!LoadLibraryPSF(lib_path.c_str(), false, depth + 1))
|
||||
{
|
||||
ERROR_LOG("Failed to load parent PSF '{}'", lib_path);
|
||||
const std::string lib_path = Path::BuildRelativePath(path, lib_name.value());
|
||||
INFO_LOG("Loading parent PSF '{}'", Path::GetFileName(lib_path));
|
||||
if (!LoadLibraryPSF(lib_path.c_str(), false, error, depth + 1))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Load(const char* path)
|
||||
bool PSFLoader::Load(const std::string& path, Error* error)
|
||||
{
|
||||
INFO_LOG("Loading PSF file from '{}'", path);
|
||||
return LoadLibraryPSF(path, true);
|
||||
return LoadLibraryPSF(path, true, error);
|
||||
}
|
||||
|
||||
} // namespace PSFLoader
|
|
@ -1,14 +1,18 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class Error;
|
||||
|
||||
namespace PSFLoader {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
@ -40,7 +44,7 @@ public:
|
|||
int GetTagInt(const char* tag_name, int default_value) const;
|
||||
float GetTagFloat(const char* tag_name, float default_value) const;
|
||||
|
||||
bool Load(const char* path);
|
||||
bool Load(const char* path, Error* error);
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
|
@ -53,6 +57,6 @@ private:
|
|||
DiscRegion m_region = DiscRegion::Other;
|
||||
};
|
||||
|
||||
bool Load(const char* path);
|
||||
bool Load(const std::string& path, Error* error);
|
||||
|
||||
} // namespace PSFLoader
|
|
@ -5,7 +5,7 @@
|
|||
#include "types.h"
|
||||
|
||||
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
|
||||
static constexpr u32 SAVE_STATE_VERSION = 67;
|
||||
static constexpr u32 SAVE_STATE_VERSION = 68;
|
||||
static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42;
|
||||
|
||||
static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);
|
||||
|
|
|
@ -108,8 +108,6 @@ static std::optional<ExtendedSaveStateInfo> InternalGetExtendedSaveStateInfo(Byt
|
|||
|
||||
static void LoadInputBindings(SettingsInterface& si, std::unique_lock<std::mutex>& lock);
|
||||
|
||||
static bool LoadEXE(const char* filename);
|
||||
|
||||
static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories);
|
||||
static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
|
||||
std::vector<u8>* out_executable_data);
|
||||
|
@ -117,6 +115,7 @@ static GameHash GetGameHashFromBuffer(std::string_view exe_name, std::span<const
|
|||
const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length);
|
||||
|
||||
static bool LoadBIOS(Error* error);
|
||||
static void ResetBootMode();
|
||||
static void InternalReset();
|
||||
static void ClearRunningGame();
|
||||
static void DestroySystem();
|
||||
|
@ -183,10 +182,11 @@ static BIOS::ImageInfo::Hash s_bios_hash = {};
|
|||
static std::string s_running_game_path;
|
||||
static std::string s_running_game_serial;
|
||||
static std::string s_running_game_title;
|
||||
static std::string s_exe_override;
|
||||
static const GameDatabase::Entry* s_running_game_entry = nullptr;
|
||||
static System::GameHash s_running_game_hash;
|
||||
static System::BootMode s_boot_mode = System::BootMode::FullBoot;
|
||||
static bool s_running_game_custom_title = false;
|
||||
static bool s_was_fast_booted = false;
|
||||
|
||||
static bool s_system_executing = false;
|
||||
static bool s_system_interrupted = false;
|
||||
|
@ -585,6 +585,11 @@ const std::string& System::GetGameTitle()
|
|||
return s_running_game_title;
|
||||
}
|
||||
|
||||
const std::string& System::GetExeOverride()
|
||||
{
|
||||
return s_exe_override;
|
||||
}
|
||||
|
||||
const GameDatabase::Entry* System::GetGameDatabaseEntry()
|
||||
{
|
||||
return s_running_game_entry;
|
||||
|
@ -600,9 +605,9 @@ bool System::IsRunningUnknownGame()
|
|||
return !s_running_game_entry;
|
||||
}
|
||||
|
||||
bool System::WasFastBooted()
|
||||
System::BootMode System::GetBootMode()
|
||||
{
|
||||
return s_was_fast_booted;
|
||||
return s_boot_mode;
|
||||
}
|
||||
|
||||
const BIOS::ImageInfo* System::GetBIOSImageInfo()
|
||||
|
@ -1034,7 +1039,7 @@ DiscRegion System::GetRegionForExe(const char* path)
|
|||
DiscRegion System::GetRegionForPsf(const char* path)
|
||||
{
|
||||
PSFLoader::File psf;
|
||||
if (!psf.Load(path))
|
||||
if (!psf.Load(path, nullptr))
|
||||
return DiscRegion::Other;
|
||||
|
||||
return psf.GetRegion();
|
||||
|
@ -1516,18 +1521,27 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
// Load CD image up and detect region.
|
||||
std::unique_ptr<CDImage> disc;
|
||||
DiscRegion disc_region = DiscRegion::NonPS1;
|
||||
bool do_exe_boot = false;
|
||||
bool do_psf_boot = false;
|
||||
BootMode boot_mode = BootMode::FullBoot;
|
||||
std::string exe_override;
|
||||
if (!parameters.filename.empty())
|
||||
{
|
||||
do_exe_boot = IsExeFileName(parameters.filename);
|
||||
do_psf_boot = (!do_exe_boot && IsPsfFileName(parameters.filename));
|
||||
if (do_exe_boot || do_psf_boot)
|
||||
if (IsExeFileName(parameters.filename))
|
||||
{
|
||||
boot_mode = BootMode::BootEXE;
|
||||
exe_override = parameters.filename;
|
||||
}
|
||||
else if (IsPsfFileName(parameters.filename))
|
||||
{
|
||||
boot_mode = BootMode::BootPSF;
|
||||
exe_override = parameters.filename;
|
||||
}
|
||||
if (boot_mode == BootMode::BootEXE || boot_mode == BootMode::BootPSF)
|
||||
{
|
||||
if (s_region == ConsoleRegion::Auto)
|
||||
{
|
||||
const DiscRegion file_region =
|
||||
(do_exe_boot ? GetRegionForExe(parameters.filename.c_str()) : GetRegionForPsf(parameters.filename.c_str()));
|
||||
((boot_mode == BootMode::BootEXE) ? GetRegionForExe(parameters.filename.c_str()) :
|
||||
GetRegionForPsf(parameters.filename.c_str()));
|
||||
INFO_LOG("EXE/PSF Region: {}", Settings::GetDiscRegionDisplayName(file_region));
|
||||
s_region = GetConsoleRegionForDiscRegion(file_region);
|
||||
}
|
||||
|
@ -1561,6 +1575,16 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
Settings::GetDiscRegionName(disc_region), Settings::GetConsoleRegionName(s_region));
|
||||
}
|
||||
}
|
||||
|
||||
const bool wants_fast_boot =
|
||||
parameters.override_fast_boot.value_or(static_cast<bool>(g_settings.bios_patch_fast_boot));
|
||||
if (wants_fast_boot)
|
||||
{
|
||||
if (disc_region == DiscRegion::NonPS1)
|
||||
ERROR_LOG("Not fast booting non-PS1 disc.");
|
||||
else
|
||||
boot_mode = BootMode::FastBoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1587,7 +1611,6 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
UpdateRunningGame(disc ? disc->GetFileName().c_str() : parameters.filename.c_str(), disc.get(), true);
|
||||
|
||||
// Get boot EXE override.
|
||||
std::string exe_boot;
|
||||
if (!parameters.override_exe.empty())
|
||||
{
|
||||
if (!FileSystem::FileExists(parameters.override_exe.c_str()) || !IsExeFileName(parameters.override_exe))
|
||||
|
@ -1601,11 +1624,8 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
}
|
||||
|
||||
INFO_LOG("Overriding boot executable: '{}'", parameters.override_exe);
|
||||
exe_boot = std::move(parameters.override_exe);
|
||||
}
|
||||
else if (do_exe_boot)
|
||||
{
|
||||
exe_boot = std::move(parameters.filename);
|
||||
boot_mode = BootMode::BootEXE;
|
||||
exe_override = std::move(parameters.override_exe);
|
||||
}
|
||||
|
||||
// Check for SBI.
|
||||
|
@ -1621,12 +1641,15 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
// Check for resuming with hardcore mode.
|
||||
if (parameters.disable_achievements_hardcore_mode)
|
||||
Achievements::DisableHardcoreMode();
|
||||
if (!parameters.save_state.empty() && Achievements::IsHardcoreModeActive())
|
||||
if ((!parameters.save_state.empty() || !exe_override.empty()) && Achievements::IsHardcoreModeActive())
|
||||
{
|
||||
const bool is_exe_override_boot = parameters.save_state.empty();
|
||||
bool cancelled;
|
||||
if (FullscreenUI::IsInitialized())
|
||||
{
|
||||
Achievements::ConfirmHardcoreModeDisableAsync(TRANSLATE("Achievements", "Resuming state"),
|
||||
Achievements::ConfirmHardcoreModeDisableAsync(is_exe_override_boot ?
|
||||
TRANSLATE("Achievements", "Overriding executable") :
|
||||
TRANSLATE("Achievements", "Resuming state"),
|
||||
[parameters = std::move(parameters)](bool approved) mutable {
|
||||
if (approved)
|
||||
{
|
||||
|
@ -1638,7 +1661,9 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
}
|
||||
else
|
||||
{
|
||||
cancelled = !Achievements::ConfirmHardcoreModeDisable(TRANSLATE("Achievements", "Resuming state"));
|
||||
cancelled = !Achievements::ConfirmHardcoreModeDisable(is_exe_override_boot ?
|
||||
TRANSLATE("Achievements", "Overriding executable") :
|
||||
TRANSLATE("Achievements", "Resuming state"));
|
||||
}
|
||||
|
||||
if (cancelled)
|
||||
|
@ -1677,45 +1702,14 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
|
|||
if (disc)
|
||||
CDROM::InsertMedia(std::move(disc), disc_region);
|
||||
|
||||
s_boot_mode = boot_mode;
|
||||
s_exe_override = std::move(exe_override);
|
||||
|
||||
UpdateControllers();
|
||||
UpdateMemoryCardTypes();
|
||||
UpdateMultitaps();
|
||||
InternalReset();
|
||||
|
||||
// Load EXE late after BIOS.
|
||||
if (!exe_boot.empty() && !LoadEXE(exe_boot.c_str()))
|
||||
{
|
||||
Error::SetStringFmt(error, "Failed to load EXE file '{}'", Path::GetFileName(exe_boot));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
else if (do_psf_boot && !PSFLoader::Load(parameters.filename.c_str()))
|
||||
{
|
||||
Error::SetStringFmt(error, "Failed to load PSF file '{}'", Path::GetFileName(parameters.filename));
|
||||
DestroySystem();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply fastboot patch if enabled.
|
||||
if (CDROM::HasMedia() && (parameters.override_fast_boot.has_value() ? parameters.override_fast_boot.value() :
|
||||
g_settings.bios_patch_fast_boot))
|
||||
{
|
||||
if (!CDROM::IsMediaPS1Disc())
|
||||
{
|
||||
ERROR_LOG("Not fast booting non-PS1 disc.");
|
||||
}
|
||||
else if (!s_bios_image_info || !s_bios_image_info->patch_compatible)
|
||||
{
|
||||
ERROR_LOG("Not patching fast boot, as BIOS is not patch compatible.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Fast boot without patches...
|
||||
BIOS::PatchBIOSFastBoot(Bus::g_bios, Bus::BIOS_SIZE);
|
||||
s_was_fast_booted = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Texture replacement preloading.
|
||||
// TODO: Move this and everything else below OnSystemStarted().
|
||||
TextureReplacements::SetGameID(s_running_game_serial);
|
||||
|
@ -1932,7 +1926,8 @@ void System::DestroySystem()
|
|||
|
||||
s_bios_hash = {};
|
||||
s_bios_image_info = nullptr;
|
||||
s_was_fast_booted = false;
|
||||
s_exe_override = {};
|
||||
s_boot_mode = BootMode::FullBoot;
|
||||
s_cheat_list.reset();
|
||||
|
||||
s_state = State::Shutdown;
|
||||
|
@ -2474,10 +2469,51 @@ void System::InternalReset()
|
|||
s_internal_frame_number = 0;
|
||||
InterruptExecution();
|
||||
ResetPerformanceCounters();
|
||||
ResetBootMode();
|
||||
|
||||
Achievements::ResetClient();
|
||||
}
|
||||
|
||||
void System::ResetBootMode()
|
||||
{
|
||||
// Preserve exe/psf boot.
|
||||
if (s_boot_mode == BootMode::BootEXE || s_boot_mode == BootMode::BootPSF)
|
||||
{
|
||||
DebugAssert(!s_exe_override.empty());
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset fast boot flag from settings.
|
||||
const bool wants_fast_boot = (g_settings.bios_patch_fast_boot && CDROM::IsMediaPS1Disc() && s_bios_image_info &&
|
||||
s_bios_image_info->patch_compatible);
|
||||
const System::BootMode new_boot_mode = (s_state != System::State::Starting) ?
|
||||
(wants_fast_boot ? System::BootMode::FastBoot : System::BootMode::FullBoot) :
|
||||
s_boot_mode;
|
||||
if (new_boot_mode != s_boot_mode)
|
||||
{
|
||||
// Need to reload the BIOS to wipe out the patching.
|
||||
Error error;
|
||||
if (!LoadBIOS(&error))
|
||||
ERROR_LOG("Failed to reload BIOS on boot mode change, the system may be unstable: {}", error.GetDescription());
|
||||
}
|
||||
|
||||
s_boot_mode = new_boot_mode;
|
||||
if (s_boot_mode == BootMode::FastBoot)
|
||||
{
|
||||
if (s_bios_image_info && s_bios_image_info->patch_compatible)
|
||||
{
|
||||
// Patch BIOS, this sucks.
|
||||
INFO_LOG("Patching BIOS for fast boot.");
|
||||
BIOS::PatchBIOSFastBoot(Bus::g_bios, Bus::BIOS_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG("Cannot fast boot, BIOS is incompatible.");
|
||||
s_boot_mode = BootMode::FullBoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string System::GetMediaPathFromSaveState(const char* path)
|
||||
{
|
||||
std::string ret;
|
||||
|
@ -3137,138 +3173,6 @@ void System::DoToggleCheats()
|
|||
Host::OSD_QUICK_DURATION);
|
||||
}
|
||||
|
||||
static bool LoadEXEToRAM(const char* filename, bool patch_bios)
|
||||
{
|
||||
std::FILE* fp = FileSystem::OpenCFile(filename, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
ERROR_LOG("Failed to open exe file '{}'", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
const u32 file_size = static_cast<u32>(std::ftell(fp));
|
||||
std::fseek(fp, 0, SEEK_SET);
|
||||
|
||||
BIOS::PSEXEHeader header;
|
||||
if (std::fread(&header, sizeof(header), 1, fp) != 1 || !BIOS::IsValidPSExeHeader(header, file_size))
|
||||
{
|
||||
ERROR_LOG("'{}' is not a valid PS-EXE", filename);
|
||||
std::fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.memfill_size > 0)
|
||||
{
|
||||
const u32 words_to_write = header.memfill_size / 4;
|
||||
u32 address = header.memfill_start & ~UINT32_C(3);
|
||||
for (u32 i = 0; i < words_to_write; i++)
|
||||
{
|
||||
CPU::SafeWriteMemoryWord(address, 0);
|
||||
address += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 file_data_size = std::min<u32>(file_size - sizeof(BIOS::PSEXEHeader), header.file_size);
|
||||
if (file_data_size >= 4)
|
||||
{
|
||||
std::vector<u32> data_words((file_data_size + 3) / 4);
|
||||
if (std::fread(data_words.data(), file_data_size, 1, fp) != 1)
|
||||
{
|
||||
std::fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 num_words = file_data_size / 4;
|
||||
u32 address = header.load_address;
|
||||
for (u32 i = 0; i < num_words; i++)
|
||||
{
|
||||
CPU::SafeWriteMemoryWord(address, data_words[i]);
|
||||
address += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
std::fclose(fp);
|
||||
|
||||
// patch the BIOS to jump to the executable directly
|
||||
const u32 r_pc = header.initial_pc;
|
||||
const u32 r_gp = header.initial_gp;
|
||||
const u32 r_sp = header.initial_sp_base + header.initial_sp_offset;
|
||||
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
|
||||
return BIOS::PatchBIOSForEXE(Bus::g_bios, Bus::BIOS_SIZE, r_pc, r_gp, r_sp, r_fp);
|
||||
}
|
||||
|
||||
bool System::LoadEXE(const char* filename)
|
||||
{
|
||||
const std::string libps_path(Path::BuildRelativePath(filename, "libps.exe"));
|
||||
if (!libps_path.empty() && FileSystem::FileExists(libps_path.c_str()) && !LoadEXEToRAM(libps_path.c_str(), false))
|
||||
{
|
||||
ERROR_LOG("Failed to load libps.exe from '{}'", libps_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadEXEToRAM(filename, true);
|
||||
}
|
||||
|
||||
bool System::InjectEXEFromBuffer(const void* buffer, u32 buffer_size, bool patch_bios)
|
||||
{
|
||||
const u8* buffer_ptr = static_cast<const u8*>(buffer);
|
||||
const u8* buffer_end = static_cast<const u8*>(buffer) + buffer_size;
|
||||
|
||||
BIOS::PSEXEHeader header;
|
||||
if (buffer_size < sizeof(header))
|
||||
return false;
|
||||
|
||||
std::memcpy(&header, buffer_ptr, sizeof(header));
|
||||
buffer_ptr += sizeof(header);
|
||||
|
||||
const u32 file_size = static_cast<u32>(static_cast<u32>(buffer_end - buffer_ptr));
|
||||
if (!BIOS::IsValidPSExeHeader(header, file_size))
|
||||
return false;
|
||||
|
||||
if (header.memfill_size > 0)
|
||||
{
|
||||
const u32 words_to_write = header.memfill_size / 4;
|
||||
u32 address = header.memfill_start & ~UINT32_C(3);
|
||||
for (u32 i = 0; i < words_to_write; i++)
|
||||
{
|
||||
CPU::SafeWriteMemoryWord(address, 0);
|
||||
address += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 file_data_size = std::min<u32>(file_size - sizeof(BIOS::PSEXEHeader), header.file_size);
|
||||
if (file_data_size >= 4)
|
||||
{
|
||||
std::vector<u32> data_words((file_data_size + 3) / 4);
|
||||
if ((buffer_end - buffer_ptr) < file_data_size)
|
||||
return false;
|
||||
|
||||
std::memcpy(data_words.data(), buffer_ptr, file_data_size);
|
||||
|
||||
const u32 num_words = file_data_size / 4;
|
||||
u32 address = header.load_address;
|
||||
for (u32 i = 0; i < num_words; i++)
|
||||
{
|
||||
CPU::SafeWriteMemoryWord(address, data_words[i]);
|
||||
address += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
// patch the BIOS to jump to the executable directly
|
||||
if (patch_bios)
|
||||
{
|
||||
const u32 r_pc = header.initial_pc;
|
||||
const u32 r_gp = header.initial_gp;
|
||||
const u32 r_sp = header.initial_sp_base + header.initial_sp_offset;
|
||||
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
|
||||
if (!BIOS::PatchBIOSForEXE(Bus::g_bios, Bus::BIOS_SIZE, r_pc, r_gp, r_sp, r_fp))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// currently not used until EXP1 is implemented
|
||||
|
||||
|
@ -5308,7 +5212,8 @@ void System::RequestDisplaySize(float scale /*= 0.0f*/)
|
|||
u32 requested_height =
|
||||
std::max<u32>(static_cast<u32>(std::ceil(static_cast<float>(g_gpu->GetCRTCDisplayHeight()) * y_scale * scale)), 1);
|
||||
|
||||
if (g_settings.display_rotation == DisplayRotation::Rotate90 || g_settings.display_rotation == DisplayRotation::Rotate180)
|
||||
if (g_settings.display_rotation == DisplayRotation::Rotate90 ||
|
||||
g_settings.display_rotation == DisplayRotation::Rotate180)
|
||||
std::swap(requested_width, requested_height);
|
||||
|
||||
Host::RequestResizeHostDisplay(static_cast<s32>(requested_width), static_cast<s32>(requested_height));
|
||||
|
|
|
@ -109,6 +109,14 @@ enum class State
|
|||
Stopping,
|
||||
};
|
||||
|
||||
enum class BootMode
|
||||
{
|
||||
FullBoot,
|
||||
FastBoot,
|
||||
BootEXE,
|
||||
BootPSF,
|
||||
};
|
||||
|
||||
using GameHash = u64;
|
||||
|
||||
extern TickCount g_ticks_per_second;
|
||||
|
@ -194,10 +202,6 @@ ALWAYS_INLINE_RELEASE TickCount UnscaleTicksToOverclock(TickCount ticks, TickCou
|
|||
TickCount GetMaxSliceTicks();
|
||||
void UpdateOverclock();
|
||||
|
||||
/// Injects a PS-EXE into memory at its specified load location. If patch_loader is set, the BIOS will be patched to
|
||||
/// direct execution to this executable.
|
||||
bool InjectEXEFromBuffer(const void* buffer, u32 buffer_size, bool patch_loader = true);
|
||||
|
||||
u32 GetGlobalTickCounter();
|
||||
u32 GetFrameNumber();
|
||||
u32 GetInternalFrameNumber();
|
||||
|
@ -207,10 +211,11 @@ void FrameDone();
|
|||
const std::string& GetDiscPath();
|
||||
const std::string& GetGameSerial();
|
||||
const std::string& GetGameTitle();
|
||||
const std::string& GetExeOverride();
|
||||
const GameDatabase::Entry* GetGameDatabaseEntry();
|
||||
GameHash GetGameHash();
|
||||
bool IsRunningUnknownGame();
|
||||
bool WasFastBooted();
|
||||
BootMode GetBootMode();
|
||||
|
||||
/// Returns the time elapsed in the current play session.
|
||||
u64 GetSessionPlayedTime();
|
||||
|
|
Loading…
Reference in a new issue