diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ec616cbb..ac5606a99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md index 32fe426de..e8f6d7ff7 100644 --- a/README.md +++ b/README.md @@ -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 ------------------ diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ec686c4e2..6543f40d2 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -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} diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index d723ddaac..1f9ebfe73 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -8,10 +8,13 @@ #include #include +#include #include #include #include +#include + #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(mReader)->reader().setModIndex(mReaderIndex-1); + static_cast(mReader)->openTes4File(path.string()); + static_cast(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(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(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(mReader)->reader(); + + switch (hdr.record.typeId) + { + + // FIXME: removed for now + + default: + reader.skipRecordData(); + } + + return false; +} diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 47046d9de..998e2866a 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -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); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index cdb1691f0..e7655b237 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -117,6 +117,7 @@ target_link_libraries(openmw ${OGRE_LIBRARIES} ${OGRE_STATIC_PLUGINS} ${SHINY_LIBRARIES} + ${ESM4_LIBRARIES} ${BSAOPTHASH_LIBRARIES} ${ZLIB_LIBRARY} ${OPENAL_LIBRARY} diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index c110e94d6..43b3913b2 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -134,7 +134,7 @@ namespace MWBase virtual const MWWorld::ESMStore& getStore() const = 0; - virtual std::vector& getEsmReader() = 0; + virtual std::vector& getEsmReader() = 0; virtual MWWorld::LocalScripts& getLocalScripts() = 0; diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 2aa817fa5..e8a785b36 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -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& reader) +MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector >& reader) : mStore (store), mReader (reader), mIdCache (40, std::pair ("", (CellStore*)0)), /// \todo make cache size configurable mIdCacheIndex (0) diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0b7d9444f..7f5adc487 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -28,7 +28,7 @@ namespace MWWorld class Cells { const MWWorld::ESMStore& mStore; - std::vector& mReader; + std::vector >& mReader; mutable std::map mInteriors; mutable std::map, CellStore> mExteriors; std::vector > mIdCache; @@ -47,7 +47,7 @@ namespace MWWorld void clear(); - Cells (const MWWorld::ESMStore& store, std::vector& reader); + Cells (const MWWorld::ESMStore& store, std::vector >& reader); CellStore *getExterior (int x, int y); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 0e392f055..c087957b2 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -400,7 +400,7 @@ namespace MWWorld + mNpcs.mList.size(); } - void CellStore::load (const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::load (const MWWorld::ESMStore &store, std::vector > &esm) { if (mState!=State_Loaded) { @@ -417,7 +417,7 @@ namespace MWWorld } } - void CellStore::preload (const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::preload (const MWWorld::ESMStore &store, std::vector > &esm) { if (mState==State_Unloaded) { @@ -427,7 +427,7 @@ namespace MWWorld } } - void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector &esm) + void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector > &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) + void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector > &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 = diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 672b6046b..689f07d4f 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -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); + void load (const MWWorld::ESMStore &store, std::vector > &esm); ///< Load references from content file. - void preload (const MWWorld::ESMStore &store, std::vector &esm); + void preload (const MWWorld::ESMStore &store, std::vector > &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); + void listRefs(const MWWorld::ESMStore &store, std::vector > &esm); - void loadRefs(const MWWorld::ESMStore &store, std::vector &esm); + void loadRefs(const MWWorld::ESMStore &store, std::vector > &esm); void loadRef (ESM::CellRef& ref, bool deleted, const ESMStore& store); ///< Make case-adjustments to \a ref and insert it into the respective container. diff --git a/apps/openmw/mwworld/contentloader.hpp b/apps/openmw/mwworld/contentloader.hpp index 46bd7d3f9..e589d6d05 100644 --- a/apps/openmw/mwworld/contentloader.hpp +++ b/apps/openmw/mwworld/contentloader.hpp @@ -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 >& contentFiles) { std::cout << "Loading content file " << filepath.string() << std::endl; mListener.setLabel(filepath.string()); diff --git a/apps/openmw/mwworld/esmloader.cpp b/apps/openmw/mwworld/esmloader.cpp index b12d646e7..ca1083c2f 100644 --- a/apps/openmw/mwworld/esmloader.cpp +++ b/apps/openmw/mwworld/esmloader.cpp @@ -2,11 +2,12 @@ #include "esmstore.hpp" #include +#include namespace MWWorld { -EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector& readers, +EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector >& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener) : ContentLoader(listener) , mEsm(readers) @@ -15,17 +16,60 @@ EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector& 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 >& contentFiles) { - ContentLoader::load(filepath.filename(), index); + int tesVerIndex = 0; // FIXME: hard coded, 0 = MW, 1 = TES4, 2 = TES5 (TODO: Fallout) + int index = 0; - ESM::ESMReader lEsm; - lEsm.setEncoder(mEncoder); - lEsm.setIndex(index); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open(filepath.string()); - mEsm[index] = lEsm; - mStore.load(mEsm[index], &mListener); + 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 */ diff --git a/apps/openmw/mwworld/esmloader.hpp b/apps/openmw/mwworld/esmloader.hpp index b96af707c..32c2e977a 100644 --- a/apps/openmw/mwworld/esmloader.hpp +++ b/apps/openmw/mwworld/esmloader.hpp @@ -22,15 +22,15 @@ class ESMStore; struct EsmLoader : public ContentLoader { - EsmLoader(MWWorld::ESMStore& store, std::vector& readers, - ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener); + EsmLoader(MWWorld::ESMStore& store, std::vector >& 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 >& contentFiles); private: - std::vector& mEsm; - MWWorld::ESMStore& mStore; - ToUTF8::Utf8Encoder* mEncoder; + std::vector >& mEsm; // Note: the ownership of the readers is with the caller + MWWorld::ESMStore& mStore; + ToUTF8::Utf8Encoder* mEncoder; }; } /* namespace MWWorld */ diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 91233cf70..74230bbf6 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include 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()); + 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; - /// \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 &masters = esm.getGameFiles(); - std::vector *allPlugins = esm.getGlobalReaderList(); - for (size_t j = 0; j < masters.size(); j++) { - ESM::Header::MasterData &mast = const_cast(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; + // 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 &masters = esm.getGameFiles(); + std::vector *allPlugins = esm.getGlobalReaderList(); + for (size_t j = 0; j < masters.size(); j++) { + ESM::Header::MasterData &mast = const_cast(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)->reader(); + reader.checkGroupStatus(); + + loadTes4Group(esm); + listener->setProgress(static_cast(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)->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)->reader(); + const ESM4::RecordHeader& hdr = reader.hdr(); + + switch (hdr.record.typeId) + { + + // FIXME: removed for now + + default: + reader.skipRecordData(); + } + + return; +} + void ESMStore::setUp() { std::map::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"); diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 05b633956..a09929840 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -6,6 +6,12 @@ #include #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 typedef std::map::const_iterator iterator; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 71cf4a143..c15fff497 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -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 >& 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& World::getEsmReader() + std::vector& 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& content, ContentLoader& contentLoader) { + std::vector > contentFiles; + contentFiles.resize(3); + std::vector::const_iterator it(content.begin()); std::vector::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); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index bf25c20bb..0324ce873 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -66,7 +66,7 @@ namespace MWWorld MWWorld::Scene *mWorldScene; MWWorld::Player *mPlayer; - std::vector mEsm; + std::vector > mEsm; MWWorld::ESMStore mStore; LocalScripts mLocalScripts; MWWorld::Globals mGlobalVariables; @@ -199,7 +199,7 @@ namespace MWWorld virtual const MWWorld::ESMStore& getStore() const; - virtual std::vector& getEsmReader(); + virtual std::vector& getEsmReader(); virtual LocalScripts& getLocalScripts(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 9b6f9fef9..1303b31da 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -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) diff --git a/components/esm/esm4reader.cpp b/components/esm/esm4reader.cpp new file mode 100644 index 000000000..4a2f5c2f8 --- /dev/null +++ b/components/esm/esm4reader.cpp @@ -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"); + +} diff --git a/components/esm/esm4reader.hpp b/components/esm/esm4reader.hpp new file mode 100644 index 000000000..c17647386 --- /dev/null +++ b/components/esm/esm4reader.hpp @@ -0,0 +1,38 @@ +#ifndef COMPONENT_ESM_4READER_H +#define COMPONENT_ESM_4READER_H + +#include +#include + +#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 diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index d90a3444d..0c0419f3a 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -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 diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index b4b44f876..1ae7bf259 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -71,12 +71,103 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) { openRaw(_esm, name); - if (getRecName() != "TES3") + NAME modVer = getRecName(); + if (modVer == "TES3") + { + getRecHeader(); + + 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"); - - getRecHeader(); - - mHeader.load (*this); } void ESMReader::open(const std::string &file) diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 3a2f32640..d2d92b23b 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -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 *list) {mGlobalReaderList = list;} - std::vector *getGlobalReaderList() {return mGlobalReaderList;} + void setGlobalReaderList(std::vector *list) {mGlobalReaderList = list;} + std::vector *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 mBuffer; - Header mHeader; - - std::vector *mGlobalReaderList; + std::vector *mGlobalReaderList; ToUTF8::Utf8Encoder* mEncoder; + +protected: + ESM_Context mCtx; + + Header mHeader; }; } #endif diff --git a/extern/esm4/CMakeLists.txt b/extern/esm4/CMakeLists.txt new file mode 100644 index 000000000..9ce5a9660 --- /dev/null +++ b/extern/esm4/CMakeLists.txt @@ -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) diff --git a/extern/esm4/achr.cpp b/extern/esm4/achr.cpp new file mode 100644 index 000000000..3a3a35ebf --- /dev/null +++ b/extern/esm4/achr.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/achr.hpp b/extern/esm4/achr.hpp new file mode 100644 index 000000000..86e2640a9 --- /dev/null +++ b/extern/esm4/achr.hpp @@ -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 diff --git a/extern/esm4/acre.cpp b/extern/esm4/acre.cpp new file mode 100644 index 000000000..e83f6dda9 --- /dev/null +++ b/extern/esm4/acre.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/acre.hpp b/extern/esm4/acre.hpp new file mode 100644 index 000000000..2b43ab7a1 --- /dev/null +++ b/extern/esm4/acre.hpp @@ -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 diff --git a/extern/esm4/acti.cpp b/extern/esm4/acti.cpp new file mode 100644 index 000000000..8016d6e47 --- /dev/null +++ b/extern/esm4/acti.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/acti.hpp b/extern/esm4/acti.hpp new file mode 100644 index 000000000..00b87408c --- /dev/null +++ b/extern/esm4/acti.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/alch.cpp b/extern/esm4/alch.cpp new file mode 100644 index 000000000..0aaffa340 --- /dev/null +++ b/extern/esm4/alch.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/alch.hpp b/extern/esm4/alch.hpp new file mode 100644 index 000000000..f0493e4ee --- /dev/null +++ b/extern/esm4/alch.hpp @@ -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 diff --git a/extern/esm4/ammo.cpp b/extern/esm4/ammo.cpp new file mode 100644 index 000000000..05cec0c2a --- /dev/null +++ b/extern/esm4/ammo.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/ammo.hpp b/extern/esm4/ammo.hpp new file mode 100644 index 000000000..6d9171bee --- /dev/null +++ b/extern/esm4/ammo.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/anio.cpp b/extern/esm4/anio.cpp new file mode 100644 index 000000000..bab68c069 --- /dev/null +++ b/extern/esm4/anio.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/anio.hpp b/extern/esm4/anio.hpp new file mode 100644 index 000000000..9ee349a1c --- /dev/null +++ b/extern/esm4/anio.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/appa.cpp b/extern/esm4/appa.cpp new file mode 100644 index 000000000..9041e2ecd --- /dev/null +++ b/extern/esm4/appa.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/appa.hpp b/extern/esm4/appa.hpp new file mode 100644 index 000000000..88bc4b953 --- /dev/null +++ b/extern/esm4/appa.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/armo.cpp b/extern/esm4/armo.cpp new file mode 100644 index 000000000..1557f7c64 --- /dev/null +++ b/extern/esm4/armo.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/armo.hpp b/extern/esm4/armo.hpp new file mode 100644 index 000000000..a697baf6c --- /dev/null +++ b/extern/esm4/armo.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/book.cpp b/extern/esm4/book.cpp new file mode 100644 index 000000000..9bc9dad56 --- /dev/null +++ b/extern/esm4/book.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/book.hpp b/extern/esm4/book.hpp new file mode 100644 index 000000000..897a93257 --- /dev/null +++ b/extern/esm4/book.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/cell.cpp b/extern/esm4/cell.cpp new file mode 100644 index 000000000..8ecbe1b70 --- /dev/null +++ b/extern/esm4/cell.cpp @@ -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 +#include + +#include // 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::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() +{ +} diff --git a/extern/esm4/cell.hpp b/extern/esm4/cell.hpp new file mode 100644 index 000000000..3b1ab22fb --- /dev/null +++ b/extern/esm4/cell.hpp @@ -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 +#include +#include + +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 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 diff --git a/extern/esm4/clas.cpp b/extern/esm4/clas.cpp new file mode 100644 index 000000000..836f11407 --- /dev/null +++ b/extern/esm4/clas.cpp @@ -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 +#include + +#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() +//{ +//} diff --git a/extern/esm4/clas.hpp b/extern/esm4/clas.hpp new file mode 100644 index 000000000..0aa4bc500 --- /dev/null +++ b/extern/esm4/clas.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/clot.cpp b/extern/esm4/clot.cpp new file mode 100644 index 000000000..8f9d3a5ac --- /dev/null +++ b/extern/esm4/clot.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/clot.hpp b/extern/esm4/clot.hpp new file mode 100644 index 000000000..c13b8a3c7 --- /dev/null +++ b/extern/esm4/clot.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/common.cpp b/extern/esm4/common.cpp new file mode 100644 index 000000000..ee9d0edb8 --- /dev/null +++ b/extern/esm4/common.cpp @@ -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 +#include +#include + +#include + +#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"); + } +} diff --git a/extern/esm4/common.hpp b/extern/esm4/common.hpp new file mode 100644 index 000000000..535ae981e --- /dev/null +++ b/extern/esm4/common.hpp @@ -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 +#include + +// 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 diff --git a/extern/esm4/cont.cpp b/extern/esm4/cont.cpp new file mode 100644 index 000000000..ea86837e1 --- /dev/null +++ b/extern/esm4/cont.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/cont.hpp b/extern/esm4/cont.hpp new file mode 100644 index 000000000..9639bc9fb --- /dev/null +++ b/extern/esm4/cont.hpp @@ -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 + +#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 mInventory; + + Container(); + virtual ~Container(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_CONT_H diff --git a/extern/esm4/crea.cpp b/extern/esm4/crea.cpp new file mode 100644 index 000000000..ec27e769d --- /dev/null +++ b/extern/esm4/crea.cpp @@ -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 +#include +#include +#include +#include // 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() +//{ +//} diff --git a/extern/esm4/crea.hpp b/extern/esm4/crea.hpp new file mode 100644 index 000000000..8d0883350 --- /dev/null +++ b/extern/esm4/crea.hpp @@ -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 + +#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 mSpell; + FormId mScript; + + AIData mAIData; + std::vector 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 mNif; // NIF filenames, get directory from mModel + std::vector mKf; + + std::vector mInventory; + + Creature(); + virtual ~Creature(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_CREA_H diff --git a/extern/esm4/door.cpp b/extern/esm4/door.cpp new file mode 100644 index 000000000..744320984 --- /dev/null +++ b/extern/esm4/door.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/door.hpp b/extern/esm4/door.hpp new file mode 100644 index 000000000..080d7473e --- /dev/null +++ b/extern/esm4/door.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/eyes.cpp b/extern/esm4/eyes.cpp new file mode 100644 index 000000000..f8d896758 --- /dev/null +++ b/extern/esm4/eyes.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/eyes.hpp b/extern/esm4/eyes.hpp new file mode 100644 index 000000000..ac7f76d06 --- /dev/null +++ b/extern/esm4/eyes.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/flor.cpp b/extern/esm4/flor.cpp new file mode 100644 index 000000000..d9d1d9220 --- /dev/null +++ b/extern/esm4/flor.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/flor.hpp b/extern/esm4/flor.hpp new file mode 100644 index 000000000..849d0c61f --- /dev/null +++ b/extern/esm4/flor.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/formid.cpp b/extern/esm4/formid.cpp new file mode 100644 index 000000000..9195b3767 --- /dev/null +++ b/extern/esm4/formid.cpp @@ -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 +#include +#include +#include // strtol + +#include + +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(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(std::stoul(str, nullptr, 16)); + } +} diff --git a/extern/esm4/formid.hpp b/extern/esm4/formid.hpp new file mode 100644 index 000000000..6bb2c475e --- /dev/null +++ b/extern/esm4/formid.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/furn.cpp b/extern/esm4/furn.cpp new file mode 100644 index 000000000..157477fa6 --- /dev/null +++ b/extern/esm4/furn.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/furn.hpp b/extern/esm4/furn.hpp new file mode 100644 index 000000000..e0ac7e6cf --- /dev/null +++ b/extern/esm4/furn.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/gras.cpp b/extern/esm4/gras.cpp new file mode 100644 index 000000000..d095a5ee7 --- /dev/null +++ b/extern/esm4/gras.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/gras.hpp b/extern/esm4/gras.hpp new file mode 100644 index 000000000..08e4a5800 --- /dev/null +++ b/extern/esm4/gras.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/grup.hpp b/extern/esm4/grup.hpp new file mode 100644 index 000000000..345fb11c8 --- /dev/null +++ b/extern/esm4/grup.hpp @@ -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 + +#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 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 mHeaders; // FIXME: is this needed? + + // FIXME: should these be pairs? i.e. 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 + // 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 mPersistent; // REFR, ACHR, ACRE + std::vector mdelPersistent; + + // FIXME: also need file index + file context of all those that has type 10 GRUP + GroupTypeHeader mHdrVisDist; + std::vector mVisibleDist; // REFR, ACHR, ACRE + std::vector 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 mTemporary; // REFR, ACHR, ACRE + std::vector 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 diff --git a/extern/esm4/hair.cpp b/extern/esm4/hair.cpp new file mode 100644 index 000000000..73cfa6fbe --- /dev/null +++ b/extern/esm4/hair.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/hair.hpp b/extern/esm4/hair.hpp new file mode 100644 index 000000000..f8086cbce --- /dev/null +++ b/extern/esm4/hair.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/idle.cpp b/extern/esm4/idle.cpp new file mode 100644 index 000000000..ed02d8536 --- /dev/null +++ b/extern/esm4/idle.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/idle.hpp b/extern/esm4/idle.hpp new file mode 100644 index 000000000..7965332d7 --- /dev/null +++ b/extern/esm4/idle.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/ingr.cpp b/extern/esm4/ingr.cpp new file mode 100644 index 000000000..0bd54689f --- /dev/null +++ b/extern/esm4/ingr.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/ingr.hpp b/extern/esm4/ingr.hpp new file mode 100644 index 000000000..6aa86ca81 --- /dev/null +++ b/extern/esm4/ingr.hpp @@ -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 + +#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 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 diff --git a/extern/esm4/keym.cpp b/extern/esm4/keym.cpp new file mode 100644 index 000000000..450742389 --- /dev/null +++ b/extern/esm4/keym.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/keym.hpp b/extern/esm4/keym.hpp new file mode 100644 index 000000000..18a458a79 --- /dev/null +++ b/extern/esm4/keym.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/land.cpp b/extern/esm4/land.cpp new file mode 100644 index 000000000..8367d08d5 --- /dev/null +++ b/extern/esm4/land.cpp @@ -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 +#include + +#include // 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::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::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() +//{ +//} diff --git a/extern/esm4/land.hpp b/extern/esm4/land.hpp new file mode 100644 index 000000000..c306eb87b --- /dev/null +++ b/extern/esm4/land.hpp @@ -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 +#include +#include + +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 data; // FIXME: is this UV map? + }; + + struct Texture + { + BTXT base; + std::vector 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 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 diff --git a/extern/esm4/ligh.cpp b/extern/esm4/ligh.cpp new file mode 100644 index 000000000..a6c7882ce --- /dev/null +++ b/extern/esm4/ligh.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/ligh.hpp b/extern/esm4/ligh.hpp new file mode 100644 index 000000000..042c07eb1 --- /dev/null +++ b/extern/esm4/ligh.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/ltex.cpp b/extern/esm4/ltex.cpp new file mode 100644 index 000000000..2e5e9cac0 --- /dev/null +++ b/extern/esm4/ltex.cpp @@ -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 +#include + +#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() +//{ +//} diff --git a/extern/esm4/ltex.hpp b/extern/esm4/ltex.hpp new file mode 100644 index 000000000..d830ddb41 --- /dev/null +++ b/extern/esm4/ltex.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/lvlc.cpp b/extern/esm4/lvlc.cpp new file mode 100644 index 000000000..dc2e71f88 --- /dev/null +++ b/extern/esm4/lvlc.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/lvlc.hpp b/extern/esm4/lvlc.hpp new file mode 100644 index 000000000..4fa1bc0e6 --- /dev/null +++ b/extern/esm4/lvlc.hpp @@ -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 + +#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 mLvlObject; + + LeveledCreature(); + virtual ~LeveledCreature(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_LVLC_H diff --git a/extern/esm4/lvli.cpp b/extern/esm4/lvli.cpp new file mode 100644 index 000000000..6585d3466 --- /dev/null +++ b/extern/esm4/lvli.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/lvli.hpp b/extern/esm4/lvli.hpp new file mode 100644 index 000000000..ca87f160d --- /dev/null +++ b/extern/esm4/lvli.hpp @@ -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 + +#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 mLvlObject; + + LeveledItem(); + virtual ~LeveledItem(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_LVLI_H diff --git a/extern/esm4/mato.cpp b/extern/esm4/mato.cpp new file mode 100644 index 000000000..9ad5cf7d8 --- /dev/null +++ b/extern/esm4/mato.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/mato.hpp b/extern/esm4/mato.hpp new file mode 100644 index 000000000..ac2c4c6aa --- /dev/null +++ b/extern/esm4/mato.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/misc.cpp b/extern/esm4/misc.cpp new file mode 100644 index 000000000..ebc90d542 --- /dev/null +++ b/extern/esm4/misc.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/misc.hpp b/extern/esm4/misc.hpp new file mode 100644 index 000000000..9936b2c7a --- /dev/null +++ b/extern/esm4/misc.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/navi.cpp b/extern/esm4/navi.cpp new file mode 100644 index 000000000..1e02efd19 --- /dev/null +++ b/extern/esm4/navi.cpp @@ -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 +#include + +#include // 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::iterator it = triangles.begin(); it != triangles.end(); ++it) + { + reader.get(*it); + } + } + + reader.get(count); // countVertex; + if (count) + { + verticies.resize(count); + for (std::vector::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::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::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::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 preferredPaths; + reader.get(count); + if (count == 1) + { + reader.get(node); + reader.get(count); + } + if (count) + { + preferredPaths.resize(count); + for (std::vector::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 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::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() +//{ +//} diff --git a/extern/esm4/navi.hpp b/extern/esm4/navi.hpp new file mode 100644 index 000000000..a53f1b5fe --- /dev/null +++ b/extern/esm4/navi.hpp @@ -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 +#include + +#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 triangles; + std::vector 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 formIdMerged; + std::vector formIdPrefMerged; + std::vector linkedDoors; + std::vector islandInfo; + std::uint32_t locationMarker; + FormId worldSpaceId; + CellGrid cellGrid; + + void load(ESM4::Reader& reader); + }; + + std::string mEditorId; + + std::vector mNavMeshInfo; + + std::vector > > mPreferredPaths; + + std::map mPathIndexMap; + + Navigation(); + virtual ~Navigation(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_NAVI_H diff --git a/extern/esm4/navm.cpp b/extern/esm4/navm.cpp new file mode 100644 index 000000000..cbd7463fb --- /dev/null +++ b/extern/esm4/navm.cpp @@ -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 + +#include // 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) + // + 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::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::iterator it = triangles.begin(); it != triangles.end(); ++it) + { + reader.get(*it); + } + } + + reader.get(count); // numExtConn; + if (count) + { + extConns.resize(count); + for (std::vector::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::iterator it = doorTriangles.begin(); it != doorTriangles.end(); ++it) + { + reader.get(*it); + } + } + + reader.get(count); // numCoverTriangles; + if (count) + { + coverTriangles.resize(count); + for (std::vector::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 indicies; + indicies.resize(count); + for (std::vector::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() +{ +} diff --git a/extern/esm4/navm.hpp b/extern/esm4/navm.hpp new file mode 100644 index 000000000..3bff408f8 --- /dev/null +++ b/extern/esm4/navm.hpp @@ -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 + +#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 verticies; + std::vector triangles; + std::vector extConns; + std::vector doorTriangles; + std::vector 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 > triSegments; + + void load(ESM4::Reader& esm); + }; + + std::vector 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 diff --git a/extern/esm4/npc_.cpp b/extern/esm4/npc_.cpp new file mode 100644 index 000000000..1d8227f9b --- /dev/null +++ b/extern/esm4/npc_.cpp @@ -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 + +#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() +//{ +//} diff --git a/extern/esm4/npc_.hpp b/extern/esm4/npc_.hpp new file mode 100644 index 000000000..27bc0e8c5 --- /dev/null +++ b/extern/esm4/npc_.hpp @@ -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 + +#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 mSpell; + FormId mScript; + + AIData mAIData; + std::vector mAIPackages; + ActorBaseConfig mBaseConfig; + ActorFaction mFaction; + Data mData; + FormId mCombatStyle; + FormId mSoundBase; + FormId mSound; + std::uint8_t mSoundChance; + float mFootWeight; + + float mBoundRadius; + std::vector mKf; // filenames only, get directory path from mModel + + std::vector mInventory; + + Npc(); + virtual ~Npc(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_NPC__H diff --git a/extern/esm4/race.cpp b/extern/esm4/race.cpp new file mode 100644 index 000000000..1940dcfc2 --- /dev/null +++ b/extern/esm4/race.cpp @@ -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 +#include // FIXME: debugging only +#include + +#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() +//{ +//} diff --git a/extern/esm4/race.hpp b/extern/esm4/race.hpp new file mode 100644 index 000000000..b7861c22a --- /dev/null +++ b/extern/esm4/race.hpp @@ -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 +#include + +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 diff --git a/extern/esm4/reader.cpp b/extern/esm4/reader.cpp new file mode 100644 index 000000000..c8d376f49 --- /dev/null +++ b/extern/esm4/reader.cpp @@ -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 +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#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::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 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& 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 fileIndex; + + for (size_t i = 0; i < files.size(); ++i) // ATTENTION: assumes current file is not included + fileIndex[boost::to_lower_copy(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::const_iterator it + = fileIndex.find(boost::to_lower_copy(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 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); +} diff --git a/extern/esm4/reader.hpp b/extern/esm4/reader.hpp new file mode 100644 index 000000000..ce52b70c7 --- /dev/null +++ b/extern/esm4/reader.hpp @@ -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 +#include +#include + +#include + +#include + +#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 > 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 mInBuf; + boost::scoped_array 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 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& 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 + 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 + 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 diff --git a/extern/esm4/refr.cpp b/extern/esm4/refr.cpp new file mode 100644 index 000000000..42660fbb6 --- /dev/null +++ b/extern/esm4/refr.cpp @@ -0,0 +1,269 @@ +/* + 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 "refr.hpp" + +#include + +#include // FIXME: debug only +#include "formid.hpp" // FIXME: debug only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Reference::Reference() : mFormId(0), mFlags(0), mDisabled(false), mBaseObj(0), mScale(1.f), + mOwner(0), mGlobal(0), mFactionRank(0), mCount(1) +{ + mEditorId.clear(); + mFullName.clear(); + + mEsp.parent = 0; + mEsp.flags = 0; + + mDoor.destDoor = 0; +} + +ESM4::Reference::~Reference() +{ +} + +void ESM4::Reference::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + // TODO: Let the engine apply this? Saved games? + //mDisabled = ((mFlags & ESM4::Rec_Disabled) != 0) ? true : false; + 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 ("REFR FULL data read error"); +#if 0 + std::string padding = ""; + padding.insert(0, reader.stackSize()*2, ' '); + std::cout << padding << "REFR Full Name: " << mFullName << std::endl; +#endif + break; + } + case ESM4::SUB_NAME: + { + reader.getFormId(mBaseObj); +#if 0 + if (mFlags & ESM4::Rec_Disabled) + std::cout << "REFR disable at start " << formIdToString(mFormId) << + " baseobj " << formIdToString(mBaseObj) << + " " << (mEditorId.empty() ? "" : mEditorId) << std::endl; // FIXME +#endif + break; + } + case ESM4::SUB_DATA: reader.get(mPosition); break; + case ESM4::SUB_XSCL: reader.get(mScale); break; + case ESM4::SUB_XOWN: reader.getFormId(mOwner); break; + case ESM4::SUB_XGLB: reader.getFormId(mGlobal); break; + case ESM4::SUB_XRNK: reader.get(mFactionRank); break; + case ESM4::SUB_XESP: + { + reader.get(mEsp); + reader.adjustFormId(mEsp.parent); + //std::cout << "REFR parent: " << formIdToString(mEsp.parent) << " ref " << formIdToString(mFormId) + //<< ", 0x" << std::hex << (mEsp.flags & 0xff) << std::endl;// FIXME + break; + } + case ESM4::SUB_XTEL: + { + reader.get(mDoor.destDoor); + reader.get(mDoor.destPos); + if (esmVer == ESM4::VER_094 || esmVer == ESM4::VER_170 || isFONV) + reader.get(mDoor.flags); // not in Obvlivion + //std::cout << "REFR dest door: " << formIdToString(mDoor.destDoor) << std::endl;// FIXME + break; + } + case ESM4::SUB_XSED: + { + // 1 or 4 bytes + if (subHdr.dataSize == 1) + { + uint8_t data = reader.get(data); + //std::cout << "REFR XSED " << std::hex << (int)data << std::endl; + break; + } + else if (subHdr.dataSize == 4) + { + uint32_t data = reader.get(data); + //std::cout << "REFR XSED " << std::hex << (int)data << std::endl; + break; + } + + //std::cout << "REFR XSED dataSize: " << subHdr.dataSize << std::endl;// FIXME + reader.skipSubRecordData(); + break; + } + case ESM4::SUB_XLOD: + { + // 12 bytes + if (subHdr.dataSize == 12) + { + uint32_t data = reader.get(data); + uint32_t data2 = reader.get(data); + uint32_t data3 = reader.get(data); + //std::cout << "REFR XLOD " << std::hex << (int)data << " " << (int)data2 << " " << (int)data3 << std::endl; + break; + } + //std::cout << "REFR XLOD dataSize: " << subHdr.dataSize << std::endl;// FIXME + reader.skipSubRecordData(); + break; + } + case ESM4::SUB_XACT: + { + if (subHdr.dataSize == 4) + { + uint32_t data = reader.get(data); + //std::cout << "REFR XACT " << std::hex << (int)data << std::endl; + break; + } + + //std::cout << "REFR XACT dataSize: " << subHdr.dataSize << std::endl;// FIXME + reader.skipSubRecordData(); + break; + } + // seems like another ref, e.g. 00064583 has base object 00000034 which is "XMarkerHeading" + case ESM4::SUB_XRTM: // formId + { + FormId id; + reader.get(id); + //std::cout << "REFR XRTM : " << formIdToString(id) << std::endl;// FIXME + break; + } + // lighting + case ESM4::SUB_LNAM: // lighting template formId + case ESM4::SUB_XLIG: // struct, FOV, fade, etc + case ESM4::SUB_XEMI: // LIGH formId + case ESM4::SUB_XRDS: // Radius or Radiance + case ESM4::SUB_XRGB: + case ESM4::SUB_XRGD: // tangent data? + case ESM4::SUB_XALP: // alpha cutoff + // + case ESM4::SUB_XLOC: // formId + case ESM4::SUB_XMRK: + case ESM4::SUB_FNAM: + case ESM4::SUB_XTRG: // formId + case ESM4::SUB_XPCI: // formId + case ESM4::SUB_XLCM: + case ESM4::SUB_XCNT: + case ESM4::SUB_TNAM: + case ESM4::SUB_ONAM: + case ESM4::SUB_VMAD: + case ESM4::SUB_XPRM: + case ESM4::SUB_INAM: + case ESM4::SUB_PDTO: + case ESM4::SUB_SCHR: + case ESM4::SUB_SCTX: + case ESM4::SUB_XAPD: + case ESM4::SUB_XAPR: + case ESM4::SUB_XCVL: + case ESM4::SUB_XCZA: + case ESM4::SUB_XCZC: + case ESM4::SUB_XEZN: + case ESM4::SUB_XFVC: + case ESM4::SUB_XHTW: + case ESM4::SUB_XIS2: + case ESM4::SUB_XLCN: + case ESM4::SUB_XLIB: + case ESM4::SUB_XLKR: + case ESM4::SUB_XLRM: + case ESM4::SUB_XLRT: + case ESM4::SUB_XLTW: + case ESM4::SUB_XMBO: + case ESM4::SUB_XMBP: + case ESM4::SUB_XMBR: + case ESM4::SUB_XNDP: + case ESM4::SUB_XOCP: + case ESM4::SUB_XPOD: + case ESM4::SUB_XPPA: + case ESM4::SUB_XPRD: + case ESM4::SUB_XPWR: + case ESM4::SUB_XRMR: + case ESM4::SUB_XSPC: + case ESM4::SUB_XTNM: + case ESM4::SUB_XTRI: + case ESM4::SUB_XWCN: + case ESM4::SUB_XWCU: + case ESM4::SUB_XATR: // Dawnguard only? + case ESM4::SUB_XHLT: // Unofficial Oblivion Patch + case ESM4::SUB_XCHG: // thievery.exp + case ESM4::SUB_XHLP: // FO3 + case ESM4::SUB_XRDO: // FO3 + case ESM4::SUB_XAMT: // FO3 + case ESM4::SUB_XAMC: // FO3 + case ESM4::SUB_XRAD: // FO3 + case ESM4::SUB_XIBS: // FO3 + case ESM4::SUB_XORD: // FO3 + case ESM4::SUB_XCLP: // FO3 + case ESM4::SUB_SCDA: // FO3 + case ESM4::SUB_SCRO: // FO3 + case ESM4::SUB_RCLR: // FO3 + case ESM4::SUB_BNAM: // FONV + case ESM4::SUB_CNAM: // FONV + case ESM4::SUB_MMRK: // FONV + case ESM4::SUB_MNAM: // FONV + case ESM4::SUB_NNAM: // FONV + case ESM4::SUB_XATO: // FONV + case ESM4::SUB_SCRV: // FONV + case ESM4::SUB_SCVR: // FONV + case ESM4::SUB_SLSD: // FONV + case ESM4::SUB_XSRF: // FONV + case ESM4::SUB_XSRD: // FONV + case ESM4::SUB_WMI1: // FONV + case ESM4::SUB_XLRL: // Unofficial Skyrim Patch + { + //std::cout << "REFR " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::REFR::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Reference::save(ESM4::Writer& writer) const +//{ +//} + +void ESM4::Reference::blank() +{ +} diff --git a/extern/esm4/refr.hpp b/extern/esm4/refr.hpp new file mode 100644 index 000000000..1cef1abe7 --- /dev/null +++ b/extern/esm4/refr.hpp @@ -0,0 +1,78 @@ +/* + 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_REFR_H +#define ESM4_REFR_H + +#include "common.hpp" // Position + +namespace ESM4 +{ + class Reader; + class Writer; + + struct TeleportDest + { + FormId destDoor; + Position destPos; + std::uint32_t flags; // 0x01 no alarm (only in TES5) + }; + + // Unlike TES3, multiple cells can have the same exterior co-ordinates. + // The cells need to be organised under world spaces. + struct Reference + { + 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; + std::uint32_t mFactionRank; + + bool mDisabled; + EnableParent mEsp; // TODO may need to check mFlags & 0x800 (initially disabled) + + std::uint32_t mCount; // only if > 1 (default 1) + + TeleportDest mDoor; + + Reference(); + virtual ~Reference(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + void blank(); + }; +} + +#endif // ESM4_REFR_H diff --git a/extern/esm4/regn.cpp b/extern/esm4/regn.cpp new file mode 100644 index 000000000..06826b13f --- /dev/null +++ b/extern/esm4/regn.cpp @@ -0,0 +1,152 @@ +/* + 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 "regn.hpp" + +#include +#include + +#include // FIXME: debug only + +#include "reader.hpp" +//#include "writer.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +ESM4::Region::Region() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + mShader.clear(); + mMapName.clear(); + //mData.unknown = 1; // FIXME: temp use to indicate not loaded + mData.resize(8); +} + +ESM4::Region::~Region() +{ +} + +void ESM4::Region::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + + RDAT_Types next = RDAT_None; + + while (reader.getSubRecordHeader()) + { + const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + switch (subHdr.typeId) + { + case ESM4::SUB_EDID: reader.getZString(mEditorId); break; + case ESM4::SUB_RCLR: reader.get(mColour); break; + case ESM4::SUB_WNAM: reader.getFormId(mWorldId); break; + case ESM4::SUB_ICON: reader.getZString(mShader); break; + case ESM4::SUB_RPLI: reader.get(mEdgeFalloff); break; + case ESM4::SUB_RPLD: + { + mRPLD.resize(subHdr.dataSize/sizeof(std::uint32_t)); + for (std::vector::iterator it = mRPLD.begin(); it != mRPLD.end(); ++it) + { + reader.get(*it); +#if 0 + std::string padding = ""; + padding.insert(0, reader.stackSize()*2, ' '); + std::cout << padding << "RPLD: 0x" << std::hex << *it << std::endl; +#endif + } + break; + } + case ESM4::SUB_RDAT: + { + RDAT rdat; + reader.get(rdat); + + next = static_cast(rdat.type); + + mData[rdat.type].type = rdat.type; + mData[rdat.type].flag = rdat.flag; + mData[rdat.type].priority = rdat.priority; + mData[rdat.type].unknown = rdat.unknown; + + break; + } + case ESM4::SUB_RDMP: + { + assert(next == RDAT_Map && "REGN unexpected data type"); + next = RDAT_None; + + if (reader.hasLocalizedStrings()) + reader.getLocalizedString(mMapName); + else if (!reader.getZString(mMapName)) + throw std::runtime_error ("REGN RDMP data read error"); + break; + } + case ESM4::SUB_RDMD: // Only in Oblivion? + case ESM4::SUB_RDSD: // Only in Oblivion? Possibly the same as RDSA // formId + case ESM4::SUB_RDGS: // Only in Oblivion? (ToddTestRegion1) // formId + case ESM4::SUB_RDMO: + case ESM4::SUB_RDSA: + case ESM4::SUB_RDWT: // formId + case ESM4::SUB_RDOT: // formId + case ESM4::SUB_RDID: // FONV + case ESM4::SUB_RDSB: // FONV + case ESM4::SUB_RDSI: // FONV + { + //RDAT skipping... following is a map + //RDMP skipping... map name + // + //RDAT skipping... following is weather + //RDWT skipping... weather data + // + //RDAT skipping... following is sound + //RDMD skipping... unknown, maybe music data + // + //RDSD skipping... unknown, maybe sound data + // + //RDAT skipping... following is grass + //RDGS skipping... unknown, maybe grass + + //std::cout << "REGN " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip + break; + } + default: + throw std::runtime_error("ESM4::REGN::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Region::save(ESM4::Writer& writer) const +//{ +//} + +void ESM4::Region::blank() +{ +} diff --git a/extern/esm4/regn.hpp b/extern/esm4/regn.hpp new file mode 100644 index 000000000..fdb1e5135 --- /dev/null +++ b/extern/esm4/regn.hpp @@ -0,0 +1,84 @@ +/* + 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_REGN_H +#define ESM4_REGN_H + +#include +#include +#include + +namespace ESM4 +{ + class Reader; + class Writer; + typedef std::uint32_t FormId; + + struct Region + { + enum RDAT_Types + { + RDAT_None = 0x00, + RDAT_Objects = 0x02, + RDAT_Weather = 0x03, + RDAT_Map = 0x04, + RDAT_Landscape = 0x05, + RDAT_Grass = 0x06, + RDAT_Sound = 0x07 + }; + + struct RDAT + { + std::uint32_t type; + std::uint8_t flag; + std::uint8_t priority; + std::uint16_t unknown; + }; + + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::uint32_t mColour; // RGBA + FormId mWorldId; // worldspace formid + + std::string mShader; //?? ICON + std::string mMapName; + std::uint32_t mEdgeFalloff; + std::vector mRPLD; // unknown + std::vector mData; // indexed by the type value + + Region(); + virtual ~Region(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + void blank(); + }; +} + +#endif // ESM4_REGN_H diff --git a/extern/esm4/sbsp.cpp b/extern/esm4/sbsp.cpp new file mode 100644 index 000000000..4cee5c893 --- /dev/null +++ b/extern/esm4/sbsp.cpp @@ -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 "sbsp.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Subspace::Subspace() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + + mDimension.x = 0.f; + mDimension.y = 0.f; + mDimension.z = 0.f; +} + +ESM4::Subspace::~Subspace() +{ +} + +void ESM4::Subspace::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.get(mDimension.x); + reader.get(mDimension.y); + reader.get(mDimension.z); + break; + } + default: + throw std::runtime_error("ESM4::SBSP::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Subspace::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Subspace::blank() +//{ +//} diff --git a/extern/esm4/sbsp.hpp b/extern/esm4/sbsp.hpp new file mode 100644 index 000000000..9962d30fa --- /dev/null +++ b/extern/esm4/sbsp.hpp @@ -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_SBSP_H +#define ESM4_SBSP_H + +#include +#include + +namespace ESM4 +{ + class Reader; + typedef std::uint32_t FormId; + + struct Subspace + { + struct Dimension + { + float x; + float y; + float z; + }; + + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + Dimension mDimension; + + Subspace(); + virtual ~Subspace(); + + virtual void load(Reader& reader); + //virtual void save(Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_SBSP_H diff --git a/extern/esm4/sgst.cpp b/extern/esm4/sgst.cpp new file mode 100644 index 000000000..022a4f984 --- /dev/null +++ b/extern/esm4/sgst.cpp @@ -0,0 +1,118 @@ +/* + 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 "sgst.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::SigilStone::SigilStone() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mIcon.clear(); + + mData.uses = 0; + mData.value = 0; + mData.weight = 0.f; + + std::memset(&mEffect, 0, sizeof(ScriptEffect)); +} + +ESM4::SigilStone::~SigilStone() +{ +} + +void ESM4::SigilStone::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.getZString(mFullName)) + throw std::runtime_error ("SGST FULL data read error"); + } + else + { + // FIXME: should be part of a struct? + std::string scriptEffectName; + if (!reader.getZString(scriptEffectName)) + throw std::runtime_error ("SGST FULL data read error"); + mScriptEffect.push_back(scriptEffectName); + } + break; + } + case ESM4::SUB_DATA: + { + reader.get(mData.uses); + 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_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_SCIT: + { + reader.get(mEffect); + reader.adjustFormId(mEffect.formId); + break; + } + case ESM4::SUB_MODT: + case ESM4::SUB_EFID: + case ESM4::SUB_EFIT: + { + //std::cout << "SGST " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::SGST::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::SigilStone::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::SigilStone::blank() +//{ +//} diff --git a/extern/esm4/sgst.hpp b/extern/esm4/sgst.hpp new file mode 100644 index 000000000..be5243429 --- /dev/null +++ b/extern/esm4/sgst.hpp @@ -0,0 +1,74 @@ +/* + 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_SGST_H +#define ESM4_SGST_H + +#include + +#include "common.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct SigilStone + { + struct Data + { + std::uint8_t uses; + std::uint32_t value; // gold + 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; + std::string mIcon; // inventory + + float mBoundRadius; + + std::vector mScriptEffect; // FIXME: prob. should be in a struct + FormId mScript; + ScriptEffect mEffect; + + Data mData; + + SigilStone(); + virtual ~SigilStone(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_SGST_H diff --git a/extern/esm4/slgm.cpp b/extern/esm4/slgm.cpp new file mode 100644 index 000000000..47265c217 --- /dev/null +++ b/extern/esm4/slgm.cpp @@ -0,0 +1,99 @@ +/* + 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 "slgm.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::SoulGem::SoulGem() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0), mSoul(0), mSoulCapacity(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mIcon.clear(); + + mData.value = 0; + mData.weight = 0.f; +} + +ESM4::SoulGem::~SoulGem() +{ +} + +void ESM4::SoulGem::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 ("SLGM 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_SOUL: reader.get(mSoul); break; + case ESM4::SUB_SLCP: reader.get(mSoulCapacity); break; + case ESM4::SUB_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_MODT: + case ESM4::SUB_KSIZ: + case ESM4::SUB_KWDA: + case ESM4::SUB_NAM0: + case ESM4::SUB_OBND: + { + //std::cout << "SLGM " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::SLGM::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::SoulGem::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::SoulGem::blank() +//{ +//} diff --git a/extern/esm4/slgm.hpp b/extern/esm4/slgm.hpp new file mode 100644 index 000000000..75261c068 --- /dev/null +++ b/extern/esm4/slgm.hpp @@ -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_SLGM_H +#define ESM4_SLGM_H + +#include +#include + +namespace ESM4 +{ + class Reader; + class Writer; + typedef std::uint32_t FormId; + + struct SoulGem + { +#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; + std::uint8_t mSoul; // 0 = None, 1 = Petty, 2 = Lesser, 3 = Common, 4 = Greater, 5 = Grand + std::uint8_t mSoulCapacity; // 0 = None, 1 = Petty, 2 = Lesser, 3 = Common, 4 = Greater, 5 = Grand + + Data mData; + + SoulGem(); + virtual ~SoulGem(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_SLGM_H diff --git a/extern/esm4/soun.cpp b/extern/esm4/soun.cpp new file mode 100644 index 000000000..02d0ff556 --- /dev/null +++ b/extern/esm4/soun.cpp @@ -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 "soun.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Sound::Sound() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + mSoundFile.clear(); +} + +ESM4::Sound::~Sound() +{ +} + +void ESM4::Sound::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_FNAM: reader.getZString(mSoundFile); break; + case ESM4::SUB_SNDX: reader.get(mData); break; + case ESM4::SUB_SNDD: + case ESM4::SUB_OBND: // TES5 only + case ESM4::SUB_SDSC: // TES5 only + case ESM4::SUB_ANAM: // FO3 + case ESM4::SUB_GNAM: // FO3 + case ESM4::SUB_HNAM: // FO3 + case ESM4::SUB_RNAM: // FONV + { + //std::cout << "SOUN " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::SOUN::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Sound::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Sound::blank() +//{ +//} diff --git a/extern/esm4/soun.hpp b/extern/esm4/soun.hpp new file mode 100644 index 000000000..388a439aa --- /dev/null +++ b/extern/esm4/soun.hpp @@ -0,0 +1,86 @@ +/* + 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_SOUN_H +#define ESM4_SOUN_H + +#include +#include + +namespace ESM4 +{ + class Reader; + class Writer; + typedef std::uint32_t FormId; + + struct Sound + { + enum Flags + { + Flags_RandomFreqShift = 0x0001, + Flags_PlayAtRandom = 0x0002, + Flags_EnvIgnored = 0x0004, + Flags_RandomLocation = 0x0008, + Flags_Loop = 0x0010, + Flags_MenuSound = 0x0020, + Flags_2D = 0x0040, + Flags_360LFE = 0x0080 + }; + +#pragma pack(push, 1) + struct SNDX + { + std::uint8_t minAttenuation; + std::uint8_t maxAttenuation; + std::int8_t freqAdjustment; // %, signed + std::uint8_t unknown; + std::uint16_t flags; + std::uint16_t unknown2; + std::uint16_t staticAttenuation; // divide by 100 to get value in dB + std::uint8_t stopTime; // multipy vy 1440/256 to get value in minutes + std::uint8_t startTime; // multipy vy 1440/256 to get value in minutes + }; +#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 mSoundFile; + SNDX mData; + + Sound(); + virtual ~Sound(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_SOUN_H diff --git a/extern/esm4/stat.cpp b/extern/esm4/stat.cpp new file mode 100644 index 000000000..850b6f504 --- /dev/null +++ b/extern/esm4/stat.cpp @@ -0,0 +1,101 @@ +/* + 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 "stat.hpp" + +#include +#include // FIXME: debug only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Static::Static() : mFormId(0), mFlags(0), mBoundRadius(0.f) +{ + mEditorId.clear(); + mModel.clear(); +} + +ESM4::Static::~Static() +{ +} + +void ESM4::Static::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_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_MODT: + { + // version is only availabe in TES5 (seems to be 27 or 28?) + //if (reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170) + //std::cout << "STAT MODT ver: " << std::hex << reader.hdr().record.version << std::endl; + + // for TES4 these are just a sequence of bytes + mMODT.resize(subHdr.dataSize/sizeof(std::uint8_t)); + for (std::vector::iterator it = mMODT.begin(); it != mMODT.end(); ++it) + { + reader.get(*it); +#if 0 + std::string padding = ""; + padding.insert(0, reader.stackSize()*2, ' '); + std::cout << padding << "MODT: " << std::hex << *it << std::endl; +#endif + } + break; + } + case ESM4::SUB_MODS: + case ESM4::SUB_OBND: + case ESM4::SUB_DNAM: + case ESM4::SUB_MNAM: + case ESM4::SUB_BRUS: // FONV + case ESM4::SUB_RNAM: // FONV + { + //std::cout << "STAT " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::STAT::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Static::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Static::blank() +//{ +//} diff --git a/extern/esm4/stat.hpp b/extern/esm4/stat.hpp new file mode 100644 index 000000000..13f93141f --- /dev/null +++ b/extern/esm4/stat.hpp @@ -0,0 +1,63 @@ +/* + 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_STAT_H +#define ESM4_STAT_H + +#include +#include +#include + +namespace ESM4 +{ + class Reader; + class Writer; + typedef std::uint32_t FormId; + + // Unlike TES3, multiple cells can have the same exterior co-ordinates. + // The cells need to be organised under world spaces. + struct Static + { + 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; + std::vector mMODT; // FIXME texture hash + + Static(); + virtual ~Static(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_STAT_H diff --git a/extern/esm4/tes4.cpp b/extern/esm4/tes4.cpp new file mode 100644 index 000000000..59e369d89 --- /dev/null +++ b/extern/esm4/tes4.cpp @@ -0,0 +1,123 @@ +/* + 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 "tes4.hpp" + +#include +#include + +#include // FIXME: debugging only + +#include "common.hpp" +#include "formid.hpp" +#include "reader.hpp" +//#include "writer.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +void ESM4::Header::load(ESM4::Reader& reader) +{ + mFlags = reader.hdr().record.flags; // 0x01 = Rec_ESM, 0x80 = Rec_Localized + + while (reader.getSubRecordHeader()) + { + const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + switch (subHdr.typeId) + { + case ESM4::SUB_HEDR: + { + if (!reader.get(mData.version) || !reader.get(mData.records) || !reader.get(mData.nextObjectId)) + throw std::runtime_error("TES4 HEDR data read error"); + + assert((size_t)subHdr.dataSize == sizeof(mData.version)+sizeof(mData.records)+sizeof(mData.nextObjectId) + && "TES4 HEDR data size mismatch"); + break; + } + case ESM4::SUB_CNAM: + { + if (!reader.getZString(mAuthor)) + throw std::runtime_error("TES4 CNAM data read error"); + break; + } + case ESM4::SUB_SNAM: + { + if (!reader.getZString(mDesc)) + throw std::runtime_error("TES4 SNAM data read error"); + break; + } + case ESM4::SUB_MAST: // multiple + { + MasterData m; + if (!reader.getZString(m.name)) + throw std::runtime_error("TES4 MAST data read error"); + + // NOTE: some mods do not have DATA following MAST so can't read DATA here + + mMaster.push_back (m); + break; + } + case ESM4::SUB_DATA: + { + // WARNING: assumes DATA always follows MAST + if (!reader.get(mMaster.back().size)) + throw std::runtime_error("TES4 DATA data read error"); + break; + } + case ESM4::SUB_ONAM: + { + mOverrides.resize(subHdr.dataSize/sizeof(FormId)); + for (std::vector::iterator it = mOverrides.begin(); it != mOverrides.end(); ++it) + { + if (!reader.get(*it)) + throw std::runtime_error("TES4 ONAM data read error"); +#if 0 + std::string padding = ""; + padding.insert(0, reader.stackSize()*2, ' '); + std::cout << padding << "ESM4::Header::ONAM overrides: " << formIdToString(*it) << std::endl; +#endif + } + break; + } + case ESM4::SUB_INTV: + case ESM4::SUB_INCC: + case ESM4::SUB_OFST: // Oblivion only? + case ESM4::SUB_DELE: // Oblivion only? + { + //std::cout << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::Header::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Header::save(ESM4::Writer& writer) +//{ +//} diff --git a/extern/esm4/tes4.hpp b/extern/esm4/tes4.hpp new file mode 100644 index 000000000..15e164366 --- /dev/null +++ b/extern/esm4/tes4.hpp @@ -0,0 +1,84 @@ +/* + 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_TES4_H +#define ESM4_TES4_H + +#include +#include +#include + +namespace ESM4 +{ + class Reader; + class Writer; + typedef std::uint32_t FormId; + + struct Header + { +#pragma pack(push, 1) + union ESMVersion + { + float f; + unsigned int ui; + }; + + struct Data + { + // The supported versions are 0.80 = 0x3f800000, 0.94 = 0x3f70a3d7 and 1.7 = 0x3fd9999a + // (also 1.34 = 0x3fab851f eventually) + ESMVersion version; // File format version. + std::int32_t records; // Number of records + std::uint32_t nextObjectId; + }; +#pragma pack(pop) + + // Defines another files (esm or esp) that this file depends upon. + struct MasterData + { + std::string name; + std::uint64_t size; + }; + + std::uint32_t mFlags; // 0x01 esm, 0x80 localised strings + + Data mData; + std::string mAuthor; // Author's name + std::string mDesc; // File description + std::vector mMaster; + + std::vector mOverrides; // Skyrim only, cell children (ACHR, LAND, NAVM, PGRE, PHZD, REFR) + + // position in the vector = mod index of master files above + // value = adjusted mod index based on all the files loaded so far + std::vector mModIndicies; + + void load (Reader& reader); + //void save (Writer& writer); + }; +} + +#endif // ESM4_TES4_H diff --git a/extern/esm4/tree.cpp b/extern/esm4/tree.cpp new file mode 100644 index 000000000..ea28b188e --- /dev/null +++ b/extern/esm4/tree.cpp @@ -0,0 +1,85 @@ +/* + 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 "tree.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Tree::Tree() : mFormId(0), mFlags(0), mBoundRadius(0.f) +{ + mEditorId.clear(); + mModel.clear(); + mLeafTexture.clear(); +} + +ESM4::Tree::~Tree() +{ +} + +void ESM4::Tree::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_ICON: reader.getZString(mLeafTexture); break; + case ESM4::SUB_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_MODT: + case ESM4::SUB_CNAM: + case ESM4::SUB_BNAM: + case ESM4::SUB_SNAM: + case ESM4::SUB_FULL: + case ESM4::SUB_OBND: + case ESM4::SUB_PFIG: + case ESM4::SUB_PFPC: + { + //std::cout << "TREE " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::TREE::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Tree::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Tree::blank() +//{ +//} diff --git a/extern/esm4/tree.hpp b/extern/esm4/tree.hpp new file mode 100644 index 000000000..b7213eacb --- /dev/null +++ b/extern/esm4/tree.hpp @@ -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_TREE_H +#define ESM4_TREE_H + +#include +#include + +namespace ESM4 +{ + class Reader; + class Writer; + typedef std::uint32_t FormId; + + struct Tree + { + 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; + + std::string mLeafTexture; + + Tree(); + virtual ~Tree(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_TREE_H diff --git a/extern/esm4/weap.cpp b/extern/esm4/weap.cpp new file mode 100644 index 000000000..f9fd4d8cd --- /dev/null +++ b/extern/esm4/weap.cpp @@ -0,0 +1,194 @@ +/* + 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 "weap.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Weapon::Weapon() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScript(0), + mEnchantmentPoints(0), mEnchantment(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mIcon.clear(); +} + +ESM4::Weapon::~Weapon() +{ +} + +void ESM4::Weapon::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 ("WEAP FULL data read error"); + + break; + } + case ESM4::SUB_DATA: + { + //if (reader.esmVersion() == ESM4::VER_094 || reader.esmVersion() == ESM4::VER_170) + if (subHdr.dataSize == 10) // FO3 has 15 bytes even though VER_094 + { + reader.get(mData.value); + reader.get(mData.weight); + reader.get(mData.damage); + } + else if (isFONV || subHdr.dataSize == 15) + { + reader.get(mData.value); + reader.get(mData.health); + reader.get(mData.weight); + reader.get(mData.damage); + reader.get(mData.clipSize); + } + else + { + reader.get(mData.type); + reader.get(mData.speed); + reader.get(mData.reach); + reader.get(mData.flags); + reader.get(mData.value); + reader.get(mData.health); + reader.get(mData.weight); + reader.get(mData.damage); + } + 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_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_BAMT: + case ESM4::SUB_BIDS: + case ESM4::SUB_INAM: + case ESM4::SUB_CNAM: + case ESM4::SUB_CRDT: + case ESM4::SUB_DESC: + case ESM4::SUB_DNAM: + case ESM4::SUB_EAMT: + case ESM4::SUB_EITM: + case ESM4::SUB_ETYP: + case ESM4::SUB_KSIZ: + case ESM4::SUB_KWDA: + case ESM4::SUB_NAM8: + case ESM4::SUB_NAM9: + case ESM4::SUB_OBND: + case ESM4::SUB_SNAM: + case ESM4::SUB_TNAM: + case ESM4::SUB_UNAM: + case ESM4::SUB_VMAD: + case ESM4::SUB_VNAM: + case ESM4::SUB_WNAM: + case ESM4::SUB_XNAM: // Dawnguard only? + case ESM4::SUB_NNAM: + case ESM4::SUB_MODS: + case ESM4::SUB_NAM0: // FO3 + case ESM4::SUB_MICO: // FO3 + case ESM4::SUB_REPL: // FO3 + case ESM4::SUB_YNAM: // FO3 + case ESM4::SUB_ZNAM: // FO3 + case ESM4::SUB_MOD2: // FO3 + case ESM4::SUB_MO2T: // FO3 + case ESM4::SUB_MO2S: // FO3 + case ESM4::SUB_NAM6: // FO3 + case ESM4::SUB_MOD4: // FO3 + case ESM4::SUB_MO4T: // FO3 + case ESM4::SUB_MO4S: // FO3 + case ESM4::SUB_BIPL: // FO3 + case ESM4::SUB_NAM7: // FO3 + case ESM4::SUB_MOD3: // FO3 + case ESM4::SUB_MO3T: // FO3 + case ESM4::SUB_MO3S: // FO3 + case ESM4::SUB_MODD: // FO3 + //case ESM4::SUB_MOSD: // 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_VATS: // FONV + case ESM4::SUB_VANM: // FONV + case ESM4::SUB_MWD1: // FONV + case ESM4::SUB_MWD2: // FONV + case ESM4::SUB_MWD3: // FONV + case ESM4::SUB_MWD4: // FONV + case ESM4::SUB_MWD5: // FONV + case ESM4::SUB_MWD6: // FONV + case ESM4::SUB_MWD7: // FONV + case ESM4::SUB_WMI1: // FONV + case ESM4::SUB_WMI2: // FONV + case ESM4::SUB_WMI3: // FONV + case ESM4::SUB_WMS1: // FONV + case ESM4::SUB_WMS2: // FONV + case ESM4::SUB_WNM1: // FONV + case ESM4::SUB_WNM2: // FONV + case ESM4::SUB_WNM3: // FONV + case ESM4::SUB_WNM4: // FONV + case ESM4::SUB_WNM5: // FONV + case ESM4::SUB_WNM6: // FONV + case ESM4::SUB_WNM7: // FONV + case MKTAG('E','F','S','D'): // FONV DeadMoney + { + //std::cout << "WEAP " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::WEAP::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Weapon::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Weapon::blank() +//{ +//} diff --git a/extern/esm4/weap.hpp b/extern/esm4/weap.hpp new file mode 100644 index 000000000..ea8d5eefb --- /dev/null +++ b/extern/esm4/weap.hpp @@ -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. + +*/ +#ifndef ESM4_WEAP_H +#define ESM4_WEAP_H + +#include +#include + +namespace ESM4 +{ + class Reader; + class Writer; + typedef std::uint32_t FormId; + + struct Weapon + { + struct Data + { + std::uint32_t type; + float speed; + float reach; + std::uint32_t flags; + std::uint32_t value; // gold + std::uint32_t health; + float weight; + std::uint16_t damage; + std::uint8_t clipSize; // FO3/FONV only + + Data() : type(0), speed(0.f), reach(0.f), flags(0), value(0), + health(0), weight(0.f), damage(0), clipSize(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; + + float mBoundRadius; + + FormId mScript; + std::uint16_t mEnchantmentPoints; + FormId mEnchantment; + + Data mData; + + Weapon(); + virtual ~Weapon(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_WEAP_H diff --git a/extern/esm4/wrld.cpp b/extern/esm4/wrld.cpp new file mode 100644 index 000000000..3826e32ad --- /dev/null +++ b/extern/esm4/wrld.cpp @@ -0,0 +1,179 @@ +/* + 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 "wrld.hpp" + +#include +#include // FIXME: debug only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::World::World() : mFormId(0), mFlags(0), mParent(0), mWorldFlags(0), mClimate(0), mWater(0), + mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mSound(0) +{ + mEditorId.clear(); + mFullName.clear(); + mMapFile.clear(); + + mMap.width = 0; + mMap.height = 0; + mMap.NWcellX = 0; + mMap.NWcellY = 0; + mMap.SEcellX = 0; + mMap.SEcellY = 0; + mMap.minHeight = 0.f; + mMap.maxHeight = 0.f; + mMap.initialPitch = 0.f; +} + +ESM4::World::~World() +{ +} + +void ESM4::World::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + + // It should be possible to save the current world formId automatically while reading in + // the record header rather than doing it manually here but possibly less efficient (may + // need to check each record?). + // + // Alternatively it may be possible to figure it out by examining the group headers, but + // apparently the label field is not reliable so the parent world formid may have been + // corrupted by the use of ignore flag (TODO: should check to verify). + reader.setCurrWorld(mFormId); // save for CELL later + + std::uint32_t subSize = 0; // for XXXX sub record + + while (reader.getSubRecordHeader()) + { + const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + switch (subHdr.typeId) + { + case ESM4::SUB_EDID: reader.getZString(mEditorId); break; + case ESM4::SUB_FULL: // Name of the worldspace + { + if (reader.hasLocalizedStrings()) + reader.getLocalizedString(mFullName); + else if (!reader.getZString(mFullName)) + throw std::runtime_error ("WRLD FULL data read error"); + + break; + } + case ESM4::SUB_WCTR: reader.get(mCenterCell); break; // Center cell, TES5 only + case ESM4::SUB_WNAM: reader.getFormId(mParent); break; + case ESM4::SUB_SNAM: reader.get(mSound); break; // sound, Oblivion only? + case ESM4::SUB_ICON: reader.getZString(mMapFile); break; // map filename, Oblivion only? + case ESM4::SUB_CNAM: reader.get(mClimate); break; + case ESM4::SUB_NAM2: reader.getFormId(mWater); break; + case ESM4::SUB_NAM0: + { + reader.get(mMinX); + reader.get(mMinY); + break; + } + case ESM4::SUB_NAM9: + { + reader.get(mMaxX); + reader.get(mMaxY); + break; + } + case ESM4::SUB_DATA: reader.get(mWorldFlags); break; + case ESM4::SUB_MNAM: + { + reader.get(mMap.width); + reader.get(mMap.height); + reader.get(mMap.NWcellX); + reader.get(mMap.NWcellY); + reader.get(mMap.SEcellX); + reader.get(mMap.SEcellY); + + if (subHdr.dataSize == 28) // Skyrim? + { + reader.get(mMap.minHeight); + reader.get(mMap.maxHeight); + reader.get(mMap.initialPitch); + } + + break; + } + case ESM4::SUB_RNAM: // multiple + case ESM4::SUB_MHDT: + case ESM4::SUB_LTMP: + case ESM4::SUB_XEZN: + case ESM4::SUB_XLCN: + case ESM4::SUB_NAM3: + case ESM4::SUB_NAM4: + case ESM4::SUB_DNAM: + case ESM4::SUB_MODL: + case ESM4::SUB_NAMA: + case ESM4::SUB_PNAM: + case ESM4::SUB_ONAM: + case ESM4::SUB_TNAM: + case ESM4::SUB_UNAM: + case ESM4::SUB_ZNAM: + case ESM4::SUB_XWEM: + case ESM4::SUB_MODT: // from Dragonborn onwards? + case ESM4::SUB_INAM: // FO3 + case ESM4::SUB_NNAM: // FO3 + case ESM4::SUB_XNAM: // FO3 + case ESM4::SUB_IMPS: // FO3 Anchorage + case ESM4::SUB_IMPF: // FO3 Anchorage + { + //std::cout << "WRLD " << ESM4::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip + break; + } + case ESM4::SUB_OFST: + { + if (subSize) + { + reader.skipSubRecordData(subSize); // special post XXXX + reader.updateRecordRemaining(subSize); // WARNING: manually update + subSize = 0; + } + else + reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip + + break; + } + case ESM4::SUB_XXXX: + { + reader.get(subSize); + break; + } + default: + throw std::runtime_error("ESM4::WRLD::load - Unknown subrecord " + ESM4::printName(subHdr.typeId)); + } + } +} + +//void ESM4::World::save(ESM4::Writer& writer) const +//{ +//} diff --git a/extern/esm4/wrld.hpp b/extern/esm4/wrld.hpp new file mode 100644 index 000000000..26eb23973 --- /dev/null +++ b/extern/esm4/wrld.hpp @@ -0,0 +1,122 @@ +/* + 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_WRLD_H +#define ESM4_WRLD_H + +#include + +#include "common.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct World + { + enum WorldFlags // TES4 TES5 + { // -------------------- ----------------- + WLD_Small = 0x01, // Small World Small World + WLD_NoFastTravel = 0x02, // Can't Fast Travel Can't Fast Travel + WLD_Oblivion = 0x04, // Oblivion worldspace + WLD_NoLODWater = 0x08, // No LOD Water + WLD_NoLandscpe = 0x10, // No LOD Water No Landscape + WLD_NoSky = 0x20, // No Sky + wLD_FixedDimension = 0x40, // Fixed Dimensions + WLD_NoGrass = 0x80 // No Grass + }; + + struct REFRcoord + { + FormId formId; + std::int16_t unknown1; + std::int16_t unknown2; + }; + + struct RNAMstruct + { + std::int16_t unknown1; + std::int16_t unknown2; + std::vector refrs; + }; + + //Map size struct 16 or 28 byte structure + struct Map + { + std::uint32_t width; // usable width of the map + std::uint32_t height; // usable height of the map + std::int16_t NWcellX; + std::int16_t NWcellY; + std::int16_t SEcellX; + std::int16_t SEcellY; + float minHeight; // Camera Data (default 50000), new as of Skyrim 1.8, purpose is not yet known. + float maxHeight; // Camera Data (default 80000) + float initialPitch; + }; + + 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 mParent; // parent worldspace formid + std::uint8_t mWorldFlags; + FormId mClimate; + FormId mWater; + + Map mMap; + + std::int32_t mMinX; + std::int32_t mMinY; + std::int32_t mMaxX; + std::int32_t mMaxY; + + // ------ TES4 only ----- + + std::int32_t mSound; // 0 = no record, 1 = Public, 2 = Dungeon + std::string mMapFile; + + // ------ TES5 only ----- + + Grid mCenterCell; + RNAMstruct mData; + + // ---------------------- + + // cache formId's of children (e.g. CELL, ROAD) + std::vector mCells; + std::vector mRoads; + + World(); + virtual ~World(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + }; +} + +#endif // ESM4_WRLD_H