Merge pull request #17 from cc9cii/TES4-ESM

TES4 ESM
pull/1955/head
cc9cii 6 years ago committed by GitHub
commit 6d38b5ae48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -580,6 +580,7 @@ add_subdirectory (extern/shiny)
add_subdirectory (extern/ogre-ffmpeg-videoplayer)
add_subdirectory (extern/oics)
add_subdirectory (extern/sdl4ogre)
add_subdirectory (extern/esm4)
add_subdirectory (extern/murmurhash)
add_subdirectory (extern/BSAOpt)

@ -127,9 +127,28 @@ Enhancements for both OpenMW and OpenCS:
* Hash based lookup for TES3 BSA files.
* TES4/TES5 BSA support.
* Experimental support of loading TES4/TES5 records (coming soon).
* Loading TES4/TES5 records (incomplete).
* Experimental support of using multiple versions of ESM files concurrently in OpenMW (coming soon)
* Experimental support of loading newer NIF records (coming soon).
* Experimental support of NavMesh (eventually).
openmw.cfg example
------------------
...
fallback-archive=Morrowind.bsa
fallback-archive=Tribunal.bsa
fallback-archive=Bloodmoon.bsa
fallback-archive=TR_Data.bsa
fallback-tes4archive=Oblivion - Meshes.bsa
#fallback-tes4archive=Skyrim - Textures.bsa
#fallback-tes4archive=Dragonborn.bsa
#fallback-tes4archive=Dawnguard.bsa
...
data="C:/Program Files (x86)/Bethesda Softworks/Morrowind/Data Files"
data="C:/Program Files (x86)/Bethesda Softworks/Oblivion/Data"
...
Build Dependencies
------------------

@ -211,6 +211,7 @@ target_link_libraries(openmw-cs
${OGRE_Overlay_LIBRARIES}
${OGRE_STATIC_PLUGINS}
${SHINY_LIBRARIES}
${ESM4_LIBRARIES}
${ZLIB_LIBRARY}
${MURMURHASH_LIBRARIES}
${BSAOPTHASH_LIBRARIES}

@ -8,10 +8,13 @@
#include <QAbstractItemModel>
#include <components/esm/esmreader.hpp>
#include <components/esm/esm4reader.hpp>
#include <components/esm/defs.hpp>
#include <components/esm/loadglob.hpp>
#include <components/esm/cellref.hpp>
#include <extern/esm4/common.hpp>
#include "idtable.hpp"
#include "idtree.hpp"
#include "columnimp.hpp"
@ -933,9 +936,24 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base
mReader = new ESM::ESMReader;
mReader->setEncoder (&mEncoder);
mReader->setIndex(mReaderIndex++);
mReader->setIndex(mReaderIndex++); // NOTE: auto increment
mReader->open (path.string());
int esmVer = mReader->getVer();
bool isTes4 = esmVer == ESM::VER_080 || esmVer == ESM::VER_100;
bool isTes5 = esmVer == ESM::VER_094 || esmVer == ESM::VER_17;
bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134;
if (isTes4 || isTes5 || isFONV)
{
mReader->close();
delete mReader;
mReader = new ESM::ESM4Reader(isTes4); // TES4 headers are 4 bytes shorter
mReader->setEncoder(&mEncoder);
mReader->setIndex(mReaderIndex-1); // use the same index
static_cast<ESM::ESM4Reader*>(mReader)->reader().setModIndex(mReaderIndex-1);
static_cast<ESM::ESM4Reader*>(mReader)->openTes4File(path.string());
static_cast<ESM::ESM4Reader*>(mReader)->reader().updateModIndicies(mLoadedFiles);
}
mLoadedFiles.push_back(path.filename().string());
// at this point mReader->mHeader.mMaster have been populated for the file being loaded
@ -986,6 +1004,16 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
if (!mReader)
throw std::logic_error ("can't continue loading, because no load has been started");
int esmVer = mReader->getVer();
bool isTes4 = esmVer == ESM::VER_080 || esmVer == ESM::VER_100;
bool isTes5 = esmVer == ESM::VER_094 || esmVer == ESM::VER_17;
bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134;
// Check if previous record/group was the final one in this group. Must be done before
// calling mReader->hasMoreRecs() below, because all records may have been processed when
// the previous group is popped off the stack.
if (isTes4 || isTes5 || isFONV)
static_cast<ESM::ESM4Reader*>(mReader)->reader().checkGroupStatus();
if (!mReader->hasMoreRecs())
{
if (mBase)
@ -1005,6 +1033,9 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
return true;
}
if (isTes4 || isTes5 || isFONV)
return loadTes4Group(messages);
ESM::NAME n = mReader->getRecName();
mReader->getRecHeader();
@ -1278,3 +1309,127 @@ const CSMWorld::Data& CSMWorld::Data::self ()
{
return *this;
}
bool CSMWorld::Data::loadTes4Group (CSMDoc::Messages& messages)
{
ESM4::Reader& reader = static_cast<ESM::ESM4Reader*>(mReader)->reader();
// check for EOF, sometimes there is a empty group at the end e.g. FONV DeadMoney.esm
if (!reader.getRecordHeader() || !mReader->hasMoreRecs())
return false;
const ESM4::RecordHeader& hdr = reader.hdr();
if (hdr.record.typeId != ESM4::REC_GRUP)
return loadTes4Record(hdr, messages);
// Skip groups that are of no interest. See also:
// http://www.uesp.net/wiki/Tes4Mod:Mod_File_Format#Hierarchical_Top_Groups
switch (hdr.group.type)
{
case ESM4::Grp_RecordType:
{
// FIXME: rewrite to workaround reliability issue
if (hdr.group.label.value == ESM4::REC_NAVI || hdr.group.label.value == ESM4::REC_WRLD ||
hdr.group.label.value == ESM4::REC_REGN || hdr.group.label.value == ESM4::REC_STAT ||
hdr.group.label.value == ESM4::REC_ANIO || hdr.group.label.value == ESM4::REC_CONT ||
hdr.group.label.value == ESM4::REC_MISC || hdr.group.label.value == ESM4::REC_ACTI ||
hdr.group.label.value == ESM4::REC_ARMO || hdr.group.label.value == ESM4::REC_NPC_ ||
hdr.group.label.value == ESM4::REC_FLOR || hdr.group.label.value == ESM4::REC_GRAS ||
hdr.group.label.value == ESM4::REC_TREE || hdr.group.label.value == ESM4::REC_LIGH ||
hdr.group.label.value == ESM4::REC_BOOK || hdr.group.label.value == ESM4::REC_FURN ||
hdr.group.label.value == ESM4::REC_SOUN || hdr.group.label.value == ESM4::REC_WEAP ||
hdr.group.label.value == ESM4::REC_DOOR || hdr.group.label.value == ESM4::REC_AMMO ||
hdr.group.label.value == ESM4::REC_CLOT || hdr.group.label.value == ESM4::REC_ALCH ||
hdr.group.label.value == ESM4::REC_APPA || hdr.group.label.value == ESM4::REC_INGR ||
hdr.group.label.value == ESM4::REC_SGST || hdr.group.label.value == ESM4::REC_SLGM ||
hdr.group.label.value == ESM4::REC_KEYM || hdr.group.label.value == ESM4::REC_HAIR ||
hdr.group.label.value == ESM4::REC_EYES || hdr.group.label.value == ESM4::REC_CELL ||
hdr.group.label.value == ESM4::REC_CREA || hdr.group.label.value == ESM4::REC_LVLC ||
hdr.group.label.value == ESM4::REC_LVLI || hdr.group.label.value == ESM4::REC_MATO ||
hdr.group.label.value == ESM4::REC_IDLE || hdr.group.label.value == ESM4::REC_LTEX
)
{
// NOTE: The label field of a group is not reliable. See:
// http://www.uesp.net/wiki/Tes4Mod:Mod_File_Format
//
// ASCII Q 0x51 0101 0001
// A 0x41 0100 0001
//
// Ignore flag 0000 1000 (i.e. probably unrelated)
//
// Workaround by getting the record header and checking its typeId
reader.saveGroupStatus();
// FIXME: comment may no longer be releavant
loadTes4Group(messages); // CELL group with record type may have sub-groups
}
else
{
//std::cout << "Skipping group... " // FIXME: testing only
//<< ESM4::printLabel(hdr.group.label, hdr.group.type) << std::endl;
reader.skipGroup();
return false;
}
break;
}
case ESM4::Grp_CellChild:
{
reader.adjustGRUPFormId(); // not needed or even shouldn't be done? (only labels anyway)
reader.saveGroupStatus();
if (!mReader->hasMoreRecs())
return false; // may have been an empty group followed by EOF
loadTes4Group(messages);
break;
}
case ESM4::Grp_WorldChild:
case ESM4::Grp_TopicChild:
// FIXME: need to save context if skipping
case ESM4::Grp_CellPersistentChild:
case ESM4::Grp_CellTemporaryChild:
case ESM4::Grp_CellVisibleDistChild:
{
reader.adjustGRUPFormId(); // not needed or even shouldn't be done? (only labels anyway)
reader.saveGroupStatus();
if (!mReader->hasMoreRecs())
return false; // may have been an empty group followed by EOF
loadTes4Group(messages);
break;
}
case ESM4::Grp_ExteriorCell:
case ESM4::Grp_ExteriorSubCell:
case ESM4::Grp_InteriorCell:
case ESM4::Grp_InteriorSubCell:
{
reader.saveGroupStatus();
loadTes4Group(messages);
break;
}
default:
break;
}
return false;
}
// Deal with Tes4 records separately, as some have the same name as Tes3, e.g. REC_CELL
bool CSMWorld::Data::loadTes4Record (const ESM4::RecordHeader& hdr, CSMDoc::Messages& messages)
{
ESM4::Reader& reader = static_cast<ESM::ESM4Reader*>(mReader)->reader();
switch (hdr.record.typeId)
{
// FIXME: removed for now
default:
reader.skipRecordData();
}
return false;
}

@ -57,6 +57,11 @@ namespace ESM
struct Dialogue;
}
namespace ESM4
{
union RecordHeader;
}
namespace CSMWorld
{
class ResourcesManager;
@ -127,6 +132,9 @@ namespace CSMWorld
const Data& self ();
bool loadTes4Group (CSMDoc::Messages& messages);
bool loadTes4Record (const ESM4::RecordHeader& hdr, CSMDoc::Messages& messages);
public:
Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager);

@ -117,6 +117,7 @@ target_link_libraries(openmw
${OGRE_LIBRARIES}
${OGRE_STATIC_PLUGINS}
${SHINY_LIBRARIES}
${ESM4_LIBRARIES}
${BSAOPTHASH_LIBRARIES}
${ZLIB_LIBRARY}
${OPENAL_LIBRARY}

@ -134,7 +134,7 @@ namespace MWBase
virtual const MWWorld::ESMStore& getStore() const = 0;
virtual std::vector<ESM::ESMReader>& getEsmReader() = 0;
virtual std::vector<ESM::ESMReader*>& getEsmReader() = 0;
virtual MWWorld::LocalScripts& getLocalScripts() = 0;

@ -83,7 +83,7 @@ void MWWorld::Cells::writeCell (ESM::ESMWriter& writer, CellStore& cell) const
writer.endRecord (ESM::REC_CSTA);
}
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<std::vector<ESM::ESMReader*> >& reader)
: mStore (store), mReader (reader),
mIdCache (40, std::pair<std::string, CellStore *> ("", (CellStore*)0)), /// \todo make cache size configurable
mIdCacheIndex (0)

@ -28,7 +28,7 @@ namespace MWWorld
class Cells
{
const MWWorld::ESMStore& mStore;
std::vector<ESM::ESMReader>& mReader;
std::vector<std::vector<ESM::ESMReader*> >& mReader;
mutable std::map<std::string, CellStore> mInteriors;
mutable std::map<std::pair<int, int>, CellStore> mExteriors;
std::vector<std::pair<std::string, CellStore *> > mIdCache;
@ -47,7 +47,7 @@ namespace MWWorld
void clear();
Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader);
Cells (const MWWorld::ESMStore& store, std::vector<std::vector<ESM::ESMReader*> >& reader);
CellStore *getExterior (int x, int y);

