Created an IffFile class which contains the IFF parsing logic, changed iffexplore to use this class.
135 lines
3.3 KiB
C++
135 lines
3.3 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 <sys/mman.h>
|
|
|
|
#include "common.hh"
|
|
#include "IffFile.hh"
|
|
|
|
void blobDump(IffFile::Object const& obj, std::string const& filename)
|
|
{
|
|
FILEUPtr file{fopen(filename.c_str(), "wb")};
|
|
if (!file)
|
|
throw POSIXException{errno, "Could not open file: " + filename};
|
|
|
|
if (fwrite(obj.begin(), obj.getSize(), 1, file.get()) != 1)
|
|
throw POSIXException{errno, "Could not write"};
|
|
}
|
|
|
|
|
|
void iffDumper(IffFile::Object const& obj, bool dumpBlobs, std::string dumpPath, unsigned& blobCount, unsigned level = 0)
|
|
{
|
|
for (unsigned i = 0;i < level;++i)
|
|
putchar('\t');
|
|
printf("%s Length %lu (0x%.lx)", obj.getType().c_str(), obj.getSize(), obj.getSize());
|
|
|
|
if (obj.isForm()) {
|
|
auto form = dynamic_cast<IffFile::Form const&>(obj);
|
|
printf(", Subtype %s\n", form.getSubtype().c_str());
|
|
for(auto it = form.childrenBegin();it != form.childrenEnd();++it)
|
|
iffDumper(*it, dumpBlobs, dumpPath, blobCount, level+1);
|
|
} else {
|
|
try {
|
|
printf(" = \"%s\"\n", static_cast<std::string>(obj).c_str());
|
|
} catch(FormatException &ex) {
|
|
if (dumpBlobs) {
|
|
std::string filename{dumpPath};
|
|
filename += obj.getType() + "-"s + std::to_string(blobCount++);
|
|
printf(" dump to %s\n", filename.c_str());
|
|
blobDump(obj, filename);
|
|
} else
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void usage(char *argv0) {
|
|
fprintf(stderr, "Usage: %s [-sh] [-d dest] iff-file\n", argv0);
|
|
fprintf(stderr, "\t-s\tPrint the iff-file's structure\n");
|
|
fprintf(stderr, "\t-d dest\tDump BLOBs as files to dest/\n");
|
|
fprintf(stderr, "\t-h\tPrint this help\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
bool printStructure = false, dumpBlobs = false;
|
|
std::string dumpPath, iffFile;
|
|
{
|
|
int opt;
|
|
while ((opt = getopt(argc, argv, "hsd:")) != -1) {
|
|
switch (opt) {
|
|
case 'h':
|
|
usage(argv[0]);
|
|
return 0;
|
|
case 's':
|
|
printStructure = true;
|
|
break;
|
|
case 'd':
|
|
dumpPath = optarg;
|
|
dumpBlobs = true;
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (optind >= argc) {
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
iffFile = argv[optind];
|
|
}
|
|
|
|
try {
|
|
FILEUPtr iffFD{fopen(iffFile.c_str(), "rb")};
|
|
if (!iffFD) {
|
|
throw POSIXException{errno, "Could not open "s + iffFile};
|
|
}
|
|
|
|
struct stat statBuf;
|
|
|
|
if (fstat(fileno(iffFD.get()), &statBuf) != 0) {
|
|
throw POSIXException(errno, "Could not stat");
|
|
}
|
|
|
|
char *mmapBase = static_cast<char*>(mmap(nullptr, statBuf.st_size, PROT_READ, MAP_SHARED, fileno(iffFD.get()), 0));
|
|
if (!mmapBase)
|
|
throw POSIXException(errno, "mmap failed");
|
|
|
|
try {
|
|
IffFile iff{mmapBase, static_cast<size_t>(statBuf.st_size)};
|
|
|
|
if (printStructure) {
|
|
unsigned blobCount = 0;
|
|
iffDumper(iff.getRoot(), dumpBlobs, dumpPath, blobCount);
|
|
}
|
|
} catch(...) {
|
|
munmap(mmapBase, statBuf.st_size);
|
|
throw;
|
|
}
|
|
|
|
if (munmap(mmapBase, statBuf.st_size) != 0)
|
|
fprintf(stderr, "Warning: munmap failed: %s\n", strerror(errno));
|
|
|
|
} catch (POSIXException &ex) {
|
|
fflush(stdout);
|
|
fprintf(stderr, "%s\n", ex.toString().c_str());
|
|
return 2;
|
|
} catch (FormatException &ex) {
|
|
fflush(stdout);
|
|
fprintf(stderr, "%s\n", ex.toString().c_str());
|
|
return 3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|