mirror of https://github.com/PCSX2/pcsx2.git
GSDump: Add extensible header and serial
Serial is used to apply hw fixes.
This commit is contained in:
parent
5569e94f41
commit
4ed748fa30
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(®s, 8192);
|
m_dump_file->Read(®s, 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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue