GS: Move dump file loading to core

This commit is contained in:
Connor McLaughlin 2022-03-12 20:38:42 +10:00 committed by refractionpcsx2
parent 4f4b14dd4d
commit 4d85d916b7
4 changed files with 533 additions and 456 deletions

View File

@ -14,28 +14,18 @@
*/
#include "PrecompiledHeader.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "GSDump.h"
#include "GSLzma.h"
GSDumpFile::GSDumpFile(char* filename, const char* repack_filename)
{
m_fp = fopen(filename, "rb");
if (m_fp == nullptr)
{
fprintf(stderr, "failed to open %s\n", filename);
throw "BAD"; // Just exit the program
}
m_repack_fp = nullptr;
if (repack_filename)
{
m_repack_fp = fopen(repack_filename, "wb");
if (m_repack_fp == nullptr)
fprintf(stderr, "failed to open %s for repack\n", repack_filename);
}
}
using namespace GSDumpTypes;
GSDumpFile::GSDumpFile(FILE* file, FILE* repack_file)
: m_repack_fp(repack_file), m_fp(file)
: m_repack_fp(repack_file)
, m_fp(file)
{
}
@ -57,13 +47,161 @@ GSDumpFile::~GSDumpFile()
fclose(m_repack_fp);
}
/******************************************************************/
GSDumpLzma::GSDumpLzma(char* filename, const char* repack_filename)
: GSDumpFile(filename, repack_filename)
std::unique_ptr<GSDumpFile> GSDumpFile::OpenGSDump(const char* filename, const char* repack_filename /*= nullptr*/)
{
Initialize();
std::FILE* fp = FileSystem::OpenCFile(filename, "rb");
if (!fp)
return nullptr;
std::FILE* repack_fp = nullptr;
if (repack_filename && std::strlen(repack_filename) > 0)
{
repack_fp = FileSystem::OpenCFile(repack_filename, "wb");
if (!repack_fp)
{
std::fclose(fp);
return nullptr;
}
}
if (StringUtil::EndsWithNoCase(filename, ".xz"))
return std::make_unique<GSDumpLzma>(fp, nullptr);
else
return std::make_unique<GSDumpRaw>(fp, nullptr);
}
bool GSDumpFile::GetPreviewImageFromDump(const char* filename, u32* width, u32* height, std::vector<u32>* pixels)
{
std::unique_ptr<GSDumpFile> dump = OpenGSDump(filename);
if (!dump)
return false;
u32 crc;
if (!dump->Read(&crc, sizeof(crc)) || crc != 0xFFFFFFFFu)
{
// not new header dump, so no preview
return false;
}
u32 header_size;
if (!dump->Read(&header_size, sizeof(header_size)) || header_size < sizeof(GSDumpHeader))
{
// doesn't have the screenshot fields
return false;
}
std::unique_ptr<u8[]> header_bits = std::make_unique<u8[]>(header_size);
if (!dump->Read(header_bits.get(), header_size))
return false;
GSDumpHeader header;
std::memcpy(&header, header_bits.get(), sizeof(header));
if (header.screenshot_size == 0 ||
header.screenshot_size < (header.screenshot_width * header.screenshot_height * sizeof(u32)) ||
(static_cast<u64>(header.screenshot_offset) + header.screenshot_size) > header_size)
{
// doesn't have a screenshot
return false;
}
*width = header.screenshot_width;
*height = header.screenshot_height;
pixels->resize(header.screenshot_width * header.screenshot_height);
std::memcpy(pixels->data(), header_bits.get() + header.screenshot_offset, header.screenshot_size);
return true;
}
bool GSDumpFile::ReadFile()
{
u32 ss;
if (!Read(&m_crc, 4) || !Read(&ss, 4))
return false;
m_state_data.resize(ss);
if (!Read(m_state_data.data(), ss))
return false;
// Pull serial out of new header, if present.
if (m_crc == 0xFFFFFFFFu)
{
GSDumpHeader header;
if (m_state_data.size() < sizeof(header))
{
Console.Error("GSDump header is corrupted.");
return false;
}
std::memcpy(&header, m_state_data.data(), sizeof(header));
m_crc = header.crc;
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.");
return false;
}
if (header.serial_size > 0)
m_serial.assign(reinterpret_cast<const char*>(m_state_data.data()) + header.serial_offset, header.serial_size);
}
// Read the real state data
m_state_data.resize(header.state_size);
if (!Read(m_state_data.data(), header.state_size))
return false;
}
m_regs_data.resize(8192);
if (!Read(m_regs_data.data(), m_regs_data.size()))
return false;
for (;;)
{
GSData packet;
packet.path = GSTransferPath::Dummy;
if (!Read(&packet.id, 1))
{
if (IsEof())
break;
return false;
}
switch (packet.id)
{
case GSType::Transfer:
{
if (!Read(&packet.path, 1) || !Read(&packet.length, 4))
return false;
}
break;
case GSType::VSync:
packet.length = 1;
break;
case GSType::ReadFIFO2:
packet.length = 4;
break;
case GSType::Registers:
packet.length = 8192;
break;
}
if (packet.length > 0)
{
packet.data = std::unique_ptr<u8[]>(new u8[packet.length]);
if (!Read(packet.data.get(), packet.length))
return false;
}
m_dump_packets.push_back(std::move(packet));
}
return true;
}
/******************************************************************/
GSDumpLzma::GSDumpLzma(FILE* file, FILE* repack_file)
: GSDumpFile(file, repack_file)
{
@ -178,11 +316,6 @@ GSDumpLzma::~GSDumpLzma()
/******************************************************************/
GSDumpRaw::GSDumpRaw(char* filename, const char* repack_filename)
: GSDumpFile(filename, repack_filename)
{
}
GSDumpRaw::GSDumpRaw(FILE* file, FILE* repack_file)
: GSDumpFile(file, repack_file)
{
@ -199,7 +332,7 @@ bool GSDumpRaw::Read(void* ptr, size_t size)
if (ret != size && ferror(m_fp))
{
fprintf(stderr, "GSDumpRaw:: Read error (%zu/%zu)\n", ret, size);
throw "BAD"; // Just exit the program
return false;
}
if (ret == size)

View File

@ -13,24 +13,328 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <lzma.h>
#include <memory>
#include <string>
#include <vector>
#define GEN_REG_ENUM_CLASS_CONTENT(ClassName, EntryName, Value) \
EntryName = Value,
#define GEN_REG_GETNAME_CONTENT(ClassName, EntryName, Value) \
case ClassName::EntryName: \
return #EntryName;
#define GEN_REG_ENUM_CLASS_AND_GETNAME(Macro, ClassName, Type, DefaultString) \
enum class ClassName : Type \
{ \
Macro(GEN_REG_ENUM_CLASS_CONTENT) \
}; \
static constexpr const char* GetName(ClassName reg) \
{ \
switch (reg) \
{ \
Macro(GEN_REG_GETNAME_CONTENT) default : return DefaultString; \
} \
}
namespace GSDumpTypes
{
// clang-format off
#define DEF_GSType(X) \
X(GSType, Transfer, 0) \
X(GSType, VSync, 1) \
X(GSType, ReadFIFO2, 2) \
X(GSType, Registers, 3)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GSType, GSType, u8, "UnknownType")
#undef DEF_GSType
#define DEF_GSTransferPath(X) \
X(GSTransferPath, Path1Old, 0) \
X(GSTransferPath, Path2, 1) \
X(GSTransferPath, Path3, 2) \
X(GSTransferPath, Path1New, 3) \
X(GSTransferPath, Dummy, 4)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GSTransferPath, GSTransferPath, u8, "UnknownPath")
#undef DEF_GSTransferPath
#define DEF_GIFFlag(X) \
X(GIFFlag, PACKED, 0) \
X(GIFFlag, REGLIST, 1) \
X(GIFFlag, IMAGE, 2) \
X(GIFFlag, IMAGE2, 3)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GIFFlag, GIFFlag, u8, "UnknownFlag")
#undef DEF_GifFlag
#define DEF_GIFReg(X) \
X(GIFReg, PRIM, 0x00) \
X(GIFReg, RGBAQ, 0x01) \
X(GIFReg, ST, 0x02) \
X(GIFReg, UV, 0x03) \
X(GIFReg, XYZF2, 0x04) \
X(GIFReg, XYZ2, 0x05) \
X(GIFReg, TEX0_1, 0x06) \
X(GIFReg, TEX0_2, 0x07) \
X(GIFReg, CLAMP_1, 0x08) \
X(GIFReg, CLAMP_2, 0x09) \
X(GIFReg, FOG, 0x0a) \
X(GIFReg, XYZF3, 0x0c) \
X(GIFReg, XYZ3, 0x0d) \
X(GIFReg, AD, 0x0e) \
X(GIFReg, NOP, 0x0f) \
X(GIFReg, TEX1_1, 0x14) \
X(GIFReg, TEX1_2, 0x15) \
X(GIFReg, TEX2_1, 0x16) \
X(GIFReg, TEX2_2, 0x17) \
X(GIFReg, XYOFFSET_1, 0x18) \
X(GIFReg, XYOFFSET_2, 0x19) \
X(GIFReg, PRMODECONT, 0x1a) \
X(GIFReg, PRMODE, 0x1b) \
X(GIFReg, TEXCLUT, 0x1c) \
X(GIFReg, SCANMSK, 0x22) \
X(GIFReg, MIPTBP1_1, 0x34) \
X(GIFReg, MIPTBP1_2, 0x35) \
X(GIFReg, MIPTBP2_1, 0x36) \
X(GIFReg, MIPTBP2_2, 0x37) \
X(GIFReg, TEXA, 0x3b) \
X(GIFReg, FOGCOL, 0x3d) \
X(GIFReg, TEXFLUSH, 0x3f) \
X(GIFReg, SCISSOR_1, 0x40) \
X(GIFReg, SCISSOR_2, 0x41) \
X(GIFReg, ALPHA_1, 0x42) \
X(GIFReg, ALPHA_2, 0x43) \
X(GIFReg, DIMX, 0x44) \
X(GIFReg, DTHE, 0x45) \
X(GIFReg, COLCLAMP, 0x46) \
X(GIFReg, TEST_1, 0x47) \
X(GIFReg, TEST_2, 0x48) \
X(GIFReg, PABE, 0x49) \
X(GIFReg, FBA_1, 0x4a) \
X(GIFReg, FBA_2, 0x4b) \
X(GIFReg, FRAME_1, 0x4c) \
X(GIFReg, FRAME_2, 0x4d) \
X(GIFReg, ZBUF_1, 0x4e) \
X(GIFReg, ZBUF_2, 0x4f) \
X(GIFReg, BITBLTBUF, 0x50) \
X(GIFReg, TRXPOS, 0x51) \
X(GIFReg, TRXREG, 0x52) \
X(GIFReg, TRXDIR, 0x53) \
X(GIFReg, HWREG, 0x54) \
X(GIFReg, SIGNAL, 0x60) \
X(GIFReg, FINISH, 0x61) \
X(GIFReg, LABEL, 0x62)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GIFReg, GIFReg, u8, "UnknownReg")
#undef DEF_GIFReg
// clang-format on
template <typename Output, typename Input, typename std::enable_if<sizeof(Input) == sizeof(Output), bool>::type = true>
static constexpr Output BitCast(Input input)
{
Output output;
memcpy(&output, &input, sizeof(input));
return output;
}
template <typename Output = u32>
static constexpr Output GetBits(u64 value, u32 shift, u32 numbits)
{
return static_cast<Output>((value >> shift) & ((1ull << numbits) - 1));
}
template <typename Output = u32>
static constexpr Output GetBits(u128 value, u32 shift, u32 numbits)
{
u64 outval = 0;
if (shift == 0)
outval = value.lo;
else if (shift < 64)
outval = (value.lo >> shift) | (value.hi << (64 - shift));
else
outval = value.hi >> (shift - 64);
return static_cast<Output>(outval & ((1ull << numbits) - 1));
}
static constexpr const char* GetNameOneBit(u8 value, const char* zero, const char* one)
{
switch (value)
{
case 0:
return zero;
case 1:
return one;
default:
return "UNKNOWN";
}
}
static constexpr const char* GetNameBool(bool value)
{
return value ? "True" : "False";
}
static constexpr const char* GetNamePRIMPRIM(u8 prim)
{
switch (prim)
{
case 0:
return "Point";
case 1:
return "Line";
case 2:
return "Line Strip";
case 3:
return "Triangle";
case 4:
return "Triangle Strip";
case 5:
return "Triangle Fan";
case 6:
return "Sprite";
case 7:
return "Invalid";
default:
return "UNKNOWN";
}
}
static constexpr const char* GetNamePRIMIIP(u8 iip)
{
return GetNameOneBit(iip, "Flat Shading", "Gouraud Shading");
}
static constexpr const char* GetNamePRIMFST(u8 fst)
{
return GetNameOneBit(fst, "STQ Value", "UV Value");
}
static constexpr const char* GetNamePRIMCTXT(u8 ctxt)
{
return GetNameOneBit(ctxt, "Context 1", "Context 2");
}
static constexpr const char* GetNamePRIMFIX(u8 fix)
{
return GetNameOneBit(fix, "Unfixed", "Fixed");
}
static constexpr const char* GetNameTEXTCC(u8 tcc)
{
return GetNameOneBit(tcc, "RGB", "RGBA");
}
static constexpr const char* GetNameTEXTFX(u8 tfx)
{
switch (tfx)
{
case 0:
return "Modulate";
case 1:
return "Decal";
case 2:
return "Highlight";
case 3:
return "Highlight2";
default:
return "UNKNOWN";
}
}
static constexpr const char* GetNameTEXCSM(u8 csm)
{
return GetNameOneBit(csm, "CSM1", "CSM2");
}
static constexpr const char* GetNameTEXPSM(u8 psm)
{
switch (psm)
{
case 000:
return "PSMCT32";
case 001:
return "PSMCT24";
case 002:
return "PSMCT16";
case 012:
return "PSMCT16S";
case 023:
return "PSMT8";
case 024:
return "PSMT4";
case 033:
return "PSMT8H";
case 044:
return "PSMT4HL";
case 054:
return "PSMT4HH";
case 060:
return "PSMZ32";
case 061:
return "PSMZ24";
case 062:
return "PSMZ16";
case 072:
return "PSMZ16S";
default:
return "UNKNOWN";
}
}
static constexpr const char* GetNameTEXCPSM(u8 psm)
{
switch (psm)
{
case 000:
return "PSMCT32";
case 002:
return "PSMCT16";
case 012:
return "PSMCT16S";
default:
return "UNKNOWN";
}
}
} // namespace GSDumpTypes
class GSDumpFile
{
FILE* m_repack_fp;
public:
struct GSData
{
GSDumpTypes::GSType id;
std::unique_ptr<u8[]> data;
s32 length;
GSDumpTypes::GSTransferPath path;
};
using ByteArray = std::vector<u8>;
using GSDataArray = std::vector<GSData>;
virtual ~GSDumpFile();
static std::unique_ptr<GSDumpFile> OpenGSDump(const char* filename, const char* repack_filename = nullptr);
static bool GetPreviewImageFromDump(const char* filename, u32* width, u32* height, std::vector<u32>* pixels);
__fi const std::string& GetSerial() const { return m_serial; }
__fi u32 GetCRC() const { return m_crc; }
__fi const ByteArray& GetRegsData() const { return m_regs_data; }
__fi const ByteArray& GetStateData() const { return m_state_data; }
__fi const GSDataArray& GetPackets() const { return m_dump_packets; }
bool ReadFile();
protected:
FILE* m_fp;
GSDumpFile(FILE* file, FILE* repack_file);
void Repack(void* ptr, size_t size);
public:
virtual bool IsEof() = 0;
virtual bool Read(void* ptr, size_t size) = 0;
GSDumpFile(char* filename, const char* repack_filename);
GSDumpFile(FILE* file, FILE* repack_file);
virtual ~GSDumpFile();
void Repack(void* ptr, size_t size);
FILE* m_fp = nullptr;
private:
FILE* m_repack_fp = nullptr;
std::string m_serial;
u32 m_crc = 0;
std::vector<u8> m_regs_data;
std::vector<u8> m_state_data;
// TODO: Allocate a single, large buffer, and store pointers to
// each packet instead of a buffer per packet.
GSDataArray m_dump_packets;
};
class GSDumpLzma : public GSDumpFile
@ -48,7 +352,6 @@ class GSDumpLzma : public GSDumpFile
void Initialize();
public:
GSDumpLzma(char* filename, const char* repack_filename);
GSDumpLzma(FILE* file, FILE* repack_file);
virtual ~GSDumpLzma();
@ -59,7 +362,6 @@ public:
class GSDumpRaw : public GSDumpFile
{
public:
GSDumpRaw(char* filename, const char* repack_filename);
GSDumpRaw(FILE* file, FILE* repack_file);
virtual ~GSDumpRaw() = default;

View File

@ -25,8 +25,6 @@
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "gui/Resources/NoIcon.h"
#include "GS.h"
#include "GS/GSDump.h"
#include "HostDisplay.h"
#include "PathDefs.h"
@ -51,192 +49,13 @@
#include <functional>
#include <optional>
template <typename Output, typename Input, typename std::enable_if<sizeof(Input) == sizeof(Output), bool>::type = true>
static constexpr Output BitCast(Input input)
{
Output output;
memcpy(&output, &input, sizeof(input));
return output;
}
template <typename Output = u32>
static constexpr Output GetBits(u64 value, u32 shift, u32 numbits)
{
return static_cast<Output>((value >> shift) & ((1ull << numbits) - 1));
}
template <typename Output = u32>
static constexpr Output GetBits(u128 value, u32 shift, u32 numbits)
{
u64 outval = 0;
if (shift == 0)
outval = value.lo;
else if (shift < 64)
outval = (value.lo >> shift) | (value.hi << (64 - shift));
else
outval = value.hi >> (shift - 64);
return static_cast<Output>(outval & ((1ull << numbits) - 1));
}
static constexpr const char* GetNameOneBit(u8 value, const char* zero, const char* one)
{
switch (value)
{
case 0: return zero;
case 1: return one;
default: return "UNKNOWN";
}
}
static constexpr const char* GetNameBool(bool value)
{
return value ? "True" : "False";
}
static constexpr const char* GetNamePRIMPRIM(u8 prim)
{
switch (prim)
{
case 0: return "Point";
case 1: return "Line";
case 2: return "Line Strip";
case 3: return "Triangle";
case 4: return "Triangle Strip";
case 5: return "Triangle Fan";
case 6: return "Sprite";
case 7: return "Invalid";
default: return "UNKNOWN";
}
}
static constexpr const char* GetNamePRIMIIP(u8 iip)
{
return GetNameOneBit(iip, "Flat Shading", "Gouraud Shading");
}
static constexpr const char* GetNamePRIMFST(u8 fst)
{
return GetNameOneBit(fst, "STQ Value", "UV Value");
}
static constexpr const char* GetNamePRIMCTXT(u8 ctxt)
{
return GetNameOneBit(ctxt, "Context 1", "Context 2");
}
static constexpr const char* GetNamePRIMFIX(u8 fix)
{
return GetNameOneBit(fix, "Unfixed", "Fixed");
}
static constexpr const char* GetNameTEXTCC(u8 tcc)
{
return GetNameOneBit(tcc, "RGB", "RGBA");
}
static constexpr const char* GetNameTEXTFX(u8 tfx)
{
switch (tfx)
{
case 0: return "Modulate";
case 1: return "Decal";
case 2: return "Highlight";
case 3: return "Highlight2";
default: return "UNKNOWN";
}
}
static constexpr const char* GetNameTEXCSM(u8 csm)
{
return GetNameOneBit(csm, "CSM1", "CSM2");
}
static constexpr const char* GetNameTEXPSM(u8 psm)
{
switch (psm)
{
case 000: return "PSMCT32";
case 001: return "PSMCT24";
case 002: return "PSMCT16";
case 012: return "PSMCT16S";
case 023: return "PSMT8";
case 024: return "PSMT4";
case 033: return "PSMT8H";
case 044: return "PSMT4HL";
case 054: return "PSMT4HH";
case 060: return "PSMZ32";
case 061: return "PSMZ24";
case 062: return "PSMZ16";
case 072: return "PSMZ16S";
default: return "UNKNOWN";
}
}
static constexpr const char* GetNameTEXCPSM(u8 psm)
{
switch (psm)
{
case 000: return "PSMCT32";
case 002: return "PSMCT16";
case 012: return "PSMCT16S";
default: return "UNKNOWN";
}
}
static std::unique_ptr<GSDumpFile> GetDumpFile(const std::string& filename)
{
std::FILE* fp = FileSystem::OpenCFile(filename.c_str(), "rb");
if (!fp)
return nullptr;
if (StringUtil::EndsWith(filename, ".xz"))
return std::make_unique<GSDumpLzma>(fp, nullptr);
else
return std::make_unique<GSDumpRaw>(fp, nullptr);
}
static bool GetPreviewImageFromDump(const std::string& filename, u32* width, u32* height, std::vector<u32>* pixels)
{
try
{
std::unique_ptr<GSDumpFile> dump = GetDumpFile(filename);
if (!dump)
return false;
u32 crc;
dump->Read(&crc, sizeof(crc));
if (crc != 0xFFFFFFFFu)
{
// not new header dump, so no preview
return false;
}
u32 header_size;
dump->Read(&header_size, sizeof(header_size));
if (header_size < sizeof(GSDumpHeader))
{
// doesn't have the screenshot fields
return false;
}
std::unique_ptr<u8[]> header_bits = std::make_unique<u8[]>(header_size);
dump->Read(header_bits.get(), header_size);
GSDumpHeader header;
std::memcpy(&header, header_bits.get(), sizeof(header));
if (header.screenshot_size == 0 ||
header.screenshot_size < (header.screenshot_width * header.screenshot_height * sizeof(u32)) ||
(static_cast<u64>(header.screenshot_offset) + header.screenshot_size) > header_size)
{
// doesn't have a screenshot
return false;
}
*width = header.screenshot_width;
*height = header.screenshot_height;
pixels->resize(header.screenshot_width * header.screenshot_height);
std::memcpy(pixels->data(), header_bits.get() + header.screenshot_offset, header.screenshot_size);
return true;
}
catch (...)
{
return false;
}
}
using namespace GSDumpTypes;
static std::optional<wxImage> GetPreviewImageFromDump(const std::string& filename)
{
std::vector<u32> pixels;
u32 width, height;
if (!GetPreviewImageFromDump(filename, &width, &height, &pixels))
if (!GSDumpFile::GetPreviewImageFromDump(filename.c_str(), &width, &height, &pixels))
return std::nullopt;
// strip alpha bytes because wx is dumb and stores on a separate plane
@ -471,7 +290,7 @@ void Dialogs::GSDumpDialog::RunDump(wxCommandEvent& event)
if (!m_run->IsEnabled())
return;
m_thread->m_dump_file = GetDumpFile(StringUtil::wxStringToUTF8String(m_selected_dump));
m_thread->m_dump_file = GSDumpFile::OpenGSDump(m_selected_dump.ToUTF8());
if (!m_thread->m_dump_file)
{
wxString s;
@ -583,11 +402,11 @@ void Dialogs::GSDumpDialog::GenPacketList()
m_gif_items.clear();
wxTreeItemId mainrootId = m_gif_list->AddRoot("root");
wxTreeItemId rootId = m_gif_list->AppendItem(mainrootId, "0 - VSync");
for (auto& element : m_dump_packets)
for (auto& element : m_thread->m_dump_file->GetPackets())
{
wxString s, t;
element.id == GSType::Transfer ? t.Printf(" - %s", GetName(element.path)) : t.Printf("");
s.Printf("%d - %s%s - %d byte", i, GetName(element.id), t, element.length);
element.id == GSType::Transfer ? t.Printf(" - %s", GSDumpTypes::GetName(element.path)) : t.Printf("");
s.Printf("%d - %s%s - %d byte", i, GSDumpTypes::GetName(element.id), t, element.length);
if (element.id == GSType::VSync)
{
m_gif_list->SetItemText(rootId, s);
@ -606,7 +425,7 @@ void Dialogs::GSDumpDialog::GenPacketList()
m_gif_list->SelectItem(m_gif_items[0]);
}
void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
void Dialogs::GSDumpDialog::GenPacketInfo(const GSDumpFile::GSData& dump)
{
m_gif_packet->DeleteAllItems();
wxTreeItemId rootId = m_gif_packet->AddRoot("root");
@ -619,7 +438,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
int idx = 0;
while (remaining >= 16)
{
wxTreeItemId trootId = m_gif_packet->AppendItem(rootId, wxString::Format("Transfer Path %s Packet %u", GetName(dump.path), idx));
wxTreeItemId trootId = m_gif_packet->AppendItem(rootId, wxString::Format("Transfer Path %s Packet %u", GSDumpTypes::GetName(dump.path), idx));
ParseTransfer(trootId, data);
m_gif_packet->Expand(trootId);
u32 size = ReadPacketSize(data);
@ -667,7 +486,7 @@ void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, u8* data)
m_gif_packet->AppendItem(trootId, wxString::Format("nloop = %u", nloop));
m_gif_packet->AppendItem(trootId, wxString::Format("eop = %u", eop));
m_gif_packet->AppendItem(trootId, wxString::Format("flg = %s", GetName(flg)));
m_gif_packet->AppendItem(trootId, wxString::Format("flg = %s", GSDumpTypes::GetName(flg)));
m_gif_packet->AppendItem(trootId, wxString::Format("pre = %u", pre));
if (pre)
{
@ -719,12 +538,12 @@ void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, u8* data)
void Dialogs::GSDumpDialog::ParsePacket(wxTreeEvent& event)
{
GenPacketInfo(m_dump_packets[wxAtoi(m_gif_list->GetItemText(event.GetItem()).BeforeFirst('-'))]);
GenPacketInfo(m_thread->m_dump_file->GetPackets()[wxAtoi(m_gif_list->GetItemText(event.GetItem()).BeforeFirst('-'))]);
}
void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data, bool packed)
{
wxTreeItemId rootId = m_gif_packet->AppendItem(id, wxString(GetName(reg)));
wxTreeItemId rootId = m_gif_packet->AppendItem(id, wxString(GSDumpTypes::GetName(reg)));
switch (reg)
{
case GIFReg::PRIM:
@ -752,7 +571,7 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
q = BitCast<float>(data._u32[1]);
}
m_gif_packet->SetItemText(rootId, wxString::Format("%s - #%02x%02x%02x%02x, %g", GetName(reg), r, g, b, a, q));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - #%02x%02x%02x%02x, %g", GSDumpTypes::GetName(reg), r, g, b, a, q));
m_gif_packet->AppendItem(rootId, wxString::Format("R = %u", r));
m_gif_packet->AppendItem(rootId, wxString::Format("G = %u", g));
m_gif_packet->AppendItem(rootId, wxString::Format("B = %u", b));
@ -770,11 +589,11 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
{
m_stored_q = BitCast<float>(data._u32[2]);
m_gif_packet->AppendItem(rootId, wxString::Format("Q = %g", m_stored_q));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g, %g)", GetName(reg), s, t, m_stored_q));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g, %g)", GSDumpTypes::GetName(reg), s, t, m_stored_q));
}
else
{
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GetName(reg), s, t));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GSDumpTypes::GetName(reg), s, t));
}
break;
}
@ -784,15 +603,15 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
float v = static_cast<float>(GetBits(data, packed ? 32 : 16, 14)) / 16.f;
m_gif_packet->AppendItem(rootId, wxString::Format("U = %g", u));
m_gif_packet->AppendItem(rootId, wxString::Format("V = %g", v));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GetName(reg), u, v));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GSDumpTypes::GetName(reg), u, v));
break;
}
case GIFReg::XYZF2:
case GIFReg::XYZF3:
{
const char* name = GetName(reg);
const char* name = GSDumpTypes::GetName(reg);
if (packed && (reg == GIFReg::XYZF2) && GetBits(data, 111, 1))
name = GetName(GIFReg::XYZF3);
name = GSDumpTypes::GetName(GIFReg::XYZF3);
float x, y;
u32 z, f;
@ -822,9 +641,9 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
case GIFReg::XYZ2:
case GIFReg::XYZ3:
{
const char* name = GetName(reg);
const char* name = GSDumpTypes::GetName(reg);
if (packed && (reg == GIFReg::XYZ2) && GetBits(data, 111, 1))
name = GetName(GIFReg::XYZ3);
name = GSDumpTypes::GetName(GIFReg::XYZ3);
float x, y;
u32 z;
@ -855,7 +674,7 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
u32 tw = GetBits(data, 26, 4);
u32 th = GetBits(data, 30, 4);
m_gif_packet->SetItemText(rootId, wxString::Format("%s - %ux%u %s", GetName(reg), 1 << tw, 1 << th, GetNameTEXPSM(psm)));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - %ux%u %s", GSDumpTypes::GetName(reg), 1 << tw, 1 << th, GetNameTEXPSM(psm)));
m_gif_packet->AppendItem(rootId, wxString::Format("TBP0 = %u", GetBits(data, 0, 14)));
m_gif_packet->AppendItem(rootId, wxString::Format("TBW = %u", GetBits(data, 14, 6)));
m_gif_packet->AppendItem(rootId, wxString::Format("PSM = %s", GetNameTEXPSM(psm)));
@ -874,7 +693,7 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
{
u32 f = GetBits(data, packed ? 100 : 56, 8);
m_gif_packet->AppendItem(rootId, wxString::Format("F = %u", f));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - %u", GetName(reg), f));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - %u", GSDumpTypes::GetName(reg), f));
break;
}
case GIFReg::AD:
@ -915,7 +734,7 @@ void Dialogs::GSDumpDialog::ParseTreePrim(wxTreeItemId& id, u32 prim)
m_gif_packet->Expand(id);
}
void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSData& event, u8* regs)
void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSDumpFile::GSData& event, u8* regs)
{
switch (event.id)
{
@ -993,8 +812,12 @@ void Dialogs::GSDumpDialog::GSThread::OnStop()
void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
{
GSDump::isRunning = true;
u32 crc = 0, ss = 0;
if (!m_dump_file->ReadFile())
{
OnStop();
return;
}
GSRendererType renderer = g_Conf->EmuOptions.GS.Renderer;
switch (m_renderer)
{
@ -1021,79 +844,6 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
default:
break;
}
u8 regs[8192];
m_dump_file->Read(&crc, 4);
m_dump_file->Read(&ss, 4);
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()};
m_root_window->m_dump_packets.clear();
while (!m_dump_file->IsEof())
{
GSType id;
GSTransferPath id_transfer = GSTransferPath::Dummy;
m_dump_file->Read(&id, 1);
s32 size = 0;
switch (id)
{
case GSType::Transfer:
m_dump_file->Read(&id_transfer, 1);
m_dump_file->Read(&size, 4);
break;
case GSType::VSync:
size = 1;
break;
case GSType::ReadFIFO2:
size = 4;
break;
case GSType::Registers:
size = 8192;
break;
}
// 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});
}
GSinit();
sApp.OpenGsPanel();
@ -1109,25 +859,35 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
}
Pcsx2Config::GSOptions config(g_Conf->EmuOptions.GS);
if (!serial.empty())
if (!m_dump_file->GetSerial().empty())
{
if (const GameDatabaseSchema::GameEntry* entry = GameDatabase::findGame(serial); entry)
if (const GameDatabaseSchema::GameEntry* entry = GameDatabase::findGame(m_dump_file->GetSerial()); entry)
{
// apply hardware fixes to config before opening (e.g. tex in rt)
entry->applyGSHardwareFixes(config);
}
}
u8 regs[8192] = {};
if (!m_dump_file->GetRegsData().empty())
std::memcpy(regs, m_dump_file->GetRegsData().data(), std::min(m_dump_file->GetRegsData().size(), sizeof(regs)));
if (!GSopen(config, renderer, regs))
{
OnStop();
return;
}
GSsetGameCRC((int)crc, 0);
GSsetGameCRC((int)m_dump_file->GetCRC(), 0);
freezeData fd = {static_cast<int>(m_dump_file->GetStateData().size()),
const_cast<u8*>(m_dump_file->GetStateData().data())};
if (GSfreeze(FreezeAction::Load, &fd))
GSDump::isRunning = false;
{
OnStop();
return;
}
GSvsync(1, false);
GSreset();
GSfreeze(FreezeAction::Load, &fd);
@ -1136,6 +896,7 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
m_debug_index = 0;
size_t debug_idx = 0;
GSDump::isRunning = true;
while (GSDump::isRunning)
{
if (m_debug)
@ -1145,7 +906,7 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
switch (m_root_window->m_button_events[0].btn)
{
case Step:
if (debug_idx >= m_root_window->m_dump_packets.size())
if (debug_idx >= m_dump_file->GetPackets().size())
debug_idx = 0;
m_debug_index = debug_idx;
break;
@ -1155,13 +916,13 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
debug_idx = 0;
break;
case RunVSync:
if (debug_idx >= m_root_window->m_dump_packets.size())
if (debug_idx >= m_dump_file->GetPackets().size())
debug_idx = 1;
if ((debug_idx + 1) < m_root_window->m_dump_packets.size())
if ((debug_idx + 1) < m_dump_file->GetPackets().size())
{
auto it = std::find_if(m_root_window->m_dump_packets.begin() + debug_idx + 1, m_root_window->m_dump_packets.end(), [](const GSData& gs) { return gs.id == GSType::Registers; });
if (it != std::end(m_root_window->m_dump_packets))
m_debug_index = std::distance(m_root_window->m_dump_packets.begin(), it);
auto it = std::find_if(m_dump_file->GetPackets().begin() + debug_idx + 1, m_dump_file->GetPackets().end(), [](const GSDumpFile::GSData& gs) { return gs.id == GSType::Registers; });
if (it != std::end(m_dump_file->GetPackets()))
m_debug_index = std::distance(m_dump_file->GetPackets().begin(), it);
}
break;
}
@ -1171,12 +932,12 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
{
while (debug_idx <= m_debug_index)
{
m_root_window->ProcessDumpEvent(m_root_window->m_dump_packets[debug_idx++], regs);
m_root_window->ProcessDumpEvent(m_dump_file->GetPackets()[debug_idx++], regs);
}
if ((debug_idx + 1) < m_root_window->m_dump_packets.size())
if ((debug_idx + 1) < m_dump_file->GetPackets().size())
{
auto it = std::find_if(m_root_window->m_dump_packets.begin() + debug_idx + 1, m_root_window->m_dump_packets.end(), [](const GSData& gs) { return gs.id == GSType::Registers; });
if (it != std::end(m_root_window->m_dump_packets))
auto it = std::find_if(m_dump_file->GetPackets().begin() + debug_idx + 1, m_dump_file->GetPackets().end(), [](const GSDumpFile::GSData& gs) { return gs.id == GSType::Registers; });
if (it != std::end(m_dump_file->GetPackets()))
m_root_window->ProcessDumpEvent(*it, regs);
}
}
@ -1185,11 +946,11 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
m_root_window->ProcessDumpEvent({GSType::VSync, 0, 0, GSTransferPath::Dummy}, regs);
}
}
else if (m_root_window->m_dump_packets.size())
else if (m_dump_file->GetPackets().size())
{
while (i < m_root_window->m_dump_packets.size())
while (i < m_dump_file->GetPackets().size())
{
GSData& packet = m_root_window->m_dump_packets[i++];
const GSDumpFile::GSData& packet = m_dump_file->GetPackets()[i++];
m_root_window->ProcessDumpEvent(packet, regs);
if (packet.id == GSType::VSync)
{
@ -1209,7 +970,7 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
}
}
if (i >= m_root_window->m_dump_packets.size())
if (i >= m_dump_file->GetPackets().size())
i = 0;
}
if (window)

