SIEdit/lib/chunk.cpp

234 lines
4.6 KiB
C++
Raw Normal View History

2022-06-23 21:36:00 -04:00
#include "chunk.h"
#include <algorithm>
#include <cstring>
2022-06-23 21:36:00 -04:00
#include <iostream>
2022-07-03 11:57:43 -04:00
#include "sitypes.h"
2022-06-23 21:36:00 -04:00
namespace si {
Chunk::Chunk(uint32_t id) :
id_(id),
offset_(0)
2022-06-23 21:36:00 -04:00
{
}
Chunk::~Chunk()
{
Clear();
}
bool Chunk::Read(const std::string &f)
{
return Read(f.c_str());
}
bool Chunk::Read(const char *f)
{
std::ifstream file(f, std::ios::in | std::ios::binary);
if (!file.is_open() || !file.good()) {
return false;
}
uint32_t alignment = 0, version = 0;
return Read(file, version, alignment);
}
bool Chunk::Read(std::ifstream &f, uint32_t &version, uint32_t &alignment)
2022-06-23 21:36:00 -04:00
{
uint32_t size;
2022-06-23 21:36:00 -04:00
offset_ = uint32_t(f.tellg());
2022-06-23 21:36:00 -04:00
// Read ID and size, which every chunk starts with
f.read((char *) &id_, sizeof(id_));
f.read((char *) &size, sizeof(size));
// Store end of this chunk
uint32_t pos = uint32_t(f.tellg());
uint32_t end = pos + size;
2022-06-23 21:36:00 -04:00
// Read custom data from this chunk
Clear();
if (RIFF *reader = GetReaderFromType(type())) {
reader->Read(f, data_, version, size);
if (type() == TYPE_MxHd) {
version = data_["Version"];
alignment = data_["BufferSize"];
}
delete reader;
}
2022-06-23 21:36:00 -04:00
// Assume any remaining data is this chunk's children
while (f.good() && (size_t(f.tellg()) + 4) < end) {
// Check alignment, if there's not enough room to for another segment, skip ahead
if (alignment > 0) {
uint32_t offset_in_buffer = f.tellg()%alignment;
if (offset_in_buffer + sizeof(uint32_t)*2 > alignment) {
f.seekg(alignment-offset_in_buffer, std::ios::cur);
}
}
2022-06-23 21:36:00 -04:00
Chunk *child = new Chunk();
child->Read(f, version, alignment);
2022-06-23 21:36:00 -04:00
this->AppendChild(child);
}
if (f.tellg() < end) {
f.seekg(end, std::ios::beg);
}
if (size%2 == 1) {
2022-06-23 21:36:00 -04:00
f.seekg(1, std::ios::cur);
}
return true;
}
bool Chunk::Write(std::ofstream &f, const uint32_t &version) const
{
// Write 4-byte ID
f.write((const char *) &id_, sizeof(id_));
// Write placeholder for size and store position so we can come back later
uint32_t chunk_size = 0;
std::ios::pos_type size_pos = f.tellp();
f.write((const char *) &chunk_size, sizeof(chunk_size));
if (RIFF *writer = GetReaderFromType(type())) {
writer->Write(f, data_, version);
delete writer;
}
for (Children::const_iterator it=GetChildren().begin(); it!=GetChildren().end(); it++) {
static_cast<Chunk*>(*it)->Write(f, version);
}
// Backtrack and write chunk size
chunk_size = uint32_t(f.tellp()) - (uint32_t(size_pos) + sizeof(uint32_t));
f.seekp(size_pos);
f.write((const char *) &chunk_size, sizeof(chunk_size));
f.seekp(0, std::ios::end);
// Byte align to 2
if (chunk_size%2 == 1) {
const char nothing = 0;
f.write(&nothing, 1);
}
return true;
}
RIFF *Chunk::GetReaderFromType(Type type)
{
switch (type) {
case TYPE_RIFF:
return new RIFF();
case TYPE_MxHd:
return new MxHd();
case TYPE_LIST:
return new LIST();
case TYPE_MxSt:
return new MxSt();
case TYPE_MxCh:
return new MxCh();
case TYPE_MxOf:
return new MxOf();
case TYPE_pad_:
return new pad_();
case TYPE_MxOb:
return new MxOb();
case TYPE_MxDa:
break;
}
return NULL;
}
2022-06-23 21:36:00 -04:00
void Chunk::Clear()
{
// Delete data
data_.clear();
2022-06-23 21:36:00 -04:00
// Delete children
DeleteChildren();
2022-06-23 21:36:00 -04:00
}
bool Chunk::Write(const std::string &f)
{
return Write(f.c_str());
}
bool Chunk::Write(const char *f)
{
std::ofstream file(f, std::ios::out | std::ios::binary);
if (!file.is_open() || !file.good()) {
return false;
}
uint32_t version = 0;
return Write(file, version);
}
2022-06-23 21:36:00 -04:00
const char *Chunk::GetTypeDescription(Type type)
{
switch (type) {
case TYPE_RIFF:
2022-06-23 21:36:00 -04:00
return "Resource Interchange File Format";
case TYPE_LIST:
2022-06-23 21:36:00 -04:00
return "List of sub-elements";
case TYPE_MxSt:
return "Stream";
case TYPE_MxHd:
2022-06-24 13:46:25 -04:00
return "Interleaf Header";
case TYPE_MxCh:
return "Data Chunk";
case TYPE_MxOf:
return "Offset Table";
case TYPE_pad_:
2022-06-24 13:46:25 -04:00
return "Padding";
case TYPE_MxOb:
return "Streamable Object";
case TYPE_MxDa:
return "Data";
2022-06-23 21:36:00 -04:00
}
return "Unknown";
}
Chunk *Chunk::FindChildWithType(Type type) const
{
for (Core *child : GetChildren()) {
Chunk *chunk = static_cast<Chunk*>(child);
if (chunk->type() == type) {
return chunk;
} else if (Chunk *grandchild = chunk->FindChildWithType(type)) {
return grandchild;
}
}
return NULL;
}
Chunk *Chunk::FindChildWithOffset(uint32_t offset) const
{
for (Core *child : GetChildren()) {
Chunk *chunk = static_cast<Chunk*>(child);
if (chunk->offset() == offset) {
return chunk;
} else if (Chunk *grandchild = chunk->FindChildWithOffset(offset)) {
return grandchild;
}
}
return NULL;
}
}