mirror of https://github.com/PCSX2/pcsx2.git
GS: Move dump file loading to core
This commit is contained in:
parent
4f4b14dd4d
commit
4d85d916b7
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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(®s, 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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue