176 lines
4.4 KiB
C++
176 lines
4.4 KiB
C++
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cerrno>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "common.hh"
|
|
|
|
struct ChunkHeader {
|
|
char typeID[4];
|
|
uint32_t length; // big endian!!
|
|
} __attribute__((__packed__));
|
|
|
|
void parseBlob(FILE *iffFile, off_t start, off_t length, char name[4], unsigned level, bool dump = false)
|
|
{
|
|
static unsigned count = 0;
|
|
|
|
if (fseeko(iffFile, start, SEEK_SET) != 0) {
|
|
throw POSIXException(errno, "Could not seek");
|
|
}
|
|
|
|
std::vector<char> buf(length);
|
|
if (fread(buf.data(), length, 1, iffFile) != 1)
|
|
throw POSIXException{errno, "Could not read data"};
|
|
|
|
// Check if BLOB is string
|
|
{
|
|
enum class State { STARTASCII, NULLS, ERROR };
|
|
State state = State::STARTASCII;
|
|
for (char const& c : buf) {
|
|
switch(state) {
|
|
case State::STARTASCII:
|
|
if (isprint(c))
|
|
continue;
|
|
if (c == '\0')
|
|
state = State::NULLS;
|
|
else
|
|
state = State::ERROR;
|
|
break;
|
|
case State::NULLS:
|
|
if (c != '\0')
|
|
state = State::ERROR;
|
|
break;
|
|
case State::ERROR:
|
|
break;
|
|
}
|
|
|
|
if (state == State::ERROR)
|
|
break;
|
|
}
|
|
if (state == State::NULLS) { // BLOB is zero(s)-terminated string
|
|
for (unsigned i = 0;i < level;++i)
|
|
putchar('\t');
|
|
printf("= '%s'\n", buf.data());
|
|
return;
|
|
} else if (state == State::STARTASCII) { // BLOB is string
|
|
buf.push_back('\0');
|
|
for (unsigned i = 0;i < level;++i)
|
|
putchar('\t');
|
|
printf("= '%s'\n", buf.data());
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Nothing string-y, so just dump it to a file
|
|
std::string filename{"tmp/"};
|
|
if (count < 10)
|
|
filename.append("0");
|
|
filename.append(std::to_string(count) + "-");
|
|
filename.append(name, 4);
|
|
|
|
if (dump) {
|
|
FILEUPtr outFile{fopen(filename.c_str(), "wb")};
|
|
if (!outFile)
|
|
throw POSIXException{errno, "Could not open " + filename};
|
|
|
|
for (unsigned i = 0;i < level;++i)
|
|
putchar('\t');
|
|
printf("Dumping BLOB of length %ld (0x%lx) to %s\n", length, length, filename.c_str());
|
|
|
|
if (fwrite(buf.data(), length, 1, outFile.get()) != 1)
|
|
throw POSIXException{errno, "Could not write data"};
|
|
} else {
|
|
for (unsigned i = 0;i < level;++i)
|
|
putchar('\t');
|
|
printf("BLOB of length %ld (0x%lx)\n", length, length);
|
|
}
|
|
|
|
++count;
|
|
}
|
|
|
|
void parseChunk(FILE* iffFile, off_t start, off_t length, unsigned level = 0)
|
|
{
|
|
off_t pos = 0;
|
|
|
|
while (pos < length) {
|
|
if (fseeko(iffFile, pos+start, SEEK_SET) != 0) {
|
|
throw POSIXException(errno, "Could not seek");
|
|
}
|
|
|
|
// pos < length due to loop condition, cast of difference to unsigned is OK
|
|
if (static_cast<unsigned>(length-pos) < sizeof(ChunkHeader))
|
|
throw FormatException{"Remaining size < header size"};
|
|
|
|
ChunkHeader header;
|
|
|
|
if (fread(&header, sizeof(ChunkHeader), 1, iffFile) != 1) {
|
|
if (feof(iffFile))
|
|
return;
|
|
throw POSIXException(errno, "Could not read header");
|
|
}
|
|
|
|
header.length = ntohl(header.length);
|
|
for (unsigned i = 0;i < level;++i)
|
|
putchar('\t');
|
|
printf("Type: %.4s, Length: %u (0x%x)", header.typeID, header.length, header.length);
|
|
if (header.length > (length-(pos+8)))
|
|
throw FormatException{"Length in header > remaining parent size"};
|
|
|
|
if (memcmp(header.typeID, "FORM", 4) == 0) {
|
|
char subType[4];
|
|
if (fread(&subType, 4, 1, iffFile) != 1) {
|
|
throw POSIXException(errno, "Could not read form subtype");
|
|
}
|
|
printf(", SubType: %.4s\n", subType);
|
|
|
|
parseChunk(iffFile, start+pos+12, header.length-4, level+1);
|
|
} else {
|
|
putchar('\n');
|
|
parseBlob(iffFile, start+pos+8, header.length, header.typeID, level+1);
|
|
}
|
|
|
|
pos += header.length+8;
|
|
if (pos%2 != 0)
|
|
++pos;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if (argc != 2) {
|
|
fprintf(stderr, "Usage: %s iff-file\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
try {
|
|
FILEUPtr iffFile{fopen(argv[1], "rb")};
|
|
if (!iffFile) {
|
|
throw POSIXException{errno, "Could not open "s + argv[1]};
|
|
}
|
|
|
|
struct stat statBuf;
|
|
|
|
if (fstat(fileno(iffFile.get()), &statBuf) != 0) {
|
|
throw POSIXException(errno, "Could not stat");
|
|
}
|
|
|
|
parseChunk(iffFile.get(), 0, statBuf.st_size);
|
|
|
|
} catch (POSIXException &ex) {
|
|
fprintf(stderr, "%s\n", ex.toString().c_str());
|
|
return 2;
|
|
} catch (FormatException &ex) {
|
|
fprintf(stderr, "%s\n", ex.toString().c_str());
|
|
return 3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|