From fc8c75ab892aa51478c3bc7fce728707c3e0cb12 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sun, 8 Apr 2012 17:04:52 +0200 Subject: [PATCH] Esmtool can now clone an esm file in such a way that it can read it back again afterwards. --- apps/esmtool/esmtool.cpp | 102 +++++++++++++++++++++++++++------ components/esm/esm_reader.hpp | 1 + components/esm/esm_writer.cpp | 99 +++++++++++++++++++++++++------- components/esm/esm_writer.hpp | 36 +++++++----- components/esm/loadarmo.cpp | 2 +- components/esm/loadfact.cpp | 2 +- components/esm/loadland.cpp | 5 +- components/esm/loadlevlist.cpp | 2 +- components/esm/loadlocks.cpp | 11 ++-- components/esm/loadpgrd.cpp | 6 +- components/esm/loadregn.cpp | 5 +- components/esm/loadscpt.cpp | 8 ++- components/esm/record.hpp | 8 +++ 13 files changed, 220 insertions(+), 67 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index ea9a388a52..97f1aa2a5b 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -22,6 +22,8 @@ struct ESMData { std::string author; std::string description; + int version; + int type; ESMReader::MasterList masters; std::list records; @@ -221,7 +223,7 @@ int load(Arguments& info) try { - if(info.raw_given) + if(info.raw_given && info.mode == "dump") { cout << "RAW file listing:\n"; @@ -233,7 +235,7 @@ int load(Arguments& info) } bool quiet = (info.quiet_given || info.mode == "clone"); - bool loadCells = (info.loadcells_given || info.mode == "clone"); + bool loadCells = (info.loadcells_given);// || info.mode == "clone"); bool save = (info.mode == "clone"); esm.open(filename); @@ -241,15 +243,23 @@ int load(Arguments& info) info.data.author = esm.getAuthor(); info.data.description = esm.getDesc(); info.data.masters = esm.getMasters(); + info.data.version = esm.getVer(); + info.data.type = esm.getType(); - cout << "Author: " << esm.getAuthor() << endl; - cout << "Description: " << esm.getDesc() << endl; - cout << "File format version: " << esm.getFVer() << endl; - cout << "Special flag: " << esm.getSpecial() << endl; - cout << "Masters:\n"; - ESMReader::MasterList m = esm.getMasters(); - for(unsigned int i=0;isetId(id); + if (save) info.data.records.push_back(rec); else @@ -720,6 +733,7 @@ int load(Arguments& info) } #include +#include int clone(Arguments& info) { @@ -735,26 +749,82 @@ int clone(Arguments& info) return 1; } - cout << "Loaded " << info.data.records.size() << " records:" << endl; + int recordCount = info.data.records.size(); + + int digitCount = 1; // For a nicer output + if (recordCount > 9) ++digitCount; + if (recordCount > 99) ++digitCount; + if (recordCount > 999) ++digitCount; + if (recordCount > 9999) ++digitCount; + if (recordCount > 99999) ++digitCount; + if (recordCount > 999999) ++digitCount; + + cout << "Loaded " << recordCount << " records:" << endl << endl; std::map records; - for (std::list::iterator it = info.data.records.begin(); it != info.data.records.end();) + for (std::list::iterator it = info.data.records.begin(); it != info.data.records.end(); ++it) { Record* rec = *it; NAME n; n.val = rec->getName(); records[n.toString()]++; - - delete rec; - info.data.records.erase(it++); } + int i = 0; for (std::map::iterator it = records.begin(); it != records.end(); ++it) { std::string n = it->first; - cout << n << ": " << it->second << " records." << endl; + float amount = it->second; + cout << setw(digitCount) << amount << " " << n << " "; + + if (++i % 3 == 0) + cout << endl; } + + if (i % 3 != 0) + cout << endl; + + cout << endl << "Saving records to: " << info.outname << "..." << endl; + + ESMWriter esm; + esm.setAuthor(info.data.author); + esm.setDescription(info.data.description); + esm.setVersion(info.data.version); + esm.setType(info.data.type); + + 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); + esm.save(save); + + int saved = 0; + for (std::list::iterator it = info.data.records.begin(); it != info.data.records.end() && i > 0; ++it) + { + Record* rec = *it; + + NAME n; + n.val = rec->getName(); + + esm.startRecord(n.toString(), 0); + std::string id = rec->getId(); + esm.writeHNOString("NAME", id); + rec->save(esm); + esm.endRecord(n.toString()); + + saved++; + int perc = ((int)saved / (float)recordCount)*100; + if (perc % 10 == 0) + { + cerr << "\r" << perc << "%"; + } + } + + cout << "\rDone!" << endl; + + esm.close(); + save.close(); return 0; } diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index 6e5592cf7b..3e69aa5281 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -61,6 +61,7 @@ public: int getVer() { return mCtx.header.version; } float getFVer() { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } int getSpecial() { return mSpf; } + int getType() { return mCtx.header.type; } const std::string getAuthor() { return mCtx.header.author.toString(); } const std::string getDesc() { return mCtx.header.desc.toString(); } const SaveData &getSaveData() { return mSaveData; } diff --git a/components/esm/esm_writer.cpp b/components/esm/esm_writer.cpp index 57517b0f53..14e28f6ba9 100644 --- a/components/esm/esm_writer.cpp +++ b/components/esm/esm_writer.cpp @@ -2,15 +2,27 @@ #include #include +bool count = true; + namespace ESM { -void ESMWriter::setVersion(Version ver) +int ESMWriter::getVersion() +{ + return m_header.version; +} + +void ESMWriter::setVersion(int ver) { m_header.version = ver; } -void ESMWriter::setType(FileType type) +int ESMWriter::getType() +{ + return m_header.type; +} + +void ESMWriter::setType(int type) { m_header.type = type; } @@ -25,75 +37,118 @@ void ESMWriter::setDescription(const std::string& desc) strncpy((char*)&m_header.desc, desc.c_str(), 256); } +void ESMWriter::addMaster(const std::string& name, uint64_t size) +{ + MasterData d; + d.name = name; + d.size = size; + m_masters.push_back(d); +} + void ESMWriter::save(const std::string& file) { std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); save(fs); - fs.close(); } void ESMWriter::save(std::ostream& file) { m_stream = &file; - startRecord("TES3"); - writeT(0); - writeT(0); + startRecord("TES3", 0); - endRecord(); + m_header.records = 0; + writeHNT("HEDR", m_header, 300); - // TODO: Saving + for (std::list::iterator it = m_masters.begin(); it != m_masters.end(); ++it) + { + writeHNString("MAST", it->name); + writeHNT("DATA", it->size); + } + + endRecord("TES3"); } void ESMWriter::close() { - // TODO: Saving + m_stream->flush(); + + if (!m_records.empty()) + throw "Unclosed record remaining"; } -void ESMWriter::startRecord(const std::string& name) +void ESMWriter::startRecord(const std::string& name, uint32_t flags) { writeName(name); RecordData rec; + rec.name = name; rec.position = m_stream->tellp(); rec.size = 0; + writeT(0); // Size goes here + writeT(0); // Unused header? + writeT(flags); m_records.push_back(rec); - writeT(0); + + assert(m_records.back().size == 0); } -void ESMWriter::endRecord() +void ESMWriter::startSubRecord(const std::string& name) +{ + writeName(name); + RecordData rec; + rec.name = name; + rec.position = m_stream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + m_records.push_back(rec); + + assert(m_records.back().size == 0); +} + +void ESMWriter::endRecord(const std::string& name) { - std::streampos cur = m_stream->tellp(); RecordData rec = m_records.back(); + assert(rec.name == name); m_records.pop_back(); m_stream->seekp(rec.position); - m_stream->write((char*)&rec.size, sizeof(int)); - m_stream->seekp(cur); + count = false; + write((char*)&rec.size, sizeof(int)); + count = true; + + m_stream->seekp(0, std::ios::end); + } void ESMWriter::writeHNString(const std::string& name, const std::string& data) { - writeName(name); + startSubRecord(name); writeHString(data); + endRecord(name); } void ESMWriter::writeHString(const std::string& data) { - writeT(data.size()-1); - write(data.c_str(), data.size()-1); + if (data.size() == 0) + write("\0", 1); + else + write(data.c_str(), data.size()); } void ESMWriter::writeName(const std::string& name) { - assert((name.size() == 4 && name[3] != '\0') || (name.size() == 5 && name[4] == '\0')); - write(name.c_str(), name.size()-1); + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); } void ESMWriter::write(const char* data, int size) { - if (!m_records.empty()) - m_records.back().size += size; + if (count && !m_records.empty()) + { + for (std::list::iterator it = m_records.begin(); it != m_records.end(); ++it) + it->size += size; + } m_stream->write(data, size); } diff --git a/components/esm/esm_writer.hpp b/components/esm/esm_writer.hpp index 3999e987d2..4439693c4d 100644 --- a/components/esm/esm_writer.hpp +++ b/components/esm/esm_writer.hpp @@ -2,7 +2,7 @@ #define _ESM_WRITER_H #include -#include +#include #include #include "esm_common.hpp" @@ -13,17 +13,22 @@ class ESMWriter { struct RecordData { + std::string name; std::streampos position; int size; }; public: - void setVersion(Version ver); - void setType(FileType type); - + int getVersion(); + void setVersion(int ver); + int getType(); + void setType(int type); +// void setEncoding(const std::string& encoding); // Write strings as UTF-8? void setAuthor(const std::string& author); void setDescription(const std::string& desc); + void addMaster(const std::string& name, uint64_t size); + void save(const std::string& file); void save(std::ostream& file); void close(); @@ -38,18 +43,20 @@ public: template void writeHNT(const std::string& name, const T& data) { - writeName(name); + startSubRecord(name); writeT(data); + endRecord(name); } template void writeHNT(const std::string& name, const T& data, int size) { - assert(sizeof(T) == size); - writeHNT(name, data); + startSubRecord(name); + writeT(data, size); + endRecord(name); } - template + /*template void writeHT(const T& data) { writeT((unsigned int)sizeof(T)); @@ -61,7 +68,7 @@ public: { assert(sizeof(T) == size); writeHT(data); - } + }*/ template void writeT(const T& data) @@ -72,18 +79,19 @@ public: template void writeT(const T& data, int size) { - assert(sizeof(T) == size); - writeT(data); + write((char*)&data, size); } - void startRecord(const std::string& name); - void endRecord(); + void startRecord(const std::string& name, uint32_t flags); + void startSubRecord(const std::string& name); + void endRecord(const std::string& name); void writeHString(const std::string& data); void writeName(const std::string& data); void write(const char* data, int size); private: - std::vector m_records; + std::list m_masters; + std::list m_records; std::ostream* m_stream; HEDRstruct m_header; diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index aecfc7e922..ca5865083f 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -19,7 +19,7 @@ void PartReferenceList::save(ESMWriter &esm) { for (std::vector::iterator it = parts.begin(); it != parts.end(); ++it) { - esm.writeHT(it->part); + esm.writeHNT("INDX", it->part); esm.writeHNOString("BNAM", it->male); esm.writeHNOString("CNAM", it->female); } diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index fdbedcbd01..2a927ad28c 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -31,7 +31,7 @@ void Faction::save(ESMWriter &esm) { esm.writeHNString("FNAM", name); - for (int i = 0; !ranks[i].empty(); i++) + for (int i = 0; i < 10; i++) { esm.writeHNString("RNAM", ranks[i]); } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 9aa02e4781..222772041a 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -84,15 +84,18 @@ void Land::load(ESMReader &esm) void Land::save(ESMWriter &esm) { - esm.writeName("INTV"); + esm.startSubRecord("INTV"); esm.writeT(X); esm.writeT(Y); + esm.endRecord("INTV"); esm.writeHNT("DATA", flags); + /* TODO: Land! if (hasData && !dataLoaded) loadData(); // I think it might be a good idea to have // the data loaded before trying to save it + */ if (dataLoaded) landData->save(esm); } diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index 811964775b..f63a720256 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -38,7 +38,7 @@ void LeveledListBase::save(ESMWriter &esm) for (std::vector::iterator it = list.begin(); it != list.end(); ++it) { - esm.writeHNString("INAM", it->id); + esm.writeHNString(recName, it->id); esm.writeHNT("INTV", it->level); } } diff --git a/components/esm/loadlocks.cpp b/components/esm/loadlocks.cpp index f49de1bc8f..b3d53f4588 100644 --- a/components/esm/loadlocks.cpp +++ b/components/esm/loadlocks.cpp @@ -36,14 +36,15 @@ void Tool::save(ESMWriter &esm) esm.writeHNString("MODL", model); esm.writeHNString("FNAM", name); + std::string typeName; switch(type) { - case Type_Repair: esm.writeName("RIDT"); break; - case Type_Pick: esm.writeName("LKDT"); break; - case Type_Probe: esm.writeName("PBDT"); break; + case Type_Repair: typeName = "RIDT"; break; + case Type_Pick: typeName = "LKDT"; break; + case Type_Probe: typeName = "PBDT"; break; } - - esm.writeT(data, 16); + + esm.writeHNT(typeName, data, 16); esm.writeHNOString("SCRI", script); esm.writeHNOString("ITEX", icon); } diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index 41dd341748..ebdb6e8371 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -74,20 +74,22 @@ void Pathgrid::save(ESMWriter &esm) if (!points.empty()) { - esm.writeName("PGRP"); + esm.startSubRecord("PGRP"); for (PointList::iterator it = points.begin(); it != points.end(); ++it) { esm.writeT(*it); } + esm.endRecord("PGRP"); } if (!edges.empty()) { - esm.writeName("PGRC"); + esm.startSubRecord("PGRC"); for (std::vector::iterator it = edges.begin(); it != edges.end(); ++it) { esm.writeT(it->v1); } + esm.endRecord("PGRC"); } } diff --git a/components/esm/loadregn.cpp b/components/esm/loadregn.cpp index d67922b4d9..f9e4a151e7 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -29,7 +29,10 @@ void Region::save(ESMWriter &esm) { esm.writeHNString("FNAM", name); - esm.writeHNT("WEAT", data); + if (esm.getVersion() == VER_12) + esm.writeHNT("WEAT", data, sizeof(data) - 2); + else + esm.writeHNT("WEAT", data); esm.writeHNOString("BNAM", sleepList); diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 291259bc08..ecb58cc31e 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -44,15 +44,17 @@ void Script::save(ESMWriter &esm) if (!varNames.empty()) { - esm.writeName("SCVR"); + esm.startSubRecord("SCVR"); for (std::vector::iterator it = varNames.begin(); it != varNames.end(); ++it) { esm.writeT(it->c_str(), it->size()); + esm.writeT('\0'); } + esm.endRecord("SCVR"); } - esm.writeHNT("SCDT", &scriptData[0], scriptData.size()); - esm.writeHNOString("SCDT", scriptText); + esm.writeHNString("SCDT", std::string(&scriptData[0], scriptData.size())); + esm.writeHNOString("SCTX", scriptText); } } diff --git a/components/esm/record.hpp b/components/esm/record.hpp index 666a6c15b9..d273b175ab 100644 --- a/components/esm/record.hpp +++ b/components/esm/record.hpp @@ -1,6 +1,8 @@ #ifndef _ESM_RECORD_H #define _ESM_RECORD_H +#include + namespace ESM { @@ -62,7 +64,13 @@ public: virtual void load(ESMReader& esm) = 0; virtual void save(ESMWriter& esm) = 0; + std::string getId() { return m_id; } + void setId(const std::string& in) { m_id = in; } + virtual int getName() = 0; + +protected: + std::string m_id; }; }