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 "GSDump.h"
#include "GSExtra.h"
#include "GSState.h"
GSDumpBase::GSDumpBase(const std::string& fn)
: m_frames(0)
@ -32,10 +33,29 @@ GSDumpBase::~GSDumpBase()
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);
AppendRawData(&fd.size, 4);
// New header: CRC of FFFFFFFF, secondary header, full header follows.
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(regs, sizeof(*regs));
}
@ -92,18 +112,18 @@ void GSDumpBase::Write(const void* data, size_t size)
// 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")
{
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);
}
void GSDump::AppendRawData(u8 c)
void GSDumpUncompressed::AppendRawData(u8 c)
{
Write(&c, 1);
}
@ -112,7 +132,7 @@ void GSDump::AppendRawData(u8 c)
// 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")
{
m_strm = LZMA_STREAM_INIT;
@ -123,7 +143,7 @@ GSDumpXz::GSDumpXz(const std::string& fn, u32 crc, const freezeData& fd, const G
return;
}
AddHeader(crc, fd, regs);
AddHeader(serial, crc, fd, regs);
}
GSDumpXz::~GSDumpXz()

View File

@ -23,7 +23,7 @@
/*
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)
- [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
{
int m_frames;
@ -46,7 +57,7 @@ class GSDumpBase
FILE* m_gs;
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);
virtual void AppendRawData(const void* data, size_t size) = 0;
@ -61,14 +72,14 @@ public:
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(u8 c) final;
public:
GSDump(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs);
virtual ~GSDump() = default;
GSDumpUncompressed(const std::string& fn, const std::string& serial, u32 crc, const freezeData& fd, const GSPrivRegSet* regs);
virtual ~GSDumpUncompressed() = default;
};
class GSDumpXz final : public GSDumpBase
@ -83,6 +94,6 @@ class GSDumpXz final : public GSDumpBase
void AppendRawData(u8 c);
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();
};

View File

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

View File

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

View File

@ -21,9 +21,15 @@
#include "PerformanceMetrics.h"
#include "pcsx2/Config.h"
#include "common/StringUtil.h"
#ifndef PCSX2_CORE
#include "gui/AppCoreThread.h"
#if defined(__unix__)
#include <X11/keysym.h>
#endif
#else
#include "VMManager.h"
#endif
GSRenderer::GSRenderer()
: m_shift_key(false)
@ -454,10 +460,16 @@ void GSRenderer::VSync(u32 field, bool registers_written)
fd.data = new u8[fd.size];
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)
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
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;
}

View File

@ -24,6 +24,7 @@
#include "common/EmbeddedImage.h"
#include "gui/Resources/NoIcon.h"
#include "GS.h"
#include "GS/GSDump.h"
#include "HostDisplay.h"
#include "PathDefs.h"
@ -31,6 +32,7 @@
#include "gui/GSFrame.h"
#include "Counters.h"
#include "PerformanceMetrics.h"
#include "GameDatabase.h"
#include <wx/mstream.h>
#include <wx/listctrl.h>
@ -514,7 +516,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
{
case GSType::Transfer:
{
char* data = dump.data.get();
u8* data = dump.data.get();
u32 remaining = dump.length;
int idx = 0;
while (remaining >= 16)
@ -532,7 +534,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
case GSType::VSync:
{
wxString s;
s.Printf("Field = %u", *(u8*)(dump.data.get()));
s.Printf("Field = %u", dump.data[0]);
m_gif_packet->AppendItem(rootId, s);
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 regs = *(u64*)(data + 8);
@ -815,7 +817,7 @@ void Dialogs::GSDumpDialog::ParseTreePrim(wxTreeItemId& id, u32 prim)
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)
{
@ -921,14 +923,46 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
default:
break;
}
char regs[8192];
u8 regs[8192];
m_dump_file->Read(&crc, 4);
m_dump_file->Read(&ss, 4);
std::unique_ptr<char[]> state_data(new char[ss]);
std::unique_ptr<u8[]> state_data = std::make_unique<u8[]>(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);
freezeData fd = {(int)ss, (u8*)state_data.get()};
@ -956,7 +990,9 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
size = 8192;
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_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;
}
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();
return;

View File

@ -239,7 +239,7 @@ namespace Dialogs
struct GSData
{
GSType id;
std::unique_ptr<char[]> data;
std::unique_ptr<u8[]> data;
int length;
GSTransferPath path;
};
@ -260,11 +260,11 @@ namespace Dialogs
std::vector<wxTreeItemId> m_gif_items;
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);
void GenPacketList();
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 ParseTreePrim(wxTreeItemId& id, u32 prim);
void CloseDump(wxCommandEvent& event);