Skip to content

Instantly share code, notes, and snippets.

@ligfx
Created April 27, 2017 03:13
Show Gist options
  • Save ligfx/a9901bc32fd639a3796c7e34b41f5554 to your computer and use it in GitHub Desktop.
Save ligfx/a9901bc32fd639a3796c7e34b41f5554 to your computer and use it in GitHub Desktop.
// Helper class for describing binary formats like FIFO log files. Create a
// subclass and implement Describe(...), then call ::Read or ::Write on it.
// Curiously-recurring template pattern
template <typename Derived>
class BinaryFormatDescription
{
template <typename T>
struct is_simple
{
static const bool value = std::is_arithmetic<T>::value || std::is_enum<T>::value;
};
public:
template <typename... Args>
static auto Read(File::IOFile* file, Args... args)
{
Derived d;
d.m_file = file;
d.m_mode = Mode::Read;
auto ret = d.Describe(args...);
return ret;
}
template <typename... Args>
static auto Write(File::IOFile* file, Args... args)
{
Derived d;
d.m_file = file;
d.m_mode = Mode::Write;
auto ret = d.Describe(args...);
return ret;
}
protected:
template <typename T>
std::enable_if_t<is_simple<T>::value> Value(T& val)
{
ReadOrWrite(&val, sizeof(val));
}
template <typename T, typename U>
std::enable_if_t<is_simple<T>::value && !std::is_same<T, U>::value> Value(U& val)
{
T t = val;
ReadOrWrite(&t, sizeof(t));
val = static_cast<U>(t);
}
template <typename T>
std::enable_if_t<is_simple<T>::value, T> Constant(const T& val)
{
T t = val;
ReadOrWrite(&t, sizeof(t));
return t;
}
template <typename T, size_t N>
std::enable_if_t<is_simple<T>::value> Value(std::array<T, N>& val)
{
ReadOrWrite(val.data(), sizeof(T) * N);
}
template <typename T>
std::enable_if_t<is_simple<T>::value> Value(std::vector<T>& val)
{
ReadOrWrite(val.data(), sizeof(T) * val.size());
}
void Skip(size_t size) { m_file->Seek(size, SEEK_CUR); }
template <typename T, typename U = u32>
void SizeOf(T& seq)
{
U size = seq.size();
ReadOrWrite(&size, sizeof(size));
if (size != seq.size())
seq.resize(size);
}
private:
void ReadOrWrite(void* data, size_t size)
{
if (m_mode == Mode::Read)
m_file->ReadBytes(data, size);
else
m_file->WriteBytes(data, size);
}
enum class Mode
{
Read,
Write
};
File::IOFile* m_file = nullptr;
Mode m_mode = Mode::Read;
};
class FifoDataFileDescription : public BinaryFormatDescription<FifoDataFileDescription>
{
public:
bool Describe(FifoDataFile* fifo, bool flags_only)
{
u32 file_id = Constant<u32>(FILE_ID);
if (file_id != FILE_ID)
return false;
Value<u32>(fifo->m_Version);
auto min_loader_version = Constant<u32>(MIN_LOADER_VERSION);
if (min_loader_version > VERSION_NUMBER)
return false;
Skip(56);
SizeOf(fifo->m_Frames);
Value<u32>(fifo->m_Flags);
Skip(52);
if (flags_only)
return true;
for (auto& frame : fifo->m_Frames)
{
Skip(8);
SizeOf(frame.fifoData);
Value<u32>(frame.fifoStart);
Value<u32>(frame.fifoEnd);
Skip(8);
SizeOf(frame.memoryUpdates);
Skip(32);
}
Value(fifo->m_BPMem);
Value(fifo->m_CPMem);
Value(fifo->m_XFMem);
Value(fifo->m_XFRegs);
if (fifo->m_Version >= 4)
{
Value(fifo->m_TexMem);
}
for (auto& frame : fifo->m_Frames)
{
Value(frame.fifoData);
for (auto& update : frame.memoryUpdates)
{
Value<u32>(update.fifoPosition);
Value<u32>(update.address);
Skip(8);
SizeOf(update.data);
Value<u8>(update.type);
Skip(3);
}
for (auto& update : frame.memoryUpdates)
{
Value(update.data);
}
}
return true;
}
};
bool FifoDataFile::Save(const std::string& filename)
{
File::IOFile file;
if (!file.Open(filename, "wb"))
return false;
return FifoDataFileDescription::Write(&file, this, false);
}
std::unique_ptr<FifoDataFile> FifoDataFile::Load(const std::string& filename, bool flagsOnly)
{
File::IOFile file;
if (!file.Open(filename, "rb"))
return nullptr;
auto dataFile = std::make_unique<FifoDataFile>();
if (FifoDataFileDescription::Read(&file, dataFile.get(), flagsOnly))
return dataFile;
return nullptr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment