Reformat all the things. Have fun with merge conflicts.

This commit is contained in:
Pierre Bourdon
2016-06-24 10:43:46 +02:00
parent 2115e8a4a6
commit 3570c7f03a
1116 changed files with 187405 additions and 180344 deletions

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
};

View File

@ -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)
}

View File

@ -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);
}
}

View File

@ -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);
};

View File

@ -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, &regs[i]);
regs = m_File->GetXFMem();
for (int i = 0; i < FifoDataFile::XF_MEM_SIZE; i += 16)
LoadXFMem16(i, &regs[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;
}

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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;
};