1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-21 10:53:51 +00:00

Working on making output identical to input.

This commit is contained in:
Alexander "Ace" Olofsson 2012-04-12 14:00:58 +02:00
parent 290d09de64
commit f16a9ce5ed
7 changed files with 150 additions and 70 deletions

View file

@ -19,13 +19,13 @@ namespace bpo = boost::program_options;
struct ESMData struct ESMData
{ {
std::string author; std::string author;
std::string description; string description;
int version; int version;
int type; int type;
ESMReader::MasterList masters; ESMReader::MasterList masters;
std::list<Record*> records; list<Record*> records;
std::map<Record*, std::list<CellRef> > cellRefs; map<Record*, list<CellRef> > cellRefs;
}; };
// Based on the legacy struct // Based on the legacy struct
@ -35,17 +35,17 @@ struct Arguments
unsigned int quiet_given; unsigned int quiet_given;
unsigned int loadcells_given; unsigned int loadcells_given;
std::string mode; string mode;
std::string encoding; string encoding;
std::string filename; string filename;
std::string outname; string outname;
ESMData data; ESMData data;
}; };
bool parseOptions (int argc, char** argv, Arguments &info) 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() desc.add_options()
("help,h", "print help message.") ("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.") ("quiet,q", "Supress all record information. Useful for speed tests.")
("loadcells,C", "Browse through contents of all cells.") ("loadcells,C", "Browse through contents of all cells.")
( "encoding,e", bpo::value<std::string>(&(info.encoding))-> ( "encoding,e", bpo::value<string>(&(info.encoding))->
default_value("win1252"), default_value("win1252"),
"Character encoding used in ESMTool:\n" "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" "\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") "\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 // input-file is hidden and used as a positional argument
bpo::options_description hidden("Hidden Options"); bpo::options_description hidden("Hidden Options");
hidden.add_options() hidden.add_options()
( "mode,m", bpo::value<std::string>(), "esmtool mode") ( "mode,m", bpo::value<string>(), "esmtool mode")
( "input-file,i", bpo::value< vector<std::string> >(), "input file") ( "input-file,i", bpo::value< vector<string> >(), "input file")
; ;
bpo::positional_options_description p; bpo::positional_options_description p;
@ -87,70 +87,70 @@ bool parseOptions (int argc, char** argv, Arguments &info)
if (variables.count ("help")) if (variables.count ("help"))
{ {
std::cout << desc << finalText << std::endl; cout << desc << finalText << endl;
return false; return false;
} }
if (variables.count ("version")) if (variables.count ("version"))
{ {
std::cout << "ESMTool version " << ESMTOOL_VERSION << std::endl; cout << "ESMTool version " << ESMTOOL_VERSION << endl;
return false; return false;
} }
if (!variables.count("mode")) if (!variables.count("mode"))
{ {
std::cout << "No mode specified!" << std::endl << std::endl cout << "No mode specified!" << endl << endl
<< desc << finalText << std::endl; << desc << finalText << endl;
return false; return false;
} }
info.mode = variables["mode"].as<std::string>(); info.mode = variables["mode"].as<string>();
if (!(info.mode == "dump" || info.mode == "clone")) if (!(info.mode == "dump" || info.mode == "clone" || info.mode == "comp"))
{ {
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"" << std::endl << std::endl cout << endl << "ERROR: invalid mode \"" << info.mode << "\"" << endl << endl
<< desc << finalText << std::endl; << desc << finalText << endl;
return false; return false;
} }
if ( !variables.count("input-file") ) if ( !variables.count("input-file") )
{ {
std::cout << "\nERROR: missing ES file\n\n"; cout << "\nERROR: missing ES file\n\n";
std::cout << desc << finalText << std::endl; cout << desc << finalText << endl;
return false; return false;
} }
// handling gracefully the user adding multiple files // handling gracefully the user adding multiple files
/* if (variables["input-file"].as< vector<std::string> >().size() > 1) /* if (variables["input-file"].as< vector<string> >().size() > 1)
{ {
std::cout << "\nERROR: more than one ES file specified\n\n"; cout << "\nERROR: more than one ES file specified\n\n";
std::cout << desc << finalText << std::endl; cout << desc << finalText << endl;
return false; return false;
}*/ }*/
info.filename = variables["input-file"].as< vector<std::string> >()[0]; info.filename = variables["input-file"].as< vector<string> >()[0];
if (variables["input-file"].as< vector<std::string> >().size() > 1) if (variables["input-file"].as< vector<string> >().size() > 1)
info.outname = variables["input-file"].as< vector<std::string> >()[1]; info.outname = variables["input-file"].as< vector<string> >()[1];
info.raw_given = variables.count ("raw"); info.raw_given = variables.count ("raw");
info.quiet_given = variables.count ("quiet"); info.quiet_given = variables.count ("quiet");
info.loadcells_given = variables.count ("loadcells"); info.loadcells_given = variables.count ("loadcells");
// Font encoding settings // Font encoding settings
info.encoding = variables["encoding"].as<std::string>(); info.encoding = variables["encoding"].as<string>();
if (info.encoding == "win1250") 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") else if (info.encoding == "win1251")
{ {
std::cout << "Using Cyrillic font encoding." << std::endl; cout << "Using Cyrillic font encoding." << endl;
} }
else else
{ {
if(info.encoding != "win1252") 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"; info.encoding = "win1252";
} }
std::cout << "Using default (English) font encoding." << std::endl; cout << "Using default (English) font encoding." << endl;
} }
return true; return true;
@ -161,6 +161,7 @@ void loadCell(Cell &cell, ESMReader &esm, Arguments& info);
int load(Arguments& info); int load(Arguments& info);
int clone(Arguments& info); int clone(Arguments& info);
int comp(Arguments& info);
int main(int argc, char**argv) int main(int argc, char**argv)
{ {
@ -172,6 +173,8 @@ int main(int argc, char**argv)
return load(info); return load(info);
else if (info.mode == "clone") else if (info.mode == "clone")
return clone(info); return clone(info);
else if (info.mode == "comp")
return comp(info);
else else
{ {
cout << "Invalid or no mode specified, dying horribly. Have a nice day." << endl; 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); esm.setEncoding(info.encoding);
string filename = info.filename; string filename = info.filename;
cout << "\nFile: " << filename << endl; cout << "Loading file: " << filename << endl;
std::list<int> skipped; list<int> skipped;
try { try {
@ -734,7 +737,7 @@ int load(Arguments& info)
{ {
cout << "\nERROR:\n\n " << e.what() << endl; cout << "\nERROR:\n\n " << e.what() << endl;
for (std::list<Record*>::iterator it = info.data.records.begin(); it != info.data.records.end();) for (list<Record*>::iterator it = info.data.records.begin(); it != info.data.records.end();)
{ {
delete *it; delete *it;
info.data.records.erase(it++); info.data.records.erase(it++);
@ -773,9 +776,9 @@ int clone(Arguments& info)
cout << "Loaded " << recordCount << " records:" << endl << endl; cout << "Loaded " << recordCount << " records:" << endl << endl;
std::map<std::string, int> records; map<string, int> records;
for (std::list<Record*>::iterator it = info.data.records.begin(); it != info.data.records.end(); ++it) for (list<Record*>::iterator it = info.data.records.begin(); it != info.data.records.end(); ++it)
{ {
Record* rec = *it; Record* rec = *it;
NAME n; NAME n;
@ -784,9 +787,9 @@ int clone(Arguments& info)
} }
int i = 0; int i = 0;
for (std::map<std::string,int>::iterator it = records.begin(); it != records.end(); ++it) for (map<string,int>::iterator it = records.begin(); it != records.end(); ++it)
{ {
std::string n = it->first; string n = it->first;
float amount = it->second; float amount = it->second;
cout << setw(digitCount) << amount << " " << n << " "; 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) for (ESMReader::MasterList::iterator it = info.data.masters.begin(); it != info.data.masters.end(); ++it)
esm.addMaster(it->name, it->size); 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); esm.save(save);
int saved = 0; int saved = 0;
for (std::list<Record*>::iterator it = info.data.records.begin(); it != info.data.records.end() && i > 0; ++it) for (list<Record*>::iterator it = info.data.records.begin(); it != info.data.records.end() && i > 0; ++it)
{ {
Record* rec = *it; Record* rec = *it;
@ -820,14 +823,18 @@ int clone(Arguments& info)
n.val = rec->getName(); n.val = rec->getName();
esm.startRecord(n.toString(), 0); esm.startRecord(n.toString(), 0);
std::string id = rec->getId(); string id = rec->getId();
esm.writeHNOString("NAME", id);
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); rec->save(esm);
if (n.val == REC_CELL && !info.data.cellRefs[rec].empty()) if (n.val == REC_CELL && !info.data.cellRefs[rec].empty())
{ {
std::list<CellRef>& refs = info.data.cellRefs[rec]; list<CellRef>& refs = info.data.cellRefs[rec];
for (std::list<CellRef>::iterator it = refs.begin(); it != refs.end(); ++it) for (list<CellRef>::iterator it = refs.begin(); it != refs.end(); ++it)
{ {
it->save(esm); it->save(esm);
} }
@ -848,5 +855,52 @@ int clone(Arguments& info)
esm.close(); esm.close();
save.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; return 0;
} }

View file

@ -53,12 +53,14 @@ void ESMWriter::save(const std::string& file)
void ESMWriter::save(std::ostream& file) void ESMWriter::save(std::ostream& file)
{ {
m_recordCount = 0;
m_stream = &file; m_stream = &file;
startRecord("TES3", 0); startRecord("TES3", 0);
m_header.records = 0; m_header.records = 0;
writeHNT("HEDR", m_header, 300); writeHNT("HEDR", m_header, 300);
m_headerPos = m_stream->tellp() - (std::streampos)4;
for (std::list<MasterData>::iterator it = m_masters.begin(); it != m_masters.end(); ++it) for (std::list<MasterData>::iterator it = m_masters.begin(); it != m_masters.end(); ++it)
{ {
@ -71,6 +73,10 @@ void ESMWriter::save(std::ostream& file)
void ESMWriter::close() void ESMWriter::close()
{ {
std::cout << "Writing amount of saved records (" << m_recordCount - 1 << ")" << std::endl;
m_stream->seekp(m_headerPos);
writeT<int>(m_recordCount-1);
m_stream->seekp(0, std::ios::end);
m_stream->flush(); m_stream->flush();
if (!m_records.empty()) if (!m_records.empty())
@ -79,6 +85,8 @@ void ESMWriter::close()
void ESMWriter::startRecord(const std::string& name, uint32_t flags) void ESMWriter::startRecord(const std::string& name, uint32_t flags)
{ {
m_recordCount++;
writeName(name); writeName(name);
RecordData rec; RecordData rec;
rec.name = name; rec.name = name;
@ -128,6 +136,21 @@ void ESMWriter::writeHNString(const std::string& name, const std::string& data)
endRecord(name); 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) void ESMWriter::writeHString(const std::string& data)
{ {
if (data.size() == 0) if (data.size() == 0)

View file

@ -34,6 +34,15 @@ public:
void close(); void close();
void writeHNString(const std::string& name, const std::string& data); 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) void writeHNOString(const std::string& name, const std::string& data)
{ {
if (!data.empty()) if (!data.empty())
@ -56,20 +65,6 @@ public:
endRecord(name); endRecord(name);
} }
/*template<typename T>
void writeHT(const T& data)
{
writeT((unsigned int)sizeof(T));
writeT(data);
}
template<typename T>
void writeHT(const T& data, int size)
{
assert(sizeof(T) == size);
writeHT(data);
}*/
template<typename T> template<typename T>
void writeT(const T& data) void writeT(const T& data)
{ {
@ -93,6 +88,8 @@ private:
std::list<MasterData> m_masters; std::list<MasterData> m_masters;
std::list<RecordData> m_records; std::list<RecordData> m_records;
std::ostream* m_stream; std::ostream* m_stream;
std::streampos m_headerPos;
int m_recordCount;
HEDRstruct m_header; HEDRstruct m_header;
SaveData m_saveData; SaveData m_saveData;

View file

@ -27,7 +27,7 @@ void Class::load(ESMReader &esm)
} }
void Class::save(ESMWriter &esm) void Class::save(ESMWriter &esm)
{ {
esm.writeHNString("FNAM", name); esm.writeHNCString("FNAM", name);
esm.writeHNT("CLDT", data, 60); esm.writeHNT("CLDT", data, 60);
esm.writeHNOString("DESC", description); esm.writeHNOString("DESC", description);
} }

View file

@ -29,11 +29,14 @@ void Faction::load(ESMReader &esm)
} }
void Faction::save(ESMWriter &esm) void Faction::save(ESMWriter &esm)
{ {
esm.writeHNString("FNAM", name); esm.writeHNCString("FNAM", name);
for (int i = 0; i < 10; i++) 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); esm.writeHNT("FADT", data, 240);

View file

@ -12,7 +12,7 @@ void Race::load(ESMReader &esm)
} }
void Race::save(ESMWriter &esm) void Race::save(ESMWriter &esm)
{ {
esm.writeHNString("FNAM", name); esm.writeHNCString("FNAM", name);
esm.writeHNT("RADT", data, 140); esm.writeHNT("RADT", data, 140);
powers.save(esm); powers.save(esm);
esm.writeHNOString("DESC", description); esm.writeHNOString("DESC", description);

View file

@ -40,19 +40,22 @@ void Script::load(ESMReader &esm)
} }
void Script::save(ESMWriter &esm) void Script::save(ESMWriter &esm)
{ {
esm.writeHNT("SCHD", data, 52); std::string varNameString;
if (!varNames.empty()) if (!varNames.empty())
{ {
esm.startSubRecord("SCVR");
for (std::vector<std::string>::iterator it = varNames.begin(); it != varNames.end(); ++it) for (std::vector<std::string>::iterator it = varNames.begin(); it != varNames.end(); ++it)
{ {
esm.writeT(it->c_str(), it->size()); varNameString.append(*it);
esm.writeT('\0'); //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.writeHNString("SCDT", std::string(&scriptData[0], scriptData.size()));
esm.writeHNOString("SCTX", scriptText); esm.writeHNOString("SCTX", scriptText);
} }