mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-01 10:39:45 -06:00
Reformat all the things. Have fun with merge conflicts.
This commit is contained in:
@ -19,263 +19,274 @@
|
||||
|
||||
namespace FifoAnalyzer
|
||||
{
|
||||
|
||||
bool s_DrawingObject;
|
||||
FifoAnalyzer::CPMemory s_CpMem;
|
||||
|
||||
void Init()
|
||||
{
|
||||
VertexLoader_Normal::Init();
|
||||
VertexLoader_Normal::Init();
|
||||
}
|
||||
|
||||
u8 ReadFifo8(u8*& data)
|
||||
{
|
||||
u8 value = data[0];
|
||||
data += 1;
|
||||
return value;
|
||||
u8 value = data[0];
|
||||
data += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
u16 ReadFifo16(u8*& data)
|
||||
{
|
||||
u16 value = Common::swap16(data);
|
||||
data += 2;
|
||||
return value;
|
||||
u16 value = Common::swap16(data);
|
||||
data += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
u32 ReadFifo32(u8*& data)
|
||||
{
|
||||
u32 value = Common::swap32(data);
|
||||
data += 4;
|
||||
return value;
|
||||
u32 value = Common::swap32(data);
|
||||
data += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
u32 AnalyzeCommand(u8* data, DecodeMode mode)
|
||||
{
|
||||
u8* dataStart = data;
|
||||
u8* dataStart = data;
|
||||
|
||||
int cmd = ReadFifo8(data);
|
||||
int cmd = ReadFifo8(data);
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case GX_NOP:
|
||||
case 0x44:
|
||||
case GX_CMD_INVL_VC:
|
||||
break;
|
||||
switch (cmd)
|
||||
{
|
||||
case GX_NOP:
|
||||
case 0x44:
|
||||
case GX_CMD_INVL_VC:
|
||||
break;
|
||||
|
||||
case GX_LOAD_CP_REG:
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
case GX_LOAD_CP_REG:
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
|
||||
u32 cmd2 = ReadFifo8(data);
|
||||
u32 value = ReadFifo32(data);
|
||||
LoadCPReg(cmd2, value, s_CpMem);
|
||||
break;
|
||||
}
|
||||
u32 cmd2 = ReadFifo8(data);
|
||||
u32 value = ReadFifo32(data);
|
||||
LoadCPReg(cmd2, value, s_CpMem);
|
||||
break;
|
||||
}
|
||||
|
||||
case GX_LOAD_XF_REG:
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
case GX_LOAD_XF_REG:
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
|
||||
u32 cmd2 = ReadFifo32(data);
|
||||
u8 streamSize = ((cmd2 >> 16) & 15) + 1;
|
||||
u32 cmd2 = ReadFifo32(data);
|
||||
u8 streamSize = ((cmd2 >> 16) & 15) + 1;
|
||||
|
||||
data += streamSize * 4;
|
||||
break;
|
||||
}
|
||||
data += streamSize * 4;
|
||||
break;
|
||||
}
|
||||
|
||||
case GX_LOAD_INDX_A:
|
||||
case GX_LOAD_INDX_B:
|
||||
case GX_LOAD_INDX_C:
|
||||
case GX_LOAD_INDX_D:
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
case GX_LOAD_INDX_A:
|
||||
case GX_LOAD_INDX_B:
|
||||
case GX_LOAD_INDX_C:
|
||||
case GX_LOAD_INDX_D:
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
|
||||
int array = 0xc + (cmd - GX_LOAD_INDX_A) / 8;
|
||||
u32 value = ReadFifo32(data);
|
||||
int array = 0xc + (cmd - GX_LOAD_INDX_A) / 8;
|
||||
u32 value = ReadFifo32(data);
|
||||
|
||||
if (mode == DECODE_RECORD)
|
||||
FifoRecordAnalyzer::ProcessLoadIndexedXf(value, array);
|
||||
break;
|
||||
}
|
||||
if (mode == DECODE_RECORD)
|
||||
FifoRecordAnalyzer::ProcessLoadIndexedXf(value, array);
|
||||
break;
|
||||
}
|
||||
|
||||
case GX_CMD_CALL_DL:
|
||||
// The recorder should have expanded display lists into the fifo stream and skipped the call to start them
|
||||
// That is done to make it easier to track where memory is updated
|
||||
_assert_(false);
|
||||
data += 8;
|
||||
break;
|
||||
case GX_CMD_CALL_DL:
|
||||
// The recorder should have expanded display lists into the fifo stream and skipped the call to
|
||||
// start them
|
||||
// That is done to make it easier to track where memory is updated
|
||||
_assert_(false);
|
||||
data += 8;
|
||||
break;
|
||||
|
||||
case GX_LOAD_BP_REG:
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
ReadFifo32(data);
|
||||
break;
|
||||
}
|
||||
case GX_LOAD_BP_REG:
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
ReadFifo32(data);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (cmd & 0x80)
|
||||
{
|
||||
s_DrawingObject = true;
|
||||
default:
|
||||
if (cmd & 0x80)
|
||||
{
|
||||
s_DrawingObject = true;
|
||||
|
||||
int sizes[21];
|
||||
FifoAnalyzer::CalculateVertexElementSizes(sizes, cmd & GX_VAT_MASK, s_CpMem);
|
||||
int sizes[21];
|
||||
FifoAnalyzer::CalculateVertexElementSizes(sizes, cmd & GX_VAT_MASK, s_CpMem);
|
||||
|
||||
// Determine offset of each element that might be a vertex array
|
||||
// The first 9 elements are never vertex arrays so we just accumulate their sizes.
|
||||
int offsets[12];
|
||||
int offset = std::accumulate(&sizes[0], &sizes[9], 0u);
|
||||
for (int i = 0; i < 12; ++i)
|
||||
{
|
||||
offsets[i] = offset;
|
||||
offset += sizes[i + 9];
|
||||
}
|
||||
// Determine offset of each element that might be a vertex array
|
||||
// The first 9 elements are never vertex arrays so we just accumulate their sizes.
|
||||
int offsets[12];
|
||||
int offset = std::accumulate(&sizes[0], &sizes[9], 0u);
|
||||
for (int i = 0; i < 12; ++i)
|
||||
{
|
||||
offsets[i] = offset;
|
||||
offset += sizes[i + 9];
|
||||
}
|
||||
|
||||
int vertexSize = offset;
|
||||
int numVertices = ReadFifo16(data);
|
||||
int vertexSize = offset;
|
||||
int numVertices = ReadFifo16(data);
|
||||
|
||||
if (mode == DECODE_RECORD && numVertices > 0)
|
||||
{
|
||||
for (int i = 0; i < 12; ++i)
|
||||
{
|
||||
FifoRecordAnalyzer::WriteVertexArray(i, data + offsets[i], vertexSize, numVertices);
|
||||
}
|
||||
}
|
||||
if (mode == DECODE_RECORD && numVertices > 0)
|
||||
{
|
||||
for (int i = 0; i < 12; ++i)
|
||||
{
|
||||
FifoRecordAnalyzer::WriteVertexArray(i, data + offsets[i], vertexSize, numVertices);
|
||||
}
|
||||
}
|
||||
|
||||
data += numVertices * vertexSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("FifoPlayer: Unknown Opcode (0x%x).\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
data += numVertices * vertexSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("FifoPlayer: Unknown Opcode (0x%x).\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return (u32)(data - dataStart);
|
||||
return (u32)(data - dataStart);
|
||||
}
|
||||
|
||||
void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem)
|
||||
{
|
||||
switch (subCmd & 0xF0)
|
||||
{
|
||||
case 0x50:
|
||||
cpMem.vtxDesc.Hex &= ~0x1FFFF; // keep the Upper bits
|
||||
cpMem.vtxDesc.Hex |= value;
|
||||
break;
|
||||
switch (subCmd & 0xF0)
|
||||
{
|
||||
case 0x50:
|
||||
cpMem.vtxDesc.Hex &= ~0x1FFFF; // keep the Upper bits
|
||||
cpMem.vtxDesc.Hex |= value;
|
||||
break;
|
||||
|
||||
case 0x60:
|
||||
cpMem.vtxDesc.Hex &= 0x1FFFF; // keep the lower 17Bits
|
||||
cpMem.vtxDesc.Hex |= (u64)value << 17;
|
||||
break;
|
||||
case 0x60:
|
||||
cpMem.vtxDesc.Hex &= 0x1FFFF; // keep the lower 17Bits
|
||||
cpMem.vtxDesc.Hex |= (u64)value << 17;
|
||||
break;
|
||||
|
||||
case 0x70:
|
||||
_assert_((subCmd & 0x0F) < 8);
|
||||
cpMem.vtxAttr[subCmd & 7].g0.Hex = value;
|
||||
break;
|
||||
case 0x70:
|
||||
_assert_((subCmd & 0x0F) < 8);
|
||||
cpMem.vtxAttr[subCmd & 7].g0.Hex = value;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
_assert_((subCmd & 0x0F) < 8);
|
||||
cpMem.vtxAttr[subCmd & 7].g1.Hex = value;
|
||||
break;
|
||||
case 0x80:
|
||||
_assert_((subCmd & 0x0F) < 8);
|
||||
cpMem.vtxAttr[subCmd & 7].g1.Hex = value;
|
||||
break;
|
||||
|
||||
case 0x90:
|
||||
_assert_((subCmd & 0x0F) < 8);
|
||||
cpMem.vtxAttr[subCmd & 7].g2.Hex = value;
|
||||
break;
|
||||
case 0x90:
|
||||
_assert_((subCmd & 0x0F) < 8);
|
||||
cpMem.vtxAttr[subCmd & 7].g2.Hex = value;
|
||||
break;
|
||||
|
||||
case 0xA0:
|
||||
cpMem.arrayBases[subCmd & 0xF] = value;
|
||||
break;
|
||||
case 0xA0:
|
||||
cpMem.arrayBases[subCmd & 0xF] = value;
|
||||
break;
|
||||
|
||||
case 0xB0:
|
||||
cpMem.arrayStrides[subCmd & 0xF] = value & 0xFF;
|
||||
break;
|
||||
}
|
||||
case 0xB0:
|
||||
cpMem.arrayStrides[subCmd & 0xF] = value & 0xFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CalculateVertexElementSizes(int sizes[], int vatIndex, const CPMemory& cpMem)
|
||||
{
|
||||
const TVtxDesc &vtxDesc = cpMem.vtxDesc;
|
||||
const VAT &vtxAttr = cpMem.vtxAttr[vatIndex];
|
||||
const TVtxDesc& vtxDesc = cpMem.vtxDesc;
|
||||
const VAT& vtxAttr = cpMem.vtxAttr[vatIndex];
|
||||
|
||||
// Colors
|
||||
const u64 colDesc[2] = {vtxDesc.Color0, vtxDesc.Color1};
|
||||
const u32 colComp[2] = {vtxAttr.g0.Color0Comp, vtxAttr.g0.Color1Comp};
|
||||
// Colors
|
||||
const u64 colDesc[2] = {vtxDesc.Color0, vtxDesc.Color1};
|
||||
const u32 colComp[2] = {vtxAttr.g0.Color0Comp, vtxAttr.g0.Color1Comp};
|
||||
|
||||
const u32 tcElements[8] =
|
||||
{
|
||||
vtxAttr.g0.Tex0CoordElements, vtxAttr.g1.Tex1CoordElements, vtxAttr.g1.Tex2CoordElements,
|
||||
vtxAttr.g1.Tex3CoordElements, vtxAttr.g1.Tex4CoordElements, vtxAttr.g2.Tex5CoordElements,
|
||||
vtxAttr.g2.Tex6CoordElements, vtxAttr.g2.Tex7CoordElements
|
||||
};
|
||||
const u32 tcElements[8] = {vtxAttr.g0.Tex0CoordElements, vtxAttr.g1.Tex1CoordElements,
|
||||
vtxAttr.g1.Tex2CoordElements, vtxAttr.g1.Tex3CoordElements,
|
||||
vtxAttr.g1.Tex4CoordElements, vtxAttr.g2.Tex5CoordElements,
|
||||
vtxAttr.g2.Tex6CoordElements, vtxAttr.g2.Tex7CoordElements};
|
||||
|
||||
const u32 tcFormat[8] =
|
||||
{
|
||||
vtxAttr.g0.Tex0CoordFormat, vtxAttr.g1.Tex1CoordFormat, vtxAttr.g1.Tex2CoordFormat,
|
||||
vtxAttr.g1.Tex3CoordFormat, vtxAttr.g1.Tex4CoordFormat, vtxAttr.g2.Tex5CoordFormat,
|
||||
vtxAttr.g2.Tex6CoordFormat, vtxAttr.g2.Tex7CoordFormat
|
||||
};
|
||||
const u32 tcFormat[8] = {vtxAttr.g0.Tex0CoordFormat, vtxAttr.g1.Tex1CoordFormat,
|
||||
vtxAttr.g1.Tex2CoordFormat, vtxAttr.g1.Tex3CoordFormat,
|
||||
vtxAttr.g1.Tex4CoordFormat, vtxAttr.g2.Tex5CoordFormat,
|
||||
vtxAttr.g2.Tex6CoordFormat, vtxAttr.g2.Tex7CoordFormat};
|
||||
|
||||
// Add position and texture matrix indices
|
||||
u64 vtxDescHex = cpMem.vtxDesc.Hex;
|
||||
for (int i = 0; i < 9; ++i)
|
||||
{
|
||||
sizes[i] = vtxDescHex & 1;
|
||||
vtxDescHex >>= 1;
|
||||
}
|
||||
// Add position and texture matrix indices
|
||||
u64 vtxDescHex = cpMem.vtxDesc.Hex;
|
||||
for (int i = 0; i < 9; ++i)
|
||||
{
|
||||
sizes[i] = vtxDescHex & 1;
|
||||
vtxDescHex >>= 1;
|
||||
}
|
||||
|
||||
// Position
|
||||
sizes[9] = VertexLoader_Position::GetSize(vtxDesc.Position, vtxAttr.g0.PosFormat, vtxAttr.g0.PosElements);
|
||||
// Position
|
||||
sizes[9] = VertexLoader_Position::GetSize(vtxDesc.Position, vtxAttr.g0.PosFormat,
|
||||
vtxAttr.g0.PosElements);
|
||||
|
||||
// Normals
|
||||
if (vtxDesc.Normal != NOT_PRESENT)
|
||||
{
|
||||
sizes[10] = VertexLoader_Normal::GetSize(vtxDesc.Normal, vtxAttr.g0.NormalFormat, vtxAttr.g0.NormalElements, vtxAttr.g0.NormalIndex3);
|
||||
}
|
||||
else
|
||||
{
|
||||
sizes[10] = 0;
|
||||
}
|
||||
// Normals
|
||||
if (vtxDesc.Normal != NOT_PRESENT)
|
||||
{
|
||||
sizes[10] = VertexLoader_Normal::GetSize(vtxDesc.Normal, vtxAttr.g0.NormalFormat,
|
||||
vtxAttr.g0.NormalElements, vtxAttr.g0.NormalIndex3);
|
||||
}
|
||||
else
|
||||
{
|
||||
sizes[10] = 0;
|
||||
}
|
||||
|
||||
// Colors
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int size = 0;
|
||||
// Colors
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
switch (colDesc[i])
|
||||
{
|
||||
case NOT_PRESENT:
|
||||
break;
|
||||
case DIRECT:
|
||||
switch (colComp[i])
|
||||
{
|
||||
case FORMAT_16B_565: size = 2; break;
|
||||
case FORMAT_24B_888: size = 3; break;
|
||||
case FORMAT_32B_888x: size = 4; break;
|
||||
case FORMAT_16B_4444: size = 2; break;
|
||||
case FORMAT_24B_6666: size = 3; break;
|
||||
case FORMAT_32B_8888: size = 4; break;
|
||||
default: _assert_(0); break;
|
||||
}
|
||||
break;
|
||||
case INDEX8:
|
||||
size = 1;
|
||||
break;
|
||||
case INDEX16:
|
||||
size = 2;
|
||||
break;
|
||||
}
|
||||
switch (colDesc[i])
|
||||
{
|
||||
case NOT_PRESENT:
|
||||
break;
|
||||
case DIRECT:
|
||||
switch (colComp[i])
|
||||
{
|
||||
case FORMAT_16B_565:
|
||||
size = 2;
|
||||
break;
|
||||
case FORMAT_24B_888:
|
||||
size = 3;
|
||||
break;
|
||||
case FORMAT_32B_888x:
|
||||
size = 4;
|
||||
break;
|
||||
case FORMAT_16B_4444:
|
||||
size = 2;
|
||||
break;
|
||||
case FORMAT_24B_6666:
|
||||
size = 3;
|
||||
break;
|
||||
case FORMAT_32B_8888:
|
||||
size = 4;
|
||||
break;
|
||||
default:
|
||||
_assert_(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case INDEX8:
|
||||
size = 1;
|
||||
break;
|
||||
case INDEX16:
|
||||
size = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
sizes[11 + i] = size;
|
||||
}
|
||||
sizes[11 + i] = size;
|
||||
}
|
||||
|
||||
// Texture coordinates
|
||||
vtxDescHex = vtxDesc.Hex >> 17;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
sizes[13 + i] = VertexLoader_TextCoord::GetSize(vtxDescHex & 3, tcFormat[i], tcElements[i]);
|
||||
vtxDescHex >>= 2;
|
||||
}
|
||||
// Texture coordinates
|
||||
vtxDescHex = vtxDesc.Hex >> 17;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
sizes[13 + i] = VertexLoader_TextCoord::GetSize(vtxDescHex & 3, tcFormat[i], tcElements[i]);
|
||||
vtxDescHex >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,32 +11,32 @@
|
||||
|
||||
namespace FifoAnalyzer
|
||||
{
|
||||
void Init();
|
||||
void Init();
|
||||
|
||||
u8 ReadFifo8(u8*& data);
|
||||
u16 ReadFifo16(u8*& data);
|
||||
u32 ReadFifo32(u8*& data);
|
||||
u8 ReadFifo8(u8*& data);
|
||||
u16 ReadFifo16(u8*& data);
|
||||
u32 ReadFifo32(u8*& data);
|
||||
|
||||
enum DecodeMode
|
||||
{
|
||||
DECODE_RECORD,
|
||||
DECODE_PLAYBACK,
|
||||
};
|
||||
enum DecodeMode
|
||||
{
|
||||
DECODE_RECORD,
|
||||
DECODE_PLAYBACK,
|
||||
};
|
||||
|
||||
u32 AnalyzeCommand(u8* data, DecodeMode mode);
|
||||
u32 AnalyzeCommand(u8* data, DecodeMode mode);
|
||||
|
||||
struct CPMemory
|
||||
{
|
||||
TVtxDesc vtxDesc;
|
||||
VAT vtxAttr[8];
|
||||
u32 arrayBases[16];
|
||||
u32 arrayStrides[16];
|
||||
};
|
||||
struct CPMemory
|
||||
{
|
||||
TVtxDesc vtxDesc;
|
||||
VAT vtxAttr[8];
|
||||
u32 arrayBases[16];
|
||||
u32 arrayStrides[16];
|
||||
};
|
||||
|
||||
void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem);
|
||||
void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem);
|
||||
|
||||
void CalculateVertexElementSizes(int sizes[], int vatIndex, const CPMemory& cpMem);
|
||||
void CalculateVertexElementSizes(int sizes[], int vatIndex, const CPMemory& cpMem);
|
||||
|
||||
extern bool s_DrawingObject;
|
||||
extern FifoAnalyzer::CPMemory s_CpMem;
|
||||
extern bool s_DrawingObject;
|
||||
extern FifoAnalyzer::CPMemory s_CpMem;
|
||||
}
|
||||
|
@ -12,263 +12,265 @@
|
||||
|
||||
using namespace FifoFileStruct;
|
||||
|
||||
FifoDataFile::FifoDataFile() :
|
||||
m_Flags(0)
|
||||
FifoDataFile::FifoDataFile() : m_Flags(0)
|
||||
{
|
||||
}
|
||||
|
||||
FifoDataFile::~FifoDataFile()
|
||||
{
|
||||
for (auto& frame : m_Frames)
|
||||
{
|
||||
for (auto& update : frame.memoryUpdates)
|
||||
delete []update.data;
|
||||
for (auto& frame : m_Frames)
|
||||
{
|
||||
for (auto& update : frame.memoryUpdates)
|
||||
delete[] update.data;
|
||||
|
||||
delete []frame.fifoData;
|
||||
}
|
||||
delete[] frame.fifoData;
|
||||
}
|
||||
}
|
||||
|
||||
bool FifoDataFile::HasBrokenEFBCopies() const
|
||||
{
|
||||
return m_Version < 2;
|
||||
return m_Version < 2;
|
||||
}
|
||||
|
||||
void FifoDataFile::SetIsWii(bool isWii)
|
||||
{
|
||||
SetFlag(FLAG_IS_WII, isWii);
|
||||
SetFlag(FLAG_IS_WII, isWii);
|
||||
}
|
||||
|
||||
bool FifoDataFile::GetIsWii() const
|
||||
{
|
||||
return GetFlag(FLAG_IS_WII);
|
||||
return GetFlag(FLAG_IS_WII);
|
||||
}
|
||||
|
||||
void FifoDataFile::AddFrame(const FifoFrameInfo& frameInfo)
|
||||
{
|
||||
m_Frames.push_back(frameInfo);
|
||||
m_Frames.push_back(frameInfo);
|
||||
}
|
||||
|
||||
bool FifoDataFile::Save(const std::string& filename)
|
||||
{
|
||||
File::IOFile file;
|
||||
if (!file.Open(filename, "wb"))
|
||||
return false;
|
||||
File::IOFile file;
|
||||
if (!file.Open(filename, "wb"))
|
||||
return false;
|
||||
|
||||
// Add space for header
|
||||
PadFile(sizeof(FileHeader), file);
|
||||
// Add space for header
|
||||
PadFile(sizeof(FileHeader), file);
|
||||
|
||||
// Add space for frame list
|
||||
u64 frameListOffset = file.Tell();
|
||||
PadFile(m_Frames.size() * sizeof(FileFrameInfo), file);
|
||||
// Add space for frame list
|
||||
u64 frameListOffset = file.Tell();
|
||||
PadFile(m_Frames.size() * sizeof(FileFrameInfo), file);
|
||||
|
||||
u64 bpMemOffset = file.Tell();
|
||||
file.WriteArray(m_BPMem, BP_MEM_SIZE);
|
||||
u64 bpMemOffset = file.Tell();
|
||||
file.WriteArray(m_BPMem, BP_MEM_SIZE);
|
||||
|
||||
u64 cpMemOffset = file.Tell();
|
||||
file.WriteArray(m_CPMem, CP_MEM_SIZE);
|
||||
u64 cpMemOffset = file.Tell();
|
||||
file.WriteArray(m_CPMem, CP_MEM_SIZE);
|
||||
|
||||
u64 xfMemOffset = file.Tell();
|
||||
file.WriteArray(m_XFMem, XF_MEM_SIZE);
|
||||
u64 xfMemOffset = file.Tell();
|
||||
file.WriteArray(m_XFMem, XF_MEM_SIZE);
|
||||
|
||||
u64 xfRegsOffset = file.Tell();
|
||||
file.WriteArray(m_XFRegs, XF_REGS_SIZE);
|
||||
u64 xfRegsOffset = file.Tell();
|
||||
file.WriteArray(m_XFRegs, XF_REGS_SIZE);
|
||||
|
||||
// Write header
|
||||
FileHeader header;
|
||||
header.fileId = FILE_ID;
|
||||
header.file_version = VERSION_NUMBER;
|
||||
header.min_loader_version = MIN_LOADER_VERSION;
|
||||
// Write header
|
||||
FileHeader header;
|
||||
header.fileId = FILE_ID;
|
||||
header.file_version = VERSION_NUMBER;
|
||||
header.min_loader_version = MIN_LOADER_VERSION;
|
||||
|
||||
header.bpMemOffset = bpMemOffset;
|
||||
header.bpMemSize = BP_MEM_SIZE;
|
||||
header.bpMemOffset = bpMemOffset;
|
||||
header.bpMemSize = BP_MEM_SIZE;
|
||||
|
||||
header.cpMemOffset = cpMemOffset;
|
||||
header.cpMemSize = CP_MEM_SIZE;
|
||||
header.cpMemOffset = cpMemOffset;
|
||||
header.cpMemSize = CP_MEM_SIZE;
|
||||
|
||||
header.xfMemOffset = xfMemOffset;
|
||||
header.xfMemSize = XF_MEM_SIZE;
|
||||
header.xfMemOffset = xfMemOffset;
|
||||
header.xfMemSize = XF_MEM_SIZE;
|
||||
|
||||
header.xfRegsOffset = xfRegsOffset;
|
||||
header.xfRegsSize = XF_REGS_SIZE;
|
||||
header.xfRegsOffset = xfRegsOffset;
|
||||
header.xfRegsSize = XF_REGS_SIZE;
|
||||
|
||||
header.frameListOffset = frameListOffset;
|
||||
header.frameCount = (u32)m_Frames.size();
|
||||
header.frameListOffset = frameListOffset;
|
||||
header.frameCount = (u32)m_Frames.size();
|
||||
|
||||
header.flags = m_Flags;
|
||||
header.flags = m_Flags;
|
||||
|
||||
file.Seek(0, SEEK_SET);
|
||||
file.WriteBytes(&header, sizeof(FileHeader));
|
||||
file.Seek(0, SEEK_SET);
|
||||
file.WriteBytes(&header, sizeof(FileHeader));
|
||||
|
||||
// Write frames list
|
||||
for (unsigned int i = 0; i < m_Frames.size(); ++i)
|
||||
{
|
||||
const FifoFrameInfo &srcFrame = m_Frames[i];
|
||||
// Write frames list
|
||||
for (unsigned int i = 0; i < m_Frames.size(); ++i)
|
||||
{
|
||||
const FifoFrameInfo& srcFrame = m_Frames[i];
|
||||
|
||||
// Write FIFO data
|
||||
file.Seek(0, SEEK_END);
|
||||
u64 dataOffset = file.Tell();
|
||||
file.WriteBytes(srcFrame.fifoData, srcFrame.fifoDataSize);
|
||||
// Write FIFO data
|
||||
file.Seek(0, SEEK_END);
|
||||
u64 dataOffset = file.Tell();
|
||||
file.WriteBytes(srcFrame.fifoData, srcFrame.fifoDataSize);
|
||||
|
||||
u64 memoryUpdatesOffset = WriteMemoryUpdates(srcFrame.memoryUpdates, file);
|
||||
u64 memoryUpdatesOffset = WriteMemoryUpdates(srcFrame.memoryUpdates, file);
|
||||
|
||||
FileFrameInfo dstFrame;
|
||||
dstFrame.fifoDataSize = srcFrame.fifoDataSize;
|
||||
dstFrame.fifoDataOffset = dataOffset;
|
||||
dstFrame.fifoStart = srcFrame.fifoStart;
|
||||
dstFrame.fifoEnd = srcFrame.fifoEnd;
|
||||
dstFrame.memoryUpdatesOffset = memoryUpdatesOffset;
|
||||
dstFrame.numMemoryUpdates = (u32)srcFrame.memoryUpdates.size();
|
||||
FileFrameInfo dstFrame;
|
||||
dstFrame.fifoDataSize = srcFrame.fifoDataSize;
|
||||
dstFrame.fifoDataOffset = dataOffset;
|
||||
dstFrame.fifoStart = srcFrame.fifoStart;
|
||||
dstFrame.fifoEnd = srcFrame.fifoEnd;
|
||||
dstFrame.memoryUpdatesOffset = memoryUpdatesOffset;
|
||||
dstFrame.numMemoryUpdates = (u32)srcFrame.memoryUpdates.size();
|
||||
|
||||
// Write frame info
|
||||
u64 frameOffset = frameListOffset + (i * sizeof(FileFrameInfo));
|
||||
file.Seek(frameOffset, SEEK_SET);
|
||||
file.WriteBytes(&dstFrame, sizeof(FileFrameInfo));
|
||||
}
|
||||
// Write frame info
|
||||
u64 frameOffset = frameListOffset + (i * sizeof(FileFrameInfo));
|
||||
file.Seek(frameOffset, SEEK_SET);
|
||||
file.WriteBytes(&dstFrame, sizeof(FileFrameInfo));
|
||||
}
|
||||
|
||||
if (!file.Close())
|
||||
return false;
|
||||
if (!file.Close())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
FifoDataFile* FifoDataFile::Load(const std::string &filename, bool flagsOnly)
|
||||
FifoDataFile* FifoDataFile::Load(const std::string& filename, bool flagsOnly)
|
||||
{
|
||||
File::IOFile file;
|
||||
file.Open(filename, "rb");
|
||||
if (!file)
|
||||
return nullptr;
|
||||
File::IOFile file;
|
||||
file.Open(filename, "rb");
|
||||
if (!file)
|
||||
return nullptr;
|
||||
|
||||
FileHeader header;
|
||||
file.ReadBytes(&header, sizeof(header));
|
||||
FileHeader header;
|
||||
file.ReadBytes(&header, sizeof(header));
|
||||
|
||||
if (header.fileId != FILE_ID || header.min_loader_version > VERSION_NUMBER)
|
||||
{
|
||||
file.Close();
|
||||
return nullptr;
|
||||
}
|
||||
if (header.fileId != FILE_ID || header.min_loader_version > VERSION_NUMBER)
|
||||
{
|
||||
file.Close();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FifoDataFile* dataFile = new FifoDataFile;
|
||||
FifoDataFile* dataFile = new FifoDataFile;
|
||||
|
||||
dataFile->m_Flags = header.flags;
|
||||
dataFile->m_Version = header.file_version;
|
||||
dataFile->m_Flags = header.flags;
|
||||
dataFile->m_Version = header.file_version;
|
||||
|
||||
if (flagsOnly)
|
||||
{
|
||||
file.Close();
|
||||
return dataFile;
|
||||
}
|
||||
if (flagsOnly)
|
||||
{
|
||||
file.Close();
|
||||
return dataFile;
|
||||
}
|
||||
|
||||
u32 size = std::min((u32)BP_MEM_SIZE, header.bpMemSize);
|
||||
file.Seek(header.bpMemOffset, SEEK_SET);
|
||||
file.ReadArray(dataFile->m_BPMem, size);
|
||||
u32 size = std::min((u32)BP_MEM_SIZE, header.bpMemSize);
|
||||
file.Seek(header.bpMemOffset, SEEK_SET);
|
||||
file.ReadArray(dataFile->m_BPMem, size);
|
||||
|
||||
size = std::min((u32)CP_MEM_SIZE, header.cpMemSize);
|
||||
file.Seek(header.cpMemOffset, SEEK_SET);
|
||||
file.ReadArray(dataFile->m_CPMem, size);
|
||||
size = std::min((u32)CP_MEM_SIZE, header.cpMemSize);
|
||||
file.Seek(header.cpMemOffset, SEEK_SET);
|
||||
file.ReadArray(dataFile->m_CPMem, size);
|
||||
|
||||
size = std::min((u32)XF_MEM_SIZE, header.xfMemSize);
|
||||
file.Seek(header.xfMemOffset, SEEK_SET);
|
||||
file.ReadArray(dataFile->m_XFMem, size);
|
||||
size = std::min((u32)XF_MEM_SIZE, header.xfMemSize);
|
||||
file.Seek(header.xfMemOffset, SEEK_SET);
|
||||
file.ReadArray(dataFile->m_XFMem, size);
|
||||
|
||||
size = std::min((u32)XF_REGS_SIZE, header.xfRegsSize);
|
||||
file.Seek(header.xfRegsOffset, SEEK_SET);
|
||||
file.ReadArray(dataFile->m_XFRegs, size);
|
||||
size = std::min((u32)XF_REGS_SIZE, header.xfRegsSize);
|
||||
file.Seek(header.xfRegsOffset, SEEK_SET);
|
||||
file.ReadArray(dataFile->m_XFRegs, size);
|
||||
|
||||
// Read frames
|
||||
for (u32 i = 0; i < header.frameCount; ++i)
|
||||
{
|
||||
u64 frameOffset = header.frameListOffset + (i * sizeof(FileFrameInfo));
|
||||
file.Seek(frameOffset, SEEK_SET);
|
||||
FileFrameInfo srcFrame;
|
||||
file.ReadBytes(&srcFrame, sizeof(FileFrameInfo));
|
||||
// Read frames
|
||||
for (u32 i = 0; i < header.frameCount; ++i)
|
||||
{
|
||||
u64 frameOffset = header.frameListOffset + (i * sizeof(FileFrameInfo));
|
||||
file.Seek(frameOffset, SEEK_SET);
|
||||
FileFrameInfo srcFrame;
|
||||
file.ReadBytes(&srcFrame, sizeof(FileFrameInfo));
|
||||
|
||||
FifoFrameInfo dstFrame;
|
||||
dstFrame.fifoData = new u8[srcFrame.fifoDataSize];
|
||||
dstFrame.fifoDataSize = srcFrame.fifoDataSize;
|
||||
dstFrame.fifoStart = srcFrame.fifoStart;
|
||||
dstFrame.fifoEnd = srcFrame.fifoEnd;
|
||||
FifoFrameInfo dstFrame;
|
||||
dstFrame.fifoData = new u8[srcFrame.fifoDataSize];
|
||||
dstFrame.fifoDataSize = srcFrame.fifoDataSize;
|
||||
dstFrame.fifoStart = srcFrame.fifoStart;
|
||||
dstFrame.fifoEnd = srcFrame.fifoEnd;
|
||||
|
||||
file.Seek(srcFrame.fifoDataOffset, SEEK_SET);
|
||||
file.ReadBytes(dstFrame.fifoData, srcFrame.fifoDataSize);
|
||||
file.Seek(srcFrame.fifoDataOffset, SEEK_SET);
|
||||
file.ReadBytes(dstFrame.fifoData, srcFrame.fifoDataSize);
|
||||
|
||||
ReadMemoryUpdates(srcFrame.memoryUpdatesOffset, srcFrame.numMemoryUpdates, dstFrame.memoryUpdates, file);
|
||||
ReadMemoryUpdates(srcFrame.memoryUpdatesOffset, srcFrame.numMemoryUpdates,
|
||||
dstFrame.memoryUpdates, file);
|
||||
|
||||
dataFile->AddFrame(dstFrame);
|
||||
}
|
||||
dataFile->AddFrame(dstFrame);
|
||||
}
|
||||
|
||||
file.Close();
|
||||
file.Close();
|
||||
|
||||
return dataFile;
|
||||
return dataFile;
|
||||
}
|
||||
|
||||
void FifoDataFile::PadFile(size_t numBytes, File::IOFile& file)
|
||||
{
|
||||
for (size_t i = 0; i < numBytes; ++i)
|
||||
fputc(0, file.GetHandle());
|
||||
for (size_t i = 0; i < numBytes; ++i)
|
||||
fputc(0, file.GetHandle());
|
||||
}
|
||||
|
||||
void FifoDataFile::SetFlag(u32 flag, bool set)
|
||||
{
|
||||
if (set)
|
||||
m_Flags |= flag;
|
||||
else
|
||||
m_Flags &= ~flag;
|
||||
if (set)
|
||||
m_Flags |= flag;
|
||||
else
|
||||
m_Flags &= ~flag;
|
||||
}
|
||||
|
||||
bool FifoDataFile::GetFlag(u32 flag) const
|
||||
{
|
||||
return !!(m_Flags & flag);
|
||||
return !!(m_Flags & flag);
|
||||
}
|
||||
|
||||
u64 FifoDataFile::WriteMemoryUpdates(const std::vector<MemoryUpdate>& memUpdates, File::IOFile& file)
|
||||
u64 FifoDataFile::WriteMemoryUpdates(const std::vector<MemoryUpdate>& memUpdates,
|
||||
File::IOFile& file)
|
||||
{
|
||||
// Add space for memory update list
|
||||
u64 updateListOffset = file.Tell();
|
||||
PadFile(memUpdates.size() * sizeof(FileMemoryUpdate), file);
|
||||
// Add space for memory update list
|
||||
u64 updateListOffset = file.Tell();
|
||||
PadFile(memUpdates.size() * sizeof(FileMemoryUpdate), file);
|
||||
|
||||
for (unsigned int i = 0; i < memUpdates.size(); ++i)
|
||||
{
|
||||
const MemoryUpdate &srcUpdate = memUpdates[i];
|
||||
for (unsigned int i = 0; i < memUpdates.size(); ++i)
|
||||
{
|
||||
const MemoryUpdate& srcUpdate = memUpdates[i];
|
||||
|
||||
// Write memory
|
||||
file.Seek(0, SEEK_END);
|
||||
u64 dataOffset = file.Tell();
|
||||
file.WriteBytes(srcUpdate.data, srcUpdate.size);
|
||||
// Write memory
|
||||
file.Seek(0, SEEK_END);
|
||||
u64 dataOffset = file.Tell();
|
||||
file.WriteBytes(srcUpdate.data, srcUpdate.size);
|
||||
|
||||
FileMemoryUpdate dstUpdate;
|
||||
dstUpdate.address = srcUpdate.address;
|
||||
dstUpdate.dataOffset = dataOffset;
|
||||
dstUpdate.dataSize = srcUpdate.size;
|
||||
dstUpdate.fifoPosition = srcUpdate.fifoPosition;
|
||||
dstUpdate.type = srcUpdate.type;
|
||||
FileMemoryUpdate dstUpdate;
|
||||
dstUpdate.address = srcUpdate.address;
|
||||
dstUpdate.dataOffset = dataOffset;
|
||||
dstUpdate.dataSize = srcUpdate.size;
|
||||
dstUpdate.fifoPosition = srcUpdate.fifoPosition;
|
||||
dstUpdate.type = srcUpdate.type;
|
||||
|
||||
u64 updateOffset = updateListOffset + (i * sizeof(FileMemoryUpdate));
|
||||
file.Seek(updateOffset, SEEK_SET);
|
||||
file.WriteBytes(&dstUpdate, sizeof(FileMemoryUpdate));
|
||||
}
|
||||
u64 updateOffset = updateListOffset + (i * sizeof(FileMemoryUpdate));
|
||||
file.Seek(updateOffset, SEEK_SET);
|
||||
file.WriteBytes(&dstUpdate, sizeof(FileMemoryUpdate));
|
||||
}
|
||||
|
||||
return updateListOffset;
|
||||
return updateListOffset;
|
||||
}
|
||||
|
||||
void FifoDataFile::ReadMemoryUpdates(u64 fileOffset, u32 numUpdates, std::vector<MemoryUpdate>& memUpdates, File::IOFile& file)
|
||||
void FifoDataFile::ReadMemoryUpdates(u64 fileOffset, u32 numUpdates,
|
||||
std::vector<MemoryUpdate>& memUpdates, File::IOFile& file)
|
||||
{
|
||||
memUpdates.resize(numUpdates);
|
||||
memUpdates.resize(numUpdates);
|
||||
|
||||
for (u32 i = 0; i < numUpdates; ++i)
|
||||
{
|
||||
u64 updateOffset = fileOffset + (i * sizeof(FileMemoryUpdate));
|
||||
file.Seek(updateOffset, SEEK_SET);
|
||||
FileMemoryUpdate srcUpdate;
|
||||
file.ReadBytes(&srcUpdate, sizeof(FileMemoryUpdate));
|
||||
for (u32 i = 0; i < numUpdates; ++i)
|
||||
{
|
||||
u64 updateOffset = fileOffset + (i * sizeof(FileMemoryUpdate));
|
||||
file.Seek(updateOffset, SEEK_SET);
|
||||
FileMemoryUpdate srcUpdate;
|
||||
file.ReadBytes(&srcUpdate, sizeof(FileMemoryUpdate));
|
||||
|
||||
MemoryUpdate& dstUpdate = memUpdates[i];
|
||||
dstUpdate.address = srcUpdate.address;
|
||||
dstUpdate.fifoPosition = srcUpdate.fifoPosition;
|
||||
dstUpdate.size = srcUpdate.dataSize;
|
||||
dstUpdate.data = new u8[srcUpdate.dataSize];
|
||||
dstUpdate.type = (MemoryUpdate::Type)srcUpdate.type;
|
||||
MemoryUpdate& dstUpdate = memUpdates[i];
|
||||
dstUpdate.address = srcUpdate.address;
|
||||
dstUpdate.fifoPosition = srcUpdate.fifoPosition;
|
||||
dstUpdate.size = srcUpdate.dataSize;
|
||||
dstUpdate.data = new u8[srcUpdate.dataSize];
|
||||
dstUpdate.type = (MemoryUpdate::Type)srcUpdate.type;
|
||||
|
||||
file.Seek(srcUpdate.dataOffset, SEEK_SET);
|
||||
file.ReadBytes(dstUpdate.data, srcUpdate.dataSize);
|
||||
}
|
||||
file.Seek(srcUpdate.dataOffset, SEEK_SET);
|
||||
file.ReadBytes(dstUpdate.data, srcUpdate.dataSize);
|
||||
}
|
||||
}
|
||||
|
@ -11,90 +11,89 @@
|
||||
|
||||
namespace File
|
||||
{
|
||||
class IOFile;
|
||||
class IOFile;
|
||||
}
|
||||
|
||||
struct MemoryUpdate
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
TEXTURE_MAP = 0x01,
|
||||
XF_DATA = 0x02,
|
||||
VERTEX_STREAM = 0x04,
|
||||
TMEM = 0x08,
|
||||
};
|
||||
enum Type
|
||||
{
|
||||
TEXTURE_MAP = 0x01,
|
||||
XF_DATA = 0x02,
|
||||
VERTEX_STREAM = 0x04,
|
||||
TMEM = 0x08,
|
||||
};
|
||||
|
||||
u32 fifoPosition;
|
||||
u32 address;
|
||||
u32 size;
|
||||
u8* data;
|
||||
Type type;
|
||||
u32 fifoPosition;
|
||||
u32 address;
|
||||
u32 size;
|
||||
u8* data;
|
||||
Type type;
|
||||
};
|
||||
|
||||
struct FifoFrameInfo
|
||||
{
|
||||
u8* fifoData;
|
||||
u32 fifoDataSize;
|
||||
u8* fifoData;
|
||||
u32 fifoDataSize;
|
||||
|
||||
u32 fifoStart;
|
||||
u32 fifoEnd;
|
||||
u32 fifoStart;
|
||||
u32 fifoEnd;
|
||||
|
||||
// Must be sorted by fifoPosition
|
||||
std::vector<MemoryUpdate> memoryUpdates;
|
||||
// Must be sorted by fifoPosition
|
||||
std::vector<MemoryUpdate> memoryUpdates;
|
||||
};
|
||||
|
||||
class FifoDataFile
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
BP_MEM_SIZE = 256,
|
||||
CP_MEM_SIZE = 256,
|
||||
XF_MEM_SIZE = 4096,
|
||||
XF_REGS_SIZE = 96,
|
||||
};
|
||||
enum
|
||||
{
|
||||
BP_MEM_SIZE = 256,
|
||||
CP_MEM_SIZE = 256,
|
||||
XF_MEM_SIZE = 4096,
|
||||
XF_REGS_SIZE = 96,
|
||||
};
|
||||
|
||||
FifoDataFile();
|
||||
~FifoDataFile();
|
||||
FifoDataFile();
|
||||
~FifoDataFile();
|
||||
|
||||
void SetIsWii(bool isWii);
|
||||
bool GetIsWii() const;
|
||||
bool HasBrokenEFBCopies() const;
|
||||
void SetIsWii(bool isWii);
|
||||
bool GetIsWii() const;
|
||||
bool HasBrokenEFBCopies() const;
|
||||
|
||||
u32 *GetBPMem() { return m_BPMem; }
|
||||
u32 *GetCPMem() { return m_CPMem; }
|
||||
u32 *GetXFMem() { return m_XFMem; }
|
||||
u32 *GetXFRegs() { return m_XFRegs; }
|
||||
u32* GetBPMem() { return m_BPMem; }
|
||||
u32* GetCPMem() { return m_CPMem; }
|
||||
u32* GetXFMem() { return m_XFMem; }
|
||||
u32* GetXFRegs() { return m_XFRegs; }
|
||||
void AddFrame(const FifoFrameInfo& frameInfo);
|
||||
const FifoFrameInfo& GetFrame(u32 frame) const { return m_Frames[frame]; }
|
||||
u32 GetFrameCount() const { return static_cast<u32>(m_Frames.size()); }
|
||||
bool Save(const std::string& filename);
|
||||
|
||||
void AddFrame(const FifoFrameInfo &frameInfo);
|
||||
const FifoFrameInfo &GetFrame(u32 frame) const { return m_Frames[frame]; }
|
||||
u32 GetFrameCount() const { return static_cast<u32>(m_Frames.size()); }
|
||||
|
||||
bool Save(const std::string& filename);
|
||||
|
||||
static FifoDataFile* Load(const std::string &filename, bool flagsOnly);
|
||||
static FifoDataFile* Load(const std::string& filename, bool flagsOnly);
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
FLAG_IS_WII = 1
|
||||
};
|
||||
enum
|
||||
{
|
||||
FLAG_IS_WII = 1
|
||||
};
|
||||
|
||||
void PadFile(size_t numBytes, File::IOFile &file);
|
||||
void PadFile(size_t numBytes, File::IOFile& file);
|
||||
|
||||
void SetFlag(u32 flag, bool set);
|
||||
bool GetFlag(u32 flag) const;
|
||||
void SetFlag(u32 flag, bool set);
|
||||
bool GetFlag(u32 flag) const;
|
||||
|
||||
u64 WriteMemoryUpdates(const std::vector<MemoryUpdate>& memUpdates, File::IOFile &file);
|
||||
static void ReadMemoryUpdates(u64 fileOffset, u32 numUpdates, std::vector<MemoryUpdate>& memUpdates, File::IOFile& file);
|
||||
u64 WriteMemoryUpdates(const std::vector<MemoryUpdate>& memUpdates, File::IOFile& file);
|
||||
static void ReadMemoryUpdates(u64 fileOffset, u32 numUpdates,
|
||||
std::vector<MemoryUpdate>& memUpdates, File::IOFile& file);
|
||||
|
||||
u32 m_BPMem[BP_MEM_SIZE];
|
||||
u32 m_CPMem[CP_MEM_SIZE];
|
||||
u32 m_XFMem[XF_MEM_SIZE];
|
||||
u32 m_XFRegs[XF_REGS_SIZE];
|
||||
u32 m_BPMem[BP_MEM_SIZE];
|
||||
u32 m_CPMem[CP_MEM_SIZE];
|
||||
u32 m_XFMem[XF_MEM_SIZE];
|
||||
u32 m_XFRegs[XF_REGS_SIZE];
|
||||
|
||||
u32 m_Flags;
|
||||
u32 m_Version;
|
||||
u32 m_Flags;
|
||||
u32 m_Version;
|
||||
|
||||
std::vector<FifoFrameInfo> m_Frames;
|
||||
std::vector<FifoFrameInfo> m_Frames;
|
||||
};
|
||||
|
@ -8,62 +8,57 @@
|
||||
|
||||
namespace FifoFileStruct
|
||||
{
|
||||
|
||||
enum
|
||||
{
|
||||
FILE_ID = 0x0d01f1f0,
|
||||
VERSION_NUMBER = 3,
|
||||
MIN_LOADER_VERSION = 1,
|
||||
FILE_ID = 0x0d01f1f0,
|
||||
VERSION_NUMBER = 3,
|
||||
MIN_LOADER_VERSION = 1,
|
||||
};
|
||||
|
||||
#pragma pack(push, 4)
|
||||
|
||||
union FileHeader
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 fileId;
|
||||
u32 file_version;
|
||||
u32 min_loader_version;
|
||||
u64 bpMemOffset;
|
||||
u32 bpMemSize;
|
||||
u64 cpMemOffset;
|
||||
u32 cpMemSize;
|
||||
u64 xfMemOffset;
|
||||
u32 xfMemSize;
|
||||
u64 xfRegsOffset;
|
||||
u32 xfRegsSize;
|
||||
u64 frameListOffset;
|
||||
u32 frameCount;
|
||||
u32 flags;
|
||||
};
|
||||
u32 rawData[32];
|
||||
union FileHeader {
|
||||
struct
|
||||
{
|
||||
u32 fileId;
|
||||
u32 file_version;
|
||||
u32 min_loader_version;
|
||||
u64 bpMemOffset;
|
||||
u32 bpMemSize;
|
||||
u64 cpMemOffset;
|
||||
u32 cpMemSize;
|
||||
u64 xfMemOffset;
|
||||
u32 xfMemSize;
|
||||
u64 xfRegsOffset;
|
||||
u32 xfRegsSize;
|
||||
u64 frameListOffset;
|
||||
u32 frameCount;
|
||||
u32 flags;
|
||||
};
|
||||
u32 rawData[32];
|
||||
};
|
||||
|
||||
union FileFrameInfo
|
||||
{
|
||||
struct
|
||||
{
|
||||
u64 fifoDataOffset;
|
||||
u32 fifoDataSize;
|
||||
u32 fifoStart;
|
||||
u32 fifoEnd;
|
||||
u64 memoryUpdatesOffset;
|
||||
u32 numMemoryUpdates;
|
||||
};
|
||||
u32 rawData[16];
|
||||
union FileFrameInfo {
|
||||
struct
|
||||
{
|
||||
u64 fifoDataOffset;
|
||||
u32 fifoDataSize;
|
||||
u32 fifoStart;
|
||||
u32 fifoEnd;
|
||||
u64 memoryUpdatesOffset;
|
||||
u32 numMemoryUpdates;
|
||||
};
|
||||
u32 rawData[16];
|
||||
};
|
||||
|
||||
|
||||
struct FileMemoryUpdate
|
||||
{
|
||||
u32 fifoPosition;
|
||||
u32 address;
|
||||
u64 dataOffset;
|
||||
u32 dataSize;
|
||||
u8 type;
|
||||
u32 fifoPosition;
|
||||
u32 address;
|
||||
u64 dataOffset;
|
||||
u32 dataSize;
|
||||
u8 type;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Core/FifoPlayer/FifoPlaybackAnalyzer.h"
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/FifoPlayer/FifoAnalyzer.h"
|
||||
#include "Core/FifoPlayer/FifoDataFile.h"
|
||||
#include "Core/FifoPlayer/FifoPlaybackAnalyzer.h"
|
||||
#include "VideoCommon/OpcodeDecoding.h"
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
#include "VideoCommon/VertexLoader.h"
|
||||
@ -17,85 +17,87 @@ using namespace FifoAnalyzer;
|
||||
#define LOG_FIFO_CMDS 0
|
||||
struct CmdData
|
||||
{
|
||||
u32 size;
|
||||
u32 offset;
|
||||
u8* ptr;
|
||||
u32 size;
|
||||
u32 offset;
|
||||
u8* ptr;
|
||||
};
|
||||
|
||||
void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file, std::vector<AnalyzedFrameInfo>& frameInfo)
|
||||
void FifoPlaybackAnalyzer::AnalyzeFrames(FifoDataFile* file,
|
||||
std::vector<AnalyzedFrameInfo>& frameInfo)
|
||||
{
|
||||
u32* cpMem = file->GetCPMem();
|
||||
FifoAnalyzer::LoadCPReg(0x50, cpMem[0x50], s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x60, cpMem[0x60], s_CpMem);
|
||||
u32* cpMem = file->GetCPMem();
|
||||
FifoAnalyzer::LoadCPReg(0x50, cpMem[0x50], s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x60, cpMem[0x60], s_CpMem);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
FifoAnalyzer::LoadCPReg(0x70 + i, cpMem[0x70 + i], s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x80 + i, cpMem[0x80 + i], s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x90 + i, cpMem[0x90 + i], s_CpMem);
|
||||
}
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
FifoAnalyzer::LoadCPReg(0x70 + i, cpMem[0x70 + i], s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x80 + i, cpMem[0x80 + i], s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x90 + i, cpMem[0x90 + i], s_CpMem);
|
||||
}
|
||||
|
||||
frameInfo.clear();
|
||||
frameInfo.resize(file->GetFrameCount());
|
||||
frameInfo.clear();
|
||||
frameInfo.resize(file->GetFrameCount());
|
||||
|
||||
for (u32 frameIdx = 0; frameIdx < file->GetFrameCount(); ++frameIdx)
|
||||
{
|
||||
const FifoFrameInfo& frame = file->GetFrame(frameIdx);
|
||||
AnalyzedFrameInfo& analyzed = frameInfo[frameIdx];
|
||||
for (u32 frameIdx = 0; frameIdx < file->GetFrameCount(); ++frameIdx)
|
||||
{
|
||||
const FifoFrameInfo& frame = file->GetFrame(frameIdx);
|
||||
AnalyzedFrameInfo& analyzed = frameInfo[frameIdx];
|
||||
|
||||
s_DrawingObject = false;
|
||||
s_DrawingObject = false;
|
||||
|
||||
u32 cmdStart = 0;
|
||||
u32 nextMemUpdate = 0;
|
||||
u32 cmdStart = 0;
|
||||
u32 nextMemUpdate = 0;
|
||||
|
||||
#if LOG_FIFO_CMDS
|
||||
// Debugging
|
||||
vector<CmdData> prevCmds;
|
||||
// Debugging
|
||||
vector<CmdData> prevCmds;
|
||||
#endif
|
||||
|
||||
while (cmdStart < frame.fifoDataSize)
|
||||
{
|
||||
// Add memory updates that have occurred before this point in the frame
|
||||
while (nextMemUpdate < frame.memoryUpdates.size() && frame.memoryUpdates[nextMemUpdate].fifoPosition <= cmdStart)
|
||||
{
|
||||
analyzed.memoryUpdates.push_back(frame.memoryUpdates[nextMemUpdate]);
|
||||
++nextMemUpdate;
|
||||
}
|
||||
while (cmdStart < frame.fifoDataSize)
|
||||
{
|
||||
// Add memory updates that have occurred before this point in the frame
|
||||
while (nextMemUpdate < frame.memoryUpdates.size() &&
|
||||
frame.memoryUpdates[nextMemUpdate].fifoPosition <= cmdStart)
|
||||
{
|
||||
analyzed.memoryUpdates.push_back(frame.memoryUpdates[nextMemUpdate]);
|
||||
++nextMemUpdate;
|
||||
}
|
||||
|
||||
bool wasDrawing = s_DrawingObject;
|
||||
bool wasDrawing = s_DrawingObject;
|
||||
|
||||
u32 cmdSize = FifoAnalyzer::AnalyzeCommand(&frame.fifoData[cmdStart], DECODE_PLAYBACK);
|
||||
u32 cmdSize = FifoAnalyzer::AnalyzeCommand(&frame.fifoData[cmdStart], DECODE_PLAYBACK);
|
||||
|
||||
#if LOG_FIFO_CMDS
|
||||
CmdData cmdData;
|
||||
cmdData.offset = cmdStart;
|
||||
cmdData.ptr = &frame.fifoData[cmdStart];
|
||||
cmdData.size = cmdSize;
|
||||
prevCmds.push_back(cmdData);
|
||||
CmdData cmdData;
|
||||
cmdData.offset = cmdStart;
|
||||
cmdData.ptr = &frame.fifoData[cmdStart];
|
||||
cmdData.size = cmdSize;
|
||||
prevCmds.push_back(cmdData);
|
||||
#endif
|
||||
|
||||
// Check for error
|
||||
if (cmdSize == 0)
|
||||
{
|
||||
// Clean up frame analysis
|
||||
analyzed.objectStarts.clear();
|
||||
analyzed.objectEnds.clear();
|
||||
// Check for error
|
||||
if (cmdSize == 0)
|
||||
{
|
||||
// Clean up frame analysis
|
||||
analyzed.objectStarts.clear();
|
||||
analyzed.objectEnds.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (wasDrawing != s_DrawingObject)
|
||||
{
|
||||
if (s_DrawingObject)
|
||||
analyzed.objectStarts.push_back(cmdStart);
|
||||
else
|
||||
analyzed.objectEnds.push_back(cmdStart);
|
||||
}
|
||||
if (wasDrawing != s_DrawingObject)
|
||||
{
|
||||
if (s_DrawingObject)
|
||||
analyzed.objectStarts.push_back(cmdStart);
|
||||
else
|
||||
analyzed.objectEnds.push_back(cmdStart);
|
||||
}
|
||||
|
||||
cmdStart += cmdSize;
|
||||
}
|
||||
cmdStart += cmdSize;
|
||||
}
|
||||
|
||||
if (analyzed.objectEnds.size() < analyzed.objectStarts.size())
|
||||
analyzed.objectEnds.push_back(cmdStart);
|
||||
}
|
||||
if (analyzed.objectEnds.size() < analyzed.objectStarts.size())
|
||||
analyzed.objectEnds.push_back(cmdStart);
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,12 @@
|
||||
|
||||
struct AnalyzedFrameInfo
|
||||
{
|
||||
std::vector<u32> objectStarts;
|
||||
std::vector<u32> objectEnds;
|
||||
std::vector<MemoryUpdate> memoryUpdates;
|
||||
std::vector<u32> objectStarts;
|
||||
std::vector<u32> objectEnds;
|
||||
std::vector<MemoryUpdate> memoryUpdates;
|
||||
};
|
||||
|
||||
namespace FifoPlaybackAnalyzer
|
||||
{
|
||||
void AnalyzeFrames(FifoDataFile* file, std::vector<AnalyzedFrameInfo>& frameInfo);
|
||||
void AnalyzeFrames(FifoDataFile* file, std::vector<AnalyzedFrameInfo>& frameInfo);
|
||||
};
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/FifoPlayer/FifoDataFile.h"
|
||||
#include "Core/FifoPlayer/FifoPlayer.h"
|
||||
#include "Core/HW/CPU.h"
|
||||
@ -20,6 +19,7 @@
|
||||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
#include "VideoCommon/CommandProcessor.h"
|
||||
@ -28,524 +28,508 @@ bool IsPlayingBackFifologWithBrokenEFBCopies = false;
|
||||
|
||||
FifoPlayer::~FifoPlayer()
|
||||
{
|
||||
delete m_File;
|
||||
delete m_File;
|
||||
}
|
||||
|
||||
bool FifoPlayer::Open(const std::string& filename)
|
||||
{
|
||||
Close();
|
||||
Close();
|
||||
|
||||
m_File = FifoDataFile::Load(filename, false);
|
||||
m_File = FifoDataFile::Load(filename, false);
|
||||
|
||||
if (m_File)
|
||||
{
|
||||
FifoAnalyzer::Init();
|
||||
FifoPlaybackAnalyzer::AnalyzeFrames(m_File, m_FrameInfo);
|
||||
if (m_File)
|
||||
{
|
||||
FifoAnalyzer::Init();
|
||||
FifoPlaybackAnalyzer::AnalyzeFrames(m_File, m_FrameInfo);
|
||||
|
||||
m_FrameRangeEnd = m_File->GetFrameCount();
|
||||
}
|
||||
m_FrameRangeEnd = m_File->GetFrameCount();
|
||||
}
|
||||
|
||||
if (m_FileLoadedCb)
|
||||
m_FileLoadedCb();
|
||||
if (m_FileLoadedCb)
|
||||
m_FileLoadedCb();
|
||||
|
||||
return (m_File != nullptr);
|
||||
return (m_File != nullptr);
|
||||
}
|
||||
|
||||
void FifoPlayer::Close()
|
||||
{
|
||||
delete m_File;
|
||||
m_File = nullptr;
|
||||
delete m_File;
|
||||
m_File = nullptr;
|
||||
|
||||
m_FrameRangeStart = 0;
|
||||
m_FrameRangeEnd = 0;
|
||||
m_FrameRangeStart = 0;
|
||||
m_FrameRangeEnd = 0;
|
||||
}
|
||||
|
||||
class FifoPlayer::CPUCore final : public CPUCoreBase
|
||||
{
|
||||
public:
|
||||
explicit CPUCore(FifoPlayer* parent)
|
||||
: m_parent(parent)
|
||||
{
|
||||
}
|
||||
CPUCore(const CPUCore&) = delete;
|
||||
~CPUCore()
|
||||
{
|
||||
}
|
||||
CPUCore& operator=(const CPUCore&) = delete;
|
||||
explicit CPUCore(FifoPlayer* parent) : m_parent(parent) {}
|
||||
CPUCore(const CPUCore&) = delete;
|
||||
~CPUCore() {}
|
||||
CPUCore& operator=(const CPUCore&) = delete;
|
||||
|
||||
void Init() override
|
||||
{
|
||||
IsPlayingBackFifologWithBrokenEFBCopies = m_parent->m_File->HasBrokenEFBCopies();
|
||||
void Init() override
|
||||
{
|
||||
IsPlayingBackFifologWithBrokenEFBCopies = m_parent->m_File->HasBrokenEFBCopies();
|
||||
|
||||
m_parent->m_CurrentFrame = m_parent->m_FrameRangeStart;
|
||||
m_parent->LoadMemory();
|
||||
}
|
||||
m_parent->m_CurrentFrame = m_parent->m_FrameRangeStart;
|
||||
m_parent->LoadMemory();
|
||||
}
|
||||
|
||||
void Shutdown() override
|
||||
{
|
||||
IsPlayingBackFifologWithBrokenEFBCopies = false;
|
||||
}
|
||||
void Shutdown() override { IsPlayingBackFifologWithBrokenEFBCopies = false; }
|
||||
void ClearCache() override
|
||||
{
|
||||
// Nothing to clear.
|
||||
}
|
||||
|
||||
void ClearCache() override
|
||||
{
|
||||
// Nothing to clear.
|
||||
}
|
||||
void SingleStep() override
|
||||
{
|
||||
// NOTE: AdvanceFrame() will get stuck forever in Dual Core because the FIFO
|
||||
// is disabled by CPU::EnableStepping(true) so the frame never gets displayed.
|
||||
PanicAlert("Cannot SingleStep the FIFO. Use Frame Advance instead.");
|
||||
}
|
||||
|
||||
void SingleStep() override
|
||||
{
|
||||
// NOTE: AdvanceFrame() will get stuck forever in Dual Core because the FIFO
|
||||
// is disabled by CPU::EnableStepping(true) so the frame never gets displayed.
|
||||
PanicAlert("Cannot SingleStep the FIFO. Use Frame Advance instead.");
|
||||
}
|
||||
const char* GetName() override { return "FifoPlayer"; }
|
||||
void Run() override
|
||||
{
|
||||
while (CPU::GetState() == CPU::CPU_RUNNING)
|
||||
{
|
||||
switch (m_parent->AdvanceFrame())
|
||||
{
|
||||
case CPU::CPU_POWERDOWN:
|
||||
CPU::Break();
|
||||
Host_Message(WM_USER_STOP);
|
||||
break;
|
||||
|
||||
const char* GetName() override
|
||||
{
|
||||
return "FifoPlayer";
|
||||
}
|
||||
|
||||
void Run() override
|
||||
{
|
||||
while (CPU::GetState() == CPU::CPU_RUNNING)
|
||||
{
|
||||
switch (m_parent->AdvanceFrame())
|
||||
{
|
||||
case CPU::CPU_POWERDOWN:
|
||||
CPU::Break();
|
||||
Host_Message(WM_USER_STOP);
|
||||
break;
|
||||
|
||||
case CPU::CPU_STEPPING:
|
||||
CPU::Break();
|
||||
Host_UpdateMainFrame();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
case CPU::CPU_STEPPING:
|
||||
CPU::Break();
|
||||
Host_UpdateMainFrame();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
FifoPlayer* m_parent;
|
||||
FifoPlayer* m_parent;
|
||||
};
|
||||
|
||||
int FifoPlayer::AdvanceFrame()
|
||||
{
|
||||
if (m_CurrentFrame >= m_FrameRangeEnd)
|
||||
{
|
||||
if (!m_Loop)
|
||||
return CPU::CPU_POWERDOWN;
|
||||
// If there are zero frames in the range then sleep instead of busy spinning
|
||||
if (m_FrameRangeStart >= m_FrameRangeEnd)
|
||||
return CPU::CPU_STEPPING;
|
||||
if (m_CurrentFrame >= m_FrameRangeEnd)
|
||||
{
|
||||
if (!m_Loop)
|
||||
return CPU::CPU_POWERDOWN;
|
||||
// If there are zero frames in the range then sleep instead of busy spinning
|
||||
if (m_FrameRangeStart >= m_FrameRangeEnd)
|
||||
return CPU::CPU_STEPPING;
|
||||
|
||||
m_CurrentFrame = m_FrameRangeStart;
|
||||
}
|
||||
m_CurrentFrame = m_FrameRangeStart;
|
||||
}
|
||||
|
||||
if (m_FrameWrittenCb)
|
||||
m_FrameWrittenCb();
|
||||
if (m_FrameWrittenCb)
|
||||
m_FrameWrittenCb();
|
||||
|
||||
if (m_EarlyMemoryUpdates && m_CurrentFrame == m_FrameRangeStart)
|
||||
WriteAllMemoryUpdates();
|
||||
if (m_EarlyMemoryUpdates && m_CurrentFrame == m_FrameRangeStart)
|
||||
WriteAllMemoryUpdates();
|
||||
|
||||
WriteFrame(m_File->GetFrame(m_CurrentFrame), m_FrameInfo[m_CurrentFrame]);
|
||||
WriteFrame(m_File->GetFrame(m_CurrentFrame), m_FrameInfo[m_CurrentFrame]);
|
||||
|
||||
++m_CurrentFrame;
|
||||
return CPU::CPU_RUNNING;
|
||||
++m_CurrentFrame;
|
||||
return CPU::CPU_RUNNING;
|
||||
}
|
||||
|
||||
std::unique_ptr<CPUCoreBase> FifoPlayer::GetCPUCore()
|
||||
{
|
||||
if (!m_File || m_File->GetFrameCount() == 0)
|
||||
return nullptr;
|
||||
if (!m_File || m_File->GetFrameCount() == 0)
|
||||
return nullptr;
|
||||
|
||||
return std::make_unique<CPUCore>(this);
|
||||
return std::make_unique<CPUCore>(this);
|
||||
}
|
||||
|
||||
u32 FifoPlayer::GetFrameObjectCount()
|
||||
{
|
||||
if (m_CurrentFrame < m_FrameInfo.size())
|
||||
{
|
||||
return (u32)(m_FrameInfo[m_CurrentFrame].objectStarts.size());
|
||||
}
|
||||
if (m_CurrentFrame < m_FrameInfo.size())
|
||||
{
|
||||
return (u32)(m_FrameInfo[m_CurrentFrame].objectStarts.size());
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FifoPlayer::SetFrameRangeStart(u32 start)
|
||||
{
|
||||
if (m_File)
|
||||
{
|
||||
u32 frameCount = m_File->GetFrameCount();
|
||||
if (start > frameCount)
|
||||
start = frameCount;
|
||||
if (m_File)
|
||||
{
|
||||
u32 frameCount = m_File->GetFrameCount();
|
||||
if (start > frameCount)
|
||||
start = frameCount;
|
||||
|
||||
m_FrameRangeStart = start;
|
||||
if (m_FrameRangeEnd < start)
|
||||
m_FrameRangeEnd = start;
|
||||
m_FrameRangeStart = start;
|
||||
if (m_FrameRangeEnd < start)
|
||||
m_FrameRangeEnd = start;
|
||||
|
||||
if (m_CurrentFrame < m_FrameRangeStart)
|
||||
m_CurrentFrame = m_FrameRangeStart;
|
||||
}
|
||||
if (m_CurrentFrame < m_FrameRangeStart)
|
||||
m_CurrentFrame = m_FrameRangeStart;
|
||||
}
|
||||
}
|
||||
|
||||
void FifoPlayer::SetFrameRangeEnd(u32 end)
|
||||
{
|
||||
if (m_File)
|
||||
{
|
||||
u32 frameCount = m_File->GetFrameCount();
|
||||
if (end > frameCount)
|
||||
end = frameCount;
|
||||
if (m_File)
|
||||
{
|
||||
u32 frameCount = m_File->GetFrameCount();
|
||||
if (end > frameCount)
|
||||
end = frameCount;
|
||||
|
||||
m_FrameRangeEnd = end;
|
||||
if (m_FrameRangeStart > end)
|
||||
m_FrameRangeStart = end;
|
||||
m_FrameRangeEnd = end;
|
||||
if (m_FrameRangeStart > end)
|
||||
m_FrameRangeStart = end;
|
||||
|
||||
if (m_CurrentFrame >= m_FrameRangeEnd)
|
||||
m_CurrentFrame = m_FrameRangeStart;
|
||||
}
|
||||
if (m_CurrentFrame >= m_FrameRangeEnd)
|
||||
m_CurrentFrame = m_FrameRangeStart;
|
||||
}
|
||||
}
|
||||
|
||||
FifoPlayer& FifoPlayer::GetInstance()
|
||||
{
|
||||
static FifoPlayer instance;
|
||||
return instance;
|
||||
static FifoPlayer instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
FifoPlayer::FifoPlayer() :
|
||||
m_CurrentFrame(0),
|
||||
m_FrameRangeStart(0),
|
||||
m_FrameRangeEnd(0),
|
||||
m_ObjectRangeStart(0),
|
||||
m_ObjectRangeEnd(10000),
|
||||
m_EarlyMemoryUpdates(false),
|
||||
m_FileLoadedCb(nullptr),
|
||||
m_FrameWrittenCb(nullptr),
|
||||
m_File(nullptr)
|
||||
FifoPlayer::FifoPlayer()
|
||||
: m_CurrentFrame(0), m_FrameRangeStart(0), m_FrameRangeEnd(0), m_ObjectRangeStart(0),
|
||||
m_ObjectRangeEnd(10000), m_EarlyMemoryUpdates(false), m_FileLoadedCb(nullptr),
|
||||
m_FrameWrittenCb(nullptr), m_File(nullptr)
|
||||
{
|
||||
m_Loop = SConfig::GetInstance().bLoopFifoReplay;
|
||||
m_Loop = SConfig::GetInstance().bLoopFifoReplay;
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& info)
|
||||
{
|
||||
// Core timing information
|
||||
m_CyclesPerFrame = SystemTimers::GetTicksPerSecond() / VideoInterface::GetTargetRefreshRate();
|
||||
m_ElapsedCycles = 0;
|
||||
m_FrameFifoSize = frame.fifoDataSize;
|
||||
// Core timing information
|
||||
m_CyclesPerFrame = SystemTimers::GetTicksPerSecond() / VideoInterface::GetTargetRefreshRate();
|
||||
m_ElapsedCycles = 0;
|
||||
m_FrameFifoSize = frame.fifoDataSize;
|
||||
|
||||
// Determine start and end objects
|
||||
u32 numObjects = (u32)(info.objectStarts.size());
|
||||
u32 drawStart = std::min(numObjects, m_ObjectRangeStart);
|
||||
u32 drawEnd = std::min(numObjects - 1, m_ObjectRangeEnd);
|
||||
// Determine start and end objects
|
||||
u32 numObjects = (u32)(info.objectStarts.size());
|
||||
u32 drawStart = std::min(numObjects, m_ObjectRangeStart);
|
||||
u32 drawEnd = std::min(numObjects - 1, m_ObjectRangeEnd);
|
||||
|
||||
u32 position = 0;
|
||||
u32 memoryUpdate = 0;
|
||||
u32 position = 0;
|
||||
u32 memoryUpdate = 0;
|
||||
|
||||
// Skip memory updates during frame if true
|
||||
if (m_EarlyMemoryUpdates)
|
||||
{
|
||||
memoryUpdate = (u32)(frame.memoryUpdates.size());
|
||||
}
|
||||
// Skip memory updates during frame if true
|
||||
if (m_EarlyMemoryUpdates)
|
||||
{
|
||||
memoryUpdate = (u32)(frame.memoryUpdates.size());
|
||||
}
|
||||
|
||||
if (numObjects > 0)
|
||||
{
|
||||
u32 objectNum = 0;
|
||||
if (numObjects > 0)
|
||||
{
|
||||
u32 objectNum = 0;
|
||||
|
||||
// Write fifo data skipping objects before the draw range
|
||||
while (objectNum < drawStart)
|
||||
{
|
||||
WriteFramePart(position, info.objectStarts[objectNum], memoryUpdate, frame, info);
|
||||
// Write fifo data skipping objects before the draw range
|
||||
while (objectNum < drawStart)
|
||||
{
|
||||
WriteFramePart(position, info.objectStarts[objectNum], memoryUpdate, frame, info);
|
||||
|
||||
position = info.objectEnds[objectNum];
|
||||
++objectNum;
|
||||
}
|
||||
position = info.objectEnds[objectNum];
|
||||
++objectNum;
|
||||
}
|
||||
|
||||
// Write objects in draw range
|
||||
if (objectNum < numObjects && drawStart <= drawEnd)
|
||||
{
|
||||
objectNum = drawEnd;
|
||||
WriteFramePart(position, info.objectEnds[objectNum], memoryUpdate, frame, info);
|
||||
position = info.objectEnds[objectNum];
|
||||
++objectNum;
|
||||
}
|
||||
// Write objects in draw range
|
||||
if (objectNum < numObjects && drawStart <= drawEnd)
|
||||
{
|
||||
objectNum = drawEnd;
|
||||
WriteFramePart(position, info.objectEnds[objectNum], memoryUpdate, frame, info);
|
||||
position = info.objectEnds[objectNum];
|
||||
++objectNum;
|
||||
}
|
||||
|
||||
// Write fifo data skipping objects after the draw range
|
||||
while (objectNum < numObjects)
|
||||
{
|
||||
WriteFramePart(position, info.objectStarts[objectNum], memoryUpdate, frame, info);
|
||||
// Write fifo data skipping objects after the draw range
|
||||
while (objectNum < numObjects)
|
||||
{
|
||||
WriteFramePart(position, info.objectStarts[objectNum], memoryUpdate, frame, info);
|
||||
|
||||
position = info.objectEnds[objectNum];
|
||||
++objectNum;
|
||||
}
|
||||
}
|
||||
position = info.objectEnds[objectNum];
|
||||
++objectNum;
|
||||
}
|
||||
}
|
||||
|
||||
// Write data after the last object
|
||||
WriteFramePart(position, frame.fifoDataSize, memoryUpdate, frame, info);
|
||||
// Write data after the last object
|
||||
WriteFramePart(position, frame.fifoDataSize, memoryUpdate, frame, info);
|
||||
|
||||
FlushWGP();
|
||||
FlushWGP();
|
||||
|
||||
// Sleep while the GPU is active
|
||||
while (!IsIdleSet())
|
||||
{
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
// Sleep while the GPU is active
|
||||
while (!IsIdleSet())
|
||||
{
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteFramePart(u32 dataStart, u32 dataEnd, u32& nextMemUpdate, const FifoFrameInfo& frame, const AnalyzedFrameInfo& info)
|
||||
void FifoPlayer::WriteFramePart(u32 dataStart, u32 dataEnd, u32& nextMemUpdate,
|
||||
const FifoFrameInfo& frame, const AnalyzedFrameInfo& info)
|
||||
{
|
||||
u8* data = frame.fifoData;
|
||||
u8* data = frame.fifoData;
|
||||
|
||||
while (nextMemUpdate < frame.memoryUpdates.size() && dataStart < dataEnd)
|
||||
{
|
||||
const MemoryUpdate &memUpdate = info.memoryUpdates[nextMemUpdate];
|
||||
while (nextMemUpdate < frame.memoryUpdates.size() && dataStart < dataEnd)
|
||||
{
|
||||
const MemoryUpdate& memUpdate = info.memoryUpdates[nextMemUpdate];
|
||||
|
||||
if (memUpdate.fifoPosition < dataEnd)
|
||||
{
|
||||
if (dataStart < memUpdate.fifoPosition)
|
||||
{
|
||||
WriteFifo(data, dataStart, memUpdate.fifoPosition);
|
||||
dataStart = memUpdate.fifoPosition;
|
||||
}
|
||||
if (memUpdate.fifoPosition < dataEnd)
|
||||
{
|
||||
if (dataStart < memUpdate.fifoPosition)
|
||||
{
|
||||
WriteFifo(data, dataStart, memUpdate.fifoPosition);
|
||||
dataStart = memUpdate.fifoPosition;
|
||||
}
|
||||
|
||||
WriteMemory(memUpdate);
|
||||
WriteMemory(memUpdate);
|
||||
|
||||
++nextMemUpdate;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteFifo(data, dataStart, dataEnd);
|
||||
dataStart = dataEnd;
|
||||
}
|
||||
}
|
||||
++nextMemUpdate;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteFifo(data, dataStart, dataEnd);
|
||||
dataStart = dataEnd;
|
||||
}
|
||||
}
|
||||
|
||||
if (dataStart < dataEnd)
|
||||
WriteFifo(data, dataStart, dataEnd);
|
||||
if (dataStart < dataEnd)
|
||||
WriteFifo(data, dataStart, dataEnd);
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteAllMemoryUpdates()
|
||||
{
|
||||
_assert_(m_File);
|
||||
_assert_(m_File);
|
||||
|
||||
for (u32 frameNum = 0; frameNum < m_File->GetFrameCount(); ++frameNum)
|
||||
{
|
||||
const FifoFrameInfo &frame = m_File->GetFrame(frameNum);
|
||||
for (auto& update : frame.memoryUpdates)
|
||||
{
|
||||
WriteMemory(update);
|
||||
}
|
||||
}
|
||||
for (u32 frameNum = 0; frameNum < m_File->GetFrameCount(); ++frameNum)
|
||||
{
|
||||
const FifoFrameInfo& frame = m_File->GetFrame(frameNum);
|
||||
for (auto& update : frame.memoryUpdates)
|
||||
{
|
||||
WriteMemory(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteMemory(const MemoryUpdate& memUpdate)
|
||||
{
|
||||
u8 *mem = nullptr;
|
||||
u8* mem = nullptr;
|
||||
|
||||
if (memUpdate.address & 0x10000000)
|
||||
mem = &Memory::m_pEXRAM[memUpdate.address & Memory::EXRAM_MASK];
|
||||
else
|
||||
mem = &Memory::m_pRAM[memUpdate.address & Memory::RAM_MASK];
|
||||
if (memUpdate.address & 0x10000000)
|
||||
mem = &Memory::m_pEXRAM[memUpdate.address & Memory::EXRAM_MASK];
|
||||
else
|
||||
mem = &Memory::m_pRAM[memUpdate.address & Memory::RAM_MASK];
|
||||
|
||||
memcpy(mem, memUpdate.data, memUpdate.size);
|
||||
memcpy(mem, memUpdate.data, memUpdate.size);
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteFifo(u8* data, u32 start, u32 end)
|
||||
{
|
||||
u32 written = start;
|
||||
u32 lastBurstEnd = end - 1;
|
||||
u32 written = start;
|
||||
u32 lastBurstEnd = end - 1;
|
||||
|
||||
// Write up to 256 bytes at a time
|
||||
while (written < end)
|
||||
{
|
||||
while (IsHighWatermarkSet())
|
||||
{
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
// Write up to 256 bytes at a time
|
||||
while (written < end)
|
||||
{
|
||||
while (IsHighWatermarkSet())
|
||||
{
|
||||
CoreTiming::Idle();
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
|
||||
u32 burstEnd = std::min(written + 255, lastBurstEnd);
|
||||
u32 burstEnd = std::min(written + 255, lastBurstEnd);
|
||||
|
||||
while (written < burstEnd)
|
||||
GPFifo::FastWrite8(data[written++]);
|
||||
while (written < burstEnd)
|
||||
GPFifo::FastWrite8(data[written++]);
|
||||
|
||||
GPFifo::Write8(data[written++]);
|
||||
GPFifo::Write8(data[written++]);
|
||||
|
||||
// Advance core timing
|
||||
u32 elapsedCycles = u32(((u64)written * m_CyclesPerFrame) / m_FrameFifoSize);
|
||||
u32 cyclesUsed = elapsedCycles - m_ElapsedCycles;
|
||||
m_ElapsedCycles = elapsedCycles;
|
||||
// Advance core timing
|
||||
u32 elapsedCycles = u32(((u64)written * m_CyclesPerFrame) / m_FrameFifoSize);
|
||||
u32 cyclesUsed = elapsedCycles - m_ElapsedCycles;
|
||||
m_ElapsedCycles = elapsedCycles;
|
||||
|
||||
PowerPC::ppcState.downcount -= cyclesUsed;
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
PowerPC::ppcState.downcount -= cyclesUsed;
|
||||
CoreTiming::Advance();
|
||||
}
|
||||
}
|
||||
|
||||
void FifoPlayer::SetupFifo()
|
||||
{
|
||||
WriteCP(CommandProcessor::CTRL_REGISTER, 0); // disable read, BP, interrupts
|
||||
WriteCP(CommandProcessor::CLEAR_REGISTER, 7); // clear overflow, underflow, metrics
|
||||
WriteCP(CommandProcessor::CTRL_REGISTER, 0); // disable read, BP, interrupts
|
||||
WriteCP(CommandProcessor::CLEAR_REGISTER, 7); // clear overflow, underflow, metrics
|
||||
|
||||
const FifoFrameInfo& frame = m_File->GetFrame(m_CurrentFrame);
|
||||
const FifoFrameInfo& frame = m_File->GetFrame(m_CurrentFrame);
|
||||
|
||||
// Set fifo bounds
|
||||
WriteCP(CommandProcessor::FIFO_BASE_LO, frame.fifoStart);
|
||||
WriteCP(CommandProcessor::FIFO_BASE_HI, frame.fifoStart >> 16);
|
||||
WriteCP(CommandProcessor::FIFO_END_LO, frame.fifoEnd);
|
||||
WriteCP(CommandProcessor::FIFO_END_HI, frame.fifoEnd >> 16);
|
||||
// Set fifo bounds
|
||||
WriteCP(CommandProcessor::FIFO_BASE_LO, frame.fifoStart);
|
||||
WriteCP(CommandProcessor::FIFO_BASE_HI, frame.fifoStart >> 16);
|
||||
WriteCP(CommandProcessor::FIFO_END_LO, frame.fifoEnd);
|
||||
WriteCP(CommandProcessor::FIFO_END_HI, frame.fifoEnd >> 16);
|
||||
|
||||
// Set watermarks, high at 75%, low at 0%
|
||||
u32 hi_watermark = (frame.fifoEnd - frame.fifoStart) * 3 / 4;
|
||||
WriteCP(CommandProcessor::FIFO_HI_WATERMARK_LO, hi_watermark);
|
||||
WriteCP(CommandProcessor::FIFO_HI_WATERMARK_HI, hi_watermark >> 16);
|
||||
WriteCP(CommandProcessor::FIFO_LO_WATERMARK_LO, 0);
|
||||
WriteCP(CommandProcessor::FIFO_LO_WATERMARK_HI, 0);
|
||||
// Set watermarks, high at 75%, low at 0%
|
||||
u32 hi_watermark = (frame.fifoEnd - frame.fifoStart) * 3 / 4;
|
||||
WriteCP(CommandProcessor::FIFO_HI_WATERMARK_LO, hi_watermark);
|
||||
WriteCP(CommandProcessor::FIFO_HI_WATERMARK_HI, hi_watermark >> 16);
|
||||
WriteCP(CommandProcessor::FIFO_LO_WATERMARK_LO, 0);
|
||||
WriteCP(CommandProcessor::FIFO_LO_WATERMARK_HI, 0);
|
||||
|
||||
// Set R/W pointers to fifo start
|
||||
WriteCP(CommandProcessor::FIFO_RW_DISTANCE_LO, 0);
|
||||
WriteCP(CommandProcessor::FIFO_RW_DISTANCE_HI, 0);
|
||||
WriteCP(CommandProcessor::FIFO_WRITE_POINTER_LO, frame.fifoStart);
|
||||
WriteCP(CommandProcessor::FIFO_WRITE_POINTER_HI, frame.fifoStart >> 16);
|
||||
WriteCP(CommandProcessor::FIFO_READ_POINTER_LO, frame.fifoStart);
|
||||
WriteCP(CommandProcessor::FIFO_READ_POINTER_HI, frame.fifoStart >> 16);
|
||||
// Set R/W pointers to fifo start
|
||||
WriteCP(CommandProcessor::FIFO_RW_DISTANCE_LO, 0);
|
||||
WriteCP(CommandProcessor::FIFO_RW_DISTANCE_HI, 0);
|
||||
WriteCP(CommandProcessor::FIFO_WRITE_POINTER_LO, frame.fifoStart);
|
||||
WriteCP(CommandProcessor::FIFO_WRITE_POINTER_HI, frame.fifoStart >> 16);
|
||||
WriteCP(CommandProcessor::FIFO_READ_POINTER_LO, frame.fifoStart);
|
||||
WriteCP(CommandProcessor::FIFO_READ_POINTER_HI, frame.fifoStart >> 16);
|
||||
|
||||
// Set fifo bounds
|
||||
WritePI(ProcessorInterface::PI_FIFO_BASE, frame.fifoStart);
|
||||
WritePI(ProcessorInterface::PI_FIFO_END, frame.fifoEnd);
|
||||
// Set fifo bounds
|
||||
WritePI(ProcessorInterface::PI_FIFO_BASE, frame.fifoStart);
|
||||
WritePI(ProcessorInterface::PI_FIFO_END, frame.fifoEnd);
|
||||
|
||||
// Set write pointer
|
||||
WritePI(ProcessorInterface::PI_FIFO_WPTR, frame.fifoStart);
|
||||
FlushWGP();
|
||||
WritePI(ProcessorInterface::PI_FIFO_WPTR, frame.fifoStart);
|
||||
// Set write pointer
|
||||
WritePI(ProcessorInterface::PI_FIFO_WPTR, frame.fifoStart);
|
||||
FlushWGP();
|
||||
WritePI(ProcessorInterface::PI_FIFO_WPTR, frame.fifoStart);
|
||||
|
||||
WriteCP(CommandProcessor::CTRL_REGISTER, 17); // enable read & GP link
|
||||
WriteCP(CommandProcessor::CTRL_REGISTER, 17); // enable read & GP link
|
||||
}
|
||||
|
||||
void FifoPlayer::LoadMemory()
|
||||
{
|
||||
UReg_MSR newMSR;
|
||||
newMSR.DR = 1;
|
||||
newMSR.IR = 1;
|
||||
MSR = newMSR.Hex;
|
||||
PowerPC::ppcState.spr[SPR_IBAT0U] = 0x80001fff;
|
||||
PowerPC::ppcState.spr[SPR_IBAT0L] = 0x00000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT0U] = 0x80001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a;
|
||||
UReg_MSR newMSR;
|
||||
newMSR.DR = 1;
|
||||
newMSR.IR = 1;
|
||||
MSR = newMSR.Hex;
|
||||
PowerPC::ppcState.spr[SPR_IBAT0U] = 0x80001fff;
|
||||
PowerPC::ppcState.spr[SPR_IBAT0L] = 0x00000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT0U] = 0x80001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a;
|
||||
|
||||
SetupFifo();
|
||||
SetupFifo();
|
||||
|
||||
u32 *regs = m_File->GetBPMem();
|
||||
for (int i = 0; i < FifoDataFile::BP_MEM_SIZE; ++i)
|
||||
{
|
||||
if (ShouldLoadBP(i))
|
||||
LoadBPReg(i, regs[i]);
|
||||
}
|
||||
u32* regs = m_File->GetBPMem();
|
||||
for (int i = 0; i < FifoDataFile::BP_MEM_SIZE; ++i)
|
||||
{
|
||||
if (ShouldLoadBP(i))
|
||||
LoadBPReg(i, regs[i]);
|
||||
}
|
||||
|
||||
regs = m_File->GetCPMem();
|
||||
LoadCPReg(0x30, regs[0x30]);
|
||||
LoadCPReg(0x40, regs[0x40]);
|
||||
LoadCPReg(0x50, regs[0x50]);
|
||||
LoadCPReg(0x60, regs[0x60]);
|
||||
regs = m_File->GetCPMem();
|
||||
LoadCPReg(0x30, regs[0x30]);
|
||||
LoadCPReg(0x40, regs[0x40]);
|
||||
LoadCPReg(0x50, regs[0x50]);
|
||||
LoadCPReg(0x60, regs[0x60]);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
LoadCPReg(0x70 + i, regs[0x70 + i]);
|
||||
LoadCPReg(0x80 + i, regs[0x80 + i]);
|
||||
LoadCPReg(0x90 + i, regs[0x90 + i]);
|
||||
}
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
LoadCPReg(0x70 + i, regs[0x70 + i]);
|
||||
LoadCPReg(0x80 + i, regs[0x80 + i]);
|
||||
LoadCPReg(0x90 + i, regs[0x90 + i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
LoadCPReg(0xa0 + i, regs[0xa0 + i]);
|
||||
LoadCPReg(0xb0 + i, regs[0xb0 + i]);
|
||||
}
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
LoadCPReg(0xa0 + i, regs[0xa0 + i]);
|
||||
LoadCPReg(0xb0 + i, regs[0xb0 + i]);
|
||||
}
|
||||
|
||||
regs = m_File->GetXFMem();
|
||||
for (int i = 0; i < FifoDataFile::XF_MEM_SIZE; i += 16)
|
||||
LoadXFMem16(i, ®s[i]);
|
||||
regs = m_File->GetXFMem();
|
||||
for (int i = 0; i < FifoDataFile::XF_MEM_SIZE; i += 16)
|
||||
LoadXFMem16(i, ®s[i]);
|
||||
|
||||
regs = m_File->GetXFRegs();
|
||||
for (int i = 0; i < FifoDataFile::XF_REGS_SIZE; ++i)
|
||||
LoadXFReg(i, regs[i]);
|
||||
regs = m_File->GetXFRegs();
|
||||
for (int i = 0; i < FifoDataFile::XF_REGS_SIZE; ++i)
|
||||
LoadXFReg(i, regs[i]);
|
||||
|
||||
FlushWGP();
|
||||
FlushWGP();
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteCP(u32 address, u16 value)
|
||||
{
|
||||
PowerPC::Write_U16(value, 0xCC000000 | address);
|
||||
PowerPC::Write_U16(value, 0xCC000000 | address);
|
||||
}
|
||||
|
||||
void FifoPlayer::WritePI(u32 address, u32 value)
|
||||
{
|
||||
PowerPC::Write_U32(value, 0xCC003000 | address);
|
||||
PowerPC::Write_U32(value, 0xCC003000 | address);
|
||||
}
|
||||
|
||||
void FifoPlayer::FlushWGP()
|
||||
{
|
||||
// Send 31 0s through the WGP
|
||||
for (int i = 0; i < 7; ++i)
|
||||
GPFifo::Write32(0);
|
||||
GPFifo::Write16(0);
|
||||
GPFifo::Write8(0);
|
||||
// Send 31 0s through the WGP
|
||||
for (int i = 0; i < 7; ++i)
|
||||
GPFifo::Write32(0);
|
||||
GPFifo::Write16(0);
|
||||
GPFifo::Write8(0);
|
||||
|
||||
GPFifo::ResetGatherPipe();
|
||||
GPFifo::ResetGatherPipe();
|
||||
}
|
||||
|
||||
void FifoPlayer::LoadBPReg(u8 reg, u32 value)
|
||||
{
|
||||
GPFifo::Write8(0x61); // load BP reg
|
||||
GPFifo::Write8(0x61); // load BP reg
|
||||
|
||||
u32 cmd = (reg << 24) & 0xff000000;
|
||||
cmd |= (value & 0x00ffffff);
|
||||
GPFifo::Write32(cmd);
|
||||
u32 cmd = (reg << 24) & 0xff000000;
|
||||
cmd |= (value & 0x00ffffff);
|
||||
GPFifo::Write32(cmd);
|
||||
}
|
||||
|
||||
void FifoPlayer::LoadCPReg(u8 reg, u32 value)
|
||||
{
|
||||
GPFifo::Write8(0x08); // load CP reg
|
||||
GPFifo::Write8(reg);
|
||||
GPFifo::Write32(value);
|
||||
GPFifo::Write8(0x08); // load CP reg
|
||||
GPFifo::Write8(reg);
|
||||
GPFifo::Write32(value);
|
||||
}
|
||||
|
||||
void FifoPlayer::LoadXFReg(u16 reg, u32 value)
|
||||
{
|
||||
GPFifo::Write8(0x10); // load XF reg
|
||||
GPFifo::Write32((reg & 0x0fff) | 0x1000); // load 4 bytes into reg
|
||||
GPFifo::Write32(value);
|
||||
GPFifo::Write8(0x10); // load XF reg
|
||||
GPFifo::Write32((reg & 0x0fff) | 0x1000); // load 4 bytes into reg
|
||||
GPFifo::Write32(value);
|
||||
}
|
||||
|
||||
void FifoPlayer::LoadXFMem16(u16 address, u32* data)
|
||||
{
|
||||
// Loads 16 * 4 bytes in xf memory starting at address
|
||||
GPFifo::Write8(0x10); // load XF reg
|
||||
GPFifo::Write32(0x000f0000 | (address & 0xffff)); // load 16 * 4 bytes into address
|
||||
for (int i = 0; i < 16; ++i)
|
||||
GPFifo::Write32(data[i]);
|
||||
// Loads 16 * 4 bytes in xf memory starting at address
|
||||
GPFifo::Write8(0x10); // load XF reg
|
||||
GPFifo::Write32(0x000f0000 | (address & 0xffff)); // load 16 * 4 bytes into address
|
||||
for (int i = 0; i < 16; ++i)
|
||||
GPFifo::Write32(data[i]);
|
||||
}
|
||||
|
||||
bool FifoPlayer::ShouldLoadBP(u8 address)
|
||||
{
|
||||
switch (address)
|
||||
{
|
||||
case BPMEM_SETDRAWDONE:
|
||||
case BPMEM_PE_TOKEN_ID:
|
||||
case BPMEM_PE_TOKEN_INT_ID:
|
||||
case BPMEM_TRIGGER_EFB_COPY:
|
||||
case BPMEM_LOADTLUT1:
|
||||
case BPMEM_PERF1:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
switch (address)
|
||||
{
|
||||
case BPMEM_SETDRAWDONE:
|
||||
case BPMEM_PE_TOKEN_ID:
|
||||
case BPMEM_PE_TOKEN_INT_ID:
|
||||
case BPMEM_TRIGGER_EFB_COPY:
|
||||
case BPMEM_LOADTLUT1:
|
||||
case BPMEM_PERF1:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FifoPlayer::IsIdleSet()
|
||||
{
|
||||
CommandProcessor::UCPStatusReg status = PowerPC::Read_U16(0xCC000000 | CommandProcessor::STATUS_REGISTER);
|
||||
return status.CommandIdle;
|
||||
CommandProcessor::UCPStatusReg status =
|
||||
PowerPC::Read_U16(0xCC000000 | CommandProcessor::STATUS_REGISTER);
|
||||
return status.CommandIdle;
|
||||
}
|
||||
|
||||
bool FifoPlayer::IsHighWatermarkSet()
|
||||
{
|
||||
CommandProcessor::UCPStatusReg status = PowerPC::Read_U16(0xCC000000 | CommandProcessor::STATUS_REGISTER);
|
||||
return status.OverflowHiWatermark;
|
||||
CommandProcessor::UCPStatusReg status =
|
||||
PowerPC::Read_U16(0xCC000000 | CommandProcessor::STATUS_REGISTER);
|
||||
return status.OverflowHiWatermark;
|
||||
}
|
||||
|
@ -22,22 +22,27 @@ struct AnalyzedFrameInfo;
|
||||
// baked into the fifo log. If you recorded with efb2ram on, the result of efb2ram would be baked
|
||||
// into the fifo. If you recorded with efb2tex or efb off, random data would be included in the fifo
|
||||
// log.
|
||||
// Later the behaviour of efb2tex was changed to zero the underlying memory and check the hash of that.
|
||||
// Later the behaviour of efb2tex was changed to zero the underlying memory and check the hash of
|
||||
// that.
|
||||
// But this broke a whole lot of fifologs due to the following sequence of events:
|
||||
// 1. fifoplayer would trigger the efb copy
|
||||
// 2. Texture cache would zero the memory backing the texture and hash it.
|
||||
// 3. Time passes.
|
||||
// 4. fifoplayer would encounter the drawcall using the efb copy
|
||||
// 5. fifoplayer would overwrite the memory backing the efb copy back to it's state when recording.
|
||||
// 5. fifoplayer would overwrite the memory backing the efb copy back to it's state when
|
||||
// recording.
|
||||
// 6. Texture cache would hash the memory and see that the hash no-longer matches
|
||||
// 7. Texture cache would load whatever data was now in memory as a texture either a baked in
|
||||
// efb2ram copy from recording time or just random data.
|
||||
// 8. The output of fifoplayer would be wrong.
|
||||
|
||||
// To keep compatibility with old fifologs, we have this flag which signals texture cache to not bother
|
||||
// To keep compatibility with old fifologs, we have this flag which signals texture cache to not
|
||||
// bother
|
||||
// hashing the memory and just assume the hash matched.
|
||||
// At a later point proper efb copy support should be added to fiforecorder and this flag will change
|
||||
// based on the version of the .dff file, but until then it will always be true when a fifolog is playing.
|
||||
// At a later point proper efb copy support should be added to fiforecorder and this flag will
|
||||
// change
|
||||
// based on the version of the .dff file, but until then it will always be true when a fifolog is
|
||||
// playing.
|
||||
|
||||
// Shitty global to fix a shitty problem
|
||||
extern bool IsPlayingBackFifologWithBrokenEFBCopies;
|
||||
@ -45,106 +50,100 @@ extern bool IsPlayingBackFifologWithBrokenEFBCopies;
|
||||
class FifoPlayer
|
||||
{
|
||||
public:
|
||||
typedef void(*CallbackFunc)(void);
|
||||
typedef void (*CallbackFunc)(void);
|
||||
|
||||
~FifoPlayer();
|
||||
~FifoPlayer();
|
||||
|
||||
bool Open(const std::string& filename);
|
||||
void Close();
|
||||
bool Open(const std::string& filename);
|
||||
void Close();
|
||||
|
||||
// Returns a CPUCoreBase instance that can be injected into PowerPC as a
|
||||
// pseudo-CPU. The instance is only valid while the FifoPlayer is Open().
|
||||
// Returns nullptr if the FifoPlayer is not initialized correctly.
|
||||
// Play/Pause/Stop of the FifoLog can be controlled normally via the
|
||||
// PowerPC state.
|
||||
std::unique_ptr<CPUCoreBase> GetCPUCore();
|
||||
// Returns a CPUCoreBase instance that can be injected into PowerPC as a
|
||||
// pseudo-CPU. The instance is only valid while the FifoPlayer is Open().
|
||||
// Returns nullptr if the FifoPlayer is not initialized correctly.
|
||||
// Play/Pause/Stop of the FifoLog can be controlled normally via the
|
||||
// PowerPC state.
|
||||
std::unique_ptr<CPUCoreBase> GetCPUCore();
|
||||
|
||||
FifoDataFile *GetFile() { return m_File; }
|
||||
FifoDataFile* GetFile() { return m_File; }
|
||||
u32 GetFrameObjectCount();
|
||||
u32 GetCurrentFrameNum() const { return m_CurrentFrame; }
|
||||
const AnalyzedFrameInfo& GetAnalyzedFrameInfo(u32 frame) const { return m_FrameInfo[frame]; }
|
||||
// Frame range
|
||||
u32 GetFrameRangeStart() const { return m_FrameRangeStart; }
|
||||
void SetFrameRangeStart(u32 start);
|
||||
|
||||
u32 GetFrameObjectCount();
|
||||
u32 GetCurrentFrameNum() const { return m_CurrentFrame; }
|
||||
u32 GetFrameRangeEnd() const { return m_FrameRangeEnd; }
|
||||
void SetFrameRangeEnd(u32 end);
|
||||
|
||||
const AnalyzedFrameInfo& GetAnalyzedFrameInfo(u32 frame) const { return m_FrameInfo[frame]; }
|
||||
|
||||
// Frame range
|
||||
u32 GetFrameRangeStart() const { return m_FrameRangeStart; }
|
||||
void SetFrameRangeStart(u32 start);
|
||||
|
||||
u32 GetFrameRangeEnd() const { return m_FrameRangeEnd; }
|
||||
void SetFrameRangeEnd(u32 end);
|
||||
|
||||
// Object range
|
||||
u32 GetObjectRangeStart() const { return m_ObjectRangeStart; }
|
||||
void SetObjectRangeStart(u32 start) { m_ObjectRangeStart = start; }
|
||||
|
||||
u32 GetObjectRangeEnd() const { return m_ObjectRangeEnd; }
|
||||
void SetObjectRangeEnd(u32 end) { m_ObjectRangeEnd = end; }
|
||||
|
||||
// If enabled then all memory updates happen at once before the first frame
|
||||
// Default is disabled
|
||||
void SetEarlyMemoryUpdates(bool enabled) { m_EarlyMemoryUpdates = enabled; }
|
||||
|
||||
// Callbacks
|
||||
void SetFileLoadedCallback(CallbackFunc callback) { m_FileLoadedCb = callback; }
|
||||
void SetFrameWrittenCallback(CallbackFunc callback) { m_FrameWrittenCb = callback; }
|
||||
|
||||
static FifoPlayer &GetInstance();
|
||||
// Object range
|
||||
u32 GetObjectRangeStart() const { return m_ObjectRangeStart; }
|
||||
void SetObjectRangeStart(u32 start) { m_ObjectRangeStart = start; }
|
||||
u32 GetObjectRangeEnd() const { return m_ObjectRangeEnd; }
|
||||
void SetObjectRangeEnd(u32 end) { m_ObjectRangeEnd = end; }
|
||||
// If enabled then all memory updates happen at once before the first frame
|
||||
// Default is disabled
|
||||
void SetEarlyMemoryUpdates(bool enabled) { m_EarlyMemoryUpdates = enabled; }
|
||||
// Callbacks
|
||||
void SetFileLoadedCallback(CallbackFunc callback) { m_FileLoadedCb = callback; }
|
||||
void SetFrameWrittenCallback(CallbackFunc callback) { m_FrameWrittenCb = callback; }
|
||||
static FifoPlayer& GetInstance();
|
||||
|
||||
private:
|
||||
class CPUCore;
|
||||
class CPUCore;
|
||||
|
||||
FifoPlayer();
|
||||
FifoPlayer();
|
||||
|
||||
int AdvanceFrame();
|
||||
int AdvanceFrame();
|
||||
|
||||
void WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo &info);
|
||||
void WriteFramePart(u32 dataStart, u32 dataEnd, u32 &nextMemUpdate, const FifoFrameInfo& frame, const AnalyzedFrameInfo& info);
|
||||
void WriteFrame(const FifoFrameInfo& frame, const AnalyzedFrameInfo& info);
|
||||
void WriteFramePart(u32 dataStart, u32 dataEnd, u32& nextMemUpdate, const FifoFrameInfo& frame,
|
||||
const AnalyzedFrameInfo& info);
|
||||
|
||||
void WriteAllMemoryUpdates();
|
||||
void WriteMemory(const MemoryUpdate &memUpdate);
|
||||
void WriteAllMemoryUpdates();
|
||||
void WriteMemory(const MemoryUpdate& memUpdate);
|
||||
|
||||
// writes a range of data to the fifo
|
||||
// start and end must be relative to frame's fifo data so elapsed cycles are figured correctly
|
||||
void WriteFifo(u8* data, u32 start, u32 end);
|
||||
// writes a range of data to the fifo
|
||||
// start and end must be relative to frame's fifo data so elapsed cycles are figured correctly
|
||||
void WriteFifo(u8* data, u32 start, u32 end);
|
||||
|
||||
void SetupFifo();
|
||||
void SetupFifo();
|
||||
|
||||
void LoadMemory();
|
||||
void LoadMemory();
|
||||
|
||||
void WriteCP(u32 address, u16 value);
|
||||
void WritePI(u32 address, u32 value);
|
||||
void WriteCP(u32 address, u16 value);
|
||||
void WritePI(u32 address, u32 value);
|
||||
|
||||
void FlushWGP();
|
||||
void FlushWGP();
|
||||
|
||||
void LoadBPReg(u8 reg, u32 value);
|
||||
void LoadCPReg(u8 reg, u32 value);
|
||||
void LoadXFReg(u16 reg, u32 value);
|
||||
void LoadXFMem16(u16 address, u32 *data);
|
||||
void LoadBPReg(u8 reg, u32 value);
|
||||
void LoadCPReg(u8 reg, u32 value);
|
||||
void LoadXFReg(u16 reg, u32 value);
|
||||
void LoadXFMem16(u16 address, u32* data);
|
||||
|
||||
bool ShouldLoadBP(u8 address);
|
||||
bool ShouldLoadBP(u8 address);
|
||||
|
||||
static bool IsIdleSet();
|
||||
static bool IsHighWatermarkSet();
|
||||
static bool IsIdleSet();
|
||||
static bool IsHighWatermarkSet();
|
||||
|
||||
bool m_Loop;
|
||||
bool m_Loop;
|
||||
|
||||
u32 m_CurrentFrame;
|
||||
u32 m_FrameRangeStart;
|
||||
u32 m_FrameRangeEnd;
|
||||
u32 m_CurrentFrame;
|
||||
u32 m_FrameRangeStart;
|
||||
u32 m_FrameRangeEnd;
|
||||
|
||||
u32 m_ObjectRangeStart;
|
||||
u32 m_ObjectRangeEnd;
|
||||
u32 m_ObjectRangeStart;
|
||||
u32 m_ObjectRangeEnd;
|
||||
|
||||
bool m_EarlyMemoryUpdates;
|
||||
bool m_EarlyMemoryUpdates;
|
||||
|
||||
u64 m_CyclesPerFrame;
|
||||
u32 m_ElapsedCycles;
|
||||
u32 m_FrameFifoSize;
|
||||
u64 m_CyclesPerFrame;
|
||||
u32 m_ElapsedCycles;
|
||||
u32 m_FrameFifoSize;
|
||||
|
||||
CallbackFunc m_FileLoadedCb;
|
||||
CallbackFunc m_FrameWrittenCb;
|
||||
CallbackFunc m_FileLoadedCb;
|
||||
CallbackFunc m_FrameWrittenCb;
|
||||
|
||||
FifoDataFile* m_File;
|
||||
FifoDataFile* m_File;
|
||||
|
||||
std::vector<AnalyzedFrameInfo> m_FrameInfo;
|
||||
std::vector<AnalyzedFrameInfo> m_FrameInfo;
|
||||
};
|
||||
|
@ -17,70 +17,71 @@ using namespace FifoAnalyzer;
|
||||
|
||||
void FifoRecordAnalyzer::Initialize(u32* cpMem)
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
s_DrawingObject = false;
|
||||
|
||||
FifoAnalyzer::LoadCPReg(0x50, *(cpMem + 0x50), s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x60, *(cpMem + 0x60), s_CpMem);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
FifoAnalyzer::LoadCPReg(0x70 + i, *(cpMem + 0x70 + i), s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x50, *(cpMem + 0x50), s_CpMem);
|
||||
FifoAnalyzer::LoadCPReg(0x60, *(cpMem + 0x60), s_CpMem);
|
||||
for (int i = 0; i < 8; ++i)
|
||||
FifoAnalyzer::LoadCPReg(0x70 + i, *(cpMem + 0x70 + i), s_CpMem);
|
||||
|
||||
memcpy(s_CpMem.arrayBases, cpMem + 0xA0, 16 * 4);
|
||||
memcpy(s_CpMem.arrayStrides, cpMem + 0xB0, 16 * 4);
|
||||
memcpy(s_CpMem.arrayBases, cpMem + 0xA0, 16 * 4);
|
||||
memcpy(s_CpMem.arrayStrides, cpMem + 0xB0, 16 * 4);
|
||||
}
|
||||
|
||||
void FifoRecordAnalyzer::ProcessLoadIndexedXf(u32 val, int array)
|
||||
{
|
||||
int index = val >> 16;
|
||||
int size = ((val >> 12) & 0xF) + 1;
|
||||
int index = val >> 16;
|
||||
int size = ((val >> 12) & 0xF) + 1;
|
||||
|
||||
u32 address = s_CpMem.arrayBases[array] + s_CpMem.arrayStrides[array] * index;
|
||||
u32 address = s_CpMem.arrayBases[array] + s_CpMem.arrayStrides[array] * index;
|
||||
|
||||
FifoRecorder::GetInstance().UseMemory(address, size * 4, MemoryUpdate::XF_DATA);
|
||||
FifoRecorder::GetInstance().UseMemory(address, size * 4, MemoryUpdate::XF_DATA);
|
||||
}
|
||||
|
||||
void FifoRecordAnalyzer::WriteVertexArray(int arrayIndex, u8* vertexData, int vertexSize, int numVertices)
|
||||
void FifoRecordAnalyzer::WriteVertexArray(int arrayIndex, u8* vertexData, int vertexSize,
|
||||
int numVertices)
|
||||
{
|
||||
// Skip if not indexed array
|
||||
int arrayType = (s_CpMem.vtxDesc.Hex >> (9 + (arrayIndex * 2))) & 3;
|
||||
if (arrayType < 2)
|
||||
return;
|
||||
// Skip if not indexed array
|
||||
int arrayType = (s_CpMem.vtxDesc.Hex >> (9 + (arrayIndex * 2))) & 3;
|
||||
if (arrayType < 2)
|
||||
return;
|
||||
|
||||
int maxIndex = 0;
|
||||
int maxIndex = 0;
|
||||
|
||||
// Determine min and max indices
|
||||
if (arrayType == INDEX8)
|
||||
{
|
||||
for (int i = 0; i < numVertices; ++i)
|
||||
{
|
||||
int index = *vertexData;
|
||||
vertexData += vertexSize;
|
||||
// Determine min and max indices
|
||||
if (arrayType == INDEX8)
|
||||
{
|
||||
for (int i = 0; i < numVertices; ++i)
|
||||
{
|
||||
int index = *vertexData;
|
||||
vertexData += vertexSize;
|
||||
|
||||
// 0xff skips the vertex
|
||||
if (index != 0xff)
|
||||
{
|
||||
if (index > maxIndex)
|
||||
maxIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numVertices; ++i)
|
||||
{
|
||||
int index = Common::swap16(vertexData);
|
||||
vertexData += vertexSize;
|
||||
// 0xff skips the vertex
|
||||
if (index != 0xff)
|
||||
{
|
||||
if (index > maxIndex)
|
||||
maxIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numVertices; ++i)
|
||||
{
|
||||
int index = Common::swap16(vertexData);
|
||||
vertexData += vertexSize;
|
||||
|
||||
// 0xffff skips the vertex
|
||||
if (index != 0xffff)
|
||||
{
|
||||
if (index > maxIndex)
|
||||
maxIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 0xffff skips the vertex
|
||||
if (index != 0xffff)
|
||||
{
|
||||
if (index > maxIndex)
|
||||
maxIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 arrayStart = s_CpMem.arrayBases[arrayIndex];
|
||||
u32 arraySize = s_CpMem.arrayStrides[arrayIndex] * (maxIndex + 1);
|
||||
u32 arrayStart = s_CpMem.arrayBases[arrayIndex];
|
||||
u32 arraySize = s_CpMem.arrayStrides[arrayIndex] * (maxIndex + 1);
|
||||
|
||||
FifoRecorder::GetInstance().UseMemory(arrayStart, arraySize, MemoryUpdate::VERTEX_STREAM);
|
||||
FifoRecorder::GetInstance().UseMemory(arrayStart, arraySize, MemoryUpdate::VERTEX_STREAM);
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
|
||||
namespace FifoRecordAnalyzer
|
||||
{
|
||||
// Must call this before analyzing Fifo commands with FifoAnalyzer::AnalyzeCommand()
|
||||
void Initialize(u32* cpMem);
|
||||
// Must call this before analyzing Fifo commands with FifoAnalyzer::AnalyzeCommand()
|
||||
void Initialize(u32* cpMem);
|
||||
|
||||
void ProcessLoadIndexedXf(u32 val, int array);
|
||||
void WriteVertexArray(int arrayIndex, u8* vertexData, int vertexSize, int numVertices);
|
||||
void ProcessLoadIndexedXf(u32 val, int array);
|
||||
void WriteVertexArray(int arrayIndex, u8* vertexData, int vertexSize, int numVertices);
|
||||
};
|
||||
|
@ -14,206 +14,200 @@
|
||||
static FifoRecorder instance;
|
||||
static std::recursive_mutex sMutex;
|
||||
|
||||
FifoRecorder::FifoRecorder() :
|
||||
m_IsRecording(false),
|
||||
m_WasRecording(false),
|
||||
m_RequestedRecordingEnd(false),
|
||||
m_RecordFramesRemaining(0),
|
||||
m_FinishedCb(nullptr),
|
||||
m_File(nullptr),
|
||||
m_SkipNextData(true),
|
||||
m_SkipFutureData(true),
|
||||
m_FrameEnded(false),
|
||||
m_Ram(Memory::RAM_SIZE),
|
||||
m_ExRam(Memory::EXRAM_SIZE)
|
||||
FifoRecorder::FifoRecorder()
|
||||
: m_IsRecording(false), m_WasRecording(false), m_RequestedRecordingEnd(false),
|
||||
m_RecordFramesRemaining(0), m_FinishedCb(nullptr), m_File(nullptr), m_SkipNextData(true),
|
||||
m_SkipFutureData(true), m_FrameEnded(false), m_Ram(Memory::RAM_SIZE),
|
||||
m_ExRam(Memory::EXRAM_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
FifoRecorder::~FifoRecorder()
|
||||
{
|
||||
m_IsRecording = false;
|
||||
m_IsRecording = false;
|
||||
}
|
||||
|
||||
void FifoRecorder::StartRecording(s32 numFrames, CallbackFunc finishedCb)
|
||||
{
|
||||
sMutex.lock();
|
||||
sMutex.lock();
|
||||
|
||||
delete m_File;
|
||||
delete m_File;
|
||||
|
||||
FifoAnalyzer::Init();
|
||||
FifoAnalyzer::Init();
|
||||
|
||||
m_File = new FifoDataFile;
|
||||
std::fill(m_Ram.begin(), m_Ram.end(), 0);
|
||||
std::fill(m_ExRam.begin(), m_ExRam.end(), 0);
|
||||
m_File = new FifoDataFile;
|
||||
std::fill(m_Ram.begin(), m_Ram.end(), 0);
|
||||
std::fill(m_ExRam.begin(), m_ExRam.end(), 0);
|
||||
|
||||
m_File->SetIsWii(SConfig::GetInstance().bWii);
|
||||
m_File->SetIsWii(SConfig::GetInstance().bWii);
|
||||
|
||||
if (!m_IsRecording)
|
||||
{
|
||||
m_WasRecording = false;
|
||||
m_IsRecording = true;
|
||||
m_RecordFramesRemaining = numFrames;
|
||||
}
|
||||
if (!m_IsRecording)
|
||||
{
|
||||
m_WasRecording = false;
|
||||
m_IsRecording = true;
|
||||
m_RecordFramesRemaining = numFrames;
|
||||
}
|
||||
|
||||
m_RequestedRecordingEnd = false;
|
||||
m_FinishedCb = finishedCb;
|
||||
m_RequestedRecordingEnd = false;
|
||||
m_FinishedCb = finishedCb;
|
||||
|
||||
sMutex.unlock();
|
||||
sMutex.unlock();
|
||||
}
|
||||
|
||||
void FifoRecorder::StopRecording()
|
||||
{
|
||||
m_RequestedRecordingEnd = true;
|
||||
m_RequestedRecordingEnd = true;
|
||||
}
|
||||
|
||||
void FifoRecorder::WriteGPCommand(u8* data, u32 size)
|
||||
{
|
||||
if (!m_SkipNextData)
|
||||
{
|
||||
// Assumes data contains all information for the command
|
||||
// Calls FifoRecorder::UseMemory
|
||||
u32 analyzed_size = FifoAnalyzer::AnalyzeCommand(data, FifoAnalyzer::DECODE_RECORD);
|
||||
if (!m_SkipNextData)
|
||||
{
|
||||
// Assumes data contains all information for the command
|
||||
// Calls FifoRecorder::UseMemory
|
||||
u32 analyzed_size = FifoAnalyzer::AnalyzeCommand(data, FifoAnalyzer::DECODE_RECORD);
|
||||
|
||||
// Make sure FifoPlayer's command analyzer agrees about the size of the command.
|
||||
if (analyzed_size != size)
|
||||
PanicAlert("FifoRecorder: Expected command to be %i bytes long, we were given %i bytes", analyzed_size, size);
|
||||
// Make sure FifoPlayer's command analyzer agrees about the size of the command.
|
||||
if (analyzed_size != size)
|
||||
PanicAlert("FifoRecorder: Expected command to be %i bytes long, we were given %i bytes",
|
||||
analyzed_size, size);
|
||||
|
||||
// Copy data to buffer
|
||||
size_t currentSize = m_FifoData.size();
|
||||
m_FifoData.resize(currentSize + size);
|
||||
memcpy(&m_FifoData[currentSize], data, size);
|
||||
}
|
||||
// Copy data to buffer
|
||||
size_t currentSize = m_FifoData.size();
|
||||
m_FifoData.resize(currentSize + size);
|
||||
memcpy(&m_FifoData[currentSize], data, size);
|
||||
}
|
||||
|
||||
if (m_FrameEnded && m_FifoData.size() > 0)
|
||||
{
|
||||
size_t dataSize = m_FifoData.size();
|
||||
m_CurrentFrame.fifoDataSize = (u32)dataSize;
|
||||
m_CurrentFrame.fifoData = new u8[dataSize];
|
||||
memcpy(m_CurrentFrame.fifoData, m_FifoData.data(), dataSize);
|
||||
if (m_FrameEnded && m_FifoData.size() > 0)
|
||||
{
|
||||
size_t dataSize = m_FifoData.size();
|
||||
m_CurrentFrame.fifoDataSize = (u32)dataSize;
|
||||
m_CurrentFrame.fifoData = new u8[dataSize];
|
||||
memcpy(m_CurrentFrame.fifoData, m_FifoData.data(), dataSize);
|
||||
|
||||
sMutex.lock();
|
||||
sMutex.lock();
|
||||
|
||||
// Copy frame to file
|
||||
// The file will be responsible for freeing the memory allocated for each frame's fifoData
|
||||
m_File->AddFrame(m_CurrentFrame);
|
||||
// Copy frame to file
|
||||
// The file will be responsible for freeing the memory allocated for each frame's fifoData
|
||||
m_File->AddFrame(m_CurrentFrame);
|
||||
|
||||
if (m_FinishedCb && m_RequestedRecordingEnd)
|
||||
m_FinishedCb();
|
||||
if (m_FinishedCb && m_RequestedRecordingEnd)
|
||||
m_FinishedCb();
|
||||
|
||||
sMutex.unlock();
|
||||
sMutex.unlock();
|
||||
|
||||
m_CurrentFrame.memoryUpdates.clear();
|
||||
m_FifoData.clear();
|
||||
m_FrameEnded = false;
|
||||
}
|
||||
m_CurrentFrame.memoryUpdates.clear();
|
||||
m_FifoData.clear();
|
||||
m_FrameEnded = false;
|
||||
}
|
||||
|
||||
m_SkipNextData = m_SkipFutureData;
|
||||
m_SkipNextData = m_SkipFutureData;
|
||||
}
|
||||
|
||||
void FifoRecorder::UseMemory(u32 address, u32 size, MemoryUpdate::Type type, bool dynamicUpdate)
|
||||
{
|
||||
u8* curData;
|
||||
u8* newData;
|
||||
if (address & 0x10000000)
|
||||
{
|
||||
curData = &m_ExRam[address & Memory::EXRAM_MASK];
|
||||
newData = &Memory::m_pEXRAM[address & Memory::EXRAM_MASK];
|
||||
}
|
||||
else
|
||||
{
|
||||
curData = &m_Ram[address & Memory::RAM_MASK];
|
||||
newData = &Memory::m_pRAM[address & Memory::RAM_MASK];
|
||||
}
|
||||
u8* curData;
|
||||
u8* newData;
|
||||
if (address & 0x10000000)
|
||||
{
|
||||
curData = &m_ExRam[address & Memory::EXRAM_MASK];
|
||||
newData = &Memory::m_pEXRAM[address & Memory::EXRAM_MASK];
|
||||
}
|
||||
else
|
||||
{
|
||||
curData = &m_Ram[address & Memory::RAM_MASK];
|
||||
newData = &Memory::m_pRAM[address & Memory::RAM_MASK];
|
||||
}
|
||||
|
||||
if (!dynamicUpdate && memcmp(curData, newData, size) != 0)
|
||||
{
|
||||
// Update current memory
|
||||
memcpy(curData, newData, size);
|
||||
if (!dynamicUpdate && memcmp(curData, newData, size) != 0)
|
||||
{
|
||||
// Update current memory
|
||||
memcpy(curData, newData, size);
|
||||
|
||||
// Record memory update
|
||||
MemoryUpdate memUpdate;
|
||||
memUpdate.address = address;
|
||||
memUpdate.fifoPosition = (u32)(m_FifoData.size());
|
||||
memUpdate.size = size;
|
||||
memUpdate.type = type;
|
||||
memUpdate.data = new u8[size];
|
||||
memcpy(memUpdate.data, newData, size);
|
||||
// Record memory update
|
||||
MemoryUpdate memUpdate;
|
||||
memUpdate.address = address;
|
||||
memUpdate.fifoPosition = (u32)(m_FifoData.size());
|
||||
memUpdate.size = size;
|
||||
memUpdate.type = type;
|
||||
memUpdate.data = new u8[size];
|
||||
memcpy(memUpdate.data, newData, size);
|
||||
|
||||
m_CurrentFrame.memoryUpdates.push_back(memUpdate);
|
||||
}
|
||||
else if (dynamicUpdate)
|
||||
{
|
||||
// Shadow the data so it won't be recorded as changed by a future UseMemory
|
||||
memcpy(curData, newData, size);
|
||||
}
|
||||
m_CurrentFrame.memoryUpdates.push_back(memUpdate);
|
||||
}
|
||||
else if (dynamicUpdate)
|
||||
{
|
||||
// Shadow the data so it won't be recorded as changed by a future UseMemory
|
||||
memcpy(curData, newData, size);
|
||||
}
|
||||
}
|
||||
|
||||
void FifoRecorder::EndFrame(u32 fifoStart, u32 fifoEnd)
|
||||
{
|
||||
// m_IsRecording is assumed to be true at this point, otherwise this function would not be called
|
||||
// m_IsRecording is assumed to be true at this point, otherwise this function would not be called
|
||||
|
||||
sMutex.lock();
|
||||
sMutex.lock();
|
||||
|
||||
m_FrameEnded = true;
|
||||
m_FrameEnded = true;
|
||||
|
||||
m_CurrentFrame.fifoStart = fifoStart;
|
||||
m_CurrentFrame.fifoEnd = fifoEnd;
|
||||
m_CurrentFrame.fifoStart = fifoStart;
|
||||
m_CurrentFrame.fifoEnd = fifoEnd;
|
||||
|
||||
if (m_WasRecording)
|
||||
{
|
||||
// If recording a fixed number of frames then check if the end of the recording was reached
|
||||
if (m_RecordFramesRemaining > 0)
|
||||
{
|
||||
--m_RecordFramesRemaining;
|
||||
if (m_RecordFramesRemaining == 0)
|
||||
m_RequestedRecordingEnd = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_WasRecording = true;
|
||||
if (m_WasRecording)
|
||||
{
|
||||
// If recording a fixed number of frames then check if the end of the recording was reached
|
||||
if (m_RecordFramesRemaining > 0)
|
||||
{
|
||||
--m_RecordFramesRemaining;
|
||||
if (m_RecordFramesRemaining == 0)
|
||||
m_RequestedRecordingEnd = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_WasRecording = true;
|
||||
|
||||
// Skip the first data which will be the frame copy command
|
||||
m_SkipNextData = true;
|
||||
m_SkipFutureData = false;
|
||||
// Skip the first data which will be the frame copy command
|
||||
m_SkipNextData = true;
|
||||
m_SkipFutureData = false;
|
||||
|
||||
m_FrameEnded = false;
|
||||
m_FrameEnded = false;
|
||||
|
||||
m_FifoData.reserve(1024 * 1024 * 4);
|
||||
m_FifoData.clear();
|
||||
}
|
||||
m_FifoData.reserve(1024 * 1024 * 4);
|
||||
m_FifoData.clear();
|
||||
}
|
||||
|
||||
if (m_RequestedRecordingEnd)
|
||||
{
|
||||
// Skip data after the next time WriteFifoData is called
|
||||
m_SkipFutureData = true;
|
||||
// Signal video backend that it should not call this function when the next frame ends
|
||||
m_IsRecording = false;
|
||||
}
|
||||
if (m_RequestedRecordingEnd)
|
||||
{
|
||||
// Skip data after the next time WriteFifoData is called
|
||||
m_SkipFutureData = true;
|
||||
// Signal video backend that it should not call this function when the next frame ends
|
||||
m_IsRecording = false;
|
||||
}
|
||||
|
||||
sMutex.unlock();
|
||||
sMutex.unlock();
|
||||
}
|
||||
|
||||
void FifoRecorder::SetVideoMemory(u32 *bpMem, u32 *cpMem, u32 *xfMem, u32 *xfRegs, u32 xfRegsSize)
|
||||
void FifoRecorder::SetVideoMemory(u32* bpMem, u32* cpMem, u32* xfMem, u32* xfRegs, u32 xfRegsSize)
|
||||
{
|
||||
sMutex.lock();
|
||||
sMutex.lock();
|
||||
|
||||
if (m_File)
|
||||
{
|
||||
memcpy(m_File->GetBPMem(), bpMem, FifoDataFile::BP_MEM_SIZE * 4);
|
||||
memcpy(m_File->GetCPMem(), cpMem, FifoDataFile::CP_MEM_SIZE * 4);
|
||||
memcpy(m_File->GetXFMem(), xfMem, FifoDataFile::XF_MEM_SIZE * 4);
|
||||
if (m_File)
|
||||
{
|
||||
memcpy(m_File->GetBPMem(), bpMem, FifoDataFile::BP_MEM_SIZE * 4);
|
||||
memcpy(m_File->GetCPMem(), cpMem, FifoDataFile::CP_MEM_SIZE * 4);
|
||||
memcpy(m_File->GetXFMem(), xfMem, FifoDataFile::XF_MEM_SIZE * 4);
|
||||
|
||||
u32 xfRegsCopySize = std::min((u32)FifoDataFile::XF_REGS_SIZE, xfRegsSize);
|
||||
memcpy(m_File->GetXFRegs(), xfRegs, xfRegsCopySize * 4);
|
||||
}
|
||||
u32 xfRegsCopySize = std::min((u32)FifoDataFile::XF_REGS_SIZE, xfRegsSize);
|
||||
memcpy(m_File->GetXFRegs(), xfRegs, xfRegsCopySize * 4);
|
||||
}
|
||||
|
||||
FifoRecordAnalyzer::Initialize(cpMem);
|
||||
FifoRecordAnalyzer::Initialize(cpMem);
|
||||
|
||||
sMutex.unlock();
|
||||
sMutex.unlock();
|
||||
}
|
||||
|
||||
FifoRecorder& FifoRecorder::GetInstance()
|
||||
{
|
||||
return instance;
|
||||
return instance;
|
||||
}
|
||||
|
@ -12,57 +12,57 @@
|
||||
class FifoRecorder
|
||||
{
|
||||
public:
|
||||
typedef void(*CallbackFunc)(void);
|
||||
typedef void (*CallbackFunc)(void);
|
||||
|
||||
FifoRecorder();
|
||||
~FifoRecorder();
|
||||
FifoRecorder();
|
||||
~FifoRecorder();
|
||||
|
||||
void StartRecording(s32 numFrames, CallbackFunc finishedCb);
|
||||
void StopRecording();
|
||||
void StartRecording(s32 numFrames, CallbackFunc finishedCb);
|
||||
void StopRecording();
|
||||
|
||||
FifoDataFile* GetRecordedFile() { return m_File; }
|
||||
FifoDataFile* GetRecordedFile() { return m_File; }
|
||||
// Called from video thread
|
||||
|
||||
// Called from video thread
|
||||
// Must write one full GP command at a time
|
||||
void WriteGPCommand(u8* data, u32 size);
|
||||
|
||||
// Must write one full GP command at a time
|
||||
void WriteGPCommand(u8* data, u32 size);
|
||||
// Track memory that has been used and write it to the fifolog if it has changed.
|
||||
// If memory is updated by the video backend (dynamicUpdate == true) take special care to make
|
||||
// sure the data
|
||||
// isn't baked into the fifolog.
|
||||
void UseMemory(u32 address, u32 size, MemoryUpdate::Type type, bool dynamicUpdate = false);
|
||||
|
||||
// Track memory that has been used and write it to the fifolog if it has changed.
|
||||
// If memory is updated by the video backend (dynamicUpdate == true) take special care to make sure the data
|
||||
// isn't baked into the fifolog.
|
||||
void UseMemory(u32 address, u32 size, MemoryUpdate::Type type, bool dynamicUpdate = false);
|
||||
void EndFrame(u32 fifoStart, u32 fifoEnd);
|
||||
|
||||
void EndFrame(u32 fifoStart, u32 fifoEnd);
|
||||
// This function must be called before writing GP commands
|
||||
// bpMem must point to the actual bp mem array used by the plugin because it will be read as fifo
|
||||
// data is recorded
|
||||
void SetVideoMemory(u32* bpMem, u32* cpMem, u32* xfMem, u32* xfRegs, u32 xfRegsSize);
|
||||
|
||||
// This function must be called before writing GP commands
|
||||
// bpMem must point to the actual bp mem array used by the plugin because it will be read as fifo data is recorded
|
||||
void SetVideoMemory(u32* bpMem, u32* cpMem, u32* xfMem, u32* xfRegs, u32 xfRegsSize);
|
||||
|
||||
// Checked once per frame prior to callng EndFrame()
|
||||
bool IsRecording() const { return m_IsRecording; }
|
||||
|
||||
static FifoRecorder& GetInstance();
|
||||
// Checked once per frame prior to callng EndFrame()
|
||||
bool IsRecording() const { return m_IsRecording; }
|
||||
static FifoRecorder& GetInstance();
|
||||
|
||||
private:
|
||||
// Accessed from both GUI and video threads
|
||||
// Accessed from both GUI and video threads
|
||||
|
||||
// True if video thread should send data
|
||||
volatile bool m_IsRecording;
|
||||
// True if m_IsRecording was true during last frame
|
||||
volatile bool m_WasRecording;
|
||||
volatile bool m_RequestedRecordingEnd;
|
||||
volatile s32 m_RecordFramesRemaining;
|
||||
volatile CallbackFunc m_FinishedCb;
|
||||
// True if video thread should send data
|
||||
volatile bool m_IsRecording;
|
||||
// True if m_IsRecording was true during last frame
|
||||
volatile bool m_WasRecording;
|
||||
volatile bool m_RequestedRecordingEnd;
|
||||
volatile s32 m_RecordFramesRemaining;
|
||||
volatile CallbackFunc m_FinishedCb;
|
||||
|
||||
FifoDataFile* volatile m_File;
|
||||
FifoDataFile* volatile m_File;
|
||||
|
||||
// Accessed only from video thread
|
||||
// Accessed only from video thread
|
||||
|
||||
bool m_SkipNextData;
|
||||
bool m_SkipFutureData;
|
||||
bool m_FrameEnded;
|
||||
FifoFrameInfo m_CurrentFrame;
|
||||
std::vector<u8> m_FifoData;
|
||||
std::vector<u8> m_Ram;
|
||||
std::vector<u8> m_ExRam;
|
||||
bool m_SkipNextData;
|
||||
bool m_SkipFutureData;
|
||||
bool m_FrameEnded;
|
||||
FifoFrameInfo m_CurrentFrame;
|
||||
std::vector<u8> m_FifoData;
|
||||
std::vector<u8> m_Ram;
|
||||
std::vector<u8> m_ExRam;
|
||||
};
|
||||
|
Reference in New Issue
Block a user