Merge remote-tracking branch 'scrawl/essimporter'
commit
a266dffb4b
@ -0,0 +1,29 @@
|
|||||||
|
set(ESSIMPORTER_FILES
|
||||||
|
main.cpp
|
||||||
|
importer.cpp
|
||||||
|
importplayer.cpp
|
||||||
|
importnpcc.cpp
|
||||||
|
importcrec.cpp
|
||||||
|
importcellref.cpp
|
||||||
|
importacdt.cpp
|
||||||
|
importinventory.cpp
|
||||||
|
importklst.cpp
|
||||||
|
importercontext.cpp
|
||||||
|
converter.cpp
|
||||||
|
convertacdt.cpp
|
||||||
|
convertnpcc.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(openmw-essimporter
|
||||||
|
${ESSIMPORTER_FILES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(openmw-essimporter
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
components
|
||||||
|
)
|
||||||
|
|
||||||
|
if (BUILD_WITH_CODE_COVERAGE)
|
||||||
|
add_definitions (--coverage)
|
||||||
|
target_link_libraries(openmw-essimporter gcov)
|
||||||
|
endif()
|
@ -0,0 +1,42 @@
|
|||||||
|
#include "convertacdt.hpp"
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
int translateDynamicIndex(int mwIndex)
|
||||||
|
{
|
||||||
|
if (mwIndex == 1)
|
||||||
|
return 2;
|
||||||
|
else if (mwIndex == 2)
|
||||||
|
return 1;
|
||||||
|
return mwIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats)
|
||||||
|
{
|
||||||
|
for (int i=0; i<3; ++i)
|
||||||
|
{
|
||||||
|
int writeIndex = translateDynamicIndex(i);
|
||||||
|
cStats.mDynamic[writeIndex].mBase = acdt.mDynamic[i][1];
|
||||||
|
cStats.mDynamic[writeIndex].mMod = acdt.mDynamic[i][1];
|
||||||
|
cStats.mDynamic[writeIndex].mCurrent = acdt.mDynamic[i][0];
|
||||||
|
}
|
||||||
|
for (int i=0; i<8; ++i)
|
||||||
|
{
|
||||||
|
cStats.mAttributes[i].mBase = acdt.mAttributes[i][1];
|
||||||
|
cStats.mAttributes[i].mMod = acdt.mAttributes[i][0];
|
||||||
|
cStats.mAttributes[i].mCurrent = acdt.mAttributes[i][0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats)
|
||||||
|
{
|
||||||
|
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||||
|
{
|
||||||
|
npcStats.mSkills[i].mRegular.mMod = actorData.mSkills[i][1];
|
||||||
|
npcStats.mSkills[i].mRegular.mCurrent = actorData.mSkills[i][1];
|
||||||
|
npcStats.mSkills[i].mRegular.mBase = actorData.mSkills[i][0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_CONVERTACDT_H
|
||||||
|
#define OPENMW_ESSIMPORT_CONVERTACDT_H
|
||||||
|
|
||||||
|
#include <components/esm/creaturestats.hpp>
|
||||||
|
#include <components/esm/npcstats.hpp>
|
||||||
|
#include <components/esm/loadskil.hpp>
|
||||||
|
|
||||||
|
#include "importacdt.hpp"
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
// OpenMW uses Health,Magicka,Fatigue, MW uses Health,Fatigue,Magicka
|
||||||
|
int translateDynamicIndex(int mwIndex);
|
||||||
|
|
||||||
|
|
||||||
|
void convertACDT (const ACDT& acdt, ESM::CreatureStats& cStats);
|
||||||
|
|
||||||
|
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,241 @@
|
|||||||
|
#include "converter.hpp"
|
||||||
|
|
||||||
|
#include <OgreImage.h>
|
||||||
|
|
||||||
|
#include <components/esm/creaturestate.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void convertImage(char* data, int size, int width, int height, Ogre::PixelFormat pf, const std::string& out)
|
||||||
|
{
|
||||||
|
Ogre::Image screenshot;
|
||||||
|
Ogre::DataStreamPtr stream (new Ogre::MemoryDataStream(data, size));
|
||||||
|
screenshot.loadRawData(stream, width, height, 1, pf);
|
||||||
|
screenshot.save(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
struct MAPH
|
||||||
|
{
|
||||||
|
unsigned int size;
|
||||||
|
unsigned int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ConvertFMAP::read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
MAPH maph;
|
||||||
|
esm.getHNT(maph, "MAPH");
|
||||||
|
std::vector<char> data;
|
||||||
|
esm.getSubNameIs("MAPD");
|
||||||
|
esm.getSubHeader();
|
||||||
|
data.resize(esm.getSubSize());
|
||||||
|
esm.getExact(&data[0], data.size());
|
||||||
|
convertImage(&data[0], data.size(), maph.size, maph.size, Ogre::PF_BYTE_RGB, "map.tga");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertCell::read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
ESM::Cell cell;
|
||||||
|
std::string id = esm.getHNString("NAME");
|
||||||
|
cell.mName = id;
|
||||||
|
cell.load(esm, false);
|
||||||
|
|
||||||
|
// note if the player is in a nameless exterior cell, we will assign the cellId later based on player position
|
||||||
|
if (id == mContext->mPlayerCellName)
|
||||||
|
{
|
||||||
|
mContext->mPlayer.mCellId = cell.getCellId();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cell newcell;
|
||||||
|
newcell.mCell = cell;
|
||||||
|
|
||||||
|
// fog of war
|
||||||
|
// seems to be a 1-bit pixel format, 16*16 pixels
|
||||||
|
// TODO: add bleeding of FOW into neighbouring cells (openmw handles this by writing to the textures,
|
||||||
|
// MW handles it when rendering only)
|
||||||
|
unsigned char nam8[32];
|
||||||
|
// exterior has 1 NAM8, interior can have multiple ones, and have an extra 4 byte flag at the start
|
||||||
|
// (probably offset of that specific fog texture?)
|
||||||
|
while (esm.isNextSub("NAM8"))
|
||||||
|
{
|
||||||
|
esm.getSubHeader();
|
||||||
|
|
||||||
|
if (esm.getSubSize() == 36)
|
||||||
|
{
|
||||||
|
// flag on interiors
|
||||||
|
esm.skip(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
esm.getExact(nam8, 32);
|
||||||
|
|
||||||
|
newcell.mFogOfWar.reserve(16*16);
|
||||||
|
for (int x=0; x<16; ++x)
|
||||||
|
{
|
||||||
|
for (int y=0; y<16; ++y)
|
||||||
|
{
|
||||||
|
size_t pos = x*16+y;
|
||||||
|
size_t bytepos = pos/8;
|
||||||
|
assert(bytepos<32);
|
||||||
|
int bit = pos%8;
|
||||||
|
newcell.mFogOfWar.push_back(((nam8[bytepos] >> bit) & (0x1)) ? 0xffffffff : 0x000000ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell.isExterior())
|
||||||
|
{
|
||||||
|
std::ostringstream filename;
|
||||||
|
filename << "fog_" << cell.mData.mX << "_" << cell.mData.mY << ".tga";
|
||||||
|
|
||||||
|
convertImage((char*)&newcell.mFogOfWar[0], newcell.mFogOfWar.size()*4, 16, 16, Ogre::PF_BYTE_RGBA, filename.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// moved reference, not handled yet
|
||||||
|
// NOTE: MVRF can also occur in within normal references (importcellref.cpp)?
|
||||||
|
// this does not match the ESM file implementation,
|
||||||
|
// verify if that can happen with ESM files too
|
||||||
|
while (esm.isNextSub("MVRF"))
|
||||||
|
{
|
||||||
|
esm.skipHSub(); // skip MVRF
|
||||||
|
esm.getSubName();
|
||||||
|
esm.skipHSub(); // skip CNDT
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<CellRef> cellrefs;
|
||||||
|
while (esm.hasMoreSubs() && esm.isNextSub("FRMR"))
|
||||||
|
{
|
||||||
|
CellRef ref;
|
||||||
|
ref.load (esm);
|
||||||
|
if (esm.isNextSub("DELE"))
|
||||||
|
{
|
||||||
|
// strangely this can be e.g. 52 instead of just 1,
|
||||||
|
std::cout << "deleted ref " << ref.mIndexedRefId << std::endl;
|
||||||
|
esm.skipHSub();
|
||||||
|
}
|
||||||
|
cellrefs.push_back(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (esm.isNextSub("MPCD"))
|
||||||
|
{
|
||||||
|
float notepos[3];
|
||||||
|
esm.getHT(notepos, 3*sizeof(float));
|
||||||
|
|
||||||
|
// Markers seem to be arranged in a 32*32 grid, notepos has grid-indices.
|
||||||
|
// This seems to be the reason markers can't be placed everywhere in interior cells,
|
||||||
|
// i.e. when the grid is exceeded.
|
||||||
|
// Converting the interior markers correctly could be rather tricky, but is probably similar logic
|
||||||
|
// as used for the FoW texture placement, which we need to figure out anyway
|
||||||
|
notepos[1] += 31.f;
|
||||||
|
notepos[0] += 0.5;
|
||||||
|
notepos[1] += 0.5;
|
||||||
|
notepos[0] = 8192 * notepos[0] / 32.f;
|
||||||
|
notepos[1] = 8192 * notepos[1] / 32.f;
|
||||||
|
if (cell.isExterior())
|
||||||
|
{
|
||||||
|
notepos[0] += 8192 * cell.mData.mX;
|
||||||
|
notepos[1] += 8192 * cell.mData.mY;
|
||||||
|
}
|
||||||
|
// TODO: what encoding is this in?
|
||||||
|
std::string note = esm.getHNString("MPNT");
|
||||||
|
ESM::CustomMarker marker;
|
||||||
|
marker.mWorldX = notepos[0];
|
||||||
|
marker.mWorldY = notepos[1];
|
||||||
|
marker.mNote = note;
|
||||||
|
marker.mCell = cell.getCellId();
|
||||||
|
mMarkers.push_back(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
newcell.mRefs = cellrefs;
|
||||||
|
|
||||||
|
// FIXME: map by ID for exterior cells
|
||||||
|
mCells[id] = newcell;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertCell::write(ESM::ESMWriter &esm)
|
||||||
|
{
|
||||||
|
for (std::map<std::string, Cell>::const_iterator it = mCells.begin(); it != mCells.end(); ++it)
|
||||||
|
{
|
||||||
|
const ESM::Cell& cell = it->second.mCell;
|
||||||
|
esm.startRecord(ESM::REC_CSTA);
|
||||||
|
ESM::CellState csta;
|
||||||
|
csta.mHasFogOfWar = 0;
|
||||||
|
csta.mId = cell.getCellId();
|
||||||
|
csta.mId.save(esm);
|
||||||
|
// TODO csta.mLastRespawn;
|
||||||
|
// shouldn't be needed if we respawn on global schedule like in original MW
|
||||||
|
csta.mWaterLevel = cell.mWater;
|
||||||
|
csta.save(esm);
|
||||||
|
|
||||||
|
for (std::vector<CellRef>::const_iterator refIt = it->second.mRefs.begin(); refIt != it->second.mRefs.end(); ++refIt)
|
||||||
|
{
|
||||||
|
const CellRef& cellref = *refIt;
|
||||||
|
ESM::CellRef out;
|
||||||
|
out.blank();
|
||||||
|
|
||||||
|
if (cellref.mIndexedRefId.size() < 8)
|
||||||
|
{
|
||||||
|
std::cerr << "CellRef with no index?" << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8);
|
||||||
|
int refIndex;
|
||||||
|
stream >> refIndex;
|
||||||
|
|
||||||
|
out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8);
|
||||||
|
|
||||||
|
std::map<std::pair<int, std::string>, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find(
|
||||||
|
std::make_pair(refIndex, out.mRefID));
|
||||||
|
if (crecIt != mContext->mCreatureChanges.end())
|
||||||
|
{
|
||||||
|
ESM::CreatureState objstate;
|
||||||
|
objstate.blank();
|
||||||
|
convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats);
|
||||||
|
objstate.mEnabled = cellref.mEnabled;
|
||||||
|
objstate.mPosition = cellref.mPos;
|
||||||
|
objstate.mRef = out;
|
||||||
|
objstate.mRef.mRefNum = cellref.mRefNum;
|
||||||
|
// FIXME: change save format to not require object type, instead look up it up using the RefId
|
||||||
|
esm.writeHNT ("OBJE", ESM::REC_CREA);
|
||||||
|
objstate.save(esm);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
|
||||||
|
std::make_pair(refIndex, out.mRefID));
|
||||||
|
if (npccIt != mContext->mNpcChanges.end())
|
||||||
|
{
|
||||||
|
ESM::NpcState objstate;
|
||||||
|
objstate.blank();
|
||||||
|
convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats);
|
||||||
|
convertNpcData(cellref.mActorData, objstate.mNpcStats);
|
||||||
|
objstate.mEnabled = cellref.mEnabled;
|
||||||
|
objstate.mPosition = cellref.mPos;
|
||||||
|
objstate.mRef = out;
|
||||||
|
objstate.mRef.mRefNum = cellref.mRefNum;
|
||||||
|
esm.writeHNT ("OBJE", ESM::REC_NPC_);
|
||||||
|
objstate.save(esm);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
esm.endRecord(ESM::REC_CSTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<ESM::CustomMarker>::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it)
|
||||||
|
{
|
||||||
|
esm.startRecord(ESM::REC_MARK);
|
||||||
|
it->save(esm);
|
||||||
|
esm.endRecord(ESM::REC_MARK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,279 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_CONVERTER_H
|
||||||
|
#define OPENMW_ESSIMPORT_CONVERTER_H
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
#include <components/esm/esmwriter.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/loadcell.hpp>
|
||||||
|
#include <components/esm/loadbook.hpp>
|
||||||
|
#include <components/esm/loadclas.hpp>
|
||||||
|
#include <components/esm/loadglob.hpp>
|
||||||
|
#include <components/esm/cellstate.hpp>
|
||||||
|
#include <components/esm/custommarkerstate.hpp>
|
||||||
|
|
||||||
|
#include "importcrec.hpp"
|
||||||
|
|
||||||
|
#include "importercontext.hpp"
|
||||||
|
#include "importcellref.hpp"
|
||||||
|
#include "importklst.hpp"
|
||||||
|
|
||||||
|
#include "convertacdt.hpp"
|
||||||
|
#include "convertnpcc.hpp"
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
class Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @return the order for writing this converter's records to the output file, in relation to other converters
|
||||||
|
virtual int getStage() { return 1; }
|
||||||
|
|
||||||
|
virtual ~Converter() {}
|
||||||
|
|
||||||
|
void setContext(Context& context) { mContext = &context; }
|
||||||
|
|
||||||
|
virtual void read(ESM::ESMReader& esm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called after the input file has been read in completely, which may be necessary
|
||||||
|
/// if the conversion process relies on information in other records
|
||||||
|
virtual void write(ESM::ESMWriter& esm)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Context* mContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Default converter: simply reads the record and writes it unmodified to the output
|
||||||
|
template <typename T>
|
||||||
|
class DefaultConverter : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual int getStage() { return 0; }
|
||||||
|
|
||||||
|
virtual void read(ESM::ESMReader& esm)
|
||||||
|
{
|
||||||
|
std::string id = esm.getHNString("NAME");
|
||||||
|
T record;
|
||||||
|
record.load(esm);
|
||||||
|
mRecords[id] = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void write(ESM::ESMWriter& esm)
|
||||||
|
{
|
||||||
|
for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)
|
||||||
|
{
|
||||||
|
esm.startRecord(T::sRecordId);
|
||||||
|
esm.writeHNString("NAME", it->first);
|
||||||
|
it->second.save(esm);
|
||||||
|
esm.endRecord(T::sRecordId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::map<std::string, T> mRecords;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertNPC : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
// this is always the player
|
||||||
|
ESM::NPC npc;
|
||||||
|
std::string id = esm.getHNString("NAME");
|
||||||
|
npc.load(esm);
|
||||||
|
if (id != "player") // seems to occur sometimes, with "chargen X" names
|
||||||
|
std::cerr << "non-player NPC record: " << id << std::endl;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
|
||||||
|
mContext->mPlayerBase = npc;
|
||||||
|
std::map<const int, float> empty;
|
||||||
|
// FIXME: not working?
|
||||||
|
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)
|
||||||
|
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertGlobal : public DefaultConverter<ESM::Global>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
std::string id = esm.getHNString("NAME");
|
||||||
|
ESM::Global global;
|
||||||
|
global.load(esm);
|
||||||
|
if (Misc::StringUtils::ciEqual(id, "gamehour"))
|
||||||
|
mContext->mHour = global.mValue.getFloat();
|
||||||
|
if (Misc::StringUtils::ciEqual(id, "day"))
|
||||||
|
mContext->mDay = global.mValue.getInteger();
|
||||||
|
if (Misc::StringUtils::ciEqual(id, "month"))
|
||||||
|
mContext->mMonth = global.mValue.getInteger();
|
||||||
|
if (Misc::StringUtils::ciEqual(id, "year"))
|
||||||
|
mContext->mYear = global.mValue.getInteger();
|
||||||
|
mRecords[id] = global;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertClass : public DefaultConverter<ESM::Class>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
std::string id = esm.getHNString("NAME");
|
||||||
|
ESM::Class class_;
|
||||||
|
class_.load(esm);
|
||||||
|
|
||||||
|
if (id == "NEWCLASSID_CHARGEN")
|
||||||
|
mContext->mCustomPlayerClassName = class_.mName;
|
||||||
|
|
||||||
|
mRecords[id] = class_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertBook : public DefaultConverter<ESM::Book>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
std::string id = esm.getHNString("NAME");
|
||||||
|
ESM::Book book;
|
||||||
|
book.load(esm);
|
||||||
|
if (book.mData.mSkillID == -1)
|
||||||
|
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(id));
|
||||||
|
|
||||||
|
mRecords[id] = book;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertNPCC : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
std::string id = esm.getHNString("NAME");
|
||||||
|
NPCC npcc;
|
||||||
|
npcc.load(esm);
|
||||||
|
if (id == "PlayerSaveGame")
|
||||||
|
{
|
||||||
|
convertNPCC(npcc, mContext->mPlayer.mObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), npcc));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertREFR : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
REFR refr;
|
||||||
|
refr.load(esm);
|
||||||
|
assert(refr.mRefID == "PlayerSaveGame");
|
||||||
|
mContext->mPlayer.mObject.mPosition = refr.mPos;
|
||||||
|
|
||||||
|
ESM::CreatureStats& cStats = mContext->mPlayer.mObject.mCreatureStats;
|
||||||
|
convertACDT(refr.mActorData.mACDT, cStats);
|
||||||
|
|
||||||
|
ESM::NpcStats& npcStats = mContext->mPlayer.mObject.mNpcStats;
|
||||||
|
convertNpcData(refr.mActorData, npcStats);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertPCDT : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
PCDT pcdt;
|
||||||
|
pcdt.load(esm);
|
||||||
|
|
||||||
|
mContext->mPlayer.mBirthsign = pcdt.mBirthsign;
|
||||||
|
mContext->mPlayer.mObject.mNpcStats.mBounty = pcdt.mBounty;
|
||||||
|
for (std::vector<PCDT::FNAM>::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it)
|
||||||
|
{
|
||||||
|
ESM::NpcStats::Faction faction;
|
||||||
|
faction.mExpelled = it->mFlags & 0x2;
|
||||||
|
faction.mRank = it->mRank;
|
||||||
|
faction.mReputation = it->mReputation;
|
||||||
|
mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertCREC : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
std::string id = esm.getHNString("NAME");
|
||||||
|
CREC crec;
|
||||||
|
crec.load(esm);
|
||||||
|
|
||||||
|
mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertFMAP : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader &esm);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertCell : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader& esm);
|
||||||
|
virtual void write(ESM::ESMWriter& esm);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Cell
|
||||||
|
{
|
||||||
|
ESM::Cell mCell;
|
||||||
|
std::vector<CellRef> mRefs;
|
||||||
|
std::vector<unsigned int> mFogOfWar;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, Cell> mCells;
|
||||||
|
|
||||||
|
std::vector<ESM::CustomMarker> mMarkers;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConvertKLST : public Converter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void read(ESM::ESMReader& esm)
|
||||||
|
{
|
||||||
|
KLST klst;
|
||||||
|
klst.load(esm);
|
||||||
|
mKillCounter = klst.mKillCounter;
|
||||||
|
|
||||||
|
mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void write(ESM::ESMWriter &esm)
|
||||||
|
{
|
||||||
|
esm.startRecord(ESM::REC_DCOU);
|
||||||
|
for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
|
||||||
|
{
|
||||||
|
esm.writeHNString("ID__", it->first);
|
||||||
|
esm.writeHNT ("COUN", it->second);
|
||||||
|
}
|
||||||
|
esm.endRecord(ESM::REC_DCOU);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, int> mKillCounter;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,10 @@
|
|||||||
|
#include "convertnpcc.hpp"
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState)
|
||||||
|
{
|
||||||
|
npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_CONVERTNPCC_H
|
||||||
|
#define OPENMW_ESSIMPORT_CONVERTNPCC_H
|
||||||
|
|
||||||
|
#include "importnpcc.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/npcstate.hpp>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void convertNPCC (const NPCC& npcc, ESM::NpcState& npcState);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,104 @@
|
|||||||
|
#include "importacdt.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/cellref.hpp>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void ActorData::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
// unsure at which point between NAME and ESM::CellRef
|
||||||
|
if (esm.isNextSub("MNAM"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
if (esm.isNextSub("ACTN"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
if (esm.isNextSub("STPR"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
ESM::CellRef bla;
|
||||||
|
bla.ESM::CellRef::loadData(esm);
|
||||||
|
|
||||||
|
// FIXME: actually should be required for all actors?, but ActorData is currently in base CellRef
|
||||||
|
esm.getHNOT(mACDT, "ACDT");
|
||||||
|
|
||||||
|
ACSC acsc;
|
||||||
|
esm.getHNOT(acsc, "ACSC");
|
||||||
|
esm.getHNOT(acsc, "ACSL");
|
||||||
|
|
||||||
|
if (esm.isNextSub("CSTN"))
|
||||||
|
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||||
|
|
||||||
|
if (esm.isNextSub("LSTN"))
|
||||||
|
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||||
|
|
||||||
|
// unsure at which point between LSTN and TGTN
|
||||||
|
if (esm.isNextSub("CSHN"))
|
||||||
|
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||||
|
|
||||||
|
// unsure if before or after CSTN/LSTN
|
||||||
|
if (esm.isNextSub("LSHN"))
|
||||||
|
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||||
|
|
||||||
|
while (esm.isNextSub("TGTN"))
|
||||||
|
esm.skipHSub(); // "PlayerSaveGame", link to some object?
|
||||||
|
|
||||||
|
while (esm.isNextSub("FGTN"))
|
||||||
|
esm.getHString(); // fight target?
|
||||||
|
|
||||||
|
// unsure at which point between TGTN and CRED
|
||||||
|
if (esm.isNextSub("AADT"))
|
||||||
|
{
|
||||||
|
// occured when a creature was in the middle of its attack, 44 bytes
|
||||||
|
esm.skipHSub();
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsure at which point between FGTN and CHRD
|
||||||
|
if (esm.isNextSub("PWPC"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("PWPS"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
if (esm.isNextSub("WNAM"))
|
||||||
|
{
|
||||||
|
esm.skipHSub(); // seen values: "ancestor guardian", "bound dagger_en". Summoned creature / bound weapons?
|
||||||
|
|
||||||
|
if (esm.isNextSub("XNAM"))
|
||||||
|
{
|
||||||
|
// "demon tanto", probably the ID of spell/item that created the bound weapon/crature?
|
||||||
|
esm.skipHSub();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esm.isNextSub("YNAM"))
|
||||||
|
esm.skipHSub(); // 4 byte, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esm.isNextSub("CHRD")) // npc only
|
||||||
|
esm.getHExact(mSkills, 27*2*sizeof(int));
|
||||||
|
|
||||||
|
if (esm.isNextSub("CRED")) // creature only
|
||||||
|
esm.getHExact(mCombatStats, 3*2*sizeof(int));
|
||||||
|
|
||||||
|
mScript = esm.getHNOString("SCRI");
|
||||||
|
|
||||||
|
// script variables?
|
||||||
|
if (!mScript.empty())
|
||||||
|
{
|
||||||
|
if (esm.isNextSub("SLCS"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("SLSD")) // Short Data?
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("SLFD")) // Float Data?
|
||||||
|
esm.skipHSub();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esm.isNextSub("ND3D"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("ANIS"))
|
||||||
|
esm.skipHSub();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_ACDT_H
|
||||||
|
#define OPENMW_ESSIMPORT_ACDT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// Actor data, shared by (at least) REFR and CellRef
|
||||||
|
struct ACDT
|
||||||
|
{
|
||||||
|
unsigned char mUnknown1[40];
|
||||||
|
float mDynamic[3][2];
|
||||||
|
unsigned char mUnknown2[16];
|
||||||
|
float mAttributes[8][2];
|
||||||
|
unsigned char mUnknown3[120];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActorData
|
||||||
|
{
|
||||||
|
ACDT mACDT;
|
||||||
|
|
||||||
|
int mSkills[27][2];
|
||||||
|
|
||||||
|
// creature combat stats, base and modified
|
||||||
|
// I think these can be ignored in the conversion, because it is not possible
|
||||||
|
// to change them ingame
|
||||||
|
int mCombatStats[3][2];
|
||||||
|
|
||||||
|
std::string mScript;
|
||||||
|
|
||||||
|
void load(ESM::ESMReader& esm);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Unknown, shared by (at least) REFR and CellRef
|
||||||
|
struct ACSC
|
||||||
|
{
|
||||||
|
unsigned char unknown[112];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,43 @@
|
|||||||
|
#include "importcellref.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void CellRef::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
// (FRMR subrecord name is already read by the loop in ConvertCell)
|
||||||
|
esm.getHT(mRefNum.mIndex); // FRMR
|
||||||
|
|
||||||
|
// this is required since openmw supports more than 255 content files
|
||||||
|
int pluginIndex = (mRefNum.mIndex & 0xff000000) >> 24;
|
||||||
|
mRefNum.mContentFile = pluginIndex-1;
|
||||||
|
mRefNum.mIndex &= 0x00ffffff;
|
||||||
|
|
||||||
|
mIndexedRefId = esm.getHNString("NAME");
|
||||||
|
|
||||||
|
if (esm.isNextSub("LVCR"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
mActorData.load(esm);
|
||||||
|
|
||||||
|
mEnabled = true;
|
||||||
|
esm.getHNOT(mEnabled, "ZNAM");
|
||||||
|
|
||||||
|
// should occur for all references but not levelled creature spawners
|
||||||
|
esm.getHNOT(mPos, "DATA", 24);
|
||||||
|
|
||||||
|
// i've seen DATA record TWICE on a creature record - and with the exact same content too! weird
|
||||||
|
// alarmvoi0000.ess
|
||||||
|
esm.getHNOT(mPos, "DATA", 24);
|
||||||
|
|
||||||
|
if (esm.isNextSub("MVRF"))
|
||||||
|
{
|
||||||
|
esm.skipHSub();
|
||||||
|
esm.getSubName();
|
||||||
|
esm.skipHSub();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_CELLREF_H
|
||||||
|
#define OPENMW_ESSIMPORT_CELLREF_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <components/esm/cellref.hpp>
|
||||||
|
|
||||||
|
#include "importacdt.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
// Not sure if we can share any code with ESM::CellRef here
|
||||||
|
struct CellRef
|
||||||
|
{
|
||||||
|
std::string mIndexedRefId;
|
||||||
|
ESM::RefNum mRefNum;
|
||||||
|
|
||||||
|
ActorData mActorData;
|
||||||
|
|
||||||
|
ESM::Position mPos;
|
||||||
|
|
||||||
|
std::string mScript;
|
||||||
|
|
||||||
|
bool mEnabled;
|
||||||
|
|
||||||
|
void load(ESM::ESMReader& esm);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,20 @@
|
|||||||
|
#include "importcrec.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void CREC::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
esm.getHNT(mIndex, "INDX");
|
||||||
|
|
||||||
|
// equivalent of ESM::Creature XSCL? probably don't have to convert this,
|
||||||
|
// since the value can't be changed
|
||||||
|
float scale;
|
||||||
|
esm.getHNOT(scale, "XSCL");
|
||||||
|
|
||||||
|
mInventory.load(esm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_CREC_H
|
||||||
|
#define OPENMW_ESSIMPORT_CREC_H
|
||||||
|
|
||||||
|
#include "importinventory.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Creature changes
|
||||||
|
struct CREC
|
||||||
|
{
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
Inventory mInventory;
|
||||||
|
|
||||||
|
void load(ESM::ESMReader& esm);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,327 @@
|
|||||||
|
#include "importer.hpp"
|
||||||
|
|
||||||
|
#include <OgreRoot.h>
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
#include <components/esm/esmwriter.hpp>
|
||||||
|
#include <components/esm/defs.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/savedgame.hpp>
|
||||||
|
#include <components/esm/player.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/loadalch.hpp>
|
||||||
|
#include <components/esm/loadclas.hpp>
|
||||||
|
#include <components/esm/loadspel.hpp>
|
||||||
|
#include <components/esm/loadarmo.hpp>
|
||||||
|
#include <components/esm/loadweap.hpp>
|
||||||
|
#include <components/esm/loadclot.hpp>
|
||||||
|
#include <components/esm/loadench.hpp>
|
||||||
|
#include <components/esm/loadweap.hpp>
|
||||||
|
#include <components/esm/loadlevlist.hpp>
|
||||||
|
#include <components/esm/loadglob.hpp>
|
||||||
|
|
||||||
|
#include "importercontext.hpp"
|
||||||
|
|
||||||
|
#include "converter.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
void writeScreenshot(const ESM::Header& fileHeader, ESM::SavedGame& out)
|
||||||
|
{
|
||||||
|
Ogre::Image screenshot;
|
||||||
|
std::vector<unsigned char> screenshotData = fileHeader.mSCRS; // MemoryDataStream doesn't work with const data :(
|
||||||
|
Ogre::DataStreamPtr screenshotStream (new Ogre::MemoryDataStream(&screenshotData[0], screenshotData.size()));
|
||||||
|
screenshot.loadRawData(screenshotStream, 128, 128, 1, Ogre::PF_BYTE_BGRA);
|
||||||
|
Ogre::DataStreamPtr encoded = screenshot.encode("jpg");
|
||||||
|
out.mScreenshot.resize(encoded->size());
|
||||||
|
encoded->read(&out.mScreenshot[0], encoded->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
Importer::Importer(const std::string &essfile, const std::string &outfile)
|
||||||
|
: mEssFile(essfile)
|
||||||
|
, mOutFile(outfile)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct File
|
||||||
|
{
|
||||||
|
struct Subrecord
|
||||||
|
{
|
||||||
|
std::string mName;
|
||||||
|
size_t mFileOffset;
|
||||||
|
std::vector<unsigned char> mData;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Record
|
||||||
|
{
|
||||||
|
std::string mName;
|
||||||
|
size_t mFileOffset;
|
||||||
|
std::vector<Subrecord> mSubrecords;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Record> mRecords;
|
||||||
|
};
|
||||||
|
|
||||||
|
void read(const std::string& filename, File& file)
|
||||||
|
{
|
||||||
|
ESM::ESMReader esm;
|
||||||
|
esm.open(filename);
|
||||||
|
|
||||||
|
while (esm.hasMoreRecs())
|
||||||
|
{
|
||||||
|
ESM::NAME n = esm.getRecName();
|
||||||
|
esm.getRecHeader();
|
||||||
|
|
||||||
|
File::Record rec;
|
||||||
|
rec.mName = n.toString();
|
||||||
|
rec.mFileOffset = esm.getFileOffset();
|
||||||
|
while (esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
File::Subrecord sub;
|
||||||
|
esm.getSubName();
|
||||||
|
esm.getSubHeader();
|
||||||
|
sub.mFileOffset = esm.getFileOffset();
|
||||||
|
sub.mName = esm.retSubName().toString();
|
||||||
|
sub.mData.resize(esm.getSubSize());
|
||||||
|
esm.getExact(&sub.mData[0], sub.mData.size());
|
||||||
|
rec.mSubrecords.push_back(sub);
|
||||||
|
}
|
||||||
|
file.mRecords.push_back(rec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Importer::compare()
|
||||||
|
{
|
||||||
|
// data that always changes (and/or is already fully decoded) should be blacklisted
|
||||||
|
std::set<std::pair<std::string, std::string> > blacklist;
|
||||||
|
blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour
|
||||||
|
blacklist.insert(std::make_pair("REFR", "DATA")); // player position
|
||||||
|
blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war
|
||||||
|
|
||||||
|
File file1;
|
||||||
|
read(mEssFile, file1);
|
||||||
|
File file2;
|
||||||
|
read(mOutFile, file2); // todo rename variable
|
||||||
|
|
||||||
|
// FIXME: use max(size1, size2)
|
||||||
|
for (unsigned int i=0; i<file1.mRecords.size(); ++i)
|
||||||
|
{
|
||||||
|
File::Record rec = file1.mRecords[i];
|
||||||
|
|
||||||
|
if (i >= file2.mRecords.size())
|
||||||
|
{
|
||||||
|
std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File::Record rec2 = file2.mRecords[i];
|
||||||
|
|
||||||
|
if (rec.mName != rec2.mName)
|
||||||
|
{
|
||||||
|
std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl;
|
||||||
|
return; // TODO: try to recover
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: use max(size1, size2)
|
||||||
|
for (unsigned int j=0; j<rec.mSubrecords.size(); ++j)
|
||||||
|
{
|
||||||
|
File::Subrecord sub = rec.mSubrecords[j];
|
||||||
|
|
||||||
|
if (j >= rec2.mSubrecords.size())
|
||||||
|
{
|
||||||
|
std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File::Subrecord sub2 = rec2.mSubrecords[j];
|
||||||
|
|
||||||
|
if (sub.mName != sub2.mName)
|
||||||
|
{
|
||||||
|
std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset
|
||||||
|
<< " (2) 0x" << sub2.mFileOffset << std::endl;
|
||||||
|
break; // TODO: try to recover
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub.mData != sub2.mData)
|
||||||
|
{
|
||||||
|
if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset
|
||||||
|
<< " (2) 0x" << sub2.mFileOffset << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Data 1:" << std::endl;
|
||||||
|
for (unsigned int k=0; k<sub.mData.size(); ++k)
|
||||||
|
{
|
||||||
|
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub.mData[k] << " ";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Data 2:" << std::endl;
|
||||||
|
for (unsigned int k=0; k<sub2.mData.size(); ++k)
|
||||||
|
{
|
||||||
|
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)sub2.mData[k] << " ";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Importer::run()
|
||||||
|
{
|
||||||
|
// construct Ogre::Root to gain access to image codecs
|
||||||
|
Ogre::LogManager logman;
|
||||||
|
Ogre::Root root;
|
||||||
|
|
||||||
|
ESM::ESMReader esm;
|
||||||
|
esm.open(mEssFile);
|
||||||
|
|
||||||
|
Context context;
|
||||||
|
|
||||||
|
const ESM::Header& header = esm.getHeader();
|
||||||
|
context.mPlayerCellName = header.mGameData.mCurrentCell.toString();
|
||||||
|
|
||||||
|
const unsigned int recREFR = ESM::FourCC<'R','E','F','R'>::value;
|
||||||
|
const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value;
|
||||||
|
const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value;
|
||||||
|
const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value;
|
||||||
|
|
||||||
|
std::map<unsigned int, boost::shared_ptr<Converter> > converters;
|
||||||
|
converters[ESM::REC_GLOB] = boost::shared_ptr<Converter>(new ConvertGlobal());
|
||||||
|
converters[ESM::REC_BOOK] = boost::shared_ptr<Converter>(new ConvertBook());
|
||||||
|
converters[ESM::REC_NPC_] = boost::shared_ptr<Converter>(new ConvertNPC());
|
||||||
|
converters[ESM::REC_NPCC] = boost::shared_ptr<Converter>(new ConvertNPCC());
|
||||||
|
converters[ESM::REC_CREC] = boost::shared_ptr<Converter>(new ConvertCREC());
|
||||||
|
converters[recREFR] = boost::shared_ptr<Converter>(new ConvertREFR());
|
||||||
|
converters[recPCDT] = boost::shared_ptr<Converter>(new ConvertPCDT());
|
||||||
|
converters[recFMAP] = boost::shared_ptr<Converter>(new ConvertFMAP());
|
||||||
|
converters[recKLST] = boost::shared_ptr<Converter>(new ConvertKLST());
|
||||||
|
converters[ESM::REC_CELL] = boost::shared_ptr<Converter>(new ConvertCell());
|
||||||
|
converters[ESM::REC_ALCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());
|
||||||
|
converters[ESM::REC_CLAS] = boost::shared_ptr<Converter>(new ConvertClass());
|
||||||
|
converters[ESM::REC_SPEL] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Spell>());
|
||||||
|
converters[ESM::REC_ARMO] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Armor>());
|
||||||
|
converters[ESM::REC_WEAP] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||||
|
converters[ESM::REC_CLOT] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Clothing>());
|
||||||
|
converters[ESM::REC_ENCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Enchantment>());
|
||||||
|
converters[ESM::REC_WEAP] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||||
|
converters[ESM::REC_LEVC] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>());
|
||||||
|
converters[ESM::REC_LEVI] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::ItemLevList>());
|
||||||
|
|
||||||
|
std::set<unsigned int> unknownRecords;
|
||||||
|
|
||||||
|
for (std::map<unsigned int, boost::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||||
|
it != converters.end(); ++it)
|
||||||
|
{
|
||||||
|
it->second->setContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (esm.hasMoreRecs())
|
||||||
|
{
|
||||||
|
ESM::NAME n = esm.getRecName();
|
||||||
|
esm.getRecHeader();
|
||||||
|
|
||||||
|
std::map<unsigned int, boost::shared_ptr<Converter> >::iterator it = converters.find(n.val);
|
||||||
|
if (it != converters.end())
|
||||||
|
{
|
||||||
|
it->second->read(esm);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (unknownRecords.insert(n.val).second)
|
||||||
|
std::cerr << "unknown record " << n.toString() << std::endl;
|
||||||
|
|
||||||
|
esm.skipRecord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::ESMWriter writer;
|
||||||
|
|
||||||
|
writer.setFormat (ESM::Header::CurrentFormat);
|
||||||
|
|
||||||
|
std::ofstream stream(mOutFile.c_str(), std::ios::binary);
|
||||||
|
// all unused
|
||||||
|
writer.setVersion(0);
|
||||||
|
writer.setType(0);
|
||||||
|
writer.setAuthor("");
|
||||||
|
writer.setDescription("");
|
||||||
|
writer.setRecordCount (0);
|
||||||
|
|
||||||
|
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin();
|
||||||
|
it != header.mMaster.end(); ++it)
|
||||||
|
writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0
|
||||||
|
|
||||||
|
writer.save (stream);
|
||||||
|
|
||||||
|
ESM::SavedGame profile;
|
||||||
|
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin();
|
||||||
|
it != header.mMaster.end(); ++it)
|
||||||
|
{
|
||||||
|
profile.mContentFiles.push_back(it->name);
|
||||||
|
}
|
||||||
|
profile.mDescription = esm.getDesc();
|
||||||
|
profile.mInGameTime.mDay = context.mDay;
|
||||||
|
profile.mInGameTime.mGameHour = context.mHour;
|
||||||
|
profile.mInGameTime.mMonth = context.mMonth;
|
||||||
|
profile.mInGameTime.mYear = context.mYear;
|
||||||
|
profile.mPlayerCell = header.mGameData.mCurrentCell.toString();
|
||||||
|
if (context.mPlayerBase.mClass == "NEWCLASSID_CHARGEN")
|
||||||
|
profile.mPlayerClassName = context.mCustomPlayerClassName;
|
||||||
|
else
|
||||||
|
profile.mPlayerClassId = context.mPlayerBase.mClass;
|
||||||
|
profile.mPlayerLevel = context.mPlayerBase.mNpdt52.mLevel;
|
||||||
|
profile.mPlayerName = header.mGameData.mPlayerName.toString();
|
||||||
|
|
||||||
|
writeScreenshot(header, profile);
|
||||||
|
|
||||||
|
writer.startRecord (ESM::REC_SAVE);
|
||||||
|
profile.save (writer);
|
||||||
|
writer.endRecord (ESM::REC_SAVE);
|
||||||
|
|
||||||
|
// Writing order should be Dynamic Store -> Cells -> Player,
|
||||||
|
// so that references to dynamic records can be recognized when loading
|
||||||
|
for (std::map<unsigned int, boost::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||||
|
it != converters.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->second->getStage() != 0)
|
||||||
|
continue;
|
||||||
|
it->second->write(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.startRecord(ESM::REC_NPC_);
|
||||||
|
writer.writeHNString("NAME", "player");
|
||||||
|
context.mPlayerBase.save(writer);
|
||||||
|
writer.endRecord(ESM::REC_NPC_);
|
||||||
|
|
||||||
|
for (std::map<unsigned int, boost::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||||
|
it != converters.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->second->getStage() != 1)
|
||||||
|
continue;
|
||||||
|
it->second->write(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.startRecord(ESM::REC_PLAY);
|
||||||
|
if (context.mPlayer.mCellId.mPaged)
|
||||||
|
{
|
||||||
|
// exterior cell -> determine cell coordinates based on position
|
||||||
|
const int cellSize = 8192;
|
||||||
|
int cellX = std::floor(context.mPlayer.mObject.mPosition.pos[0]/cellSize);
|
||||||
|
int cellY = std::floor(context.mPlayer.mObject.mPosition.pos[1]/cellSize);
|
||||||
|
context.mPlayer.mCellId.mIndex.mX = cellX;
|
||||||
|
context.mPlayer.mCellId.mIndex.mY = cellY;
|
||||||
|
}
|
||||||
|
context.mPlayer.save(writer);
|
||||||
|
writer.endRecord(ESM::REC_PLAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORTER_IMPORTER_H
|
||||||
|
#define OPENMW_ESSIMPORTER_IMPORTER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
class Importer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Importer(const std::string& essfile, const std::string& outfile);
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
void compare();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mEssFile;
|
||||||
|
std::string mOutFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,51 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_CONTEXT_H
|
||||||
|
#define OPENMW_ESSIMPORT_CONTEXT_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <components/esm/loadnpc.hpp>
|
||||||
|
#include <components/esm/player.hpp>
|
||||||
|
|
||||||
|
#include "importnpcc.hpp"
|
||||||
|
#include "importcrec.hpp"
|
||||||
|
#include "importplayer.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Context
|
||||||
|
{
|
||||||
|
// set from the TES3 header
|
||||||
|
std::string mPlayerCellName;
|
||||||
|
|
||||||
|
ESM::Player mPlayer;
|
||||||
|
ESM::NPC mPlayerBase;
|
||||||
|
std::string mCustomPlayerClassName;
|
||||||
|
|
||||||
|
int mDay, mMonth, mYear;
|
||||||
|
float mHour;
|
||||||
|
|
||||||
|
// key <refIndex, refId>
|
||||||
|
std::map<std::pair<int, std::string>, CREC> mCreatureChanges;
|
||||||
|
std::map<std::pair<int, std::string>, NPCC> mNpcChanges;
|
||||||
|
|
||||||
|
Context()
|
||||||
|
{
|
||||||
|
mPlayer.mAutoMove = 0;
|
||||||
|
ESM::CellId playerCellId;
|
||||||
|
playerCellId.mPaged = true;
|
||||||
|
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
|
||||||
|
mPlayer.mCellId = playerCellId;
|
||||||
|
//mPlayer.mLastKnownExteriorPosition
|
||||||
|
mPlayer.mHasMark = 0; // TODO
|
||||||
|
mPlayer.mCurrentCrimeId = 0; // TODO
|
||||||
|
mPlayer.mObject.blank();
|
||||||
|
mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,46 @@
|
|||||||
|
#include "importinventory.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void Inventory::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
while (esm.isNextSub("NPCO"))
|
||||||
|
{
|
||||||
|
InventoryItem item;
|
||||||
|
item.mId = esm.getHString();
|
||||||
|
|
||||||
|
if (esm.isNextSub("XIDX"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
std::string script = esm.getHNOString("SCRI");
|
||||||
|
// script variables?
|
||||||
|
// unsure if before or after ESM::CellRef
|
||||||
|
if (!script.empty())
|
||||||
|
{
|
||||||
|
if (esm.isNextSub("SLCS"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("SLSD")) // Short Data?
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("SLFD")) // Float Data?
|
||||||
|
esm.skipHSub();
|
||||||
|
}
|
||||||
|
|
||||||
|
// for XSOL and XCHG seen so far, but probably others too
|
||||||
|
item.ESM::CellRef::loadData(esm);
|
||||||
|
|
||||||
|
item.mCondition = -1;
|
||||||
|
esm.getHNOT(item.mCondition, "XHLT");
|
||||||
|
mItems.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (esm.isNextSub("WIDX"))
|
||||||
|
{
|
||||||
|
// equipping?
|
||||||
|
esm.skipHSub();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_IMPORTINVENTORY_H
|
||||||
|
#define OPENMW_ESSIMPORT_IMPORTINVENTORY_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <components/esm/cellref.hpp>
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Inventory
|
||||||
|
{
|
||||||
|
struct InventoryItem : public ESM::CellRef
|
||||||
|
{
|
||||||
|
std::string mId;
|
||||||
|
int mCondition;
|
||||||
|
};
|
||||||
|
std::vector<InventoryItem> mItems;
|
||||||
|
|
||||||
|
void load(ESM::ESMReader& esm);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,22 @@
|
|||||||
|
#include "importklst.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void KLST::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
while (esm.isNextSub("KNAM"))
|
||||||
|
{
|
||||||
|
std::string refId = esm.getHString();
|
||||||
|
int count;
|
||||||
|
esm.getHNT(count, "CNAM");
|
||||||
|
mKillCounter[refId] = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWerewolfKills = 0;
|
||||||
|
esm.getHNOT(mWerewolfKills, "INTV");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_KLST_H
|
||||||
|
#define OPENMW_ESSIMPORT_KLST_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Kill Stats
|
||||||
|
struct KLST
|
||||||
|
{
|
||||||
|
void load(ESM::ESMReader& esm);
|
||||||
|
|
||||||
|
/// RefId, kill count
|
||||||
|
std::map<std::string, int> mKillCounter;
|
||||||
|
|
||||||
|
int mWerewolfKills;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,25 @@
|
|||||||
|
#include "importnpcc.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void NPCC::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
mIndex = 0;
|
||||||
|
esm.getHNOT(mIndex, "INDX");
|
||||||
|
|
||||||
|
esm.getHNT(mNPDT, "NPDT");
|
||||||
|
|
||||||
|
if (esm.isNextSub("AI_E"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("AI_T"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("AI_F"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
mInventory.load(esm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_NPCC_H
|
||||||
|
#define OPENMW_ESSIMPORT_NPCC_H
|
||||||
|
|
||||||
|
#include <components/esm/loadcont.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/aipackage.hpp>
|
||||||
|
|
||||||
|
#include "importinventory.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
struct NPCC
|
||||||
|
{
|
||||||
|
struct NPDT
|
||||||
|
{
|
||||||
|
unsigned char unknown[2];
|
||||||
|
unsigned char mReputation;
|
||||||
|
unsigned char unknown2[5];
|
||||||
|
} mNPDT;
|
||||||
|
|
||||||
|
Inventory mInventory;
|
||||||
|
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
void load(ESM::ESMReader &esm);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,75 @@
|
|||||||
|
#include "importplayer.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
void REFR::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
esm.getHNT(mRefNum.mIndex, "FRMR");
|
||||||
|
|
||||||
|
mRefID = esm.getHNString("NAME");
|
||||||
|
|
||||||
|
mActorData.load(esm);
|
||||||
|
|
||||||
|
esm.getHNOT(mPos, "DATA", 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PCDT::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
while (esm.isNextSub("DNAM"))
|
||||||
|
{
|
||||||
|
// TODO: deal with encoding?
|
||||||
|
mKnownDialogueTopics.push_back(esm.getHString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esm.isNextSub("PNAM"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("SNAM"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("NAM9"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
mBounty = 0;
|
||||||
|
esm.getHNOT(mBounty, "CNAM");
|
||||||
|
|
||||||
|
mBirthsign = esm.getHNOString("BNAM");
|
||||||
|
|
||||||
|
// Holds the names of the last used Alchemy apparatus. Don't need to import this ATM,
|
||||||
|
// because our GUI auto-selects the best apparatus.
|
||||||
|
if (esm.isNextSub("NAM0"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("NAM1"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("NAM2"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("NAM3"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
if (esm.isNextSub("ENAM"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
if (esm.isNextSub("LNAM"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
while (esm.isNextSub("FNAM"))
|
||||||
|
{
|
||||||
|
FNAM fnam;
|
||||||
|
esm.getHT(fnam);
|
||||||
|
mFactions.push_back(fnam);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esm.isNextSub("KNAM"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
if (esm.isNextSub("WERE"))
|
||||||
|
{
|
||||||
|
// some werewolf data, 152 bytes
|
||||||
|
// maybe current skills and attributes for werewolf form
|
||||||
|
esm.getSubHeader();
|
||||||
|
esm.skip(152);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef OPENMW_ESSIMPORT_PLAYER_H
|
||||||
|
#define OPENMW_ESSIMPORT_PLAYER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <components/esm/defs.hpp>
|
||||||
|
#include <components/esm/cellref.hpp>
|
||||||
|
#include <components/esm/esmcommon.hpp>
|
||||||
|
|
||||||
|
#include "importacdt.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESSImport
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Player-agnostic player data
|
||||||
|
struct REFR
|
||||||
|
{
|
||||||
|
ActorData mActorData;
|
||||||
|
|
||||||
|
std::string mRefID;
|
||||||
|
ESM::Position mPos;
|
||||||
|
ESM::RefNum mRefNum;
|
||||||
|
|
||||||
|
void load(ESM::ESMReader& esm);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Other player data
|
||||||
|
struct PCDT
|
||||||
|
{
|
||||||
|
int mBounty;
|
||||||
|
std::string mBirthsign;
|
||||||
|
|
||||||
|
std::vector<std::string> mKnownDialogueTopics;
|
||||||
|
|
||||||
|
struct FNAM
|
||||||
|
{
|
||||||
|
unsigned char mRank;
|
||||||
|
unsigned char mUnknown1[3];
|
||||||
|
int mReputation;
|
||||||
|
unsigned char mFlags; // 0x1: unknown, 0x2: expelled
|
||||||
|
unsigned char mUnknown2[3];
|
||||||
|
ESM::NAME32 mFactionName;
|
||||||
|
};
|
||||||
|
std::vector<FNAM> mFactions;
|
||||||
|
|
||||||
|
void load(ESM::ESMReader& esm);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,60 @@
|
|||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
|
#include "importer.hpp"
|
||||||
|
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
namespace bfs = boost::filesystem;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, const char** argv)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bpo::options_description desc("Syntax: openmw-essimporter <options> infile.ess outfile.omwsave\nAllowed options");
|
||||||
|
bpo::positional_options_description p_desc;
|
||||||
|
desc.add_options()
|
||||||
|
("help,h", "produce help message")
|
||||||
|
("mwsave,m", bpo::value<std::string>(), "morrowind .ess save file")
|
||||||
|
("output,o", bpo::value<std::string>(), "output file (.omwsave)")
|
||||||
|
("compare,c", "compare two .ess files")
|
||||||
|
;
|
||||||
|
p_desc.add("mwsave", 1).add("output", 1);
|
||||||
|
|
||||||
|
bpo::variables_map vm;
|
||||||
|
|
||||||
|
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
|
||||||
|
.options(desc)
|
||||||
|
.positional(p_desc)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
bpo::store(parsed, vm);
|
||||||
|
|
||||||
|
if(vm.count("help") || !vm.count("mwsave") || !vm.count("output")) {
|
||||||
|
std::cout << desc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpo::notify(vm);
|
||||||
|
|
||||||
|
std::string essFile = vm["mwsave"].as<std::string>();
|
||||||
|
std::string outputFile = vm["output"].as<std::string>();
|
||||||
|
|
||||||
|
ESSImport::Importer importer(essFile, outputFile);
|
||||||
|
|
||||||
|
if (vm.count("compare"))
|
||||||
|
importer.compare();
|
||||||
|
else
|
||||||
|
importer.run();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
#include "custommarkerstate.hpp"
|
||||||
|
|
||||||
|
#include "esmwriter.hpp"
|
||||||
|
#include "esmreader.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void CustomMarker::save(ESM::ESMWriter &esm) const
|
||||||
|
{
|
||||||
|
esm.writeHNT("POSX", mWorldX);
|
||||||
|
esm.writeHNT("POSY", mWorldY);
|
||||||
|
mCell.save(esm);
|
||||||
|
if (!mNote.empty())
|
||||||
|
esm.writeHNString("NOTE", mNote);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomMarker::load(ESM::ESMReader &esm)
|
||||||
|
{
|
||||||
|
esm.getHNT(mWorldX, "POSX");
|
||||||
|
esm.getHNT(mWorldY, "POSY");
|
||||||
|
mCell.load(esm);
|
||||||
|
mNote = esm.getHNOString("NOTE");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef OPENMW_ESM_CUSTOMMARKERSTATE_H
|
||||||
|
#define OPENMW_ESM_CUSTOMMARKERSTATE_H
|
||||||
|
|
||||||
|
#include "cellid.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
// format 0, saved games only
|
||||||
|
struct CustomMarker
|
||||||
|
{
|
||||||
|
float mWorldX;
|
||||||
|
float mWorldY;
|
||||||
|
|
||||||
|
ESM::CellId mCell;
|
||||||
|
|
||||||
|
std::string mNote;
|
||||||
|
|
||||||
|
bool operator == (const CustomMarker& other)
|
||||||
|
{
|
||||||
|
return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load (ESM::ESMReader& reader);
|
||||||
|
void save (ESM::ESMWriter& writer) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue