mirror of https://github.com/PCSX2/pcsx2.git
VMManager: Support playing back GS dumps
This commit is contained in:
parent
4d85d916b7
commit
4331ae1925
|
@ -28,6 +28,7 @@
|
|||
#include "pcsx2/Frontend/InputManager.h"
|
||||
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/HostDisplay.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
|
@ -677,6 +678,9 @@ bool Host::BeginPresentFrame(bool frame_skip)
|
|||
|
||||
void Host::EndPresentFrame()
|
||||
{
|
||||
if (GSDumpReplayer::IsReplayingDump())
|
||||
GSDumpReplayer::RenderUI();
|
||||
|
||||
ImGuiManager::RenderOSD();
|
||||
s_host_display->EndPresent();
|
||||
ImGuiManager::NewFrame();
|
||||
|
|
|
@ -44,9 +44,15 @@
|
|||
#include "svnrev.h"
|
||||
|
||||
static constexpr char DISC_IMAGE_FILTER[] =
|
||||
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u);;Single-Track Raw Images (*.bin "
|
||||
"*.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;CSO Images (*.cso);;"
|
||||
"ELF Executables (*.elf);;IRX Executables (*.irx);;Playlists (*.m3u)");
|
||||
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u *.gs *.gs.xz);;"
|
||||
"Single-Track Raw Images (*.bin *.iso);;"
|
||||
"Cue Sheets (*.cue);;"
|
||||
"MAME CHD Images (*.chd);;"
|
||||
"CSO Images (*.cso);;"
|
||||
"ELF Executables (*.elf);;"
|
||||
"IRX Executables (*.irx);;"
|
||||
"Playlists (*.m3u);;"
|
||||
"GS Dumps (*.gs *.gs.xz)");
|
||||
|
||||
const char* MainWindow::DEFAULT_THEME_NAME = "darkfusion";
|
||||
|
||||
|
|
|
@ -983,6 +983,7 @@ if(PCSX2_CORE)
|
|||
Frontend/InputManager.cpp
|
||||
Frontend/InputSource.cpp
|
||||
Frontend/LayeredSettingsInterface.cpp
|
||||
GSDumpReplayer.cpp
|
||||
HostSettings.cpp
|
||||
VMManager.cpp
|
||||
)
|
||||
|
@ -992,6 +993,7 @@ if(PCSX2_CORE)
|
|||
Frontend/InputManager.h
|
||||
Frontend/InputSource.h
|
||||
Frontend/LayeredSettingsInterface.h
|
||||
GSDumpReplayer.h
|
||||
HostSettings.h
|
||||
VMManager.h)
|
||||
endif()
|
||||
|
|
|
@ -630,3 +630,17 @@ void ImGuiManager::RenderOSD()
|
|||
DrawOSDMessages();
|
||||
}
|
||||
|
||||
float ImGuiManager::GetGlobalScale()
|
||||
{
|
||||
return s_global_scale;
|
||||
}
|
||||
|
||||
ImFont* ImGuiManager::GetStandardFont()
|
||||
{
|
||||
return s_standard_font;
|
||||
}
|
||||
|
||||
ImFont* ImGuiManager::GetFixedFont()
|
||||
{
|
||||
return s_fixed_font;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
struct ImFont;
|
||||
|
||||
namespace ImGuiManager
|
||||
{
|
||||
/// Initializes ImGui, creates fonts, etc.
|
||||
|
@ -34,5 +36,14 @@ namespace ImGuiManager
|
|||
|
||||
/// Renders any on-screen display elements.
|
||||
void RenderOSD();
|
||||
|
||||
/// Returns the scale of all on-screen elements.
|
||||
float GetGlobalScale();
|
||||
|
||||
/// Returns the standard font for external drawing.
|
||||
ImFont* GetStandardFont();
|
||||
|
||||
/// Returns the fixed-width font for external drawing.
|
||||
ImFont* GetFixedFont();
|
||||
} // namespace ImGuiManager
|
||||
|
||||
|
|
|
@ -0,0 +1,342 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
// Has to come before Gif.h
|
||||
#include "MemoryTypes.h"
|
||||
|
||||
#include "Frontend/ImGuiManager.h"
|
||||
#include "Frontend/GameList.h"
|
||||
#include "Gif.h"
|
||||
#include "Gif_Unit.h"
|
||||
#include "GSDumpReplayer.h"
|
||||
#include "GS/GSLzma.h"
|
||||
#include "GS.h"
|
||||
#include "Host.h"
|
||||
#include "R3000A.h"
|
||||
#include "R5900.h"
|
||||
#include "VMManager.h"
|
||||
#include "VUmicro.h"
|
||||
|
||||
static void GSDumpReplayerCpuReserve();
|
||||
static void GSDumpReplayerCpuShutdown();
|
||||
static void GSDumpReplayerCpuReset();
|
||||
static void GSDumpReplayerCpuStep();
|
||||
static void GSDumpReplayerCpuExecute();
|
||||
static void GSDumpReplayerCpuCheckExecutionState();
|
||||
static void GSDumpReplayerCpuThrowException(const BaseException& ex);
|
||||
static void GSDumpReplayerCpuThrowCpuException(const BaseR5900Exception& ex);
|
||||
static void GSDumpReplayerCpuClear(u32 addr, u32 size);
|
||||
static uint GSDumpReplayerCpuGetCacheReserve();
|
||||
static void GSDumpReplayerCpuSetCacheReserve(uint reserveInMegs);
|
||||
|
||||
static std::unique_ptr<GSDumpFile> s_dump_file;
|
||||
static u32 s_current_packet = 0;
|
||||
static u32 s_dump_frame_number = 0;
|
||||
static bool s_dump_running = false;
|
||||
static bool s_needs_state_loaded = false;
|
||||
static u64 s_frame_ticks = 0;
|
||||
static u64 s_next_frame_time = 0;
|
||||
|
||||
R5900cpu GSDumpReplayerCpu = {
|
||||
GSDumpReplayerCpuReserve,
|
||||
GSDumpReplayerCpuShutdown,
|
||||
GSDumpReplayerCpuReset,
|
||||
GSDumpReplayerCpuStep,
|
||||
GSDumpReplayerCpuExecute,
|
||||
GSDumpReplayerCpuCheckExecutionState,
|
||||
GSDumpReplayerCpuThrowException,
|
||||
GSDumpReplayerCpuThrowCpuException,
|
||||
GSDumpReplayerCpuClear,
|
||||
GSDumpReplayerCpuGetCacheReserve,
|
||||
GSDumpReplayerCpuSetCacheReserve};
|
||||
|
||||
static InterpVU0 gsDumpVU0;
|
||||
static InterpVU1 gsDumpVU1;
|
||||
|
||||
bool GSDumpReplayer::IsReplayingDump()
|
||||
{
|
||||
return static_cast<bool>(s_dump_file);
|
||||
}
|
||||
|
||||
bool GSDumpReplayer::Initialize(const char* filename)
|
||||
{
|
||||
Common::Timer timer;
|
||||
Console.WriteLn("(GSDumpReplayer) Reading file...");
|
||||
|
||||
s_dump_file = GSDumpFile::OpenGSDump(filename);
|
||||
if (!s_dump_file || !s_dump_file->ReadFile())
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("GSDumpReplayer", "Failed to open or read '%s'.", filename);
|
||||
s_dump_file.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
Console.WriteLn("(GSDumpReplayer) Read file in %.2f ms.", timer.GetTimeMilliseconds());
|
||||
|
||||
// We replace all CPUs.
|
||||
Cpu = &GSDumpReplayerCpu;
|
||||
psxCpu = &psxInt;
|
||||
CpuVU0 = &gsDumpVU0;
|
||||
CpuVU1 = &gsDumpVU1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSDumpReplayer::Reset()
|
||||
{
|
||||
GSDumpReplayerCpuReset();
|
||||
}
|
||||
|
||||
void GSDumpReplayer::Shutdown()
|
||||
{
|
||||
Console.WriteLn("(GSDumpReplayer) Shutting down.");
|
||||
|
||||
Cpu = nullptr;
|
||||
psxCpu = nullptr;
|
||||
CpuVU0 = nullptr;
|
||||
CpuVU1 = nullptr;
|
||||
s_dump_file.reset();
|
||||
}
|
||||
|
||||
std::string GSDumpReplayer::GetDumpSerial()
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
if (!s_dump_file->GetSerial().empty())
|
||||
{
|
||||
ret = s_dump_file->GetSerial();
|
||||
}
|
||||
else if (s_dump_file->GetCRC() != 0)
|
||||
{
|
||||
// old dump files don't have serials, but we have the crc...
|
||||
// so, let's try searching the game list for a crc match.
|
||||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = GameList::GetEntryByCRC(s_dump_file->GetCRC());
|
||||
if (entry)
|
||||
ret = entry->serial;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 GSDumpReplayer::GetDumpCRC()
|
||||
{
|
||||
return s_dump_file->GetCRC();
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuReserve()
|
||||
{
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuReset()
|
||||
{
|
||||
s_needs_state_loaded = true;
|
||||
s_current_packet = 0;
|
||||
s_dump_frame_number = 0;
|
||||
}
|
||||
|
||||
static void GSDumpReplayerLoadInitialState()
|
||||
{
|
||||
// reset GS registers to initial dump values
|
||||
std::memcpy(PS2MEM_GS, s_dump_file->GetRegsData().data(),
|
||||
std::min(Ps2MemSize::GSregs, static_cast<u32>(s_dump_file->GetRegsData().size())));
|
||||
|
||||
// load GS state
|
||||
freezeData fd = {static_cast<int>(s_dump_file->GetStateData().size()),
|
||||
const_cast<u8*>(s_dump_file->GetStateData().data())};
|
||||
MTGS_FreezeData mfd = {&fd, 0};
|
||||
GetMTGS().Freeze(FreezeAction::Load, mfd);
|
||||
if (mfd.retval != 0)
|
||||
Host::ReportFormattedErrorAsync("GSDumpReplayer", "Failed to load GS state.");
|
||||
}
|
||||
|
||||
static void GSDumpReplayerSendPacketToMTGS(GIF_PATH path, const u8* data, u32 length)
|
||||
{
|
||||
pxAssert((length % 16) == 0);
|
||||
|
||||
Gif_Path& gifPath = gifUnit.gifPath[path];
|
||||
gifPath.CopyGSPacketData(const_cast<u8*>(data), length);
|
||||
|
||||
GS_Packet gsPack;
|
||||
gsPack.offset = gifPath.curOffset;
|
||||
gsPack.size = length;
|
||||
gifPath.curOffset += length;
|
||||
Gif_AddCompletedGSPacket(gsPack, path);
|
||||
}
|
||||
|
||||
static void GSDumpReplayerUpdateFrameLimit()
|
||||
{
|
||||
constexpr u32 default_frame_limit = 60;
|
||||
const u32 frame_limit = static_cast<u32>(default_frame_limit * EmuConfig.GS.LimitScalar);
|
||||
|
||||
if (frame_limit > 0)
|
||||
s_frame_ticks = (GetTickFrequency() + (frame_limit / 2)) / frame_limit;
|
||||
else
|
||||
s_frame_ticks = 0;
|
||||
}
|
||||
|
||||
static void GSDumpReplayerFrameLimit()
|
||||
{
|
||||
if (s_frame_ticks == 0)
|
||||
return;
|
||||
|
||||
// Frame limiter
|
||||
u64 now = GetCPUTicks();
|
||||
const s64 ms = GetTickFrequency() / 1000;
|
||||
const s64 sleep = s_next_frame_time - now - ms;
|
||||
if (sleep > ms)
|
||||
Threading::Sleep(sleep / ms);
|
||||
while ((now = GetCPUTicks()) < s_next_frame_time)
|
||||
ShortSpin();
|
||||
s_next_frame_time = std::max(now, s_next_frame_time + s_frame_ticks);
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuStep()
|
||||
{
|
||||
if (s_needs_state_loaded)
|
||||
{
|
||||
GSDumpReplayerLoadInitialState();
|
||||
s_needs_state_loaded = false;
|
||||
}
|
||||
|
||||
const GSDumpFile::GSData& packet = s_dump_file->GetPackets()[s_current_packet];
|
||||
s_current_packet = (s_current_packet + 1) % static_cast<u32>(s_dump_file->GetPackets().size());
|
||||
if (s_current_packet == 0)
|
||||
s_dump_frame_number = 0;
|
||||
|
||||
switch (packet.id)
|
||||
{
|
||||
case GSDumpTypes::GSType::Transfer:
|
||||
{
|
||||
switch (packet.path)
|
||||
{
|
||||
case GSDumpTypes::GSTransferPath::Path1Old:
|
||||
{
|
||||
std::unique_ptr<u8[]> data(new u8[16384]);
|
||||
const s32 addr = 16384 - packet.length;
|
||||
std::memcpy(data.get(), packet.data.get() + addr, packet.length);
|
||||
GSDumpReplayerSendPacketToMTGS(GIF_PATH_1, data.get(), packet.length);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSDumpTypes::GSTransferPath::Path1New:
|
||||
case GSDumpTypes::GSTransferPath::Path2:
|
||||
case GSDumpTypes::GSTransferPath::Path3:
|
||||
{
|
||||
GSDumpReplayerSendPacketToMTGS(static_cast<GIF_PATH>(static_cast<u8>(packet.path) - 1),
|
||||
reinterpret_cast<const u8*>(packet.data.get()), packet.length);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GSDumpTypes::GSType::VSync:
|
||||
{
|
||||
s_dump_frame_number++;
|
||||
GSDumpReplayerCpuCheckExecutionState();
|
||||
GSDumpReplayerUpdateFrameLimit();
|
||||
GSDumpReplayerFrameLimit();
|
||||
GetMTGS().PostVsyncStart(false);
|
||||
VMManager::Internal::VSyncOnCPUThread();
|
||||
}
|
||||
break;
|
||||
|
||||
case GSDumpTypes::GSType::ReadFIFO2:
|
||||
{
|
||||
std::unique_ptr<char[]> arr(new char[*((int*)packet.data.get())]);
|
||||
GSreadFIFO2((u8*)arr.get(), *((int*)packet.data.get()));
|
||||
}
|
||||
break;
|
||||
|
||||
case GSDumpTypes::GSType::Registers:
|
||||
{
|
||||
std::memcpy(PS2MEM_GS, packet.data.get(), std::min<s32>(packet.length, Ps2MemSize::GSregs));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuExecute()
|
||||
{
|
||||
s_dump_running = true;
|
||||
s_next_frame_time = GetCPUTicks();
|
||||
|
||||
while (s_dump_running)
|
||||
{
|
||||
GSDumpReplayerCpuStep();
|
||||
}
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuCheckExecutionState()
|
||||
{
|
||||
if (VMManager::Internal::IsExecutionInterrupted())
|
||||
s_dump_running = false;
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuThrowException(const BaseException& ex)
|
||||
{
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuThrowCpuException(const BaseR5900Exception& ex)
|
||||
{
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuClear(u32 addr, u32 size)
|
||||
{
|
||||
}
|
||||
|
||||
uint GSDumpReplayerCpuGetCacheReserve()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GSDumpReplayerCpuSetCacheReserve(uint reserveInMegs)
|
||||
{
|
||||
}
|
||||
|
||||
void GSDumpReplayer::RenderUI()
|
||||
{
|
||||
const float scale = ImGuiManager::GetGlobalScale();
|
||||
const float shadow_offset = std::ceil(1.0f * scale);
|
||||
const float margin = std::ceil(10.0f * scale);
|
||||
const float spacing = std::ceil(5.0f * scale);
|
||||
float position_y = margin;
|
||||
|
||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||
ImFont* font = ImGuiManager::GetFixedFont();
|
||||
FastFormatAscii text;
|
||||
ImVec2 text_size;
|
||||
|
||||
#define DRAW_LINE(font, text, color) \
|
||||
do \
|
||||
{ \
|
||||
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
|
||||
dl->AddText(font, font->FontSize, ImVec2(margin + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100), (text)); \
|
||||
dl->AddText(font, font->FontSize, ImVec2(margin, position_y), color, (text)); \
|
||||
position_y += text_size.y + spacing; \
|
||||
} while (0)
|
||||
|
||||
text.Write("Dump Frame: %u", s_dump_frame_number);
|
||||
DRAW_LINE(font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||
|
||||
text.Clear();
|
||||
text.Write("Packet Number: %u/%u", s_current_packet, static_cast<u32>(s_dump_file->GetPackets().size()));
|
||||
DRAW_LINE(font, text.c_str(), IM_COL32(255, 255, 255, 255));
|
||||
|
||||
#undef DRAW_LINE
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
namespace GSDumpReplayer
|
||||
{
|
||||
bool IsReplayingDump();
|
||||
|
||||
bool Initialize(const char* filename);
|
||||
void Reset();
|
||||
void Shutdown();
|
||||
|
||||
std::string GetDumpSerial();
|
||||
u32 GetDumpCRC();
|
||||
|
||||
void RenderUI();
|
||||
}
|
|
@ -49,6 +49,10 @@ BIOS
|
|||
|
||||
#include "common/PageFaultSource.h"
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
#include "GSDumpReplayer.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLECACHE
|
||||
#include "Cache.h"
|
||||
#endif
|
||||
|
@ -850,7 +854,13 @@ void eeMemoryReserve::Reset()
|
|||
vtlb_VMap(0x00000000,0x00000000,0x20000000);
|
||||
vtlb_VMapUnmap(0x20000000,0x60000000);
|
||||
|
||||
if (!LoadBIOS())
|
||||
#ifdef PCSX2_CORE
|
||||
const bool needs_bios = !GSDumpReplayer::IsReplayingDump();
|
||||
#else
|
||||
constexpr bool needs_bios = true;
|
||||
#endif
|
||||
|
||||
if (needs_bios && !LoadBIOS())
|
||||
pxFailRel("Failed to load BIOS");
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
#include "common/MemsetFast.inl"
|
||||
#include "common/Perf.h"
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
#include "GSDumpReplayer.h"
|
||||
|
||||
extern R5900cpu GSDumpReplayerCpu;
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// RecompiledCodeReserve (implementations)
|
||||
|
@ -576,6 +581,11 @@ void SysCpuProviderPack::ApplyConfig() const
|
|||
|
||||
if( EmuConfig.Cpu.Recompiler.EnableVU1 )
|
||||
CpuVU1 = (BaseVUmicroCPU*)CpuProviders->microVU1;
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
if (GSDumpReplayer::IsReplayingDump())
|
||||
Cpu = &GSDumpReplayerCpu;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "Elfheader.h"
|
||||
#include "FW.h"
|
||||
#include "GS.h"
|
||||
#include "GSDumpReplayer.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "HostSettings.h"
|
||||
#include "IopBios.h"
|
||||
|
@ -222,6 +223,10 @@ void VMManager::LoadSettings()
|
|||
EmuConfig.GS.MaskUserHacks();
|
||||
EmuConfig.GS.MaskUpscalingHacks();
|
||||
|
||||
// Force MTVU off when playing back GS dumps, it doesn't get used.
|
||||
if (GSDumpReplayer::IsReplayingDump())
|
||||
EmuConfig.Speedhacks.vuThread = false;
|
||||
|
||||
if (HasValidVM())
|
||||
ApplyGameFixes();
|
||||
}
|
||||
|
@ -454,11 +459,20 @@ void VMManager::UpdateRunningGame(bool force)
|
|||
// we have the CRC but we're still at the bios and the settings are changed
|
||||
// (e.g. the user presses TAB to speed up emulation), we don't want to apply the
|
||||
// settings as if the game is already running (title, loadeding patches, etc).
|
||||
bool ingame = (ElfCRC && (g_GameLoading || g_GameStarted));
|
||||
const u32 new_crc = ingame ? ElfCRC : 0;
|
||||
const std::string crc_string(StringUtil::StdStringFromFormat("%08X", new_crc));
|
||||
u32 new_crc;
|
||||
std::string new_serial;
|
||||
if (!GSDumpReplayer::IsReplayingDump())
|
||||
{
|
||||
const bool ingame = (ElfCRC && (g_GameLoading || g_GameStarted));
|
||||
new_crc = ingame ? ElfCRC : 0;
|
||||
new_serial = ingame ? SysGetDiscID().ToStdString() : SysGetBiosDiscID().ToStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
new_crc = GSDumpReplayer::GetDumpCRC();
|
||||
new_serial = GSDumpReplayer::GetDumpSerial();
|
||||
}
|
||||
|
||||
std::string new_serial(ingame ? SysGetDiscID().ToStdString() : SysGetBiosDiscID().ToStdString());
|
||||
if (!force && s_game_crc == new_crc && s_game_serial == new_serial)
|
||||
return;
|
||||
|
||||
|
@ -488,7 +502,7 @@ void VMManager::UpdateRunningGame(bool force)
|
|||
ApplySettings();
|
||||
|
||||
ForgetLoadedPatches();
|
||||
LoadPatches(crc_string, true, false);
|
||||
LoadPatches(StringUtil::StdStringFromFormat("%08X", new_crc), true, false);
|
||||
GetMTGS().SendGameCRC(new_crc);
|
||||
|
||||
Host::OnGameChanged(s_disc_path, s_game_serial, s_game_name, s_game_crc);
|
||||
|
@ -544,7 +558,18 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
|
|||
};
|
||||
|
||||
LoadSettings();
|
||||
ApplyBootParameters(boot_params);
|
||||
|
||||
if (IsGSDumpFileName(boot_params.source))
|
||||
{
|
||||
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
|
||||
if (!GSDumpReplayer::Initialize(boot_params.source.c_str()))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyBootParameters(boot_params);
|
||||
}
|
||||
|
||||
EmuConfig.LimiterMode = GetInitialLimiterMode();
|
||||
|
||||
Console.WriteLn("Allocating memory map...");
|
||||
|
@ -661,17 +686,18 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
|
|||
frameLimitReset();
|
||||
cpuReset();
|
||||
|
||||
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
|
||||
s_state.store(VMState::Paused);
|
||||
Host::OnVMStarted();
|
||||
|
||||
UpdateRunningGame(true);
|
||||
|
||||
SetEmuThreadAffinities(true);
|
||||
|
||||
PerformanceMetrics::Clear();
|
||||
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
|
||||
s_state.store(VMState::Paused);
|
||||
Host::OnVMStarted();
|
||||
|
||||
// do we want to load state?
|
||||
if (!boot_params.save_state.empty())
|
||||
if (!GSDumpReplayer::IsReplayingDump() && !boot_params.save_state.empty())
|
||||
{
|
||||
if (!DoLoadState(boot_params.save_state.c_str()))
|
||||
{
|
||||
|
@ -692,12 +718,16 @@ void VMManager::Shutdown(bool allow_save_resume_state /* = true */)
|
|||
vu1Thread.WaitVU();
|
||||
GetMTGS().WaitGS();
|
||||
|
||||
if (allow_save_resume_state && ShouldSaveResumeState())
|
||||
if (!GSDumpReplayer::IsReplayingDump() && allow_save_resume_state && ShouldSaveResumeState())
|
||||
{
|
||||
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
||||
if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1))
|
||||
Console.Error("Failed to save resume state");
|
||||
}
|
||||
else if (GSDumpReplayer::IsReplayingDump())
|
||||
{
|
||||
GSDumpReplayer::Shutdown();
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock lock(s_info_mutex);
|
||||
|
@ -790,6 +820,9 @@ std::string VMManager::GetCurrentSaveStateFileName(s32 slot)
|
|||
|
||||
bool VMManager::DoLoadState(const char* filename)
|
||||
{
|
||||
if (GSDumpReplayer::IsReplayingDump())
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
Host::OnSaveStateLoading(filename);
|
||||
|
@ -808,6 +841,9 @@ bool VMManager::DoLoadState(const char* filename)
|
|||
|
||||
bool VMManager::DoSaveState(const char* filename, s32 slot_for_message)
|
||||
{
|
||||
if (GSDumpReplayer::IsReplayingDump())
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
std::unique_ptr<ArchiveEntryList> elist = std::make_unique<ArchiveEntryList>(new VmStateBuffer(L"Zippable Savestate"));
|
||||
|
@ -920,6 +956,11 @@ bool VMManager::IsElfFileName(const std::string& path)
|
|||
return (StringUtil::Strcasecmp(&path[pos], ".elf") == 0);
|
||||
}
|
||||
|
||||
bool VMManager::IsGSDumpFileName(const std::string& path)
|
||||
{
|
||||
return (StringUtil::EndsWithNoCase(path, ".gs") || StringUtil::EndsWithNoCase(path, ".gs.xz"));
|
||||
}
|
||||
|
||||
void VMManager::SetBootParametersForPath(const std::string& path, VMBootParameters* params)
|
||||
{
|
||||
if (IsElfFileName(path))
|
||||
|
@ -927,6 +968,11 @@ void VMManager::SetBootParametersForPath(const std::string& path, VMBootParamete
|
|||
params->elf_override = path;
|
||||
params->source_type = CDVD_SourceType::NoDisc;
|
||||
}
|
||||
else if (IsGSDumpFileName(path))
|
||||
{
|
||||
params->source_type = CDVD_SourceType::NoDisc;
|
||||
params->source = path;
|
||||
}
|
||||
else if (!path.empty())
|
||||
{
|
||||
params->source_type = CDVD_SourceType::Iso;
|
||||
|
|
|
@ -135,6 +135,9 @@ namespace VMManager
|
|||
/// Returns true if the specified path is an ELF.
|
||||
bool IsElfFileName(const std::string& path);
|
||||
|
||||
/// Returns true if the specified path is a GS Dump.
|
||||
bool IsGSDumpFileName(const std::string& path);
|
||||
|
||||
/// Updates boot parameters for a given start filename. If it's an elf, it'll set elf_override, otherwise source.
|
||||
void SetBootParametersForPath(const std::string& path, VMBootParameters* params);
|
||||
|
||||
|
|
|
@ -186,6 +186,7 @@
|
|||
<ClCompile Include="GameDatabase.cpp" />
|
||||
<ClCompile Include="Gif_Logger.cpp" />
|
||||
<ClCompile Include="Gif_Unit.cpp" />
|
||||
<ClCompile Include="GSDumpReplayer.cpp" />
|
||||
<ClCompile Include="GS\Renderers\DX11\D3D.cpp" />
|
||||
<ClCompile Include="GS\Renderers\HW\GSTextureReplacementLoaders.cpp" />
|
||||
<ClCompile Include="GS\Renderers\HW\GSTextureReplacements.cpp" />
|
||||
|
@ -488,6 +489,7 @@
|
|||
<ClInclude Include="Frontend\XInputSource.h" />
|
||||
<ClInclude Include="GameDatabase.h" />
|
||||
<ClInclude Include="Gif_Unit.h" />
|
||||
<ClInclude Include="GSDumpReplayer.h" />
|
||||
<ClInclude Include="GS\Renderers\DX11\D3D.h" />
|
||||
<ClInclude Include="GS\Renderers\HW\GSTextureReplacements.h" />
|
||||
<ClInclude Include="GS\Window\GSwxDialog.h" />
|
||||
|
|
|
@ -1202,6 +1202,7 @@
|
|||
<ClCompile Include="x86\iR5900Analysis.cpp">
|
||||
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GSDumpReplayer.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Patch.h">
|
||||
|
@ -1985,6 +1986,7 @@
|
|||
<ClInclude Include="x86\iR5900Analysis.h">
|
||||
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GSDumpReplayer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="GS\GS.rc">
|
||||
|
|
Loading…
Reference in New Issue