GSDump: Add extensible header and serial

Serial is used to apply hw fixes.
This commit is contained in:
Connor McLaughlin 2022-03-05 14:25:43 +10:00 committed by refractionpcsx2
parent 5569e94f41
commit 4ed748fa30
7 changed files with 123 additions and 32 deletions

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "GSDump.h" #include "GSDump.h"
#include "GSExtra.h" #include "GSExtra.h"
#include "GSState.h"
GSDumpBase::GSDumpBase(const std::string& fn) GSDumpBase::GSDumpBase(const std::string& fn)
: m_frames(0) : m_frames(0)
@ -32,10 +33,29 @@ GSDumpBase::~GSDumpBase()
fclose(m_gs); fclose(m_gs);
} }
void GSDumpBase::AddHeader(u32 crc, const freezeData& fd, const GSPrivRegSet* regs) void GSDumpBase::AddHeader(const std::string& serial, u32 crc, const freezeData& fd, const GSPrivRegSet* regs)
{ {
AppendRawData(&crc, 4); // New header: CRC of FFFFFFFF, secondary header, full header follows.
AppendRawData(&fd.size, 4); const u32 fake_crc = 0xFFFFFFFFu;
AppendRawData(&fake_crc, 4);
// Compute full header size (with serial).
// This acts as the state size for loading older dumps.
const u32 header_size = sizeof(GSDumpHeader) + static_cast<u32>(serial.size());
AppendRawData(&header_size, 4);
// Write hader.
GSDumpHeader header = {};
header.state_version = GSState::STATE_VERSION;
header.state_size = fd.size;
header.crc = crc;
header.serial_offset = sizeof(header);
header.serial_size = static_cast<u32>(serial.size());
AppendRawData(&header, sizeof(header));
if (!serial.empty())
AppendRawData(serial.data(), serial.size());
// Then the real state data.
AppendRawData(fd.data, fd.size); AppendRawData(fd.data, fd.size);
AppendRawData(regs, sizeof(*regs)); AppendRawData(regs, sizeof(*regs));
} }
@ -92,18 +112,18 @@ void GSDumpBase::Write(const void* data, size_t size)
// GSDump implementation // GSDump implementation
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
GSDump::GSDump(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs) GSDumpUncompressed::GSDumpUncompressed(const std::string& fn, const std::string& serial, u32 crc, const freezeData& fd, const GSPrivRegSet* regs)
: GSDumpBase(fn + ".gs") : GSDumpBase(fn + ".gs")
{ {
AddHeader(crc, fd, regs); AddHeader(serial, crc, fd, regs);
} }
void GSDump::AppendRawData(const void* data, size_t size) void GSDumpUncompressed::AppendRawData(const void* data, size_t size)
{ {
Write(data, size); Write(data, size);
} }
void GSDump::AppendRawData(u8 c) void GSDumpUncompressed::AppendRawData(u8 c)
{ {
Write(&c, 1); Write(&c, 1);
} }
@ -112,7 +132,7 @@ void GSDump::AppendRawData(u8 c)
// GSDumpXz implementation // GSDumpXz implementation
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
GSDumpXz::GSDumpXz(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs) GSDumpXz::GSDumpXz(const std::string& fn, const std::string& serial, u32 crc, const freezeData& fd, const GSPrivRegSet* regs)
: GSDumpBase(fn + ".gs.xz") : GSDumpBase(fn + ".gs.xz")
{ {
m_strm = LZMA_STREAM_INIT; m_strm = LZMA_STREAM_INIT;
@ -123,7 +143,7 @@ GSDumpXz::GSDumpXz(const std::string& fn, u32 crc, const freezeData& fd, const G
return; return;
} }
AddHeader(crc, fd, regs); AddHeader(serial, crc, fd, regs);
} }
GSDumpXz::~GSDumpXz() GSDumpXz::~GSDumpXz()

View File

