Esmtool can now clone an esm file in such a way that it can read it back again afterwards.

actorid
Alexander "Ace" Olofsson 13 years ago
parent 0fd48c4229
commit fc8c75ab89

@ -22,6 +22,8 @@ struct ESMData
{
std::string author;
std::string description;
int version;
int type;
ESMReader::MasterList masters;
std::list<Record*> 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;i<m.size();i++)
cout << " " << m[i].name << ", " << m[i].size << " bytes\n";
if (!quiet)
{
cout << "Author: " << esm.getAuthor() << endl
<< "Description: " << esm.getDesc() << endl
<< "File format version: " << esm.getFVer() << endl
<< "Special flag: " << esm.getSpecial() << endl;
ESMReader::MasterList m = esm.getMasters();
if (!m.empty())
{
cout << "Masters:" << endl;
for(unsigned int i=0;i<m.size();i++)
cout << " " << m[i].name << ", " << m[i].size << " bytes" << endl;
}
}
// Loop through all records
while(esm.hasMoreRecs())
@ -512,6 +522,7 @@ int load(Arguments& info)
cout << " Name: " << l.name << endl
<< " Weight: " << l.data.weight << endl
<< " Value: " << l.data.value << endl;
break;
}
case REC_LOCK:
{
@ -697,6 +708,8 @@ int load(Arguments& info)
if (rec != NULL)
{
rec->setId(id);
if (save)
info.data.records.push_back(rec);
else
@ -720,6 +733,7 @@ int load(Arguments& info)
}
#include <map>
#include <iomanip>
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<std::string, int> records;
for (std::list<Record*>::iterator it = info.data.records.begin(); it != info.data.records.end();)
for (std::list<Record*>::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<std::string,int>::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<Record*>::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;
}

@ -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; }

@ -2,15 +2,27 @@
#include <fstream>
#include <cstring>
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<int>(0);
writeT<int>(0);
startRecord("TES3", 0);
m_header.records = 0;
writeHNT("HEDR", m_header, 300);
endRecord();
for (std::list<MasterData>::iterator it = m_masters.begin(); it != m_masters.end(); ++it)
{
writeHNString("MAST", it->name);
writeHNT("DATA", it->size);
}
// TODO: Saving
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<int>(0); // Size goes here
writeT<int>(0); // Unused header?
writeT(flags);
m_records.push_back(rec);
writeT<int>(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<int>(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<int>(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<RecordData>::iterator it = m_records.begin(); it != m_records.end(); ++it)
it->size += size;
}
m_stream->write(data, size);
}

@ -2,7 +2,7 @@
#define _ESM_WRITER_H
#include <iostream>
#include <vector>
#include <list>
#include <assert.h>
#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<typename T>
void writeHNT(const std::string& name, const T& data)
{
writeName(name);
startSubRecord(name);
writeT(data);
endRecord(name);
}
template<typename T>
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<typename T>
/*template<typename T>
void writeHT(const T& data)
{
writeT((unsigned int)sizeof(T));
@ -61,7 +68,7 @@ public:
{
assert(sizeof(T) == size);
writeHT(data);
}
}*/
template<typename T>
void writeT(const T& data)
@ -72,18 +79,19 @@ public:
template<typename T>
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<RecordData> m_records;
std::list<MasterData> m_masters;
std::list<RecordData> m_records;
std::ostream* m_stream;
HEDRstruct m_header;

@ -19,7 +19,7 @@ void PartReferenceList::save(ESMWriter &esm)
{
for (std::vector<PartReference>::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);
}

@ -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]);
}

@ -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);
}

@ -38,7 +38,7 @@ void LeveledListBase::save(ESMWriter &esm)
for (std::vector<LevelItem>::iterator it = list.begin(); it != list.end(); ++it)
{
esm.writeHNString("INAM", it->id);
esm.writeHNString(recName, it->id);
esm.writeHNT("INTV", it->level);
}
}

@ -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);
}

@ -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<Edge>::iterator it = edges.begin(); it != edges.end(); ++it)
{
esm.writeT(it->v1);
}
esm.endRecord("PGRC");
}
}

@ -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);

@ -44,15 +44,17 @@ void Script::save(ESMWriter &esm)
if (!varNames.empty())
{
esm.writeName("SCVR");
esm.startSubRecord("SCVR");
for (std::vector<std::string>::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);
}
}

@ -1,6 +1,8 @@
#ifndef _ESM_RECORD_H
#define _ESM_RECORD_H
#include <string>
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;
};
}

Loading…
Cancel
Save