From f16a9ce5ed26bfad1cfe55e480abeb56bd970113 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Thu, 12 Apr 2012 14:00:58 +0200 Subject: [PATCH] Working on making output identical to input. --- apps/esmtool/esmtool.cpp | 146 +++++++++++++++++++++++----------- components/esm/esm_writer.cpp | 23 ++++++ components/esm/esm_writer.hpp | 25 +++--- components/esm/loadclas.cpp | 2 +- components/esm/loadfact.cpp | 7 +- components/esm/loadrace.cpp | 2 +- components/esm/loadscpt.cpp | 15 ++-- 7 files changed, 150 insertions(+), 70 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 06348e949..002758bed 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -19,13 +19,13 @@ namespace bpo = boost::program_options; struct ESMData { std::string author; - std::string description; + string description; int version; int type; ESMReader::MasterList masters; - std::list records; - std::map > cellRefs; + list records; + map > cellRefs; }; // Based on the legacy struct @@ -35,17 +35,17 @@ struct Arguments unsigned int quiet_given; unsigned int loadcells_given; - std::string mode; - std::string encoding; - std::string filename; - std::string outname; + string mode; + string encoding; + string filename; + string outname; ESMData data; }; bool parseOptions (int argc, char** argv, Arguments &info) { - bpo::options_description desc("Inspect and extract from Morrowind ES files (ESM, ESP, ESS)\nSyntax: esmtool [options] mode infile [outfile]\nAllowed modes:\n dump\t Dumps all readable data from the input file\n clone\t Clones the input file to the output file.\n\nAllowed options"); + bpo::options_description desc("Inspect and extract from Morrowind ES files (ESM, ESP, ESS)\nSyntax: esmtool [options] mode infile [outfile]\nAllowed modes:\n dump\t Dumps all readable data from the input file.\n clone\t Clones the input file to the output file.\n comp\t Compares the given files.\n\nAllowed options"); desc.add_options() ("help,h", "print help message.") @@ -54,7 +54,7 @@ bool parseOptions (int argc, char** argv, Arguments &info) ("quiet,q", "Supress all record information. Useful for speed tests.") ("loadcells,C", "Browse through contents of all cells.") - ( "encoding,e", bpo::value(&(info.encoding))-> + ( "encoding,e", bpo::value(&(info.encoding))-> default_value("win1252"), "Character encoding used in ESMTool:\n" "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" @@ -62,14 +62,14 @@ bool parseOptions (int argc, char** argv, Arguments &info) "\n\twin1252 - Western European (Latin) alphabet, used by default") ; - std::string finalText = "\nIf no option is given, the default action is to parse all records in the archive\nand display diagnostic information."; + string finalText = "\nIf no option is given, the default action is to parse all records in the archive\nand display diagnostic information."; // input-file is hidden and used as a positional argument bpo::options_description hidden("Hidden Options"); hidden.add_options() - ( "mode,m", bpo::value(), "esmtool mode") - ( "input-file,i", bpo::value< vector >(), "input file") + ( "mode,m", bpo::value(), "esmtool mode") + ( "input-file,i", bpo::value< vector >(), "input file") ; bpo::positional_options_description p; @@ -87,70 +87,70 @@ bool parseOptions (int argc, char** argv, Arguments &info) if (variables.count ("help")) { - std::cout << desc << finalText << std::endl; + cout << desc << finalText << endl; return false; } if (variables.count ("version")) { - std::cout << "ESMTool version " << ESMTOOL_VERSION << std::endl; + cout << "ESMTool version " << ESMTOOL_VERSION << endl; return false; } if (!variables.count("mode")) { - std::cout << "No mode specified!" << std::endl << std::endl - << desc << finalText << std::endl; + cout << "No mode specified!" << endl << endl + << desc << finalText << endl; return false; } - info.mode = variables["mode"].as(); - if (!(info.mode == "dump" || info.mode == "clone")) + info.mode = variables["mode"].as(); + if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp")) { - std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"" << std::endl << std::endl - << desc << finalText << std::endl; + cout << endl << "ERROR: invalid mode \"" << info.mode << "\"" << endl << endl + << desc << finalText << endl; return false; } if ( !variables.count("input-file") ) { - std::cout << "\nERROR: missing ES file\n\n"; - std::cout << desc << finalText << std::endl; + cout << "\nERROR: missing ES file\n\n"; + cout << desc << finalText << endl; return false; } // handling gracefully the user adding multiple files -/* if (variables["input-file"].as< vector >().size() > 1) +/* if (variables["input-file"].as< vector >().size() > 1) { - std::cout << "\nERROR: more than one ES file specified\n\n"; - std::cout << desc << finalText << std::endl; + cout << "\nERROR: more than one ES file specified\n\n"; + cout << desc << finalText << endl; return false; }*/ - info.filename = variables["input-file"].as< vector >()[0]; - if (variables["input-file"].as< vector >().size() > 1) - info.outname = variables["input-file"].as< vector >()[1]; + info.filename = variables["input-file"].as< vector >()[0]; + if (variables["input-file"].as< vector >().size() > 1) + info.outname = variables["input-file"].as< vector >()[1]; info.raw_given = variables.count ("raw"); info.quiet_given = variables.count ("quiet"); info.loadcells_given = variables.count ("loadcells"); // Font encoding settings - info.encoding = variables["encoding"].as(); + info.encoding = variables["encoding"].as(); if (info.encoding == "win1250") { - std::cout << "Using Central and Eastern European font encoding." << std::endl; + cout << "Using Central and Eastern European font encoding." << endl; } else if (info.encoding == "win1251") { - std::cout << "Using Cyrillic font encoding." << std::endl; + cout << "Using Cyrillic font encoding." << endl; } else { if(info.encoding != "win1252") { - std::cout << info.encoding << " is not a valid encoding option." << std::endl; + cout << info.encoding << " is not a valid encoding option." << endl; info.encoding = "win1252"; } - std::cout << "Using default (English) font encoding." << std::endl; + cout << "Using default (English) font encoding." << endl; } return true; @@ -161,6 +161,7 @@ void loadCell(Cell &cell, ESMReader &esm, Arguments& info); int load(Arguments& info); int clone(Arguments& info); +int comp(Arguments& info); int main(int argc, char**argv) { @@ -172,6 +173,8 @@ int main(int argc, char**argv) return load(info); else if (info.mode == "clone") return clone(info); + else if (info.mode == "comp") + return comp(info); else { cout << "Invalid or no mode specified, dying horribly. Have a nice day." << endl; @@ -231,9 +234,9 @@ int load(Arguments& info) esm.setEncoding(info.encoding); string filename = info.filename; - cout << "\nFile: " << filename << endl; + cout << "Loading file: " << filename << endl; - std::list skipped; + list skipped; try { @@ -734,7 +737,7 @@ int load(Arguments& info) { cout << "\nERROR:\n\n " << e.what() << endl; - for (std::list::iterator it = info.data.records.begin(); it != info.data.records.end();) + for (list::iterator it = info.data.records.begin(); it != info.data.records.end();) { delete *it; info.data.records.erase(it++); @@ -773,9 +776,9 @@ int clone(Arguments& info) cout << "Loaded " << recordCount << " records:" << endl << endl; - std::map records; + map records; - for (std::list::iterator it = info.data.records.begin(); it != info.data.records.end(); ++it) + for (list::iterator it = info.data.records.begin(); it != info.data.records.end(); ++it) { Record* rec = *it; NAME n; @@ -784,9 +787,9 @@ int clone(Arguments& info) } int i = 0; - for (std::map::iterator it = records.begin(); it != records.end(); ++it) + for (map::iterator it = records.begin(); it != records.end(); ++it) { - std::string n = it->first; + string n = it->first; float amount = it->second; cout << setw(digitCount) << amount << " " << n << " "; @@ -808,11 +811,11 @@ int clone(Arguments& info) for (ESMReader::MasterList::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it) esm.addMaster(it->name, it->size); - std::fstream save(info.outname.c_str(), std::fstream::out | std::fstream::binary); + fstream save(info.outname.c_str(), fstream::out | fstream::binary); esm.save(save); int saved = 0; - for (std::list::iterator it = info.data.records.begin(); it != info.data.records.end() && i > 0; ++it) + for (list::iterator it = info.data.records.begin(); it != info.data.records.end() && i > 0; ++it) { Record* rec = *it; @@ -820,14 +823,18 @@ int clone(Arguments& info) n.val = rec->getName(); esm.startRecord(n.toString(), 0); - std::string id = rec->getId(); - esm.writeHNOString("NAME", id); + string id = rec->getId(); + + if (n.val == REC_GLOB || n.val == REC_CLAS || n.val == REC_FACT || n.val == REC_RACE) + esm.writeHNCString("NAME", id); + else + esm.writeHNOString("NAME", id); rec->save(esm); if (n.val == REC_CELL && !info.data.cellRefs[rec].empty()) { - std::list& refs = info.data.cellRefs[rec]; - for (std::list::iterator it = refs.begin(); it != refs.end(); ++it) + list& refs = info.data.cellRefs[rec]; + for (list::iterator it = refs.begin(); it != refs.end(); ++it) { it->save(esm); } @@ -848,5 +855,52 @@ int clone(Arguments& info) esm.close(); save.close(); + return 0; +} + +int comp(Arguments& info) +{ + if (info.filename.empty() || info.outname.empty()) + { + cout << "You need to specify two input files" << endl; + return 1; + } + + Arguments fileOne; + Arguments fileTwo; + + fileOne.raw_given = 0; + fileTwo.raw_given = 0; + + fileOne.mode = "clone"; + fileTwo.mode = "clone"; + + fileOne.encoding = info.encoding; + fileTwo.encoding = info.encoding; + + fileOne.filename = info.filename; + fileTwo.filename = info.outname; + + if (load(fileOne) != 0) + { + cout << "Failed to load " << info.filename << ", aborting comparison." << endl; + return 1; + } + + if (load(fileTwo) != 0) + { + cout << "Failed to load " << info.outname << ", aborting comparison." << endl; + return 1; + } + + if (fileOne.data.records.size() != fileTwo.data.records.size()) + { + cout << "Not equal, different amount of records." << endl; + return 1; + } + + + + return 0; } diff --git a/components/esm/esm_writer.cpp b/components/esm/esm_writer.cpp index 14e28f6ba..4e0a6f460 100644 --- a/components/esm/esm_writer.cpp +++ b/components/esm/esm_writer.cpp @@ -53,12 +53,14 @@ void ESMWriter::save(const std::string& file) void ESMWriter::save(std::ostream& file) { + m_recordCount = 0; m_stream = &file; startRecord("TES3", 0); m_header.records = 0; writeHNT("HEDR", m_header, 300); + m_headerPos = m_stream->tellp() - (std::streampos)4; for (std::list::iterator it = m_masters.begin(); it != m_masters.end(); ++it) { @@ -71,6 +73,10 @@ void ESMWriter::save(std::ostream& file) void ESMWriter::close() { + std::cout << "Writing amount of saved records (" << m_recordCount - 1 << ")" << std::endl; + m_stream->seekp(m_headerPos); + writeT(m_recordCount-1); + m_stream->seekp(0, std::ios::end); m_stream->flush(); if (!m_records.empty()) @@ -79,6 +85,8 @@ void ESMWriter::close() void ESMWriter::startRecord(const std::string& name, uint32_t flags) { + m_recordCount++; + writeName(name); RecordData rec; rec.name = name; @@ -128,6 +136,21 @@ void ESMWriter::writeHNString(const std::string& name, const std::string& data) endRecord(name); } + void ESMWriter::writeHNString(const std::string& name, const std::string& data, int size) +{ + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (int i = data.size(); i < size; ++i) + write("\0",1); + } + + endRecord(name); +} + void ESMWriter::writeHString(const std::string& data) { if (data.size() == 0) diff --git a/components/esm/esm_writer.hpp b/components/esm/esm_writer.hpp index 4439693c4..337af9e5e 100644 --- a/components/esm/esm_writer.hpp +++ b/components/esm/esm_writer.hpp @@ -34,6 +34,15 @@ public: void close(); void writeHNString(const std::string& name, const std::string& data); + void writeHNString(const std::string& name, const std::string& data, int size); + void writeHNCString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + endRecord(name); + } void writeHNOString(const std::string& name, const std::string& data) { if (!data.empty()) @@ -56,20 +65,6 @@ public: endRecord(name); } - /*template - void writeHT(const T& data) - { - writeT((unsigned int)sizeof(T)); - writeT(data); - } - - template - void writeHT(const T& data, int size) - { - assert(sizeof(T) == size); - writeHT(data); - }*/ - template void writeT(const T& data) { @@ -93,6 +88,8 @@ private: std::list m_masters; std::list m_records; std::ostream* m_stream; + std::streampos m_headerPos; + int m_recordCount; HEDRstruct m_header; SaveData m_saveData; diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index e13442dcf..6e6de4e71 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -27,7 +27,7 @@ void Class::load(ESMReader &esm) } void Class::save(ESMWriter &esm) { - esm.writeHNString("FNAM", name); + esm.writeHNCString("FNAM", name); esm.writeHNT("CLDT", data, 60); esm.writeHNOString("DESC", description); } diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index 2a927ad28..1a151cbe1 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -29,11 +29,14 @@ void Faction::load(ESMReader &esm) } void Faction::save(ESMWriter &esm) { - esm.writeHNString("FNAM", name); + esm.writeHNCString("FNAM", name); for (int i = 0; i < 10; i++) { - esm.writeHNString("RNAM", ranks[i]); + if (ranks[i].empty()) + break; + + esm.writeHNString("RNAM", ranks[i], 32); } esm.writeHNT("FADT", data, 240); diff --git a/components/esm/loadrace.cpp b/components/esm/loadrace.cpp index 04752add1..8c82bd38a 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -12,7 +12,7 @@ void Race::load(ESMReader &esm) } void Race::save(ESMWriter &esm) { - esm.writeHNString("FNAM", name); + esm.writeHNCString("FNAM", name); esm.writeHNT("RADT", data, 140); powers.save(esm); esm.writeHNOString("DESC", description); diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index ecb58cc31..658f97a49 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -40,19 +40,22 @@ void Script::load(ESMReader &esm) } void Script::save(ESMWriter &esm) { - esm.writeHNT("SCHD", data, 52); - + std::string varNameString; if (!varNames.empty()) { - esm.startSubRecord("SCVR"); for (std::vector::iterator it = varNames.begin(); it != varNames.end(); ++it) { - esm.writeT(it->c_str(), it->size()); - esm.writeT('\0'); + varNameString.append(*it); + //varNameString.append("\0"); } - esm.endRecord("SCVR"); + + data.stringTableSize = varNameString.size(); } + esm.writeHNT("SCHD", data, 52); + + esm.writeHNOString("SCVR", varNameString); + esm.writeHNString("SCDT", std::string(&scriptData[0], scriptData.size())); esm.writeHNOString("SCTX", scriptText); }