treexplore: Add nested IFF parsing
Use IffFile to add an option to treexplore to show the structure of contained IFF files
This commit is contained in:
2
Makefile
2
Makefile
@@ -5,7 +5,7 @@ LDOPTS=
|
|||||||
IFFEXPLORE_CXXSRCS=iffexplore.cc IffFile.cc
|
IFFEXPLORE_CXXSRCS=iffexplore.cc IffFile.cc
|
||||||
IFFEXPLORE_OBJS=$(addprefix objs/,$(IFFEXPLORE_CXXSRCS:.cc=.o))
|
IFFEXPLORE_OBJS=$(addprefix objs/,$(IFFEXPLORE_CXXSRCS:.cc=.o))
|
||||||
|
|
||||||
TREEXPLORE_CXXSRCS=treexplore.cc TreFile.cc
|
TREEXPLORE_CXXSRCS=treexplore.cc TreFile.cc IffFile.cc
|
||||||
TREEXPLORE_OBJS=$(addprefix objs/,$(TREEXPLORE_CXXSRCS:.cc=.o))
|
TREEXPLORE_OBJS=$(addprefix objs/,$(TREEXPLORE_CXXSRCS:.cc=.o))
|
||||||
|
|
||||||
FONT2PNG_CXXSRCS=font2png.cc
|
FONT2PNG_CXXSRCS=font2png.cc
|
||||||
|
|||||||
102
TreFile.cc
102
TreFile.cc
@@ -73,33 +73,68 @@ TreFile::~TreFile()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> TreFile::getNames() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
for (auto ent : table2_) {
|
||||||
|
ret.push_back(ent.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> TreFile::getCRCs() const
|
||||||
|
{
|
||||||
|
std::vector<uint32_t> ret;
|
||||||
|
for (auto ent : table1_) {
|
||||||
|
ret.push_back(ent.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreFile::File TreFile::openName(std::string const& name) const
|
||||||
|
{
|
||||||
|
return openIdx_(findName_(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
TreFile::File TreFile::openCRC(uint32_t crc) const
|
||||||
|
{
|
||||||
|
return openIdx_(findCRC_(crc));
|
||||||
|
}
|
||||||
|
|
||||||
|
TreFile::Stat TreFile::statName(std::string const& name) const
|
||||||
|
{
|
||||||
|
return statIdx_(findName_(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
TreFile::Stat TreFile::statCRC(uint32_t crc) const
|
||||||
|
{
|
||||||
|
return statIdx_(findCRC_(crc));
|
||||||
|
}
|
||||||
|
|
||||||
void TreFile::dumpName(std::string path, std::string const& name)
|
void TreFile::dumpName(std::string path, std::string const& name)
|
||||||
{
|
{
|
||||||
auto it = table2_.find(name);
|
auto idx = findName_(name);
|
||||||
if (it == table2_.end())
|
|
||||||
throw Exception{name + " not found"};
|
|
||||||
|
|
||||||
if (path.back() != '/')
|
if (path.back() != '/')
|
||||||
path.push_back('/');
|
path.push_back('/');
|
||||||
path.append(name);
|
path.append(name);
|
||||||
|
|
||||||
dumpIdx_(path, it->second);
|
dumpIdx_(path, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreFile::dumpCRC(std::string path, uint32_t crc)
|
void TreFile::dumpCRC(std::string path, uint32_t crc)
|
||||||
{
|
{
|
||||||
|
auto idx = findCRC_(crc);
|
||||||
char crcStr[9];
|
char crcStr[9];
|
||||||
snprintf(crcStr, 9, "%.8X", crc);
|
snprintf(crcStr, 9, "%.8X", crc);
|
||||||
|
|
||||||
auto it = table1_.find(crc);
|
|
||||||
if (it == table1_.end())
|
|
||||||
throw Exception{"CRC "s + crcStr + " not found"s};
|
|
||||||
|
|
||||||
if (path.back() != '/')
|
if (path.back() != '/')
|
||||||
path.push_back('/');
|
path.push_back('/');
|
||||||
path.append(crcStr);
|
path.append(crcStr);
|
||||||
|
|
||||||
dumpIdx_(path, it->second);
|
dumpIdx_(path, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreFile::dumpAll(std::string path)
|
void TreFile::dumpAll(std::string path)
|
||||||
@@ -302,6 +337,27 @@ void TreFile::construct_()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t TreFile::findName_(std::string const& name) const
|
||||||
|
{
|
||||||
|
auto it = table2_.find(name);
|
||||||
|
if (it == table2_.end())
|
||||||
|
throw Exception{name + " not found"};
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TreFile::findCRC_(uint32_t crc) const
|
||||||
|
{
|
||||||
|
auto it = table1_.find(crc);
|
||||||
|
if (it == table1_.end()) {
|
||||||
|
char crcStr[9];
|
||||||
|
snprintf(crcStr, 9, "%.8X", crc);
|
||||||
|
throw Exception{"CRC "s + crcStr + " not found"s};
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
void TreFile::dumpIdx_(std::string const& name, size_t table3Idx)
|
void TreFile::dumpIdx_(std::string const& name, size_t table3Idx)
|
||||||
{
|
{
|
||||||
uint32_t ofs, len;
|
uint32_t ofs, len;
|
||||||
@@ -327,3 +383,31 @@ void TreFile::dumpIdx_(std::string const& name, size_t table3Idx)
|
|||||||
if (fwrite(buf.data(), len, 1, outFile.get()) != 1)
|
if (fwrite(buf.data(), len, 1, outFile.get()) != 1)
|
||||||
throw POSIXException{errno, "Could not write"};
|
throw POSIXException{errno, "Could not write"};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TreFile::File TreFile::openIdx_(size_t table3Idx) const
|
||||||
|
{
|
||||||
|
uint32_t ofs, len;
|
||||||
|
uint8_t flags;
|
||||||
|
std::tie(ofs, len, flags) = table3_[table3Idx];
|
||||||
|
|
||||||
|
if(flags&0x80)
|
||||||
|
throw Exception{"Compressed TRE objects NYI"};
|
||||||
|
|
||||||
|
std::vector<char> buf(len);
|
||||||
|
|
||||||
|
if (fseeko(file_, start_+ofs, SEEK_SET) != 0)
|
||||||
|
throw POSIXException{errno, "Could not seek"};
|
||||||
|
|
||||||
|
if (fread(buf.data(), len, 1, file_) != 1)
|
||||||
|
throw POSIXException{errno, "Could not read data"};
|
||||||
|
|
||||||
|
return File{buf};
|
||||||
|
}
|
||||||
|
|
||||||
|
TreFile::Stat TreFile::statIdx_(size_t table3Idx) const
|
||||||
|
{
|
||||||
|
Stat ret;
|
||||||
|
std::tie(std::ignore, ret.size, ret.flags) = table3_[table3Idx];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|||||||
41
TreFile.hh
41
TreFile.hh
@@ -16,6 +16,9 @@ public:
|
|||||||
|
|
||||||
TreFile(TreFile const& copy) = delete;
|
TreFile(TreFile const& copy) = delete;
|
||||||
TreFile& operator=(TreFile const& copy) = delete;
|
TreFile& operator=(TreFile const& copy) = delete;
|
||||||
|
|
||||||
|
std::vector<std::string> getNames() const;
|
||||||
|
std::vector<uint32_t> getCRCs() const;
|
||||||
|
|
||||||
void dumpName(std::string path, std::string const& name);
|
void dumpName(std::string path, std::string const& name);
|
||||||
void dumpCRC(std::string path, uint32_t crc);
|
void dumpCRC(std::string path, uint32_t crc);
|
||||||
@@ -23,15 +26,51 @@ public:
|
|||||||
|
|
||||||
void printStructure();
|
void printStructure();
|
||||||
|
|
||||||
|
class File {
|
||||||
|
public:
|
||||||
|
char const* data() const {
|
||||||
|
return data_.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const {
|
||||||
|
return data_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<char> data_;
|
||||||
|
|
||||||
|
File(std::vector<char> data)
|
||||||
|
: data_(std::move(data)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class TreFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
File openName(std::string const& name) const;
|
||||||
|
File openCRC(uint32_t crc) const;
|
||||||
|
|
||||||
|
struct Stat {
|
||||||
|
uint32_t size;
|
||||||
|
uint8_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
Stat statName(std::string const& name) const;
|
||||||
|
Stat statCRC(uint32_t crc) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void construct_();
|
void construct_();
|
||||||
|
|
||||||
|
size_t findName_(std::string const& name) const;
|
||||||
|
size_t findCRC_(uint32_t crc) const;
|
||||||
|
|
||||||
void dumpIdx_(std::string const& name, size_t table3Idx);
|
void dumpIdx_(std::string const& name, size_t table3Idx);
|
||||||
|
File openIdx_(size_t table3Idx) const;
|
||||||
|
Stat statIdx_(size_t table3Idx) const;
|
||||||
|
|
||||||
std::map<uint32_t, size_t> table1_;
|
std::map<uint32_t, size_t> table1_;
|
||||||
std::map<std::string, size_t> table2_;
|
std::map<std::string, size_t> table2_;
|
||||||
std::vector<std::tuple<uint32_t, uint32_t, uint8_t> > table3_;
|
std::vector<std::tuple<uint32_t, uint32_t, uint8_t> > table3_;
|
||||||
FILE* file_;
|
mutable FILE* file_;
|
||||||
off_t start_;
|
off_t start_;
|
||||||
size_t length_;
|
size_t length_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,23 +12,25 @@
|
|||||||
|
|
||||||
#include "common.hh"
|
#include "common.hh"
|
||||||
#include "TreFile.hh"
|
#include "TreFile.hh"
|
||||||
|
#include "IffFile.hh"
|
||||||
|
|
||||||
void usage(char *argv0) {
|
void usage(char *argv0) {
|
||||||
fprintf(stderr, "Usage: %s [-sh] [-d dest] tre-file [filenames/crcs...]\n", argv0);
|
fprintf(stderr, "Usage: %s [-sh] [-d dest] tre-file [filenames/crcs...]\n", argv0);
|
||||||
fprintf(stderr, "\t-s\tPrint the tre-file's structure\n");
|
fprintf(stderr, "\t-s\tPrint the tre-file's structure\n");
|
||||||
fprintf(stderr, "\t-d dest\tDump files to dest/\n");
|
fprintf(stderr, "\t-d dest\tDump files to dest/\n");
|
||||||
fprintf(stderr, "\t\tif dilenames/crcs are supplied, dump those object, else dump all\n");
|
fprintf(stderr, "\t\tif dilenames/crcs are supplied, dump those object, else dump all\n");
|
||||||
|
fprintf(stderr, "\t-i\tAttempt to print iff-file's structures\n");
|
||||||
fprintf(stderr, "\t-h\tPrint this help\n");
|
fprintf(stderr, "\t-h\tPrint this help\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
bool printStructure = false, dumpFiles = false;
|
bool printStructure = false, dumpFiles = false, dumpIff = false;
|
||||||
std::string dumpPath, treFile;
|
std::string dumpPath, treFile;
|
||||||
std::vector<std::string> fileSpecs;
|
std::vector<std::string> fileSpecs;
|
||||||
|
|
||||||
{
|
{
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "hsd:")) != -1) {
|
while ((opt = getopt(argc, argv, "hsd:i")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
@@ -36,6 +38,9 @@ int main(int argc, char *argv[]) {
|
|||||||
case 's':
|
case 's':
|
||||||
printStructure = true;
|
printStructure = true;
|
||||||
break;
|
break;
|
||||||
|
case 'i':
|
||||||
|
dumpIff = true;
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
dumpPath = optarg;
|
dumpPath = optarg;
|
||||||
dumpFiles = true;
|
dumpFiles = true;
|
||||||
@@ -62,6 +67,36 @@ int main(int argc, char *argv[]) {
|
|||||||
if (printStructure)
|
if (printStructure)
|
||||||
file.printStructure();
|
file.printStructure();
|
||||||
|
|
||||||
|
if (dumpIff) {
|
||||||
|
for(auto name : file.getNames()) {
|
||||||
|
auto s = file.statName(name);
|
||||||
|
if (s.flags&0x80)
|
||||||
|
continue;
|
||||||
|
auto f = file.openName(name);
|
||||||
|
try {
|
||||||
|
IffFile iff{f.data(), f.size()};
|
||||||
|
printf("%s:\n", name.c_str());
|
||||||
|
iff.printStructure(1);
|
||||||
|
} catch(FormatException &ex) {
|
||||||
|
printf("%s: Not an IFF\n", name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto crc : file.getCRCs()) {
|
||||||
|
auto s = file.statCRC(crc);
|
||||||
|
if (s.flags&0x80)
|
||||||
|
continue;
|
||||||
|
auto f = file.openCRC(crc);
|
||||||
|
try {
|
||||||
|
IffFile iff{f.data(), f.size()};
|
||||||
|
printf("%.8x:\n", crc);
|
||||||
|
iff.printStructure(1);
|
||||||
|
} catch(FormatException &ex) {
|
||||||
|
printf("%.8x: Not an IFF\n", crc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dumpFiles) {
|
if (dumpFiles) {
|
||||||
if (fileSpecs.empty())
|
if (fileSpecs.empty())
|
||||||
file.dumpAll(dumpPath);
|
file.dumpAll(dumpPath);
|
||||||
@@ -83,7 +118,7 @@ int main(int argc, char *argv[]) {
|
|||||||
} catch (POSIXException &ex) {
|
} catch (POSIXException &ex) {
|
||||||
fprintf(stderr, "%s\n", ex.toString().c_str());
|
fprintf(stderr, "%s\n", ex.toString().c_str());
|
||||||
return 2;
|
return 2;
|
||||||
} catch (FormatException &ex) {
|
} catch (Exception &ex) {
|
||||||
fprintf(stderr, "%s\n", ex.toString().c_str());
|
fprintf(stderr, "%s\n", ex.toString().c_str());
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user