// Copyright 2013 Dolphin Emulator Project // Licensed under GPLv2 // Refer to the license.txt file included. #include #include #include "Common/FileUtil.h" #include "Core/FifoPlayer/FifoDataFile.h" #include "Core/FifoPlayer/FifoFileStruct.h" using namespace FifoFileStruct; FifoDataFile::FifoDataFile() : m_Flags(0) { } FifoDataFile::~FifoDataFile() { for (auto& frame : m_Frames) { for (auto& update : frame.memoryUpdates) delete []update.data; delete []frame.fifoData; } } void FifoDataFile::SetIsWii(bool isWii) { SetFlag(FLAG_IS_WII, isWii); } bool FifoDataFile::GetIsWii() const { return GetFlag(FLAG_IS_WII); } void FifoDataFile::AddFrame(const FifoFrameInfo &frameInfo) { m_Frames.push_back(frameInfo); } bool FifoDataFile::Save(const std::string& filename) { File::IOFile file; if (!file.Open(filename, "wb")) return false; // Add space for header PadFile(sizeof(FileHeader), 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 cpMemOffset = file.Tell(); file.WriteArray(m_CPMem, CP_MEM_SIZE); u64 xfMemOffset = file.Tell(); file.WriteArray(m_XFMem, XF_MEM_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; header.bpMemOffset = bpMemOffset; header.bpMemSize = BP_MEM_SIZE; header.cpMemOffset = cpMemOffset; header.cpMemSize = CP_MEM_SIZE; header.xfMemOffset = xfMemOffset; header.xfMemSize = XF_MEM_SIZE; header.xfRegsOffset = xfRegsOffset; header.xfRegsSize = XF_REGS_SIZE; header.frameListOffset = frameListOffset; header.frameCount = (u32)m_Frames.size(); header.flags = m_Flags; 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 FIFO data file.Seek(0, SEEK_END); u64 dataOffset = file.Tell(); file.WriteBytes(srcFrame.fifoData, srcFrame.fifoDataSize); 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(); // Write frame info u64 frameOffset = frameListOffset + (i * sizeof(FileFrameInfo)); file.Seek(frameOffset, SEEK_SET); file.WriteBytes(&dstFrame, sizeof(FileFrameInfo)); } if (!file.Close()) return false; return true; } FifoDataFile *FifoDataFile::Load(const std::string &filename, bool flagsOnly) { File::IOFile file; file.Open(filename, "rb"); if (!file) return nullptr; FileHeader header; file.ReadBytes(&header, sizeof(header)); if (header.fileId != FILE_ID || header.min_loader_version > VERSION_NUMBER) { file.Close(); return nullptr; } FifoDataFile* dataFile = new FifoDataFile; dataFile->m_Flags = header.flags; 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); 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_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)); 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); ReadMemoryUpdates(srcFrame.memoryUpdatesOffset, srcFrame.numMemoryUpdates, dstFrame.memoryUpdates, file); dataFile->AddFrame(dstFrame); } file.Close(); return dataFile; } void FifoDataFile::PadFile(size_t numBytes, File::IOFile& file) { const u8 zero = 0; fwrite(&zero, sizeof(zero), numBytes, file.GetHandle()); } void FifoDataFile::SetFlag(u32 flag, bool set) { if (set) m_Flags |= flag; else m_Flags &= ~flag; } bool FifoDataFile::GetFlag(u32 flag) const { return !!(m_Flags & flag); } u64 FifoDataFile::WriteMemoryUpdates(const std::vector &memUpdates, File::IOFile &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]; // 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; u64 updateOffset = updateListOffset + (i * sizeof(FileMemoryUpdate)); file.Seek(updateOffset, SEEK_SET); file.WriteBytes(&dstUpdate, sizeof(FileMemoryUpdate)); } return updateListOffset; } void FifoDataFile::ReadMemoryUpdates(u64 fileOffset, u32 numUpdates, std::vector &memUpdates, File::IOFile &file) { 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)); 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); } }