mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 16:39:41 +00:00
various ESMWriter fixes
This commit is contained in:
parent
8326ac9b6f
commit
bcd36bd378
3 changed files with 262 additions and 256 deletions
|
@ -2,185 +2,182 @@
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <stdexcept>
|
||||||
|
|
||||||
bool count = true;
|
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
ESMWriter::ESMWriter() : mRecordCount (0), mCounting (false) {}
|
||||||
|
|
||||||
int ESMWriter::getVersion()
|
unsigned int ESMWriter::getVersion() const
|
||||||
{
|
|
||||||
return mHeader.mData.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::setVersion(int ver)
|
|
||||||
{
|
|
||||||
mHeader.mData.version = ver;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::setAuthor(const std::string& auth)
|
|
||||||
{
|
|
||||||
mHeader.mData.author.assign (auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::setDescription(const std::string& desc)
|
|
||||||
{
|
|
||||||
mHeader.mData.desc.assign (desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::setRecordCount (int count)
|
|
||||||
{
|
|
||||||
mHeader.mData.records = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::setFormat (int format)
|
|
||||||
{
|
|
||||||
mHeader.mFormat = format;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::addMaster(const std::string& name, uint64_t size)
|
|
||||||
{
|
|
||||||
Header::MasterData d;
|
|
||||||
d.name = name;
|
|
||||||
d.size = size;
|
|
||||||
mHeader.mMaster.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::save(std::ostream& file)
|
|
||||||
{
|
|
||||||
m_recordCount = 0;
|
|
||||||
m_stream = &file;
|
|
||||||
|
|
||||||
startRecord("TES3", 0);
|
|
||||||
|
|
||||||
mHeader.save (*this);
|
|
||||||
|
|
||||||
endRecord("TES3");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::close()
|
|
||||||
{
|
|
||||||
m_stream->flush();
|
|
||||||
|
|
||||||
if (!m_records.empty())
|
|
||||||
throw "Unclosed record remaining";
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::startRecord(const std::string& name, uint32_t flags)
|
|
||||||
{
|
|
||||||
m_recordCount++;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
assert(m_records.back().size == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
RecordData rec = m_records.back();
|
|
||||||
assert(rec.name == name);
|
|
||||||
m_records.pop_back();
|
|
||||||
|
|
||||||
m_stream->seekp(rec.position);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
startSubRecord(name);
|
|
||||||
writeHString(data);
|
|
||||||
endRecord(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size)
|
|
||||||
{
|
|
||||||
assert(data.size() <= size);
|
|
||||||
startSubRecord(name);
|
|
||||||
writeHString(data);
|
|
||||||
|
|
||||||
if (data.size() < size)
|
|
||||||
{
|
{
|
||||||
for (size_t i = data.size(); i < size; ++i)
|
return mHeader.mData.version;
|
||||||
write("\0",1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endRecord(name);
|
void ESMWriter::setVersion(unsigned int ver)
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::writeHString(const std::string& data)
|
|
||||||
{
|
|
||||||
if (data.size() == 0)
|
|
||||||
write("\0", 1);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Convert to UTF8 and return
|
mHeader.mData.version = ver;
|
||||||
std::string ascii = m_encoder->getLegacyEnc(data);
|
|
||||||
|
|
||||||
write(ascii.c_str(), ascii.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::writeHCString(const std::string& data)
|
|
||||||
{
|
|
||||||
writeHString(data);
|
|
||||||
if (data.size() > 0 && data[data.size()-1] != '\0')
|
|
||||||
write("\0", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::writeName(const std::string& name)
|
|
||||||
{
|
|
||||||
assert((name.size() == 4 && name[3] != '\0'));
|
|
||||||
write(name.c_str(), name.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESMWriter::write(const char* data, size_t 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);
|
void ESMWriter::setAuthor(const std::string& auth)
|
||||||
}
|
{
|
||||||
|
mHeader.mData.author.assign (auth);
|
||||||
|
}
|
||||||
|
|
||||||
void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)
|
void ESMWriter::setDescription(const std::string& desc)
|
||||||
{
|
{
|
||||||
m_encoder = encoder;
|
mHeader.mData.desc.assign (desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESMWriter::setRecordCount (int count)
|
||||||
|
{
|
||||||
|
mHeader.mData.records = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::setFormat (int format)
|
||||||
|
{
|
||||||
|
mHeader.mFormat = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::addMaster(const std::string& name, uint64_t size)
|
||||||
|
{
|
||||||
|
Header::MasterData d;
|
||||||
|
d.name = name;
|
||||||
|
d.size = size;
|
||||||
|
mHeader.mMaster.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::save(std::ostream& file)
|
||||||
|
{
|
||||||
|
mRecordCount = 0;
|
||||||
|
mRecords.clear();
|
||||||
|
mStream = &file;
|
||||||
|
|
||||||
|
startRecord("TES3", 0);
|
||||||
|
|
||||||
|
mHeader.save (*this);
|
||||||
|
|
||||||
|
endRecord("TES3");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::close()
|
||||||
|
{
|
||||||
|
if (!mRecords.empty())
|
||||||
|
throw std::runtime_error ("Unclosed record remaining");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::startRecord(const std::string& name, uint32_t flags)
|
||||||
|
{
|
||||||
|
mRecordCount++;
|
||||||
|
|
||||||
|
writeName(name);
|
||||||
|
RecordData rec;
|
||||||
|
rec.name = name;
|
||||||
|
rec.position = mStream->tellp();
|
||||||
|
rec.size = 0;
|
||||||
|
writeT<int>(0); // Size goes here
|
||||||
|
writeT<int>(0); // Unused header?
|
||||||
|
writeT(flags);
|
||||||
|
mRecords.push_back(rec);
|
||||||
|
|
||||||
|
assert(mRecords.back().size == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::startSubRecord(const std::string& name)
|
||||||
|
{
|
||||||
|
writeName(name);
|
||||||
|
RecordData rec;
|
||||||
|
rec.name = name;
|
||||||
|
rec.position = mStream->tellp();
|
||||||
|
rec.size = 0;
|
||||||
|
writeT<int>(0); // Size goes here
|
||||||
|
mRecords.push_back(rec);
|
||||||
|
|
||||||
|
assert(mRecords.back().size == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::endRecord(const std::string& name)
|
||||||
|
{
|
||||||
|
RecordData rec = mRecords.back();
|
||||||
|
assert(rec.name == name);
|
||||||
|
mRecords.pop_back();
|
||||||
|
|
||||||
|
mStream->seekp(rec.position);
|
||||||
|
|
||||||
|
mCounting = false;
|
||||||
|
write (reinterpret_cast<const char*> (&rec.size), sizeof(int));
|
||||||
|
mCounting = true;
|
||||||
|
|
||||||
|
mStream->seekp(0, std::ios::end);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::writeHNString(const std::string& name, const std::string& data)
|
||||||
|
{
|
||||||
|
startSubRecord(name);
|
||||||
|
writeHString(data);
|
||||||
|
endRecord(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size)
|
||||||
|
{
|
||||||
|
assert(data.size() <= size);
|
||||||
|
startSubRecord(name);
|
||||||
|
writeHString(data);
|
||||||
|
|
||||||
|
if (data.size() < size)
|
||||||
|
{
|
||||||
|
for (size_t i = data.size(); i < size; ++i)
|
||||||
|
write("\0",1);
|
||||||
|
}
|
||||||
|
|
||||||
|
endRecord(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::writeHString(const std::string& data)
|
||||||
|
{
|
||||||
|
if (data.size() == 0)
|
||||||
|
write("\0", 1);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Convert to UTF8 and return
|
||||||
|
std::string ascii = mEncoder->getLegacyEnc(data);
|
||||||
|
|
||||||
|
write(ascii.c_str(), ascii.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::writeHCString(const std::string& data)
|
||||||
|
{
|
||||||
|
writeHString(data);
|
||||||
|
if (data.size() > 0 && data[data.size()-1] != '\0')
|
||||||
|
write("\0", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::writeName(const std::string& name)
|
||||||
|
{
|
||||||
|
assert((name.size() == 4 && name[3] != '\0'));
|
||||||
|
write(name.c_str(), name.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::write(const char* data, size_t size)
|
||||||
|
{
|
||||||
|
if (mCounting && !mRecords.empty())
|
||||||
|
{
|
||||||
|
for (std::list<RecordData>::iterator it = mRecords.begin(); it != mRecords.end(); ++it)
|
||||||
|
it->size += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
mStream->write(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder)
|
||||||
|
{
|
||||||
|
mEncoder = encoder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,92 +13,101 @@ namespace ESM {
|
||||||
|
|
||||||
class ESMWriter
|
class ESMWriter
|
||||||
{
|
{
|
||||||
struct RecordData
|
struct RecordData
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::streampos position;
|
std::streampos position;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ESMWriter();
|
||||||
|
|
||||||
|
unsigned int getVersion() const;
|
||||||
|
void setVersion(unsigned int ver = 0x3fa66666);
|
||||||
|
void setEncoder(ToUTF8::Utf8Encoder *encoding);
|
||||||
|
void setAuthor(const std::string& author);
|
||||||
|
void setDescription(const std::string& desc);
|
||||||
|
void setRecordCount (int count);
|
||||||
|
void setFormat (int format);
|
||||||
|
|
||||||
|
void addMaster(const std::string& name, uint64_t size);
|
||||||
|
|
||||||
|
void save(const std::string& file);
|
||||||
|
///< Start saving a file by writing the TES3 header.
|
||||||
|
|
||||||
|
void save(std::ostream& file);
|
||||||
|
///< Start saving a file by writing the TES3 header.
|
||||||
|
|
||||||
|
void close();
|
||||||
|
///< \note Does not close the stream.
|
||||||
|
|
||||||
|
void writeHNString(const std::string& name, const std::string& data);
|
||||||
|
void writeHNString(const std::string& name, const std::string& data, size_t size);
|
||||||
|
void writeHNCString(const std::string& name, const std::string& data)
|
||||||
|
{
|
||||||
|
startSubRecord(name);
|
||||||
|
writeHCString(data);
|
||||||
|
endRecord(name);
|
||||||
|
}
|
||||||
|
void writeHNOString(const std::string& name, const std::string& data)
|
||||||
|
{
|
||||||
|
if (!data.empty())
|
||||||
|
writeHNString(name, data);
|
||||||
|
}
|
||||||
|
void writeHNOCString(const std::string& name, const std::string& data)
|
||||||
|
{
|
||||||
|
if (!data.empty())
|
||||||
|
writeHNCString(name, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void writeHNT(const std::string& name, const T& data)
|
||||||
|
{
|
||||||
|
startSubRecord(name);
|
||||||
|
writeT(data);
|
||||||
|
endRecord(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void writeHNT(const std::string& name, const T& data, int size)
|
||||||
|
{
|
||||||
|
startSubRecord(name);
|
||||||
|
writeT(data, size);
|
||||||
|
endRecord(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void writeT(const T& data)
|
||||||
|
{
|
||||||
|
write((char*)&data, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void writeT(const T& data, size_t size)
|
||||||
|
{
|
||||||
|
write((char*)&data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 writeHCString(const std::string& data);
|
||||||
|
void writeName(const std::string& data);
|
||||||
|
void write(const char* data, size_t size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::list<RecordData> mRecords;
|
||||||
|
std::ostream* mStream;
|
||||||
|
std::streampos mHeaderPos;
|
||||||
|
ToUTF8::Utf8Encoder* mEncoder;
|
||||||
|
int mRecordCount;
|
||||||
|
bool mCounting;
|
||||||
|
|
||||||
|
Header mHeader;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
|
||||||
int getVersion();
|
|
||||||
void setVersion(int ver);
|
|
||||||
void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8?
|
|
||||||
void setAuthor(const std::string& author);
|
|
||||||
void setDescription(const std::string& desc);
|
|
||||||
void setRecordCount (int count);
|
|
||||||
void setFormat (int format);
|
|
||||||
|
|
||||||
void addMaster(const std::string& name, uint64_t size);
|
|
||||||
|
|
||||||
void save(const std::string& file);
|
|
||||||
void save(std::ostream& file);
|
|
||||||
void close();
|
|
||||||
|
|
||||||
void writeHNString(const std::string& name, const std::string& data);
|
|
||||||
void writeHNString(const std::string& name, const std::string& data, size_t size);
|
|
||||||
void writeHNCString(const std::string& name, const std::string& data)
|
|
||||||
{
|
|
||||||
startSubRecord(name);
|
|
||||||
writeHCString(data);
|
|
||||||
endRecord(name);
|
|
||||||
}
|
|
||||||
void writeHNOString(const std::string& name, const std::string& data)
|
|
||||||
{
|
|
||||||
if (!data.empty())
|
|
||||||
writeHNString(name, data);
|
|
||||||
}
|
|
||||||
void writeHNOCString(const std::string& name, const std::string& data)
|
|
||||||
{
|
|
||||||
if (!data.empty())
|
|
||||||
writeHNCString(name, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void writeHNT(const std::string& name, const T& data)
|
|
||||||
{
|
|
||||||
startSubRecord(name);
|
|
||||||
writeT(data);
|
|
||||||
endRecord(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void writeHNT(const std::string& name, const T& data, int size)
|
|
||||||
{
|
|
||||||
startSubRecord(name);
|
|
||||||
writeT(data, size);
|
|
||||||
endRecord(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void writeT(const T& data)
|
|
||||||
{
|
|
||||||
write((char*)&data, sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void writeT(const T& data, size_t size)
|
|
||||||
{
|
|
||||||
write((char*)&data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 writeHCString(const std::string& data);
|
|
||||||
void writeName(const std::string& data);
|
|
||||||
void write(const char* data, size_t size);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::list<RecordData> m_records;
|
|
||||||
std::ostream* m_stream;
|
|
||||||
std::streampos m_headerPos;
|
|
||||||
ToUTF8::Utf8Encoder* m_encoder;
|
|
||||||
int m_recordCount;
|
|
||||||
|
|
||||||
Header mHeader;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace ESM
|
||||||
versions are 1.2 and 1.3. These correspond to:
|
versions are 1.2 and 1.3. These correspond to:
|
||||||
1.2 = 0x3f99999a and 1.3 = 0x3fa66666
|
1.2 = 0x3f99999a and 1.3 = 0x3fa66666
|
||||||
*/
|
*/
|
||||||
int version;
|
unsigned int version;
|
||||||
int type; // 0=esp, 1=esm, 32=ess (unused)
|
int type; // 0=esp, 1=esm, 32=ess (unused)
|
||||||
NAME32 author; // Author's name
|
NAME32 author; // Author's name
|
||||||
NAME256 desc; // File description
|
NAME256 desc; // File description
|
||||||
|
|
Loading…
Reference in a new issue