CPU: HLE implementation of PCDrv (host file access)
This commit is contained in:
parent
5439718ec3
commit
84e5fbe0c6
|
@ -76,6 +76,8 @@ add_library(core
|
|||
negcon.h
|
||||
pad.cpp
|
||||
pad.h
|
||||
pcdrv.cpp
|
||||
pcdrv.h
|
||||
pgxp.cpp
|
||||
pgxp.h
|
||||
playstation_mouse.cpp
|
||||
|
|
|
@ -2019,6 +2019,27 @@ bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SafeReadMemoryCString(VirtualMemoryAddress addr, std::string* value, u32 max_length /*= 1024*/)
|
||||
{
|
||||
value->clear();
|
||||
|
||||
u8 ch;
|
||||
while (SafeReadMemoryByte(addr, &ch))
|
||||
{
|
||||
if (ch == 0)
|
||||
return true;
|
||||
|
||||
value->push_back(ch);
|
||||
if (value->size() >= max_length)
|
||||
return true;
|
||||
|
||||
addr++;
|
||||
}
|
||||
|
||||
value->clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value)
|
||||
{
|
||||
u32 temp = ZeroExtend32(value);
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
<ClCompile Include="negcon.cpp" />
|
||||
<ClCompile Include="pad.cpp" />
|
||||
<ClCompile Include="controller.cpp" />
|
||||
<ClCompile Include="pcdrv.cpp" />
|
||||
<ClCompile Include="pgxp.cpp" />
|
||||
<ClCompile Include="playstation_mouse.cpp" />
|
||||
<ClCompile Include="psf_loader.cpp" />
|
||||
|
@ -139,6 +140,7 @@
|
|||
<ClInclude Include="negcon.h" />
|
||||
<ClInclude Include="pad.h" />
|
||||
<ClInclude Include="controller.h" />
|
||||
<ClInclude Include="pcdrv.h" />
|
||||
<ClInclude Include="pgxp.h" />
|
||||
<ClInclude Include="playstation_mouse.h" />
|
||||
<ClInclude Include="psf_loader.h" />
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
<ClCompile Include="gpu_hw_d3d12.cpp" />
|
||||
<ClCompile Include="host.cpp" />
|
||||
<ClCompile Include="game_database.cpp" />
|
||||
<ClCompile Include="pcdrv.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="types.h" />
|
||||
|
@ -125,5 +126,6 @@
|
|||
<ClInclude Include="achievements.h" />
|
||||
<ClInclude Include="game_database.h" />
|
||||
<ClInclude Include="input_types.h" />
|
||||
<ClInclude Include="pcdrv.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -11,6 +11,7 @@
|
|||
#include "cpu_recompiler_thunks.h"
|
||||
#include "gte.h"
|
||||
#include "host.h"
|
||||
#include "pcdrv.h"
|
||||
#include "pgxp.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
|
@ -293,6 +294,20 @@ void RaiseException(Exception excode)
|
|||
g_state.current_instruction_pc, GetExceptionVector());
|
||||
}
|
||||
|
||||
void RaiseBreakException(u32 CAUSE_bits, u32 EPC, u32 instruction_bits)
|
||||
{
|
||||
if (PCDrv::HandleSyscall(instruction_bits, g_state.regs))
|
||||
{
|
||||
// immediately return
|
||||
g_state.regs.npc = EPC + 4;
|
||||
FlushPipeline();
|
||||
return;
|
||||
}
|
||||
|
||||
// normal exception
|
||||
RaiseException(CAUSE_bits, EPC, GetExceptionVector());
|
||||
}
|
||||
|
||||
void SetExternalInterrupt(u8 bit)
|
||||
{
|
||||
g_state.cop0_regs.cause.Ip |= static_cast<u8>(1u << bit);
|
||||
|
@ -1109,7 +1124,10 @@ restart_instruction:
|
|||
|
||||
case InstructionFunct::break_:
|
||||
{
|
||||
RaiseException(Exception::BP);
|
||||
RaiseBreakException(Cop0Registers::CAUSE::MakeValueForException(
|
||||
Exception::BP, g_state.current_instruction_in_branch_delay_slot,
|
||||
g_state.current_instruction_was_branch_taken, g_state.current_instruction.cop.cop_n),
|
||||
g_state.current_instruction_pc, g_state.current_instruction.bits);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "types.h"
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class StateWrapper;
|
||||
|
@ -147,6 +148,7 @@ ALWAYS_INLINE bool InKernelMode()
|
|||
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
|
||||
bool SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
|
||||
bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value);
|
||||
bool SafeReadMemoryCString(VirtualMemoryAddress addr, std::string* value, u32 max_length = 1024);
|
||||
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value);
|
||||
bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
|
||||
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace CPU {
|
|||
// exceptions
|
||||
void RaiseException(Exception excode);
|
||||
void RaiseException(u32 CAUSE_bits, u32 EPC);
|
||||
void RaiseBreakException(u32 CAUSE_bits, u32 EPC, u32 instruction_bits);
|
||||
|
||||
ALWAYS_INLINE bool HasPendingInterrupt()
|
||||
{
|
||||
|
|
|
@ -931,8 +931,17 @@ void CodeGenerator::GenerateExceptionExit(const CodeBlockInstruction& cbi, Excep
|
|||
m_register_cache.FlushAllGuestRegisters(true, true);
|
||||
m_register_cache.FlushLoadDelay(true);
|
||||
|
||||
EmitFunctionCall(nullptr, static_cast<void (*)(u32, u32)>(&CPU::RaiseException), CAUSE_bits,
|
||||
GetCurrentInstructionPC());
|
||||
if (excode == Exception::BP)
|
||||
{
|
||||
EmitFunctionCall(nullptr, static_cast<void (*)(u32, u32, u32)>(&CPU::RaiseBreakException), CAUSE_bits,
|
||||
GetCurrentInstructionPC(), Value::FromConstantU32(cbi.instruction.bits));
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFunctionCall(nullptr, static_cast<void (*)(u32, u32)>(&CPU::RaiseException), CAUSE_bits,
|
||||
GetCurrentInstructionPC());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "pcdrv.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
#include "common/string_util.h"
|
||||
#include "cpu_core.h"
|
||||
#include "settings.h"
|
||||
Log_SetChannel(PCDrv);
|
||||
|
||||
static constexpr u32 MAX_FILES = 100;
|
||||
|
||||
static std::vector<FileSystem::ManagedCFilePtr> s_files;
|
||||
|
||||
enum PCDrvAttribute : u32
|
||||
{
|
||||
PCDRV_ATTRIBUTE_READ_ONLY = (1 << 0),
|
||||
PCDRV_ATTRIBUTE_HIDDEN = (1 << 1),
|
||||
PCDRV_ATTRIBUTE_SYSTEM = (1 << 2),
|
||||
PCDRV_ATTRIBUTE_DIRECTORY = (1 << 4),
|
||||
PCDRV_ATTRIBUTE_ARCHIVE = (1 << 5),
|
||||
};
|
||||
|
||||
static s32 GetFreeFileHandle()
|
||||
{
|
||||
for (s32 i = 0; i < static_cast<s32>(s_files.size()); i++)
|
||||
{
|
||||
if (!s_files[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
if (s_files.size() >= MAX_FILES)
|
||||
{
|
||||
Log_ErrorPrint("Too many open files.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const s32 index = static_cast<s32>(s_files.size());
|
||||
s_files.emplace_back(nullptr, [](std::FILE*) {});
|
||||
return index;
|
||||
}
|
||||
|
||||
static void CloseAllFiles()
|
||||
{
|
||||
if (!s_files.empty())
|
||||
Log_DevPrintf("Closing %zu open files.", s_files.size());
|
||||
|
||||
s_files.clear();
|
||||
}
|
||||
|
||||
static FILE* GetFileFromHandle(u32 handle)
|
||||
{
|
||||
if (handle >= static_cast<u32>(s_files.size()) || !s_files[handle])
|
||||
{
|
||||
Log_ErrorPrintf("Invalid file handle %d", static_cast<s32>(handle));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return s_files[handle].get();
|
||||
}
|
||||
|
||||
static bool CloseFileHandle(u32 handle)
|
||||
{
|
||||
if (handle >= static_cast<u32>(s_files.size()) || !s_files[handle])
|
||||
{
|
||||
Log_ErrorPrintf("Invalid file handle %d", static_cast<s32>(handle));
|
||||
return false;
|
||||
}
|
||||
|
||||
s_files[handle].reset();
|
||||
while (!s_files.empty() && !s_files.back())
|
||||
s_files.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string ResolveHostPath(const std::string& path)
|
||||
{
|
||||
// Double-check that it falls within the directory of the elf.
|
||||
// Not a real sandbox, but emulators shouldn't be treated as such. Don't run untrusted code!
|
||||
const std::string& root = g_settings.pcdrv_root;
|
||||
std::string canonicalized_path = Path::Canonicalize(Path::Combine(root, path));
|
||||
if (canonicalized_path.length() < root.length() || // Length has to be longer (a file),
|
||||
!StringUtil::StartsWith(canonicalized_path, root) || // and start with the host root,
|
||||
canonicalized_path[root.length()] != FS_OSPATH_SEPARATOR_CHARACTER) // and we can't access a sibling.
|
||||
{
|
||||
Log_ErrorPrintf("Denying access to path outside of PCDrv directory. Requested path: '%s', "
|
||||
"Resolved path: '%s', Root directory: '%s'",
|
||||
path.c_str(), root.c_str(), canonicalized_path.c_str());
|
||||
canonicalized_path.clear();
|
||||
}
|
||||
|
||||
return canonicalized_path;
|
||||
}
|
||||
|
||||
void PCDrv::Initialize()
|
||||
{
|
||||
if (!g_settings.pcdrv_enable)
|
||||
return;
|
||||
|
||||
Log_WarningPrintf("%s PCDrv is enabled at '%s'", g_settings.pcdrv_enable_writes ? "Read/Write" : "Read-Only",
|
||||
g_settings.pcdrv_root.c_str());
|
||||
}
|
||||
|
||||
void PCDrv::Reset()
|
||||
{
|
||||
CloseAllFiles();
|
||||
}
|
||||
|
||||
void PCDrv::Shutdown()
|
||||
{
|
||||
CloseAllFiles();
|
||||
}
|
||||
|
||||
bool PCDrv::HandleSyscall(u32 instruction_bits, CPU::Registers& regs)
|
||||
{
|
||||
// Based on https://problemkaputt.de/psxspx-bios-pc-file-server.htm
|
||||
|
||||
#define RETURN_ERROR() \
|
||||
regs.v0 = 0xffffffff; \
|
||||
regs.v1 = 0xffffffff; // error code
|
||||
|
||||
if (!g_settings.pcdrv_enable)
|
||||
return false;
|
||||
|
||||
const u32 code = (instruction_bits >> 6) & 0xfffff; // 20 bits, funct = 0
|
||||
switch (code)
|
||||
{
|
||||
case 0x101: // PCinit
|
||||
{
|
||||
Log_DevPrintf("PCinit");
|
||||
CloseAllFiles();
|
||||
regs.v0 = 0;
|
||||
regs.v1 = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0x102: // PCcreat
|
||||
case 0x103: // PCopen
|
||||
{
|
||||
const bool is_open = (code == 0x103);
|
||||
const char* func = (code == 0x102) ? "PCcreat" : "PCopen";
|
||||
const u32 mode = regs.a2;
|
||||
std::string filename;
|
||||
if (!CPU::SafeReadMemoryCString(regs.a1, &filename))
|
||||
{
|
||||
Log_ErrorPrintf("%s: Invalid string", func);
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_DebugPrintf("%s: '%s' mode %u", func, filename.c_str(), mode);
|
||||
if ((filename = ResolveHostPath(filename)).empty())
|
||||
{
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_open && !g_settings.pcdrv_enable_writes)
|
||||
{
|
||||
Log_ErrorPrintf("%s: Writes are not enabled", func);
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Directories are unsupported for now, ignore other attributes
|
||||
if (mode & PCDRV_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
Log_ErrorPrintf("%s: Directories are unsupported", func);
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create empty file, truncate if exists.
|
||||
const s32 handle = GetFreeFileHandle();
|
||||
if (handle < 0)
|
||||
{
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
s_files[handle] = FileSystem::OpenManagedCFile(filename.c_str(),
|
||||
is_open ? (g_settings.pcdrv_enable_writes ? "r+b" : "rb") : "w+b");
|
||||
if (!s_files[handle])
|
||||
{
|
||||
Log_ErrorPrintf("%s: Failed to open '%s'", func, filename.c_str());
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
Log_DebugPrintf("PCDrv: Opened '%s' => %d", filename.c_str(), handle);
|
||||
regs.v0 = 0;
|
||||
regs.v1 = static_cast<u32>(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0x104: // PCclose
|
||||
{
|
||||
Log_DebugPrintf("PCclose(%u)", regs.a1);
|
||||
|
||||
if (!CloseFileHandle(regs.a1))
|
||||
{
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
regs.v0 = 0;
|
||||
regs.v1 = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0x105: // PCread
|
||||
{
|
||||
Log_DebugPrintf("PCread(%u, %u, 0x%08x)", regs.a1, regs.a2, regs.a3);
|
||||
|
||||
std::FILE* fp = GetFileFromHandle(regs.a1);
|
||||
if (!fp)
|
||||
{
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
const u32 count = regs.a2;
|
||||
u32 dstaddr = regs.a3;
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
// Certainly less than optimal, but it's not like you're going to be reading megabytes of data here.
|
||||
u8 val;
|
||||
if (std::fread(&val, 1, 1, fp) != 1)
|
||||
{
|
||||
// Does not stop at EOF according to psx-spx.
|
||||
if (std::ferror(fp) != 0)
|
||||
{
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
val = 0;
|
||||
}
|
||||
|
||||
CPU::SafeWriteMemoryByte(dstaddr, val);
|
||||
dstaddr++;
|
||||
}
|
||||
|
||||
regs.v0 = 0;
|
||||
regs.v1 = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0x106: // PCwrite
|
||||
{
|
||||
Log_DebugPrintf("PCwrite(%u, %u, 0x%08x)", regs.a1, regs.a2, regs.a3);
|
||||
|
||||
std::FILE* fp = GetFileFromHandle(regs.a1);
|
||||
if (!fp)
|
||||
{
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
const u32 count = regs.a2;
|
||||
u32 srcaddr = regs.a3;
|
||||
u32 written = 0;
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
u8 val;
|
||||
if (!CPU::SafeReadMemoryByte(srcaddr, &val))
|
||||
break;
|
||||
|
||||
if (std::fwrite(&val, 1, 1, fp) != 1)
|
||||
{
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
srcaddr++;
|
||||
written++;
|
||||
}
|
||||
|
||||
regs.v0 = 0;
|
||||
regs.v1 = written;
|
||||
return true;
|
||||
}
|
||||
|
||||
case 0x107: // PClseek
|
||||
{
|
||||
Log_DebugPrintf("PClseek(%u, %u, %u)", regs.a1, regs.a2, regs.a3);
|
||||
|
||||
std::FILE* fp = GetFileFromHandle(regs.a1);
|
||||
if (!fp)
|
||||
{
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
const s32 offset = static_cast<s32>(regs.a2);
|
||||
const u32 mode = regs.a3;
|
||||
int hmode;
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
hmode = SEEK_SET;
|
||||
break;
|
||||
case 1:
|
||||
hmode = SEEK_CUR;
|
||||
break;
|
||||
case 2:
|
||||
hmode = SEEK_END;
|
||||
break;
|
||||
default:
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FileSystem::FSeek64(fp, offset, hmode) != 0)
|
||||
{
|
||||
Log_ErrorPrintf("FSeek for PCDrv failed: %d %u", offset, hmode);
|
||||
RETURN_ERROR();
|
||||
return true;
|
||||
}
|
||||
|
||||
regs.v0 = 0;
|
||||
regs.v1 = static_cast<u32>(static_cast<s32>(FileSystem::FTell64(fp)));
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
#include "cpu_types.h"
|
||||
#include "types.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// HLE Implementation of PCDrv
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace PCDrv {
|
||||
void Initialize();
|
||||
void Reset();
|
||||
void Shutdown();
|
||||
|
||||
bool HandleSyscall(u32 instruction_bits, CPU::Registers& regs);
|
||||
}
|
|
@ -303,6 +303,9 @@ void Settings::Load(SettingsInterface& si)
|
|||
audio_dump_on_boot = si.GetBoolValue("Audio", "DumpOnBoot", false);
|
||||
|
||||
use_old_mdec_routines = si.GetBoolValue("Hacks", "UseOldMDECRoutines", false);
|
||||
pcdrv_enable = si.GetBoolValue("PCDrv", "Enabled", false);
|
||||
pcdrv_enable_writes = si.GetBoolValue("PCDrv", "EnableWrites", false);
|
||||
pcdrv_root = si.GetStringValue("PCDrv", "Root");
|
||||
|
||||
dma_max_slice_ticks = si.GetIntValue("Hacks", "DMAMaxSliceTicks", DEFAULT_DMA_MAX_SLICE_TICKS);
|
||||
dma_halt_ticks = si.GetIntValue("Hacks", "DMAHaltTicks", DEFAULT_DMA_HALT_TICKS);
|
||||
|
@ -522,6 +525,10 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetIntValue("Hacks", "GPUFIFOSize", gpu_fifo_size);
|
||||
si.SetIntValue("Hacks", "GPUMaxRunAhead", gpu_max_run_ahead);
|
||||
|
||||
si.SetBoolValue("PCDrv", "Enabled", pcdrv_enable);
|
||||
si.SetBoolValue("PCDrv", "EnableWrites", pcdrv_enable_writes);
|
||||
si.SetStringValue("PCDrv", "Root", pcdrv_root.c_str());
|
||||
|
||||
si.SetBoolValue("BIOS", "PatchTTYEnable", bios_patch_tty_enable);
|
||||
si.SetBoolValue("BIOS", "PatchFastBoot", bios_patch_fast_boot);
|
||||
|
||||
|
@ -610,10 +617,17 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages)
|
|||
g_settings.cdrom_mute_cd_audio = false;
|
||||
g_settings.texture_replacements.enable_vram_write_replacements = false;
|
||||
g_settings.use_old_mdec_routines = false;
|
||||
g_settings.pcdrv_enable = false;
|
||||
g_settings.bios_patch_fast_boot = false;
|
||||
g_settings.bios_patch_tty_enable = false;
|
||||
}
|
||||
|
||||
if (g_settings.pcdrv_enable && g_settings.pcdrv_root.empty())
|
||||
{
|
||||
Log_WarningPrintf("Disabling PCDrv because no root directory is specified.");
|
||||
g_settings.pcdrv_enable = false;
|
||||
}
|
||||
|
||||
if (g_settings.display_integer_scaling && g_settings.display_linear_filtering)
|
||||
{
|
||||
Log_WarningPrintf("Disabling linear filter due to integer upscaling.");
|
||||
|
|
|
@ -170,6 +170,7 @@ struct Settings
|
|||
bool audio_dump_on_boot = false;
|
||||
|
||||
bool use_old_mdec_routines = false;
|
||||
bool pcdrv_enable = false;
|
||||
|
||||
// timing hacks section
|
||||
TickCount dma_max_slice_ticks = DEFAULT_DMA_MAX_SLICE_TICKS;
|
||||
|
@ -228,8 +229,6 @@ struct Settings
|
|||
}
|
||||
} texture_replacements;
|
||||
|
||||
// TODO: Controllers, memory cards, etc.
|
||||
|
||||
bool bios_patch_tty_enable = false;
|
||||
bool bios_patch_fast_boot = DEFAULT_FAST_BOOT_VALUE;
|
||||
bool enable_8mb_ram = false;
|
||||
|
@ -243,6 +242,9 @@ struct Settings
|
|||
|
||||
MultitapMode multitap_mode = DEFAULT_MULTITAP_MODE;
|
||||
|
||||
std::string pcdrv_root;
|
||||
bool pcdrv_enable_writes = false;
|
||||
|
||||
std::array<TinyString, NUM_CONTROLLER_AND_CARD_PORTS> GeneratePortLabels() const;
|
||||
|
||||
LOGLEVEL log_level = DEFAULT_LOG_LEVEL;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "memory_card.h"
|
||||
#include "multitap.h"
|
||||
#include "pad.h"
|
||||
#include "pcdrv.h"
|
||||
#include "pgxp.h"
|
||||
#include "psf_loader.h"
|
||||
#include "save_state_version.h"
|
||||
|
@ -1424,6 +1425,7 @@ bool System::Initialize(bool force_software_renderer)
|
|||
SPU::Initialize();
|
||||
MDEC::Initialize();
|
||||
SIO::Initialize();
|
||||
PCDrv::Initialize();
|
||||
|
||||
static constexpr float WARNING_DURATION = 15.0f;
|
||||
|
||||
|
@ -1479,6 +1481,7 @@ void System::DestroySystem()
|
|||
|
||||
g_texture_replacements.Shutdown();
|
||||
|
||||
PCDrv::Shutdown();
|
||||
SIO::Shutdown();
|
||||
MDEC::Shutdown();
|
||||
SPU::Shutdown();
|
||||
|
@ -1832,6 +1835,7 @@ void System::InternalReset()
|
|||
SPU::Reset();
|
||||
MDEC::Reset();
|
||||
SIO::Reset();
|
||||
PCDrv::Reset();
|
||||
s_frame_number = 1;
|
||||
s_internal_frame_number = 0;
|
||||
TimingEvents::Reset();
|
||||
|
|
Loading…
Reference in New Issue