@ -400,7 +400,7 @@ namespace MWWorld
+ mNpcs.mList.size();
}
void CellStore::load (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
void CellStore::load (const MWWorld::ESMStore &store, std::vector<std::vector<ESM::ESMReader*> > &esm)
{
if (mState!=State_Loaded)
{
@ -417,7 +417,7 @@ namespace MWWorld
}
}
void CellStore::preload (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
void CellStore::preload (const MWWorld::ESMStore &store, std::vector<std::vector<ESM::ESMReader*> > &esm)
{
if (mState==State_Unloaded)
{
@ -427,7 +427,7 @@ namespace MWWorld
}
}
void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector<std::vector<ESM::ESMReader*> > &esm)
{
assert (mCell);
@ -439,13 +439,13 @@ namespace MWWorld
{
// Reopen the ESM reader and seek to the right position.
int index = mCell->mContextList.at(i).index;
mCell->restore (esm[index], i);
mCell->restore (*esm[0][index], (int)i); // FIXME: hardcoded 0 means TES3
ESM::CellRef ref;
// Get each reference in turn
bool deleted = false;
while (mCell->getNextRef (esm[index], ref, deleted))
while (mCell->getNextRef (*esm[0][index], ref, deleted)) // FIXME hardcoded 0 means TES3
{
if (deleted)
continue;
@ -472,7 +472,7 @@ namespace MWWorld
std::sort (mIds.begin(), mIds.end());
}
void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector<std::vector<ESM::ESMReader*> > &esm)
{
assert (mCell);
@ -484,14 +484,14 @@ namespace MWWorld
{
// Reopen the ESM reader and seek to the right position.
int index = mCell->mContextList.at(i).index;
mCell->restore (esm[index], i);
mCell->restore (*esm[0][index], (int)i); // FIXME: hardcoded 0 means TES3
ESM::CellRef ref;
ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile;
// Get each reference in turn
bool deleted = false;
while(mCell->getNextRef(esm[index], ref, deleted))
while(mCell->getNextRef(*esm[0][index], ref, deleted)) // FIXME: 0 means TES3
{
// Don't load reference if it was moved to a different cell.
ESM::MovedCellRefTracker::const_iterator iter =

@ -112,10 +112,10 @@ namespace MWWorld
int count() const;
///< Return total number of references, including deleted ones.
void load (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
void load (const MWWorld::ESMStore &store, std::vector<std::vector<ESM::ESMReader*> > &esm);
///< Load references from content file.
void preload (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
void preload (const MWWorld::ESMStore &store, std::vector<std::vector<ESM::ESMReader*> > &esm);
///< Build ID list from content file.
/// Call functor (ref) for each reference. functor must return a bool. Returning
@ -213,9 +213,9 @@ namespace MWWorld
}
/// Run through references and store IDs
void listRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
void listRefs(const MWWorld::ESMStore &store, std::vector<std::vector<ESM::ESMReader*> > &esm);
void loadRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
void loadRefs(const MWWorld::ESMStore &store, std::vector<std::vector<ESM::ESMReader*> > &esm);
void loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store);
///< Make case-adjustments to \a ref and insert it into the respective container.

@ -21,7 +21,7 @@ struct ContentLoader
{
}
virtual void load(const boost::filesystem::path& filepath, int& index)
virtual void load(const boost::filesystem::path& filepath, std::vector<std::vector<std::string> >& contentFiles)
{
std::cout << "Loading content file " << filepath.string() << std::endl;
mListener.setLabel(filepath.string());

@ -2,11 +2,12 @@
#include "esmstore.hpp"
#include <components/esm/esmreader.hpp>
#include <components/esm/esm4reader.hpp>
namespace MWWorld
{
EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<std::vector<ESM::ESMReader*> >& readers,
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener)
: ContentLoader(listener)
, mEsm(readers)
@ -15,17 +16,60 @@ EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& read
{
}
void EsmLoader::load(const boost::filesystem::path& filepath, int& index)
// FIXME: tesVerIndex stuff is rather clunky, needs to be refactored
void EsmLoader::load(const boost::filesystem::path& filepath, std::vector<std::vector<std::string> >& contentFiles)
{
ContentLoader::load(filepath.filename(), index);
ESM::ESMReader lEsm;
lEsm.setEncoder(mEncoder);
lEsm.setIndex(index);
lEsm.setGlobalReaderList(&mEsm);
lEsm.open(filepath.string());
mEsm[index] = lEsm;
mStore.load(mEsm[index], &mListener);
int tesVerIndex = 0; // FIXME: hard coded, 0 = MW, 1 = TES4, 2 = TES5 (TODO: Fallout)
int index = 0;
ContentLoader::load(filepath.filename(), contentFiles); // set the label on the loading bar
ESM::ESMReader *lEsm = new ESM::ESMReader();
lEsm->setEncoder(mEncoder);
lEsm->setGlobalReaderList(&mEsm[tesVerIndex]); // global reader list is used by ESMStore::load only
lEsm->open(filepath.string());
int esmVer = lEsm->getVer();
bool isTes4 = esmVer == ESM::VER_080 || esmVer == ESM::VER_100;
bool isTes5 = esmVer == ESM::VER_094 || esmVer == ESM::VER_17;
bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134;
if (isTes4 || isTes5 || isFONV)
{
if (isTes4)
tesVerIndex = 1;
else if (isTes5)
tesVerIndex = 2;
else
tesVerIndex = 3;
lEsm->close();
delete lEsm;
ESM::ESM4Reader *esm = new ESM::ESM4Reader(isTes4); // NOTE: TES4 headers are 4 bytes shorter
esm->setEncoder(mEncoder);
index = contentFiles[tesVerIndex].size();
contentFiles[tesVerIndex].push_back(filepath.filename().string());
esm->setIndex(index);
esm->reader().setModIndex(index);
esm->openTes4File(filepath.string());
esm->reader().updateModIndicies(contentFiles[tesVerIndex]);
// FIXME: this does not work well (copies the base class pointer)
//i.e. have to check TES4/TES5 versions each time before use within EsmStore::load,
//static casting as required
mEsm[tesVerIndex].push_back(esm);
}
else
{
tesVerIndex = 0; // 0 = MW
index = contentFiles[tesVerIndex].size();
contentFiles[tesVerIndex].push_back(filepath.filename().string());
lEsm->setIndex(index);
mEsm[tesVerIndex].push_back(lEsm);
}
mStore.load(*mEsm[tesVerIndex][index], &mListener);
}
} /* namespace MWWorld */

@ -22,15 +22,15 @@ class ESMStore;
struct EsmLoader : public ContentLoader
{
EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers,
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener);
EsmLoader(MWWorld::ESMStore& store, std::vector<std::vector<ESM::ESMReader*> >& readers,
ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener);
void load(const boost::filesystem::path& filepath, int& index);
void load(const boost::filesystem::path& filepath, std::vector<std::vector<std::string> >& contentFiles);
private:
std::vector<ESM::ESMReader>& mEsm;
MWWorld::ESMStore& mStore;
ToUTF8::Utf8Encoder* mEncoder;
std::vector<std::vector<ESM::ESMReader*> >& mEsm; // Note: the ownership of the readers is with the caller
MWWorld::ESMStore& mStore;
ToUTF8::Utf8Encoder* mEncoder;
};
} /* namespace MWWorld */

@ -8,6 +8,8 @@
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esm4reader.hpp>
namespace MWWorld
{
@ -31,43 +33,67 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
ESM::Dialogue *dialogue = 0;
// Land texture loading needs to use a separate internal store for each plugin.
// We set the number of plugins here to avoid continual resizes during loading,
// and so we can properly verify if valid plugin indices are being passed to the
// LandTexture Store retrieval methods.
mLandTextures.resize(esm.getGlobalReaderList()->size());
/// \todo Move this to somewhere else. ESMReader?
// Cache parent esX files by tracking their indices in the global list of
// all files/readers used by the engine. This will greaty accelerate
// refnumber mangling, as required for handling moved references.
const std::vector<ESM::Header::MasterData> &masters = esm.getGameFiles();
std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList();
for (size_t j = 0; j < masters.size(); j++) {
ESM::Header::MasterData &mast = const_cast<ESM::Header::MasterData&>(masters[j]);
std::string fname = mast.name;
int index = ~0;
for (int i = 0; i < esm.getIndex(); i++) {
const std::string &candidate = allPlugins->at(i).getContext().filename;
std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();
if (Misc::StringUtils::ciEqual(fname, fnamecandidate)) {
index = i;
break;
int esmVer = esm.getVer();
bool isTes4 = esmVer == ESM::VER_080 || esmVer == ESM::VER_100;
bool isTes5 = esmVer == ESM::VER_094 || esmVer == ESM::VER_17;
bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134;
// FIXME: temporary workaround
if (!(isTes4 || isTes5 || isFONV)) // MW only
{
// Land texture loading needs to use a separate internal store for each plugin.
// We set the number of plugins here to avoid continual resizes during loading,
// and so we can properly verify if valid plugin indices are being passed to the
// LandTexture Store retrieval methods.
mLandTextures.resize(esm.getGlobalReaderList()->size()); // FIXME: size should be for MW only
}
// FIXME: for TES4/TES5 whether a dependent file is loaded is already checked in
// ESM4::Reader::updateModIndicies() which is called in EsmLoader::load() before this
if (!(isTes4 || isTes5 || isFONV)) // MW only
{
/// \todo Move this to somewhere else. ESMReader?
// Cache parent esX files by tracking their indices in the global list of
// all files/readers used by the engine. This will greaty accelerate
// refnumber mangling, as required for handling moved references.
const std::vector<ESM::Header::MasterData> &masters = esm.getGameFiles();
std::vector<ESM::ESMReader*> *allPlugins = esm.getGlobalReaderList();
for (size_t j = 0; j < masters.size(); j++) {
ESM::Header::MasterData &mast = const_cast<ESM::Header::MasterData&>(masters[j]);
std::string fname = mast.name;
int index = ~0;
for (int i = 0; i < esm.getIndex(); i++) {
const std::string &candidate = allPlugins->at(i)->getContext().filename;
std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();
if (Misc::StringUtils::ciEqual(fname, fnamecandidate)) {
index = i;
break;
}
}
if (index == (int)~0) {
// Tried to load a parent file that has not been loaded yet. This is bad,
// the launcher should have taken care of this.
std::string fstring = "File " + esm.getName() + " asks for parent file " + masters[j].name
+ ", but it has not been loaded yet. Please check your load order.";
esm.fail(fstring);
}
mast.index = index;
}
if (index == (int)~0) {
// Tried to load a parent file that has not been loaded yet. This is bad,
// the launcher should have taken care of this.
std::string fstring = "File " + esm.getName() + " asks for parent file " + masters[j].name
+ ", but it has not been loaded yet. Please check your load order.";
esm.fail(fstring);
}
mast.index = index;
}
// Loop through all records
while(esm.hasMoreRecs())
{
if (isTes4 || isTes5 || isFONV)
{
ESM4::Reader& reader = static_cast<ESM::ESM4Reader*>(&esm)->reader();
reader.checkGroupStatus();
loadTes4Group(esm);
listener->setProgress(static_cast<size_t>(esm.getFileOffset() / (float)esm.getFileSize() * 1000));
continue;
}
ESM::NAME n = esm.getRecName();
esm.getRecHeader();
@ -122,6 +148,138 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
}
}
// Can't use ESM4::Reader& as the parameter here because we need esm.hasMoreRecs() for
// checking an empty group followed by EOF
void ESMStore::loadTes4Group (ESM::ESMReader &esm)
{
ESM4::Reader& reader = static_cast<ESM::ESM4Reader*>(&esm)->reader();
reader.getRecordHeader();
const ESM4::RecordHeader& hdr = reader.hdr();
if (hdr.record.typeId != ESM4::REC_GRUP)
return loadTes4Record(esm);
switch (hdr.group.type)
{
case ESM4::Grp_RecordType:
{
// FIXME: rewrite to workaround reliability issue
if (hdr.group.label.value == ESM4::REC_NAVI || hdr.group.label.value == ESM4::REC_WRLD ||
hdr.group.label.value == ESM4::REC_REGN || hdr.group.label.value == ESM4::REC_STAT ||
hdr.group.label.value == ESM4::REC_ANIO || hdr.group.label.value == ESM4::REC_CONT ||
hdr.group.label.value == ESM4::REC_MISC || hdr.group.label.value == ESM4::REC_ACTI ||
hdr.group.label.value == ESM4::REC_ARMO || hdr.group.label.value == ESM4::REC_NPC_ ||
hdr.group.label.value == ESM4::REC_FLOR || hdr.group.label.value == ESM4::REC_GRAS ||
hdr.group.label.value == ESM4::REC_TREE || hdr.group.label.value == ESM4::REC_LIGH ||
hdr.group.label.value == ESM4::REC_BOOK || hdr.group.label.value == ESM4::REC_FURN ||
hdr.group.label.value == ESM4::REC_SOUN || hdr.group.label.value == ESM4::REC_WEAP ||
hdr.group.label.value == ESM4::REC_DOOR || hdr.group.label.value == ESM4::REC_AMMO ||
hdr.group.label.value == ESM4::REC_CLOT || hdr.group.label.value == ESM4::REC_ALCH ||
hdr.group.label.value == ESM4::REC_APPA || hdr.group.label.value == ESM4::REC_INGR ||
hdr.group.label.value == ESM4::REC_SGST || hdr.group.label.value == ESM4::REC_SLGM ||
hdr.group.label.value == ESM4::REC_KEYM || hdr.group.label.value == ESM4::REC_HAIR ||
hdr.group.label.value == ESM4::REC_EYES || hdr.group.label.value == ESM4::REC_CELL ||
hdr.group.label.value == ESM4::REC_CREA || hdr.group.label.value == ESM4::REC_LVLC ||
hdr.group.label.value == ESM4::REC_LVLI || hdr.group.label.value == ESM4::REC_MATO ||
hdr.group.label.value == ESM4::REC_IDLE || hdr.group.label.value == ESM4::REC_LTEX ||
hdr.group.label.value == ESM4::REC_RACE || hdr.group.label.value == ESM4::REC_SBSP
)
{
reader.saveGroupStatus();
loadTes4Group(esm);
}
else
{
// Skip groups that are of no interest (for now).
// GMST GLOB CLAS FACT SKIL MGEF SCPT ENCH SPEL BSGN WTHR CLMT DIAL
// QUST PACK CSTY LSCR LVSP WATR EFSH
// FIXME: The label field of a group is not reliable, so we will need to check here as well
//std::cout << "skipping group... " << ESM4::printLabel(hdr.group.label, hdr.group.type) << std::endl;
reader.skipGroup();
return;
}
break;
}
case ESM4::Grp_CellChild:
case ESM4::Grp_WorldChild:
case ESM4::Grp_TopicChild:
case ESM4::Grp_CellPersistentChild:
{
reader.adjustGRUPFormId(); // not needed or even shouldn't be done? (only labels anyway)
reader.saveGroupStatus();
//#if 0
// Below test shows that Oblivion.esm does not have any persistent cell child
// groups under exterior world sub-block group. Haven't checked other files yet.
if (reader.grp(0).type == ESM4::Grp_CellPersistentChild &&
reader.grp(1).type == ESM4::Grp_CellChild &&
!(reader.grp(2).type == ESM4::Grp_WorldChild || reader.grp(2).type == ESM4::Grp_InteriorSubCell))
std::cout << "Unexpected persistent child group in exterior subcell" << std::endl;
//#endif
if (!esm.hasMoreRecs())
return; // may have been an empty group followed by EOF
loadTes4Group(esm);
break;
}
case ESM4::Grp_CellTemporaryChild:
case ESM4::Grp_CellVisibleDistChild:
{
// NOTE: preload strategy and persistent records
//
// Current strategy defers loading of "temporary" or "visible when distant"
// references and other records (land and pathgrid) until they are needed.
//
// The "persistent" records need to be loaded up front, however. This is to allow,
// for example, doors to work. A door reference will have a FormId of the
// destination door FormId. But we have no way of knowing to which cell the
// destination FormId belongs until that cell and that reference is loaded.
//
// For worldspaces the persistent records are usully (always?) stored in a dummy
// cell under a "world child" group. It may be possible to skip the whole "cell
// child" group without scanning for persistent records. See above short test.
reader.skipGroup();
break;
}
case ESM4::Grp_ExteriorCell:
case ESM4::Grp_ExteriorSubCell:
case ESM4::Grp_InteriorCell:
case ESM4::Grp_InteriorSubCell:
{
reader.saveGroupStatus();
loadTes4Group(esm);
break;
}
default:
reader.skipGroup();
break;
}
return;
}
void ESMStore::loadTes4Record (ESM::ESMReader& esm)
{
// Assumes that the reader has just read the record header only.
ESM4::Reader& reader = static_cast<ESM::ESM4Reader*>(&esm)->reader();
const ESM4::RecordHeader& hdr = reader.hdr();
switch (hdr.record.typeId)
{
// FIXME: removed for now
default:
reader.skipRecordData();
}
return;
}
void ESMStore::setUp()
{
std::map<int, StoreBase *>::iterator it = mStores.begin();
@ -199,7 +357,7 @@ void ESMStore::setUp()
if (type==ESM::REC_NPC_)
{
// NPC record will always be last and we know that there can be only one
// dynamic NPC record (player) -> We are done here with dynamic record laoding
// dynamic NPC record (player) -> We are done here with dynamic record loading
setUp();
const ESM::NPC *player = mNpcs.find ("player");

@ -6,6 +6,12 @@
#include <components/esm/records.hpp>
#include "store.hpp"
namespace ESM4
{
class Reader;
union RecordHeader;
}
namespace Loading
{
class Listener;
@ -73,6 +79,9 @@ namespace MWWorld
unsigned int mDynamicCount;
void loadTes4Group (ESM::ESMReader& esm);
void loadTes4Record (ESM::ESMReader& esm);
public:
/// \todo replace with SharedIterator<StoreBase>
typedef std::map<int, StoreBase *>::const_iterator iterator;

@ -90,12 +90,12 @@ namespace MWWorld
return mLoaders.insert(std::make_pair(extension, loader)).second;
}
void load(const boost::filesystem::path& filepath, int& index)
void load(const boost::filesystem::path& filepath, std::vector<std::vector<std::string> >& contentFiles)
{
LoadersContainer::iterator it(mLoaders.find(Misc::StringUtils::lowerCase(filepath.extension().string())));
if (it != mLoaders.end())
{
it->second->load(filepath, index);
it->second->load(filepath, contentFiles);
}
else
{
@ -169,7 +169,7 @@ namespace MWWorld
mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback);
mEsm.resize(contentFiles.size());
mEsm.resize(3); // FIXME: 0 - TES3, 1 - TES4, 2 - TES5 (TODO: Fallout)
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
listener->loadingOn();
@ -187,7 +187,7 @@ namespace MWWorld
listener->loadingOff();
// insert records that may not be present in all versions of MW
if (mEsm[0].getFormat() == 0)
if (mEsm[0][0]->getFormat() == 0) // FIXME: first file may not be for MW
ensureNeededRecords();
mStore.setUp();
@ -474,6 +474,13 @@ namespace MWWorld
delete mPhysics;
delete mPlayer;
for (unsigned int i = 0; i < mEsm.size(); ++i)
for (unsigned int j = 0; j < mEsm[i].size(); ++j)
{
mEsm[i][j]->close();
delete mEsm[i][j];
}
}
const ESM::Cell *World::getExterior (const std::string& cellName) const
@ -542,9 +549,9 @@ namespace MWWorld
return mStore;
}
std::vector<ESM::ESMReader>& World::getEsmReader()
std::vector<ESM::ESMReader*>& World::getEsmReader()
{
return mEsm;
return mEsm[0]; // FIXME: only MW for now (but doesn't seem to be used anywhere?)
}
LocalScripts& World::getLocalScripts()
@ -2609,18 +2616,35 @@ namespace MWWorld
return mScriptsEnabled;
}
// The aim is to allow loading various types of TES files in any combination, as long as
// the dependent files are loaded first. To achieve this, separate indicies for each TES
// versions are required.
//
// The trouble is that until the file is opened by an ESM reader to check the version from
// the header we don't know which index to increment.
//
// One option is to allow the content loader to manage.
// FIXME: Appears to be loading all the files named in 'content' located in fileCollections
// based on the extension string (e.g. .esm). This probably means that the contents are in
// the correct load order.
//
// 'contentLoader' has a number of loaders that can deal with various extension types.
void World::loadContentFiles(const Files::Collections& fileCollections,
const std::vector<std::string>& content, ContentLoader& contentLoader)
{
std::vector<std::vector<std::string> > contentFiles;
contentFiles.resize(3);
std::vector<std::string>::const_iterator it(content.begin());
std::vector<std::string>::const_iterator end(content.end());
for (int idx = 0; it != end; ++it, ++idx)
for (; it != end; ++it)
{
boost::filesystem::path filename(*it);
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
if (col.doesExist(*it))
{
contentLoader.load(col.getPath(*it), idx);
contentLoader.load(col.getPath(*it), contentFiles);
}
else
{
@ -2937,7 +2961,7 @@ namespace MWWorld
MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition(), false);
action.execute(ptr);
}
void World::updateWeather(float duration, bool paused)
{
if (mPlayer->wasTeleported())
@ -2945,7 +2969,7 @@ namespace MWWorld
mPlayer->setTeleported(false);
mWeatherManager->switchToNextWeather(true);
}
mWeatherManager->update(duration, paused);
}

@ -66,7 +66,7 @@ namespace MWWorld
MWWorld::Scene *mWorldScene;
MWWorld::Player *mPlayer;
std::vector<ESM::ESMReader> mEsm;
std::vector<std::vector<ESM::ESMReader*> > mEsm;
MWWorld::ESMStore mStore;
LocalScripts mLocalScripts;
MWWorld::Globals mGlobalVariables;
@ -199,7 +199,7 @@ namespace MWWorld
virtual const MWWorld::ESMStore& getStore() const;
virtual std::vector<ESM::ESMReader>& getEsmReader();
virtual std::vector<ESM::ESMReader*>& getEsmReader();
virtual LocalScripts& getLocalScripts();

@ -6,16 +6,16 @@ set (VERSION_HPP ${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp)
if (GIT_CHECKOUT)
add_custom_target (git-version
COMMAND ${CMAKE_COMMAND}
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
-DVERSION_HPP_IN=${VERSION_HPP_IN}
-DVERSION_HPP=${VERSION_HPP}
-DOPENMW_VERSION_MAJOR=${OPENMW_VERSION_MAJOR}
-DOPENMW_VERSION_MINOR=${OPENMW_VERSION_MINOR}
-DOPENMW_VERSION_RELEASE=${OPENMW_VERSION_RELEASE}
-DOPENMW_VERSION=${OPENMW_VERSION}
-P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/GitVersion.cmake
VERBATIM)
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
-DVERSION_HPP_IN=${VERSION_HPP_IN}
-DVERSION_HPP=${VERSION_HPP}
-DOPENMW_VERSION_MAJOR=${OPENMW_VERSION_MAJOR}
-DOPENMW_VERSION_MINOR=${OPENMW_VERSION_MINOR}
-DOPENMW_VERSION_RELEASE=${OPENMW_VERSION_RELEASE}
-DOPENMW_VERSION=${OPENMW_VERSION}
-P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/GitVersion.cmake
VERBATIM)
else (GIT_CHECKOUT)
configure_file(${VERSION_HPP_IN} ${VERSION_HPP})
endif (GIT_CHECKOUT)
@ -62,7 +62,7 @@ add_component_dir (esm
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
aisequence magiceffects util custommarkerstate stolenitems transport
aisequence magiceffects util custommarkerstate stolenitems transport esm4reader
)
add_component_dir (esmterrain
@ -165,10 +165,10 @@ include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR})
target_link_libraries(components
${Boost_LIBRARIES}
target_link_libraries(components
${Boost_LIBRARIES}
${OGRE_LIBRARIES}
${OENGINE_LIBRARY}
${OENGINE_LIBRARY}
)
if (WIN32)

@ -0,0 +1,94 @@
#include "esm4reader.hpp"
ESM::ESM4Reader::ESM4Reader(bool oldHeader)
{
// TES4 header size is 4 bytes smaller than TES5 header
mReader.setRecHeaderSize(oldHeader ? sizeof(ESM4::RecordHeader)-4 : sizeof(ESM4::RecordHeader));
}
ESM::ESM4Reader::~ESM4Reader()
{
}
void ESM::ESM4Reader::openTes4File(const std::string &name)
{
mCtx.filename = name;
// WARNING: may throw
mCtx.leftFile = mReader.openTes4File(name);
mReader.registerForUpdates(this); // for updating mCtx.leftFile
mReader.getRecordHeader();
if (mReader.hdr().record.typeId == ESM4::REC_TES4)
{
mReader.loadHeader();
mCtx.leftFile -= mReader.hdr().record.dataSize;
// Hack: copy over values to TES3 header for getVer() and getRecordCount() to work
mHeader.mData.version = mReader.esmVersion();
mHeader.mData.records = mReader.numRecords();
mReader.buildLStringIndex(); // for localised strings in Skyrim
}
else
fail("Unknown file format");
}
ESM4::ReaderContext ESM::ESM4Reader::getESM4Context()
{
return mReader.getContext();
}
void ESM::ESM4Reader::restoreESM4Context(const ESM4::ReaderContext& ctx)
{
// Reopen the file if necessary
if (mCtx.filename != ctx.filename)
openTes4File(ctx.filename);
// mCtx.leftFile is the only thing used in the old context. Strictly speaking, updating it
// with the correct value is not really necessary since we're not going to load the rest of
// the file (most likely to load a CELL or LAND then be done with it).
mCtx.leftFile = mReader.getFileSize() - mReader.getFileOffset();
// restore group stack, load the header, etc.
mReader.restoreContext(ctx);
}
void ESM::ESM4Reader::restoreCellChildrenContext(const ESM4::ReaderContext& ctx)
{
// Reopen the file if necessary
if (mCtx.filename != ctx.filename)
openTes4File(ctx.filename);
mReader.restoreContext(ctx); // restore group stack, load the CELL header, etc.
if (mReader.hdr().record.typeId != ESM4::REC_CELL) // FIXME: testing only
fail("Restore Cell Children failed");
mReader.skipRecordData(); // skip the CELL record
mReader.getRecordHeader(); // load the header for cell child group (hopefully)
// this is a hack to load only the cell child group...
if (mReader.hdr().group.typeId == ESM4::REC_GRUP && mReader.hdr().group.type == ESM4::Grp_CellChild)
{
mCtx.leftFile = mReader.hdr().group.groupSize - ctx.recHeaderSize;
return;
}
// But some cells may have no child groups...
// Suspect "ICMarketDistrict" 7 18 is one, followed by cell record 00165F2C "ICMarketDistrict" 6 17
if (mReader.hdr().group.typeId != ESM4::REC_GRUP && mReader.hdr().record.typeId == ESM4::REC_CELL)
{
mCtx.leftFile = 0;
return;
}
// Maybe the group is completed
// See "ICMarketDistrict" 9 15 which is followed by a exterior sub-cell block
ESM4::ReaderContext tempCtx = mReader.getContext();
if (!tempCtx.groupStack.empty() && tempCtx.groupStack.back().second == 0)
{
mCtx.leftFile = 0;
return;
}
else
fail("Restore Cell Children failed");
}

@ -0,0 +1,38 @@
#ifndef COMPONENT_ESM_4READER_H
#define COMPONENT_ESM_4READER_H
#include <extern/esm4/tes4.hpp>
#include <extern/esm4/reader.hpp>
#include "esmreader.hpp"
namespace ESM
{
// Wrapper class for integrating into OpenCS
class ESM4Reader : public ESMReader, public ESM4::ReaderObserver
{
ESM4::Reader mReader;
public:
ESM4Reader(bool oldHeader = true);
virtual ~ESM4Reader();
ESM4::Reader& reader() { return mReader; }
// Added for use with OpenMW (loading progress bar)
inline size_t getFileSize() { return mReader.getFileSize(); }
inline size_t getFileOffset() { return mReader.getFileOffset(); }
// Added for loading Cell/Land
ESM4::ReaderContext getESM4Context();
void restoreESM4Context(const ESM4::ReaderContext& ctx);
void restoreCellChildrenContext(const ESM4::ReaderContext& ctx);
void openTes4File(const std::string &name);
// callback from mReader to ensure hasMoreRecs() can reliably track to EOF
inline void update(std::size_t size) { mCtx.leftFile -= size; }
};
}
#endif // COMPONENT_ESM_4READER_H

@ -11,8 +11,15 @@ namespace ESM
{
enum Version
{
VER_12 = 0x3f99999a,
VER_13 = 0x3fa66666
VER_12 = 0x3f99999a,
VER_13 = 0x3fa66666,
VER_080 = 0x3f4ccccd, // TES4
VER_100 = 0x3f800000, // TES4
VER_132 = 0x3fa8f5c3, // FONV Courier's Stash, DeadMoney
VER_133 = 0x3faa3d71, // FONV HonestHearts
VER_134 = 0x3fab851f, // FONV, GunRunnersArsenal, LonesomeRoad, OldWorldBlues
VER_094 = 0x3f70a3d7, // TES5/FO3
VER_17 = 0x3fd9999a // TES5
};
/* A structure used for holding fixed-length strings. In the case of

@ -71,12 +71,103 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name)
{
openRaw(_esm, name);
if (getRecName() != "TES3")
fail("Not a valid Morrowind file");
getRecHeader();
NAME modVer = getRecName();
if (modVer == "TES3")
{
getRecHeader();
mHeader.load (*this);
mHeader.load (*this);
}
else if (modVer == "TES4")
{
mHeader.mData.author.assign("");
mHeader.mData.desc.assign("");
char buf[512]; // arbitrary number
unsigned short size;
skip(16); // skip the rest of the header, note it may be 4 bytes longer
NAME rec = getRecName();
if (rec != "HEDR")
rec = getRecName(); // adjust for extra 4 bytes
bool readRec = true;
while (mEsm->size() - mEsm->tell() >= 4) // Shivering Isle or Bashed Patch can end here
{
if (!readRec) // may be already read
rec = getRecName();
else
readRec = false;
switch (rec.val)
{
case 0x52444548: // HEDR
{
skip(2); // data size
getT(mHeader.mData.version);
getT(mHeader.mData.records);
skip(4); // skip next available object id
break;
}
case 0x4d414e43: // CNAM
{
getT(size);
getExact(buf, size);
std::string author;
size = std::min(size, (unsigned short)32); // clamp for TES3 format
author.assign(buf, size - 1); // don't copy null terminator
mHeader.mData.author.assign(author);
break;
}
case 0x4d414e53: // SNAM
{
getT(size);
getExact(buf, size);
std::string desc;
size = std::min(size, (unsigned short)256); // clamp for TES3 format
desc.assign(buf, size - 1); // don't copy null terminator
mHeader.mData.desc.assign(desc);
break;
}
case 0x5453414d: // MAST
{
Header::MasterData m;
getT(size);
getExact(buf, size);
m.name.assign(buf, size-1); // don't copy null terminator
rec = getRecName();
if (rec == "DATA")
{
getT(size);
getT(m.size); // 64 bits
}
else
{
// some esp's don't have DATA subrecord
m.size = 0;
readRec = true; // don't read again at the top of while loop
}
mHeader.mMaster.push_back (m);
break;
}
case 0x56544e49: // INTV
case 0x43434e49: // INCC
case 0x4d414e4f: // ONAM
{
getT(size);
skip(size);
break;
}
case 0x50555247: // GRUP
default:
return; // all done
}
}
return;
}
else
fail("Not a valid Morrowind file");
}
void ESMReader::open(const std::string &file)

@ -23,6 +23,7 @@ class ESMReader
public:
ESMReader();
virtual ~ESMReader() {}
/*************************************************************************
*
@ -74,9 +75,9 @@ public:
void openRaw(const std::string &file);
/// Get the file size. Make sure that the file has been opened!
size_t getFileSize() { return mEsm->size(); }
virtual size_t getFileSize() { return mEsm->size(); }
/// Get the current position in the file. Make sure that the file has been opened!
size_t getFileOffset() { return mEsm->tell(); }
virtual size_t getFileOffset() { return mEsm->tell(); }
// This is a quick hack for multiple esm/esp files. Each plugin introduces its own
// terrain palette, but ESMReader does not pass a reference to the correct plugin
@ -86,8 +87,8 @@ public:
void setIndex(const int index) {mIdx = index; mCtx.index = index;}
int getIndex() {return mIdx;}
void setGlobalReaderList(std::vector<ESMReader> *list) {mGlobalReaderList = list;}
std::vector<ESMReader> *getGlobalReaderList() {return mGlobalReaderList;}
void setGlobalReaderList(std::vector<ESMReader*> *list) {mGlobalReaderList = list;}
std::vector<ESMReader*> *getGlobalReaderList() {return mGlobalReaderList;}
/*************************************************************************
*
@ -292,8 +293,6 @@ public:
private:
Ogre::DataStreamPtr mEsm;
ESM_Context mCtx;
unsigned int mRecordFlags;
// Special file signifier (see SpecialFile enum above)
@ -301,10 +300,13 @@ private:
// Buffer for ESM strings
std::vector<char> mBuffer;
Header mHeader;
std::vector<ESMReader> *mGlobalReaderList;
std::vector<ESMReader*> *mGlobalReaderList;
ToUTF8::Utf8Encoder* mEncoder;
protected:
ESM_Context mCtx;
Header mHeader;
};
}
#endif

@ -0,0 +1,80 @@
#
# Copyright (C) 2016-2018 cc9cii
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
#
# cc9cii cc9c@iinet.net.au
#
set(ESM4_LIBRARY "esm4")
set(ESM4_SOURCE_FILES
common.cpp
tes4.cpp
navi.cpp
wrld.cpp
navm.cpp
land.cpp
ltex.cpp
cell.cpp
regn.cpp
stat.cpp
refr.cpp
anio.cpp
cont.cpp
misc.cpp
acti.cpp
armo.cpp
npc_.cpp
flor.cpp
gras.cpp
tree.cpp
ligh.cpp
achr.cpp
book.cpp
furn.cpp
soun.cpp
weap.cpp
door.cpp
clot.cpp
alch.cpp
ammo.cpp
appa.cpp
ingr.cpp
sgst.cpp
slgm.cpp
keym.cpp
hair.cpp
eyes.cpp
crea.cpp
lvlc.cpp
lvli.cpp
acre.cpp
idle.cpp
mato.cpp
sbsp.cpp
race.cpp
clas.cpp
formid.cpp
reader.cpp
)
add_library(${ESM4_LIBRARY} STATIC ${ESM4_SOURCE_FILES})
set(ESM4_LIBRARIES ${ESM4_LIBRARY})
link_directories(${CMAKE_CURRENT_BINARY_DIR})
set(ESM4_LIBRARIES ${ESM4_LIBRARIES} PARENT_SCOPE)

117
extern/esm4/achr.cpp vendored

@ -0,0 +1,117 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "achr.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::ActorCharacter::ActorCharacter() : mFormId(0), mFlags(0), mDisabled(false), mBaseObj(0),
mScale(1.f), mOwner(0), mGlobal(0)
{
mEditorId.clear();
mFullName.clear();
mEsp.parent = 0;
mEsp.flags = 0;
}
ESM4::ActorCharacter::~ActorCharacter()
{
}
void ESM4::ActorCharacter::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL: reader.getZString(mFullName); break;
case ESM4::SUB_NAME: reader.getFormId(mBaseObj); break;
case ESM4::SUB_DATA: reader.get(mPosition); break;
case ESM4::SUB_XSCL: reader.get(mScale); break;
case ESM4::SUB_XOWN: reader.get(mOwner); break;
case ESM4::SUB_XESP:
{
reader.get(mEsp);
reader.adjustFormId(mEsp.parent);
break;
}
case ESM4::SUB_XRGD: // ragdoll
case ESM4::SUB_XHRS: // horse formId
case ESM4::SUB_XMRC: // merchant container formId
// TES5
case ESM4::SUB_XAPD: // activation parent
case ESM4::SUB_XAPR: // active parent
case ESM4::SUB_XEZN: // encounter zone
case ESM4::SUB_XHOR:
case ESM4::SUB_XLCM: // leveled creature
case ESM4::SUB_XLCN: // location
case ESM4::SUB_XLKR: // location route?
case ESM4::SUB_XLRT: // location type
//
case ESM4::SUB_XPRD:
case ESM4::SUB_XPPA:
case ESM4::SUB_INAM:
case ESM4::SUB_PDTO:
//
case ESM4::SUB_XRGB:
case ESM4::SUB_XIS2:
case ESM4::SUB_XPCI: // formId
case ESM4::SUB_XLOD:
case ESM4::SUB_VMAD:
case ESM4::SUB_XLRL: // Unofficial Skyrim Patch
case ESM4::SUB_XRDS: // FO3
case ESM4::SUB_XIBS: // FO3
case ESM4::SUB_SCHR: // FO3
case ESM4::SUB_TNAM: // FO3
case ESM4::SUB_XATO: // FONV
{
//std::cout << "ACHR " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::ACHR::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::ActorCharacter::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::ActorCharacter::blank()
//{
//}

@ -0,0 +1,64 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_ACHR_H
#define ESM4_ACHR_H
#include "common.hpp" // Position
namespace ESM4
{
class Reader;
class Writer;
struct ActorCharacter
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
FormId mBaseObj;
Position mPosition;
float mScale; // default 1.f
FormId mOwner;
FormId mGlobal;
bool mDisabled;
EnableParent mEsp; // TODO may need to check mFlags & 0x800 (initially disabled)
ActorCharacter();
virtual ~ActorCharacter();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_ACHR_H

@ -0,0 +1,93 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "acre.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::ActorCreature::ActorCreature() : mFormId(0), mFlags(0), mDisabled(false), mBaseObj(0), mScale(1.f),
mOwner(0), mGlobal(0), mFactionRank(0)
{
mEditorId.clear();
mEsp.parent = 0;
mEsp.flags = 0;
}
ESM4::ActorCreature::~ActorCreature()
{
}
void ESM4::ActorCreature::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_NAME: reader.getFormId(mBaseObj); break;
case ESM4::SUB_DATA: reader.get(mPosition); break;
case ESM4::SUB_XSCL: reader.get(mScale); break;
case ESM4::SUB_XESP:
{
reader.get(mEsp);
reader.adjustFormId(mEsp.parent);
break;
}
case ESM4::SUB_XOWN: reader.getFormId(mOwner); break;
case ESM4::SUB_XGLB: reader.get(mGlobal); break; // FIXME: formId?
case ESM4::SUB_XRNK: reader.get(mFactionRank); break;
case ESM4::SUB_XRGD: // ragdoll
case ESM4::SUB_XLKR: // FO3
case ESM4::SUB_XLCM: // FO3
case ESM4::SUB_XEZN: // FO3
case ESM4::SUB_XRGB: // FO3
{
//std::cout << "ACRE " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::ACRE::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::ActorCreature::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::ActorCreature::blank()
//{
//}

@ -0,0 +1,64 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_ACRE_H
#define ESM4_ACRE_H
#include "common.hpp" // EnableParent
namespace ESM4
{
class Reader;
class Writer;
struct ActorCreature
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
FormId mBaseObj;
Position mPosition;
float mScale; // default 1.f
FormId mOwner;
FormId mGlobal;
std::uint32_t mFactionRank;
bool mDisabled;
EnableParent mEsp; // TODO may need to check mFlags & 0x800 (initially disabled)
ActorCreature();
virtual ~ActorCreature();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_ACRE_H

107
extern/esm4/acti.cpp vendored

@ -0,0 +1,107 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "acti.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Activator::Activator() : mFormId(0), mFlags(0), mScript(0), mSound(0), mBoundRadius(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
}
ESM4::Activator::~Activator()
{
}
void ESM4::Activator::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("ACTI FULL data read error");
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_SNAM: reader.getFormId(mSound); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_MODS:
case ESM4::SUB_DEST:
case ESM4::SUB_DMDL:
case ESM4::SUB_DMDS:
case ESM4::SUB_DMDT:
case ESM4::SUB_DSTD:
case ESM4::SUB_DSTF:
case ESM4::SUB_FNAM:
case ESM4::SUB_KNAM:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_OBND:
case ESM4::SUB_PNAM:
case ESM4::SUB_RNAM:
case ESM4::SUB_VMAD:
case ESM4::SUB_VNAM:
case ESM4::SUB_WNAM:
case ESM4::SUB_INAM: // FONV
case ESM4::SUB_XATO: // FONV
{
//std::cout << "ACTI " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::ACTI::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Activator::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Activator::blank()
//{
//}

@ -0,0 +1,63 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_ACTI_H
#define ESM4_ACTI_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Activator
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
FormId mScript;
FormId mSound;
float mBoundRadius;
Activator();
virtual ~Activator();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_ACTI_H

112
extern/esm4/alch.cpp vendored

@ -0,0 +1,112 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "alch.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Potion::Potion() : mFormId(0), mFlags(0), mScript(0), mBoundRadius(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
mData.weight = 0.f;
std::memset(&mEffect, 0, sizeof(ScriptEffect));
}
ESM4::Potion::~Potion()
{
}
void ESM4::Potion::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("ALCH FULL data read error");
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_DATA: reader.get(mData); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_SCIT:
{
reader.get(mEffect);
reader.adjustFormId(mEffect.formId);
break;
}
case ESM4::SUB_MODT:
case ESM4::SUB_ENIT:
case ESM4::SUB_EFID:
case ESM4::SUB_EFIT:
case ESM4::SUB_CTDA:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_MODS:
case ESM4::SUB_OBND:
case ESM4::SUB_YNAM:
case ESM4::SUB_ZNAM:
case ESM4::SUB_ETYP: // FO3
case ESM4::SUB_MICO: // FO3
{
//std::cout << "ALCH " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::ALCH::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Potion::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Potion::blank()
//{
//}

@ -0,0 +1,71 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_ALCH_H
#define ESM4_ALCH_H
#include "common.hpp"
namespace ESM4
{
class Reader;
class Writer;
struct Potion
{
#pragma pack(push, 1)
struct Data
{
float weight;
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon; // inventory
FormId mScript;
ScriptEffect mEffect;
float mBoundRadius;
Data mData;
Potion();
virtual ~Potion();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_ALCH_H

139
extern/esm4/ammo.cpp vendored

@ -0,0 +1,139 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "ammo.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Ammo::Ammo() : mFormId(0), mFlags(0), mBoundRadius(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
}
ESM4::Ammo::~Ammo()
{
}
void ESM4::Ammo::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
std::uint32_t esmVer = reader.esmVersion();
bool isFONV = esmVer == ESM4::VER_132 || esmVer == ESM4::VER_133 || esmVer == ESM4::VER_134;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("AMMO FULL data read error");
break;
}
case ESM4::SUB_DATA:
{
//if (reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170)
if (subHdr.dataSize == 16) // FO3 has 13 bytes even though VER_094
{
FormId projectile;
reader.get(projectile); // FIXME: add to mData
reader.get(mData.flags);
reader.get(mData.weight);
float damageInFloat;
reader.get(damageInFloat); // FIXME: add to mData
}
else if (isFONV || subHdr.dataSize == 13)
{
reader.get(mData.speed);
std::uint8_t flags;
reader.get(flags);
mData.flags = flags;
static std::uint8_t dummy;
reader.get(dummy);
reader.get(dummy);
reader.get(dummy);
reader.get(mData.value);
reader.get(mData.clipRounds);
}
else // TES4
{
reader.get(mData.speed);
reader.get(mData.flags);
reader.get(mData.value);
reader.get(mData.weight);
reader.get(mData.damage);
}
break;
}
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_ANAM: reader.get(mEnchantmentPoints); break;
case ESM4::SUB_ENAM: reader.getFormId(mEnchantment); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_OBND:
case ESM4::SUB_YNAM:
case ESM4::SUB_ZNAM:
case ESM4::SUB_DESC:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_MICO: // FO3
case ESM4::SUB_ONAM: // FO3
case ESM4::SUB_DAT2: // FONV
case ESM4::SUB_QNAM: // FONV
case ESM4::SUB_RCIL: // FONV
case ESM4::SUB_SCRI: // FONV
{
//std::cout << "AMMO " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::AMMO::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Ammo::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Ammo::blank()
//{
//}

@ -0,0 +1,78 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_AMMO_H
#define ESM4_AMMO_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Ammo
{
struct Data // FIXME: TES5 projectile, damage (float)
{
float speed;
std::uint32_t flags;
std::uint32_t value; // gold
float weight;
std::uint16_t damage;
std::uint8_t clipRounds; // only in FO3/FONV
Data() : speed(0.f), flags(0), value(0), weight(0.f), damage(0), clipRounds(0) {}
};
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon; // inventory
float mBoundRadius;
std::uint16_t mEnchantmentPoints;
FormId mEnchantment;
Data mData;
Ammo();
virtual ~Ammo();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_AMMO_H

@ -0,0 +1,80 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "anio.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::AnimObject::AnimObject() : mFormId(0), mFlags(0), mBoundRadius(0.f), mIdleAnim(0)
{
mEditorId.clear();
mModel.clear();
mUnloadEvent.clear();
}
ESM4::AnimObject::~AnimObject()
{
}
void ESM4::AnimObject::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_BNAM: reader.getZString(mUnloadEvent); break;
case ESM4::SUB_DATA: reader.getFormId(mIdleAnim); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT: // TES5 only
case ESM4::SUB_MODS: // TES5 only
{
//std::cout << "ANIO " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::ANIO::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::AnimObject::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::AnimObject::blank()
//{
//}

@ -0,0 +1,62 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_ANIO_H
#define ESM4_ANIO_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct AnimObject
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mModel;
float mBoundRadius;
FormId mIdleAnim; // only in TES4
std::string mUnloadEvent; // only in TES5
AnimObject();
virtual ~AnimObject();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_ANIO_H

113
extern/esm4/appa.cpp vendored

@ -0,0 +1,113 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "appa.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Apparatus::Apparatus() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
mData.type = 0;
mData.value = 0;
mData.weight = 0.f;
mData.quality = 0.f;
}
ESM4::Apparatus::~Apparatus()
{
}
void ESM4::Apparatus::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("APPA FULL data read error");
break;
}
case ESM4::SUB_DATA:
{
if (reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170)
{
reader.get(mData.value);
reader.get(mData.weight);
}
else
{
reader.get(mData.type);
reader.get(mData.value);
reader.get(mData.weight);
reader.get(mData.quality);
}
break;
}
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_DESC:
case ESM4::SUB_OBND:
case ESM4::SUB_QUAL:
{
//std::cout << "APPA " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::APPAPPAoad - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Apparatus::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Apparatus::blank()
//{
//}

@ -0,0 +1,73 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_APPA_H
#define ESM4_APPA_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Apparatus
{
struct Data
{
std::uint8_t type; // 0 = Mortar and Pestle, 1 = Alembic, 2 = Calcinator, 3 = Retort
std::uint32_t value; // gold
float weight;
float quality;
};
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon; // inventory
float mBoundRadius;
FormId mScript;
Data mData;
Apparatus();
virtual ~Apparatus();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_APPA_H

166
extern/esm4/armo.cpp vendored

@ -0,0 +1,166 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "armo.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Armor::Armor() : mFormId(0), mFlags(0), mBoundRadius(0.f), mArmorFlags(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIconMale.clear();
mIconFemale.clear();
mData.armor = 0;
mData.value = 0;
mData.health = 0;
mData.weight = 0.f;
}
ESM4::Armor::~Armor()
{
}
void ESM4::Armor::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
std::uint32_t esmVer = reader.esmVersion();
bool isFONV = esmVer == ESM4::VER_132 || esmVer == ESM4::VER_133 || esmVer == ESM4::VER_134;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("ARMO FULL data read error");
break;
}
case ESM4::SUB_DATA:
{
//if (reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170)
if (subHdr.dataSize == 8) // FO3 has 12 bytes even though VER_094
{
reader.get(mData.value);
reader.get(mData.weight);
}
else if (isFONV || subHdr.dataSize == 12)
{
reader.get(mData.value);
reader.get(mData.health);
reader.get(mData.weight);
}
else
reader.get(mData); // TES4
break;
}
case ESM4::SUB_MODL: // seems only for Dawnguard/Dragonborn?
{
if (!reader.getZString(mModel))
throw std::runtime_error ("ARMO MODL data read error");
break;
}
case ESM4::SUB_ICON: reader.getZString(mIconMale); break;
case ESM4::SUB_ICO2: reader.getZString(mIconFemale); break;
//case ESM4::SUB_BMDT: reader.get(mArmorFlags); break; // see below re. FO3
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_ANAM: reader.get(mEnchantmentPoints); break;
case ESM4::SUB_ENAM: reader.getFormId(mEnchantment); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_MOD2:
case ESM4::SUB_MOD3:
case ESM4::SUB_MOD4:
case ESM4::SUB_MO2B:
case ESM4::SUB_MO3B:
case ESM4::SUB_MO4B:
case ESM4::SUB_MO2T:
case ESM4::SUB_MO2S:
case ESM4::SUB_MO3T:
case ESM4::SUB_MO4T:
case ESM4::SUB_MO4S:
case ESM4::SUB_OBND:
case ESM4::SUB_BODT:
case ESM4::SUB_BOD2:
case ESM4::SUB_YNAM:
case ESM4::SUB_ZNAM:
case ESM4::SUB_RNAM:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_DESC:
case ESM4::SUB_TNAM:
case ESM4::SUB_DNAM:
case ESM4::SUB_BAMT:
case ESM4::SUB_BIDS:
case ESM4::SUB_ETYP:
case ESM4::SUB_BMCT:
case ESM4::SUB_MICO:
case ESM4::SUB_MIC2:
case ESM4::SUB_EAMT:
case ESM4::SUB_EITM:
case ESM4::SUB_VMAD:
case ESM4::SUB_BMDT: // FO3 FIXME might have different format
case ESM4::SUB_REPL: // FO3
case ESM4::SUB_BIPL: // FO3
case ESM4::SUB_MODD: // FO3
case ESM4::SUB_MOSD: // FO3
case ESM4::SUB_MODS: // FO3
case ESM4::SUB_MO3S: // FO3
case ESM4::SUB_BNAM: // FONV
case ESM4::SUB_SNAM: // FONV
{
//std::cout << "ARMO " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::ARMO::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Armor::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Armor::blank()
//{
//}

105
extern/esm4/armo.hpp vendored

@ -0,0 +1,105 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_ARMO_H
#define ESM4_ARMO_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Armor
{
enum Flags
{
// Biped Object Flags
Flag_Head = 0x00000001,
Flag_Hair = 0x00000002,
Flag_UpperBody = 0x00000004,
Flag_LowerBody = 0x00000008,
Flag_Hand = 0x00000010,
Flag_Foot = 0x00000020,
Flag_RightRing = 0x00000040,
Flag_LeftRing = 0x00000080,
Flag_Amulet = 0x00000100,
Flag_Weapon = 0x00000200,
Flag_BackWeapon = 0x00000400,
Flag_SideWeapon = 0x00000800,
Flag_Quiver = 0x00001000,
Flag_Shield = 0x00002000,
Flag_Torch = 0x00004000,
Flag_Tail = 0x00008000,
// General Flags
Flag_HideRings = 0x00010000,
Flag_HideAmulet = 0x00020000,
Flag_NonPlayable = 0x00400000,
Flag_Unknown = 0xCD000000
};
#pragma pack(push, 1)
struct Data
{
std::uint16_t armor; // Only in TES4?
std::uint32_t value;
std::uint32_t health; // not in TES5?
float weight;
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIconMale;
std::string mIconFemale;
float mBoundRadius;
std::uint32_t mArmorFlags;
FormId mScript;
std::uint16_t mEnchantmentPoints;
FormId mEnchantment;
Data mData;
Armor();
virtual ~Armor();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_ARMO_H

141
extern/esm4/book.cpp vendored

@ -0,0 +1,141 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "book.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Book::Book() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0),
mEnchantmentPoints(0), mEnchantment(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mText.clear();
mIcon.clear();
mData.flags = 0;
mData.type = 0;
mData.bookSkill = 0;
mData.value = 0;
mData.weight = 0.f;
}
ESM4::Book::~Book()
{
}
void ESM4::Book::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
std::uint32_t esmVer = reader.esmVersion();
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("BOOK FULL data read error");
break;
}
case ESM4::SUB_DESC:
{
if (reader.hasLocalizedStrings())
{
std::uint32_t formid;
reader.get(formid);
if (formid)
reader.getLocalizedString(formid, mText); // sometimes formid is null
}
else if (!reader.getZString(mText))
throw std::runtime_error ("BOOK DESC data read error");
break;
}
case ESM4::SUB_DATA:
{
reader.get(mData.flags);
//if (reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170)
if (subHdr.dataSize == 16) // FO3 has 10 bytes even though VER_094
{
static std::uint8_t dummy;
reader.get(mData.type);
reader.get(dummy);
reader.get(dummy);
reader.get(mData.teaches);
}
else
{
reader.get(mData.bookSkill);
}
reader.get(mData.value);
reader.get(mData.weight);
break;
}
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_ANAM: reader.get(mEnchantmentPoints); break;
case ESM4::SUB_ENAM: reader.getFormId(mEnchantment); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_OBND:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_CNAM:
case ESM4::SUB_INAM:
case ESM4::SUB_YNAM:
case ESM4::SUB_VMAD:
{
//std::cout << "BOOK " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::BOOK::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Book::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Book::blank()
//{
//}

110
extern/esm4/book.hpp vendored

@ -0,0 +1,110 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_BOOK_H
#define ESM4_BOOK_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Book
{
enum Flags
{
Flag_Scroll = 0x0001,
Flag_NoTake = 0x0002
};
enum BookSkill // for TES4 only
{
BookSkill_None = -1,
BookSkill_Armorer = 0,
BookSkill_Athletics = 1,
BookSkill_Blade = 2,
BookSkill_Block = 3,
BookSkill_Blunt = 4,
BookSkill_HandToHand = 5,
BookSkill_HeavyArmor = 6,
BookSkill_Alchemy = 7,
BookSkill_Alteration = 8,
BookSkill_Conjuration = 9,
BookSkill_Destruction = 10,
BookSkill_Illusion = 11,
BookSkill_Mysticism = 12,
BookSkill_Restoration = 13,
BookSkill_Acrobatics = 14,
BookSkill_LightArmor = 15,
BookSkill_Marksman = 16,
BookSkill_Mercantile = 17,
BookSkill_Security = 18,
BookSkill_Sneak = 19,
BookSkill_Speechcraft = 20
};
struct Data
{
std::uint8_t flags;
std::uint8_t type; // TES5 only
std::uint32_t teaches; // TES5 only
std::int8_t bookSkill; // not in TES5
std::uint32_t value;
float weight;
};
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
float mBoundRadius;
std::string mText;
FormId mScript;
std::string mIcon;
std::uint16_t mEnchantmentPoints;
FormId mEnchantment;
Data mData;
Book();
virtual ~Book();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_BOOK_H

268
extern/esm4/cell.cpp vendored

@ -0,0 +1,268 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "cell.hpp"
#include <cassert>
#include <stdexcept>
#include <iostream> // FIXME: debug only
#include "reader.hpp"
//#include "writer.hpp"
#ifdef NDEBUG // FIXME: debuggigng only
#undef NDEBUG
#endif
ESM4::Cell::Cell() : mParent(0), mFormId(0), mFlags(0), mCellFlags(0), mX(0), mY(0), mOwner(0),
mGlobal(0), mClimate(0), mWater(0), mWaterHeight(0.f), mPreloaded(false)
{
mEditorId.clear();
mFullName.clear();
mLighting.ambient = 0;
mLighting.directional = 0;
mLighting.fogNear = 0;
mLighting.unknown1 = 0.f;
mLighting.unknown2 = 0.f;
mLighting.unknown3 = 0;
mLighting.unknown4 = 0;
mLighting.unknown5 = 0.f;
mLighting.unknown6 = 0.f;
}
ESM4::Cell::~Cell()
{
}
void ESM4::Cell::init(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
mParent = reader.currWorld();
reader.clearCellGrid(); // clear until XCLC FIXME: somehow do this automatically?
// Sometimes cell 0,0 does not have an XCLC sub record (e.g. ToddLand 000009BF)
// To workaround this issue put a default value if group is "exterior sub cell" and its
// grid from label is "0 0". Note the reversed X/Y order (no matter since they're both 0
// anyway).
if (reader.grp().type == ESM4::Grp_ExteriorSubCell
&& reader.grp().label.grid[1] == 0 && reader.grp().label.grid[0] == 0)
{
ESM4::CellGrid currCellGrid;
currCellGrid.grid.x = 0;
currCellGrid.grid.y = 0;
reader.setCurrCellGrid(currCellGrid); // side effect: sets mCellGridValid true
}
}
// TODO: Try loading only EDID and XCLC (along with mFormId, mFlags and mParent)
//
// But, for external cells we may be scanning the whole record since we don't know if there is
// going to be an EDID subrecord. And the vast majority of cells are these kinds.
//
// So perhaps some testing needs to be done to see if scanning and skipping takes
// longer/shorter/same as loading the subrecords.
bool ESM4::Cell::preload(ESM4::Reader& reader)
{
if (!mPreloaded)
load(reader);
mPreloaded = true;
//return reader.skipNextGroupCellChild(); // true if found cell child group and skipped
return true; // FIXME
}
void ESM4::Cell::load(ESM4::Reader& reader)
{
if (mPreloaded)
return;
// FIXME: we may need to call setCurrCell (and maybe setCurrCellGrid?) again before loading
// cell child groups if we are loading them after restoring the context
init(reader);
reader.setCurrCell(mFormId); // save for LAND (and other children) to access later
std::uint32_t esmVer = reader.esmVersion();
bool isFONV = esmVer == ESM4::VER_132 || esmVer == ESM4::VER_133 || esmVer == ESM4::VER_134;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID:
{
if (!reader.getZString(mEditorId))
throw std::runtime_error ("CELL EDID data read error");
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
std::cout << padding << "CELL Editor ID: " << mEditorId << std::endl;
#endif
break;
}
case ESM4::SUB_XCLC:
{
//(X, Y) grid location of the cell followed by flags. Always in
//exterior cells and never in interior cells.
//
// int32 - X
// int32 - Y
// uint32 - flags (high bits look random)
//
// 0x1 - Force Hide Land Quad 1
// 0x2 - Force Hide Land Quad 2
// 0x4 - Force Hide Land Quad 3
// 0x8 - Force Hide Land Quad 4
uint32_t flags;
reader.get(mX);
reader.get(mY);
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
std::cout << padding << "CELL group " << ESM4::printLabel(reader.grp().label, reader.grp().type) << std::endl;
std::cout << padding << "CELL formId " << std::hex << reader.hdr().record.id << std::endl;
std::cout << padding << "CELL X " << std::dec << mX << ", Y " << mY << std::endl;
#endif
if (esmVer == ESM4::VER_094 || esmVer == ESM4::VER_170 || isFONV)
if (subHdr.dataSize == 12)
reader.get(flags); // not in Obvlivion, nor FO3/FONV
// Remember cell grid for later (loading LAND, NAVM which should be CELL temporary children)
// Note that grids only apply for external cells. For interior cells use the cell's formid.
ESM4::CellGrid currCell;
currCell.grid.x = (int16_t)mX;
currCell.grid.y = (int16_t)mY;
reader.setCurrCellGrid(currCell);
break;
}
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("CELL FULL data read error");
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
std::cout << padding << "Name: " << mFullName << std::endl;
#endif
break;
}
case ESM4::SUB_DATA:
{
if (esmVer == ESM4::VER_094 || esmVer == ESM4::VER_170 || isFONV)
if (subHdr.dataSize == 2)
reader.get(mCellFlags);
else
{
assert(subHdr.dataSize == 1 && "CELL unexpected DATA flag size");
reader.get((std::uint8_t&)mCellFlags);
}
else
{
reader.get((std::uint8_t&)mCellFlags); // 8 bits in Obvlivion
}
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
std::cout << padding << "flags: " << std::hex << mCellFlags << std::endl;
#endif
break;
}
case ESM4::SUB_XCLR:
{
mRegions.resize(subHdr.dataSize/sizeof(FormId));
for (std::vector<FormId>::iterator it = mRegions.begin(); it != mRegions.end(); ++it)
{
reader.getFormId(*it);
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
std::cout << padding << "region: " << std::hex << *it << std::endl;
#endif
}
break;
}
case ESM4::SUB_XOWN: reader.getFormId(mOwner); break;
case ESM4::SUB_XGLB: reader.getFormId(mGlobal); break; // Oblivion only?
case ESM4::SUB_XCCM: reader.getFormId(mClimate); break;
case ESM4::SUB_XCWT: reader.getFormId(mWater); break;
case ESM4::SUB_XCLW: reader.get(mWaterHeight); break;
case ESM4::SUB_XCLL:
{
// 92 bytes for TES5, 19*4 = 76 bytes for FO3/FONV
if (esmVer == ESM4::VER_094 || esmVer == ESM4::VER_170 || isFONV)
reader.skipSubRecordData();
else
{
assert(subHdr.dataSize == 36 && "CELL lighting size error");
reader.get(mLighting);
}
break;
}
case ESM4::SUB_TVDT:
case ESM4::SUB_MHDT:
case ESM4::SUB_XCGD:
case ESM4::SUB_LTMP:
case ESM4::SUB_LNAM:
case ESM4::SUB_XNAM:
case ESM4::SUB_XLCN:
case ESM4::SUB_XWCS:
case ESM4::SUB_XWCU:
case ESM4::SUB_XWCN:
case ESM4::SUB_XCAS:
case ESM4::SUB_XCIM:
case ESM4::SUB_XCMO:
case ESM4::SUB_XEZN:
case ESM4::SUB_XWEM:
case ESM4::SUB_XILL:
case ESM4::SUB_XCMT: // Oblivion only?
case ESM4::SUB_XRNK: // Oblivion only?
case ESM4::SUB_XCET: // FO3
case ESM4::SUB_IMPF: // FO3 Zeta
{
//std::cout << "CELL " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::CELL::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Cell::save(ESM4::Writer& writer) const
//{
//}
void ESM4::Cell::blank()
{
}

113
extern/esm4/cell.hpp vendored

@ -0,0 +1,113 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_CELL_H
#define ESM4_CELL_H
#include <cstdint>
#include <string>
#include <vector>
namespace ESM4
{
class Reader;
class Writer;
struct ReaderContext;
struct CellGroup;
typedef std::uint32_t FormId;
enum CellFlags // TES4 TES5
{ // ----------------------- ------------------------------------
CELL_Interior = 0x0001, // Can't travel from here Interior
CELL_HasWater = 0x0002, // Has water Has Water
CELL_NoTravel = 0x0004, // not Can't Travel From Here(Int only)
CELL_HideLand = 0x0008, // Force hide land (Ext) No LOD Water
// Oblivion interior (Int)
CELL_Public = 0x0020, // Public place Public Area
CELL_HandChgd = 0x0040, // Hand changed Hand Changed
CELL_QuasiExt = 0x0080, // Behave like exterior Show Sky
CELL_SkyLight = 0x0100 // Use Sky Lighting
};
// Unlike TES3, multiple cells can have the same exterior co-ordinates.
// The cells need to be organised under world spaces.
struct Cell
{
#pragma pack(push, 1)
// TES4 (guesses only), TES5 are 96 bytes
struct Lighting
{ // | Aichan Prison values
std::uint32_t ambient; // | 16 17 19 00 (RGBA)
std::uint32_t directional; // | 00 00 00 00 (RGBA)
std::uint32_t fogNear; // | 1D 1B 16 00 (RGBA)
float unknown1; // Fog Near | 00 00 00 00 = 0.f
float unknown2; // Fog Far | 00 80 3B 45 = 3000.f
std::int32_t unknown3; // rotation xy | 00 00 00 00 = 0
std::int32_t unknown4; // rotation z | 00 00 00 00 = 0
float unknown5; // Fog dir fade | 00 00 80 3F = 1.f
float unknown6; // Fog clip dist | 00 80 3B 45 = 3000.f
};
#pragma pack(pop)
FormId mParent; // world formId (for grouping cells), from the loading sequence
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::uint16_t mCellFlags; // TES5 can also be 8 bits
std::int32_t mX;
std::int32_t mY;
FormId mOwner;
FormId mGlobal;
FormId mClimate;
FormId mWater;
float mWaterHeight;
std::vector<FormId> mRegions;
Lighting mLighting;
CellGroup *mCellGroup;
Cell();
virtual ~Cell();
void init(ESM4::Reader& reader); // common setup for both preload() and load()
bool mPreloaded;
bool preload(ESM4::Reader& reader);
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
void blank();
};
}
#endif // ESM4_CELL_H

103
extern/esm4/clas.cpp vendored

@ -0,0 +1,103 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "clas.hpp"
#include <cassert>
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
#ifdef NDEBUG // FIXME: debuggigng only
#undef NDEBUG
#endif
ESM4::Class::Class()
{
mEditorId.clear();
mFullName.clear();
mDesc.clear();
mIcon.clear();
}
ESM4::Class::~Class()
{
}
void ESM4::Class::load(ESM4::Reader& reader)
{
//mFormId = reader.adjustFormId(reader.hdr().record.id); // FIXME: use master adjusted?
mFormId = reader.hdr().record.id;
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("CLAS FULL data read error");
break;
}
case ESM4::SUB_DESC:
{
if (reader.hasLocalizedStrings())
{
std::uint32_t formid;
reader.get(formid);
if (formid)
reader.getLocalizedString(formid, mDesc); // sometimes formid is null
}
else if (!reader.getZString(mDesc))
throw std::runtime_error ("CLAS DESC data read error");
break;
}
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_DATA:
{
//std::cout << "CLAS " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::CLAS::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Class::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Class::blank()
//{
//}

@ -0,0 +1,65 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_CLAS_H
#define ESM4_CLAS_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Class
{
struct Data
{
std::uint32_t attr;
};
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mDesc;
std::string mIcon;
Data mData;
Class();
~Class();
void load(ESM4::Reader& reader);
//void save(ESM4::Writer& reader) const;
//void blank();
};
}
#endif // ESM4_CLAS_H

100
extern/esm4/clot.cpp vendored

@ -0,0 +1,100 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "clot.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Clothing::Clothing() : mFormId(0), mFlags(0), mBoundRadius(0.f), mClothingFlags(0),
mScript(0), mEnchantmentPoints(0), mEnchantment(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIconMale.clear();
mIconFemale.clear();
mData.value = 0;
mData.weight = 0.f;
}
ESM4::Clothing::~Clothing()
{
}
void ESM4::Clothing::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL: reader.getZString(mFullName); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_ICON: reader.getZString(mIconMale); break;
case ESM4::SUB_ICO2: reader.getZString(mIconFemale); break;
case ESM4::SUB_DATA: reader.get(mData); break;
case ESM4::SUB_BMDT: reader.get(mClothingFlags); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_ENAM: reader.getFormId(mEnchantment); break;
case ESM4::SUB_ANAM: reader.get(mEnchantmentPoints); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_MOD2:
case ESM4::SUB_MOD3:
case ESM4::SUB_MOD4:
case ESM4::SUB_MO2B:
case ESM4::SUB_MO3B:
case ESM4::SUB_MO4B:
case ESM4::SUB_MO2T:
case ESM4::SUB_MO3T:
case ESM4::SUB_MO4T:
{
//std::cout << "CLOT " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::CLOT::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Clothing::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Clothing::blank()
//{
//}

103
extern/esm4/clot.hpp vendored

@ -0,0 +1,103 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_CLOT_H
#define ESM4_CLOT_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Clothing
{
enum Flags
{
// Biped Object Flags
Flag_Head = 0x00000001,
Flag_Hair = 0x00000002,
Flag_UpperBody = 0x00000004,
Flag_LowerBody = 0x00000008,
Flag_Hand = 0x00000010,
Flag_Foot = 0x00000020,
Flag_RightRing = 0x00000040,
Flag_LeftRing = 0x00000080,
Flag_Amulet = 0x00000100,
Flag_Weapon = 0x00000200,
Flag_BackWeapon = 0x00000400,
Flag_SideWeapon = 0x00000800,
Flag_Quiver = 0x00001000,
Flag_Shield = 0x00002000,
Flag_Torch = 0x00004000,
Flag_Tail = 0x00008000,
// General Flags
Flag_HideRings = 0x00010000,
Flag_HideAmulet = 0x00020000,
Flag_NonPlayable = 0x00400000,
Flag_Unknown = 0xCD000000
};
#pragma pack(push, 1)
struct Data
{
std::uint32_t value; // gold
float weight;
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIconMale; // inventory
std::string mIconFemale; // inventory
float mBoundRadius;
std::uint32_t mClothingFlags;
FormId mScript;
std::uint16_t mEnchantmentPoints;
FormId mEnchantment;
Data mData;
Clothing();
virtual ~Clothing();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_CLOT_H

@ -0,0 +1,111 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "common.hpp"
#include <sstream>
#include <algorithm>
#include <stdexcept>
#include <libs/platform/strings.h>
#include "formid.hpp"
namespace ESM4
{
const char *sGroupType[] =
{
"Record Type", "World Child", "Interior Cell", "Interior Sub Cell", "Exterior Cell",
"Exterior Sub Cell", "Cell Child", "Topic Child", "Cell Persistent Child",
"Cell Temporary Child", "Cell Visible Dist Child", "Unknown"
};
std::string printLabel(const GroupLabel& label, const std::uint32_t type)
{
std::ostringstream ss;
ss << std::string(sGroupType[std::min(type, (uint32_t)11)]); // avoid out of range
switch (type)
{
case ESM4::Grp_RecordType:
{
ss << ": " << std::string((char*)label.recordType, 4);
break;
}
case ESM4::Grp_ExteriorCell:
case ESM4::Grp_ExteriorSubCell:
{
//short x, y;
//y = label & 0xff;
//x = (label >> 16) & 0xff;
ss << ": grid (x, y) " << std::dec << label.grid[1] << ", " << label.grid[0];
break;
}
case ESM4::Grp_InteriorCell:
case ESM4::Grp_InteriorSubCell:
{
ss << ": block 0x" << std::hex << label.value;
break;
}
case ESM4::Grp_WorldChild:
case ESM4::Grp_CellChild:
case ESM4::Grp_TopicChild:
case ESM4::Grp_CellPersistentChild:
case ESM4::Grp_CellTemporaryChild:
case ESM4::Grp_CellVisibleDistChild:
{
ss << ": FormId 0x" << formIdToString(label.value);
break;
}
default:
break;
}
return ss.str();
}
std::string printName(const std::uint32_t typeId)
{
unsigned char typeName[4];
typeName[0] = typeId & 0xff;
typeName[1] = (typeId >> 8) & 0xff;
typeName[2] = (typeId >> 16) & 0xff;
typeName[3] = (typeId >> 24) & 0xff;
return std::string((char*)typeName, 4);
}
void gridToString(std::int16_t x, std::int16_t y, std::string& str)
{
char buf[6+6+2+1]; // longest signed 16 bit number is 6 characters (-32768)
int res = snprintf(buf, 6+6+2+1, "#%d %d", x, y);
if (res > 0 && res < 6+6+2+1)
str.assign(buf);
else
throw std::runtime_error("possible buffer overflow while converting grid");
}
}

@ -0,0 +1,933 @@
/*
Copyright (C) 2015-2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
MKTAG macro was adapated from ScummVM.
*/
#ifndef ESM4_COMMON_H
#define ESM4_COMMON_H
#include <cstdint>
#include <string>
// From ScummVM's endianness.h but for little endian
#define MKTAG(a0,a1,a2,a3) ((std::uint32_t)((a0) | ((a1) << 8) | ((a2) << 16) | ((a3) << 24)))
namespace ESM4
{
enum ESMVersions
{
VER_080 = 0x3f4ccccd, // TES4
VER_100 = 0x3f800000, // TES4
VER_132 = 0x3fa8f5c3, // FONV Courier's Stash, DeadMoney
VER_133 = 0x3faa3d71, // FONV HonestHearts
VER_134 = 0x3fab851f, // FONV, GunRunnersArsenal, LonesomeRoad, OldWorldBlues
VER_094 = 0x3f70a3d7, // TES5/FO3
VER_170 = 0x3fd9999a // TES5
};
// Based on http://www.uesp.net/wiki/Tes5Mod:Mod_File_Format
enum RecordTypes
{
REC_AACT = MKTAG('A','A','C','T'), // Action
REC_ACHR = MKTAG('A','C','H','R'), // Actor Reference
REC_ACTI = MKTAG('A','C','T','I'), // Activator
REC_ADDN = MKTAG('A','D','D','N'), // Addon Node
REC_ALCH = MKTAG('A','L','C','H'), // Potion
REC_AMMO = MKTAG('A','M','M','O'), // Ammo
REC_ANIO = MKTAG('A','N','I','O'), // Animated Object
REC_APPA = MKTAG('A','P','P','A'), // Apparatus (probably unused)
REC_ARMA = MKTAG('A','R','M','A'), // Armature (Model)
REC_ARMO = MKTAG('A','R','M','O'), // Armor
REC_ARTO = MKTAG('A','R','T','O'), // Art Object
REC_ASPC = MKTAG('A','S','P','C'), // Acoustic Space
REC_ASTP = MKTAG('A','S','T','P'), // Association Type
REC_AVIF = MKTAG('A','V','I','F'), // Actor Values/Perk Tree Graphics
REC_BOOK = MKTAG('B','O','O','K'), // Book
REC_BPTD = MKTAG('B','P','T','D'), // Body Part Data
REC_CAMS = MKTAG('C','A','M','S'), // Camera Shot
REC_CELL = MKTAG('C','E','L','L'), // Cell
REC_CLAS = MKTAG('C','L','A','S'), // Class
REC_CLFM = MKTAG('C','L','F','M'), // Color
REC_CLMT = MKTAG('C','L','M','T'), // Climate
REC_CLOT = MKTAG('C','L','O','T'), // Clothing
REC_COBJ = MKTAG('C','O','B','J'), // Constructible Object (recipes)
REC_COLL = MKTAG('C','O','L','L'), // Collision Layer
REC_CONT = MKTAG('C','O','N','T'), // Container
REC_CPTH = MKTAG('C','P','T','H'), // Camera Path
REC_CREA = MKTAG('C','R','E','A'), // Creature
REC_CSTY = MKTAG('C','S','T','Y'), // Combat Style
REC_DEBR = MKTAG('D','E','B','R'), // Debris
REC_DIAL = MKTAG('D','I','A','L'), // Dialog Topic
REC_DLBR = MKTAG('D','L','B','R'), // Dialog Branch
REC_DLVW = MKTAG('D','L','V','W'), // Dialog View
REC_DOBJ = MKTAG('D','O','B','J'), // Default Object Manager
REC_DOOR = MKTAG('D','O','O','R'), // Door
REC_DUAL = MKTAG('D','U','A','L'), // Dual Cast Data (possibly unused)
//REC_ECZN = MKTAG('E','C','Z','N'), // Encounter Zone
REC_EFSH = MKTAG('E','F','S','H'), // Effect Shader
REC_ENCH = MKTAG('E','N','C','H'), // Enchantment
REC_EQUP = MKTAG('E','Q','U','P'), // Equip Slot (flag-type values)
REC_EXPL = MKTAG('E','X','P','L'), // Explosion
REC_EYES = MKTAG('E','Y','E','S'), // Eyes
REC_FACT = MKTAG('F','A','C','T'), // Faction
REC_FLOR = MKTAG('F','L','O','R'), // Flora
REC_FLST = MKTAG('F','L','S','T'), // Form List (non-leveled list)
REC_FSTP = MKTAG('F','S','T','P'), // Footstep
REC_FSTS = MKTAG('F','S','T','S'), // Footstep Set
REC_FURN = MKTAG('F','U','R','N'), // Furniture
REC_GLOB = MKTAG('G','L','O','B'), // Global Variable
REC_GMST = MKTAG('G','M','S','T'), // Game Setting
REC_GRAS = MKTAG('G','R','A','S'), // Grass
REC_GRUP = MKTAG('G','R','U','P'), // Form Group
REC_HAIR = MKTAG('H','A','I','R'), // Hair
//REC_HAZD = MKTAG('H','A','Z','D'), // Hazard
REC_HDPT = MKTAG('H','D','P','T'), // Head Part
REC_IDLE = MKTAG('I','D','L','E'), // Idle Animation
REC_IDLM = MKTAG('I','D','L','M'), // Idle Marker
REC_IMAD = MKTAG('I','M','A','D'), // Image Space Modifier
REC_IMGS = MKTAG('I','M','G','S'), // Image Space
REC_INFO = MKTAG('I','N','F','O'), // Dialog Topic Info
REC_INGR = MKTAG('I','N','G','R'), // Ingredient
REC_IPCT = MKTAG('I','P','C','T'), // Impact Data
REC_IPDS = MKTAG('I','P','D','S'), // Impact Data Set
REC_KEYM = MKTAG('K','E','Y','M'), // Key
REC_KYWD = MKTAG('K','Y','W','D'), // Keyword
REC_LAND = MKTAG('L','A','N','D'), // Land
REC_LCRT = MKTAG('L','C','R','T'), // Location Reference Type
REC_LCTN = MKTAG('L','C','T','N'), // Location
REC_LGTM = MKTAG('L','G','T','M'), // Lighting Template
REC_LIGH = MKTAG('L','I','G','H'), // Light
REC_LSCR = MKTAG('L','S','C','R'), // Load Screen
REC_LTEX = MKTAG('L','T','E','X'), // Land Texture
REC_LVLC = MKTAG('L','V','L','C'), // Leveled Creature
REC_LVLI = MKTAG('L','V','L','I'), // Leveled Item
REC_LVLN = MKTAG('L','V','L','N'), // Leveled Actor
REC_LVSP = MKTAG('L','V','S','P'), // Leveled Spell
REC_MATO = MKTAG('M','A','T','O'), // Material Object
REC_MATT = MKTAG('M','A','T','T'), // Material Type
REC_MESG = MKTAG('M','E','S','G'), // Message
REC_MGEF = MKTAG('M','G','E','F'), // Magic Effect
REC_MISC = MKTAG('M','I','S','C'), // Misc. Object
REC_MOVT = MKTAG('M','O','V','T'), // Movement Type
REC_MSTT = MKTAG('M','S','T','T'), // Movable Static
REC_MUSC = MKTAG('M','U','S','C'), // Music Type
REC_MUST = MKTAG('M','U','S','T'), // Music Track
REC_NAVI = MKTAG('N','A','V','I'), // Navigation (master data)
REC_NAVM = MKTAG('N','A','V','M'), // Nav Mesh
REC_NPC_ = MKTAG('N','P','C','_'), // Actor (NPC, Creature)
REC_OTFT = MKTAG('O','T','F','T'), // Outfit
REC_PACK = MKTAG('P','A','C','K'), // AI Package
REC_PERK = MKTAG('P','E','R','K'), // Perk
REC_PGRE = MKTAG('P','G','R','E'), // Placed grenade
REC_PHZD = MKTAG('P','H','Z','D'), // Placed hazard
REC_PROJ = MKTAG('P','R','O','J'), // Projectile
REC_QUST = MKTAG('Q','U','S','T'), // Quest
REC_RACE = MKTAG('R','A','C','E'), // Race / Creature type
REC_REFR = MKTAG('R','E','F','R'), // Object Reference
REC_REGN = MKTAG('R','E','G','N'), // Region (Audio/Weather)
REC_RELA = MKTAG('R','E','L','A'), // Relationship
REC_REVB = MKTAG('R','E','V','B'), // Reverb Parameters
REC_RFCT = MKTAG('R','F','C','T'), // Visual Effect
REC_SBSP = MKTAG('S','B','S','P'), // Subspace (TES4 only?)
REC_SCEN = MKTAG('S','C','E','N'), // Scene
REC_SCRL = MKTAG('S','C','R','L'), // Scroll
REC_SGST = MKTAG('S','G','S','T'), // Sigil Stone
REC_SHOU = MKTAG('S','H','O','U'), // Shout
REC_SLGM = MKTAG('S','L','G','M'), // Soul Gem
REC_SMBN = MKTAG('S','M','B','N'), // Story Manager Branch Node
REC_SMEN = MKTAG('S','M','E','N'), // Story Manager Event Node
REC_SMQN = MKTAG('S','M','Q','N'), // Story Manager Quest Node
REC_SNCT = MKTAG('S','N','C','T'), // Sound Category
REC_SNDR = MKTAG('S','N','D','R'), // Sound Reference
REC_SOPM = MKTAG('S','O','P','M'), // Sound Output Model
REC_SOUN = MKTAG('S','O','U','N'), // Sound
REC_SPEL = MKTAG('S','P','E','L'), // Spell
REC_SPGD = MKTAG('S','P','G','D'), // Shader Particle Geometry
REC_STAT = MKTAG('S','T','A','T'), // Static
REC_TACT = MKTAG('T','A','C','T'), // Talking Activator
REC_TES4 = MKTAG('T','E','S','4'), // Plugin info
REC_TREE = MKTAG('T','R','E','E'), // Tree
REC_TXST = MKTAG('T','X','S','T'), // Texture Set
REC_VTYP = MKTAG('V','T','Y','P'), // Voice Type
REC_WATR = MKTAG('W','A','T','R'), // Water Type
REC_WEAP = MKTAG('W','E','A','P'), // Weapon
REC_WOOP = MKTAG('W','O','O','P'), // Word Of Power
REC_WRLD = MKTAG('W','R','L','D'), // World Space
REC_WTHR = MKTAG('W','T','H','R'), // Weather
REC_ACRE = MKTAG('A','C','R','E'), // Placed Creature (TES4 only?)
REC_PGRD = MKTAG('P','G','R','D'), // Pathgrid (TES4 only?)
REC_ROAD = MKTAG('R','O','A','D') // Road (TES4 only?)
};
enum SubRecordTypes
{
SUB_HEDR = MKTAG('H','E','D','R'),
SUB_CNAM = MKTAG('C','N','A','M'),
SUB_SNAM = MKTAG('S','N','A','M'), // TES4 only?
SUB_MAST = MKTAG('M','A','S','T'),
SUB_DATA = MKTAG('D','A','T','A'),
SUB_ONAM = MKTAG('O','N','A','M'),
SUB_INTV = MKTAG('I','N','T','V'),
SUB_INCC = MKTAG('I','N','C','C'),
SUB_OFST = MKTAG('O','F','S','T'), // TES4 only?
SUB_DELE = MKTAG('D','E','L','E'), // TES4 only?
SUB_DNAM = MKTAG('D','N','A','M'),
SUB_EDID = MKTAG('E','D','I','D'),
SUB_FULL = MKTAG('F','U','L','L'),
SUB_LTMP = MKTAG('L','T','M','P'),
SUB_MHDT = MKTAG('M','H','D','T'),
SUB_MNAM = MKTAG('M','N','A','M'),
SUB_MODL = MKTAG('M','O','D','L'),
SUB_NAM0 = MKTAG('N','A','M','0'),
SUB_NAM2 = MKTAG('N','A','M','2'),
SUB_NAM3 = MKTAG('N','A','M','3'),
SUB_NAM4 = MKTAG('N','A','M','4'),
SUB_NAM9 = MKTAG('N','A','M','9'),
SUB_NAMA = MKTAG('N','A','M','A'),
SUB_PNAM = MKTAG('P','N','A','M'),
SUB_RNAM = MKTAG('R','N','A','M'),
SUB_TNAM = MKTAG('T','N','A','M'),
SUB_UNAM = MKTAG('U','N','A','M'),
SUB_WCTR = MKTAG('W','C','T','R'),
SUB_WNAM = MKTAG('W','N','A','M'),
SUB_XEZN = MKTAG('X','E','Z','N'),
SUB_XLCN = MKTAG('X','L','C','N'),
SUB_XXXX = MKTAG('X','X','X','X'),
SUB_ZNAM = MKTAG('Z','N','A','M'),
SUB_MODT = MKTAG('M','O','D','T'),
SUB_ICON = MKTAG('I','C','O','N'), // TES4 only?
SUB_NVER = MKTAG('N','V','E','R'),
SUB_NVMI = MKTAG('N','V','M','I'),
SUB_NVPP = MKTAG('N','V','P','P'),
SUB_NVSI = MKTAG('N','V','S','I'),
SUB_NVNM = MKTAG('N','V','N','M'),
SUB_NNAM = MKTAG('N','N','A','M'),
SUB_XCLC = MKTAG('X','C','L','C'),
SUB_XCLL = MKTAG('X','C','L','L'),
SUB_TVDT = MKTAG('T','V','D','T'),
SUB_XCGD = MKTAG('X','C','G','D'),
SUB_LNAM = MKTAG('L','N','A','M'),
SUB_XCLW = MKTAG('X','C','L','W'),
SUB_XNAM = MKTAG('X','N','A','M'),
SUB_XCLR = MKTAG('X','C','L','R'),
SUB_XWCS = MKTAG('X','W','C','S'),
SUB_XWCN = MKTAG('X','W','C','N'),
SUB_XWCU = MKTAG('X','W','C','U'),
SUB_XCWT = MKTAG('X','C','W','T'),
SUB_XOWN = MKTAG('X','O','W','N'),
SUB_XILL = MKTAG('X','I','L','L'),
SUB_XWEM = MKTAG('X','W','E','M'),
SUB_XCCM = MKTAG('X','C','C','M'),
SUB_XCAS = MKTAG('X','C','A','S'),
SUB_XCMO = MKTAG('X','C','M','O'),
SUB_XCIM = MKTAG('X','C','I','M'),
SUB_XCMT = MKTAG('X','C','M','T'), // TES4 only?
SUB_XRNK = MKTAG('X','R','N','K'), // TES4 only?
SUB_XGLB = MKTAG('X','G','L','B'), // TES4 only?
SUB_VNML = MKTAG('V','N','M','L'),
SUB_VHGT = MKTAG('V','H','G','T'),
SUB_VCLR = MKTAG('V','C','L','R'),
SUA_BTXT = MKTAG('B','T','X','T'),
SUB_ATXT = MKTAG('A','T','X','T'),
SUB_VTXT = MKTAG('V','T','X','T'),
SUB_VTEX = MKTAG('V','T','E','X'),
SUB_HNAM = MKTAG('H','N','A','M'),
SUB_GNAM = MKTAG('G','N','A','M'),
SUB_RCLR = MKTAG('R','C','L','R'),
SUB_RPLI = MKTAG('R','P','L','I'),
SUB_RPLD = MKTAG('R','P','L','D'),
SUB_RDAT = MKTAG('R','D','A','T'),
SUB_RDMD = MKTAG('R','D','M','D'), // TES4 only?
SUB_RDSD = MKTAG('R','D','S','D'), // TES4 only?
SUB_RDGS = MKTAG('R','D','G','S'), // TES4 only?
SUB_RDMO = MKTAG('R','D','M','O'),
SUB_RDSA = MKTAG('R','D','S','A'),
SUB_RDWT = MKTAG('R','D','W','T'),
SUB_RDOT = MKTAG('R','D','O','T'),
SUB_RDMP = MKTAG('R','D','M','P'),
SUB_MODB = MKTAG('M','O','D','B'),
SUB_OBND = MKTAG('O','B','N','D'),
SUB_MODS = MKTAG('M','O','D','S'),
SUB_NAME = MKTAG('N','A','M','E'),
SUB_XMRK = MKTAG('X','M','R','K'),
SUB_FNAM = MKTAG('F','N','A','M'),
SUB_XSCL = MKTAG('X','S','C','L'),
SUB_XTEL = MKTAG('X','T','E','L'),
SUB_XTRG = MKTAG('X','T','R','G'),
SUB_XSED = MKTAG('X','S','E','D'),
SUB_XLOD = MKTAG('X','L','O','D'),
SUB_XPCI = MKTAG('X','P','C','I'),
SUB_XLOC = MKTAG('X','L','O','C'),
SUB_XESP = MKTAG('X','E','S','P'),
SUB_XLCM = MKTAG('X','L','C','M'),
SUB_XRTM = MKTAG('X','R','T','M'),
SUB_XACT = MKTAG('X','A','C','T'),
SUB_XCNT = MKTAG('X','C','N','T'),
SUB_VMAD = MKTAG('V','M','A','D'),
SUB_XPRM = MKTAG('X','P','R','M'),
SUB_XMBO = MKTAG('X','M','B','O'),
SUB_XPOD = MKTAG('X','P','O','D'),
SUB_XRMR = MKTAG('X','R','M','R'),
SUB_INAM = MKTAG('I','N','A','M'),
SUB_SCHR = MKTAG('S','C','H','R'),
SUB_XLRM = MKTAG('X','L','R','M'),
SUB_XRGD = MKTAG('X','R','G','D'),
SUB_XRDS = MKTAG('X','R','D','S'),
SUB_XEMI = MKTAG('X','E','M','I'),
SUB_XLIG = MKTAG('X','L','I','G'),
SUB_XALP = MKTAG('X','A','L','P'),
SUB_XNDP = MKTAG('X','N','D','P'),
SUB_XAPD = MKTAG('X','A','P','D'),
SUB_XAPR = MKTAG('X','A','P','R'),
SUB_XLIB = MKTAG('X','L','I','B'),
SUB_XLKR = MKTAG('X','L','K','R'),
SUB_XLRT = MKTAG('X','L','R','T'),
SUB_XCVL = MKTAG('X','C','V','L'),
SUB_XCVR = MKTAG('X','C','V','R'),
SUB_XCZA = MKTAG('X','C','Z','A'),
SUB_XCZC = MKTAG('X','C','Z','C'),
SUB_XFVC = MKTAG('X','F','V','C'),
SUB_XHTW = MKTAG('X','H','T','W'),
SUB_XIS2 = MKTAG('X','I','S','2'),
SUB_XMBR = MKTAG('X','M','B','R'),
SUB_XCCP = MKTAG('X','C','C','P'),
SUB_XPWR = MKTAG('X','P','W','R'),
SUB_XTRI = MKTAG('X','T','R','I'),
SUB_XATR = MKTAG('X','A','T','R'),
SUB_XPRD = MKTAG('X','P','R','D'),
SUB_XPPA = MKTAG('X','P','P','A'),
SUB_PDTO = MKTAG('P','D','T','O'),
SUB_XLRL = MKTAG('X','L','R','L'),
SUB_QNAM = MKTAG('Q','N','A','M'),
SUB_COCT = MKTAG('C','O','C','T'),
SUB_COED = MKTAG('C','O','E','D'),
SUB_CNTO = MKTAG('C','N','T','O'),
SUB_SCRI = MKTAG('S','C','R','I'),
SUB_BNAM = MKTAG('B','N','A','M'),
SUB_BMDT = MKTAG('B','M','D','T'),
SUB_MOD2 = MKTAG('M','O','D','2'),
SUB_MOD3 = MKTAG('M','O','D','3'),
SUB_MOD4 = MKTAG('M','O','D','4'),
SUB_MO2B = MKTAG('M','O','2','B'),
SUB_MO3B = MKTAG('M','O','3','B'),
SUB_MO4B = MKTAG('M','O','4','B'),
SUB_MO2T = MKTAG('M','O','2','T'),
SUB_MO3T = MKTAG('M','O','3','T'),
SUB_MO4T = MKTAG('M','O','4','T'),
SUB_ANAM = MKTAG('A','N','A','M'),
SUB_ENAM = MKTAG('E','N','A','M'),
SUB_ICO2 = MKTAG('I','C','O','2'),
SUB_ACBS = MKTAG('A','C','B','S'),
SUB_SPLO = MKTAG('S','P','L','O'),
SUB_AIDT = MKTAG('A','I','D','T'),
SUB_PKID = MKTAG('P','K','I','D'),
SUB_HCLR = MKTAG('H','C','L','R'),
SUB_FGGS = MKTAG('F','G','G','S'),
SUB_FGGA = MKTAG('F','G','G','A'),
SUB_FGTS = MKTAG('F','G','T','S'),
SUB_KFFZ = MKTAG('K','F','F','Z'),
SUB_PFIG = MKTAG('P','F','I','G'),
SUB_PFPC = MKTAG('P','F','P','C'),
SUB_XHRS = MKTAG('X','H','R','S'),
SUB_XMRC = MKTAG('X','M','R','C'),
SUB_SNDD = MKTAG('S','N','D','D'),
SUB_SNDX = MKTAG('S','N','D','X'),
SUB_DESC = MKTAG('D','E','S','C'),
SUB_ENIT = MKTAG('E','N','I','T'),
SUB_EFID = MKTAG('E','F','I','D'),
SUB_EFIT = MKTAG('E','F','I','T'),
SUB_SCIT = MKTAG('S','C','I','T'),
SUB_SOUL = MKTAG('S','O','U','L'),
SUB_SLCP = MKTAG('S','L','C','P'),
SUB_CSCR = MKTAG('C','S','C','R'),
SUB_CSDI = MKTAG('C','S','D','I'),
SUB_CSDC = MKTAG('C','S','D','C'),
SUB_NIFZ = MKTAG('N','I','F','Z'),
SUB_CSDT = MKTAG('C','S','D','T'),
SUB_NAM1 = MKTAG('N','A','M','1'),
SUB_NIFT = MKTAG('N','I','F','T'),
SUB_LVLD = MKTAG('L','V','L','D'),
SUB_LVLF = MKTAG('L','V','L','F'),
SUB_LVLO = MKTAG('L','V','L','O'),
SUB_BODT = MKTAG('B','O','D','T'),
SUB_YNAM = MKTAG('Y','N','A','M'),
SUB_DEST = MKTAG('D','E','S','T'),
SUB_DMDL = MKTAG('D','M','D','L'),
SUB_DMDS = MKTAG('D','M','D','S'),
SUB_DMDT = MKTAG('D','M','D','T'),
SUB_DSTD = MKTAG('D','S','T','D'),
SUB_DSTF = MKTAG('D','S','T','F'),
SUB_KNAM = MKTAG('K','N','A','M'),
SUB_KSIZ = MKTAG('K','S','I','Z'),
SUB_KWDA = MKTAG('K','W','D','A'),
SUB_VNAM = MKTAG('V','N','A','M'),
SUB_SDSC = MKTAG('S','D','S','C'),
SUB_MO2S = MKTAG('M','O','2','S'),
SUB_MO4S = MKTAG('M','O','4','S'),
SUB_BOD2 = MKTAG('B','O','D','2'),
SUB_BAMT = MKTAG('B','A','M','T'),
SUB_BIDS = MKTAG('B','I','D','S'),
SUB_ETYP = MKTAG('E','T','Y','P'),
SUB_BMCT = MKTAG('B','M','C','T'),
SUB_MICO = MKTAG('M','I','C','O'),
SUB_MIC2 = MKTAG('M','I','C','2'),
SUB_EAMT = MKTAG('E','A','M','T'),
SUB_EITM = MKTAG('E','I','T','M'),
SUB_SCTX = MKTAG('S','C','T','X'),
SUB_XLTW = MKTAG('X','L','T','W'),
SUB_XMBP = MKTAG('X','M','B','P'),
SUB_XOCP = MKTAG('X','O','C','P'),
SUB_XRGB = MKTAG('X','R','G','B'),
SUB_XSPC = MKTAG('X','S','P','C'),
SUB_XTNM = MKTAG('X','T','N','M'),
SUB_ATKR = MKTAG('A','T','K','R'),
SUB_CRIF = MKTAG('C','R','I','F'),
SUB_DOFT = MKTAG('D','O','F','T'),
SUB_DPLT = MKTAG('D','P','L','T'),
SUB_ECOR = MKTAG('E','C','O','R'),
SUB_ATKD = MKTAG('A','T','K','D'),
SUB_ATKE = MKTAG('A','T','K','E'),
SUB_FTST = MKTAG('F','T','S','T'),
SUB_HCLF = MKTAG('H','C','L','F'),
SUB_NAM5 = MKTAG('N','A','M','5'),
SUB_NAM6 = MKTAG('N','A','M','6'),
SUB_NAM7 = MKTAG('N','A','M','7'),
SUB_NAM8 = MKTAG('N','A','M','8'),
SUB_PRKR = MKTAG('P','R','K','R'),
SUB_PRKZ = MKTAG('P','R','K','Z'),
SUB_SOFT = MKTAG('S','O','F','T'),
SUB_SPCT = MKTAG('S','P','C','T'),
SUB_TINC = MKTAG('T','I','N','C'),
SUB_TIAS = MKTAG('T','I','A','S'),
SUB_TINI = MKTAG('T','I','N','I'),
SUB_TINV = MKTAG('T','I','N','V'),
SUB_TPLT = MKTAG('T','P','L','T'),
SUB_VTCK = MKTAG('V','T','C','K'),
SUB_SHRT = MKTAG('S','H','R','T'),
SUB_SPOR = MKTAG('S','P','O','R'),
SUB_XHOR = MKTAG('X','H','O','R'),
SUB_CTDA = MKTAG('C','T','D','A'),
SUB_CRDT = MKTAG('C','R','D','T'),
SUB_FNMK = MKTAG('F','N','M','K'),
SUB_FNPR = MKTAG('F','N','P','R'),
SUB_WBDT = MKTAG('W','B','D','T'),
SUB_QUAL = MKTAG('Q','U','A','L'),
SUB_INDX = MKTAG('I','N','D','X'),
SUB_ATTR = MKTAG('A','T','T','R'),
SUB_XHLT = MKTAG('X','H','L','T'), // Unofficial Oblivion Patch
SUB_XCHG = MKTAG('X','C','H','G'), // thievery.exp
SUB_XIBS = MKTAG('X','I','B','S'), // FO3
SUB_REPL = MKTAG('R','E','P','L'), // FO3
SUB_BIPL = MKTAG('B','I','P','L'), // FO3
SUB_MODD = MKTAG('M','O','D','D'), // FO3
SUB_MOSD = MKTAG('M','O','S','D'), // FO3
SUB_MO3S = MKTAG('M','O','3','S'), // FO3
SUB_XCET = MKTAG('X','C','E','T'), // FO3
SUB_LVLG = MKTAG('L','V','L','G'), // FO3
SUB_NVCI = MKTAG('N','V','C','I'), // FO3
SUB_NVVX = MKTAG('N','V','V','X'), // FO3
SUB_NVTR = MKTAG('N','V','T','R'), // FO3
SUB_NVCA = MKTAG('N','V','C','A'), // FO3
SUB_NVDP = MKTAG('N','V','D','P'), // FO3
SUB_NVGD = MKTAG('N','V','G','D'), // FO3
SUB_NVEX = MKTAG('N','V','E','X'), // FO3
SUB_XHLP = MKTAG('X','H','L','P'), // FO3
SUB_XRDO = MKTAG('X','R','D','O'), // FO3
SUB_XAMT = MKTAG('X','A','M','T'), // FO3
SUB_XAMC = MKTAG('X','A','M','C'), // FO3
SUB_XRAD = MKTAG('X','R','A','D'), // FO3
SUB_XORD = MKTAG('X','O','R','D'), // FO3
SUB_XCLP = MKTAG('X','C','L','P'), // FO3
SUB_SCDA = MKTAG('S','C','D','A'), // FO3
SUB_SCRO = MKTAG('S','C','R','O'), // FO3
SUB_IMPS = MKTAG('I','M','P','S'), // FO3 Anchorage
SUB_IMPF = MKTAG('I','M','P','F'), // FO3 Anchorage
SUB_XATO = MKTAG('X','A','T','O'), // FONV
SUB_DAT2 = MKTAG('D','A','T','2'), // FONV
SUB_RCIL = MKTAG('R','C','I','L'), // FONV
SUB_MMRK = MKTAG('M','M','R','K'), // FONV
SUB_SCRV = MKTAG('S','C','R','V'), // FONV
SUB_SCVR = MKTAG('S','C','V','R'), // FONV
SUB_SLSD = MKTAG('S','L','S','D'), // FONV
SUB_XSRF = MKTAG('X','S','R','F'), // FONV
SUB_XSRD = MKTAG('X','S','R','D'), // FONV
SUB_WMI1 = MKTAG('W','M','I','1'), // FONV
SUB_RDID = MKTAG('R','D','I','D'), // FONV
SUB_RDSB = MKTAG('R','D','S','B'), // FONV
SUB_RDSI = MKTAG('R','D','S','I'), // FONV
SUB_BRUS = MKTAG('B','R','U','S'), // FONV
SUB_VATS = MKTAG('V','A','T','S'), // FONV
SUB_VANM = MKTAG('V','A','N','M'), // FONV
SUB_MWD1 = MKTAG('M','W','D','1'), // FONV
SUB_MWD2 = MKTAG('M','W','D','2'), // FONV
SUB_MWD3 = MKTAG('M','W','D','3'), // FONV
SUB_MWD4 = MKTAG('M','W','D','4'), // FONV
SUB_MWD5 = MKTAG('M','W','D','5'), // FONV
SUB_MWD6 = MKTAG('M','W','D','6'), // FONV
SUB_MWD7 = MKTAG('M','W','D','7'), // FONV
SUB_WMI2 = MKTAG('W','M','I','2'), // FONV
SUB_WMI3 = MKTAG('W','M','I','3'), // FONV
SUB_WMS1 = MKTAG('W','M','S','1'), // FONV
SUB_WMS2 = MKTAG('W','M','S','2'), // FONV
SUB_WNM1 = MKTAG('W','N','M','1'), // FONV
SUB_WNM2 = MKTAG('W','N','M','2'), // FONV
SUB_WNM3 = MKTAG('W','N','M','3'), // FONV
SUB_WNM4 = MKTAG('W','N','M','4'), // FONV
SUB_WNM5 = MKTAG('W','N','M','5'), // FONV
SUB_WNM6 = MKTAG('W','N','M','6'), // FONV
SUB_WNM7 = MKTAG('W','N','M','7'), // FONV
};
enum MagicEffectID
{
// Alteration
EFI_BRDN = MKTAG('B','R','D','N'),
EFI_FTHR = MKTAG('F','T','H','R'),
EFI_FISH = MKTAG('F','I','S','H'),
EFI_FRSH = MKTAG('F','R','S','H'),
EFI_OPEN = MKTAG('O','P','N','N'),
EFI_SHLD = MKTAG('S','H','L','D'),
EFI_LISH = MKTAG('L','I','S','H'),
EFI_WABR = MKTAG('W','A','B','R'),
EFI_WAWA = MKTAG('W','A','W','A'),
// Conjuration
EFI_BABO = MKTAG('B','A','B','O'), // Bound Boots
EFI_BACU = MKTAG('B','A','C','U'), // Bound Cuirass
EFI_BAGA = MKTAG('B','A','G','A'), // Bound Gauntlets
EFI_BAGR = MKTAG('B','A','G','R'), // Bound Greaves
EFI_BAHE = MKTAG('B','A','H','E'), // Bound Helmet
EFI_BASH = MKTAG('B','A','S','H'), // Bound Shield
EFI_BWAX = MKTAG('B','W','A','X'), // Bound Axe
EFI_BWBO = MKTAG('B','W','B','O'), // Bound Bow
EFI_BWDA = MKTAG('B','W','D','A'), // Bound Dagger
EFI_BWMA = MKTAG('B','W','M','A'), // Bound Mace
EFI_BWSW = MKTAG('B','W','S','W'), // Bound Sword
EFI_Z001 = MKTAG('Z','0','0','1'), // Summon Rufio's Ghost
EFI_Z002 = MKTAG('Z','0','0','2'), // Summon Ancestor Guardian
EFI_Z003 = MKTAG('Z','0','0','3'), // Summon Spiderling
EFI_Z005 = MKTAG('Z','0','0','5'), // Summon Bear
EFI_ZCLA = MKTAG('Z','C','L','A'), // Summon Clannfear
EFI_ZDAE = MKTAG('Z','D','A','E'), // Summon Daedroth
EFI_ZDRE = MKTAG('Z','D','R','E'), // Summon Dremora
EFI_ZDRL = MKTAG('Z','D','R','L'), // Summon Dremora Lord
EFI_ZFIA = MKTAG('Z','F','I','A'), // Summon Flame Atronach
EFI_ZFRA = MKTAG('Z','F','R','A'), // Summon Frost Atronach
EFI_ZGHO = MKTAG('Z','G','H','O'), // Summon Ghost
EFI_ZHDZ = MKTAG('Z','H','D','Z'), // Summon Headless Zombie
EFI_ZLIC = MKTAG('Z','L','I','C'), // Summon Lich
EFI_ZSCA = MKTAG('Z','S','C','A'), // Summon Scamp
EFI_ZSKE = MKTAG('Z','S','K','E'), // Summon Skeleton
EFI_ZSKA = MKTAG('Z','S','K','A'), // Summon Skeleton Guardian
EFI_ZSKH = MKTAG('Z','S','K','H'), // Summon Skeleton Hero
EFI_ZSKC = MKTAG('Z','S','K','C'), // Summon Skeleton Champion
EFI_ZSPD = MKTAG('Z','S','P','D'), // Summon Spider Daedra
EFI_ZSTA = MKTAG('Z','S','T','A'), // Summon Storm Atronach
EFI_ZWRA = MKTAG('Z','W','R','A'), // Summon Faded Wraith
EFI_ZWRL = MKTAG('Z','W','R','L'), // Summon Gloom Wraith
EFI_ZXIV = MKTAG('Z','X','I','V'), // Summon Xivilai
EFI_ZZOM = MKTAG('Z','Z','O','M'), // Summon Zombie
EFI_TURN = MKTAG('T','U','R','N'), // Turn Undead
// Destruction
EFI_DGAT = MKTAG('D','G','A','T'), // Damage Attribute
EFI_DGFA = MKTAG('D','G','F','A'), // Damage Fatigue
EFI_DGHE = MKTAG('D','G','H','E'), // Damage Health
EFI_DGSP = MKTAG('D','G','S','P'), // Damage Magicka
EFI_DIAR = MKTAG('D','I','A','R'), // Disintegrate Armor
EFI_DIWE = MKTAG('D','I','W','E'), // Disintegrate Weapon
EFI_DRAT = MKTAG('D','R','A','T'), // Drain Attribute
EFI_DRFA = MKTAG('D','R','F','A'), // Drain Fatigue
EFI_DRHE = MKTAG('D','R','H','E'), // Drain Health
EFI_DRSP = MKTAG('D','R','S','P'), // Drain Magicka
EFI_DRSK = MKTAG('D','R','S','K'), // Drain Skill
EFI_FIDG = MKTAG('F','I','D','G'), // Fire Damage
EFI_FRDG = MKTAG('F','R','D','G'), // Frost Damage
EFI_SHDG = MKTAG('S','H','D','G'), // Shock Damage
EFI_WKDI = MKTAG('W','K','D','I'), // Weakness to Disease
EFI_WKFI = MKTAG('W','K','F','I'), // Weakness to Fire
EFI_WKFR = MKTAG('W','K','F','R'), // Weakness to Frost
EFI_WKMA = MKTAG('W','K','M','A'), // Weakness to Magic
EFI_WKNW = MKTAG('W','K','N','W'), // Weakness to Normal Weapons
EFI_WKPO = MKTAG('W','K','P','O'), // Weakness to Poison
EFI_WKSH = MKTAG('W','K','S','H'), // Weakness to Shock
// Illusion
EFI_CALM = MKTAG('C','A','L','M'), // Calm
EFI_CHML = MKTAG('C','H','M','L'), // Chameleon
EFI_CHRM = MKTAG('C','H','R','M'), // Charm
EFI_COCR = MKTAG('C','O','C','R'), // Command Creature
EFI_COHU = MKTAG('C','O','H','U'), // Command Humanoid
EFI_DEMO = MKTAG('D','E','M','O'), // Demoralize
EFI_FRNZ = MKTAG('F','R','N','Z'), // Frenzy
EFI_INVI = MKTAG('I','N','V','I'), // Invisibility
EFI_LGHT = MKTAG('L','G','H','T'), // Light
EFI_NEYE = MKTAG('N','E','Y','E'), // Night-Eye
EFI_PARA = MKTAG('P','A','R','A'), // Paralyze
EFI_RALY = MKTAG('R','A','L','Y'), // Rally
EFI_SLNC = MKTAG('S','L','N','C'), // Silence
// Mysticism
EFI_DTCT = MKTAG('D','T','C','T'), // Detect Life
EFI_DSPL = MKTAG('D','S','P','L'), // Dispel
EFI_REDG = MKTAG('R','E','D','G'), // Reflect Damage
EFI_RFLC = MKTAG('R','F','L','C'), // Reflect Spell
EFI_STRP = MKTAG('S','T','R','P'), // Soul Trap
EFI_SABS = MKTAG('S','A','B','S'), // Spell Absorption
EFI_TELE = MKTAG('T','E','L','E'), // Telekinesis
// Restoration
EFI_ABAT = MKTAG('A','B','A','T'), // Absorb Attribute
EFI_ABFA = MKTAG('A','B','F','A'), // Absorb Fatigue
EFI_ABHe = MKTAG('A','B','H','e'), // Absorb Health
EFI_ABSP = MKTAG('A','B','S','P'), // Absorb Magicka
EFI_ABSK = MKTAG('A','B','S','K'), // Absorb Skill
EFI_1400 = MKTAG('1','4','0','0'), // Cure Disease
EFI_CUPA = MKTAG('C','U','P','A'), // Cure Paralysis
EFI_CUPO = MKTAG('C','U','P','O'), // Cure Poison
EFI_FOAT = MKTAG('F','O','A','T'), // Fortify Attribute
EFI_FOFA = MKTAG('F','O','F','A'), // Fortify Fatigue
EFI_FOHE = MKTAG('F','O','H','E'), // Fortify Health
EFI_FOSP = MKTAG('F','O','S','P'), // Fortify Magicka
EFI_FOSK = MKTAG('F','O','S','K'), // Fortify Skill
EFI_RSDI = MKTAG('R','S','D','I'), // Resist Disease
EFI_RSFI = MKTAG('R','S','F','I'), // Resist Fire
EFI_RSFR = MKTAG('R','S','F','R'), // Resist Frost
EFI_RSMA = MKTAG('R','S','M','A'), // Resist Magic
EFI_RSNW = MKTAG('R','S','N','W'), // Resist Normal Weapons
EFI_RSPA = MKTAG('R','S','P','A'), // Resist Paralysis
EFI_RSPO = MKTAG('R','S','P','O'), // Resist Poison
EFI_RSSH = MKTAG('R','S','S','H'), // Resist Shock
EFI_REAT = MKTAG('R','E','A','T'), // Restore Attribute
EFI_REFA = MKTAG('R','E','F','A'), // Restore Fatigue
EFI_REHE = MKTAG('R','E','H','E'), // Restore Health
EFI_RESP = MKTAG('R','E','S','P'), // Restore Magicka
// Effects
EFI_LOCK = MKTAG('L','O','C','K'), // Lock Lock
EFI_SEFF = MKTAG('S','E','F','F'), // Script Effect
EFI_Z020 = MKTAG('Z','0','2','0'), // Summon 20 Extra
EFI_MYHL = MKTAG('M','Y','H','L'), // Summon Mythic Dawn Helmet
EFI_MYTH = MKTAG('M','Y','T','H'), // Summon Mythic Dawn Armor
EFI_REAN = MKTAG('R','E','A','N'), // Reanimate
EFI_DISE = MKTAG('D','I','S','E'), // Disease Info
EFI_POSN = MKTAG('P','O','S','N'), // Poison Info
EFI_DUMY = MKTAG('D','U','M','Y'), // Mehrunes Dagon Custom Effect
EFI_STMA = MKTAG('S','T','M','A'), // Stunted Magicka
EFI_SUDG = MKTAG('S','U','D','G'), // Sun Damage
EFI_VAMP = MKTAG('V','A','M','P'), // Vampirism
EFI_DARK = MKTAG('D','A','R','K'), // Darkness
EFI_RSWD = MKTAG('R','S','W','D') // Resist Water Damage
};
// Based on http://www.uesp.net/wiki/Tes5Mod:Mod_File_Format#Groups
enum GroupType
{
Grp_RecordType = 0,
Grp_WorldChild = 1,
Grp_InteriorCell = 2,
Grp_InteriorSubCell = 3,
Grp_ExteriorCell = 4,
Grp_ExteriorSubCell = 5,
Grp_CellChild = 6,
Grp_TopicChild = 7,
Grp_CellPersistentChild = 8,
Grp_CellTemporaryChild = 9,
Grp_CellVisibleDistChild = 10
};
// Based on http://www.uesp.net/wiki/Tes5Mod:Mod_File_Format#Records
enum RecordFlag
{
Rec_ESM = 0x00000001, // (TES4 record only) Master (ESM) file.
Rec_Deleted = 0x00000020, // Deleted
Rec_Constant = 0x00000040, // Constant
Rec_HiddenLMap = 0x00000040, // (REFR) Hidden From Local Map (Needs Confirmation: Related to shields)
Rec_Localized = 0x00000080, // (TES4 record only) Is localized. This will make Skyrim load the
// .STRINGS, .DLSTRINGS, and .ILSTRINGS files associated with the mod.
// If this flag is not set, lstrings are treated as zstrings.
Rec_FireOff = 0x00000080, // (PHZD) Turn off fire
Rec_UpdateAnim = 0x00000100, // Must Update Anims
Rec_NoAccess = 0x00000100, // (REFR) Inaccessible
Rec_Hidden = 0x00000200, // (REFR) Hidden from local map
Rec_StartDead = 0x00000200, // (ACHR) Starts dead /(REFR) MotionBlurCastsShadows
Rec_Persistent = 0x00000400, // Quest item / Persistent reference
Rec_DispMenu = 0x00000400, // (LSCR) Displays in Main Menu
Rec_Disabled = 0x00000800, // Initially disabled
Rec_Ignored = 0x00001000, // Ignored
Rec_DistVis = 0x00008000, // Visible when distant
Rec_RandAnim = 0x00010000, // (ACTI) Random Animation Start
Rec_Danger = 0x00020000, // (ACTI) Dangerous / Off limits (Interior cell)
// Dangerous Can't be set withough Ignore Object Interaction
Rec_Compressed = 0x00040000, // Data is compressed
Rec_CanNotWait = 0x00080000, // Can't wait
Rec_IgnoreObj = 0x00100000, // (ACTI) Ignore Object Interaction
// Ignore Object Interaction Sets Dangerous Automatically
Rec_Marker = 0x00800000, // Is Marker
Rec_Obstacle = 0x02000000, // (ACTI) Obstacle / (REFR) No AI Acquire
Rec_NavMFilter = 0x04000000, // NavMesh Gen - Filter
Rec_NavMBBox = 0x08000000, // NavMesh Gen - Bounding Box
Rec_ExitToTalk = 0x10000000, // (FURN) Must Exit to Talk
Rec_Refected = 0x10000000, // (REFR) Reflected By Auto Water
Rec_ChildUse = 0x20000000, // (FURN/IDLM) Child Can Use
Rec_NoHavok = 0x20000000, // (REFR) Don't Havok Settle
Rec_NavMGround = 0x40000000, // NavMesh Gen - Ground
Rec_NoRespawn = 0x40000000, // (REFR) NoRespawn
Rec_MultiBound = 0x80000000 // (REFR) MultiBound
};
typedef std::uint32_t FormId;
#pragma pack(push, 1)
// NOTE: the label field of a group is not reliable (http://www.uesp.net/wiki/Tes4Mod:Mod_File_Format)
union GroupLabel
{
std::uint32_t value; // formId, blockNo or raw int representation of type
char recordType[4]; // record type in ascii
std::int16_t grid[2]; // grid y, x (note the reverse order)
};
union TypeId
{
std::uint32_t value;
char name[4]; // record type in ascii
};
struct GroupTypeHeader
{
std::uint32_t typeId;
std::uint32_t groupSize; // includes the 24 bytes (20 for TES4) of header (i.e. this struct)
GroupLabel label; // format based on type
std::int32_t type;
std::uint16_t stamp; // & 0xff for day, & 0xff00 for months since Dec 2002 (i.e. 1 = Jan 2003)
std::uint16_t unknown;
std::uint16_t version; // not in TES4
std::uint16_t unknown2; // not in TES4
};
struct RecordTypeHeader
{
std::uint32_t typeId;
std::uint32_t dataSize; // does *not* include 24 bytes (20 for TES4) of header
std::uint32_t flags;
FormId id;
std::uint32_t revision;
std::uint16_t version; // not in TES4
std::uint16_t unknown; // not in TES4
};
union RecordHeader
{
struct GroupTypeHeader group;
struct RecordTypeHeader record;
};
struct SubRecordHeader
{
std::uint32_t typeId;
std::uint16_t dataSize;
};
// Grid, CellGrid and Vertex are shared by NVMI(NAVI) and NVNM(NAVM)
struct Grid
{
std::int16_t x;
std::int16_t y;
};
union CellGrid
{
FormId cellId;
Grid grid;
};
struct Vector3
{
float x;
float y;
float z;
};
typedef Vector3 Vertex;
// REFR, ACHR, ACRE
struct Position
{
Vector3 pos;
Vector3 rot; // angles are in radian, rz applied first and rx applied last
};
// REFR, ACHR, ACRE
struct EnableParent
{
FormId parent;
std::uint32_t flags; //0x0001 = Set Enable State Opposite Parent, 0x0002 = Pop In
};
// LVLC, LVLI
struct LVLO
{
std::int16_t level;
std::uint16_t unknown; // sometimes missing
FormId item;
std::int16_t count;
std::uint16_t unknown2; // sometimes missing
};
struct InventoryItem // NPC_, CREA, CONT
{
FormId item;
std::uint32_t count;
};
struct AIData // NPC_, CREA
{
std::uint8_t aggression;
std::uint8_t confidence;
std::uint8_t energyLevel;
std::uint8_t responsibility;
std::uint32_t aiFlags;
std::uint8_t trainSkill;
std::uint8_t trainLevel;
std::uint16_t unknown;
};
struct AttributeValues
{
std::uint8_t strength;
std::uint8_t intelligence;
std::uint8_t willpower;
std::uint8_t agility;
std::uint8_t speed;
std::uint8_t endurance;
std::uint8_t personality;
std::uint8_t luck;
};
struct ActorBaseConfig
{
#if 0
enum ACBS_NPC
{
ACBS_Female = 0x000001,
ACBS_Essential = 0x000002,
ACBS_Respawn = 0x000008,
ACBS_Autocalcstats = 0x000010,
ACBS_PCLevelOffset = 0x000080,
ACBS_NoLowLevelProcessing = 0x000200,
ACBS_NoRumors = 0x002000,
ACBS_Summonable = 0x004000,
ACBS_NoPersuasion = 0x008000, // different meaning to crea
ACBS_CanCorpseCheck = 0x100000 // opposite of crea
};
enum ACBS_CREA
{
ACBS_Essential = 0x000002,
ACBS_WeapAndShield = 0x000004,
ACBS_Respawn = 0x000008,
ACBS_PCLevelOffset = 0x000080,
ACBS_NoLowLevelProcessing = 0x000200,
ACBS_NoHead = 0x008000, // different meaning to npc_
ACBS_NoRightArm = 0x010000,
ACBS_NoLeftArm = 0x020000,
ACBS_NoCombatWater = 0x040000,
ACBS_NoShadow = 0x080000,
ACBS_NoCorpseCheck = 0x100000 // opposite of npc_
};
#endif
std::uint32_t flags;
std::uint16_t baseSpell; // Base spell points
std::uint16_t fatigue; // Fatigue
std::uint16_t barterGold; // Barter gold
std::int16_t level; // Level/Offset level
std::uint16_t calcMin; // Calc Min
std::uint16_t calcMax; // Calc Max
};
struct ActorFaction
{
FormId faction;
std::int8_t rank;
std::uint8_t unknown1;
std::uint8_t unknown2;
std::uint8_t unknown3;
};
union EFI_Label
{
std::uint32_t value;
char effect[4];
};
struct ScriptEffect
{
FormId formId; // Script effect (Magic effect must be SEFF)
std::int32_t school; // Magic school. See Magic schools for more information.
EFI_Label visualEffect; // Visual effect name or 0x00000000 if None
std::uint8_t flags; // 0x01 = Hostile
std::uint8_t unknown1;
std::uint8_t unknown2;
std::uint8_t unknown3;
};
#pragma pack(pop)
// For pretty printing GroupHeader labels
std::string printLabel(const GroupLabel& label, const std::uint32_t type);
std::string printName(const std::uint32_t typeId);
void gridToString(std::int16_t x, std::int16_t y, std::string& str);
}
#endif // ESM4_COMMON_H

115
extern/esm4/cont.cpp vendored

@ -0,0 +1,115 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "cont.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Container::Container() : mFormId(0), mFlags(0), mBoundRadius(0.f), mDataFlags(0), mWeight(0.f),
mOpenSound(0), mCloseSound(0), mScript(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
}
ESM4::Container::~Container()
{
}
void ESM4::Container::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("CONT FULL data read error");
break;
}
case ESM4::SUB_DATA:
{
reader.get(mDataFlags);
reader.get(mWeight);
break;
}
case ESM4::SUB_CNTO:
{
static InventoryItem inv; // FIXME: use unique_ptr here?
reader.get(inv);
reader.adjustFormId(inv.item);
mInventory.push_back(inv);
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_SNAM: reader.getFormId(mOpenSound); break;
case ESM4::SUB_QNAM: reader.getFormId(mCloseSound); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_MODS: // TES5 only
case ESM4::SUB_VMAD: // TES5 only
case ESM4::SUB_OBND: // TES5 only
case ESM4::SUB_COCT: // TES5 only
case ESM4::SUB_COED: // TES5 only
case ESM4::SUB_DEST: // FONV
case ESM4::SUB_DSTD: // FONV
case ESM4::SUB_DSTF: // FONV
case ESM4::SUB_DMDL: // FONV
case ESM4::SUB_DMDT: // FONV
case ESM4::SUB_RNAM: // FONV
{
//std::cout << "CONT " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::CONT::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Container::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Container::blank()
//{
//}

@ -0,0 +1,68 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_CONT_H
#define ESM4_CONT_H
#include <vector>
#include "common.hpp" // InventoryItem
namespace ESM4
{
class Reader;
class Writer;
struct Container
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
float mBoundRadius;
unsigned char mDataFlags;
float mWeight;
FormId mOpenSound;
FormId mCloseSound;
FormId mScript; // TES4 only
std::vector<InventoryItem> mInventory;
Container();
virtual ~Container();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_CONT_H

223
extern/esm4/crea.cpp vendored

@ -0,0 +1,223 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "crea.hpp"
#include <cassert>
#include <stdexcept>
#include <string>
#include <sstream>
#include <iostream> // FIXME
#include "reader.hpp"
//#include "writer.hpp"
#ifdef NDEBUG // FIXME: debuggigng only
#undef NDEBUG
#endif
ESM4::Creature::Creature() : mFormId(0), mFlags(0), mDeathItem(0), mScript(0), mCombatStyle(0),
mSoundBase(0), mSound(0), mSoundChance(0), mBaseScale(0.f),
mTurningSpeed(0.f), mFootWeight(0.f), mBoundRadius(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mBloodSpray.clear();
mBloodDecal.clear();
mAIData.aggression = 0;
mAIData.confidence = 0;
mAIData.energyLevel = 0;
mAIData.responsibility = 0;
mAIData.aiFlags = 0;
mAIData.trainSkill = 0;
mAIData.trainLevel = 0;
std::memset(&mData, 0, sizeof(Data));
}
ESM4::Creature::~Creature()
{
}
void ESM4::Creature::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL: reader.getZString(mFullName); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_CNTO:
{
static InventoryItem inv; // FIXME: use unique_ptr here?
reader.get(inv);
reader.adjustFormId(inv.item);
mInventory.push_back(inv);
break;
}
case ESM4::SUB_SPLO:
{
FormId id;
reader.getFormId(id);
mSpell.push_back(id);
break;
}
case ESM4::SUB_PKID:
{
FormId id;
reader.getFormId(id);
mAIPackages.push_back(id);
break;
}
case ESM4::SUB_SNAM:
{
reader.get(mFaction);
reader.adjustFormId(mFaction.faction);
break;
}
case ESM4::SUB_INAM: reader.getFormId(mDeathItem); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_AIDT:
{
if (subHdr.dataSize == 20) // FO3
reader.skipSubRecordData();
else
reader.get(mAIData); // 12 bytes
break;
}
case ESM4::SUB_ACBS:
{
if (subHdr.dataSize == 24) // FO3
reader.skipSubRecordData();
else
reader.get(mBaseConfig);
break;
}
case ESM4::SUB_DATA:
{
if (subHdr.dataSize == 17) // FO3
reader.skipSubRecordData();
else
reader.get(mData);
break;
}
case ESM4::SUB_ZNAM: reader.getFormId(mCombatStyle); break;
case ESM4::SUB_CSCR: reader.getFormId(mSoundBase); break;
case ESM4::SUB_CSDI: reader.getFormId(mSound); break;
case ESM4::SUB_CSDC: reader.get(mSoundChance); break;
case ESM4::SUB_BNAM: reader.get(mBaseScale); break;
case ESM4::SUB_TNAM: reader.get(mTurningSpeed); break;
case ESM4::SUB_WNAM: reader.get(mFootWeight); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_NAM0: reader.getZString(mBloodSpray); break;
case ESM4::SUB_NAM1: reader.getZString(mBloodDecal); break;
case ESM4::SUB_NIFZ:
{
std::string str;
if (!reader.getZString(str))
throw std::runtime_error ("CREA NIFZ data read error");
std::stringstream ss(str);
std::string file;
while (std::getline(ss, file, '\0')) // split the strings
mNif.push_back(file);
break;
}
case ESM4::SUB_NIFT:
{
if (subHdr.dataSize != 4) // FIXME: FO3
{
reader.skipSubRecordData();
break;
}
assert(subHdr.dataSize == 4 && "CREA NIFT datasize error");
std::uint32_t nift;
reader.get(nift);
if (nift)
std::cout << "CREA NIFT " << mFormId << ", non-zero " << nift << std::endl;
break;
}
case ESM4::SUB_KFFZ:
{
std::string str;
if (!reader.getZString(str))
throw std::runtime_error ("CREA KFFZ data read error");
std::stringstream ss(str);
std::string file;
while (std::getline(ss, file, '\0')) // split the strings
mKf.push_back(file);
break;
}
case ESM4::SUB_MODT:
case ESM4::SUB_RNAM:
case ESM4::SUB_CSDT:
case ESM4::SUB_OBND: // FO3
case ESM4::SUB_EAMT: // FO3
case ESM4::SUB_VTCK: // FO3
case ESM4::SUB_TPLT: // FO3
case ESM4::SUB_PNAM: // FO3
case ESM4::SUB_NAM4: // FO3
case ESM4::SUB_NAM5: // FO3
case ESM4::SUB_CNAM: // FO3
case ESM4::SUB_LNAM: // FO3
case ESM4::SUB_EITM: // FO3
case ESM4::SUB_DEST: // FO3
case ESM4::SUB_DSTD: // FO3
case ESM4::SUB_DSTF: // FO3
case ESM4::SUB_DMDL: // FO3
case ESM4::SUB_DMDT: // FO3
case ESM4::SUB_COED: // FO3
{
//std::cout << "CREA " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::CREA::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Creature::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Creature::blank()
//{
//}

@ -0,0 +1,98 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_CREA_H
#define ESM4_CREA_H
#include <vector>
#include "common.hpp"
namespace ESM4
{
class Reader;
class Writer;
struct Creature
{
#pragma pack(push, 1)
struct Data
{
std::uint8_t unknown;
std::uint8_t combat;
std::uint8_t magic;
std::uint8_t stealth;
std::uint16_t soul;
std::uint16_t health;
std::uint16_t unknown2;
std::uint16_t damage;
AttributeValues attribs;
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
FormId mDeathItem;
std::vector<FormId> mSpell;
FormId mScript;
AIData mAIData;
std::vector<FormId> mAIPackages;
ActorBaseConfig mBaseConfig;
ActorFaction mFaction;
Data mData;
FormId mCombatStyle;
FormId mSoundBase;
FormId mSound;
std::uint8_t mSoundChance;
float mBaseScale;
float mTurningSpeed;
float mFootWeight;
std::string mBloodSpray;
std::string mBloodDecal;
float mBoundRadius;
std::vector<std::string> mNif; // NIF filenames, get directory from mModel
std::vector<std::string> mKf;
std::vector<InventoryItem> mInventory;
Creature();
virtual ~Creature();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_CREA_H

101
extern/esm4/door.cpp vendored

@ -0,0 +1,101 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "door.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Door::Door() : mFormId(0), mFlags(0), mDoorFlags(0), mBoundRadius(0.f), mScript(0),
mOpenSound(0), mCloseSound(0), mLoopSound(0), mRandomTeleport(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
}
ESM4::Door::~Door()
{
}
void ESM4::Door::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("DOOR FULL data read error");
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_SNAM: reader.getFormId(mOpenSound); break;
case ESM4::SUB_ANAM: reader.getFormId(mCloseSound); break;
case ESM4::SUB_BNAM: reader.getFormId(mLoopSound); break;
case ESM4::SUB_FNAM: reader.get(mDoorFlags); break;
case ESM4::SUB_TNAM: reader.getFormId(mRandomTeleport); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_MODS:
case ESM4::SUB_OBND:
case ESM4::SUB_VMAD:
case ESM4::SUB_DEST: // FO3
case ESM4::SUB_DSTD: // FO3
case ESM4::SUB_DSTF: // FO3
case ESM4::SUB_DMDL: // FO3
case ESM4::SUB_DMDT: // FO3
{
//std::cout << "DOOR " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::DOOR::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Door::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Door::blank()
//{
//}

@ -0,0 +1,75 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_DOOR_H
#define ESM4_DOOR_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Door
{
enum Flags
{
Flag_OblivionGate = 0x01,
Flag_AutomaticDoor = 0x02,
Flag_Hidden = 0x04,
Flag_MinimalUse = 0x08
};
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
float mBoundRadius;
std::uint8_t mDoorFlags;
FormId mScript;
FormId mOpenSound;
FormId mCloseSound;
FormId mLoopSound;
FormId mRandomTeleport;
Door();
virtual ~Door();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_DOOR_H

@ -0,0 +1,82 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "eyes.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Eyes::Eyes() : mFormId(0), mFlags(0)
{
mEditorId.clear();
mFullName.clear();
mIcon.clear();
mData.flags = 0;
}
ESM4::Eyes::~Eyes()
{
}
void ESM4::Eyes::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("EYES FULL data read error");
break;
}
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_DATA: reader.get(mData); break;
default:
throw std::runtime_error("ESM4::EYES::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Eyes::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Eyes::blank()
//{
//}

@ -0,0 +1,67 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_EYES_H
#define ESM4_EYES_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Eyes
{
#pragma pack(push, 1)
struct Data
{
std::uint8_t flags; // 0x01 = playable?
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mIcon; // inventory
Data mData;
Eyes();
virtual ~Eyes();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_EYES_H

@ -0,0 +1,97 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "flor.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Flora::Flora() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0), mIngredient(0),
mSound(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
}
ESM4::Flora::~Flora()
{
}
void ESM4::Flora::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("FLOR FULL data read error");
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_PFIG: reader.getFormId(mIngredient); break;
case ESM4::SUB_PFPC: reader.get(mPercentHarvest); break;
case ESM4::SUB_SNAM: reader.getFormId(mSound); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_MODS:
case ESM4::SUB_FNAM:
case ESM4::SUB_OBND:
case ESM4::SUB_PNAM:
case ESM4::SUB_RNAM:
case ESM4::SUB_VMAD:
{
//std::cout << "FLOR " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::FLOR::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Flora::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Flora::blank()
//{
//}

@ -0,0 +1,77 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_FLOR_H
#define ESM4_FLOR_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Flora
{
#pragma pack(push, 1)
struct Production
{
std::uint8_t spring;
std::uint8_t summer;
std::uint8_t autumn;
std::uint8_t winter;
Production::Production() : spring(0), summer(0), autumn(0), winter(0) {}
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
float mBoundRadius;
FormId mScript;
FormId mIngredient;
FormId mSound;
Production mPercentHarvest;
Flora();
virtual ~Flora();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_FLOR_H

@ -0,0 +1,76 @@
/*
Copyright (C) 2016 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
*/
#include "formid.hpp"
#include <sstream>
#include <algorithm>
#include <stdexcept>
#include <cstdlib> // strtol
#include <libs/platform/strings.h>
namespace ESM4
{
void formIdToString(FormId formId, std::string& str)
{
char buf[8+1];
int res = snprintf(buf, 8+1, "%08X", formId);
if (res > 0 && res < 8+1)
str.assign(buf);
else
throw std::runtime_error("Possible buffer overflow while converting formId");
}
std::string formIdToString(FormId formId)
{
std::string str;
formIdToString(formId, str);
return str;
}
bool isFormId(const std::string& str, FormId *id)
{
if (str.size() != 8)
return false;
char *tmp;
errno = 0;
unsigned long val = strtol(str.c_str(), &tmp, 16);
if (tmp == str.c_str() || *tmp != '\0' || ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
return false;
if (id != nullptr)
*id = static_cast<FormId>(val);
return true;
}
FormId stringToFormId(const std::string& str)
{
if (str.size() != 8)
throw std::out_of_range("StringToFormId: incorrect string size");
return static_cast<FormId>(std::stoul(str, nullptr, 16));
}
}

@ -0,0 +1,42 @@
/*
Copyright (C) 2016 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
*/
#ifndef ESM4_FORMID_H
#define ESM4_FORMID_H
#include <cstdint>
#include <string>
namespace ESM4
{
typedef std::uint32_t FormId;
void formIdToString(FormId formId, std::string& str);
std::string formIdToString(FormId formId);
bool isFormId(const std::string& str, FormId *id = nullptr);
FormId stringToFormId(const std::string& str);
}
#endif // ESM4_FORMID_H

106
extern/esm4/furn.cpp vendored

@ -0,0 +1,106 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "furn.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Furniture::Furniture() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0),
mActiveMarkerFlags(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
}
ESM4::Furniture::~Furniture()
{
}
void ESM4::Furniture::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("FURN FULL data read error");
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_MNAM: reader.get(mActiveMarkerFlags); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_DEST:
case ESM4::SUB_DSTD:
case ESM4::SUB_DSTF:
case ESM4::SUB_ENAM:
case ESM4::SUB_FNAM:
case ESM4::SUB_FNMK:
case ESM4::SUB_FNPR:
case ESM4::SUB_KNAM:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_MODS:
case ESM4::SUB_NAM0:
case ESM4::SUB_OBND:
case ESM4::SUB_PNAM:
case ESM4::SUB_VMAD:
case ESM4::SUB_WBDT:
case ESM4::SUB_XMRK:
{
//std::cout << "FURN " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::FURN::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Furniture::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Furniture::blank()
//{
//}

@ -0,0 +1,63 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_FURN_H
#define ESM4_FURN_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Furniture
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
float mBoundRadius;
FormId mScript;
std::uint32_t mActiveMarkerFlags;
Furniture();
virtual ~Furniture();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_FURN_H

@ -0,0 +1,78 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "gras.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Grass::Grass() : mFormId(0), mFlags(0), mBoundRadius(0.f)
{
mEditorId.clear();
mModel.clear();
}
ESM4::Grass::~Grass()
{
}
void ESM4::Grass::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_DATA: reader.get(mData); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_OBND:
{
//std::cout << "GRAS " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::GRAS::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Grass::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Grass::blank()
//{
//}

@ -0,0 +1,97 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_GRAS_H
#define ESM4_GRAS_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Grass
{
#pragma pack(push, 1)
// unused fields are probably packing
struct Data
{
std::uint8_t density;
std::uint8_t minSlope;
std::uint8_t maxSlope;
std::uint8_t unused;
std::uint16_t distanceFromWater;
std::uint16_t unused2;
/*
1 Above - At Least
2 Above - At Most
3 Below - At Least
4 Below - At Most
5 Either - At Least
6 Either - At Most
7 Either - At Most Above
8 Either - At Most Below
*/
std::uint32_t waterDistApplication;
float positionRange;
float heightRange;
float colorRange;
float wavePeriod;
/*
0x01 Vertex Lighting
0x02 Uniform Scaling
0x04 Fit to Slope
*/
std::uint8_t flags;
std::uint8_t unused3;
std::uint16_t unused4;
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mModel;
float mBoundRadius;
Data mData;
Grass();
virtual ~Grass();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_GRAS_H

157
extern/esm4/grup.hpp vendored

@ -0,0 +1,157 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_GRUP_H
#define ESM4_GRUP_H
#include <vector>
#include "common.hpp" // GroupLabel
namespace ESM4
{
// http://www.uesp.net/wiki/Tes4Mod:Mod_File_Format#Hierarchical_Top_Groups
//
// Type | Info |
// ------+--------------------------------------+-------------------
// 2 | Interior Cell Block |
// 3 | Interior Cell Sub-Block |
// R | CELL |
// 6 | Cell Childen |
// 8 | Persistent children |
// R | REFR, ACHR, ACRE |
// 10 | Visible distant children |
// R | REFR, ACHR, ACRE |
// 9 | Temp Children |
// R | PGRD |
// R | REFR, ACHR, ACRE |
// | |
// 0 | Top (Type) |
// R | WRLD |
// 1 | World Children |
// R | ROAD |
// R | CELL |
// 6 | Cell Childen |
// 8 | Persistent children |
// R | REFR, ACHR, ACRE |
// 10 | Visible distant children |
// R | REFR, ACHR, ACRE |
// 9 | Temp Children |
// R | PGRD |
// R | REFR, ACHR, ACRE |
// 4 | Exterior World Block |
// 5 | Exterior World Sub-block |
// R | CELL |
// 6 | Cell Childen |
// 8 | Persistent children |
// R | REFR, ACHR, ACRE |
// 10 | Visible distant children |
// R | REFR, ACHR, ACRE |
// 9 | Temp Children |
// R | LAND |
// R | PGRD |
// R | REFR, ACHR, ACRE |
//
struct WorldGroup
{
FormId mWorld; // WRLD record for this group
// occurs only after World Child (type 1)
// since GRUP label may not be reliable, need to keep the formid of the current WRLD in
// the reader's context
FormId mRoad;
std::vector<FormId> mCells; // FIXME should this be CellGroup* instead?
WorldGroup() : mWorld(0), mRoad(0) {}
};
// http://www.uesp.net/wiki/Tes4Mod:Mod_File_Format/CELL
//
// The block and subblock groups for an interior cell are determined by the last two decimal
// digits of the lower 3 bytes of the cell form ID (the modindex is not included in the
// calculation). For example, for form ID 0x000CF2=3314, the block is 4 and the subblock is 1.
//
// The block and subblock groups for an exterior cell are determined by the X-Y coordinates of
// the cell. Each block contains 16 subblocks (4x4) and each subblock contains 64 cells (8x8).
// So each block contains 1024 cells (32x32).
//
// NOTE: There may be many CELL records in one subblock
struct CellGroup
{
FormId mCell; // CELL record for this cell group
int mCellModIndex; // from which file to get the CELL record (e.g. may have been updated)
// For retrieving parent group size (for lazy loading or skipping) and sub-block number / grid
// NOTE: There can be more than one file that adds/modifies records to this cell group
//
// Use Case 1: To quickly get only the visble when distant records:
//
// - Find the FormId of the CELL (maybe WRLD/X/Y grid lookup or from XTEL of a REFR)
// - search a map of CELL FormId to CellGroup
// - load CELL and its child groups (or load the visible distant only, or whatever)
//
// Use Case 2: Scan the files but don't load CELL or cell group
//
// - Load referenceables and other records up front, updating them as required
// - Don't load CELL, LAND, PGRD or ROAD (keep FormId's and file index, and file
// context then skip the rest of the group)
//
std::vector<GroupTypeHeader> mHeaders; // FIXME: is this needed?
// FIXME: should these be pairs? i.e. <FormId, modindex> so that we know from which file
// the formid came (it may have been updated by a mod)
// but does it matter? the record itself keeps track of whether it is base,
// added or modified anyway
// FIXME: should these be maps? e.g. std::map<FormId, std::uint8_t>
// or vector for storage with a corresponding map of index?
// cache (modindex adjusted) formId's of children
// FIXME: also need file index + file context of all those that has type 8 GRUP
GroupTypeHeader mHdrPersist;
std::vector<FormId> mPersistent; // REFR, ACHR, ACRE
std::vector<FormId> mdelPersistent;
// FIXME: also need file index + file context of all those that has type 10 GRUP
GroupTypeHeader mHdrVisDist;
std::vector<FormId> mVisibleDist; // REFR, ACHR, ACRE
std::vector<FormId> mdelVisibleDist;
// FIXME: also need file index + file context of all those that has type 9 GRUP
GroupTypeHeader mHdrTemp;
FormId mLand; // if present, assume only one LAND per exterior CELL
FormId mPgrd; // if present, seems to be the first record after LAND in Temp Cell Child GRUP
std::vector<FormId> mTemporary; // REFR, ACHR, ACRE
std::vector<FormId> mdelTemporary;
// need to keep modindex and context for lazy loading (of all the files that contribute
// to this group)
CellGroup() : mCell(0), mLand(0), mPgrd(0) {}
};
}
#endif // ESM4_GRUP_H

@ -0,0 +1,83 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "hair.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Hair::Hair() : mFormId(0), mFlags(0), mBoundRadius(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
mData.flags = 0;
}
ESM4::Hair::~Hair()
{
}
void ESM4::Hair::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL: reader.getZString(mFullName); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_DATA: reader.get(mData); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
{
//std::cout << "HAIR " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::HAIR::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Hair::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Hair::blank()
//{
//}

@ -0,0 +1,70 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_HAIR
#define ESM4_HAIR
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Hair
{
#pragma pack(push, 1)
struct Data
{
std::uint8_t flags; // 0x01 = not playable, 0x02 = not male, 0x04 = not female, ?? = fixed
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon; // inventory
float mBoundRadius;
Data mData;
Hair();
virtual ~Hair();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_HAIR

@ -0,0 +1,84 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "idle.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::IdleAnimation::IdleAnimation() : mFormId(0), mFlags(0), mParent(0), mPrevious(0)
{
mEditorId.clear();
mCollision.clear();
mEvent.clear();
}
ESM4::IdleAnimation::~IdleAnimation()
{
}
void ESM4::IdleAnimation::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_DNAM: reader.getZString(mCollision); break;
case ESM4::SUB_ENAM: reader.getZString(mEvent); break;
case ESM4::SUB_ANAM:
{
reader.get(mParent);
reader.get(mPrevious);
break;
}
case ESM4::SUB_CTDA: // formId
case ESM4::SUB_DATA: // formId
{
//std::cout << "IDLE " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::IDLE::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::IdleAnimation::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::IdleAnimation::blank()
//{
//}

@ -0,0 +1,61 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_IDLE_H
#define ESM4_IDLE_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct IdleAnimation
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mCollision;
std::string mEvent;
FormId mParent; // IDLE or AACT
FormId mPrevious;
IdleAnimation();
virtual ~IdleAnimation();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_IDLE_H

137
extern/esm4/ingr.cpp vendored

@ -0,0 +1,137 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "ingr.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Ingredient::Ingredient() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
mData.value = 0;
mData.weight = 0.f;
mEnchantment.value = 0;
mEnchantment.flags = 0;
std::memset(&mEffect, 0, sizeof(ScriptEffect));
}
ESM4::Ingredient::~Ingredient()
{
}
void ESM4::Ingredient::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (mFullName.empty())
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("INGR FULL data read error");
break;
}
else // in TES4 subsequent FULL records are script effect names
{
// FIXME: should be part of a struct?
std::string scriptEffectName;
if (!reader.getZString(scriptEffectName))
throw std::runtime_error ("INGR FULL data read error");
mScriptEffect.push_back(scriptEffectName);
break;
}
}
case ESM4::SUB_DATA:
{
//if (reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170)
if (subHdr.dataSize == 8) // FO3 is size 4 even though VER_094
reader.get(mData);
else
reader.get(mData.weight);
break;
}
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_ENIT: reader.get(mEnchantment); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_SCIT:
{
reader.get(mEffect);
reader.adjustFormId(mEffect.formId);
break;
}
case ESM4::SUB_MODT:
case ESM4::SUB_MODS: // Dragonborn only?
case ESM4::SUB_EFID:
case ESM4::SUB_EFIT:
case ESM4::SUB_OBND:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_VMAD:
case ESM4::SUB_YNAM:
case ESM4::SUB_ZNAM:
case ESM4::SUB_ETYP: // FO3
{
//std::cout << "INGR " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::INGR::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Ingredient::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Ingredient::blank()
//{
//}

@ -0,0 +1,82 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_INGR_H
#define ESM4_INGR_H
#include <vector>
#include "common.hpp"
namespace ESM4
{
class Reader;
class Writer;
struct Ingredient
{
#pragma pack(push, 1)
struct Data
{
std::uint32_t value;
float weight;
};
struct ENIT
{
std::uint32_t value;
std::uint32_t flags;
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon; // inventory
float mBoundRadius;
std::vector<std::string> mScriptEffect; // FIXME: prob. should be in a struct
FormId mScript;
ScriptEffect mEffect;
ENIT mEnchantment;
Data mData;
Ingredient();
virtual ~Ingredient();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_INGR_H

100
extern/esm4/keym.cpp vendored

@ -0,0 +1,100 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "keym.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Key::Key() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
mData.value = 0;
mData.weight = 0.f;
}
ESM4::Key::~Key()
{
}
void ESM4::Key::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("KEYM FULL data read error");
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_DATA: reader.get(mData); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_OBND:
case ESM4::SUB_VMAD:
case ESM4::SUB_YNAM:
case ESM4::SUB_ZNAM:
case ESM4::SUB_MICO: // FO3
{
//std::cout << "KEYM " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::KEYM::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Key::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Key::blank()
//{
//}

@ -0,0 +1,73 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_KEYM_H
#define ESM4_KEYM_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Key
{
#pragma pack(push, 1)
struct Data
{
std::uint32_t value; // gold
float weight;
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon; // inventory
float mBoundRadius;
FormId mScript;
Data mData;
Key();
virtual ~Key();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_KEYM_H

222
extern/esm4/land.cpp vendored

@ -0,0 +1,222 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "land.hpp"
#include <cassert>
#include <stdexcept>
#include <iostream> // FIXME: debug only
#include "reader.hpp"
//#include "writer.hpp"
#ifdef NDEBUG // FIXME: debuggigng only
#undef NDEBUG
#endif
ESM4::Land::Land() : mFormId(0), mFlags(0), mLandFlags(0), mDataTypes(0)
{
for (int i = 0; i < 4; ++i)
mTextures[i].base.formId = 0;
}
ESM4::Land::~Land()
{
}
// overlap north
//
// 32
// 31
// 30
// overlap .
// west .
// .
// 2
// 1
// 0
// 0 1 2 ... 30 31 32
//
// overlap south
//
void ESM4::Land::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
TxtLayer layer;
std::int8_t currentAddQuad = -1; // for VTXT following ATXT
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_DATA:
{
reader.get(mLandFlags);
break;
}
case ESM4::SUB_VNML: // vertex normals, 33x33x(1+1+1) = 3267
{
reader.get(mVertNorm);
mDataTypes |= LAND_VNML;
break;
}
case ESM4::SUB_VHGT: // vertex height gradient, 4+33x33+3 = 4+1089+3 = 1096
{
#if 0
reader.get(mHeightMap.heightOffset);
reader.get(mHeightMap.gradientData);
reader.get(mHeightMap.unknown1);
reader.get(mHeightMap.unknown2);
#endif
reader.get(mHeightMap);
mDataTypes |= LAND_VHGT;
break;
}
case ESM4::SUB_VCLR: // vertex colours, 24bit RGB, 33x33x(1+1+1) = 3267
{
reader.get(mVertColr);
mDataTypes |= LAND_VCLR;
break;
}
case ESM4::SUA_BTXT:
{
BTXT base;
if (reader.get(base))
{
assert(base.quadrant < 4 && base.quadrant >= 0 && "base texture quadrant index error");
reader.adjustFormId(base.formId);
mTextures[base.quadrant].base = base; // FIXME: any way to avoid double-copying?
#if 0
std::cout << "Base Texture formid: 0x"
<< std::hex << mTextures[base.quadrant].base.formId
<< ", quad " << std::dec << (int)base.quadrant << std::endl;
#endif
}
break;
}
case ESM4::SUB_ATXT:
{
if (currentAddQuad != -1)
{
// FIXME: sometimes there are no VTXT following an ATXT? Just add a dummy one for now
//std::cout << "ESM4::Land VTXT empty layer " << (int)layer.additional.layer << std::endl;
mTextures[currentAddQuad].layers.push_back(layer);
}
reader.get(layer.additional);
reader.adjustFormId(layer.additional.formId);
assert(layer.additional.quadrant < 4 && layer.additional.quadrant >= 0
&& "additional texture quadrant index error");
#if 0
std::cout << "Additional Texture formId: 0x"
<< std::hex << layer.additional.formId
<< ", quad " << std::dec << (int)layer.additional.quadrant << std::endl;
std::cout << "Additional Texture layer: "
<< std::dec << (int)layer.additional.layer << std::endl;
#endif
currentAddQuad = layer.additional.quadrant;
break;
}
case ESM4::SUB_VTXT:
{
assert(currentAddQuad != -1 && "VTXT without ATXT found");
int count = (int)reader.subRecordHeader().dataSize / sizeof(ESM4::Land::VTXT);
int remainder = reader.subRecordHeader().dataSize % sizeof(ESM4::Land::VTXT);
assert(remainder == 0 && "ESM4::LAND VTXT data size error");
if (count)
{
layer.data.resize(count);
std::vector<ESM4::Land::VTXT>::iterator it = layer.data.begin();
for (;it != layer.data.end(); ++it)
{
reader.get(*it);
// FIXME: debug only
//std::cout << "pos: " << std::dec << (int)(*it).position << std::endl;
}
}
mTextures[currentAddQuad].layers.push_back(layer);
// Assumed that the layers are added in the correct sequence
// FIXME: Knights.esp doesn't seem to observe this - investigate more
//assert(layer.additional.layer == mTextures[currentAddQuad].layers.size()-1
//&& "additional texture layer index error");
currentAddQuad = -1;
// FIXME: debug only
//std::cout << "VTXT: count " << std::dec << count << std::endl;
break;
}
case ESM4::SUB_VTEX: // only in Oblivion?
{
int count = (int)reader.subRecordHeader().dataSize / sizeof(FormId);
int remainder = reader.subRecordHeader().dataSize % sizeof(FormId);
assert(remainder == 0 && "ESM4::LAND VTEX data size error");
if (count)
{
mIds.resize(count);
for (std::vector<FormId>::iterator it = mIds.begin(); it != mIds.end(); ++it)
{
reader.getFormId(*it);
// FIXME: debug only
//std::cout << "VTEX: " << std::hex << *it << std::endl;
}
}
break;
}
default:
throw std::runtime_error("ESM4::LAND::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
bool missing = false;
for (int i = 0; i < 4; ++i)
{
if (mTextures[i].base.formId == 0)
{
//std::cout << "ESM::LAND " << ESM4::formIdToString(mFormId) << " missing base, quad " << i << std::endl;
missing = true;
}
}
// at least one of the quadrants do not have a base texture, return without setting the flag
if (!missing)
mDataTypes |= LAND_VTEX;
}
//void ESM4::Land::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Land::blank()
//{
//}

134
extern/esm4/land.hpp vendored

@ -0,0 +1,134 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_LAND_H
#define ESM4_LAND_H
#include <vector>
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
typedef std::uint32_t FormId;
struct Land
{
enum
{
LAND_VNML = 1,
LAND_VHGT = 2,
LAND_WNAM = 4, // only in TES3?
LAND_VCLR = 8,
LAND_VTEX = 16
};
// number of vertices per side
static const int VERTS_PER_SIDE = 33;
// cell terrain size in world coords
static const int REAL_SIZE = 4096;
// total number of vertices
static const int LAND_NUM_VERTS = VERTS_PER_SIDE * VERTS_PER_SIDE;
static const int HEIGHT_SCALE = 8;
//number of textures per side of a land quadrant
static const int QUAD_TEXTURE_PER_SIDE = 16;
#pragma pack(push,1)
struct VHGT
{
float heightOffset;
std::int8_t gradientData[VERTS_PER_SIDE * VERTS_PER_SIDE];
std::uint16_t unknown1;
unsigned char unknown2;
};
struct BTXT
{
FormId formId;
std::uint8_t quadrant; // 0 = bottom left. 1 = bottom right. 2 = upper-left. 3 = upper-right
std::uint8_t unknown1;
std::uint16_t unknown2;
};
struct ATXT
{
FormId formId;
std::uint8_t quadrant; // 0 = bottom left. 1 = bottom right. 2 = upper-left. 3 = upper-right
std::uint8_t unknown;
std::uint16_t layer; // texture layer, 0..7
};
struct VTXT
{
std::uint16_t position; // 0..288 (17x17 grid)
std::uint8_t unknown1;
std::uint8_t unknown2;
float opacity;
};
#pragma pack(pop)
struct TxtLayer
{
ATXT additional;
std::vector<VTXT> data; // FIXME: is this UV map?
};
struct Texture
{
BTXT base;
std::vector<TxtLayer> layers;
};
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::uint32_t mLandFlags; // from DATA subrecord
// FIXME: lazy loading not yet implemented
int mDataTypes; // which data types are loaded
signed char mVertNorm[VERTS_PER_SIDE * VERTS_PER_SIDE * 3]; // from VNML subrecord
signed char mVertColr[VERTS_PER_SIDE * VERTS_PER_SIDE * 3]; // from VCLR subrecord
VHGT mHeightMap;
Texture mTextures[4]; // 0 = bottom left. 1 = bottom right. 2 = upper-left. 3 = upper-right
std::vector<FormId> mIds; // land texture (LTEX) formids
Land();
virtual ~Land();
virtual void load(Reader& reader);
//virtual void save(Writer& writer) const;
//void blank();
};
}
#endif // ESM4_LAND_H

127
extern/esm4/ligh.cpp vendored

@ -0,0 +1,127 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "ligh.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Light::Light() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0), mSound(0),
mFade(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
}
ESM4::Light::~Light()
{
}
void ESM4::Light::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
std::uint32_t esmVer = reader.esmVersion();
bool isFONV = esmVer == ESM4::VER_132 || esmVer == ESM4::VER_133 || esmVer == ESM4::VER_134;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("LIGH FULL data read error");
break;
}
case ESM4::SUB_DATA:
{
if (isFONV || (esmVer == ESM4::VER_094 && subHdr.dataSize == 32)/*FO3*/)
{
reader.get(mData.time); // uint32
}
else
reader.get(mData.duration); // float
reader.get(mData.radius);
reader.get(mData.colour);
reader.get(mData.flags);
//if (reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170)
if (subHdr.dataSize == 48)
{
reader.get(mData.falloff);
reader.get(mData.FOV);
reader.get(mData.nearClip);
reader.get(mData.frequency);
reader.get(mData.intensityAmplitude);
reader.get(mData.movementAmplitude);
}
else if (subHdr.dataSize == 32)
{
reader.get(mData.falloff);
reader.get(mData.FOV);
}
reader.get(mData.value);
reader.get(mData.weight);
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_SNAM: reader.getFormId(mSound); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_FNAM: reader.get(mFade); break;
case ESM4::SUB_MODT:
case ESM4::SUB_OBND:
case ESM4::SUB_VMAD: // Dragonborn only?
{
//std::cout << "LIGH " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::LIGH::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Light::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Light::blank()
//{
//}

@ -0,0 +1,89 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_LIGH_H
#define ESM4_LIGH_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Light
{
struct Data
{
std::uint32_t time; // FO/FONV only
float duration;
std::uint32_t radius;
std::uint32_t colour; // RGBA
std::int32_t flags;
float falloff;
float FOV;
float nearClip; // TES5 only
float frequency; // TES5 only
float intensityAmplitude; // TES5 only
float movementAmplitude; // TES5 only
std::uint32_t value; // gold
float weight;
Data() : duration(-1), radius(0), flags(0), colour(0), falloff(1.f), FOV(90),
nearClip(0.f), frequency(0.f), intensityAmplitude(0.f), movementAmplitude(0.f),
value(0), weight(0.f) // FIXME: FOV in degrees or radians?
{}
};
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon;
float mBoundRadius;
FormId mScript;
FormId mSound;
float mFade;
Data mData;
Light();
virtual ~Light();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_LIGH_H

106
extern/esm4/ltex.cpp vendored

@ -0,0 +1,106 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "ltex.hpp"
#include <cassert>
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
#ifdef NDEBUG // FIXME: debuggigng only
#undef NDEBUG
#endif
ESM4::LandTexture::LandTexture() : mFormId(0), mFlags(0), mHavokFriction(0), mHavokRestitution(0),
mTextureSpecular(0), mGrass(0), mHavokMaterial(0), mTexture(0),
mMaterial(0)
{
mEditorId.clear();
mTextureFile.clear();
}
ESM4::LandTexture::~LandTexture()
{
}
void ESM4::LandTexture::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
std::uint32_t esmVer = reader.esmVersion();
bool isFONV = esmVer == ESM4::VER_132 || esmVer == ESM4::VER_133 || esmVer == ESM4::VER_134;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_HNAM:
{
if (isFONV)
{
reader.skipSubRecordData(); // FIXME: skip FONV for now
break;
}
if ((reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170)
&& subHdr.dataSize == 2) // FO3 is VER_094 but dataSize 3
{
//assert(subHdr.dataSize == 2 && "LTEX unexpected HNAM size");
reader.get(mHavokFriction);
reader.get(mHavokRestitution);
}
else
{
assert(subHdr.dataSize == 3 && "LTEX unexpected HNAM size");
reader.get(mHavokMaterial);
reader.get(mHavokFriction);
reader.get(mHavokRestitution);
}
break;
}
case ESM4::SUB_ICON: reader.getZString(mTextureFile); break; // Oblivion only?
case ESM4::SUB_SNAM: reader.get(mTextureSpecular); break;
case ESM4::SUB_GNAM: reader.getFormId(mGrass); break;
case ESM4::SUB_TNAM: reader.getFormId(mTexture); break; // TES5 only
case ESM4::SUB_MNAM: reader.getFormId(mMaterial); break; // TES5 only
default:
throw std::runtime_error("ESM4::LTEX::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::LandTexture::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::LandTexture::blank()
//{
//}

@ -0,0 +1,74 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_LTEX_H
#define ESM4_LTEX_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct LandTexture
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::uint8_t mHavokFriction;
std::uint8_t mHavokRestitution;
std::uint8_t mTextureSpecular; // default 30
FormId mGrass;
// ------ TES4 only -----
std::string mTextureFile;
std::uint8_t mHavokMaterial;
// ------ TES5 only -----
FormId mTexture;
FormId mMaterial;
// ----------------------
LandTexture();
virtual ~LandTexture();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_LTEX_H

105
extern/esm4/lvlc.cpp vendored

@ -0,0 +1,105 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "lvlc.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::LeveledCreature::LeveledCreature() : mFormId(0), mFlags(0), mScript(0), mTemplate(0),
mChanceNone(0), mLvlCreaFlags(0)
{
mEditorId.clear();
}
ESM4::LeveledCreature::~LeveledCreature()
{
}
void ESM4::LeveledCreature::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_TNAM: reader.getFormId(mTemplate); break;
case ESM4::SUB_LVLD: reader.get(mChanceNone); break;
case ESM4::SUB_LVLF: reader.get(mLvlCreaFlags); break;
case ESM4::SUB_LVLO:
{
static LVLO lvlo;
if (subHdr.dataSize != 12)
{
if (subHdr.dataSize == 8)
{
reader.get(lvlo.level);
reader.get(lvlo.item);
reader.get(lvlo.count);
//std::cout << "LVLC " << mEditorId << " LVLO lev " << lvlo.level << ", item " << lvlo.item
//<< ", count " << lvlo.count << std::endl;
// FIXME: seems to happen only once, don't add to mLvlObject
// LVLC TesKvatchCreature LVLO lev 1, item 1393819648, count 2
// 0x0001, 0x5314 0000, 0x0002
break;
}
else
throw std::runtime_error("ESM4::LVLC::load - " + mEditorId + " LVLO size error");
}
else
reader.get(lvlo);
reader.adjustFormId(lvlo.item);
mLvlObject.push_back(lvlo);
break;
}
case ESM4::SUB_OBND: // FO3
{
//std::cout << "LVLC " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::LVLC::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::LeveledCreature::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::LeveledCreature::blank()
//{
//}

@ -0,0 +1,62 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_LVLC_H
#define ESM4_LVLC_H
#include <vector>
#include "common.hpp"
namespace ESM4
{
class Reader;
class Writer;
struct LeveledCreature
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
FormId mScript;
FormId mTemplate;
std::int8_t mChanceNone;
std::uint8_t mLvlCreaFlags;
std::vector<LVLO> mLvlObject;
LeveledCreature();
virtual ~LeveledCreature();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_LVLC_H

103
extern/esm4/lvli.cpp vendored

@ -0,0 +1,103 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "lvli.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::LeveledItem::LeveledItem() : mFormId(0), mFlags(0), mChanceNone(0), mLvlItemFlags(0), mData(0)
{
mEditorId.clear();
}
ESM4::LeveledItem::~LeveledItem()
{
}
void ESM4::LeveledItem::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_LVLD: reader.get(mChanceNone); break;
case ESM4::SUB_LVLF: reader.get(mLvlItemFlags); break;
case ESM4::SUB_DATA: reader.get(mData); break;
case ESM4::SUB_LVLO:
{
static LVLO lvlo;
if (subHdr.dataSize != 12)
{
if (subHdr.dataSize == 8)
{
reader.get(lvlo.level);
reader.get(lvlo.item);
reader.get(lvlo.count);
std::cout << "LVLI " << mEditorId << " LVLO lev " << lvlo.level << ", item " << lvlo.item
<< ", count " << lvlo.count << std::endl;
break;
}
else
throw std::runtime_error("ESM4::LVLI::load - " + mEditorId + " LVLO size error");
}
else
reader.get(lvlo);
reader.adjustFormId(lvlo.item);
mLvlObject.push_back(lvlo);
break;
}
case ESM4::SUB_OBND: // FO3
case ESM4::SUB_COED: // FO3
case ESM4::SUB_LVLG: // FO3
{
//std::cout << "LVLI " << ESM4::printName(subHdr.typeId) << " skipping..." << subHdr.dataSize << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::LVLI::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::LeveledItem::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::LeveledItem::blank()
//{
//}

@ -0,0 +1,61 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_LVLI_H
#define ESM4_LVLI_H
#include <vector>
#include "common.hpp"
namespace ESM4
{
class Reader;
class Writer;
struct LeveledItem
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::int8_t mChanceNone;
std::uint8_t mLvlItemFlags;
std::uint8_t mData;
std::vector<LVLO> mLvlObject;
LeveledItem();
virtual ~LeveledItem();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_LVLI_H

@ -0,0 +1,76 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "mato.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Material::Material() : mFormId(0), mFlags(0)
{
mEditorId.clear();
mModel.clear();
}
ESM4::Material::~Material()
{
}
void ESM4::Material::load(ESM4::Reader& reader)
{
//mFormId = reader.adjustFormId(reader.hdr().record.id); // FIXME: use master adjusted?
mFormId = reader.hdr().record.id;
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_DNAM:
case ESM4::SUB_DATA:
{
//std::cout << "MATO " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::MATO::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Material::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Material::blank()
//{
//}

@ -0,0 +1,57 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_MATO_H
#define ESM4_MATO_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Material
{
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mModel;
Material();
virtual ~Material();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_MATO_H

102
extern/esm4/misc.cpp vendored

@ -0,0 +1,102 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "misc.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::MiscItem::MiscItem() : mFormId(0), mFlags(0), mScript(0), mBoundRadius(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
mData.value = 0;
mData.weight = 0.f;
}
ESM4::MiscItem::~MiscItem()
{
}
void ESM4::MiscItem::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("MISC FULL data read error");
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_ICON: reader.getZString(mIcon); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
case ESM4::SUB_DATA: reader.get(mData); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_MODT:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_MODS:
case ESM4::SUB_OBND:
case ESM4::SUB_VMAD:
case ESM4::SUB_YNAM:
case ESM4::SUB_ZNAM:
case ESM4::SUB_MICO: // FO3
case ESM4::SUB_RNAM: // FONV
{
//std::cout << "MISC " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::MISC::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::MiscItem::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::MiscItem::blank()
//{
//}

@ -0,0 +1,72 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_MISC_H
#define ESM4_MISC_H
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct MiscItem
{
#pragma pack(push, 1)
struct Data
{
std::uint32_t value; // gold
float weight;
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon; // inventory
FormId mScript;
float mBoundRadius;
Data mData;
MiscItem();
virtual ~MiscItem();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_MISC_H

373
extern/esm4/navi.cpp vendored

@ -0,0 +1,373 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "navi.hpp"
#include <cassert>
#include <stdexcept>
#include <iostream> // FIXME: debugging only
#include "reader.hpp"
//#include "writer.hpp"
#ifdef NDEBUG // FIXME: debuggigng only
#undef NDEBUG
#endif
ESM4::Navigation::Navigation()
{
mEditorId.clear();
}
ESM4::Navigation::~Navigation()
{
}
void ESM4::Navigation::IslandInfo::load(ESM4::Reader& reader)
{
reader.get(minX);
reader.get(minY);
reader.get(minZ);
reader.get(maxX);
reader.get(maxY);
reader.get(maxZ);
std::uint32_t count;
reader.get(count); // countTriangle;
if (count)
{
triangles.resize(count);
//std::cout << "NVMI island triangles " << std::dec << count << std::endl; // FIXME
for (std::vector<Navigation::Triangle>::iterator it = triangles.begin(); it != triangles.end(); ++it)
{
reader.get(*it);
}
}
reader.get(count); // countVertex;
if (count)
{
verticies.resize(count);
for (std::vector<ESM4::Vertex>::iterator it = verticies.begin(); it != verticies.end(); ++it)
{
reader.get(*it);
// FIXME: debugging only
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
std::cout << padding << "NVMI vert " << std::dec << (*it).x << ", " << (*it).y << ", " << (*it).z << std::endl;
#endif
}
}
}
void ESM4::Navigation::NavMeshInfo::load(ESM4::Reader& reader)
{
std::uint32_t count;
reader.get(formId);
reader.get(flags);
reader.get(x);
reader.get(y);
reader.get(z);
// FIXME: for debugging only
#if 0
std::string padding = "";
if (flags == ESM4::FLG_Modified)
padding.insert(0, 2, '-');
else if (flags == ESM4::FLG_Unmodified)
padding.insert(0, 4, '.');
padding.insert(0, reader.stackSize()*2, ' ');
std::cout << padding << "NVMI formId: 0x" << std::hex << formId << std::endl;
std::cout << padding << "NVMI flags: " << std::hex << flags << std::endl;
std::cout << padding << "NVMI center: " << std::dec << x << ", " << y << ", " << z << std::endl;
#endif
reader.get(flagPrefMerges);
reader.get(count); // countMerged;
if (count)
{
//std::cout << "NVMI countMerged " << std::dec << count << std::endl;
formIdMerged.resize(count);
for (std::vector<FormId>::iterator it = formIdMerged.begin(); it != formIdMerged.end(); ++it)
{
reader.get(*it);
}
}
reader.get(count); // countPrefMerged;
if (count)
{
//std::cout << "NVMI countPrefMerged " << std::dec << count << std::endl;
formIdPrefMerged.resize(count);
for (std::vector<FormId>::iterator it = formIdPrefMerged.begin(); it != formIdPrefMerged.end(); ++it)
{
reader.get(*it);
}
}
reader.get(count); // countLinkedDoors;
if (count)
{
//std::cout << "NVMI countLinkedDoors " << std::dec << count << std::endl;
linkedDoors.resize(count);
for (std::vector<DoorRef>::iterator it = linkedDoors.begin(); it != linkedDoors.end(); ++it)
{
reader.get(*it);
}
}
unsigned char island;
reader.get(island);
if (island)
{
Navigation::IslandInfo island;
island.load(reader);
islandInfo.push_back(island); // Maybe don't use a vector for just one entry?
}
else if (flags == FLG_Island) // FIXME: debug only
std::cerr << "nvmi no island but has 0x20 flag" << std::endl;
reader.get(locationMarker);
reader.get(worldSpaceId);
//FLG_Tamriel = 0x0000003c, // grid info follows, possibly Tamriel?
//FLG_Morrowind = 0x01380000, // grid info follows, probably Skywind
if (worldSpaceId == 0x0000003c || worldSpaceId == 0x01380000)
{
reader.get(cellGrid.grid.y); // NOTE: reverse order
reader.get(cellGrid.grid.x);
// FIXME: debugging only
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
if (worldSpaceId == ESM4::FLG_Morrowind)
std::cout << padding << "NVMI MW: X " << std::dec << cellGrid.grid.x << ", Y " << cellGrid.grid.y << std::endl;
else
std::cout << padding << "NVMI SR: X " << std::dec << cellGrid.grid.x << ", Y " << cellGrid.grid.y << std::endl;
#endif
}
else
{
reader.get(cellGrid.cellId);
#if 0
if (worldSpaceId == 0) // interior
std::cout << "NVMI Interior: cellId " << std::hex << cellGrid.cellId << std::endl;
else
std::cout << "NVMI FormID: cellId " << std::hex << cellGrid.cellId << std::endl;
#endif
}
}
// NVPP data seems to be organised this way (total is 0x64 = 100)
//
// (0) total | 0x1 | formid (index 0) | count | formid's
// (1) | count | formid's
// (2) | count | formid's
// (3) | count | formid's
// (4) | count | formid's
// (5) | count | formid's
// (6) | count | formid's
// (7) | count | formid's
// (8) | count | formid's
// (9) | count | formid's
// (10) | 0x1 | formid (index 1) | count | formid's
// (11) | count | formid's
// (12) | count | formid's
// (13) | count | formid's
// (14) | count | formid's
// (15) | count | formid's
// ...
//
// (88) | count | formid's
// (89) | count | formid's
//
// Here the pattern changes (final count is 0xa = 10)
//
// (90) | 0x1 | formid (index 9) | count | formid | index
// (91) | formid | index
// (92) | formid | index
// (93) | formid | index
// (94) | formid | index
// (95) | formid | index
// (96) | formid | index
// (97) | formid | index
// (98) | formid | index
// (99) | formid | index
//
// Note that the index values are not sequential, i.e. the first index value
// (i.e. row 90) for Update.esm is 2.
//
// Also note that there's no list of formid's following the final node (index 9)
//
// The same 10 formids seem to be used for the indicies, but not necessarily
// with the same index value (but only Update.esm differs?)
//
// formid cellid X Y Editor ID other formids in same X,Y S U D D
// -------- ------ --- --- --------------------------- ---------------------------- - - - -
// 00079bbf 9639 5 -4 WhiterunExterior17 00079bc3 0 6 0 0
// 0010377b 8ed5 6 24 DawnstarWesternMineExterior 1 1 1 1
// 000a3f44 9577 -22 2 RoriksteadEdge 2 9 2 2
// 00100f4b 8ea2 26 25 WinterholdExterior01 00100f4a, 00100f49 3 3 3 3
// 00103120 bc8e 42 -22 (near Riften) 4 2 4 4
// 00105e9a 929d -18 24 SolitudeExterior03 5 0 5 5
// 001030cb 7178 -40 1 SalviusFarmExterior01 (east of Markarth) 6 8 6 6
// 00098776 980b 4 -19 HelgenExterior 000cce3d 7 5 7 7
// 000e88cc 93de -9 14 (near Morthal) 0010519e, 0010519d, 000e88d2 8 7 8 8
// 000b87df b51d 33 5 WindhelmAttackStart05 9 4 9 9
//
void ESM4::Navigation::load(ESM4::Reader& reader)
{
//mFormId = reader.hdr().record.id;
//mFlags = reader.hdr().record.flags;
std::uint32_t esmVer = reader.esmVersion();
bool isFONV = esmVer == ESM4::VER_132 || esmVer == ESM4::VER_133 || esmVer == ESM4::VER_134;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: // seems to be unused?
{
if (!reader.getZString(mEditorId))
throw std::runtime_error ("NAVI EDID data read error");
break;
}
case ESM4::SUB_NVPP:
{
std::uint32_t total;
std::uint32_t count;
reader.get(total);
if (!total)
{
reader.get(count); // throw away
break;
}
total -= 10; // HACK
std::uint32_t node;
for (std::uint32_t i = 0; i < total; ++i)
{
std::vector<FormId> preferredPaths;
reader.get(count);
if (count == 1)
{
reader.get(node);
reader.get(count);
}
if (count)
{
preferredPaths.resize(count);
for (std::vector<FormId>::iterator it = preferredPaths.begin();
it != preferredPaths.end(); ++it)
{
reader.get(*it);
}
}
mPreferredPaths.push_back(std::make_pair(node, preferredPaths));
#if 0
std::cout << "node " << std::hex << node // FIXME: debugging only
<< ", count " << count << ", i " << std::dec << i << std::endl;
#endif
}
reader.get(count);
assert(count == 1 && "expected separator");
reader.get(node); // HACK
std::vector<FormId> preferredPaths;
mPreferredPaths.push_back(std::make_pair(node, preferredPaths)); // empty
#if 0
std::cout << "node " << std::hex << node // FIXME: debugging only
<< ", count " << 0 << std::endl;
#endif
reader.get(count); // HACK
assert(count == 10 && "expected 0xa");
std::uint32_t index;
for (std::uint32_t i = 0; i < count; ++i)
{
reader.get(node);
reader.get(index);
#if 0
std::cout << "node " << std::hex << node // FIXME: debugging only
<< ", index " << index << ", i " << std::dec << total+i << std::endl;
#endif
std::pair<std::map<FormId, std::uint32_t>::iterator, bool> res =
mPathIndexMap.insert(std::make_pair(node, index));
// FIXME: this throws if more than one file is being loaded
//if (!res.second)
//throw std::runtime_error ("node already exists in the preferred path index map");
}
break;
}
case ESM4::SUB_NVER:
{
std::uint32_t version; // always the same? (0x0c)
reader.get(version); // TODO: store this or use it for merging?
//std::cout << "NAVI version " << std::dec << version << std::endl;
break;
}
case ESM4::SUB_NVMI: // multiple
{
if (esmVer == ESM4::VER_094 || esmVer == ESM4::VER_170 || isFONV)
{
reader.skipSubRecordData(); // FIXME: FO3/FONV have different form of NavMeshInfo
break;
}
//std::cout << "\nNVMI start" << std::endl;
NavMeshInfo nvmi;
nvmi.load(reader);
mNavMeshInfo.push_back (nvmi);
break;
}
case ESM4::SUB_NVSI: // from Dawnguard onwards
case ESM4::SUB_NVCI: // FO3
{
reader.skipSubRecordData(); // FIXME:
break;
}
default:
{
throw std::runtime_error("ESM4::NAVI::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
}
//void ESM4::Navigation::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Navigation::blank()
//{
//}

116
extern/esm4/navi.hpp vendored

@ -0,0 +1,116 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_NAVI_H
#define ESM4_NAVI_H
#include <vector>
#include <map>
#include "common.hpp" // CellGrid, Vertex
namespace ESM4
{
class Reader;
class Writer;
struct Navigation
{
#pragma pack(push,1)
struct DoorRef
{
std::uint32_t unknown;
FormId formId;
};
struct Triangle
{
std::uint16_t vertexIndex0;
std::uint16_t vertexIndex1;
std::uint16_t vertexIndex2;
};
#pragma pack(pop)
struct IslandInfo
{
float minX;
float minY;
float minZ;
float maxX;
float maxY;
float maxZ;
std::vector<Triangle> triangles;
std::vector<Vertex> verticies;
void load(ESM4::Reader& reader);
};
enum Flags // NVMI island flags (not certain)
{
FLG_Island = 0x00000020,
FLG_Modified = 0x00000000, // not island
FLG_Unmodified = 0x00000040 // not island
};
struct NavMeshInfo
{
FormId formId;
std::uint32_t flags;
// center point of the navmesh
float x;
float y;
float z;
std::uint32_t flagPrefMerges;
std::vector<FormId> formIdMerged;
std::vector<FormId> formIdPrefMerged;
std::vector<DoorRef> linkedDoors;
std::vector<IslandInfo> islandInfo;
std::uint32_t locationMarker;
FormId worldSpaceId;
CellGrid cellGrid;
void load(ESM4::Reader& reader);
};
std::string mEditorId;
std::vector<NavMeshInfo> mNavMeshInfo;
std::vector<std::pair<std::uint32_t, std::vector<FormId> > > mPreferredPaths;
std::map<FormId, std::uint32_t> mPathIndexMap;
Navigation();
virtual ~Navigation();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_NAVI_H

268
extern/esm4/navm.cpp vendored

@ -0,0 +1,268 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "navm.hpp"
#include <stdexcept>
#include <iostream> // FIXME: debugging only
#include "reader.hpp"
//#include "writer.hpp"
ESM4::NavMesh::NavMesh() : mFormId(0), mFlags(0)
{
}
ESM4::NavMesh::~NavMesh()
{
}
void ESM4::NavMesh::NVNMstruct::load(ESM4::Reader& reader)
{
//std::cout << "start: divisor " << std::dec << divisor << ", segments " << triSegments.size() << //std::endl;
//"this 0x" << this << std::endl; // FIXME
std::uint32_t count;
reader.get(unknownNVER);
reader.get(unknownLCTN);
reader.get(worldSpaceId);
//FLG_Tamriel = 0x0000003c, // grid info follows, possibly Tamriel?
//FLG_Morrowind = 0x01380000, // grid info follows, probably Skywind
if (worldSpaceId == 0x0000003c || worldSpaceId == 0x01380000)
{
// ^
// Y | X Y Index
// | 0,0 0
// 1 |23 0,1 1
// 0 |01 1,0 2
// +--- 1,1 3
// 01 ->
// X
//
// e.g. Dagonfel X:13,14,15,16 Y:43,44,45,46 (Morrowind X:7 Y:22)
//
// Skywind: -4,-3 -2,-1 0,1 2,3 4,5 6,7
// Morrowind: -2 -1 0 1 2 3
//
// Formula seems to be floor(Skywind coord / 2) <cmath>
//
reader.get(cellGrid.grid.y); // NOTE: reverse order
reader.get(cellGrid.grid.x);
// FIXME: debugging only
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
if (worldSpaceId == ESM4::FLG_Morrowind)
std::cout << padding << "NVNM MW: X " << std::dec << cellGrid.grid.x << ", Y " << cellGrid.grid.y << std::endl;
else
std::cout << padding << "NVNM SR: X " << std::dec << cellGrid.grid.x << ", Y " << cellGrid.grid.y << std::endl;
#endif
}
else
{
reader.get(cellGrid.cellId);
#if 0
std::string padding = ""; // FIXME
padding.insert(0, reader.stackSize()*2, ' ');
if (worldSpaceId == 0) // interior
std::cout << padding << "NVNM Interior: cellId " << std::hex << cellGrid.cellId << std::endl;
else
std::cout << padding << "NVNM FormID: cellId " << std::hex << cellGrid.cellId << std::endl;
#endif
}
reader.get(count); // numVerticies
if (count)
{
verticies.resize(count);
for (std::vector<Vertex>::iterator it = verticies.begin(); it != verticies.end(); ++it)
{
reader.get(*it);
// FIXME: debugging only
#if 0
//if (reader.hdr().record.id == 0x2004ecc) // FIXME
std::cout << "nvnm vert " << (*it).x << ", " << (*it).y << ", " << (*it).z << std::endl;
#endif
}
}
reader.get(count); // numTriangles;
if (count)
{
triangles.resize(count);
for (std::vector<Triangle>::iterator it = triangles.begin(); it != triangles.end(); ++it)
{
reader.get(*it);
}
}
reader.get(count); // numExtConn;
if (count)
{
extConns.resize(count);
for (std::vector<ExtConnection>::iterator it = extConns.begin(); it != extConns.end(); ++it)
{
reader.get(*it);
// FIXME: debugging only
#if 0
std::cout << "nvnm ext 0x" << std::hex << (*it).navMesh << std::endl;
#endif
}
}
reader.get(count); // numDoorTriangles;
if (count)
{
doorTriangles.resize(count);
for (std::vector<DoorTriangle>::iterator it = doorTriangles.begin(); it != doorTriangles.end(); ++it)
{
reader.get(*it);
}
}
reader.get(count); // numCoverTriangles;
if (count)
{
coverTriangles.resize(count);
for (std::vector<std::uint16_t>::iterator it = coverTriangles.begin(); it != coverTriangles.end(); ++it)
{
reader.get(*it);
}
}
// abs((maxX - minX) / divisor) = Max X Distance
reader.get(divisor); // FIXME: latest over-writes old
reader.get(maxXDist); // FIXME: update with formula
reader.get(maxYDist);
reader.get(minX); // FIXME: use std::min
reader.get(minY);
reader.get(minZ);
reader.get(maxX);
reader.get(maxY);
reader.get(maxZ);
// FIXME: should check remaining size here
// there are divisor^2 segments, each segment is a vector of triangle indicies
for (unsigned int i = 0; i < divisor*divisor; ++i)
{
reader.get(count); // NOTE: count may be zero
std::vector<std::uint16_t> indicies;
indicies.resize(count);
for (std::vector<std::uint16_t>::iterator it = indicies.begin(); it != indicies.end(); ++it)
{
reader.get(*it);
}
triSegments.push_back(indicies);
}
assert(triSegments.size() == divisor*divisor && "tiangle segments size is not the square of divisor");
#if 0
if (triSegments.size() != divisor*divisor)
std::cout << "divisor " << std::dec << divisor << ", segments " << triSegments.size() << //std::endl;
"this 0x" << this << std::endl;
#endif
}
void ESM4::NavMesh::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
mFlags = reader.hdr().record.flags;
//std::cout << "NavMesh 0x" << std::hex << this << std::endl; // FIXME
std::uint32_t subSize = 0; // for XXXX sub record
// FIXME: debugging only
#if 0
std::string padding = "";
padding.insert(0, reader.stackSize()*2, ' ');
std::cout << padding << "NAVM flags 0x" << std::hex << reader.hdr().record.flags << std::endl;
std::cout << padding << "NAVM id 0x" << std::hex << reader.hdr().record.id << std::endl;
#endif
while (reader.getSubRecordHeader())
{
switch (reader.subRecordHeader().typeId)
{
case ESM4::SUB_NVNM:
{
NVNMstruct nvnm;
nvnm.load(reader);
mData.push_back(nvnm); // FIXME try swap
break;
}
case ESM4::SUB_ONAM:
case ESM4::SUB_PNAM:
case ESM4::SUB_NNAM:
{
if (subSize)
{
reader.skipSubRecordData(subSize); // special post XXXX
reader.updateRecordRemaining(subSize); // WARNING: manual update
subSize = 0;
}
else
//const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
//std::cout << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip
break;
}
case ESM4::SUB_XXXX:
{
reader.get(subSize);
break;
}
case ESM4::SUB_NVER: // FO3
case ESM4::SUB_DATA: // FO3
case ESM4::SUB_NVVX: // FO3
case ESM4::SUB_NVTR: // FO3
case ESM4::SUB_NVCA: // FO3
case ESM4::SUB_NVDP: // FO3
case ESM4::SUB_NVGD: // FO3
case ESM4::SUB_NVEX: // FO3
case ESM4::SUB_EDID: // FO3
{
reader.skipSubRecordData(); // FIXME:
break;
}
default:
throw std::runtime_error("ESM4::NAVM::load - Unknown subrecord " +
ESM4::printName(reader.subRecordHeader().typeId));
}
}
//std::cout << "num nvnm " << std::dec << mData.size() << std::endl; // FIXME
}
//void ESM4::NavMesh::save(ESM4::Writer& writer) const
//{
//}
void ESM4::NavMesh::blank()
{
}

111
extern/esm4/navm.hpp vendored

@ -0,0 +1,111 @@
/*
Copyright (C) 2015, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_NAVM_H
#define ESM4_NAVM_H
#include <vector>
#include "common.hpp" // CellGrid, Vertex
namespace ESM4
{
class Reader;
class Writer;
struct NavMesh
{
#pragma pack(push,1)
struct Triangle
{
std::uint16_t vertexIndex0;
std::uint16_t vertexIndex1;
std::uint16_t vertexIndex2;
std::uint16_t edge0;
std::uint16_t edge1;
std::uint16_t edge2;
std::uint16_t coverMarker;
std::uint16_t coverFlags;
};
struct ExtConnection
{
std::uint32_t unknown;
FormId navMesh;
std::uint16_t triangleIndex;
};
struct DoorTriangle
{
std::uint16_t triangleIndex;
std::uint32_t unknown;
FormId doorRef;
};
#pragma pack(pop)
struct NVNMstruct
{
std::uint32_t unknownNVER;
std::uint32_t unknownLCTN;
FormId worldSpaceId;
CellGrid cellGrid;
std::vector<Vertex> verticies;
std::vector<Triangle> triangles;
std::vector<ExtConnection> extConns;
std::vector<DoorTriangle> doorTriangles;
std::vector<std::uint16_t> coverTriangles;
std::uint32_t divisor;
float maxXDist;
float maxYDist;
float minX;
float minY;
float minZ;
float maxX;
float maxY;
float maxZ;
// there are divisor^2 segments, each segment is a vector of triangle indicies
std::vector<std::vector<std::uint16_t> > triSegments;
void load(ESM4::Reader& esm);
};
std::vector<NVNMstruct> mData; // Up to 4 skywind cells in one Morrowind cell
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
NavMesh();
virtual ~NavMesh();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
void blank();
};
}
#endif // ESM4_NAVM_H

237
extern/esm4/npc_.cpp vendored

@ -0,0 +1,237 @@
/*
Copyright (C) 2016-2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "npc_.hpp"
#include <stdexcept>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Npc::Npc() : mFormId(0), mFlags(0), mRace(0), mClass(0), mHair(0), mEyes(0), mDeathItem(0),
mScript(0), mCombatStyle(0), mSoundBase(0), mSound(0), mSoundChance(0),
mFootWeight(0.f), mBoundRadius(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
std::memset(&mAIData, 0, sizeof(AIData));
std::memset(&mData, 0, sizeof(Data));
std::memset(&mBaseConfig, 0, sizeof(ActorBaseConfig));
std::memset(&mFaction, 0, sizeof(ActorFaction));
}
ESM4::Npc::~Npc()
{
}
void ESM4::Npc::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
std::uint32_t esmVer = reader.esmVersion();
bool isFONV = esmVer == ESM4::VER_132 || esmVer == ESM4::VER_133 || esmVer == ESM4::VER_134;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID: reader.getZString(mEditorId); break;
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("NPC_ FULL data read error");
break;
}
case ESM4::SUB_CNTO:
{
static InventoryItem inv; // FIXME: use unique_ptr here?
reader.get(inv);
reader.adjustFormId(inv.item);
mInventory.push_back(inv);
break;
}
case ESM4::SUB_SPLO:
{
FormId id;
reader.getFormId(id);
mSpell.push_back(id);
break;
}
case ESM4::SUB_PKID:
{
FormId id;
reader.getFormId(id);
mAIPackages.push_back(id);
break;
}
case ESM4::SUB_SNAM:
{
reader.get(mFaction);
reader.adjustFormId(mFaction.faction);
break;
}
case ESM4::SUB_RNAM: reader.getFormId(mRace); break;
case ESM4::SUB_CNAM: reader.getFormId(mClass); break;
case ESM4::SUB_HNAM: reader.getFormId(mHair); break;
case ESM4::SUB_ENAM: reader.getFormId(mEyes); break;
//
case ESM4::SUB_INAM: reader.getFormId(mDeathItem); break;
case ESM4::SUB_SCRI: reader.getFormId(mScript); break;
//
case ESM4::SUB_AIDT:
{
if (esmVer == ESM4::VER_094 || esmVer == ESM4::VER_170 || isFONV)
{
reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip
break;
}
reader.get(mAIData);
break;
}
case ESM4::SUB_ACBS:
{
if (esmVer == ESM4::VER_094 || esmVer == ESM4::VER_170 || isFONV)
{
reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip
break;
}
reader.get(mBaseConfig);
break;
}
case ESM4::SUB_DATA:
{
if (esmVer == ESM4::VER_094 || esmVer == ESM4::VER_170 || isFONV)
{
if (subHdr.dataSize != 0) // FIXME FO3
reader.skipSubRecordData();
break; // zero length
}
reader.get(&mData, 33); // FIXME: check packing
break;
}
case ESM4::SUB_ZNAM: reader.getFormId(mCombatStyle); break;
case ESM4::SUB_CSCR: reader.getFormId(mSoundBase); break;
case ESM4::SUB_CSDI: reader.getFormId(mSound); break;
case ESM4::SUB_CSDC: reader.get(mSoundChance); break;
case ESM4::SUB_WNAM: reader.get(mFootWeight); break;
//
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
case ESM4::SUB_KFFZ:
{
std::string str;
if (!reader.getZString(str))
throw std::runtime_error ("NPC_ KFFZ data read error");
// Seems to be only below 3, and only happens 3 times while loading TES4:
// Forward_SheogorathWithCane.kf
// TurnLeft_SheogorathWithCane.kf
// TurnRight_SheogorathWithCane.kf
std::stringstream ss(str);
std::string file;
while (std::getline(ss, file, '\0')) // split the strings
mKf.push_back(file);
break;
}
case ESM4::SUB_LNAM:
case ESM4::SUB_HCLR:
case ESM4::SUB_FGGS:
case ESM4::SUB_FGGA:
case ESM4::SUB_FGTS:
case ESM4::SUB_FNAM:
case ESM4::SUB_ATKR:
case ESM4::SUB_COCT:
case ESM4::SUB_CRIF:
case ESM4::SUB_CSDT:
case ESM4::SUB_DNAM:
case ESM4::SUB_DOFT:
case ESM4::SUB_DPLT:
case ESM4::SUB_ECOR:
case ESM4::SUB_ANAM:
case ESM4::SUB_ATKD:
case ESM4::SUB_ATKE:
case ESM4::SUB_DEST:
case ESM4::SUB_DSTD:
case ESM4::SUB_DSTF:
case ESM4::SUB_FTST:
case ESM4::SUB_HCLF:
case ESM4::SUB_KSIZ:
case ESM4::SUB_KWDA:
case ESM4::SUB_NAM5:
case ESM4::SUB_NAM6:
case ESM4::SUB_NAM7:
case ESM4::SUB_NAM8:
case ESM4::SUB_NAM9:
case ESM4::SUB_NAMA:
case ESM4::SUB_OBND:
case ESM4::SUB_PNAM:
case ESM4::SUB_PRKR:
case ESM4::SUB_PRKZ:
case ESM4::SUB_QNAM:
case ESM4::SUB_SOFT:
case ESM4::SUB_SPCT:
case ESM4::SUB_TIAS:
case ESM4::SUB_TINC:
case ESM4::SUB_TINI:
case ESM4::SUB_TINV:
case ESM4::SUB_TPLT:
case ESM4::SUB_VMAD:
case ESM4::SUB_VTCK:
case ESM4::SUB_GNAM:
case ESM4::SUB_SHRT:
case ESM4::SUB_SPOR:
case ESM4::SUB_EAMT: // FO3
case ESM4::SUB_NAM4: // FO3
case ESM4::SUB_COED: // FO3
{
//std::cout << "NPC_ " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::NPC_::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Npc::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Npc::blank()
//{
//}

115
extern/esm4/npc_.hpp vendored

@ -0,0 +1,115 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_NPC__H
#define ESM4_NPC__H
#include <vector>
#include "common.hpp"
namespace ESM4
{
class Reader;
class Writer;
struct Npc
{
struct SkillValues
{
std::uint8_t armorer;
std::uint8_t athletics;
std::uint8_t blade;
std::uint8_t block;
std::uint8_t blunt;
std::uint8_t handToHand;
std::uint8_t heavyArmor;
std::uint8_t alchemy;
std::uint8_t alteration;
std::uint8_t conjuration;
std::uint8_t destruction;
std::uint8_t illusion;
std::uint8_t mysticism;
std::uint8_t restoration;
std::uint8_t acrobatics;
std::uint8_t lightArmor;
std::uint8_t marksman;
std::uint8_t mercantile;
std::uint8_t security;
std::uint8_t sneak;
std::uint8_t speechcraft;
};
struct Data
{
SkillValues skills;
std::uint32_t health;
AttributeValues attribs;
};
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
FormId mRace;
FormId mClass;
FormId mHair;
FormId mEyes;
FormId mDeathItem;
std::vector<FormId> mSpell;
FormId mScript;
AIData mAIData;
std::vector<FormId> mAIPackages;
ActorBaseConfig mBaseConfig;
ActorFaction mFaction;
Data mData;
FormId mCombatStyle;
FormId mSoundBase;
FormId mSound;
std::uint8_t mSoundChance;
float mFootWeight;
float mBoundRadius;
std::vector<std::string> mKf; // filenames only, get directory path from mModel
std::vector<InventoryItem> mInventory;
Npc();
virtual ~Npc();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_NPC__H

229
extern/esm4/race.cpp vendored

@ -0,0 +1,229 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#include "race.hpp"
#include <stdexcept>
#include <iostream> // FIXME: debugging only
#include <iomanip>
#include "reader.hpp"
//#include "writer.hpp"
ESM4::Race::Race() : mFormId(0), mFlags(0), mBoundRadius(0.f)
{
mEditorId.clear();
mFullName.clear();
mModel.clear();
mIcon.clear();
mData.flags = 0;
}
ESM4::Race::~Race()
{
}
void ESM4::Race::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.id;
reader.adjustFormId(mFormId);
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())
{
const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader();
switch (subHdr.typeId)
{
case ESM4::SUB_EDID:
{
reader.getZString(mEditorId);
std::cout << "RACE " << mEditorId << std::endl;
break;
}
case ESM4::SUB_FULL:
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mFullName);
else if (!reader.getZString(mFullName))
throw std::runtime_error ("RACE FULL data read error");
break;
}
case ESM4::SUB_MODL: reader.getZString(mModel); break;
case ESM4::SUB_ICON: reader.getZString(mIcon); break; // Only in TES4?
//case ESM4::SUB_DATA: reader.get(mData); break;
case ESM4::SUB_MODB: reader.get(mBoundRadius); break;
//case ESM4::SUB_MODT:
case ESM4::SUB_DESC: //skipping...1 <- different lenghts
{
if (reader.hasLocalizedStrings())
reader.getLocalizedString(mDesc); // TODO check if formid is null
else if (!reader.getZString(mDesc))
throw std::runtime_error ("RACE DESC data read error");
break;
}
case ESM4::SUB_ATTR: //skipping...16 // Only in TES4? guess - 8 attrib each for male/female?
// Argonian 28 28 1e 32 32 1e 1e 32 28 32 28 28 28 1e 1e 32
// 40 40 30 50 50 30 30 50 40 50 40 40 40 30 30 50
// Nord 32 1e 1e 28 28 32 1e 32 32 1e 28 28 28 28 1e 32
// 50 30 30 40 40 50 30 50 50 30 40 40 40 40 30 50
// StrIntWilAglSpdEndPerLuk
// Male Female
case ESM4::SUB_CNAM: //skipping...1 // Only in TES4?
// Sheogorath 0x00
// Golden Saint 0x03
// Dark Seducer 0x0C
// Vampire Race 0x00
// Dremora 0x07
// Argonian 0x00
// Nord 0x05
// Breton 0x05
// Wood Elf 0x0D
// khajiit 0x05
// Dark Elf 0x00
// Orc 0x0C
// High Elf 0x0F
// Redguard 0x0D
// Imperial 0x0D
case ESM4::SUB_DATA: //skipping...36 // ?? different length to TES5
// Altimer
//
// hex 13 05 14 0a 15 05 16 0a 17 05 18 0a ff 00 00 00
// dec 5 10 5 10 5 10 -1 0
// alc alt conj dest illu myst none unknown (always 00 00)
//
// cd cc 8c 3f : 1.1 height Male
// cd cc 8c 3f : 1.1 height Female
// 00 00 80 3f : 1.0 weihgt Male
// 00 00 80 3f : 1.0 weight Female
// 01 00 00 00 fist byte 1 means playable? uint32_t flag?
//
// Redguard
//
// hex 0d 0a 10 0a 12 05 1b 05 0e 0a 1d 05 ff 00 00 00
// dec 10 10 5 5 10 5 -1
// ath blun h.arm l.arm blade merch
//
//
// 0a d7 83 3f : 1.03 height Male
// 00 00 80 3f : 1.0 height Female
// 0a d7 83 3f : 1.03 weight Male
// 00 00 80 3f : 1.0 weight Female
// 01 00 00 00
//
// skill index
// 0x0C Armorer
// 0x0D Athletics
// 0x0E Blade
// 0x0F Block
// 0x10 Blunt
// 0x11 HandToHand
// 0x12 HeavyArmor
// 0x13 Alchemy
// 0x14 Alteration
// 0x15 Conjuration
// 0x16 Destruction
// 0x17 Illusion
// 0x18 Mysticism
// 0x19 Restoration
// 0x1A Acrobatics
// 0x1B LightArmor
// 0x1C Marksman
// 0x1D Mercantile
// 0x1E Security
// 0x1F Sneak
// 0x20 Speechcraft
case ESM4::SUB_SNAM: //skipping...2 // only in TES4?
case ESM4::SUB_XNAM: //skipping...8 // only in TES4? Often has similar numbers to VNAM
case ESM4::SUB_ENAM: //skipping...0 <- different lengthts, maybe formids for EYES?
case ESM4::SUB_HNAM: //skipping...0 <- different lengthts, maybe formids for HAIR?
case ESM4::SUB_VNAM: //skipping...8 // equipment type flags meant to be uint32 ???
// GLOB reference? shows up in SCRO in sript
// records and CTDA in INFO records
{
std::cout << "RACE " << ESM4::printName(subHdr.typeId) << " skipping..." << subHdr.dataSize << std::endl;
// For debugging only
//#if 0
unsigned char mDataBuf[256/*bufSize*/];
reader.get(&mDataBuf[0], subHdr.dataSize);
std::ostringstream ss;
for (unsigned int i = 0; i < subHdr.dataSize; ++i)
{
//if (mDataBuf[i] > 64 && mDataBuf[i] < 91)
//ss << (char)(mDataBuf[i]) << " ";
//else
ss << std::setfill('0') << std::setw(2) << std::hex << (int)(mDataBuf[i]);
if ((i & 0x000f) == 0xf)
ss << "\n";
else if (i < 256/*bufSize*/-1)
ss << " ";
}
std::cout << ss.str() << std::endl;
//reader.skipSubRecordData();
break;
}
//#endif
case ESM4::SUB_DNAM: //skipping...8 // decapitate armor, 2 formids
case ESM4::SUB_FGGA: //skipping...120 // prob face gen stuff
case ESM4::SUB_FGGS: //skipping...200 // prob face gen stuff
case ESM4::SUB_FGTS: //skipping...200 // prob face gen stuff
case ESM4::SUB_FNAM: //skipping...0 // start marker female model
case ESM4::SUB_INDX: //skipping...4 // marker preceding egt models? uint32 always 0
case ESM4::SUB_MNAM: //skipping...0 // start marker male model
case ESM4::SUB_NAM0: //skipping...0 // start marker head data
case ESM4::SUB_NAM1: //skipping...0 // strat marker egt models
case ESM4::SUB_PNAM: //skipping...4 // face gen main clamp float
case ESM4::SUB_SPLO: //skipping...4 // bonus spell formid (TES5 may have SPCT and multiple SPLO)
case ESM4::SUB_UNAM: //skipping...4 // face gen face clamp float
case ESM4::SUB_YNAM: // FO3
case ESM4::SUB_NAM2: // FO3
case ESM4::SUB_VTCK: // FO3
case ESM4::SUB_MODT: // FO3
case ESM4::SUB_MODD: // FO3
case ESM4::SUB_ONAM: // FO3
{
//std::cout << "RACE " << ESM4::printName(subHdr.typeId) << " skipping..." << subHdr.dataSize << std::endl;
reader.skipSubRecordData();
break;
}
default:
throw std::runtime_error("ESM4::RACE::load - Unknown subrecord " + ESM4::printName(subHdr.typeId));
}
}
}
//void ESM4::Race::save(ESM4::Writer& writer) const
//{
//}
//void ESM4::Race::blank()
//{
//}

@ -0,0 +1,71 @@
/*
Copyright (C) 2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
Much of the information on the data structures are based on the information
from Tes4Mod:Mod_File_Format and Tes5Mod:File_Formats but also refined by
trial & error. See http://en.uesp.net/wiki for details.
*/
#ifndef ESM4_RACE
#define ESM4_RACE
#include <string>
#include <cstdint>
namespace ESM4
{
class Reader;
class Writer;
typedef std::uint32_t FormId;
struct Race
{
#pragma pack(push, 1)
struct Data
{
std::uint8_t flags; // 0x01 = not playable, 0x02 = not male, 0x04 = not female, ?? = fixed
};
#pragma pack(pop)
FormId mFormId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
std::string mFullName;
std::string mModel;
std::string mIcon; // inventory
std::string mDesc;
float mBoundRadius;
Data mData;
Race();
virtual ~Race();
virtual void load(ESM4::Reader& reader);
//virtual void save(ESM4::Writer& writer) const;
//void blank();
};
}
#endif // ESM4_RACE

@ -0,0 +1,560 @@
/*
Copyright (C) 2015-2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
*/
#include "reader.hpp"
#include <cassert>
#include <stdexcept>
#include <unordered_map>
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <OgreResourceGroupManager.h>
#include <zlib.h>
#include "formid.hpp"
#ifdef NDEBUG // FIXME: debugging only
#undef NDEBUG
#endif
ESM4::Reader::Reader() : mObserver(nullptr), mRecordRemaining(0), mCellGridValid(false)
{
mCtx.modIndex = 0;
mCtx.currWorld = 0;
mCtx.currCell = 0;
mCtx.recHeaderSize = sizeof(ESM4::RecordHeader);
mInBuf.reset();
mDataBuf.reset();
mStream.setNull();
mSavedStream.setNull();
}
ESM4::Reader::~Reader()
{
}
// Since the record data may have been compressed, it is not always possible to use seek() to
// go to a position of a sub record.
//
// The record header needs to be saved in the context or the header needs to be re-loaded after
// restoring the context. The latter option was chosen.
ESM4::ReaderContext ESM4::Reader::getContext()
{
mCtx.filePos = mStream->tell() - mCtx.recHeaderSize; // update file position
return mCtx;
}
// NOTE: Assumes that the caller has reopened the file if necessary
bool ESM4::Reader::restoreContext(const ESM4::ReaderContext& ctx)
{
if (!mSavedStream.isNull())
{
mStream = mSavedStream;
mSavedStream.setNull();
}
mCtx.groupStack.clear(); // probably not necessary?
mCtx = ctx;
mStream->seek(ctx.filePos); // update file position
//return getRecordHeader(); // can't use it because mStream may have been switched
if (mObserver)
mObserver->update(mCtx.recHeaderSize);
return (mStream->read(&mRecordHeader, mCtx.recHeaderSize) == mCtx.recHeaderSize
&& (mRecordRemaining = mRecordHeader.record.dataSize)); // for keeping track of sub records
}
bool ESM4::Reader::skipNextGroupCellChild()
{
if (mStream->eof())
return false;
std::size_t pos = mStream->tell(); // save
ESM4::RecordHeader hdr;
if (!mStream->read(&hdr, mCtx.recHeaderSize))
throw std::runtime_error("ESM4::Reader::could not peek header");
if (hdr.group.type != ESM4::Grp_CellChild)
{
mStream->seek(pos); // go back to saved
return false;
}
mCtx.groupStack.back().second -= hdr.group.groupSize;
mStream->skip(hdr.group.groupSize - (std::uint32_t)mCtx.recHeaderSize); // already read the header
if (mObserver)
mObserver->update(hdr.group.groupSize);
return true;
}
// TODO: consider checking file path using boost::filesystem::exists()
std::size_t ESM4::Reader::openTes4File(const std::string& name)
{
mCtx.filename = name;
mStream = Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(
OGRE_NEW_T(std::ifstream(name.c_str(), std::ios_base::binary),
Ogre::MEMCATEGORY_GENERAL), /*freeOnClose*/true));
return mStream->size();
}
void ESM4::Reader::setRecHeaderSize(const std::size_t size)
{
mCtx.recHeaderSize = size;
}
void ESM4::Reader::registerForUpdates(ESM4::ReaderObserver *observer)
{
mObserver = observer;
}
// FIXME: only "English" strings supported for now
void ESM4::Reader::buildLStringIndex()
{
if ((mHeader.mFlags & Rec_ESM) == 0 || (mHeader.mFlags & Rec_Localized) == 0)
return;
boost::filesystem::path p(mCtx.filename);
std::string filename = p.stem().filename().string();
buildLStringIndex("Strings/" + filename + "_English.STRINGS", Type_Strings);
buildLStringIndex("Strings/" + filename + "_English.ILSTRINGS", Type_ILStrings);
buildLStringIndex("Strings/" + filename + "_English.DLSTRINGS", Type_DLStrings);
}
void ESM4::Reader::buildLStringIndex(const std::string& stringFile, LocalizedStringType stringType)
{
std::uint32_t numEntries;
std::uint32_t dataSize;
std::uint32_t stringId;
LStringOffset sp;
sp.type = stringType;
// TODO: possibly check if the resource exists?
Ogre::DataStreamPtr filestream = Ogre::ResourceGroupManager::getSingleton().openResource(stringFile);
switch (stringType)
{
case Type_Strings: mStrings = filestream; break;
case Type_ILStrings: mILStrings = filestream; break;
case Type_DLStrings: mDLStrings = filestream; break;
default:
throw std::runtime_error("ESM4::Reader::unexpected string type");
}
filestream->read(&numEntries, sizeof(numEntries));
filestream->read(&dataSize, sizeof(dataSize));
std::size_t dataStart = filestream->size() - dataSize;
for (unsigned int i = 0; i < numEntries; ++i)
{
filestream->read(&stringId, sizeof(stringId));
filestream->read(&sp.offset, sizeof(sp.offset));
sp.offset += (std::uint32_t)dataStart;
mLStringIndex[stringId] = sp;
}
//assert(dataStart - filestream->tell() == 0 && "String file start of data section mismatch");
}
void ESM4::Reader::getLocalizedString(std::string& str)
{
std::uint32_t stringId;
get(stringId);
getLocalizedString(stringId, str);
}
// FIXME: very messy and probably slow/inefficient
void ESM4::Reader::getLocalizedString(const FormId stringId, std::string& str)
{
const std::map<FormId, LStringOffset>::const_iterator it = mLStringIndex.find(stringId);
if (it != mLStringIndex.end())
{
Ogre::DataStreamPtr filestream;
switch (it->second.type)
{
case Type_Strings:
{
filestream = mStrings;
filestream->seek(it->second.offset);
char ch;
std::vector<char> data;
do {
filestream->read(&ch, sizeof(ch));
data.push_back(ch);
} while (ch != 0);
str = std::string(data.data());
return;
}
case Type_ILStrings: filestream = mILStrings; break;
case Type_DLStrings: filestream = mDLStrings; break;
default:
throw std::runtime_error("ESM4::Reader::getLocalizedString unexpected string type");
}
// get ILStrings or DLStrings
filestream->seek(it->second.offset);
getZString(str, filestream);
}
else
throw std::runtime_error("ESM4::Reader::getLocalizedString localized string not found");
}
bool ESM4::Reader::getRecordHeader()
{
// FIXME: this seems very hacky but we may have skipped subrecords from within an inflated data block
if (/*mStream->eof() && */!mSavedStream.isNull())
{
mStream = mSavedStream;
mSavedStream.setNull();
}
// keep track of data left to read from the file
// FIXME: having a default instance of mObserver might be faster than checking for null all the time?
if (mObserver)
mObserver->update(mCtx.recHeaderSize);
return (mStream->read(&mRecordHeader, mCtx.recHeaderSize) == mCtx.recHeaderSize
&& (mRecordRemaining = mRecordHeader.record.dataSize)); // for keeping track of sub records
// After reading the record header we can cache a WRLD or CELL formId for convenient access later.
// (currently currWorld and currCell are set manually when loading the WRLD and CELL records)
}
bool ESM4::Reader::getSubRecordHeader()
{
bool result = false;
// NOTE: some SubRecords have 0 dataSize (e.g. SUB_RDSD in one of REC_REGN records in Oblivion.esm).
// Also SUB_XXXX has zero dataSize and the following 4 bytes represent the actual dataSize
// - hence it require manual updtes to mRecordRemaining. See ESM4::NavMesh and ESM4::World.
if (mRecordRemaining >= sizeof(mSubRecordHeader))
{
result = get(mSubRecordHeader);
mRecordRemaining -= (sizeof(mSubRecordHeader) + mSubRecordHeader.dataSize);
}
return result;
}
// NOTE: the parameter 'files' must have the file names in the loaded order
void ESM4::Reader::updateModIndicies(const std::vector<std::string>& files)
{
if (files.size() >= 0xff)
throw std::runtime_error("ESM4::Reader::updateModIndicies too many files"); // 0xff is reserved
// NOTE: this map is rebuilt each time this method is called (i.e. each time a file is loaded)
// Perhaps there is an opportunity to optimize this by saving the result somewhere.
// But then, the number of files is at most around 250 so perhaps keeping it simple might be better.
// build a lookup map
std::unordered_map<std::string, size_t> fileIndex;
for (size_t i = 0; i < files.size(); ++i) // ATTENTION: assumes current file is not included
fileIndex[boost::to_lower_copy<std::string>(files[i])] = i;
mHeader.mModIndicies.resize(mHeader.mMaster.size());
for (unsigned int i = 0; i < mHeader.mMaster.size(); ++i)
{
// locate the position of the dependency in already loaded files
std::unordered_map<std::string, size_t>::const_iterator it
= fileIndex.find(boost::to_lower_copy<std::string>(mHeader.mMaster[i].name));
if (it != fileIndex.end())
mHeader.mModIndicies[i] = (std::uint32_t)((it->second << 24) & 0xff000000);
else
throw std::runtime_error("ESM4::Reader::updateModIndicies required dependency file not loaded");
#if 0
std::cout << "Master Mod: " << mHeader.mMaster[i].name << ", " // FIXME: debugging only
<< ESM4::formIdToString(mHeader.mModIndicies[i]) << std::endl;
#endif
}
if (!mHeader.mModIndicies.empty() && mHeader.mModIndicies[0] != 0)
throw std::runtime_error("ESM4::Reader::updateModIndicies base modIndex is not zero");
}
void ESM4::Reader::saveGroupStatus()
{
#if 0
std::string padding = ""; // FIXME: debugging only
padding.insert(0, mCtx.groupStack.size()*2, ' ');
std::cout << padding << "Starting record group "
<< ESM4::printLabel(mRecordHeader.group.label, mRecordHeader.group.type) << std::endl;
#endif
if (mRecordHeader.group.groupSize == (std::uint32_t)mCtx.recHeaderSize)
{
#if 0
std::cout << padding << "Igorning record group " // FIXME: debugging only
<< ESM4::printLabel(mRecordHeader.group.label, mRecordHeader.group.type)
<< " (empty)" << std::endl;
#endif
if (!mCtx.groupStack.empty()) // top group may be empty (e.g. HAIR in Skyrim)
{
// don't put on the stack, checkGroupStatus() may not get called before recursing into this method
mCtx.groupStack.back().second -= mRecordHeader.group.groupSize;
checkGroupStatus();
}
return; // DLCMehrunesRazor - Unofficial Patch.esp is at EOF after one of these empty groups...
}
// push group
mCtx.groupStack.push_back(std::make_pair(mRecordHeader.group,
mRecordHeader.group.groupSize - (std::uint32_t)mCtx.recHeaderSize));
}
const ESM4::CellGrid& ESM4::Reader::currCellGrid() const
{
// Maybe should throw an exception instead?
assert(mCellGridValid && "Attempt to use an invalid cell grid");
return mCurrCellGrid;
}
void ESM4::Reader::checkGroupStatus()
{
// pop finished groups
while (!mCtx.groupStack.empty() && mCtx.groupStack.back().second == 0)
{
ESM4::GroupTypeHeader grp = mCtx.groupStack.back().first; // FIXME: grp is for debugging only
uint32_t groupSize = mCtx.groupStack.back().first.groupSize;
mCtx.groupStack.pop_back();
#if 0
std::string padding = ""; // FIXME: debugging only
padding.insert(0, mCtx.groupStack.size()*2, ' ');
std::cout << padding << "Finished record group " << ESM4::printLabel(grp.label, grp.type) << std::endl;
#endif
// Check if the previous group was the final one
if (mCtx.groupStack.empty())
return;
//assert (mCtx.groupStack.back().second >= groupSize && "Read more records than available");
#if 0
if (mCtx.groupStack.back().second < groupSize) // FIXME: debugging only
std::cerr << ESM4::printLabel(mCtx.groupStack.back().first.label,
mCtx.groupStack.back().first.type)
<< " read more records than available" << std::endl;
#endif
mCtx.groupStack.back().second -= groupSize;
}
}
// WARNING: this method should be used after first calling saveGroupStatus()
const ESM4::GroupTypeHeader& ESM4::Reader::grp(std::size_t pos) const
{
assert(pos <= mCtx.groupStack.size()-1 && "ESM4::Reader::grp - exceeded stack depth");
return (*(mCtx.groupStack.end()-pos-1)).first;
}
void ESM4::Reader::getRecordData()
{
std::uint32_t bufSize = 0;
if ((mRecordHeader.record.flags & ESM4::Rec_Compressed) != 0)
{
mInBuf.reset(new unsigned char[mRecordHeader.record.dataSize-(int)sizeof(bufSize)]);
mStream->read(&bufSize, sizeof(bufSize));
mStream->read(mInBuf.get(), mRecordHeader.record.dataSize-(int)sizeof(bufSize));
mDataBuf.reset(new unsigned char[bufSize]);
int ret;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = bufSize;
strm.next_in = mInBuf.get();
ret = inflateInit(&strm);
if (ret != Z_OK)
throw std::runtime_error("ESM4::Reader::getRecordData - inflateInit failed");
strm.avail_out = bufSize;
strm.next_out = mDataBuf.get();
ret = inflate(&strm, Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR && "ESM4::Reader::getRecordData - inflate - state clobbered");
switch (ret)
{
case Z_NEED_DICT:
ret = Z_DATA_ERROR; /* and fall through */
case Z_DATA_ERROR: //FONV.esm 0xB0CFF04 LAND record zlip DATA_ERROR
case Z_MEM_ERROR:
inflateEnd(&strm);
getRecordDataPostActions();
throw std::runtime_error("ESM4::Reader::getRecordData - inflate failed");
}
assert(ret == Z_OK || ret == Z_STREAM_END);
// For debugging only
#if 0
std::ostringstream ss;
for (unsigned int i = 0; i < bufSize; ++i)
{
if (mDataBuf[i] > 64 && mDataBuf[i] < 91)
ss << (char)(mDataBuf[i]) << " ";
else
ss << std::setfill('0') << std::setw(2) << std::hex << (int)(mDataBuf[i]);
if ((i & 0x000f) == 0xf)
ss << "\n";
else if (i < bufSize-1)
ss << " ";
}
std::cout << ss.str() << std::endl;
#endif
inflateEnd(&strm);
mSavedStream = mStream;
mStream = Ogre::DataStreamPtr(new Ogre::MemoryDataStream(mDataBuf.get(), bufSize, false, true));
}
getRecordDataPostActions();
//std::cout << "data size 0x" << std::hex << mRecordHeader.record.dataSize << std::endl; // FIXME: debug only
}
void ESM4::Reader::getRecordDataPostActions()
{
// keep track of data left to read from the current group
assert (!mCtx.groupStack.empty() && "Read data for a record without a group");
mCtx.groupStack.back().second -= (std::uint32_t)mCtx.recHeaderSize + mRecordHeader.record.dataSize;
// keep track of data left to read from the file
if (mObserver)
mObserver->update(mRecordHeader.record.dataSize);
}
bool ESM4::Reader::getZString(std::string& str)
{
return getZString(str, mStream);
}
// FIXME: how to without using a temp buffer?
bool ESM4::Reader::getZString(std::string& str, Ogre::DataStreamPtr filestream)
{
std::uint32_t size = 0;
if (filestream == mStream)
size = mSubRecordHeader.dataSize; // WARNING: assumed size from the header is correct
else
filestream->read(&size, sizeof(size));
boost::scoped_array<char> buf(new char[size]);
if (filestream->read(buf.get(), size) == (size_t)size)
{
if (buf[size - 1] != 0)
{
str.assign(buf.get(), size);
//std::cerr << "ESM4::Reader::getZString string is not terminated with a zero" << std::endl;
}
else
str.assign(buf.get(), size - 1);// don't copy null terminator
//assert((size_t)size-1 == str.size() && "ESM4::Reader::getZString string size mismatch");
return true;
}
else
{
str.clear();
return false; // FIXME: throw instead?
}
}
// Assumes that saveGroupStatus() is not called before this (hence we don't update mCtx.groupStack)
void ESM4::Reader::skipGroup()
{
#if 0
std::string padding = ""; // FIXME: debugging only
padding.insert(0, mCtx.groupStack.size()*2, ' ');
std::cout << padding << "Skipping record group "
<< ESM4::printLabel(mRecordHeader.group.label, mRecordHeader.group.type) << std::endl;
#endif
// Note: subtract the size of header already read before skipping
mStream->skip(mRecordHeader.group.groupSize - (std::uint32_t)mCtx.recHeaderSize);
// keep track of data left to read from the file
if (mObserver)
mObserver->update((std::size_t)mRecordHeader.group.groupSize - mCtx.recHeaderSize);
if (!mCtx.groupStack.empty())
mCtx.groupStack.back().second -= mRecordHeader.group.groupSize;
}
void ESM4::Reader::skipRecordData()
{
mStream->skip(mRecordHeader.record.dataSize);
// keep track of data left to read from the current group
assert (!mCtx.groupStack.empty() && "Skipping a record without a group");
mCtx.groupStack.back().second -= (std::uint32_t)mCtx.recHeaderSize + mRecordHeader.record.dataSize;
// keep track of data left to read from the file
if (mObserver)
mObserver->update(mRecordHeader.record.dataSize);
}
void ESM4::Reader::skipSubRecordData()
{
mStream->skip(mSubRecordHeader.dataSize);
}
void ESM4::Reader::skipSubRecordData(std::uint32_t size)
{
mStream->skip(size);
}
// ModIndex adjusted formId according to master file dependencies
// (see http://www.uesp.net/wiki/Tes4Mod:FormID_Fixup)
// NOTE: need to update modindex to mModIndicies.size() before saving
void ESM4::Reader::adjustFormId(FormId& id)
{
if (mHeader.mModIndicies.empty())
return;
unsigned int index = (id >> 24) & 0xff;
if (index < mHeader.mModIndicies.size())
id = mHeader.mModIndicies[index] | (id & 0x00ffffff);
else
id = mCtx.modIndex | (id & 0x00ffffff);
}
bool ESM4::Reader::getFormId(FormId& id)
{
if (!get(id))
return false;
adjustFormId(id);
return true;
}
void ESM4::Reader::adjustGRUPFormId()
{
adjustFormId(mRecordHeader.group.label.value);
}

@ -0,0 +1,278 @@
/*
Copyright (C) 2015-2016, 2018 cc9cii
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
cc9cii cc9c@iinet.net.au
*/
#ifndef ESM4_READER_H
#define ESM4_READER_H
#include <vector>
#include <map>
#include <cstddef>
#include <boost/scoped_array.hpp>
#include <OgreDataStream.h>
#include "common.hpp"
#include "tes4.hpp"
namespace ESM4
{
class ReaderObserver
{
public:
ReaderObserver() {}
virtual ~ReaderObserver() {}
virtual void update(std::size_t size) = 0;
};
typedef std::vector<std::pair<ESM4::GroupTypeHeader, std::uint32_t> > GroupStack;
struct ReaderContext
{
std::string filename; // from openTes4File()
std::uint32_t modIndex; // the sequential position of this file in the load order:
// 0x00 reserved, 0xFF in-game (see notes below)
GroupStack groupStack; // keep track of bytes left to find when a group is done
FormId currWorld; // formId of current world - for grouping CELL records
FormId currCell; // formId of current cell
std::size_t recHeaderSize; // normally should be already set correctly, but just in
// case the file was re-opened. default = TES5 size,
// can be reduced for TES4 by setRecHeaderSize()
std::size_t filePos; // assume that the record header will be re-read once
// the context is restored.
};
class Reader
{
ReaderObserver *mObserver; // observer for tracking bytes read
Header mHeader; // ESM header // FIXME
RecordHeader mRecordHeader; // header of the current record or group being processed
SubRecordHeader mSubRecordHeader; // header of the current sub record being processed
std::size_t mRecordRemaining; // number of bytes to be read by sub records following current
// FIXME: try to get rid of these two members, seem like massive hacks
CellGrid mCurrCellGrid; // TODO: should keep a map of cell formids
bool mCellGridValid;
ReaderContext mCtx;
// Use scoped arrays to avoid memory leak due to exceptions, etc.
// TODO: try fixed size buffers on the stack for both buffers (may be faster)
boost::scoped_array<unsigned char> mInBuf;
boost::scoped_array<unsigned char> mDataBuf;
Ogre::DataStreamPtr mStream;
Ogre::DataStreamPtr mSavedStream; // mStream is saved here while using deflated memory stream
Ogre::DataStreamPtr mStrings;
Ogre::DataStreamPtr mILStrings;
Ogre::DataStreamPtr mDLStrings;
enum LocalizedStringType
{
Type_Strings = 0,
Type_ILStrings = 1,
Type_DLStrings = 2
};
struct LStringOffset
{
LocalizedStringType type;
std::uint32_t offset;
};
std::map<FormId, LStringOffset> mLStringIndex;
void getRecordDataPostActions(); // housekeeping actions before processing the next record
void buildLStringIndex(const std::string& stringFile, LocalizedStringType stringType);
public:
Reader();
~Reader();
// Methods added for updating loading progress bars
inline std::size_t getFileSize() const { return mStream->size(); }
inline std::size_t getFileOffset() const { return mStream->tell(); }
// Methods added for saving/restoring context
ReaderContext getContext();
bool restoreContext(const ReaderContext& ctx); // returns the result of re-reading the header
bool skipNextGroupCellChild(); // returns true if skipped
std::size_t openTes4File(const std::string& name);
// NOTE: must be called before calling getRecordHeader()
void setRecHeaderSize(const std::size_t size);
inline void loadHeader() { mHeader.load(*this); }
inline unsigned int esmVersion() const { return mHeader.mData.version.ui; }
inline unsigned int numRecords() const { return mHeader.mData.records; }
void buildLStringIndex();
inline bool hasLocalizedStrings() const { return (mHeader.mFlags & Rec_Localized) != 0; }
void getLocalizedString(std::string& str); // convenience method for below
void getLocalizedString(const FormId stringId, std::string& str);
// Read 24 bytes of header. The caller can then decide whether to process or skip the data.
bool getRecordHeader();
inline const RecordHeader& hdr() const { return mRecordHeader; }
const GroupTypeHeader& grp(std::size_t pos = 0) const;
// The object setting up this reader needs to supply the file's load order index
// so that the formId's in this file can be adjusted with the file (i.e. mod) index.
void setModIndex(int index) { mCtx.modIndex = (index << 24) & 0xff000000; }
void updateModIndicies(const std::vector<std::string>& files);
// Maybe should throw an exception if called when not valid?
const CellGrid& currCellGrid() const;
inline const bool hasCellGrid() const { return mCellGridValid; }
// This is set while loading a CELL record (XCLC sub record) and invalidated
// each time loading a CELL (see clearCellGrid())
inline void setCurrCellGrid(const CellGrid& currCell) {
mCellGridValid = true;
mCurrCellGrid = currCell;
}
// FIXME: This is called each time a new CELL record is read. Rather than calling this
// methos explicitly, mCellGridValid should be set automatically somehow.
//
// Cell 2c143 is loaded immedicatly after 1bdb1 and can mistakely appear to have grid 0, 1.
inline void clearCellGrid() { mCellGridValid = false; }
// Should be set at the beginning of a CELL load
inline void setCurrCell(FormId formId) { mCtx.currCell = formId; }
inline FormId currCell() const { return mCtx.currCell; }
// Should be set at the beginning of a WRLD load
inline void setCurrWorld(FormId formId) { mCtx.currWorld = formId; }
inline FormId currWorld() const { return mCtx.currWorld; }
// Get the data part of a record
// Note: assumes the header was read correctly and nothing else was read
void getRecordData();
// Skip the data part of a record
// Note: assumes the header was read correctly and nothing else was read
void skipRecordData();
// Skip the rest of the group
// Note: assumes the header was read correctly and nothing else was read
void skipGroup();
// Read 6 bytes of header. The caller can then decide whether to process or skip the data.
bool getSubRecordHeader();
// Manally update (i.e. reduce) the bytes remaining to be read after SUB_XXXX
inline void updateRecordRemaining(std::uint32_t subSize) { mRecordRemaining -= subSize; }
inline const SubRecordHeader& subRecordHeader() const { return mSubRecordHeader; }
// Skip the data part of a subrecord
// Note: assumes the header was read correctly and nothing else was read
void skipSubRecordData();
// Special for a subrecord following a XXXX subrecord
void skipSubRecordData(std::uint32_t size);
// Get a subrecord of a particular type and data type
template<typename T>
bool getSubRecord(const ESM4::SubRecordTypes type, T& t)
{
ESM4::SubRecordHeader hdr;
if (!get(hdr) || (hdr.typeId != type) || (hdr.dataSize != sizeof(T)))
return false;
return get(t);
}
template<typename T>
inline bool get(T& t) {
return mStream->read(&t, sizeof(T)) == sizeof(T); // FIXME: try/catch block needed?
}
// for arrays
inline bool get(void* p, std::size_t size) {
return mStream->read(p, size) == size; // FIXME: try/catch block needed?
}
// ModIndex adjusted formId according to master file dependencies
void adjustFormId(FormId& id);
bool getFormId(FormId& id);
void adjustGRUPFormId();
// Note: does not convert to UTF8
// Note: assumes string size from the subrecord header
bool getZString(std::string& str);
bool getZString(std::string& str, Ogre::DataStreamPtr fileStream);
void checkGroupStatus();
void saveGroupStatus();
void registerForUpdates(ReaderObserver *observer);
// for debugging only
size_t stackSize() const { return mCtx.groupStack.size(); }
};
// An idea on extending the 254 mods limit
// ---------------------------------------
//
// By using a 64bit formid internally it should be possible to extend the limit. However
// saved games won't be compatible.
//
// One or two digits can be used, which will allow 4096-2=4094 or 65535-2=65533 mods.
// With the remaining digits one can be used as a game index (e.g. TES3=0, TES4=1, etc).
//
// The remaining bits might still be useful for indicating something else about the object.
//
// game index
// |
// | mod index extend to 4 digits (or 3 digits?)
// | +---+
// | | |
// v v v
// 0xfffff f ff ff ffffff
// ^^ ^ ^
// || | |
// || +----+
// || 6 digit obj index
// ++
// 2 digit mod index
//
}
#endif // ESM4_READER_H

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save