mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 18:29:55 +00:00
Esmtool can now clone an esm file in such a way that it can read it back again afterwards.
This commit is contained in:
parent
0fd48c4229
commit
fc8c75ab89
13 changed files with 220 additions and 67 deletions
|
@ -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);
|
||||
|
||||
endRecord();
|
||||
m_header.records = 0;
|
||||
writeHNT("HEDR", m_header, 300);
|
||||
|
||||
// TODO: Saving
|
||||
for (std::list<MasterData>::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<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…
Reference in a new issue