@ -23,7 +23,7 @@
/* /*
Dump file format: Dump file format:
- [crc/4] [state size/4] [state data/size] [PMODE/0x2000] [id/1] [data/?] .. [id/1] [data/?] - [0xFFFFFFFF] [Header] [state size/4] [state data/size] [PMODE/0x2000] [id/1] [data/?] .. [id/1] [data/?]
Transfer data (id == 0) Transfer data (id == 0)
- [0/1] [path index/1] [size/4] [data/size] - [0/1] [path index/1] [size/4] [data/size]
@ -39,6 +39,17 @@ Regs data (id == 3)
*/ */
#pragma pack(push, 4)
struct GSDumpHeader
{
u32 state_version; ///< Must always be first in struct to safely prevent old PCSX2 versions from crashing.
u32 state_size;
u32 serial_offset;
u32 serial_size;
u32 crc;
};
#pragma pack(pop)
class GSDumpBase class GSDumpBase
{ {
int m_frames; int m_frames;
@ -46,7 +57,7 @@ class GSDumpBase
FILE* m_gs; FILE* m_gs;
protected: protected:
void AddHeader(u32 crc, const freezeData& fd, const GSPrivRegSet* regs); void AddHeader(const std::string& serial, u32 crc, const freezeData& fd, const GSPrivRegSet* regs);
void Write(const void* data, size_t size); void Write(const void* data, size_t size);
virtual void AppendRawData(const void* data, size_t size) = 0; virtual void AppendRawData(const void* data, size_t size) = 0;
@ -61,14 +72,14 @@ public:
bool VSync(int field, bool last, const GSPrivRegSet* regs); bool VSync(int field, bool last, const GSPrivRegSet* regs);
}; };
class GSDump final : public GSDumpBase class GSDumpUncompressed final : public GSDumpBase
{ {
void AppendRawData(const void* data, size_t size) final; void AppendRawData(const void* data, size_t size) final;
void AppendRawData(u8 c) final; void AppendRawData(u8 c) final;
public: public:
GSDump(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs); GSDumpUncompressed(const std::string& fn, const std::string& serial, u32 crc, const freezeData& fd, const GSPrivRegSet* regs);
virtual ~GSDump() = default; virtual ~GSDumpUncompressed() = default;
}; };
class GSDumpXz final : public GSDumpBase class GSDumpXz final : public GSDumpBase
@ -83,6 +94,6 @@ class GSDumpXz final : public GSDumpBase
void AppendRawData(u8 c); void AppendRawData(u8 c);
public: public:
GSDumpXz(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs); GSDumpXz(const std::string& fn, const std::string& serial, u32 crc, const freezeData& fd, const GSPrivRegSet* regs);
virtual ~GSDumpXz(); virtual ~GSDumpXz();
}; };

View File

@ -29,7 +29,7 @@ static __fi bool IsAutoFlushEnabled()
} }
GSState::GSState() GSState::GSState()
: m_version(7) : m_version(STATE_VERSION)
, m_gsc(NULL) , m_gsc(NULL)
, m_skip(0) , m_skip(0)
, m_skip_offset(0) , m_skip_offset(0)
@ -2173,7 +2173,7 @@ int GSState::Defrost(const freezeData* fd)
u8* data = fd->data; u8* data = fd->data;
int version; u32 version;
ReadState(&version, data); ReadState(&version, data);

View File

