|
|
|
#include "esmwriter.hpp"
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <fstream>
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
#include <components/to_utf8/to_utf8.hpp>
|
|
|
|
|
|
|
|
namespace ESM
|
|
|
|
{
|
|
|
|
ESMWriter::ESMWriter()
|
|
|
|
: mRecords()
|
|
|
|
, mStream(NULL)
|
|
|
|
, mHeaderPos()
|
|
|
|
, mEncoder(NULL)
|
|
|
|
, mRecordCount(0)
|
|
|
|
, mCounting(true)
|
|
|
|
, mHeader()
|
|
|
|
{}
|
|
|
|
|
|
|
|
unsigned int ESMWriter::getVersion() const
|
|
|
|
{
|
|
|
|
return mHeader.mData.version;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ESMWriter::setVersion(unsigned int ver)
|
|
|
|
{
|
|
|
|
mHeader.mData.version = ver;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ESMWriter::setType(int type)
|
|
|
|
{
|
|
|
|
mHeader.mData.type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
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::clearMaster()
|
|
|
|
{
|
|
|
|
mHeader.mMaster.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
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(std::ostream& file)
|
|
|
|
{
|
|
|
|
mRecordCount = 0;
|
|
|
|
mRecords.clear();
|
|
|
|
mCounting = true;
|
|
|
|
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<uint32_t>(0); // Size goes here
|
|
|
|
writeT<uint32_t>(0); // Unused header?
|
|
|
|
writeT(flags);
|
|
|
|
mRecords.push_back(rec);
|
|
|
|
|
|
|
|
assert(mRecords.back().size == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ESMWriter::startRecord (uint32_t name, uint32_t flags)
|
|
|
|
{
|
|
|
|
std::string type;
|
|
|
|
for (int i=0; i<4; ++i)
|
|
|
|
/// \todo make endianess agnostic
|
|
|
|
type += reinterpret_cast<const char *> (&name)[i];
|
|
|
|
|
|
|
|
startRecord (type, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ESMWriter::startSubRecord(const std::string& name)
|
|
|
|
{
|
|
|
|
// Sub-record hierarchies are not properly supported in ESMReader. This should be fixed later.
|
|
|
|
assert (mRecords.size() <= 1);
|
|
|
|
|
|
|
|
writeName(name);
|
|
|
|
RecordData rec;
|
|
|
|
rec.name = name;
|
|
|
|
rec.position = mStream->tellp();
|
|
|
|
rec.size = 0;
|
|
|
|
writeT<uint32_t>(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(uint32_t));
|
|
|
|
mCounting = true;
|
|
|
|
|
|
|
|
mStream->seekp(0, std::ios::end);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void ESMWriter::endRecord (uint32_t name)
|
|
|
|
{
|
|
|
|
std::string type;
|
|
|
|
for (int i=0; i<4; ++i)
|
|
|
|
/// \todo make endianess agnostic
|
|
|
|
type += reinterpret_cast<const char *> (&name)[i];
|
|
|
|
|
|
|
|
endRecord (type);
|
|
|
|
}
|
|
|
|
|
|
|
|
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::writeFixedSizeString(const std::string &data, int size)
|
|
|
|
{
|
|
|
|
std::string string;
|
|
|
|
if (!data.empty())
|
|
|
|
string = mEncoder ? mEncoder->getLegacyEnc(data) : data;
|
|
|
|
string.resize(size);
|
|
|
|
write(string.c_str(), string.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ESMWriter::writeHString(const std::string& data)
|
|
|
|
{
|
|
|
|
if (data.size() == 0)
|
|
|
|
write("\0", 1);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Convert to UTF8 and return
|
|
|
|
std::string string = mEncoder ? mEncoder->getLegacyEnc(data) : data;
|
|
|
|
|
|
|
|
write(string.c_str(), string.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;
|
|
|
|
}
|
|
|
|
}
|