View File

@ -25,28 +25,6 @@
#include <wx/treectrl.h>
#include <wx/fswatcher.h>
#define GEN_REG_ENUM_CLASS_CONTENT(ClassName, EntryName, Value) \
EntryName = Value,
#define GEN_REG_GETNAME_CONTENT(ClassName, EntryName, Value) \
case ClassName::EntryName: \
return #EntryName;
#define GEN_REG_ENUM_CLASS_AND_GETNAME(Macro, ClassName, Type, DefaultString) \
enum class ClassName : Type \
{ \
Macro(GEN_REG_ENUM_CLASS_CONTENT) \
}; \
static constexpr const char* GetName(ClassName reg) \
{ \
switch (reg) \
{ \
Macro(GEN_REG_GETNAME_CONTENT) \
default: \
return DefaultString; \
} \
}
class FirstTimeWizard : public wxWizard
{
typedef wxWizard _parent;
@ -147,102 +125,6 @@ namespace Dialogs
ID_FRAMERATE,
};
// clang-format off
#define DEF_GSType(X) \
X(GSType, Transfer, 0) \
X(GSType, VSync, 1) \
X(GSType, ReadFIFO2, 2) \
X(GSType, Registers, 3)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GSType, GSType, u8, "UnknownType")
#undef DEF_GSType
#define DEF_GSTransferPath(X) \
X(GSTransferPath, Path1Old, 0) \
X(GSTransferPath, Path2, 1) \
X(GSTransferPath, Path3, 2) \
X(GSTransferPath, Path1New, 3) \
X(GSTransferPath, Dummy, 4)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GSTransferPath, GSTransferPath, u8, "UnknownPath")
#undef DEF_GSTransferPath
#define DEF_GIFFlag(X) \
X(GIFFlag, PACKED, 0) \
X(GIFFlag, REGLIST, 1) \
X(GIFFlag, IMAGE, 2) \
X(GIFFlag, IMAGE2, 3)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GIFFlag, GIFFlag, u8, "UnknownFlag")
#undef DEF_GifFlag
#define DEF_GIFReg(X) \
X(GIFReg, PRIM, 0x00) \
X(GIFReg, RGBAQ, 0x01) \
X(GIFReg, ST, 0x02) \
X(GIFReg, UV, 0x03) \
X(GIFReg, XYZF2, 0x04) \
X(GIFReg, XYZ2, 0x05) \
X(GIFReg, TEX0_1, 0x06) \
X(GIFReg, TEX0_2, 0x07) \
X(GIFReg, CLAMP_1, 0x08) \
X(GIFReg, CLAMP_2, 0x09) \
X(GIFReg, FOG, 0x0a) \
X(GIFReg, XYZF3, 0x0c) \
X(GIFReg, XYZ3, 0x0d) \
X(GIFReg, AD, 0x0e) \
X(GIFReg, NOP, 0x0f) \
X(GIFReg, TEX1_1, 0x14) \
X(GIFReg, TEX1_2, 0x15) \
X(GIFReg, TEX2_1, 0x16) \
X(GIFReg, TEX2_2, 0x17) \
X(GIFReg, XYOFFSET_1, 0x18) \
X(GIFReg, XYOFFSET_2, 0x19) \
X(GIFReg, PRMODECONT, 0x1a) \
X(GIFReg, PRMODE, 0x1b) \
X(GIFReg, TEXCLUT, 0x1c) \
X(GIFReg, SCANMSK, 0x22) \
X(GIFReg, MIPTBP1_1, 0x34) \
X(GIFReg, MIPTBP1_2, 0x35) \
X(GIFReg, MIPTBP2_1, 0x36) \
X(GIFReg, MIPTBP2_2, 0x37) \
X(GIFReg, TEXA, 0x3b) \
X(GIFReg, FOGCOL, 0x3d) \
X(GIFReg, TEXFLUSH, 0x3f) \
X(GIFReg, SCISSOR_1, 0x40) \
X(GIFReg, SCISSOR_2, 0x41) \
X(GIFReg, ALPHA_1, 0x42) \
X(GIFReg, ALPHA_2, 0x43) \
X(GIFReg, DIMX, 0x44) \
X(GIFReg, DTHE, 0x45) \
X(GIFReg, COLCLAMP, 0x46) \
X(GIFReg, TEST_1, 0x47) \
X(GIFReg, TEST_2, 0x48) \
X(GIFReg, PABE, 0x49) \
X(GIFReg, FBA_1, 0x4a) \
X(GIFReg, FBA_2, 0x4b) \
X(GIFReg, FRAME_1, 0x4c) \
X(GIFReg, FRAME_2, 0x4d) \
X(GIFReg, ZBUF_1, 0x4e) \
X(GIFReg, ZBUF_2, 0x4f) \
X(GIFReg, BITBLTBUF, 0x50) \
X(GIFReg, TRXPOS, 0x51) \
X(GIFReg, TRXREG, 0x52) \
X(GIFReg, TRXDIR, 0x53) \
X(GIFReg, HWREG, 0x54) \
X(GIFReg, SIGNAL, 0x60) \
X(GIFReg, FINISH, 0x61) \
X(GIFReg, LABEL, 0x62)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GIFReg, GIFReg, u8, "UnknownReg")
#undef DEF_GIFReg
// clang-format on
struct GSData
{
GSType id;
std::unique_ptr<u8[]> data;
int length;
GSTransferPath path;
};
enum ButtonState
{
Step,
@ -256,16 +138,15 @@ namespace Dialogs
int index;
};
std::vector<GSEvent> m_button_events;
std::vector<GSData> m_dump_packets;
std::vector<wxTreeItemId> m_gif_items;
float m_stored_q = 1.0;
void ProcessDumpEvent(const GSData& event, u8* regs);
void ProcessDumpEvent(const GSDumpFile::GSData& event, u8* regs);
u32 ReadPacketSize(const void* packet);
void GenPacketList();
void GenPacketInfo(GSData& dump);
void GenPacketInfo(const GSDumpFile::GSData& dump);
void ParseTransfer(wxTreeItemId& id, u8* data);
void ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data, bool packed);
void ParseTreeReg(wxTreeItemId& id, GSDumpTypes::GIFReg reg, u128 data, bool packed);
void ParseTreePrim(wxTreeItemId& id, u32 prim);
void CloseDump(wxCommandEvent& event);
class GSThread : public pxThread