@ -123,7 +123,7 @@ class GSState : public GSAlignedClass<32>
template<bool auto_flush, bool index_swap> template<bool auto_flush, bool index_swap>
void SetPrimHandlers(); void SetPrimHandlers();
int m_version; u32 m_version;
int m_sssize; int m_sssize;
struct GSTransferBuffer struct GSTransferBuffer
@ -243,6 +243,8 @@ public:
int s_savel; int s_savel;
std::string m_dump_root; std::string m_dump_root;
static constexpr u32 STATE_VERSION = 8;
enum PRIM_OVERLAP enum PRIM_OVERLAP
{ {
PRIM_OVERLAP_UNKNOW, PRIM_OVERLAP_UNKNOW,

View File

@ -21,9 +21,15 @@
#include "PerformanceMetrics.h" #include "PerformanceMetrics.h"
#include "pcsx2/Config.h" #include "pcsx2/Config.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#ifndef PCSX2_CORE
#include "gui/AppCoreThread.h"
#if defined(__unix__) #if defined(__unix__)
#include <X11/keysym.h> #include <X11/keysym.h>
#endif #endif
#else
#include "VMManager.h"
#endif
GSRenderer::GSRenderer() GSRenderer::GSRenderer()
: m_shift_key(false) : m_shift_key(false)
@ -454,10 +460,16 @@ void GSRenderer::VSync(u32 field, bool registers_written)
fd.data = new u8[fd.size]; fd.data = new u8[fd.size];
Freeze(&fd, false); Freeze(&fd, false);
#ifndef PCSX2_CORE
const std::string serial(StringUtil::wxStringToUTF8String(GameInfo::gameSerial));
#else
const std::string serial(VMManager::GetGameSerial());
#endif
if (m_control_key) if (m_control_key)
m_dump = std::unique_ptr<GSDumpBase>(new GSDump(m_snapshot, m_crc, fd, m_regs)); m_dump = std::unique_ptr<GSDumpBase>(new GSDumpUncompressed(m_snapshot, serial, m_crc, fd, m_regs));
else else
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpXz(m_snapshot, m_crc, fd, m_regs)); m_dump = std::unique_ptr<GSDumpBase>(new GSDumpXz(m_snapshot, serial, m_crc, fd, m_regs));
delete[] fd.data; delete[] fd.data;
} }

View File

@ -24,6 +24,7 @@
#include "common/EmbeddedImage.h" #include "common/EmbeddedImage.h"
#include "gui/Resources/NoIcon.h" #include "gui/Resources/NoIcon.h"
#include "GS.h" #include "GS.h"
#include "GS/GSDump.h"
#include "HostDisplay.h" #include "HostDisplay.h"
#include "PathDefs.h" #include "PathDefs.h"
@ -31,6 +32,7 @@
#include "gui/GSFrame.h" #include "gui/GSFrame.h"
#include "Counters.h" #include "Counters.h"
#include "PerformanceMetrics.h" #include "PerformanceMetrics.h"
#include "GameDatabase.h"
#include <wx/mstream.h> #include <wx/mstream.h>
#include <wx/listctrl.h> #include <wx/listctrl.h>
@ -514,7 +516,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
{ {
case GSType::Transfer: case GSType::Transfer:
{ {
char* data = dump.data.get(); u8* data = dump.data.get();
u32 remaining = dump.length; u32 remaining = dump.length;
int idx = 0; int idx = 0;
while (remaining >= 16) while (remaining >= 16)
@ -532,7 +534,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
case GSType::VSync: case GSType::VSync:
{ {
wxString s; wxString s;
s.Printf("Field = %u", *(u8*)(dump.data.get())); s.Printf("Field = %u", dump.data[0]);
m_gif_packet->AppendItem(rootId, s); m_gif_packet->AppendItem(rootId, s);
break; break;
} }
@ -549,7 +551,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
} }
} }
void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, char* data) void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, u8* data)
{ {
u64 tag = *(u64*)data; u64 tag = *(u64*)data;
u64 regs = *(u64*)(data + 8); u64 regs = *(u64*)(data + 8);
@ -815,7 +817,7 @@ void Dialogs::GSDumpDialog::ParseTreePrim(wxTreeItemId& id, u32 prim)
m_gif_packet->Expand(id); m_gif_packet->Expand(id);
} }
void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSData& event, char* regs) void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSData& event, u8* regs)
{ {
switch (event.id) switch (event.id)
{ {
@ -921,14 +923,46 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
default: default:
break; break;
} }
char regs[8192]; u8 regs[8192];
m_dump_file->Read(&crc, 4); m_dump_file->Read(&crc, 4);
m_dump_file->Read(&ss, 4); m_dump_file->Read(&ss, 4);
std::unique_ptr<u8[]> state_data = std::make_unique<u8[]>(ss);
std::unique_ptr<char[]> state_data(new char[ss]);
m_dump_file->Read(state_data.get(), ss); m_dump_file->Read(state_data.get(), ss);
// Pull serial out of new header, if present.
std::string serial;
if (crc == 0xFFFFFFFFu)
{
GSDumpHeader header;
if (ss < sizeof(header))
{
Console.Error("GSDump header is corrupted.");
GSDump::isRunning = false;
return;
}
std::memcpy(&header, state_data.get(), sizeof(header));
if (header.serial_size > 0)
{
if (header.serial_offset > ss || (static_cast<u64>(header.serial_offset) + header.serial_size) > ss)
{
Console.Error("GSDump header is corrupted.");
GSDump::isRunning = false;
return;
}
if (header.serial_size > 0)
serial.assign(reinterpret_cast<const char*>(state_data.get()) + header.serial_offset, header.serial_size);
}
// Read the real state data
ss = header.state_size;
state_data = std::make_unique<u8[]>(ss);
m_dump_file->Read(state_data.get(), ss);
}
m_dump_file->Read(&regs, 8192); m_dump_file->Read(&regs, 8192);
freezeData fd = {(int)ss, (u8*)state_data.get()}; freezeData fd = {(int)ss, (u8*)state_data.get()};
@ -956,7 +990,9 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
size = 8192; size = 8192;
break; break;
} }
std::unique_ptr<char[]> data(new char[size]); // make_unique would zero the data out, which is pointless since we're reading it anyway,
// and this loop is executed a *ton* of times.
std::unique_ptr<u8[]> data(new u8[size]);
m_dump_file->Read(data.get(), size); m_dump_file->Read(data.get(), size);
m_root_window->m_dump_packets.push_back({id, std::move(data), size, id_transfer}); m_root_window->m_dump_packets.push_back({id, std::move(data), size, id_transfer});
} }
@ -974,7 +1010,17 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
g_FrameCount = 0; g_FrameCount = 0;
} }
if (!GSopen(g_Conf->EmuOptions.GS, renderer, (u8*)regs)) Pcsx2Config::GSOptions config(g_Conf->EmuOptions.GS);
if (!serial.empty())
{
if (const GameDatabaseSchema::GameEntry* entry = GameDatabase::findGame(serial); entry)
{
// apply hardware fixes to config before opening (e.g. tex in rt)
entry->applyGSHardwareFixes(config);
}
}
if (!GSopen(config, renderer, regs))
{ {
OnStop(); OnStop();
return; return;

View File

@ -239,7 +239,7 @@ namespace Dialogs
struct GSData struct GSData
{ {
GSType id; GSType id;
std::unique_ptr<char[]> data; std::unique_ptr<u8[]> data;
int length; int length;
GSTransferPath path; GSTransferPath path;
}; };
@ -260,11 +260,11 @@ namespace Dialogs
std::vector<wxTreeItemId> m_gif_items; std::vector<wxTreeItemId> m_gif_items;
float m_stored_q = 1.0; float m_stored_q = 1.0;
void ProcessDumpEvent(const GSData& event, char* regs); void ProcessDumpEvent(const GSData& event, u8* regs);
u32 ReadPacketSize(const void* packet); u32 ReadPacketSize(const void* packet);
void GenPacketList(); void GenPacketList();
void GenPacketInfo(GSData& dump); void GenPacketInfo(GSData& dump);
void ParseTransfer(wxTreeItemId& id, char* data); void ParseTransfer(wxTreeItemId& id, u8* data);
void ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data, bool packed); void ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data, bool packed);
void ParseTreePrim(wxTreeItemId& id, u32 prim); void ParseTreePrim(wxTreeItemId& id, u32 prim);
void CloseDump(wxCommandEvent& event); void CloseDump(wxCommandEvent& event);