From ba3ae448d4f69cb802b28dac31e28bd092af7fc0 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 30 Jan 2022 11:07:39 +0100 Subject: [PATCH] Initial import of esm4 by cc9cii --- components/CMakeLists.txt | 12 +- components/esm/common.cpp | 15 + components/esm/common.hpp | 57 ++ components/esm/reader.cpp | 114 +++ components/esm/reader.hpp | 60 ++ components/esm4/acti.hpp | 70 ++ components/esm4/actor.hpp | 163 +++++ components/esm4/common.cpp | 100 +++ components/esm4/common.hpp | 939 +++++++++++++++++++++++++ components/esm4/dialogue.hpp | 46 ++ components/esm4/effect.hpp | 56 ++ components/esm4/formid.cpp | 78 ++ components/esm4/formid.hpp | 42 ++ components/esm4/inventory.hpp | 55 ++ components/esm4/lighting.hpp | 79 +++ components/esm4/loadachr.cpp | 124 ++++ components/esm4/loadachr.hpp | 70 ++ components/esm4/loadacre.cpp | 106 +++ components/esm4/loadacre.hpp | 67 ++ components/esm4/loadacti.cpp | 103 +++ components/esm4/loadalch.cpp | 121 ++++ components/esm4/loadalch.hpp | 88 +++ components/esm4/loadaloc.cpp | 172 +++++ components/esm4/loadaloc.hpp | 88 +++ components/esm4/loadammo.cpp | 133 ++++ components/esm4/loadammo.hpp | 84 +++ components/esm4/loadanio.cpp | 80 +++ components/esm4/loadanio.hpp | 63 ++ components/esm4/loadappa.cpp | 106 +++ components/esm4/loadappa.hpp | 75 ++ components/esm4/loadarma.cpp | 151 ++++ components/esm4/loadarma.hpp | 70 ++ components/esm4/loadarmo.cpp | 218 ++++++ components/esm4/loadarmo.hpp | 196 ++++++ components/esm4/loadaspc.cpp | 94 +++ components/esm4/loadaspc.hpp | 66 ++ components/esm4/loadbook.cpp | 121 ++++ components/esm4/loadbook.hpp | 114 +++ components/esm4/loadbptd.cpp | 113 +++ components/esm4/loadbptd.hpp | 128 ++++ components/esm4/loadcell.cpp | 269 +++++++ components/esm4/loadcell.hpp | 109 +++ components/esm4/loadclas.cpp | 84 +++ components/esm4/loadclas.hpp | 66 ++ components/esm4/loadclfm.cpp | 93 +++ components/esm4/loadclfm.hpp | 70 ++ components/esm4/loadclot.cpp | 106 +++ components/esm4/loadclot.hpp | 83 +++ components/esm4/loadcont.cpp | 107 +++ components/esm4/loadcont.hpp | 71 ++ components/esm4/loadcrea.cpp | 232 ++++++ components/esm4/loadcrea.hpp | 151 ++++ components/esm4/loaddial.cpp | 125 ++++ components/esm4/loaddial.hpp | 70 ++ components/esm4/loaddobj.cpp | 128 ++++ components/esm4/loaddobj.hpp | 100 +++ components/esm4/loaddoor.cpp | 93 +++ components/esm4/loaddoor.hpp | 76 ++ components/esm4/loadeyes.cpp | 74 ++ components/esm4/loadeyes.hpp | 68 ++ components/esm4/loadflor.cpp | 89 +++ components/esm4/loadflor.hpp | 78 ++ components/esm4/loadflst.cpp | 81 +++ components/esm4/loadflst.hpp | 60 ++ components/esm4/loadfurn.cpp | 98 +++ components/esm4/loadfurn.hpp | 64 ++ components/esm4/loadglob.cpp | 82 +++ components/esm4/loadglob.hpp | 60 ++ components/esm4/loadgras.cpp | 78 ++ components/esm4/loadgras.hpp | 98 +++ components/esm4/loadgrup.hpp | 158 +++++ components/esm4/loadhair.cpp | 84 +++ components/esm4/loadhair.hpp | 71 ++ components/esm4/loadhdpt.cpp | 104 +++ components/esm4/loadhdpt.hpp | 66 ++ components/esm4/loadidle.cpp | 84 +++ components/esm4/loadidle.hpp | 62 ++ components/esm4/loadidlm.cpp | 103 +++ components/esm4/loadidlm.hpp | 63 ++ components/esm4/loadimod.cpp | 89 +++ components/esm4/loadimod.hpp | 59 ++ components/esm4/loadinfo.cpp | 222 ++++++ components/esm4/loadinfo.hpp | 90 +++ components/esm4/loadingr.cpp | 133 ++++ components/esm4/loadingr.hpp | 83 +++ components/esm4/loadkeym.cpp | 93 +++ components/esm4/loadkeym.hpp | 77 ++ components/esm4/loadland.cpp | 255 +++++++ components/esm4/loadland.hpp | 136 ++++ components/esm4/loadlgtm.cpp | 105 +++ components/esm4/loadlgtm.hpp | 63 ++ components/esm4/loadligh.cpp | 120 ++++ components/esm4/loadligh.hpp | 101 +++ components/esm4/loadltex.cpp | 107 +++ components/esm4/loadltex.hpp | 75 ++ components/esm4/loadlvlc.cpp | 130 ++++ components/esm4/loadlvlc.hpp | 71 ++ components/esm4/loadlvli.cpp | 142 ++++ components/esm4/loadlvli.hpp | 72 ++ components/esm4/loadlvln.cpp | 114 +++ components/esm4/loadlvln.hpp | 69 ++ components/esm4/loadmato.cpp | 76 ++ components/esm4/loadmato.hpp | 58 ++ components/esm4/loadmisc.cpp | 95 +++ components/esm4/loadmisc.hpp | 77 ++ components/esm4/loadmset.cpp | 115 +++ components/esm4/loadmset.hpp | 99 +++ components/esm4/loadmstt.cpp | 87 +++ components/esm4/loadmstt.hpp | 60 ++ components/esm4/loadmusc.cpp | 86 +++ components/esm4/loadmusc.hpp | 60 ++ components/esm4/loadnavi.cpp | 373 ++++++++++ components/esm4/loadnavi.hpp | 117 +++ components/esm4/loadnavm.cpp | 270 +++++++ components/esm4/loadnavm.hpp | 112 +++ components/esm4/loadnote.cpp | 86 +++ components/esm4/loadnote.hpp | 60 ++ components/esm4/loadnpc.cpp | 333 +++++++++ components/esm4/loadnpc.hpp | 230 ++++++ components/esm4/loadotft.cpp | 84 +++ components/esm4/loadotft.hpp | 60 ++ components/esm4/loadpack.cpp | 200 ++++++ components/esm4/loadpack.hpp | 110 +++ components/esm4/loadpgrd.cpp | 177 +++++ components/esm4/loadpgrd.hpp | 96 +++ components/esm4/loadpgre.cpp | 105 +++ components/esm4/loadpgre.hpp | 59 ++ components/esm4/loadpwat.cpp | 82 +++ components/esm4/loadpwat.hpp | 59 ++ components/esm4/loadqust.cpp | 179 +++++ components/esm4/loadqust.hpp | 84 +++ components/esm4/loadrace.cpp | 715 +++++++++++++++++++ components/esm4/loadrace.hpp | 174 +++++ components/esm4/loadrefr.cpp | 349 +++++++++ components/esm4/loadrefr.hpp | 119 ++++ components/esm4/loadregn.cpp | 164 +++++ components/esm4/loadregn.hpp | 98 +++ components/esm4/loadroad.cpp | 123 ++++ components/esm4/loadroad.hpp | 89 +++ components/esm4/loadsbsp.cpp | 78 ++ components/esm4/loadsbsp.hpp | 64 ++ components/esm4/loadscol.cpp | 84 +++ components/esm4/loadscol.hpp | 59 ++ components/esm4/loadscpt.cpp | 173 +++++ components/esm4/loadscpt.hpp | 59 ++ components/esm4/loadscrl.cpp | 99 +++ components/esm4/loadscrl.hpp | 68 ++ components/esm4/loadsgst.cpp | 118 ++++ components/esm4/loadsgst.hpp | 75 ++ components/esm4/loadslgm.cpp | 91 +++ components/esm4/loadslgm.hpp | 76 ++ components/esm4/loadsndr.cpp | 94 +++ components/esm4/loadsndr.hpp | 86 +++ components/esm4/loadsoun.cpp | 95 +++ components/esm4/loadsoun.hpp | 101 +++ components/esm4/loadstat.cpp | 101 +++ components/esm4/loadstat.hpp | 62 ++ components/esm4/loadtact.cpp | 89 +++ components/esm4/loadtact.hpp | 76 ++ components/esm4/loadterm.cpp | 101 +++ components/esm4/loadterm.hpp | 66 ++ components/esm4/loadtes4.cpp | 113 +++ components/esm4/loadtes4.hpp | 69 ++ components/esm4/loadtree.cpp | 85 +++ components/esm4/loadtree.hpp | 62 ++ components/esm4/loadtxst.cpp | 92 +++ components/esm4/loadtxst.hpp | 66 ++ components/esm4/loadweap.cpp | 188 +++++ components/esm4/loadweap.hpp | 96 +++ components/esm4/loadwrld.cpp | 205 ++++++ components/esm4/loadwrld.hpp | 137 ++++ components/esm4/reader.cpp | 639 +++++++++++++++++ components/esm4/reader.hpp | 298 ++++++++ components/esm4/records.hpp | 74 ++ components/esm4/reference.hpp | 68 ++ components/esm4/script.hpp | 384 ++++++++++ components/to_utf8/to_utf8.cpp | 40 +- components/to_utf8/to_utf8.hpp | 3 + components/translation/translation.cpp | 11 +- 179 files changed, 20848 insertions(+), 18 deletions(-) create mode 100644 components/esm/common.cpp create mode 100644 components/esm/common.hpp create mode 100644 components/esm/reader.cpp create mode 100644 components/esm/reader.hpp create mode 100644 components/esm4/acti.hpp create mode 100644 components/esm4/actor.hpp create mode 100644 components/esm4/common.cpp create mode 100644 components/esm4/common.hpp create mode 100644 components/esm4/dialogue.hpp create mode 100644 components/esm4/effect.hpp create mode 100644 components/esm4/formid.cpp create mode 100644 components/esm4/formid.hpp create mode 100644 components/esm4/inventory.hpp create mode 100644 components/esm4/lighting.hpp create mode 100644 components/esm4/loadachr.cpp create mode 100644 components/esm4/loadachr.hpp create mode 100644 components/esm4/loadacre.cpp create mode 100644 components/esm4/loadacre.hpp create mode 100644 components/esm4/loadacti.cpp create mode 100644 components/esm4/loadalch.cpp create mode 100644 components/esm4/loadalch.hpp create mode 100644 components/esm4/loadaloc.cpp create mode 100644 components/esm4/loadaloc.hpp create mode 100644 components/esm4/loadammo.cpp create mode 100644 components/esm4/loadammo.hpp create mode 100644 components/esm4/loadanio.cpp create mode 100644 components/esm4/loadanio.hpp create mode 100644 components/esm4/loadappa.cpp create mode 100644 components/esm4/loadappa.hpp create mode 100644 components/esm4/loadarma.cpp create mode 100644 components/esm4/loadarma.hpp create mode 100644 components/esm4/loadarmo.cpp create mode 100644 components/esm4/loadarmo.hpp create mode 100644 components/esm4/loadaspc.cpp create mode 100644 components/esm4/loadaspc.hpp create mode 100644 components/esm4/loadbook.cpp create mode 100644 components/esm4/loadbook.hpp create mode 100644 components/esm4/loadbptd.cpp create mode 100644 components/esm4/loadbptd.hpp create mode 100644 components/esm4/loadcell.cpp create mode 100644 components/esm4/loadcell.hpp create mode 100644 components/esm4/loadclas.cpp create mode 100644 components/esm4/loadclas.hpp create mode 100644 components/esm4/loadclfm.cpp create mode 100644 components/esm4/loadclfm.hpp create mode 100644 components/esm4/loadclot.cpp create mode 100644 components/esm4/loadclot.hpp create mode 100644 components/esm4/loadcont.cpp create mode 100644 components/esm4/loadcont.hpp create mode 100644 components/esm4/loadcrea.cpp create mode 100644 components/esm4/loadcrea.hpp create mode 100644 components/esm4/loaddial.cpp create mode 100644 components/esm4/loaddial.hpp create mode 100644 components/esm4/loaddobj.cpp create mode 100644 components/esm4/loaddobj.hpp create mode 100644 components/esm4/loaddoor.cpp create mode 100644 components/esm4/loaddoor.hpp create mode 100644 components/esm4/loadeyes.cpp create mode 100644 components/esm4/loadeyes.hpp create mode 100644 components/esm4/loadflor.cpp create mode 100644 components/esm4/loadflor.hpp create mode 100644 components/esm4/loadflst.cpp create mode 100644 components/esm4/loadflst.hpp create mode 100644 components/esm4/loadfurn.cpp create mode 100644 components/esm4/loadfurn.hpp create mode 100644 components/esm4/loadglob.cpp create mode 100644 components/esm4/loadglob.hpp create mode 100644 components/esm4/loadgras.cpp create mode 100644 components/esm4/loadgras.hpp create mode 100644 components/esm4/loadgrup.hpp create mode 100644 components/esm4/loadhair.cpp create mode 100644 components/esm4/loadhair.hpp create mode 100644 components/esm4/loadhdpt.cpp create mode 100644 components/esm4/loadhdpt.hpp create mode 100644 components/esm4/loadidle.cpp create mode 100644 components/esm4/loadidle.hpp create mode 100644 components/esm4/loadidlm.cpp create mode 100644 components/esm4/loadidlm.hpp create mode 100644 components/esm4/loadimod.cpp create mode 100644 components/esm4/loadimod.hpp create mode 100644 components/esm4/loadinfo.cpp create mode 100644 components/esm4/loadinfo.hpp create mode 100644 components/esm4/loadingr.cpp create mode 100644 components/esm4/loadingr.hpp create mode 100644 components/esm4/loadkeym.cpp create mode 100644 components/esm4/loadkeym.hpp create mode 100644 components/esm4/loadland.cpp create mode 100644 components/esm4/loadland.hpp create mode 100644 components/esm4/loadlgtm.cpp create mode 100644 components/esm4/loadlgtm.hpp create mode 100644 components/esm4/loadligh.cpp create mode 100644 components/esm4/loadligh.hpp create mode 100644 components/esm4/loadltex.cpp create mode 100644 components/esm4/loadltex.hpp create mode 100644 components/esm4/loadlvlc.cpp create mode 100644 components/esm4/loadlvlc.hpp create mode 100644 components/esm4/loadlvli.cpp create mode 100644 components/esm4/loadlvli.hpp create mode 100644 components/esm4/loadlvln.cpp create mode 100644 components/esm4/loadlvln.hpp create mode 100644 components/esm4/loadmato.cpp create mode 100644 components/esm4/loadmato.hpp create mode 100644 components/esm4/loadmisc.cpp create mode 100644 components/esm4/loadmisc.hpp create mode 100644 components/esm4/loadmset.cpp create mode 100644 components/esm4/loadmset.hpp create mode 100644 components/esm4/loadmstt.cpp create mode 100644 components/esm4/loadmstt.hpp create mode 100644 components/esm4/loadmusc.cpp create mode 100644 components/esm4/loadmusc.hpp create mode 100644 components/esm4/loadnavi.cpp create mode 100644 components/esm4/loadnavi.hpp create mode 100644 components/esm4/loadnavm.cpp create mode 100644 components/esm4/loadnavm.hpp create mode 100644 components/esm4/loadnote.cpp create mode 100644 components/esm4/loadnote.hpp create mode 100644 components/esm4/loadnpc.cpp create mode 100644 components/esm4/loadnpc.hpp create mode 100644 components/esm4/loadotft.cpp create mode 100644 components/esm4/loadotft.hpp create mode 100644 components/esm4/loadpack.cpp create mode 100644 components/esm4/loadpack.hpp create mode 100644 components/esm4/loadpgrd.cpp create mode 100644 components/esm4/loadpgrd.hpp create mode 100644 components/esm4/loadpgre.cpp create mode 100644 components/esm4/loadpgre.hpp create mode 100644 components/esm4/loadpwat.cpp create mode 100644 components/esm4/loadpwat.hpp create mode 100644 components/esm4/loadqust.cpp create mode 100644 components/esm4/loadqust.hpp create mode 100644 components/esm4/loadrace.cpp create mode 100644 components/esm4/loadrace.hpp create mode 100644 components/esm4/loadrefr.cpp create mode 100644 components/esm4/loadrefr.hpp create mode 100644 components/esm4/loadregn.cpp create mode 100644 components/esm4/loadregn.hpp create mode 100644 components/esm4/loadroad.cpp create mode 100644 components/esm4/loadroad.hpp create mode 100644 components/esm4/loadsbsp.cpp create mode 100644 components/esm4/loadsbsp.hpp create mode 100644 components/esm4/loadscol.cpp create mode 100644 components/esm4/loadscol.hpp create mode 100644 components/esm4/loadscpt.cpp create mode 100644 components/esm4/loadscpt.hpp create mode 100644 components/esm4/loadscrl.cpp create mode 100644 components/esm4/loadscrl.hpp create mode 100644 components/esm4/loadsgst.cpp create mode 100644 components/esm4/loadsgst.hpp create mode 100644 components/esm4/loadslgm.cpp create mode 100644 components/esm4/loadslgm.hpp create mode 100644 components/esm4/loadsndr.cpp create mode 100644 components/esm4/loadsndr.hpp create mode 100644 components/esm4/loadsoun.cpp create mode 100644 components/esm4/loadsoun.hpp create mode 100644 components/esm4/loadstat.cpp create mode 100644 components/esm4/loadstat.hpp create mode 100644 components/esm4/loadtact.cpp create mode 100644 components/esm4/loadtact.hpp create mode 100644 components/esm4/loadterm.cpp create mode 100644 components/esm4/loadterm.hpp create mode 100644 components/esm4/loadtes4.cpp create mode 100644 components/esm4/loadtes4.hpp create mode 100644 components/esm4/loadtree.cpp create mode 100644 components/esm4/loadtree.hpp create mode 100644 components/esm4/loadtxst.cpp create mode 100644 components/esm4/loadtxst.hpp create mode 100644 components/esm4/loadweap.cpp create mode 100644 components/esm4/loadweap.hpp create mode 100644 components/esm4/loadwrld.cpp create mode 100644 components/esm4/loadwrld.hpp create mode 100644 components/esm4/reader.cpp create mode 100644 components/esm4/reader.hpp create mode 100644 components/esm4/records.hpp create mode 100644 components/esm4/reference.hpp create mode 100644 components/esm4/script.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2d986660bc..3a7b1a43b8 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -76,7 +76,7 @@ add_component_dir (to_utf8 to_utf8 ) -add_component_dir(esm attr defs esmcommon records util luascripts) +add_component_dir(esm attr common defs esmcommon reader records util luascripts) add_component_dir (esm3 esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell @@ -94,6 +94,16 @@ add_component_dir (esm3terrain storage ) +add_component_dir (esm4 + loadachr loadacre loadacti loadalch loadaloc loadammo loadanio loadappa loadarma loadarmo loadaspc loadbook + loadbptd loadcell loadclas loadclfm loadclot common loadcont loadcrea loaddial loaddobj loaddoor loadeyes + loadflor loadflst formid loadfurn loadglob loadgras loadhair loadhdpt loadidle loadidlm loadimod loadinfo + loadingr loadkeym loadland loadlgtm loadligh loadltex loadlvlc loadlvli loadlvln loadmato loadmisc loadmset + loadmstt loadmusc loadnavi loadnavm loadnote loadnpc loadotft loadpack loadpgrd loadpgre loadpwat loadqust + loadrace loadrefr loadregn loadroad loadsbsp loadscol loadscpt loadscrl loadsgst loadslgm loadsndr + loadsoun loadstat loadtact loadterm loadtes4 loadtree loadtxst loadweap loadwrld reader +) + add_component_dir (misc constants utf8stream stringops resourcehelpers rng messageformatparser weakcache thread compression osguservalues errorMarker color diff --git a/components/esm/common.cpp b/components/esm/common.cpp new file mode 100644 index 0000000000..d04033edef --- /dev/null +++ b/components/esm/common.cpp @@ -0,0 +1,15 @@ +#include "sstream" + +namespace ESM +{ + 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); + } +} diff --git a/components/esm/common.hpp b/components/esm/common.hpp new file mode 100644 index 0000000000..3a8894e321 --- /dev/null +++ b/components/esm/common.hpp @@ -0,0 +1,57 @@ +#ifndef COMPONENT_ESM_COMMON_H +#define COMPONENT_ESM_COMMON_H + +#include +#include + +namespace ESM +{ +#pragma pack(push, 1) + union ESMVersion + { + float f; + std::uint32_t ui; + }; + + union TypeId + { + std::uint32_t value; + char name[4]; // record type in ascii + }; +#pragma pack(pop) + + enum ESMVersions + { + VER_120 = 0x3f99999a, // TES3 + VER_130 = 0x3fa66666, // TES3 + 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 + }; + + // Defines another files (esm or esp) that this file depends upon. + struct MasterData + { + std::string name; + std::uint64_t size; + }; + + enum VarType + { + VT_Unknown = 0, + VT_None, + VT_Short, // stored as a float, kinda + VT_Int, + VT_Long, // stored as a float + VT_Float, + VT_String + }; + + std::string printName(const std::uint32_t typeId); +} + +#endif // COMPONENT_ESM_COMMON_H diff --git a/components/esm/reader.cpp b/components/esm/reader.cpp new file mode 100644 index 0000000000..1f22b95b47 --- /dev/null +++ b/components/esm/reader.cpp @@ -0,0 +1,114 @@ +#include "reader.hpp" + +//#ifdef NDEBUG +//#undef NDEBUG +//#endif + +#include +#include + +#include + +#include "components/esm3/esmreader.hpp" +#include "components/esm4/reader.hpp" + +namespace ESM +{ + Reader* Reader::getReader(const std::string &filename) + { + Files::IStreamPtr esmStream(Files::openConstrainedFileStream (filename.c_str ())); + + std::uint32_t modVer = 0; // get the first 4 bytes of the record header only + esmStream->read((char*)&modVer, sizeof(modVer)); + if (esmStream->gcount() == sizeof(modVer)) + { + esmStream->seekg(0); + + if (modVer == ESM4::REC_TES4) + { + return new ESM4::Reader(esmStream, filename); + } + else + { + //return new ESM3::ESMReader(esmStream, filename); + } + } + + throw std::runtime_error("Unknown file format"); + } + + bool Reader::getStringImpl(std::string& str, std::size_t size, + Files::IStreamPtr filestream, ToUTF8::Utf8Encoder* encoder, bool hasNull) + { + std::size_t newSize = size; + + if (encoder) + { + if (!hasNull) + newSize += 1; // Utf8Encoder::getLength() expects a null terminator + + std::string tmp; + tmp.resize(newSize); + filestream->read(&tmp[0], size); + if ((std::size_t)filestream->gcount() == size) + { + if (hasNull) + { + assert (tmp[newSize - 1] == '\0' + && "ESM4::Reader::getString string is not terminated with a null"); + } + else + { + // NOTE: script text of some mods have terminating null; + // unfortunately we just have to deal with it + //if (tmp[newSize - 2] != '\0') + tmp[newSize - 1] = '\0'; // for Utf8Encoder::getLength() + } + + encoder->toUtf8(tmp, str, newSize - 1); + // NOTE: the encoder converts “keep” as “keep†which increases the length, + // which results in below resize() truncating the string + if (str.size() == newSize) // ascii + str.resize(newSize - 1); // don't want the null terminator + else + { + // this is a horrible hack but fortunately there's only a few like this + std::string tmp2(str.data()); + str.swap(tmp2); + } + + return true; + } + } + else + { + if (hasNull) + newSize -= 1; // don't read the null terminator yet + + str.resize(newSize); // assumed C++11 + filestream->read(&str[0], newSize); + if ((std::size_t)filestream->gcount() == newSize) + { + if (hasNull) + { + char ch; + filestream->read(&ch, 1); // read the null terminator + assert (ch == '\0' + && "ESM4::Reader::getString string is not terminated with a null"); + } +#if 0 + else + { + // NOTE: normal ESMs don't but omwsave has locals or spells with null terminator + assert (str[newSize - 1] != '\0' + && "ESM4::Reader::getString string is unexpectedly terminated with a null"); + } +#endif + return true; + } + } + + str.clear(); + return false; // FIXME: throw instead? + } +} diff --git a/components/esm/reader.hpp b/components/esm/reader.hpp new file mode 100644 index 0000000000..a5e3dd516b --- /dev/null +++ b/components/esm/reader.hpp @@ -0,0 +1,60 @@ +#ifndef COMPONENT_ESM_READER_H +#define COMPONENT_ESM_READER_H + +#include + +#include +#include + +#include "common.hpp" // MasterData + +namespace ToUTF8 +{ + class Utf8Encoder; +} + +namespace ESM +{ + class Reader + { + std::vector* mGlobalReaderList; + + public: + virtual ~Reader() {} + + static Reader* getReader(const std::string& filename); + + void setGlobalReaderList(std::vector *list) {mGlobalReaderList = list;} + std::vector *getGlobalReaderList() {return mGlobalReaderList;} + + virtual inline bool isEsm4() const = 0; + + virtual inline bool hasMoreRecs() const = 0; + + virtual inline void setEncoder(ToUTF8::Utf8Encoder* encoder) = 0; + + // used to check for dependencies e.g. CS::Editor::run() + virtual inline const std::vector& getGameFiles() const = 0; + + // used by ContentSelector::ContentModel::addFiles() + virtual inline const std::string getAuthor() const = 0; + virtual inline const std::string getDesc() const = 0; + virtual inline int getFormat() const = 0; + + virtual inline std::string getFileName() const = 0; + + // used by CSMWorld::Data::startLoading() and getTotalRecords() for loading progress bar + virtual inline int getRecordCount() const = 0; + + virtual void setModIndex(std::uint32_t index) = 0; + + // used by CSMWorld::Data::getTotalRecords() + virtual void close() = 0; + + protected: + bool getStringImpl(std::string& str, std::size_t size, + Files::IStreamPtr filestream, ToUTF8::Utf8Encoder* encoder, bool hasNull = false); + }; +} + +#endif // COMPONENT_ESM_READER_H diff --git a/components/esm4/acti.hpp b/components/esm4/acti.hpp new file mode 100644 index 0000000000..4ec3fe1e5e --- /dev/null +++ b/components/esm4/acti.hpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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 mScriptId; + FormId mLoopingSound; // SOUN + FormId mActivationSound; // SOUN + + float mBoundRadius; + + FormId mRadioTemplate; // SOUN + FormId mRadioStation; // TACT + + std::string mActivationPrompt; + + 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/components/esm4/actor.hpp b/components/esm4/actor.hpp new file mode 100644 index 0000000000..ae56a49564 --- /dev/null +++ b/components/esm4/actor.hpp @@ -0,0 +1,163 @@ +/* + Copyright (C) 2020 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_ACTOR_H +#define ESM4_ACTOR_H + +#include + +#include "formid.hpp" + +namespace ESM4 +{ +#pragma pack(push, 1) + 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 ACBS_TES4 + { + std::uint32_t flags; + std::uint16_t baseSpell; + std::uint16_t fatigue; + std::uint16_t barterGold; + std::int16_t levelOrOffset; + std::uint16_t calcMin; + std::uint16_t calcMax; + std::uint32_t padding1; + std::uint32_t padding2; + }; + + struct ACBS_FO3 + { + std::uint32_t flags; + std::uint16_t fatigue; + std::uint16_t barterGold; + std::int16_t levelOrMult; + std::uint16_t calcMinlevel; + std::uint16_t calcMaxlevel; + std::uint16_t speedMultiplier; + float karma; + std::int16_t dispositionBase; + std::uint16_t templateFlags; + }; + + struct ACBS_TES5 + { + std::uint32_t flags; + std::uint16_t magickaOffset; + std::uint16_t staminaOffset; + std::uint16_t levelOrMult; // TODO: check if int16_t + std::uint16_t calcMinlevel; + std::uint16_t calcMaxlevel; + std::uint16_t speedMultiplier; + std::uint16_t dispositionBase; // TODO: check if int16_t + std::uint16_t templateFlags; + std::uint16_t healthOffset; + std::uint16_t bleedoutOverride; + }; + + union ActorBaseConfig + { + ACBS_TES4 tes4; + ACBS_FO3 fo3; + ACBS_TES5 tes5; + }; + + struct ActorFaction + { + FormId faction; + std::int8_t rank; + std::uint8_t unknown1; + std::uint8_t unknown2; + std::uint8_t unknown3; + }; +#pragma pack(pop) + + struct BodyTemplate // TES5 + { + // 0x00000001 - Head + // 0x00000002 - Hair + // 0x00000004 - Body + // 0x00000008 - Hands + // 0x00000010 - Forearms + // 0x00000020 - Amulet + // 0x00000040 - Ring + // 0x00000080 - Feet + // 0x00000100 - Calves + // 0x00000200 - Shield + // 0x00000400 - Tail + // 0x00000800 - Long Hair + // 0x00001000 - Circlet + // 0x00002000 - Ears + // 0x00004000 - Body AddOn 3 + // 0x00008000 - Body AddOn 4 + // 0x00010000 - Body AddOn 5 + // 0x00020000 - Body AddOn 6 + // 0x00040000 - Body AddOn 7 + // 0x00080000 - Body AddOn 8 + // 0x00100000 - Decapitate Head + // 0x00200000 - Decapitate + // 0x00400000 - Body AddOn 9 + // 0x00800000 - Body AddOn 10 + // 0x01000000 - Body AddOn 11 + // 0x02000000 - Body AddOn 12 + // 0x04000000 - Body AddOn 13 + // 0x08000000 - Body AddOn 14 + // 0x10000000 - Body AddOn 15 + // 0x20000000 - Body AddOn 16 + // 0x40000000 - Body AddOn 17 + // 0x80000000 - FX01 + std::uint32_t bodyPart; + std::uint8_t flags; + std::uint8_t unknown1; // probably padding + std::uint8_t unknown2; // probably padding + std::uint8_t unknown3; // probably padding + std::uint32_t type; // 0 = light, 1 = heavy, 2 = none (cloth?) + }; +} + +#endif // ESM4_ACTOR_H diff --git a/components/esm4/common.cpp b/components/esm4/common.cpp new file mode 100644 index 0000000000..752660fd85 --- /dev/null +++ b/components/esm4/common.cpp @@ -0,0 +1,100 @@ +/* + Copyright (C) 2015-2016, 2018, 2021 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(); + } + + 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/components/esm4/common.hpp b/components/esm4/common.hpp new file mode 100644 index 0000000000..2f769c28c9 --- /dev/null +++ b/components/esm4/common.hpp @@ -0,0 +1,939 @@ +/* + Copyright (C) 2015-2020 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 + +#include "formid.hpp" + +// From ScummVM's endianness.h but for little endian +#ifndef MKTAG +#define MKTAG(a0,a1,a2,a3) ((std::uint32_t)((a0) | ((a1) << 8) | ((a2) << 16) | ((a3) << 24))) +#endif + +namespace ESM4 +{ + + // 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-levelled 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_NOTE = MKTAG('N','O','T','E'), // Note + 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_SCPT = MKTAG('S','C','P','T'), // Script + 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_TERM = MKTAG('T','E','R','M'), // Terminal + 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?) + REC_IMOD = MKTAG('I','M','O','D'), // Item Mod + REC_PWAT = MKTAG('P','W','A','T'), // Placeable Water + REC_SCOL = MKTAG('S','C','O','L'), // Static Collection + REC_CCRD = MKTAG('C','C','R','D'), // Caravan Card + REC_CMNY = MKTAG('C','M','N','Y'), // Caravan Money + REC_ALOC = MKTAG('A','L','O','C'), // Audio Location Controller + REC_MSET = MKTAG('M','S','E','T') // Media Set + }; + + 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_MTNM = MKTAG('M','T','N','M'), + SUB_UNES = MKTAG('U','N','E','S'), + SUB_TIND = MKTAG('T','I','N','D'), + SUB_TINL = MKTAG('T','I','N','L'), + SUB_TINP = MKTAG('T','I','N','P'), + SUB_TINT = MKTAG('T','I','N','T'), + SUB_TIRS = MKTAG('T','I','R','S'), + SUB_PHWT = MKTAG('P','H','W','T'), + SUB_AHCF = MKTAG('A','H','C','F'), + SUB_AHCM = MKTAG('A','H','C','M'), + SUB_HEAD = MKTAG('H','E','A','D'), + SUB_MPAI = MKTAG('M','P','A','I'), + SUB_MPAV = MKTAG('M','P','A','V'), + SUB_DFTF = MKTAG('D','F','T','F'), + SUB_DFTM = MKTAG('D','F','T','M'), + SUB_FLMV = MKTAG('F','L','M','V'), + SUB_FTSF = MKTAG('F','T','S','F'), + SUB_FTSM = MKTAG('F','T','S','M'), + SUB_MTYP = MKTAG('M','T','Y','P'), + SUB_PHTN = MKTAG('P','H','T','N'), + SUB_RNMV = MKTAG('R','N','M','V'), + SUB_RPRF = MKTAG('R','P','R','F'), + SUB_RPRM = MKTAG('R','P','R','M'), + SUB_SNMV = MKTAG('S','N','M','V'), + SUB_SPED = MKTAG('S','P','E','D'), + SUB_SWMV = MKTAG('S','W','M','V'), + SUB_WKMV = MKTAG('W','K','M','V'), + SUB_LLCT = MKTAG('L','L','C','T'), + SUB_IDLF = MKTAG('I','D','L','F'), + SUB_IDLA = MKTAG('I','D','L','A'), + SUB_IDLC = MKTAG('I','D','L','C'), + SUB_IDLT = MKTAG('I','D','L','T'), + SUB_DODT = MKTAG('D','O','D','T'), + SUB_TX00 = MKTAG('T','X','0','0'), + SUB_TX01 = MKTAG('T','X','0','1'), + SUB_TX02 = MKTAG('T','X','0','2'), + SUB_TX03 = MKTAG('T','X','0','3'), + SUB_TX04 = MKTAG('T','X','0','4'), + SUB_TX05 = MKTAG('T','X','0','5'), + SUB_TX06 = MKTAG('T','X','0','6'), + SUB_TX07 = MKTAG('T','X','0','7'), + SUB_BPND = MKTAG('B','P','N','D'), + SUB_BPTN = MKTAG('B','P','T','N'), + SUB_BPNN = MKTAG('B','P','N','N'), + SUB_BPNT = MKTAG('B','P','N','T'), + SUB_BPNI = MKTAG('B','P','N','I'), + SUB_RAGA = MKTAG('R','A','G','A'), + + SUB_QSTI = MKTAG('Q','S','T','I'), + SUB_QSTR = MKTAG('Q','S','T','R'), + SUB_QSDT = MKTAG('Q','S','D','T'), + SUB_SCDA = MKTAG('S','C','D','A'), + SUB_SCRO = MKTAG('S','C','R','O'), + SUB_QSTA = MKTAG('Q','S','T','A'), + SUB_CTDT = MKTAG('C','T','D','T'), + SUB_SCHD = MKTAG('S','C','H','D'), + SUB_TCLF = MKTAG('T','C','L','F'), + SUB_TCLT = MKTAG('T','C','L','T'), + SUB_TRDT = MKTAG('T','R','D','T'), + SUB_TPIC = MKTAG('T','P','I','C'), + + SUB_PKDT = MKTAG('P','K','D','T'), + SUB_PSDT = MKTAG('P','S','D','T'), + SUB_PLDT = MKTAG('P','L','D','T'), + SUB_PTDT = MKTAG('P','T','D','T'), + SUB_PGRP = MKTAG('P','G','R','P'), + SUB_PGRR = MKTAG('P','G','R','R'), + SUB_PGRI = MKTAG('P','G','R','I'), + SUB_PGRL = MKTAG('P','G','R','L'), + SUB_PGAG = MKTAG('P','G','A','G'), + SUB_FLTV = MKTAG('F','L','T','V'), + + SUB_XHLT = MKTAG('X','H','L','T'), // Unofficial Oblivion Patch + SUB_XCHG = MKTAG('X','C','H','G'), // thievery.exp + + SUB_ITXT = MKTAG('I','T','X','T'), + SUB_MO5T = MKTAG('M','O','5','T'), + SUB_MOD5 = MKTAG('M','O','D','5'), + SUB_MDOB = MKTAG('M','D','O','B'), + SUB_SPIT = MKTAG('S','P','I','T'), + SUB_PTDA = MKTAG('P','T','D','A'), // TES5 + SUB_PFOR = MKTAG('P','F','O','R'), // TES5 + SUB_PFO2 = MKTAG('P','F','O','2'), // TES5 + SUB_PRCB = MKTAG('P','R','C','B'), // TES5 + SUB_PKCU = MKTAG('P','K','C','U'), // TES5 + SUB_PKC2 = MKTAG('P','K','C','2'), // TES5 + SUB_CITC = MKTAG('C','I','T','C'), // TES5 + SUB_CIS1 = MKTAG('C','I','S','1'), // TES5 + SUB_CIS2 = MKTAG('C','I','S','2'), // TES5 + SUB_TIFC = MKTAG('T','I','F','C'), // TES5 + SUB_ALCA = MKTAG('A','L','C','A'), // TES5 + SUB_ALCL = MKTAG('A','L','C','L'), // TES5 + SUB_ALCO = MKTAG('A','L','C','O'), // TES5 + SUB_ALDN = MKTAG('A','L','D','N'), // TES5 + SUB_ALEA = MKTAG('A','L','E','A'), // TES5 + SUB_ALED = MKTAG('A','L','E','D'), // TES5 + SUB_ALEQ = MKTAG('A','L','E','Q'), // TES5 + SUB_ALFA = MKTAG('A','L','F','A'), // TES5 + SUB_ALFC = MKTAG('A','L','F','C'), // TES5 + SUB_ALFD = MKTAG('A','L','F','D'), // TES5 + SUB_ALFE = MKTAG('A','L','F','E'), // TES5 + SUB_ALFI = MKTAG('A','L','F','I'), // TES5 + SUB_ALFL = MKTAG('A','L','F','L'), // TES5 + SUB_ALFR = MKTAG('A','L','F','R'), // TES5 + SUB_ALID = MKTAG('A','L','I','D'), // TES5 + SUB_ALLS = MKTAG('A','L','L','S'), // TES5 + SUB_ALNA = MKTAG('A','L','N','A'), // TES5 + SUB_ALNT = MKTAG('A','L','N','T'), // TES5 + SUB_ALPC = MKTAG('A','L','P','C'), // TES5 + SUB_ALRT = MKTAG('A','L','R','T'), // TES5 + SUB_ALSP = MKTAG('A','L','S','P'), // TES5 + SUB_ALST = MKTAG('A','L','S','T'), // TES5 + SUB_ALUA = MKTAG('A','L','U','A'), // TES5 + SUB_FLTR = MKTAG('F','L','T','R'), // TES5 + SUB_QTGL = MKTAG('Q','T','G','L'), // TES5 + SUB_TWAT = MKTAG('T','W','A','T'), // TES5 + 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_NEXT = MKTAG('N','E','X','T'), // FO3 + SUB_QOBJ = MKTAG('Q','O','B','J'), // FO3 + SUB_POBA = MKTAG('P','O','B','A'), // FO3 + SUB_POCA = MKTAG('P','O','C','A'), // FO3 + SUB_POEA = MKTAG('P','O','E','A'), // FO3 + SUB_PKDD = MKTAG('P','K','D','D'), // FO3 + SUB_PKD2 = MKTAG('P','K','D','2'), // FO3 + SUB_PKPT = MKTAG('P','K','P','T'), // FO3 + SUB_PKED = MKTAG('P','K','E','D'), // FO3 + SUB_PKE2 = MKTAG('P','K','E','2'), // FO3 + SUB_PKAM = MKTAG('P','K','A','M'), // FO3 + SUB_PUID = MKTAG('P','U','I','D'), // FO3 + SUB_PKW3 = MKTAG('P','K','W','3'), // FO3 + SUB_PTD2 = MKTAG('P','T','D','2'), // FO3 + SUB_PLD2 = MKTAG('P','L','D','2'), // FO3 + SUB_PKFD = MKTAG('P','K','F','D'), // FO3 + SUB_IDLB = MKTAG('I','D','L','B'), // FO3 + SUB_XDCR = MKTAG('X','D','C','R'), // FO3 + SUB_DALC = MKTAG('D','A','L','C'), // 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_INFC = MKTAG('I','N','F','C'), // FONV + SUB_INFX = MKTAG('I','N','F','X'), // FONV + SUB_TDUM = MKTAG('T','D','U','M'), // FONV + SUB_TCFU = MKTAG('T','C','F','U'), // 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 + SUB_JNAM = MKTAG('J','N','A','M'), // FONV + SUB_EFSD = MKTAG('E','F','S','D'), // FONV DeadMoney + }; + + 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_VisDistant = 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 + }; + +#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) + }; + + 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 Vertex + { + float x; + float y; + float z; + }; +#pragma pack(pop) + + // For pretty printing GroupHeader labels + std::string printLabel(const GroupLabel& label, const std::uint32_t type); + + void gridToString(std::int16_t x, std::int16_t y, std::string& str); +} + +#endif // ESM4_COMMON_H diff --git a/components/esm4/dialogue.hpp b/components/esm4/dialogue.hpp new file mode 100644 index 0000000000..e77d192901 --- /dev/null +++ b/components/esm4/dialogue.hpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2020 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_DIALOGUE_H +#define ESM4_DIALOGUE_H + +namespace ESM4 +{ + enum DialType + { + DTYP_Topic = 0, + DTYP_Conversation = 1, + DTYP_Combat = 2, + DTYP_Persuation = 3, + DTYP_Detection = 4, + DTYP_Service = 5, + DTYP_Miscellaneous = 6, + // below FO3/FONV + DTYP_Radio = 7 + }; +} + +#endif // ESM4_DIALOGUE_H diff --git a/components/esm4/effect.hpp b/components/esm4/effect.hpp new file mode 100644 index 0000000000..8580a478dc --- /dev/null +++ b/components/esm4/effect.hpp @@ -0,0 +1,56 @@ +/* + Copyright (C) 2020 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_EFFECT_H +#define ESM4_EFFECT_H + +#include + +#include "formid.hpp" + +namespace ESM4 +{ +#pragma pack(push, 1) + 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) +} + +#endif // ESM4_EFFECT_H diff --git a/components/esm4/formid.cpp b/components/esm4/formid.cpp new file mode 100644 index 0000000000..f5493fd0ca --- /dev/null +++ b/components/esm4/formid.cpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2016, 2020-2021 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 // LONG_MIN, LONG_MAX for gcc + +#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 == (unsigned long)LONG_MIN || val == (unsigned long)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/components/esm4/formid.hpp b/components/esm4/formid.hpp new file mode 100644 index 0000000000..6bb2c475e3 --- /dev/null +++ b/components/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/components/esm4/inventory.hpp b/components/esm4/inventory.hpp new file mode 100644 index 0000000000..31181ce33c --- /dev/null +++ b/components/esm4/inventory.hpp @@ -0,0 +1,55 @@ +/* + Copyright (C) 2020 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_INVENTORY_H +#define ESM4_INVENTORY_H + +#include + +#include "formid.hpp" + +namespace ESM4 +{ +#pragma pack(push, 1) + // 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; + }; +#pragma pack(pop) +} + +#endif // ESM4_INVENTORY_H diff --git a/components/esm4/lighting.hpp b/components/esm4/lighting.hpp new file mode 100644 index 0000000000..42e76eceee --- /dev/null +++ b/components/esm4/lighting.hpp @@ -0,0 +1,79 @@ +/* + Copyright (C) 2020 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_LIGHTING_H +#define ESM4_LIGHTING_H + +#include + +namespace ESM4 +{ +#pragma pack(push, 1) + // guesses only for TES4 + 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 fogColor; // | 1D 1B 16 00 (RGBA) + float fogNear; // Fog Near | 00 00 00 00 = 0.f + float fogFar; // Fog Far | 00 80 3B 45 = 3000.f + std::int32_t rotationXY; // rotation xy | 00 00 00 00 = 0 + std::int32_t rotationZ; // rotation z | 00 00 00 00 = 0 + float fogDirFade; // Fog dir fade | 00 00 80 3F = 1.f + float fogClipDist; // Fog clip dist | 00 80 3B 45 = 3000.f + float fogPower; + }; + + struct Lighting_TES5 + { + std::uint32_t ambient; + std::uint32_t directional; + std::uint32_t fogColor; + float fogNear; + float fogFar; + std::int32_t rotationXY; + std::int32_t rotationZ; + float fogDirFade; + float fogClipDist; + float fogPower; + std::uint32_t unknown1; + std::uint32_t unknown2; + std::uint32_t unknown3; + std::uint32_t unknown4; + std::uint32_t unknown5; + std::uint32_t unknown6; + std::uint32_t unknown7; + std::uint32_t unknown8; + std::uint32_t fogColorFar; + float fogMax; + float LightFadeStart; + float LightFadeEnd; + std::uint32_t padding; + }; +#pragma pack(pop) +} + +#endif // ESM4_LIGHTING_H diff --git a/components/esm4/loadachr.cpp b/components/esm4/loadachr.cpp new file mode 100644 index 0000000000..fe03fcb59e --- /dev/null +++ b/components/esm4/loadachr.cpp @@ -0,0 +1,124 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 "loadachr.hpp" + +#include +//#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::ActorCharacter::ActorCharacter() : mFormId(0), mFlags(0), mBaseObj(0), + mScale(1.f), mOwner(0), mGlobal(0), mInitiallyDisabled(false) +{ + 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; + mParent = reader.currCell(); // NOTE: only for persistent achr? (aren't they all persistent?) + + 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(mPlacement); 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_XRGB: // ragdoll biped + { + //std::cout << "ACHR " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + 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: // levelled 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_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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::ACHR::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::ActorCharacter::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::ActorCharacter::blank() +//{ +//} diff --git a/components/esm4/loadachr.hpp b/components/esm4/loadachr.hpp new file mode 100644 index 0000000000..a8656ea926 --- /dev/null +++ b/components/esm4/loadachr.hpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 + +#include "reference.hpp" // FormId, Placement, EnableParent + +namespace ESM4 +{ + class Reader; + class Writer; + + struct ActorCharacter + { + FormId mParent; // cell formId, from the loading sequence + // NOTE: for exterior cells it will be the dummy cell FormId + + 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; + + Placement mPlacement; + float mScale; // default 1.f + FormId mOwner; + FormId mGlobal; + + bool mInitiallyDisabled; // TODO may need to check mFlags & 0x800 (initially disabled) + + EnableParent mEsp; + + 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/components/esm4/loadacre.cpp b/components/esm4/loadacre.cpp new file mode 100644 index 0000000000..3510986d3c --- /dev/null +++ b/components/esm4/loadacre.cpp @@ -0,0 +1,106 @@ +/* + Copyright (C) 2016, 2018, 2020 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 "loadacre.hpp" + +#include +//#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::ActorCreature::ActorCreature() : mFormId(0), mFlags(0), mBaseObj(0), mScale(1.f), + mOwner(0), mGlobal(0), mFactionRank(0), mInitiallyDisabled(false) +{ + 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(mPlacement); 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_XRGB: // ragdoll biped + { + // seems to occur only for dead bodies, e.g. DeadMuffy, DeadDogVicious + //std::cout << "ACRE " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + case ESM4::SUB_XLKR: // FO3 + case ESM4::SUB_XLCM: // FO3 + case ESM4::SUB_XEZN: // FO3 + case ESM4::SUB_XMRC: // FO3 + case ESM4::SUB_XAPD: // FO3 + case ESM4::SUB_XAPR: // FO3 + case ESM4::SUB_XRDS: // FO3 + case ESM4::SUB_XPRD: // FO3 + case ESM4::SUB_XATO: // FONV + { + //std::cout << "ACRE " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::ACRE::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::ActorCreature::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::ActorCreature::blank() +//{ +//} diff --git a/components/esm4/loadacre.hpp b/components/esm4/loadacre.hpp new file mode 100644 index 0000000000..26cab34a4e --- /dev/null +++ b/components/esm4/loadacre.hpp @@ -0,0 +1,67 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "reference.hpp" // FormId, Placement, 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; + + Placement mPlacement; + float mScale; // default 1.f + FormId mOwner; + FormId mGlobal; + std::uint32_t mFactionRank; + + bool mInitiallyDisabled; // TODO may need to check mFlags & 0x800 (initially disabled) + + EnableParent mEsp; + + 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/components/esm4/loadacti.cpp b/components/esm4/loadacti.cpp new file mode 100644 index 0000000000..abbe901014 --- /dev/null +++ b/components/esm4/loadacti.cpp @@ -0,0 +1,103 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 // FIXME + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Activator::Activator() : mFormId(0), mFlags(0), mScriptId(0), mLoopingSound(0), mActivationSound(0), + mBoundRadius(0.f), mRadioTemplate(0), mRadioStation(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + + mActivationPrompt.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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); break; + case ESM4::SUB_SNAM: reader.getFormId(mLoopingSound); break; + case ESM4::SUB_VNAM: reader.getFormId(mActivationSound); break; + case ESM4::SUB_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_INAM: reader.getFormId(mRadioTemplate); break; // FONV + case ESM4::SUB_RNAM: reader.getFormId(mRadioStation); break; + case ESM4::SUB_XATO: reader.getZString(mActivationPrompt); break; // FONV + 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_VMAD: + case ESM4::SUB_WNAM: + { + //std::cout << "ACTI " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::ACTI::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Activator::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Activator::blank() +//{ +//} diff --git a/components/esm4/loadalch.cpp b/components/esm4/loadalch.cpp new file mode 100644 index 0000000000..b720b740ca --- /dev/null +++ b/components/esm4/loadalch.cpp @@ -0,0 +1,121 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 "loadalch.hpp" + +#include +#include +//#include // FIXME + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Potion::Potion() : mFormId(0), mFlags(0), mPickUpSound(0), mDropSound(0), mScriptId(0), mBoundRadius(0.f) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mIcon.clear(); + mMiniIcon.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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_ICON: reader.getZString(mIcon); break; + case ESM4::SUB_MICO: reader.getZString(mMiniIcon); break; // FO3 + case ESM4::SUB_DATA: reader.get(mData); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); break; + case ESM4::SUB_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_SCIT: + { + reader.get(mEffect); + reader.adjustFormId(mEffect.formId); + break; + } + case ESM4::SUB_ENIT: + { + if (subHdr.dataSize == 8) // TES4 + { + reader.get(&mItem, 8); + mItem.withdrawl = 0; + mItem.sound = 0; + break; + } + + reader.get(mItem); + reader.adjustFormId(mItem.withdrawl); + reader.adjustFormId(mItem.sound); + break; + } + case ESM4::SUB_YNAM: reader.getFormId(mPickUpSound); break; + case ESM4::SUB_ZNAM: reader.getFormId(mDropSound); break; + case ESM4::SUB_MODT: + 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_ETYP: // FO3 + { + //std::cout << "ALCH " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::ALCH::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Potion::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Potion::blank() +//{ +//} diff --git a/components/esm4/loadalch.hpp b/components/esm4/loadalch.hpp new file mode 100644 index 0000000000..e6680dc93b --- /dev/null +++ b/components/esm4/loadalch.hpp @@ -0,0 +1,88 @@ +/* + Copyright (C) 2016, 2018, 2020 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 +#include + +#include "effect.hpp" // FormId, ScriptEffect + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Potion + { +#pragma pack(push, 1) + struct Data + { + float weight; + }; + + struct EnchantedItem + { + std::int32_t value; + std::uint32_t flags; + FormId withdrawl; + float chanceAddition; + FormId sound; + }; +#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 mMiniIcon; // inventory + + FormId mPickUpSound; + FormId mDropSound; + + FormId mScriptId; + ScriptEffect mEffect; + + float mBoundRadius; + + Data mData; + EnchantedItem mItem; + + 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/components/esm4/loadaloc.cpp b/components/esm4/loadaloc.cpp new file mode 100644 index 0000000000..a84e56592d --- /dev/null +++ b/components/esm4/loadaloc.cpp @@ -0,0 +1,172 @@ +/* + Copyright (C) 2020-2021 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 "loadaloc.hpp" + +#include +#include +//#include // FIXME: for debugging only +//#include // FIXME: for debugging only + +//#include // FIXME + +//#include "formid.hpp" // FIXME: + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::MediaLocationController::MediaLocationController() : mFormId(0), mFlags(0), + mConditionalFaction(0), mLocationDelay(0.f), mRetriggerDelay(0.f), mDayStart(0), mNightStart(0) +{ + mEditorId.clear(); + mFullName.clear(); + + std::memset(&mMediaFlags, 0, sizeof(MLC_Flags)); +} + +ESM4::MediaLocationController::~MediaLocationController() +{ +} + +void ESM4::MediaLocationController::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_GNAM: + { + FormId id; + reader.getFormId(id); + mBattleSets.push_back(id); + + break; + } + case ESM4::SUB_LNAM: + { + FormId id; + reader.getFormId(id); + mLocationSets.push_back(id); + + break; + } + case ESM4::SUB_YNAM: + { + FormId id; + reader.getFormId(id); + mEnemySets.push_back(id); + + break; + } + case ESM4::SUB_HNAM: + { + FormId id; + reader.getFormId(id); + mNeutralSets.push_back(id); + + break; + } + case ESM4::SUB_XNAM: + { + FormId id; + reader.getFormId(id); + mFriendSets.push_back(id); + + break; + } + case ESM4::SUB_ZNAM: + { + FormId id; + reader.getFormId(id); + mAllySets.push_back(id); + + break; + } + case ESM4::SUB_RNAM: reader.getFormId(mConditionalFaction); break; + case ESM4::SUB_NAM1: + { + reader.get(mMediaFlags); + std::uint8_t flags = mMediaFlags.loopingOptions; + mMediaFlags.loopingOptions = (flags & 0xF0) >> 4; + mMediaFlags.factionNotFound = flags & 0x0F; // WARN: overwriting data + break; + } + case ESM4::SUB_NAM4: reader.get(mLocationDelay); break; + case ESM4::SUB_NAM7: reader.get(mRetriggerDelay); break; + case ESM4::SUB_NAM5: reader.get(mDayStart); break; + case ESM4::SUB_NAM6: reader.get(mNightStart); break; + case ESM4::SUB_NAM2: // always 0? 4 bytes + case ESM4::SUB_NAM3: // always 0? 4 bytes + case ESM4::SUB_FNAM: // always 0? 4 bytes + { +#if 0 + boost::scoped_array mDataBuf(new unsigned char[subHdr.dataSize]); + reader.get(&mDataBuf[0], subHdr.dataSize); + + std::ostringstream ss; + ss << mEditorId << " " << ESM::printName(subHdr.typeId) << ":size " << subHdr.dataSize << "\n"; + for (std::size_t i = 0; i < subHdr.dataSize; ++i) + { + //if (mDataBuf[i] > 64 && mDataBuf[i] < 91) // looks like printable ascii char + //ss << (char)(mDataBuf[i]) << " "; + //else + ss << std::setfill('0') << std::setw(2) << std::hex << (int)(mDataBuf[i]); + if ((i & 0x000f) == 0xf) // wrap around + ss << "\n"; + else if (i < subHdr.dataSize-1) + ss << " "; + } + std::cout << ss.str() << std::endl; +#else + //std::cout << "ALOC " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); +#endif + break; + } + default: + //std::cout << "ALOC " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + //reader.skipSubRecordData(); + throw std::runtime_error("ESM4::ALOC::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::MediaLocationController::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::MediaLocationController::blank() +//{ +//} diff --git a/components/esm4/loadaloc.hpp b/components/esm4/loadaloc.hpp new file mode 100644 index 0000000000..1c2d17d11e --- /dev/null +++ b/components/esm4/loadaloc.hpp @@ -0,0 +1,88 @@ +/* + Copyright (C) 2020 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_ALOC_H +#define ESM4_ALOC_H + +#include +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + +#pragma pack(push, 1) + struct MLC_Flags + { + // use day/night transition: 0 = loop, 1 = random, 2 = retrigger, 3 = none + // use defaults (6:00/23:54): 4 = loop, 5 = random, 6 = retrigger, 7 = none + std::uint8_t loopingOptions; + // 0 = neutral, 1 = enemy, 2 = ally, 3 = friend, 4 = location, 5 = none + std::uint8_t factionNotFound; // WARN: overwriting whatever is in this + std::uint16_t unknown; // padding? + }; +#pragma pack(pop) + + struct MediaLocationController + { + 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::vector mBattleSets; + std::vector mLocationSets; + std::vector mEnemySets; + std::vector mNeutralSets; + std::vector mFriendSets; + std::vector mAllySets; + + MLC_Flags mMediaFlags; + + FormId mConditionalFaction; + + float mLocationDelay; + float mRetriggerDelay; + + std::uint32_t mDayStart; + std::uint32_t mNightStart; + + MediaLocationController(); + virtual ~MediaLocationController(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_ALOC_H diff --git a/components/esm4/loadammo.cpp b/components/esm4/loadammo.cpp new file mode 100644 index 0000000000..7ccf8e1582 --- /dev/null +++ b/components/esm4/loadammo.cpp @@ -0,0 +1,133 @@ +/* + Copyright (C) 2016, 2018-2021 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 "loadammo.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Ammunition::Ammunition() : mFormId(0), mFlags(0), mPickUpSound(0), mDropSound(0), mBoundRadius(0.f) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mText.clear(); + mIcon.clear(); + mMiniIcon.clear(); +} + +ESM4::Ammunition::~Ammunition() +{ +} + +void ESM4::Ammunition::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 == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_DATA: + { + //if (reader.esmVersion() == ESM::VER_094 || reader.esmVersion() == ESM::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_MICO: reader.getZString(mMiniIcon); break; // FO3 + 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_DESC: reader.getLocalizedString(mText); break; + case ESM4::SUB_YNAM: reader.getFormId(mPickUpSound); break; + case ESM4::SUB_ZNAM: reader.getFormId(mDropSound); break; + case ESM4::SUB_MODT: + case ESM4::SUB_OBND: + case ESM4::SUB_KSIZ: + case ESM4::SUB_KWDA: + 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::AMMO::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Ammunition::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Ammunition::blank() +//{ +//} diff --git a/components/esm4/loadammo.hpp b/components/esm4/loadammo.hpp new file mode 100644 index 0000000000..8ff9cd9390 --- /dev/null +++ b/components/esm4/loadammo.hpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2016, 2018-2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Ammunition + { + 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 mText; + std::string mIcon; // inventory + std::string mMiniIcon; // inventory + + FormId mPickUpSound; + FormId mDropSound; + + float mBoundRadius; + + std::uint16_t mEnchantmentPoints; + FormId mEnchantment; + + Data mData; + + Ammunition(); + virtual ~Ammunition(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_AMMO_H diff --git a/components/esm4/loadanio.cpp b/components/esm4/loadanio.cpp new file mode 100644 index 0000000000..b6b97a1a47 --- /dev/null +++ b/components/esm4/loadanio.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 "loadanio.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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::ANIO::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::AnimObject::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::AnimObject::blank() +//{ +//} diff --git a/components/esm4/loadanio.hpp b/components/esm4/loadanio.hpp new file mode 100644 index 0000000000..4259abe7b3 --- /dev/null +++ b/components/esm4/loadanio.hpp @@ -0,0 +1,63 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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/components/esm4/loadappa.cpp b/components/esm4/loadappa.cpp new file mode 100644 index 0000000000..7ad7635927 --- /dev/null +++ b/components/esm4/loadappa.cpp @@ -0,0 +1,106 @@ +/* + Copyright (C) 2016, 2018-2021 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 "loadappa.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Apparatus::Apparatus() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScriptId(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mText.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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_DATA: + { + if (reader.esmVersion() == ESM::VER_094 || reader.esmVersion() == ESM::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(mScriptId); break; + case ESM4::SUB_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_DESC: reader.getLocalizedString(mText); break; + case ESM4::SUB_MODT: + case ESM4::SUB_OBND: + case ESM4::SUB_QUAL: + { + //std::cout << "APPA " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::APPA::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Apparatus::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Apparatus::blank() +//{ +//} diff --git a/components/esm4/loadappa.hpp b/components/esm4/loadappa.hpp new file mode 100644 index 0000000000..1723c969ee --- /dev/null +++ b/components/esm4/loadappa.hpp @@ -0,0 +1,75 @@ +/* + Copyright (C) 2016, 2018-2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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 mText; + std::string mIcon; // inventory + + float mBoundRadius; + + FormId mScriptId; + + 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/components/esm4/loadarma.cpp b/components/esm4/loadarma.cpp new file mode 100644 index 0000000000..8b29000bb3 --- /dev/null +++ b/components/esm4/loadarma.cpp @@ -0,0 +1,151 @@ +/* + Copyright (C) 2019, 2020 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 "loadarma.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::ArmorAddon::ArmorAddon() : mFormId(0), mFlags(0), mTextureMale(0), mTextureFemale(0), + mRacePrimary(0) +{ + mEditorId.clear(); + + mModelMale.clear(); + mModelFemale.clear(); +} + +ESM4::ArmorAddon::~ArmorAddon() +{ +} + +void ESM4::ArmorAddon::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_MOD2: reader.getZString(mModelMale); break; + case ESM4::SUB_MOD3: reader.getZString(mModelFemale); break; + case ESM4::SUB_MOD4: + case ESM4::SUB_MOD5: + { + std::string model; + reader.getZString(model); + + //std::cout << mEditorId << " " << ESM::printName(subHdr.typeId) << " " << model << std::endl; + + break; + } + case ESM4::SUB_NAM0: reader.getFormId(mTextureMale); break; + case ESM4::SUB_NAM1: reader.getFormId(mTextureFemale); break; + case ESM4::SUB_RNAM: reader.getFormId(mRacePrimary); break; + case ESM4::SUB_MODL: + { + if ((esmVer == ESM::VER_094 || esmVer == ESM::VER_170) && subHdr.dataSize == 4) // TES5 + { + FormId formId; + reader.getFormId(formId); + mRaces.push_back(formId); + } + else + reader.skipSubRecordData(); // FIXME: this should be mModelMale for FO3/FONV + + break; + } + case ESM4::SUB_BODT: // body template + { + reader.get(mBodyTemplate.bodyPart); + reader.get(mBodyTemplate.flags); + reader.get(mBodyTemplate.unknown1); // probably padding + reader.get(mBodyTemplate.unknown2); // probably padding + reader.get(mBodyTemplate.unknown3); // probably padding + reader.get(mBodyTemplate.type); + + break; + } + case ESM4::SUB_BOD2: // TES5 + { + reader.get(mBodyTemplate.bodyPart); + mBodyTemplate.flags = 0; + mBodyTemplate.unknown1 = 0; // probably padding + mBodyTemplate.unknown2 = 0; // probably padding + mBodyTemplate.unknown3 = 0; // probably padding + reader.get(mBodyTemplate.type); + + break; + } + case ESM4::SUB_DNAM: + case ESM4::SUB_MO2T: // FIXME: should group with MOD2 + case ESM4::SUB_MO2S: // FIXME: should group with MOD2 + case ESM4::SUB_MO3T: // FIXME: should group with MOD3 + case ESM4::SUB_MO3S: // FIXME: should group with MOD3 + case ESM4::SUB_MOSD: // FO3 // FIXME: should group with MOD3 + case ESM4::SUB_MO4T: // FIXME: should group with MOD4 + case ESM4::SUB_MO4S: // FIXME: should group with MOD4 + case ESM4::SUB_MO5T: + case ESM4::SUB_NAM2: // txst formid male + case ESM4::SUB_NAM3: // txst formid female + case ESM4::SUB_SNDD: // footset sound formid + case ESM4::SUB_BMDT: // FO3 + case ESM4::SUB_DATA: // FO3 + case ESM4::SUB_ETYP: // FO3 + case ESM4::SUB_FULL: // FO3 + case ESM4::SUB_ICO2: // FO3 // female + case ESM4::SUB_ICON: // FO3 // male + case ESM4::SUB_MODT: // FO3 // FIXME: should group with MODL + case ESM4::SUB_MODS: // FO3 // FIXME: should group with MODL + case ESM4::SUB_MODD: // FO3 // FIXME: should group with MODL + case ESM4::SUB_OBND: // FO3 + { + //std::cout << "ARMA " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::ARMA::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::ArmorAddon::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::ArmorAddon::blank() +//{ +//} diff --git a/components/esm4/loadarma.hpp b/components/esm4/loadarma.hpp new file mode 100644 index 0000000000..59c4550b7f --- /dev/null +++ b/components/esm4/loadarma.hpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2019, 2020 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_ARMA_H +#define ESM4_ARMA_H + +#include +#include +#include + +#include "formid.hpp" +#include "actor.hpp" // BodyTemplate + +namespace ESM4 +{ + class Reader; + class Writer; + + struct ArmorAddon + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + std::string mModelMale; + std::string mModelFemale; + + FormId mTextureMale; + FormId mTextureFemale; + + FormId mRacePrimary; + std::vector mRaces; // TES5 only + + BodyTemplate mBodyTemplate; // TES5 + + ArmorAddon(); + virtual ~ArmorAddon(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_ARMA_H diff --git a/components/esm4/loadarmo.cpp b/components/esm4/loadarmo.cpp new file mode 100644 index 0000000000..bc0765c869 --- /dev/null +++ b/components/esm4/loadarmo.cpp @@ -0,0 +1,218 @@ +/* + Copyright (C) 2016, 2018-2021 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 "loadarmo.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Armor::Armor() : mFormId(0), mFlags(0), mIsTES4(false), mIsFO3(false), mIsFONV(false), + mPickUpSound(0), mDropSound(0), mBoundRadius(0.f), + mArmorFlags(0), mGeneralFlags(0), mScriptId(0), mEnchantmentPoints(0), mEnchantment(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModelMale.clear(); + mModelMaleWorld.clear(); + mModelFemale.clear(); + mModelFemaleWorld.clear(); + mText.clear(); + mIconMale.clear(); + mMiniIconMale.clear(); + mIconFemale.clear(); + mMiniIconFemale.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(); + mIsFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_DATA: + { + //if (reader.esmVersion() == ESM::VER_094 || reader.esmVersion() == ESM::VER_170) + if (subHdr.dataSize == 8) // FO3 has 12 bytes even though VER_094 + { + reader.get(mData.value); + reader.get(mData.weight); + mIsFO3 = true; + } + else if (mIsFONV || subHdr.dataSize == 12) + { + reader.get(mData.value); + reader.get(mData.health); + reader.get(mData.weight); + } + else + { + reader.get(mData); // TES4 + mIsTES4 = true; + } + + break; + } + case ESM4::SUB_MODL: // seems only for Dawnguard/Dragonborn? + { + //if (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || isFONV) + if (subHdr.dataSize == 4) // FO3 has zstring even though VER_094 + { + FormId formId; + reader.getFormId(formId); + + mAddOns.push_back(formId); + } + else + { + if (!reader.getZString(mModelMale)) + throw std::runtime_error ("ARMO MODL data read error"); + } + + break; + } + case ESM4::SUB_MOD2: reader.getZString(mModelMaleWorld);break; + case ESM4::SUB_MOD3: reader.getZString(mModelFemale); break; + case ESM4::SUB_MOD4: reader.getZString(mModelFemaleWorld); break; + case ESM4::SUB_ICON: reader.getZString(mIconMale); break; + case ESM4::SUB_MICO: reader.getZString(mMiniIconMale); break; + case ESM4::SUB_ICO2: reader.getZString(mIconFemale); break; + case ESM4::SUB_MIC2: reader.getZString(mMiniIconFemale); break; + case ESM4::SUB_BMDT: + { + if (subHdr.dataSize == 8) // FO3 + { + reader.get(mArmorFlags); + reader.get(mGeneralFlags); + mGeneralFlags &= 0x000000ff; + mGeneralFlags |= TYPE_FO3; + } + else // TES4 + { + reader.get(mArmorFlags); + mGeneralFlags = (mArmorFlags & 0x00ff0000) >> 16; + mGeneralFlags |= TYPE_TES4; + } + break; + } + case ESM4::SUB_BODT: + { + reader.get(mArmorFlags); + uint32_t flags = 0; + if (subHdr.dataSize == 12) + reader.get(flags); + reader.get(mGeneralFlags); // skill + mGeneralFlags &= 0x0000000f; // 0 (light), 1 (heavy) or 2 (none) + if (subHdr.dataSize == 12) + mGeneralFlags |= (flags & 0x0000000f) << 3; + mGeneralFlags |= TYPE_TES5; + break; + } + case ESM4::SUB_BOD2: + { + reader.get(mArmorFlags); + reader.get(mGeneralFlags); + mGeneralFlags &= 0x0000000f; // 0 (light), 1 (heavy) or 2 (none) + mGeneralFlags |= TYPE_TES5; + break; + } + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); 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_DESC: reader.getLocalizedString(mText); break; + case ESM4::SUB_YNAM: reader.getFormId(mPickUpSound); break; + case ESM4::SUB_ZNAM: reader.getFormId(mDropSound); break; + case ESM4::SUB_MODT: + 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_RNAM: // race formid + case ESM4::SUB_KSIZ: + case ESM4::SUB_KWDA: + 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_EAMT: + case ESM4::SUB_EITM: + case ESM4::SUB_VMAD: + 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::ARMO::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } + //if ((mArmorFlags&0xffff) == 0x02) // only hair + //std::cout << "only hair " << mEditorId << std::endl; +} + +//void ESM4::Armor::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Armor::blank() +//{ +//} diff --git a/components/esm4/loadarmo.hpp b/components/esm4/loadarmo.hpp new file mode 100644 index 0000000000..f17521f507 --- /dev/null +++ b/components/esm4/loadarmo.hpp @@ -0,0 +1,196 @@ +/* + Copyright (C) 2016, 2018-2020 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 +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Armor + { + // WARN: TES4 Armorflags still has the general flags high bits + enum ArmorFlags + { + TES4_Head = 0x00000001, + TES4_Hair = 0x00000002, + TES4_UpperBody = 0x00000004, + TES4_LowerBody = 0x00000008, + TES4_Hands = 0x00000010, + TES4_Feet = 0x00000020, + TES4_RightRing = 0x00000040, + TES4_LeftRing = 0x00000080, + TES4_Amulet = 0x00000100, + TES4_Weapon = 0x00000200, + TES4_BackWeapon = 0x00000400, + TES4_SideWeapon = 0x00000800, + TES4_Quiver = 0x00001000, + TES4_Shield = 0x00002000, + TES4_Torch = 0x00004000, + TES4_Tail = 0x00008000, + // + FO3_Head = 0x00000001, + FO3_Hair = 0x00000002, + FO3_UpperBody = 0x00000004, + FO3_LeftHand = 0x00000008, + FO3_RightHand = 0x00000010, + FO3_Weapon = 0x00000020, + FO3_PipBoy = 0x00000040, + FO3_Backpack = 0x00000080, + FO3_Necklace = 0x00000100, + FO3_Headband = 0x00000200, + FO3_Hat = 0x00000400, + FO3_EyeGlasses = 0x00000800, + FO3_NoseRing = 0x00001000, + FO3_Earrings = 0x00002000, + FO3_Mask = 0x00004000, + FO3_Choker = 0x00008000, + FO3_MouthObject = 0x00010000, + FO3_BodyAddOn1 = 0x00020000, + FO3_BodyAddOn2 = 0x00040000, + FO3_BodyAddOn3 = 0x00080000, + // + TES5_Head = 0x00000001, + TES5_Hair = 0x00000002, + TES5_Body = 0x00000004, + TES5_Hands = 0x00000008, + TES5_Forearms = 0x00000010, + TES5_Amulet = 0x00000020, + TES5_Ring = 0x00000040, + TES5_Feet = 0x00000080, + TES5_Calves = 0x00000100, + TES5_Shield = 0x00000200, + TES5_Tail = 0x00000400, + TES5_LongHair = 0x00000800, + TES5_Circlet = 0x00001000, + TES5_Ears = 0x00002000, + TES5_BodyAddOn3 = 0x00004000, + TES5_BodyAddOn4 = 0x00008000, + TES5_BodyAddOn5 = 0x00010000, + TES5_BodyAddOn6 = 0x00020000, + TES5_BodyAddOn7 = 0x00040000, + TES5_BodyAddOn8 = 0x00080000, + TES5_DecapHead = 0x00100000, + TES5_Decapitate = 0x00200000, + TES5_BodyAddOn9 = 0x00400000, + TES5_BodyAddOn10 = 0x00800000, + TES5_BodyAddOn11 = 0x01000000, + TES5_BodyAddOn12 = 0x02000000, + TES5_BodyAddOn13 = 0x04000000, + TES5_BodyAddOn14 = 0x08000000, + TES5_BodyAddOn15 = 0x10000000, + TES5_BodyAddOn16 = 0x20000000, + TES5_BodyAddOn17 = 0x40000000, + TES5_FX01 = 0x80000000 + }; + + enum GeneralFlags + { + TYPE_TES4 = 0x1000, + TYPE_FO3 = 0x2000, + TYPE_TES5 = 0x3000, + TYPE_FONV = 0x4000, + // + TES4_HideRings = 0x0001, + TES4_HideAmulet = 0x0002, + TES4_NonPlayable = 0x0040, + TES4_HeavyArmor = 0x0080, + // + FO3_PowerArmor = 0x0020, + FO3_NonPlayable = 0x0040, + FO3_HeavyArmor = 0x0080, + // + TES5_LightArmor = 0x0000, + TES5_HeavyArmor = 0x0001, + TES5_None = 0x0002, + TES5_ModVoice = 0x0004, // note bit shift + TES5_NonPlayable = 0x0040 // note bit shift + }; + +#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 + + bool mIsTES4; // TODO: check that these match the general flags + bool mIsFO3; + bool mIsFONV; + + std::string mEditorId; + std::string mFullName; + std::string mModelMale; + std::string mModelMaleWorld; + std::string mModelFemale; + std::string mModelFemaleWorld; + std::string mText; + std::string mIconMale; + std::string mMiniIconMale; + std::string mIconFemale; + std::string mMiniIconFemale; + + FormId mPickUpSound; + FormId mDropSound; + + std::string mModel; // FIXME: for OpenCS + + float mBoundRadius; + + std::uint32_t mArmorFlags; + std::uint32_t mGeneralFlags; + FormId mScriptId; + std::uint16_t mEnchantmentPoints; + FormId mEnchantment; + + std::vector mAddOns; // TES5 ARMA + 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/components/esm4/loadaspc.cpp b/components/esm4/loadaspc.cpp new file mode 100644 index 0000000000..a4cb6f3939 --- /dev/null +++ b/components/esm4/loadaspc.cpp @@ -0,0 +1,94 @@ +/* + Copyright (C) 2020 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 "loadaspc.hpp" + +#include +//#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::AcousticSpace::AcousticSpace() : mFormId(0), mFlags(0), mEnvironmentType(0), mSoundRegion(0), + mIsInterior(0) +{ + mEditorId.clear(); +} + +ESM4::AcousticSpace::~AcousticSpace() +{ +} + +void ESM4::AcousticSpace::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_ANAM: reader.get(mEnvironmentType); break; + case ESM4::SUB_SNAM: + { + FormId id; + reader.getFormId(id); + mAmbientLoopSounds.push_back(id); + break; + } + case ESM4::SUB_RDAT: reader.getFormId(mSoundRegion); break; + case ESM4::SUB_INAM: reader.get(mIsInterior); break; + case ESM4::SUB_WNAM: // usually 0 for FONV (maybe # of close Actors for Walla to trigger) + { + std::uint32_t dummy; + reader.get(dummy); + //std::cout << "WNAM " << mEditorId << " " << dummy << std::endl; + break; + } + case ESM4::SUB_BNAM: // TES5 reverb formid + case ESM4::SUB_OBND: + { + //std::cout << "ASPC " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::ASPC::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::AcousticSpace::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::AcousticSpace::blank() +//{ +//} diff --git a/components/esm4/loadaspc.hpp b/components/esm4/loadaspc.hpp new file mode 100644 index 0000000000..3ab1a8484d --- /dev/null +++ b/components/esm4/loadaspc.hpp @@ -0,0 +1,66 @@ +/* + Copyright (C) 2020 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_ASPC_H +#define ESM4_ASPC_H + +#include +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct AcousticSpace + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + std::uint32_t mEnvironmentType; + + // 0 Dawn (5:00 start), 1 Afternoon (8:00), 2 Dusk (18:00), 3 Night (20:00) + std::vector mAmbientLoopSounds; + FormId mSoundRegion; + + std::uint32_t mIsInterior; // if true only use mAmbientLoopSounds[0] + + AcousticSpace(); + virtual ~AcousticSpace(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_ASPC_H diff --git a/components/esm4/loadbook.cpp b/components/esm4/loadbook.cpp new file mode 100644 index 0000000000..0ffc69ef0e --- /dev/null +++ b/components/esm4/loadbook.cpp @@ -0,0 +1,121 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 "loadbook.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Book::Book() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScriptId(0), + mEnchantmentPoints(0), mEnchantment(0), mPickUpSound(0), mDropSound(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(); // currently unused + + 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.getLocalizedString(mFullName); break; + case ESM4::SUB_DESC: reader.getLocalizedString(mText); break; + case ESM4::SUB_DATA: + { + reader.get(mData.flags); + //if (reader.esmVersion() == ESM::VER_094 || reader.esmVersion() == ESM::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(mScriptId); 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_YNAM: reader.getFormId(mPickUpSound); break; + case ESM4::SUB_ZNAM: reader.getFormId(mDropSound); break; // TODO: does this exist? + 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_VMAD: + { + //std::cout << "BOOK " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::BOOK::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Book::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Book::blank() +//{ +//} diff --git a/components/esm4/loadbook.hpp b/components/esm4/loadbook.hpp new file mode 100644 index 0000000000..c5c1a3529f --- /dev/null +++ b/components/esm4/loadbook.hpp @@ -0,0 +1,114 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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 mScriptId; + std::string mIcon; + std::uint16_t mEnchantmentPoints; + FormId mEnchantment; + + Data mData; + + FormId mPickUpSound; + FormId mDropSound; + + 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/components/esm4/loadbptd.cpp b/components/esm4/loadbptd.cpp new file mode 100644 index 0000000000..6c6c1bc26c --- /dev/null +++ b/components/esm4/loadbptd.cpp @@ -0,0 +1,113 @@ +/* + Copyright (C) 2019-2021 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 "loadbptd.hpp" + +#include +#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +void ESM4::BodyPartData::BodyPart::clear() +{ + mPartName.clear(); + mNodeName.clear(); + mVATSTarget.clear(); + mIKStartNode.clear(); + std::memset(&mData, 0, sizeof(BPND)); + mLimbReplacementModel.clear(); + mGoreEffectsTarget.clear(); +} + +ESM4::BodyPartData::BodyPartData() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); +} + +ESM4::BodyPartData::~BodyPartData() +{ +} + +void ESM4::BodyPartData::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + + BodyPart bodyPart; + bodyPart.clear(); + + 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.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_BPTN: reader.getLocalizedString(bodyPart.mPartName); break; + case ESM4::SUB_BPNN: reader.getZString(bodyPart.mNodeName); break; + case ESM4::SUB_BPNT: reader.getZString(bodyPart.mVATSTarget); break; + case ESM4::SUB_BPNI: reader.getZString(bodyPart.mIKStartNode); break; + case ESM4::SUB_BPND: reader.get(bodyPart.mData); break; + case ESM4::SUB_NAM1: reader.getZString(bodyPart.mLimbReplacementModel); break; + case ESM4::SUB_NAM4: // FIXME: assumed occurs last + { + reader.getZString(bodyPart.mGoreEffectsTarget); // target bone + + mBodyParts.push_back(bodyPart); // FIXME: possible without copying? + + bodyPart.clear(); + break; + } + case ESM4::SUB_NAM5: + case ESM4::SUB_RAGA: // ragdoll + case ESM4::SUB_MODS: + case ESM4::SUB_MODT: + { + //std::cout << "BPTD " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::BPTD::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } + + //if (mEditorId == "DefaultBodyPartData") + //std::cout << "BPTD: " << mEditorId << std::endl; // FIXME: testing only +} + +//void ESM4::BodyPartData::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::BodyPartData::blank() +//{ +//} diff --git a/components/esm4/loadbptd.hpp b/components/esm4/loadbptd.hpp new file mode 100644 index 0000000000..0a29cd22cb --- /dev/null +++ b/components/esm4/loadbptd.hpp @@ -0,0 +1,128 @@ +/* + Copyright (C) 2019, 2020 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_BPTD_H +#define ESM4_BPTD_H + +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct BodyPartData + { +#pragma pack(push, 1) + struct BPND + { + float damageMult; + + // Severable + // IK Data + // IK Data - Biped Data + // Explodable + // IK Data - Is Head + // IK Data - Headtracking + // To Hit Chance - Absolute + std::uint8_t flags; + + // Torso + // Head + // Eye + // LookAt + // Fly Grab + // Saddle + std::uint8_t partType; + + std::uint8_t healthPercent; + std::int8_t actorValue; //(Actor Values) + std::uint8_t toHitChance; + + std::uint8_t explExplosionChance; // % + std::uint16_t explDebrisCount; + FormId explDebris; + FormId explExplosion; + float trackingMaxAngle; + float explDebrisScale; + + std::int32_t sevDebrisCount; + FormId sevDebris; + FormId sevExplosion; + float sevDebrisScale; + + //Struct - Gore Effects Positioning + float transX; + float transY; + float transZ; + float rotX; + float rotY; + float rotZ; + + FormId sevImpactDataSet; + FormId explImpactDataSet; + uint8_t sevDecalCount; + uint8_t explDecalCount; + uint16_t Unknown; + float limbReplacementScale; + }; +#pragma pack(pop) + + struct BodyPart + { + std::string mPartName; + std::string mNodeName; + std::string mVATSTarget; + std::string mIKStartNode; + BPND mData; + std::string mLimbReplacementModel; + std::string mGoreEffectsTarget; + + void clear(); + }; + + 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::vector mBodyParts; + + BodyPartData(); + virtual ~BodyPartData(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_BPTD_H diff --git a/components/esm4/loadcell.cpp b/components/esm4/loadcell.cpp new file mode 100644 index 0000000000..eb1f864b92 --- /dev/null +++ b/components/esm4/loadcell.cpp @@ -0,0 +1,269 @@ +/* + Copyright (C) 2015-2016, 2018-2021 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 "loadcell.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +#include +#include +#include // FLT_MAX for gcc + +#include // FIXME: debug only + +#include "reader.hpp" +//#include "writer.hpp" + +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), + mLightingTemplate(0), mLightingTemplateFlags(0), mMusic(0), mAcousticSpace(0), + mMusicType(0), mPreloaded(false) +{ + mEditorId.clear(); + mFullName.clear(); + + mLighting.ambient = 0; + mLighting.directional = 0; + mLighting.fogColor = 0; + mLighting.fogNear = 0.f; + mLighting.fogFar = 0.f; + mLighting.rotationXY = 0; + mLighting.rotationZ = 0; + mLighting.fogDirFade = 0.f; + mLighting.fogClipDist = 0.f; + mLighting.fogPower = FLT_MAX; // hack way to detect TES4 + + mRegions.clear(); +} + +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 true; +} + +void ESM4::Cell::load(ESM4::Reader& reader) +{ + if (mPreloaded) + return; + + // WARN: we need to call setCurrCell (and maybe setCurrCellGrid?) again before loading + // cell child groups if we are loading them after restoring the context + // (may be easier to update the context before saving?) + init(reader); + reader.setCurrCell(mFormId); // save for LAND (and other children) to access later + std::uint32_t esmVer = reader.esmVersion(); + bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::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 == ESM::VER_094 || esmVer == ESM::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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_DATA: + { + if (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || isFONV) + if (subHdr.dataSize == 2) + reader.get(mCellFlags); + else + { + assert(subHdr.dataSize == 1 && "CELL unexpected DATA flag size"); + reader.get(&mCellFlags, sizeof(std::uint8_t)); + } + 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: // for exterior cells + { + 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: + { + if (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || isFONV) + { + if (subHdr.dataSize == 40) // FO3/FONV + reader.get(mLighting); + else if (subHdr.dataSize == 92) // TES5 + { + reader.get(mLighting); + reader.skipSubRecordData(52); // FIXME + } + else + reader.skipSubRecordData(); + } + else + reader.get(&mLighting, 36); // TES4 + + break; + } + case ESM4::SUB_XCMT: reader.get(mMusicType); break; // Oblivion only? + case ESM4::SUB_LTMP: reader.getFormId(mLightingTemplate); break; + case ESM4::SUB_LNAM: reader.get(mLightingTemplateFlags); break; // seems to always follow LTMP + case ESM4::SUB_XCMO: reader.getFormId(mMusic); break; + case ESM4::SUB_XCAS: reader.getFormId(mAcousticSpace); break; + case ESM4::SUB_TVDT: + case ESM4::SUB_MHDT: + case ESM4::SUB_XCGD: + case ESM4::SUB_XNAM: + case ESM4::SUB_XLCN: + case ESM4::SUB_XWCS: + case ESM4::SUB_XWCU: + case ESM4::SUB_XWCN: + case ESM4::SUB_XCIM: + case ESM4::SUB_XEZN: + case ESM4::SUB_XWEM: + case ESM4::SUB_XILL: + case ESM4::SUB_XRNK: // Oblivion only? + case ESM4::SUB_XCET: // FO3 + case ESM4::SUB_IMPF: // FO3 Zeta + { + //std::cout << "CELL " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::CELL::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Cell::save(ESM4::Writer& writer) const +//{ +//} + +void ESM4::Cell::blank() +{ +} diff --git a/components/esm4/loadcell.hpp b/components/esm4/loadcell.hpp new file mode 100644 index 0000000000..70dc94e73a --- /dev/null +++ b/components/esm4/loadcell.hpp @@ -0,0 +1,109 @@ +/* + Copyright (C) 2015-2016, 2018-2020 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 + +#include "formid.hpp" +#include "lighting.hpp" + +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 (Int) Has Water (Int) + 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 + { + 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; + + FormId mLightingTemplate; // FO3/FONV + std::uint32_t mLightingTemplateFlags; // FO3/FONV + + FormId mMusic; // FO3/FONV + FormId mAcousticSpace; // FO3/FONV + // TES4: 0 = default, 1 = public, 2 = dungeon + // FO3/FONV have more types (not sure how they are used, however) + std::uint8_t mMusicType; + + 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/components/esm4/loadclas.cpp b/components/esm4/loadclas.cpp new file mode 100644 index 0000000000..2948976709 --- /dev/null +++ b/components/esm4/loadclas.cpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loadclas.hpp" + +//#ifdef NDEBUG // FIXME: debugging only +//#undef NDEBUG +//#endif + +//#include +#include + +#include "reader.hpp" +//#include "writer.hpp" + +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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_DESC: reader.getLocalizedString(mDesc); break; + case ESM4::SUB_ICON: reader.getZString(mIcon); break; + case ESM4::SUB_DATA: + { + //std::cout << "CLAS " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::CLAS::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Class::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Class::blank() +//{ +//} diff --git a/components/esm4/loadclas.hpp b/components/esm4/loadclas.hpp new file mode 100644 index 0000000000..83e8372f4a --- /dev/null +++ b/components/esm4/loadclas.hpp @@ -0,0 +1,66 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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/components/esm4/loadclfm.cpp b/components/esm4/loadclfm.cpp new file mode 100644 index 0000000000..b926cb93fd --- /dev/null +++ b/components/esm4/loadclfm.cpp @@ -0,0 +1,93 @@ +/* + Copyright (C) 2020-2021 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 "loadclfm.hpp" + +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Colour::Colour() : mFormId(0), mFlags(0), mPlayable(0) +{ + mEditorId.clear(); + mFullName.clear(); + + mColour.red = 0; + mColour.green = 0; + mColour.blue = 0; + mColour.custom = 0; +} + +ESM4::Colour::~Colour() +{ +} + +void ESM4::Colour::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.getLocalizedString(mFullName); break; + case ESM4::SUB_CNAM: + { + reader.get(mColour.red); + reader.get(mColour.green); + reader.get(mColour.blue); + reader.get(mColour.custom); + + break; + } + case ESM4::SUB_FNAM: + { + reader.get(mPlayable); + + break; + } + default: + //std::cout << "CLFM " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + //reader.skipSubRecordData(); + throw std::runtime_error("ESM4::CLFM::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Colour::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Colour::blank() +//{ +//} diff --git a/components/esm4/loadclfm.hpp b/components/esm4/loadclfm.hpp new file mode 100644 index 0000000000..2a18e42cd6 --- /dev/null +++ b/components/esm4/loadclfm.hpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2020 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_CLFM_H +#define ESM4_CLFM_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + // FIXME: duplicate with Npc + struct ColourRGB + { + std::uint8_t red; + std::uint8_t green; + std::uint8_t blue; + std::uint8_t custom; // alpha? + }; + + struct Colour + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::string mFullName; + + ColourRGB mColour; + std::uint32_t mPlayable; + + Colour(); + virtual ~Colour(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_CLFM_H diff --git a/components/esm4/loadclot.cpp b/components/esm4/loadclot.cpp new file mode 100644 index 0000000000..da6473c393 --- /dev/null +++ b/components/esm4/loadclot.cpp @@ -0,0 +1,106 @@ +/* + Copyright (C) 2016, 2018, 2020 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 "loadclot.hpp" + +#include +//#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Clothing::Clothing() : mFormId(0), mFlags(0), mBoundRadius(0.f), mClothingFlags(0), + mScriptId(0), mEnchantmentPoints(0), mEnchantment(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModelMale.clear(); + mModelMaleWorld.clear(); + mModelFemale.clear(); + mModelFemaleWorld.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_DATA: reader.get(mData); break; + case ESM4::SUB_BMDT: reader.get(mClothingFlags); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); 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_MODL: reader.getZString(mModelMale); break; + case ESM4::SUB_MOD2: reader.getZString(mModelMaleWorld); break; + case ESM4::SUB_MOD3: reader.getZString(mModelFemale); break; + case ESM4::SUB_MOD4: reader.getZString(mModelFemaleWorld); break; + case ESM4::SUB_ICON: reader.getZString(mIconMale); break; + case ESM4::SUB_ICO2: reader.getZString(mIconFemale); break; + case ESM4::SUB_MODT: + 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::CLOT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } + //if ((mClothingFlags&0xffff) == 0x02) // only hair + //std::cout << "only hair " << mEditorId << std::endl; +} + +//void ESM4::Clothing::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Clothing::blank() +//{ +//} diff --git a/components/esm4/loadclot.hpp b/components/esm4/loadclot.hpp new file mode 100644 index 0000000000..06e019137b --- /dev/null +++ b/components/esm4/loadclot.hpp @@ -0,0 +1,83 @@ +/* + Copyright (C) 2016, 2018-2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Clothing + { +#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 mModelMale; + std::string mModelMaleWorld; + std::string mModelFemale; + std::string mModelFemaleWorld; + std::string mIconMale; // texture + std::string mIconFemale; // texture + + std::string mModel; // FIXME: for OpenCS + + float mBoundRadius; + + std::uint32_t mClothingFlags; // see Armor::ArmorFlags for the values + FormId mScriptId; + 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/components/esm4/loadcont.cpp b/components/esm4/loadcont.cpp new file mode 100644 index 0000000000..06366c88cc --- /dev/null +++ b/components/esm4/loadcont.cpp @@ -0,0 +1,107 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loadcont.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), mScriptId(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: reader.getLocalizedString(mFullName); 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(mScriptId); 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::CONT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Container::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Container::blank() +//{ +//} diff --git a/components/esm4/loadcont.hpp b/components/esm4/loadcont.hpp new file mode 100644 index 0000000000..e059d9a361 --- /dev/null +++ b/components/esm4/loadcont.hpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2016, 2018, 2020 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 +#include + +#include "formid.hpp" +#include "inventory.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 mScriptId; // 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/components/esm4/loadcrea.cpp b/components/esm4/loadcrea.cpp new file mode 100644 index 0000000000..5fdd414256 --- /dev/null +++ b/components/esm4/loadcrea.cpp @@ -0,0 +1,232 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 "loadcrea.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +#include +#include +#include +#include +#include +#include // FIXME + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Creature::Creature() : mFormId(0), mFlags(0), mDeathItem(0), mScriptId(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(mScriptId); break; + case ESM4::SUB_AIDT: + { + if (subHdr.dataSize == 20) // FO3 + reader.skipSubRecordData(); + else + reader.get(mAIData); // 12 bytes + break; + } + case ESM4::SUB_ACBS: + { + //if (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || mIsFONV) + if (subHdr.dataSize == 24) + reader.get(mBaseConfig); + else + reader.get(&mBaseConfig, 16); // TES4 + 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_TPLT: reader.get(mBaseTemplate); break; // FO3 + case ESM4::SUB_PNAM: // FO3/FONV/TES5 + { + FormId bodyPart; + reader.get(bodyPart); + mBodyParts.push_back(bodyPart); + + 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_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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::CREA::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Creature::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Creature::blank() +//{ +//} diff --git a/components/esm4/loadcrea.hpp b/components/esm4/loadcrea.hpp new file mode 100644 index 0000000000..c2889d1713 --- /dev/null +++ b/components/esm4/loadcrea.hpp @@ -0,0 +1,151 @@ +/* + Copyright (C) 2016, 2018, 2020 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 +#include + +#include "actor.hpp" +#include "inventory.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Creature + { + enum ACBS_TES4 + { + TES4_Essential = 0x000002, + TES4_WeapAndShield = 0x000004, + TES4_Respawn = 0x000008, + TES4_PCLevelOffset = 0x000080, + TES4_NoLowLevelProc = 0x000200, + TES4_NoHead = 0x008000, // different meaning to npc_ + TES4_NoRightArm = 0x010000, + TES4_NoLeftArm = 0x020000, + TES4_NoCombatWater = 0x040000, + TES4_NoShadow = 0x080000, + TES4_NoCorpseCheck = 0x100000 // opposite of npc_ + }; + + enum ACBS_FO3 + { + FO3_Biped = 0x00000001, + FO3_Essential = 0x00000002, + FO3_Weap_Shield = 0x00000004, + FO3_Respawn = 0x00000008, + FO3_CanSwim = 0x00000010, + FO3_CanFly = 0x00000020, + FO3_CanWalk = 0x00000040, + FO3_PCLevelMult = 0x00000080, + FO3_NoLowLevelProc = 0x00000200, + FO3_NoBloodSpray = 0x00000800, + FO3_NoBloodDecal = 0x00001000, + FO3_NoHead = 0x00008000, + FO3_NoRightArm = 0x00010000, + FO3_NoLeftArm = 0x00020000, + FO3_NoWaterCombat = 0x00040000, + FO3_NoShadow = 0x00080000, + FO3_NoVATSMelee = 0x00100000, + FO3_AllowPCDialog = 0x00200000, + FO3_NoOpenDoors = 0x00400000, + FO3_Immobile = 0x00800000, + FO3_TiltFrontBack = 0x01000000, + FO3_TiltLeftRight = 0x02000000, + FO3_NoKnockdown = 0x04000000, + FO3_NotPushable = 0x08000000, + FO3_AllowPickpoket = 0x10000000, + FO3_IsGhost = 0x20000000, + FO3_NoRotateHead = 0x40000000, + FO3_Invulnerable = 0x80000000 + }; + +#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 mScriptId; + + 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; + + FormId mBaseTemplate; // FO3/FONV + std::vector mBodyParts; // FO3/FONV + + 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/components/esm4/loaddial.cpp b/components/esm4/loaddial.cpp new file mode 100644 index 0000000000..716da441d5 --- /dev/null +++ b/components/esm4/loaddial.cpp @@ -0,0 +1,125 @@ +/* + Copyright (C) 2020-2021 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 "loaddial.hpp" + +#include +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Dialogue::Dialogue() : mFormId(0), mFlags(0), mDoAllBeforeRepeat(false), + mDialType(0), mDialFlags(0), mPriority(0.f) +{ + mEditorId.clear(); + mTopicName.clear(); + + mTextDumb.clear(); // FIXME: temp name +} + +ESM4::Dialogue::~Dialogue() +{ +} + +void ESM4::Dialogue::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(mTopicName); break; + case ESM4::SUB_QSTI: + { + FormId questId; + reader.getFormId(questId); + mQuests.push_back(questId); + + break; + } + case ESM4::SUB_QSTR: // Seems never used in TES4 + { + FormId questRem; + reader.getFormId(questRem); + mQuestsRemoved.push_back(questRem); + + break; + } + case ESM4::SUB_DATA: + { + if (subHdr.dataSize == 4) // TES5 + { + std::uint8_t dummy; + reader.get(dummy); + if (dummy != 0) + mDoAllBeforeRepeat = true; + } + + reader.get(mDialType); // TES4/FO3/FONV/TES5 + + if (subHdr.dataSize >= 2) // FO3/FONV/TES5 + reader.get(mDialFlags); + + if (subHdr.dataSize >= 3) // TES5 + reader.skipSubRecordData(1); // unknown + + break; + } + case ESM4::SUB_PNAM: reader.get(mPriority); break; // FO3/FONV + case ESM4::SUB_TDUM: reader.getZString(mTextDumb); break; // FONV + case ESM4::SUB_SCRI: + case ESM4::SUB_INFC: // FONV info connection + case ESM4::SUB_INFX: // FONV info index + case ESM4::SUB_QNAM: // TES5 + case ESM4::SUB_BNAM: // TES5 + case ESM4::SUB_SNAM: // TES5 + case ESM4::SUB_TIFC: // TES5 + { + //std::cout << "DIAL " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::DIAL::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Dialogue::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Dialogue::blank() +//{ +//} diff --git a/components/esm4/loaddial.hpp b/components/esm4/loaddial.hpp new file mode 100644 index 0000000000..ef44c841fc --- /dev/null +++ b/components/esm4/loaddial.hpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2020-2021 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_DIAL_H +#define ESM4_DIAL_H + +#include +#include +#include + +#include "formid.hpp" +#include "dialogue.hpp" // DialType + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Dialogue + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::vector mQuests; + std::vector mQuestsRemoved; // FONV only? + std::string mTopicName; + + std::string mTextDumb; // FIXME: temp name + + bool mDoAllBeforeRepeat; // TES5 only + std::uint8_t mDialType; // DialType + std::uint8_t mDialFlags; // FO3/FONV: 0x1 rumours, 0x2 top-level + + float mPriority; + + Dialogue(); + virtual ~Dialogue(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_DIAL_H diff --git a/components/esm4/loaddobj.cpp b/components/esm4/loaddobj.cpp new file mode 100644 index 0000000000..307674a5db --- /dev/null +++ b/components/esm4/loaddobj.cpp @@ -0,0 +1,128 @@ +/* + Copyright (C) 2020-2021 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#include "loaddobj.hpp" + +#include +#include +//#include // FIXME: for debugging only + +//#include "formid.hpp" + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::DefaultObj::DefaultObj() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + + std::memset(&mData, 0, sizeof(Defaults)); +} + +ESM4::DefaultObj::~DefaultObj() +{ +} + +void ESM4::DefaultObj::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; // "DefaultObjectManager" + case ESM4::SUB_DATA: + { + reader.getFormId(mData.stimpack); + reader.getFormId(mData.superStimpack); + reader.getFormId(mData.radX); + reader.getFormId(mData.radAway); + reader.getFormId(mData.morphine); + reader.getFormId(mData.perkParalysis); + reader.getFormId(mData.playerFaction); + reader.getFormId(mData.mysteriousStrangerNPC); + reader.getFormId(mData.mysteriousStrangerFaction); + reader.getFormId(mData.defaultMusic); + reader.getFormId(mData.battleMusic); + reader.getFormId(mData.deathMusic); + reader.getFormId(mData.successMusic); + reader.getFormId(mData.levelUpMusic); + reader.getFormId(mData.playerVoiceMale); + reader.getFormId(mData.playerVoiceMaleChild); + reader.getFormId(mData.playerVoiceFemale); + reader.getFormId(mData.playerVoiceFemaleChild); + reader.getFormId(mData.eatPackageDefaultFood); + reader.getFormId(mData.everyActorAbility); + reader.getFormId(mData.drugWearsOffImageSpace); + // below FONV only + if (subHdr.dataSize == 136) // FONV 136/4 = 34 formid + { + reader.getFormId(mData.doctorsBag); + reader.getFormId(mData.missFortuneNPC); + reader.getFormId(mData.missFortuneFaction); + reader.getFormId(mData.meltdownExplosion); + reader.getFormId(mData.unarmedForwardPA); + reader.getFormId(mData.unarmedBackwardPA); + reader.getFormId(mData.unarmedLeftPA); + reader.getFormId(mData.unarmedRightPA); + reader.getFormId(mData.unarmedCrouchPA); + reader.getFormId(mData.unarmedCounterPA); + reader.getFormId(mData.spotterEffect); + reader.getFormId(mData.itemDetectedEfect); + reader.getFormId(mData.cateyeMobileEffectNYI); + } + + break; + } + case ESM4::SUB_DNAM: + { + //std::cout << "DOBJ " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + //std::cout << "DOBJ " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + //reader.skipSubRecordData(); + throw std::runtime_error("ESM4::DOBJ::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::DefaultObj::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::DefaultObj::blank() +//{ +//} diff --git a/components/esm4/loaddobj.hpp b/components/esm4/loaddobj.hpp new file mode 100644 index 0000000000..6d708fcbbc --- /dev/null +++ b/components/esm4/loaddobj.hpp @@ -0,0 +1,100 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#ifndef ESM4_DOBJ_H +#define ESM4_DOBJ_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Defaults + { + FormId stimpack; + FormId superStimpack; + FormId radX; + FormId radAway; + FormId morphine; + FormId perkParalysis; + FormId playerFaction; + FormId mysteriousStrangerNPC; + FormId mysteriousStrangerFaction; + FormId defaultMusic; + FormId battleMusic; + FormId deathMusic; + FormId successMusic; + FormId levelUpMusic; + FormId playerVoiceMale; + FormId playerVoiceMaleChild; + FormId playerVoiceFemale; + FormId playerVoiceFemaleChild; + FormId eatPackageDefaultFood; + FormId everyActorAbility; + FormId drugWearsOffImageSpace; + // below FONV only + FormId doctorsBag; + FormId missFortuneNPC; + FormId missFortuneFaction; + FormId meltdownExplosion; + FormId unarmedForwardPA; + FormId unarmedBackwardPA; + FormId unarmedLeftPA; + FormId unarmedRightPA; + FormId unarmedCrouchPA; + FormId unarmedCounterPA; + FormId spotterEffect; + FormId itemDetectedEfect; + FormId cateyeMobileEffectNYI; + }; + + struct DefaultObj + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + Defaults mData; + + DefaultObj(); + virtual ~DefaultObj(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_DOBJ_H diff --git a/components/esm4/loaddoor.cpp b/components/esm4/loaddoor.cpp new file mode 100644 index 0000000000..45ac1f0b6c --- /dev/null +++ b/components/esm4/loaddoor.cpp @@ -0,0 +1,93 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loaddoor.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Door::Door() : mFormId(0), mFlags(0), mBoundRadius(0.f), mDoorFlags(0), mScriptId(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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::DOOR::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Door::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Door::blank() +//{ +//} diff --git a/components/esm4/loaddoor.hpp b/components/esm4/loaddoor.hpp new file mode 100644 index 0000000000..0a332339d5 --- /dev/null +++ b/components/esm4/loaddoor.hpp @@ -0,0 +1,76 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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 mScriptId; + FormId mOpenSound; // SNDR for TES5, SOUN for others + FormId mCloseSound; // SNDR for TES5, SOUN for others + 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/components/esm4/loadeyes.cpp b/components/esm4/loadeyes.cpp new file mode 100644 index 0000000000..059bee6c37 --- /dev/null +++ b/components/esm4/loadeyes.cpp @@ -0,0 +1,74 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loadeyes.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: reader.getLocalizedString(mFullName); 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 " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Eyes::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Eyes::blank() +//{ +//} diff --git a/components/esm4/loadeyes.hpp b/components/esm4/loadeyes.hpp new file mode 100644 index 0000000000..814e2a9647 --- /dev/null +++ b/components/esm4/loadeyes.hpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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; // texture + + 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/components/esm4/loadflor.cpp b/components/esm4/loadflor.cpp new file mode 100644 index 0000000000..fab6c3f0ed --- /dev/null +++ b/components/esm4/loadflor.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loadflor.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Flora::Flora() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScriptId(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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::FLOR::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Flora::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Flora::blank() +//{ +//} diff --git a/components/esm4/loadflor.hpp b/components/esm4/loadflor.hpp new file mode 100644 index 0000000000..2f9e22d548 --- /dev/null +++ b/components/esm4/loadflor.hpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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() : 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 mScriptId; + 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/components/esm4/loadflst.cpp b/components/esm4/loadflst.cpp new file mode 100644 index 0000000000..32b23ff284 --- /dev/null +++ b/components/esm4/loadflst.cpp @@ -0,0 +1,81 @@ +/* + Copyright (C) 2020 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 "loadflst.hpp" + +#include +//#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::FormIdList::FormIdList() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); +} + +ESM4::FormIdList::~FormIdList() +{ +} + +void ESM4::FormIdList::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_LNAM: + { + FormId formId; + reader.getFormId(formId); + + mObjects.push_back(formId); + + break; + } + default: + //std::cout << "FLST " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + //reader.skipSubRecordData(); + throw std::runtime_error("ESM4::FLST::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } + //std::cout << "flst " << mEditorId << " " << mObjects.size() << std::endl; // FIXME +} + +//void ESM4::FormIdList::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::FormIdList::blank() +//{ +//} diff --git a/components/esm4/loadflst.hpp b/components/esm4/loadflst.hpp new file mode 100644 index 0000000000..6fd381aa44 --- /dev/null +++ b/components/esm4/loadflst.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2020 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_FLST_H +#define ESM4_FLST_H + +#include +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct FormIdList + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + std::vector mObjects; + + FormIdList(); + virtual ~FormIdList(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_FLST_H diff --git a/components/esm4/loadfurn.cpp b/components/esm4/loadfurn.cpp new file mode 100644 index 0000000000..6fba8bc3ce --- /dev/null +++ b/components/esm4/loadfurn.cpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loadfurn.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Furniture::Furniture() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScriptId(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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::FURN::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Furniture::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Furniture::blank() +//{ +//} diff --git a/components/esm4/loadfurn.hpp b/components/esm4/loadfurn.hpp new file mode 100644 index 0000000000..89da61ebf9 --- /dev/null +++ b/components/esm4/loadfurn.hpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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 mScriptId; + 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/components/esm4/loadglob.cpp b/components/esm4/loadglob.cpp new file mode 100644 index 0000000000..6a2fa8b00a --- /dev/null +++ b/components/esm4/loadglob.cpp @@ -0,0 +1,82 @@ +/* + Copyright (C) 2020 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 "loadglob.hpp" + +#include +#include // FIXME + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::GlobalVariable::GlobalVariable() : mFormId(0), mFlags(0), mType(0), mValue(0.f) +{ + mEditorId.clear(); +} + +ESM4::GlobalVariable::~GlobalVariable() +{ +} + +void ESM4::GlobalVariable::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.get(mType); break; + case ESM4::SUB_FLTV: reader.get(mValue); break; + case ESM4::SUB_FULL: + case ESM4::SUB_MODL: + case ESM4::SUB_MODB: + case ESM4::SUB_ICON: + case ESM4::SUB_DATA: + case ESM4::SUB_OBND: // TES5 + case ESM4::SUB_VMAD: // TES5 + { + //std::cout << "GLOB " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::GLOB::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::GlobalVariable::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::GlobalVariable::blank() +//{ +//} diff --git a/components/esm4/loadglob.hpp b/components/esm4/loadglob.hpp new file mode 100644 index 0000000000..15cf4a07ac --- /dev/null +++ b/components/esm4/loadglob.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2020 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_GLOB_H +#define ESM4_GLOB_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct GlobalVariable + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + std::uint8_t mType; + float mValue; + + GlobalVariable(); + virtual ~GlobalVariable(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_GLOB_H diff --git a/components/esm4/loadgras.cpp b/components/esm4/loadgras.cpp new file mode 100644 index 0000000000..3818654fc1 --- /dev/null +++ b/components/esm4/loadgras.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 "loadgras.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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::GRAS::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Grass::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Grass::blank() +//{ +//} diff --git a/components/esm4/loadgras.hpp b/components/esm4/loadgras.hpp new file mode 100644 index 0000000000..d7754d03d9 --- /dev/null +++ b/components/esm4/loadgras.hpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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/components/esm4/loadgrup.hpp b/components/esm4/loadgrup.hpp new file mode 100644 index 0000000000..8eb53a0e58 --- /dev/null +++ b/components/esm4/loadgrup.hpp @@ -0,0 +1,158 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#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/components/esm4/loadhair.cpp b/components/esm4/loadhair.cpp new file mode 100644 index 0000000000..661c333cbd --- /dev/null +++ b/components/esm4/loadhair.cpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2016, 2018, 2020 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 "loadhair.hpp" + +#include +//#include // FIXME: for debugging only + +#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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::HAIR::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Hair::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Hair::blank() +//{ +//} diff --git a/components/esm4/loadhair.hpp b/components/esm4/loadhair.hpp new file mode 100644 index 0000000000..c9a4fc2756 --- /dev/null +++ b/components/esm4/loadhair.hpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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; // mesh + std::string mIcon; // texture + + 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/components/esm4/loadhdpt.cpp b/components/esm4/loadhdpt.cpp new file mode 100644 index 0000000000..f188890246 --- /dev/null +++ b/components/esm4/loadhdpt.cpp @@ -0,0 +1,104 @@ +/* + Copyright (C) 2019-2021 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 "loadhdpt.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::HeadPart::HeadPart() : mFormId(0), mFlags(0), mData(0), mAdditionalPart(0), mBaseTexture(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + + mTriFile.resize(3); +} + +ESM4::HeadPart::~HeadPart() +{ +} + +void ESM4::HeadPart::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + + std::uint32_t type; + + 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.getLocalizedString(mFullName); break; + case ESM4::SUB_DATA: reader.get(mData); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_HNAM: reader.getFormId(mAdditionalPart); break; + case ESM4::SUB_NAM0: // TES5 + { + reader.get(type); + + break; + } + case ESM4::SUB_NAM1: // TES5 + { + std::string file; + reader.getZString(file); + + // FIXME: check type >= 0 && type < 3 + mTriFile[type] = std::move(file); + + break; + } + case ESM4::SUB_TNAM: reader.getFormId(mBaseTexture); break; + case ESM4::SUB_PNAM: + case ESM4::SUB_MODS: + case ESM4::SUB_MODT: + case ESM4::SUB_RNAM: + { + //std::cout << "HDPT " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::HDPT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::HeadPart::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::HeadPart::blank() +//{ +//} diff --git a/components/esm4/loadhdpt.hpp b/components/esm4/loadhdpt.hpp new file mode 100644 index 0000000000..d93411bb7e --- /dev/null +++ b/components/esm4/loadhdpt.hpp @@ -0,0 +1,66 @@ +/* + Copyright (C) 2019, 2020 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_HDPT_H +#define ESM4_HDPT_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct HeadPart + { + 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::uint8_t mData; + + FormId mAdditionalPart; + + std::vector mTriFile; + FormId mBaseTexture; + + HeadPart(); + virtual ~HeadPart(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_HDPT_H diff --git a/components/esm4/loadidle.cpp b/components/esm4/loadidle.cpp new file mode 100644 index 0000000000..3d45b76e82 --- /dev/null +++ b/components/esm4/loadidle.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 "loadidle.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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::IDLE::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::IdleAnimation::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::IdleAnimation::blank() +//{ +//} diff --git a/components/esm4/loadidle.hpp b/components/esm4/loadidle.hpp new file mode 100644 index 0000000000..cadd056122 --- /dev/null +++ b/components/esm4/loadidle.hpp @@ -0,0 +1,62 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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/components/esm4/loadidlm.cpp b/components/esm4/loadidlm.cpp new file mode 100644 index 0000000000..ba823df300 --- /dev/null +++ b/components/esm4/loadidlm.cpp @@ -0,0 +1,103 @@ +/* + Copyright (C) 2019, 2020 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 "loadidlm.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::IdleMarker::IdleMarker() : mFormId(0), mFlags(0), mIdleFlags(0), mIdleCount(0), mIdleTimer(0.f), mIdleAnim(0) +{ + mEditorId.clear(); +} + +ESM4::IdleMarker::~IdleMarker() +{ +} + +void ESM4::IdleMarker::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_IDLF: reader.get(mIdleFlags); break; + case ESM4::SUB_IDLC: + { + if (subHdr.dataSize != 1) // FO3 can have 4? + { + reader.skipSubRecordData(); + break; + } + + reader.get(mIdleCount); + break; + } + case ESM4::SUB_IDLT: reader.get(mIdleTimer); break; + case ESM4::SUB_IDLA: + { + bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134; + if (esmVer == ESM::VER_094 || isFONV) // FO3? 4 or 8 bytes + { + reader.skipSubRecordData(); + break; + } + + mIdleAnim.resize(mIdleCount); + for (unsigned int i = 0; i < static_cast(mIdleCount); ++i) + reader.get(mIdleAnim.at(i)); + break; + } + case ESM4::SUB_OBND: // object bounds + { + //std::cout << "IDLM " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::IDLM::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::IdleMarker::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::IdleMarker::blank() +//{ +//} diff --git a/components/esm4/loadidlm.hpp b/components/esm4/loadidlm.hpp new file mode 100644 index 0000000000..78d03a7024 --- /dev/null +++ b/components/esm4/loadidlm.hpp @@ -0,0 +1,63 @@ +/* + Copyright (C) 2019, 2020 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_IDLM_H +#define ESM4_IDLM_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct IdleMarker + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::string mModel; + + std::uint8_t mIdleFlags; + std::uint8_t mIdleCount; + float mIdleTimer; + std::vector mIdleAnim; + + IdleMarker(); + virtual ~IdleMarker(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_IDLM_H diff --git a/components/esm4/loadimod.cpp b/components/esm4/loadimod.cpp new file mode 100644 index 0000000000..9950c59b7b --- /dev/null +++ b/components/esm4/loadimod.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#include "loadimod.hpp" + +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::ItemMod::ItemMod() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); +} + +ESM4::ItemMod::~ItemMod() +{ +} + +void ESM4::ItemMod::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_OBND: + case ESM4::SUB_FULL: + case ESM4::SUB_MODL: + case ESM4::SUB_ICON: + case ESM4::SUB_MICO: + case ESM4::SUB_SCRI: + case ESM4::SUB_DESC: + case ESM4::SUB_YNAM: + case ESM4::SUB_ZNAM: + case ESM4::SUB_DATA: + { + //std::cout << "IMOD " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + std::cout << "IMOD " << ESM::printName(subHdr.typeId) << " skipping..." + << subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + //throw std::runtime_error("ESM4::IMOD::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::ItemMod::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::ItemMod::blank() +//{ +//} diff --git a/components/esm4/loadimod.hpp b/components/esm4/loadimod.hpp new file mode 100644 index 0000000000..a72fbe472d --- /dev/null +++ b/components/esm4/loadimod.hpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#ifndef ESM4_IMOD_H +#define ESM4_IMOD_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct ItemMod + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + ItemMod(); + virtual ~ItemMod(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_IMOD_H diff --git a/components/esm4/loadinfo.cpp b/components/esm4/loadinfo.cpp new file mode 100644 index 0000000000..a68774112b --- /dev/null +++ b/components/esm4/loadinfo.cpp @@ -0,0 +1,222 @@ +/* + Copyright (C) 2020-2021 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 "loadinfo.hpp" + +#include +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::DialogInfo::DialogInfo() : mFormId(0), mFlags(0), mQuest(0), mSound(0), + mDialType(0), mNextSpeaker(0), mInfoFlags(0), mParam3(0) +{ + std::memset(&mResponseData, 0, sizeof(TargetResponseData)); + mResponse.clear(); + mNotes.clear(); + mEdits.clear(); + + std::memset(&mTargetCondition, 0, sizeof(TargetCondition)); + + std::memset(&mScript.scriptHeader, 0, sizeof(ScriptHeader)); + mScript.scriptSource.clear(); + mScript.globReference = 0; +} + +ESM4::DialogInfo::~DialogInfo() +{ +} + +void ESM4::DialogInfo::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + + mEditorId = formIdToString(mFormId); // FIXME: quick workaround to use existing code + + static ScriptLocalVariableData localVar; + bool ignore = false; + + while (reader.getSubRecordHeader()) + { + const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + switch (subHdr.typeId) + { + case ESM4::SUB_QSTI: reader.getFormId(mQuest); break; // FormId quest id + case ESM4::SUB_SNDD: reader.getFormId(mSound); break; // FO3 (not used in FONV?) + case ESM4::SUB_TRDT: + { + if (subHdr.dataSize == 16) // TES4 + reader.get(&mResponseData, 16); + else if (subHdr.dataSize == 20) // FO3 + reader.get(&mResponseData, 20); + else // FO3/FONV + { + reader.get(mResponseData); + if (mResponseData.sound) + reader.adjustFormId(mResponseData.sound); + } + + break; + } + case ESM4::SUB_NAM1: reader.getZString(mResponse); break; // response text + case ESM4::SUB_NAM2: reader.getZString(mNotes); break; // actor notes + case ESM4::SUB_NAM3: reader.getZString(mEdits); break; // not in TES4 + case ESM4::SUB_CTDA: // FIXME: how to detect if 1st/2nd param is a formid? + { + if (subHdr.dataSize == 24) // TES4 + reader.get(&mTargetCondition, 24); + else if (subHdr.dataSize == 20) // FO3 + reader.get(&mTargetCondition, 20); + else if (subHdr.dataSize == 28) + { + reader.get(mTargetCondition); // FO3/FONV + if (mTargetCondition.reference) + reader.adjustFormId(mTargetCondition.reference); + } + else // TES5 + { + reader.get(&mTargetCondition, 20); + if (subHdr.dataSize == 36) + reader.getFormId(mParam3); + reader.get(mTargetCondition.runOn); + reader.get(mTargetCondition.reference); + if (mTargetCondition.reference) + reader.adjustFormId(mTargetCondition.reference); + reader.skipSubRecordData(4); // unknown + } + + break; + } + case ESM4::SUB_SCHR: + { + if (!ignore) + reader.get(mScript.scriptHeader); + else + reader.skipSubRecordData(); // TODO: does the second one ever used? + + break; + } + case ESM4::SUB_SCDA: reader.skipSubRecordData(); break; // compiled script data + case ESM4::SUB_SCTX: reader.getString(mScript.scriptSource); break; + case ESM4::SUB_SCRO: reader.getFormId(mScript.globReference); break; + case ESM4::SUB_SLSD: + { + localVar.clear(); + reader.get(localVar.index); + reader.get(localVar.unknown1); + reader.get(localVar.unknown2); + reader.get(localVar.unknown3); + reader.get(localVar.type); + reader.get(localVar.unknown4); + // WARN: assumes SCVR will follow immediately + + break; + } + case ESM4::SUB_SCVR: // assumed always pair with SLSD + { + reader.getZString(localVar.variableName); + + mScript.localVarData.push_back(localVar); + + break; + } + case ESM4::SUB_SCRV: + { + std::uint32_t index; + reader.get(index); + + mScript.localRefVarIndex.push_back(index); + + break; + } + case ESM4::SUB_NEXT: // FO3/FONV marker for next script header + { + ignore = true; + + break; + } + case ESM4::SUB_DATA: // always 3 for TES4 ? + { + if (subHdr.dataSize == 4) // FO3/FONV + { + reader.get(mDialType); + reader.get(mNextSpeaker); + reader.get(mInfoFlags); + } + else + reader.skipSubRecordData(); // FIXME + break; + } + case ESM4::SUB_NAME: // FormId add topic (not always present) + case ESM4::SUB_CTDT: // older version of CTDA? 20 bytes + case ESM4::SUB_SCHD: // 28 bytes + case ESM4::SUB_TCLT: // FormId choice + case ESM4::SUB_TCLF: // FormId + case ESM4::SUB_PNAM: // TES4 DLC + case ESM4::SUB_TPIC: // TES4 DLC + case ESM4::SUB_ANAM: // FO3 speaker formid + case ESM4::SUB_DNAM: // FO3 speech challenge + case ESM4::SUB_KNAM: // FO3 formid + case ESM4::SUB_LNAM: // FONV + case ESM4::SUB_TCFU: // FONV + case ESM4::SUB_TIFC: // TES5 + case ESM4::SUB_TWAT: // TES5 + case ESM4::SUB_CIS2: // TES5 + case ESM4::SUB_CNAM: // TES5 + case ESM4::SUB_ENAM: // TES5 + case ESM4::SUB_EDID: // TES5 + case ESM4::SUB_VMAD: // TES5 + case ESM4::SUB_BNAM: // TES5 + case ESM4::SUB_SNAM: // TES5 + case ESM4::SUB_ONAM: // TES5 + case ESM4::SUB_QNAM: // TES5 for mScript + case ESM4::SUB_RNAM: // TES5 + { + //std::cout << "INFO " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + std::cout << "INFO " << ESM::printName(subHdr.typeId) << " skipping..." + << subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + //throw std::runtime_error("ESM4::INFO::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::DialogInfo::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::DialogInfo::blank() +//{ +//} diff --git a/components/esm4/loadinfo.hpp b/components/esm4/loadinfo.hpp new file mode 100644 index 0000000000..be7090cd32 --- /dev/null +++ b/components/esm4/loadinfo.hpp @@ -0,0 +1,90 @@ +/* + Copyright (C) 2020 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_INFO_H +#define ESM4_INFO_H + +#include +#include + +#include "formid.hpp" +#include "script.hpp" // TargetCondition +#include "dialogue.hpp" // DialType + +namespace ESM4 +{ + class Reader; + class Writer; + + enum InfoFlag + { + INFO_Goodbye = 0x0001, + INFO_Random = 0x0002, + INFO_SayOnce = 0x0004, + INFO_RunImmediately = 0x0008, + INFO_InfoRefusal = 0x0010, + INFO_RandomEnd = 0x0020, + INFO_RunForRumors = 0x0040, + INFO_SpeechChallenge = 0x0080, + INFO_SayOnceADay = 0x0100, + INFO_AlwaysDarken = 0x0200 + }; + + struct DialogInfo + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; // FIXME: no such record for INFO, but keep here to avoid extra work for now + + FormId mQuest; + FormId mSound; // unused? + + TargetResponseData mResponseData; + std::string mResponse; + std::string mNotes; + std::string mEdits; + + std::uint8_t mDialType; // DialType + std::uint8_t mNextSpeaker; + std::uint16_t mInfoFlags; // see above enum + + TargetCondition mTargetCondition; + FormId mParam3; // TES5 only + + ScriptDefinition mScript; // FIXME: ignoring the second one after the NEXT sub-record + + DialogInfo(); + virtual ~DialogInfo(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_INFO_H diff --git a/components/esm4/loadingr.cpp b/components/esm4/loadingr.cpp new file mode 100644 index 0000000000..03994dbfb4 --- /dev/null +++ b/components/esm4/loadingr.cpp @@ -0,0 +1,133 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loadingr.hpp" + +#include +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Ingredient::Ingredient() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScriptId(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()) + { + reader.getLocalizedString(mFullName); 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() == ESM::VER_094 || reader.esmVersion() == ESM::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(mScriptId); 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::INGR::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Ingredient::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Ingredient::blank() +//{ +//} diff --git a/components/esm4/loadingr.hpp b/components/esm4/loadingr.hpp new file mode 100644 index 0000000000..44312eb24b --- /dev/null +++ b/components/esm4/loadingr.hpp @@ -0,0 +1,83 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "effect.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 mScriptId; + 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/components/esm4/loadkeym.cpp b/components/esm4/loadkeym.cpp new file mode 100644 index 0000000000..a5aba621af --- /dev/null +++ b/components/esm4/loadkeym.cpp @@ -0,0 +1,93 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 "loadkeym.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Key::Key() : mFormId(0), mFlags(0), mPickUpSound(0), mDropSound(0), mBoundRadius(0.f), mScriptId(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mIcon.clear(); + mMiniIcon.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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_ICON: reader.getZString(mIcon); break; + case ESM4::SUB_MICO: reader.getZString(mMiniIcon); break; // FO3 + case ESM4::SUB_DATA: reader.get(mData); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); break; + case ESM4::SUB_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_YNAM: reader.getFormId(mPickUpSound); break; + case ESM4::SUB_ZNAM: reader.getFormId(mDropSound); break; + case ESM4::SUB_MODT: + case ESM4::SUB_KSIZ: + case ESM4::SUB_KWDA: + case ESM4::SUB_OBND: + case ESM4::SUB_VMAD: + { + //std::cout << "KEYM " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::KEYM::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Key::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Key::blank() +//{ +//} diff --git a/components/esm4/loadkeym.hpp b/components/esm4/loadkeym.hpp new file mode 100644 index 0000000000..d59b3826d5 --- /dev/null +++ b/components/esm4/loadkeym.hpp @@ -0,0 +1,77 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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 + std::string mMiniIcon; // inventory + + FormId mPickUpSound; + FormId mDropSound; + + float mBoundRadius; + FormId mScriptId; + + 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/components/esm4/loadland.cpp b/components/esm4/loadland.cpp new file mode 100644 index 0000000000..2bb260da33 --- /dev/null +++ b/components/esm4/loadland.cpp @@ -0,0 +1,255 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 "loadland.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +#include +#include + +#include // FIXME: debug only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Land::Land() : mFormId(0), mFlags(0), mLandFlags(0), mDataTypes(0) +{ + for (int i = 0; i < 4; ++i) + { + mTextures[i].base.formId = 0; + mTextures[i].base.quadrant = 0; + mTextures[i].base.unknown1 = 0; + mTextures[i].base.unknown2 = 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 + + //std::map uniqueTextures; // FIXME: for temp testing only + + 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.getExact(base)) + { + assert(base.quadrant < 4 && base.quadrant >= 0 && "base texture quadrant index error"); + + reader.adjustFormId(base.formId); + mTextures[base.quadrant].base = std::move(base); +#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.texture.layerIndex << std::endl; + mTextures[currentAddQuad].layers.push_back(layer); + } + reader.get(layer.texture); + reader.adjustFormId(layer.texture.formId); + assert(layer.texture.quadrant < 4 && layer.texture.quadrant >= 0 + && "additional texture quadrant index error"); +#if 0 + FormId txt = layer.texture.formId; + std::map::iterator lb = uniqueTextures.lower_bound(txt); + if (lb != uniqueTextures.end() && !(uniqueTextures.key_comp()(txt, lb->first))) + { + lb->second += 1; + } + else + uniqueTextures.insert(lb, std::make_pair(txt, 1)); +#endif +#if 0 + std::cout << "Additional Texture formId: 0x" + << std::hex << layer.texture.formId + << ", quad " << std::dec << (int)layer.texture.quadrant << std::endl; + std::cout << "Additional Texture layer: " + << std::dec << (int)layer.texture.layerIndex << std::endl; +#endif + currentAddQuad = layer.texture.quadrant; + break; + } + case ESM4::SUB_VTXT: + { + assert(currentAddQuad != -1 && "VTXT without ATXT found"); + + int count = (int)reader.subRecordHeader().dataSize / sizeof(ESM4::Land::VTXT); + assert((reader.subRecordHeader().dataSize % sizeof(ESM4::Land::VTXT)) == 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.texture.layerIndex == mTextures[currentAddQuad].layers.size()-1 + //&& "additional texture layer index error"); + + currentAddQuad = -1; + layer.data.clear(); + // 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); + assert((reader.subRecordHeader().dataSize % sizeof(FormId)) == 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 " + ESM::printName(subHdr.typeId)); + } + } + + if (currentAddQuad != -1) + { + // FIXME: not sure if it happens here as well + std::cout << "ESM4::Land VTXT empty layer " << (int)layer.texture.layerIndex << " quad " << (int)layer.texture.quadrant << std::endl; + mTextures[currentAddQuad].layers.push_back(layer); + } + + bool missing = false; + for (int i = 0; i < 4; ++i) + { + if (mTextures[i].base.formId == 0) + { + //std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " missing base, quad " << i << std::endl; + //std::cout << "layers " << mTextures[i].layers.size() << std::endl; + // NOTE: can't set the default here since FO3/FONV may have different defaults + //mTextures[i].base.formId = 0x000008C0; // TerrainHDDirt01.dds + missing = true; + } + //else + //{ + // std::cout << "ESM4::LAND " << ESM4::formIdToString(mFormId) << " base, quad " << i << std::endl; + // std::cout << "layers " << mTextures[i].layers.size() << std::endl; + //} + } + // 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/components/esm4/loadland.hpp b/components/esm4/loadland.hpp new file mode 100644 index 0000000000..6d53a99ff2 --- /dev/null +++ b/components/esm4/loadland.hpp @@ -0,0 +1,136 @@ +/* + Copyright (C) 2015-2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + + 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 + // (for TES4 - based on vanilla observations) + static const int QUAD_TEXTURE_PER_SIDE = 6; + +#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 = top left, 3 = top right + std::uint8_t unknown1; + std::uint16_t unknown2; + }; + + struct ATXT + { + FormId formId; + std::uint8_t quadrant; // 0 = bottom left, 1 = bottom right, 2 = top left, 3 = top right + std::uint8_t unknown; + std::uint16_t layerIndex; // 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 texture; + std::vector data; // alpha data + }; + + 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 = top left, 3 = top 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/components/esm4/loadlgtm.cpp b/components/esm4/loadlgtm.cpp new file mode 100644 index 0000000000..79867d4106 --- /dev/null +++ b/components/esm4/loadlgtm.cpp @@ -0,0 +1,105 @@ +/* + Copyright (C) 2020-2021 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#include "loadlgtm.hpp" + +#include +#include // FLT_MAX for gcc +//#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::LightingTemplate::LightingTemplate() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + + mLighting.ambient = 0; + mLighting.directional = 0; + mLighting.fogColor = 0; + mLighting.fogNear = 0.f; + mLighting.fogFar = 0.f; + mLighting.rotationXY = 0; + mLighting.rotationZ = 0; + mLighting.fogDirFade = 0.f; + mLighting.fogClipDist = 0.f; + mLighting.fogPower = FLT_MAX; // hack way to detect TES4 +} + +ESM4::LightingTemplate::~LightingTemplate() +{ +} + +void ESM4::LightingTemplate::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_DATA: + { + if (subHdr.dataSize == 36) // TES4 + reader.get(&mLighting, 36); + if (subHdr.dataSize == 40) // FO3/FONV + reader.get(mLighting); + else if (subHdr.dataSize == 92) // TES5 + { + reader.get(mLighting); + reader.skipSubRecordData(52); // FIXME + } + else + reader.skipSubRecordData(); // throw? + + break; + } + case ESM4::SUB_DALC: // TES5 + { + //std::cout << "LGTM " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::LGTM::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::LightingTemplate::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::LightingTemplate::blank() +//{ +//} diff --git a/components/esm4/loadlgtm.hpp b/components/esm4/loadlgtm.hpp new file mode 100644 index 0000000000..9724d8b16e --- /dev/null +++ b/components/esm4/loadlgtm.hpp @@ -0,0 +1,63 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#ifndef ESM4_LGTM_H +#define ESM4_LGTM_H + +#include +#include + +#include "formid.hpp" +#include "lighting.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + typedef std::uint32_t FormId; + + struct LightingTemplate + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + Lighting mLighting; + + LightingTemplate(); + virtual ~LightingTemplate(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_LGTM_H diff --git a/components/esm4/loadligh.cpp b/components/esm4/loadligh.cpp new file mode 100644 index 0000000000..bdd125c6bb --- /dev/null +++ b/components/esm4/loadligh.cpp @@ -0,0 +1,120 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 "loadligh.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Light::Light() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScriptId(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 == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_DATA: + { + // FIXME: TES4 might be uint32 as well, need to check + if (isFONV || (esmVer == ESM::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() == ESM::VER_094 || reader.esmVersion() == ESM::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) // TES4 + { + 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(mScriptId); 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::LIGH::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Light::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Light::blank() +//{ +//} diff --git a/components/esm4/loadligh.hpp b/components/esm4/loadligh.hpp new file mode 100644 index 0000000000..4a0120d71a --- /dev/null +++ b/components/esm4/loadligh.hpp @@ -0,0 +1,101 @@ +/* + Copyright (C) 2016, 2018-2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Light + { + struct Data + { + std::uint32_t time; // FO/FONV only + float duration; + std::uint32_t radius; + std::uint32_t colour; // RGBA + // flags: + // 0x00000001 = Dynamic + // 0x00000002 = Can be Carried + // 0x00000004 = Negative + // 0x00000008 = Flicker + // 0x00000020 = Off By Default + // 0x00000040 = Flicker Slow + // 0x00000080 = Pulse + // 0x00000100 = Pulse Slow + // 0x00000200 = Spot Light + // 0x00000400 = Spot Shadow + 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), colour(0), flags(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 mScriptId; + 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/components/esm4/loadltex.cpp b/components/esm4/loadltex.cpp new file mode 100644 index 0000000000..577d5dc8e1 --- /dev/null +++ b/components/esm4/loadltex.cpp @@ -0,0 +1,107 @@ +/* + 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 "loadltex.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +#include +#include +//#include // FIXME: debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +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 == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::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() == ESM::VER_094 || reader.esmVersion() == ESM::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 " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::LandTexture::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::LandTexture::blank() +//{ +//} diff --git a/components/esm4/loadltex.hpp b/components/esm4/loadltex.hpp new file mode 100644 index 0000000000..934e7cc708 --- /dev/null +++ b/components/esm4/loadltex.hpp @@ -0,0 +1,75 @@ +/* + Copyright (C) 2015-2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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/components/esm4/loadlvlc.cpp b/components/esm4/loadlvlc.cpp new file mode 100644 index 0000000000..0088446277 --- /dev/null +++ b/components/esm4/loadlvlc.cpp @@ -0,0 +1,130 @@ +/* + Copyright (C) 2016, 2018, 2020 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 "loadlvlc.hpp" + +#include +//#include // FIXME + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::LevelledCreature::LevelledCreature() : mFormId(0), mFlags(0), mScriptId(0), mTemplate(0), + mChanceNone(0), mLvlCreaFlags(0) +{ + mEditorId.clear(); +} + +ESM4::LevelledCreature::~LevelledCreature() +{ +} + +void ESM4::LevelledCreature::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(mScriptId); 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::LVLC::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +bool ESM4::LevelledCreature::calcAllLvlLessThanPlayer() const +{ + if (mHasLvlCreaFlags) + return (mLvlCreaFlags & 0x01) != 0; + else + return (mChanceNone & 0x80) != 0; // FIXME: 0x80 is just a guess +} + +bool ESM4::LevelledCreature::calcEachItemInCount() const +{ + if (mHasLvlCreaFlags) + return (mLvlCreaFlags & 0x02) != 0; + else + return true; // FIXME: just a guess +} + +std::int8_t ESM4::LevelledCreature::chanceNone() const +{ + if (mHasLvlCreaFlags) + return mChanceNone; + else + return (mChanceNone & 0x7f); // FIXME: 0x80 is just a guess +} + +//void ESM4::LevelledCreature::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::LevelledCreature::blank() +//{ +//} diff --git a/components/esm4/loadlvlc.hpp b/components/esm4/loadlvlc.hpp new file mode 100644 index 0000000000..ccd08944b5 --- /dev/null +++ b/components/esm4/loadlvlc.hpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" +#include "inventory.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct LevelledCreature + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + FormId mScriptId; + FormId mTemplate; + std::int8_t mChanceNone; + + bool mHasLvlCreaFlags; + std::uint8_t mLvlCreaFlags; + + std::vector mLvlObject; + + bool calcAllLvlLessThanPlayer() const; + bool calcEachItemInCount() const; + std::int8_t chanceNone() const; + + LevelledCreature(); + virtual ~LevelledCreature(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_LVLC_H diff --git a/components/esm4/loadlvli.cpp b/components/esm4/loadlvli.cpp new file mode 100644 index 0000000000..1237a0add4 --- /dev/null +++ b/components/esm4/loadlvli.cpp @@ -0,0 +1,142 @@ +/* + Copyright (C) 2016, 2018-2020 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 "loadlvli.hpp" + +#include +#include // FIXME: for debugging + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::LevelledItem::LevelledItem() : mFormId(0), mFlags(0), mChanceNone(0), mHasLvlItemFlags(false), + mLvlItemFlags(0), mData(0) +{ + mEditorId.clear(); +} + +ESM4::LevelledItem::~LevelledItem() +{ +} + +void ESM4::LevelledItem::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); mHasLvlItemFlags = true; 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_LLCT: + case ESM4::SUB_OBND: // FO3/FONV + case ESM4::SUB_COED: // FO3/FONV + case ESM4::SUB_LVLG: // FO3/FONV + { + + //std::cout << "LVLI " << ESM::printName(subHdr.typeId) << " skipping..." << subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::LVLI::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } + + // FIXME: testing + //if (mHasLvlItemFlags && mChanceNone >= 90) + //std::cout << "LVLI " << mEditorId << " chance none " << int(mChanceNone) << std::endl; +} + +bool ESM4::LevelledItem::calcAllLvlLessThanPlayer() const +{ + if (mHasLvlItemFlags) + return (mLvlItemFlags & 0x01) != 0; + else + return (mChanceNone & 0x80) != 0; // FIXME: 0x80 is just a guess +} + +bool ESM4::LevelledItem::calcEachItemInCount() const +{ + if (mHasLvlItemFlags) + return (mLvlItemFlags & 0x02) != 0; + else + return mData != 0; +} + +std::int8_t ESM4::LevelledItem::chanceNone() const +{ + if (mHasLvlItemFlags) + return mChanceNone; + else + return (mChanceNone & 0x7f); // FIXME: 0x80 is just a guess +} + +bool ESM4::LevelledItem::useAll() const +{ + if (mHasLvlItemFlags) + return (mLvlItemFlags & 0x04) != 0; + else + return false; +} + +//void ESM4::LevelledItem::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::LevelledItem::blank() +//{ +//} diff --git a/components/esm4/loadlvli.hpp b/components/esm4/loadlvli.hpp new file mode 100644 index 0000000000..0964370e63 --- /dev/null +++ b/components/esm4/loadlvli.hpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" +#include "inventory.hpp" // LVLO + +namespace ESM4 +{ + class Reader; + class Writer; + + struct LevelledItem + { + 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; + + bool mHasLvlItemFlags; + std::uint8_t mLvlItemFlags; + + std::uint8_t mData; + + std::vector mLvlObject; + + LevelledItem(); + virtual ~LevelledItem(); + + bool calcAllLvlLessThanPlayer() const; + bool calcEachItemInCount() const; + bool useAll() const; + std::int8_t chanceNone() const; + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_LVLI_H diff --git a/components/esm4/loadlvln.cpp b/components/esm4/loadlvln.cpp new file mode 100644 index 0000000000..fdd860b9d9 --- /dev/null +++ b/components/esm4/loadlvln.cpp @@ -0,0 +1,114 @@ +/* + Copyright (C) 2019-2021 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 "loadlvln.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::LevelledNpc::LevelledNpc() : mFormId(0), mFlags(0), mChanceNone(0), mLvlActorFlags(0), mListCount(0) +{ + mEditorId.clear(); + mModel.clear(); +} + +ESM4::LevelledNpc::~LevelledNpc() +{ +} + +void ESM4::LevelledNpc::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + //std::uint32_t esmVer = reader.esmVersion(); // currently unused + + 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_LLCT: reader.get(mListCount); break; + case ESM4::SUB_LVLD: reader.get(mChanceNone); break; + case ESM4::SUB_LVLF: reader.get(mLvlActorFlags); 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); + break; + } + else + throw std::runtime_error("ESM4::LVLN::load - " + mEditorId + " LVLO size error"); + } +// else if (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || isFONV) +// { +// std::uint32_t level; +// reader.get(level); +// lvlo.level = static_cast(level); +// reader.get(lvlo.item); +// std::uint32_t count; +// reader.get(count); +// lvlo.count = static_cast(count); +// } + else + reader.get(lvlo); + + reader.adjustFormId(lvlo.item); + mLvlObject.push_back(lvlo); + break; + } + case ESM4::SUB_COED: // owner + case ESM4::SUB_OBND: // object bounds + case ESM4::SUB_MODT: // model texture data + { + //std::cout << "LVLN " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::LVLN::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::LevelledNpc::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::LevelledNpc::blank() +//{ +//} diff --git a/components/esm4/loadlvln.hpp b/components/esm4/loadlvln.hpp new file mode 100644 index 0000000000..057ed9581d --- /dev/null +++ b/components/esm4/loadlvln.hpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2019, 2020 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_LVLN_H +#define ESM4_LVLN_H + +#include +#include + +#include "formid.hpp" +#include "inventory.hpp" // LVLO + +namespace ESM4 +{ + class Reader; + class Writer; + + struct LevelledNpc + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::string mModel; + + std::int8_t mChanceNone; + std::uint8_t mLvlActorFlags; + + std::uint8_t mListCount; + std::vector mLvlObject; + + LevelledNpc(); + virtual ~LevelledNpc(); + + inline bool calcAllLvlLessThanPlayer() const { return (mLvlActorFlags & 0x01) != 0; } + inline bool calcEachItemInCount() const { return (mLvlActorFlags & 0x02) != 0; } + inline std::int8_t chanceNone() const { return mChanceNone; } + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_LVLN_H diff --git a/components/esm4/loadmato.cpp b/components/esm4/loadmato.cpp new file mode 100644 index 0000000000..fdbdc66ebc --- /dev/null +++ b/components/esm4/loadmato.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 "loadmato.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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::MATO::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Material::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Material::blank() +//{ +//} diff --git a/components/esm4/loadmato.hpp b/components/esm4/loadmato.hpp new file mode 100644 index 0000000000..9b0d6c3947 --- /dev/null +++ b/components/esm4/loadmato.hpp @@ -0,0 +1,58 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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/components/esm4/loadmisc.cpp b/components/esm4/loadmisc.cpp new file mode 100644 index 0000000000..ee3c10d0b6 --- /dev/null +++ b/components/esm4/loadmisc.cpp @@ -0,0 +1,95 @@ +/* + Copyright (C) 2016, 2018, 2020-2021 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 "loadmisc.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::MiscItem::MiscItem() : mFormId(0), mFlags(0), mPickUpSound(0), mDropSound(0), mBoundRadius(0.f), mScriptId(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mIcon.clear(); + mMiniIcon.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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_ICON: reader.getZString(mIcon); break; + case ESM4::SUB_MICO: reader.getZString(mMiniIcon); break; // FO3 + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); break; + case ESM4::SUB_DATA: reader.get(mData); break; + case ESM4::SUB_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_YNAM: reader.getFormId(mPickUpSound); break; + case ESM4::SUB_ZNAM: reader.getFormId(mDropSound); 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_RNAM: // FONV + { + //std::cout << "MISC " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::MISC::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::MiscItem::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::MiscItem::blank() +//{ +//} diff --git a/components/esm4/loadmisc.hpp b/components/esm4/loadmisc.hpp new file mode 100644 index 0000000000..94f09b1a23 --- /dev/null +++ b/components/esm4/loadmisc.hpp @@ -0,0 +1,77 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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 + std::string mMiniIcon; // inventory + + FormId mPickUpSound; + FormId mDropSound; + + float mBoundRadius; + FormId mScriptId; + + 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/components/esm4/loadmset.cpp b/components/esm4/loadmset.cpp new file mode 100644 index 0000000000..b7859f9fa5 --- /dev/null +++ b/components/esm4/loadmset.cpp @@ -0,0 +1,115 @@ +/* + Copyright (C) 2020 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 "loadmset.hpp" + +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::MediaSet::MediaSet() : mFormId(0), mFlags(0), mSetType(-1), mEnabled(0), + mBoundaryDayOuter(0.f), mBoundaryDayMiddle(0.f), mBoundaryDayInner(0.f), + mBoundaryNightOuter(0.f), mBoundaryNightMiddle(0.f), mBoundaryNightInner(0.f), + mLevel8(0.f), mLevel9(0.f), mLevel0(0.f), mLevelA(0.f), mLevelB(0.f), mLevelC(0.f), + mTime1(0.f), mTime2(0.f), mTime3(0.f), mTime4(0.f), + mSoundIntro(0), mSoundOutro(0) +{ + mEditorId.clear(); + mFullName.clear(); + + mSet2.clear(); + mSet3.clear(); + mSet4.clear(); + mSet5.clear(); + mSet6.clear(); + mSet7.clear(); +} + +ESM4::MediaSet::~MediaSet() +{ +} + +void ESM4::MediaSet::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_NAM1: reader.get(mSetType); break; + case ESM4::SUB_PNAM: reader.get(mEnabled); break; + case ESM4::SUB_NAM2: reader.getZString(mSet2); break; + case ESM4::SUB_NAM3: reader.getZString(mSet3); break; + case ESM4::SUB_NAM4: reader.getZString(mSet4); break; + case ESM4::SUB_NAM5: reader.getZString(mSet5); break; + case ESM4::SUB_NAM6: reader.getZString(mSet6); break; + case ESM4::SUB_NAM7: reader.getZString(mSet7); break; + case ESM4::SUB_HNAM: reader.getFormId(mSoundIntro); break; + case ESM4::SUB_INAM: reader.getFormId(mSoundOutro); break; + case ESM4::SUB_NAM8: reader.get(mLevel8); break; + case ESM4::SUB_NAM9: reader.get(mLevel9); break; + case ESM4::SUB_NAM0: reader.get(mLevel0); break; + case ESM4::SUB_ANAM: reader.get(mLevelA); break; + case ESM4::SUB_BNAM: reader.get(mLevelB); break; + case ESM4::SUB_CNAM: reader.get(mLevelC); break; + case ESM4::SUB_JNAM: reader.get(mBoundaryDayOuter); break; + case ESM4::SUB_KNAM: reader.get(mBoundaryDayMiddle); break; + case ESM4::SUB_LNAM: reader.get(mBoundaryDayInner); break; + case ESM4::SUB_MNAM: reader.get(mBoundaryNightOuter); break; + case ESM4::SUB_NNAM: reader.get(mBoundaryNightMiddle); break; + case ESM4::SUB_ONAM: reader.get(mBoundaryNightInner); break; + case ESM4::SUB_DNAM: reader.get(mTime1); break; + case ESM4::SUB_ENAM: reader.get(mTime2); break; + case ESM4::SUB_FNAM: reader.get(mTime3); break; + case ESM4::SUB_GNAM: reader.get(mTime4); break; + case ESM4::SUB_DATA: + { + //std::cout << "MSET " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::MSET::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::MediaSet::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::MediaSet::blank() +//{ +//} diff --git a/components/esm4/loadmset.hpp b/components/esm4/loadmset.hpp new file mode 100644 index 0000000000..b13c4d6f64 --- /dev/null +++ b/components/esm4/loadmset.hpp @@ -0,0 +1,99 @@ +/* + Copyright (C) 2020 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_MSET_H +#define ESM4_MSET_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct MediaSet + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::string mFullName; + + // -1 none, 0 battle, 1 location, 2 dungeon, 3 incidental + // Battle - intro (HNAM), loop (NAM2), outro (INAM) + // Location - day outer (NAM2), day middle (NAM3), day inner (NAM4), + // night outer (NAM5), night middle (NAM6), night inner (NAM7) + // Dungeon - intro (HNAM), battle (NAM2), explore (NAM3), suspence (NAM4), outro (INAM) + // Incidental - daytime (HNAM), nighttime (INAM) + std::int32_t mSetType; + // 0x01 day outer, 0x02 day middle, 0x04 day inner + // 0x08 night outer, 0x10 night middle, 0x20 night inner + std::uint8_t mEnabled; // for location + + float mBoundaryDayOuter; // % + float mBoundaryDayMiddle; // % + float mBoundaryDayInner; // % + float mBoundaryNightOuter; // % + float mBoundaryNightMiddle; // % + float mBoundaryNightInner; // % + + // start at 2 to reduce confusion + std::string mSet2; // NAM2 + std::string mSet3; // NAM3 + std::string mSet4; // NAM4 + std::string mSet5; // NAM5 + std::string mSet6; // NAM6 + std::string mSet7; // NAM7 + + float mLevel8; // dB + float mLevel9; // dB + float mLevel0; // dB + float mLevelA; // dB + float mLevelB; // dB + float mLevelC; // dB + + float mTime1; + float mTime2; + float mTime3; + float mTime4; + + FormId mSoundIntro; // HNAM + FormId mSoundOutro; // INAM + + MediaSet(); + virtual ~MediaSet(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_MSET_H diff --git a/components/esm4/loadmstt.cpp b/components/esm4/loadmstt.cpp new file mode 100644 index 0000000000..61d6147b2f --- /dev/null +++ b/components/esm4/loadmstt.cpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2019, 2020 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 "loadmstt.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::MovableStatic::MovableStatic() : mFormId(0), mFlags(0), mData(0), mLoopingSound(0) +{ + mEditorId.clear(); + mModel.clear(); +} + +ESM4::MovableStatic::~MovableStatic() +{ +} + +void ESM4::MovableStatic::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_SNAM: reader.get(mLoopingSound); break; + case ESM4::SUB_DEST: // destruction data + case ESM4::SUB_OBND: // object bounds + case ESM4::SUB_MODT: // model texture data + case ESM4::SUB_DMDL: + case ESM4::SUB_DMDT: + case ESM4::SUB_DSTD: + case ESM4::SUB_DSTF: + case ESM4::SUB_MODS: + case ESM4::SUB_FULL: + case ESM4::SUB_MODB: + { + //std::cout << "MSTT " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::MSTT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::MovableStatic::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::MovableStatic::blank() +//{ +//} diff --git a/components/esm4/loadmstt.hpp b/components/esm4/loadmstt.hpp new file mode 100644 index 0000000000..77dc453d35 --- /dev/null +++ b/components/esm4/loadmstt.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2019, 2020 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_MSTT_H +#define ESM4_MSTT_H + +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct MovableStatic + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::string mModel; + + std::int8_t mData; + FormId mLoopingSound; + + MovableStatic(); + virtual ~MovableStatic(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_MSTT_H diff --git a/components/esm4/loadmusc.cpp b/components/esm4/loadmusc.cpp new file mode 100644 index 0000000000..903bbb5a1f --- /dev/null +++ b/components/esm4/loadmusc.cpp @@ -0,0 +1,86 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#include "loadmusc.hpp" + +#include +//#include // FIXME: for debugging only + +//#include "formid.hpp" + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Music::Music() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + mMusicFile.clear(); +} + +ESM4::Music::~Music() +{ +} + +void ESM4::Music::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(mMusicFile); + //std::cout << "music: " << /*formIdToString(mFormId)*/mEditorId << " " << mMusicFile << std::endl; + break; + case ESM4::SUB_ANAM: // FONV float (attenuation in db? loop if positive?) + case ESM4::SUB_WNAM: // TES5 + case ESM4::SUB_PNAM: // TES5 + case ESM4::SUB_TNAM: // TES5 + { + //std::cout << "MUSC " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::MUSC::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Music::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Music::blank() +//{ +//} diff --git a/components/esm4/loadmusc.hpp b/components/esm4/loadmusc.hpp new file mode 100644 index 0000000000..ab3889a6c0 --- /dev/null +++ b/components/esm4/loadmusc.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#ifndef ESM4_MUSC_H +#define ESM4_MUSC_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Music + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::string mMusicFile; + + Music(); + virtual ~Music(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_MUSC_H diff --git a/components/esm4/loadnavi.cpp b/components/esm4/loadnavi.cpp new file mode 100644 index 0000000000..0655edb1e8 --- /dev/null +++ b/components/esm4/loadnavi.cpp @@ -0,0 +1,373 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 "loadnavi.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +#include +#include + +#include // FIXME: debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +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 island2; + island2.load(reader); + islandInfo.push_back(island2); // 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 indices, 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 == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::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 == ESM::VER_094 || esmVer == ESM::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 " + ESM::printName(subHdr.typeId)); + } + } + } +} + +//void ESM4::Navigation::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Navigation::blank() +//{ +//} diff --git a/components/esm4/loadnavi.hpp b/components/esm4/loadnavi.hpp new file mode 100644 index 0000000000..0db9650c4e --- /dev/null +++ b/components/esm4/loadnavi.hpp @@ -0,0 +1,117 @@ +/* + Copyright (C) 2015-2016, 2018, 2020 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 + +#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/components/esm4/loadnavm.cpp b/components/esm4/loadnavm.cpp new file mode 100644 index 0000000000..1f3eebe434 --- /dev/null +++ b/components/esm4/loadnavm.cpp @@ -0,0 +1,270 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 "loadnavm.hpp" + +#include +#include +#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 indices + for (unsigned int i = 0; i < divisor*divisor; ++i) + { + reader.get(count); // NOTE: count may be zero + + std::vector indices; + indices.resize(count); + for (std::vector::iterator it = indices.begin(); it != indices.end(); ++it) + { + reader.get(*it); + } + triSegments.push_back(indices); + } + 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.updateRecordRead(subSize); // WARNING: manual update + subSize = 0; + } + else + //const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + //std::cout << ESM::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 " + + ESM::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/components/esm4/loadnavm.hpp b/components/esm4/loadnavm.hpp new file mode 100644 index 0000000000..f5fb9b5e20 --- /dev/null +++ b/components/esm4/loadnavm.hpp @@ -0,0 +1,112 @@ +/* + Copyright (C) 2015, 2018, 2020 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 + +#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 indices + 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/components/esm4/loadnote.cpp b/components/esm4/loadnote.cpp new file mode 100644 index 0000000000..ee897258b7 --- /dev/null +++ b/components/esm4/loadnote.cpp @@ -0,0 +1,86 @@ +/* + Copyright (C) 2019-2021 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 "loadnote.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Note::Note() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mIcon.clear(); +} + +ESM4::Note::~Note() +{ +} + +void ESM4::Note::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.getLocalizedString(mFullName); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_ICON: reader.getZString(mIcon); break; + case ESM4::SUB_DATA: + case ESM4::SUB_MODB: + case ESM4::SUB_ONAM: + case ESM4::SUB_SNAM: + case ESM4::SUB_TNAM: + case ESM4::SUB_XNAM: + case ESM4::SUB_OBND: + { + //std::cout << "NOTE " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::NOTE::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Note::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Note::blank() +//{ +//} diff --git a/components/esm4/loadnote.hpp b/components/esm4/loadnote.hpp new file mode 100644 index 0000000000..1ba9bfec24 --- /dev/null +++ b/components/esm4/loadnote.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2019, 2020 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_NOTE_H +#define ESM4_NOTE_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Note + { + 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; + + Note(); + virtual ~Note(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_NOTE_H diff --git a/components/esm4/loadnpc.cpp b/components/esm4/loadnpc.cpp new file mode 100644 index 0000000000..e32ee6e90d --- /dev/null +++ b/components/esm4/loadnpc.cpp @@ -0,0 +1,333 @@ +/* + Copyright (C) 2016-2021 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 "loadnpc.hpp" + +#include +#include +#include // getline +#include // NOTE: for testing only +#include // NOTE: for testing only +#include // NOTE: for testing only + +//#include +//#include +#include + +#include "formid.hpp" // NOTE: for testing only +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Npc::Npc() : mFormId(0), mFlags(0), mIsTES4(false), mIsFONV(false), mRace(0), mClass(0), mHair(0), + mEyes(0), mHairLength(0.f), mHairColourId(0), mDeathItem(0), + mScriptId(0), mCombatStyle(0), mSoundBase(0), mSound(0), mSoundChance(0), + mFootWeight(0.f), mBoundRadius(0.f), mBaseTemplate(0), mWornArmor(0), + mDefaultOutfit(0), mSleepOutfit(0), mDefaultPkg(0), mFgRace(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + + mHairColour.red = 0; + mHairColour.green = 0; + mHairColour.blue = 0; + mHairColour.custom = 0; + + 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(); + mIsTES4 = esmVer == ESM::VER_080 || esmVer == ESM::VER_100; + mIsFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134; + //mIsTES5 = esmVer == ESM::VER_094 || esmVer == ESM::VER_170; // WARN: FO3 is also VER_094 + + 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; // not for TES5, see Race + case ESM4::SUB_FULL: reader.getLocalizedString(mFullName); 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; // not for TES5 + case ESM4::SUB_ENAM: reader.getFormId(mEyes); break; + // + case ESM4::SUB_INAM: reader.getFormId(mDeathItem); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); break; + // + case ESM4::SUB_AIDT: + { + if (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || mIsFONV) + { + reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip + break; + } + + reader.get(mAIData); // TES4 + break; + } + case ESM4::SUB_ACBS: + { + //if (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || mIsFONV) + if (subHdr.dataSize == 24) + reader.get(mBaseConfig); + else + reader.get(&mBaseConfig, 16); // TES4 + + break; + } + case ESM4::SUB_DATA: + { + if (esmVer == ESM::VER_094 || esmVer == ESM::VER_170 || mIsFONV) + { + 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: + { + if (reader.esmVersion() == ESM::VER_094 || reader.esmVersion() == ESM::VER_170) + reader.get(mWornArmor); + else + 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: reader.get(mHairLength); break; + case ESM4::SUB_HCLR: + { + reader.get(mHairColour.red); + reader.get(mHairColour.green); + reader.get(mHairColour.blue); + reader.get(mHairColour.custom); + + break; + } + case ESM4::SUB_TPLT: reader.get(mBaseTemplate); break; + case ESM4::SUB_FGGS: + { + mSymShapeModeCoefficients.resize(50); + for (std::size_t i = 0; i < 50; ++i) + reader.get(mSymShapeModeCoefficients.at(i)); + + break; + } + case ESM4::SUB_FGGA: + { + mAsymShapeModeCoefficients.resize(30); + for (std::size_t i = 0; i < 30; ++i) + reader.get(mAsymShapeModeCoefficients.at(i)); + + break; + } + case ESM4::SUB_FGTS: + { + mSymTextureModeCoefficients.resize(50); + for (std::size_t i = 0; i < 50; ++i) + reader.get(mSymTextureModeCoefficients.at(i)); + + break; + } + case ESM4::SUB_FNAM: + { + reader.get(mFgRace); + //std::cout << "race " << mEditorId << " " << mRace << std::endl; // FIXME + //std::cout << "fg race " << mEditorId << " " << mFgRace << std::endl; // FIXME + break; + } + case ESM4::SUB_PNAM: // FO3/FONV/TES5 + { + FormId headPart; + reader.getFormId(headPart); + mHeadParts.push_back(headPart); + + break; + } + case ESM4::SUB_HCLF: // TES5 hair colour + { + reader.getFormId(mHairColourId); + + break; + } + case ESM4::SUB_COCT: // TES5 + { + std::uint32_t count; + reader.get(count); + + break; + } + case ESM4::SUB_DOFT: reader.getFormId(mDefaultOutfit); break; + case ESM4::SUB_SOFT: reader.getFormId(mSleepOutfit); break; + case ESM4::SUB_DPLT: reader.getFormId(mDefaultPkg); break; // AI package list + case ESM4::SUB_DEST: + case ESM4::SUB_DSTD: + case ESM4::SUB_DSTF: + { +#if 1 + boost::scoped_array dataBuf(new unsigned char[subHdr.dataSize]); + reader.get(&dataBuf[0], subHdr.dataSize); + + std::ostringstream ss; + ss << mEditorId << " " << ESM::printName(subHdr.typeId) << ":size " << subHdr.dataSize << "\n"; + for (std::size_t i = 0; i < subHdr.dataSize; ++i) + { + if (dataBuf[i] > 64 && dataBuf[i] < 91) // looks like printable ascii char + ss << (char)(dataBuf[i]) << " "; + else + ss << std::setfill('0') << std::setw(2) << std::hex << (int)(dataBuf[i]); + if ((i & 0x000f) == 0xf) // wrap around + ss << "\n"; + else if (i < (size_t)(subHdr.dataSize-1)) // quiesce gcc + ss << " "; + } + std::cout << ss.str() << std::endl; +#else + //std::cout << "NPC_ " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); +#endif + break; + } + case ESM4::SUB_NAM6: // height mult + case ESM4::SUB_NAM7: // weight mult + case ESM4::SUB_ATKR: + case ESM4::SUB_CRIF: + case ESM4::SUB_CSDT: + case ESM4::SUB_DNAM: + case ESM4::SUB_ECOR: + case ESM4::SUB_ANAM: + case ESM4::SUB_ATKD: + case ESM4::SUB_ATKE: + case ESM4::SUB_FTST: + case ESM4::SUB_KSIZ: + case ESM4::SUB_KWDA: + case ESM4::SUB_NAM5: + case ESM4::SUB_NAM8: + case ESM4::SUB_NAM9: + case ESM4::SUB_NAMA: + case ESM4::SUB_OBND: + case ESM4::SUB_PRKR: + case ESM4::SUB_PRKZ: + case ESM4::SUB_QNAM: + case ESM4::SUB_SPCT: + case ESM4::SUB_TIAS: + case ESM4::SUB_TINC: + case ESM4::SUB_TINI: + case ESM4::SUB_TINV: + 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_ " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::NPC_::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Npc::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Npc::blank() +//{ +//} diff --git a/components/esm4/loadnpc.hpp b/components/esm4/loadnpc.hpp new file mode 100644 index 0000000000..d30ca5f14f --- /dev/null +++ b/components/esm4/loadnpc.hpp @@ -0,0 +1,230 @@ +/* + Copyright (C) 2016, 2018-2021 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 +#include + +#include "actor.hpp" +#include "inventory.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Npc + { + enum ACBS_TES4 + { + TES4_Female = 0x000001, + TES4_Essential = 0x000002, + TES4_Respawn = 0x000008, + TES4_AutoCalcStats = 0x000010, + TES4_PCLevelOffset = 0x000080, + TES4_NoLowLevelProc = 0x000200, + TES4_NoRumors = 0x002000, + TES4_Summonable = 0x004000, + TES4_NoPersuasion = 0x008000, // different meaning to crea + TES4_CanCorpseCheck = 0x100000 // opposite of crea + }; + + enum ACBS_FO3 + { + FO3_Female = 0x00000001, + FO3_Essential = 0x00000002, + FO3_PresetFace = 0x00000004, // Is CharGen Face Preset + FO3_Respawn = 0x00000008, + FO3_AutoCalcStats = 0x00000010, + FO3_PCLevelMult = 0x00000080, + FO3_UseTemplate = 0x00000100, + FO3_NoLowLevelProc = 0x00000200, + FO3_NoBloodSpray = 0x00000800, + FO3_NoBloodDecal = 0x00001000, + FO3_NoVATSMelee = 0x00100000, + FO3_AnyRace = 0x00400000, + FO3_AutoCalcServ = 0x00800000, + FO3_NoKnockdown = 0x04000000, + FO3_NotPushable = 0x08000000, + FO3_NoRotateHead = 0x40000000 + }; + + enum ACBS_TES5 + { + TES5_Female = 0x00000001, + TES5_Essential = 0x00000002, + TES5_PresetFace = 0x00000004, // Is CharGen Face Preset + TES5_Respawn = 0x00000008, + TES5_AutoCalcStats = 0x00000010, + TES5_Unique = 0x00000020, + TES5_NoStealth = 0x00000040, // Doesn't affect stealth meter + TES5_PCLevelMult = 0x00000080, + //TES5_Unknown = 0x00000100, // Audio template? + TES5_Protected = 0x00000800, + TES5_Summonable = 0x00004000, + TES5_NoBleeding = 0x00010000, + TES5_Owned = 0x00040000, // owned/follow? (Horses, Atronachs, NOT Shadowmere) + TES5_GenderAnim = 0x00080000, // Opposite Gender Anims + TES5_SimpleActor = 0x00100000, + TES5_LoopedScript = 0x00200000, // AAvenicci, Arcadia, Silvia, Afflicted, TortureVictims + TES5_LoopedAudio = 0x10000000, // AAvenicci, Arcadia, Silvia, DA02 Cultists, Afflicted, TortureVictims + TES5_IsGhost = 0x20000000, // Ghost/non-interactable (Ghosts, Nocturnal) + TES5_Invulnerable = 0x80000000 + }; + + enum Template_Flags + { + TES5_UseTraits = 0x0001, // Destructible Object; Traits tab, including race, gender, height, weight, + // voice type, death item; Sounds tab; Animation tab; Character Gen tabs + TES5_UseStats = 0x0002, // Stats tab, including level, autocalc, skills, health/magicka/stamina, + // speed, bleedout, class + TES5_UseFactions = 0x0004, // both factions and assigned crime faction + TES5_UseSpellList = 0x0008, // both spells and perks + TES5_UseAIData = 0x0010, // AI Data tab, including aggression/confidence/morality, combat style and + // gift filter + TES5_UseAIPackage = 0x0020, // only the basic Packages listed on the AI Packages tab; + // rest of tab controlled by Def Pack List + TES5_UseBaseData = 0x0080, // including name and short name, and flags for Essential, Protected, + // Respawn, Summonable, Simple Actor, and Doesn't affect stealth meter + TES5_UseInventory = 0x0100, // Inventory tab, including all outfits and geared-up item + // -- but not death item + TES5_UseScript = 0x0200, + TES5_UseDefined = 0x0400, // Def Pack List (the dropdown-selected package lists on the AI Packages tab) + TES5_UseAtkData = 0x0800, // Attack Data tab, including override from behavior graph race, + // events, and data) + TES5_UseKeywords = 0x1000 + }; + +#pragma pack(push, 1) + 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 HairColour + { + std::uint8_t red; + std::uint8_t green; + std::uint8_t blue; + std::uint8_t custom; // alpha? + }; + + struct Data + { + SkillValues skills; + std::uint32_t health; + AttributeValues attribs; + }; + +#pragma pack(pop) + + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + bool mIsTES4; + bool mIsFONV; + + std::string mEditorId; + std::string mFullName; + std::string mModel; // skeleton model (can be a marker in FO3/FONV) + + FormId mRace; + FormId mClass; + FormId mHair; // not for TES5, see mHeadParts + FormId mEyes; + + std::vector mHeadParts; // FO3/FONV/TES5 + + float mHairLength; + HairColour mHairColour; // TES4/FO3/FONV + FormId mHairColourId; // TES5 + + FormId mDeathItem; + std::vector mSpell; + FormId mScriptId; + + AIData mAIData; + std::vector mAIPackages; // seems to be in priority order, 0 = highest priority + ActorBaseConfig mBaseConfig; // union + 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; + + FormId mBaseTemplate; // FO3/FONV/TES5 + FormId mWornArmor; // TES5 only? + + FormId mDefaultOutfit; // TES5 OTFT + FormId mSleepOutfit; // TES5 OTFT + FormId mDefaultPkg; + + std::vector mSymShapeModeCoefficients; // size 0 or 50 + std::vector mAsymShapeModeCoefficients; // size 0 or 30 + std::vector mSymTextureModeCoefficients; // size 0 or 50 + std::int16_t mFgRace; + + 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/components/esm4/loadotft.cpp b/components/esm4/loadotft.cpp new file mode 100644 index 0000000000..dec2a05666 --- /dev/null +++ b/components/esm4/loadotft.cpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2020 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 "loadotft.hpp" + +#include +//#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Outfit::Outfit() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); +} + +ESM4::Outfit::~Outfit() +{ +} + +void ESM4::Outfit::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_INAM: + { + std::size_t numObj = subHdr.dataSize / sizeof(FormId); + for (std::size_t i = 0; i < numObj; ++i) + { + FormId formId; + reader.getFormId(formId); + + mInventory.push_back(formId); + } + + break; + } + default: + //std::cout << "OTFT " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + //reader.skipSubRecordData(); + throw std::runtime_error("ESM4::OTFT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Outfit::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Outfit::blank() +//{ +//} diff --git a/components/esm4/loadotft.hpp b/components/esm4/loadotft.hpp new file mode 100644 index 0000000000..3b1db34d0e --- /dev/null +++ b/components/esm4/loadotft.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2020 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_OTFT_H +#define ESM4_OTFT_H + +#include +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Outfit + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + std::vector mInventory; + + Outfit(); + virtual ~Outfit(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_OTFT_H diff --git a/components/esm4/loadpack.cpp b/components/esm4/loadpack.cpp new file mode 100644 index 0000000000..241147df00 --- /dev/null +++ b/components/esm4/loadpack.cpp @@ -0,0 +1,200 @@ +/* + Copyright (C) 2020-2021 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 "loadpack.hpp" + +#include +#include +//#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::AIPackage::AIPackage() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + + std::memset(&mData, 0, sizeof(PKDT)); + std::memset(&mSchedule, 0, sizeof(PSDT)); + std::memset(&mLocation, 0, sizeof(PLDT)); + mLocation.type = 0xff; // default to indicate no location data + std::memset(&mTarget, 0, sizeof(PTDT)); + mTarget.type = 0xff; // default to indicate no target data + + mConditions.clear(); +} + +ESM4::AIPackage::~AIPackage() +{ +} + +void ESM4::AIPackage::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_PKDT: + { + if (subHdr.dataSize != sizeof(PKDT) && subHdr.dataSize == 4) + { + //std::cout << "skip fallout" << mEditorId << std::endl; // FIXME + reader.get(mData.flags); + mData.type = 0; // FIXME + } + else if (subHdr.dataSize != sizeof(mData)) + reader.skipSubRecordData(); // FIXME: FO3 + else + reader.get(mData); + + break; + } + case ESM4::SUB_PSDT: //reader.get(mSchedule); break; + { + if (subHdr.dataSize != sizeof(mSchedule)) + reader.skipSubRecordData(); // FIXME: + else + reader.get(mSchedule); // TES4 + + break; + } + case ESM4::SUB_PLDT: + { + if (subHdr.dataSize != sizeof(mLocation)) + reader.skipSubRecordData(); // FIXME: + else + { + reader.get(mLocation); // TES4 + if (mLocation.type != 5) + reader.adjustFormId(mLocation.location); + } + + break; + } + case ESM4::SUB_PTDT: + { + if (subHdr.dataSize != sizeof(mTarget)) + reader.skipSubRecordData(); // FIXME: FO3 + else + { + reader.get(mTarget); // TES4 + if (mLocation.type != 2) + reader.adjustFormId(mTarget.target); + } + + break; + } + case ESM4::SUB_CTDA: + { + if (subHdr.dataSize != sizeof(CTDA)) + { + reader.skipSubRecordData(); // FIXME: FO3 + break; + } + + static CTDA condition; + reader.get(condition); + // FIXME: how to "unadjust" if not FormId? + //adjustFormId(condition.param1); + //adjustFormId(condition.param2); + mConditions.push_back(condition); + + break; + } + case ESM4::SUB_CTDT: // always 20 for TES4 + case ESM4::SUB_TNAM: // FO3 + case ESM4::SUB_INAM: // FO3 + case ESM4::SUB_CNAM: // FO3 + case ESM4::SUB_SCHR: // FO3 + case ESM4::SUB_POBA: // FO3 + case ESM4::SUB_POCA: // FO3 + case ESM4::SUB_POEA: // FO3 + case ESM4::SUB_SCTX: // FO3 + case ESM4::SUB_SCDA: // FO3 + case ESM4::SUB_SCRO: // FO3 + case ESM4::SUB_IDLA: // FO3 + case ESM4::SUB_IDLC: // FO3 + case ESM4::SUB_IDLF: // FO3 + case ESM4::SUB_IDLT: // FO3 + case ESM4::SUB_PKDD: // FO3 + case ESM4::SUB_PKD2: // FO3 + case ESM4::SUB_PKPT: // FO3 + case ESM4::SUB_PKED: // FO3 + case ESM4::SUB_PKE2: // FO3 + case ESM4::SUB_PKAM: // FO3 + case ESM4::SUB_PUID: // FO3 + case ESM4::SUB_PKW3: // FO3 + case ESM4::SUB_PTD2: // FO3 + case ESM4::SUB_PLD2: // FO3 + case ESM4::SUB_PKFD: // FO3 + case ESM4::SUB_SLSD: // FO3 + case ESM4::SUB_SCVR: // FO3 + case ESM4::SUB_SCRV: // FO3 + case ESM4::SUB_IDLB: // FO3 + case ESM4::SUB_ANAM: // TES5 + case ESM4::SUB_BNAM: // TES5 + case ESM4::SUB_FNAM: // TES5 + case ESM4::SUB_PNAM: // TES5 + case ESM4::SUB_QNAM: // TES5 + case ESM4::SUB_UNAM: // TES5 + case ESM4::SUB_XNAM: // TES5 + case ESM4::SUB_PDTO: // TES5 + case ESM4::SUB_PTDA: // TES5 + case ESM4::SUB_PFOR: // TES5 + case ESM4::SUB_PFO2: // TES5 + case ESM4::SUB_PRCB: // TES5 + case ESM4::SUB_PKCU: // TES5 + case ESM4::SUB_PKC2: // TES5 + case ESM4::SUB_CITC: // TES5 + case ESM4::SUB_CIS1: // TES5 + case ESM4::SUB_CIS2: // TES5 + case ESM4::SUB_VMAD: // TES5 + case ESM4::SUB_TPIC: // TES5 + { + //std::cout << "PACK " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::PACK::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::AIPackage::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::AIPackage::blank() +//{ +//} diff --git a/components/esm4/loadpack.hpp b/components/esm4/loadpack.hpp new file mode 100644 index 0000000000..93a9a8290c --- /dev/null +++ b/components/esm4/loadpack.hpp @@ -0,0 +1,110 @@ +/* + Copyright (C) 2020-2021 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_PACK_H +#define ESM4_PACK_H + +#include +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct AIPackage + { +#pragma pack(push, 1) + struct PKDT // data + { + std::uint32_t flags; + std::int32_t type; + }; + + struct PSDT // schedule + { + std::uint8_t month; // Any = 0xff + std::uint8_t dayOfWeek; // Any = 0xff + std::uint8_t date; // Any = 0 + std::uint8_t time; // Any = 0xff + std::uint32_t duration; + }; + + struct PLDT // location + { + std::int32_t type; // 0 = near ref, 1 = in cell, 2 = current loc, 3 = editor loc, 4 = obj id, 5 = obj type + FormId location; // uint32_t if type = 5 + std::int32_t radius; + }; + + struct PTDT // target + { + std::int32_t type; // 0 = specific ref, 1 = obj id, 2 = obj type + FormId target; // uint32_t if type = 2 + std::int32_t distance; + }; + + // NOTE: param1/param2 can be FormId or number, but assume FormId so that adjustFormId + // can be called + struct CTDA + { + std::uint8_t condition; + std::uint8_t unknown1; // probably padding + std::uint8_t unknown2; // probably padding + std::uint8_t unknown3; // probably padding + float compValue; + std::int32_t fnIndex; + FormId param1; + FormId param2; + std::uint32_t unknown4; // probably padding + }; +#pragma pack(pop) + + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + PKDT mData; + PSDT mSchedule; + PLDT mLocation; + PTDT mTarget; + std::vector mConditions; + + AIPackage(); + virtual ~AIPackage(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_PACK_H diff --git a/components/esm4/loadpgrd.cpp b/components/esm4/loadpgrd.cpp new file mode 100644 index 0000000000..a0bfc9b119 --- /dev/null +++ b/components/esm4/loadpgrd.cpp @@ -0,0 +1,177 @@ +/* + Copyright (C) 2020-2021 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 "loadpgrd.hpp" + +#include +//#include // FIXME: for debugging only +//#include // FIXME: for debugging only + +//#include // FIXME for debugging only + +#include "formid.hpp" // FIXME: for mEditorId workaround +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Pathgrid::Pathgrid() : mFormId(0), mFlags(0), mData(0) +{ + mEditorId.clear(); + + mNodes.clear(); + mLinks.clear(); + mForeign.clear(); + mObjects.clear(); +} + +ESM4::Pathgrid::~Pathgrid() +{ +} + +void ESM4::Pathgrid::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + + mEditorId = formIdToString(mFormId); // FIXME: quick workaround to use existing code + + while (reader.getSubRecordHeader()) + { + const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + switch (subHdr.typeId) + { + case ESM4::SUB_DATA: reader.get(mData); break; + case ESM4::SUB_PGRP: + { + std::size_t numNodes = subHdr.dataSize / sizeof(PGRP); + if (numNodes != std::size_t(mData)) // keep gcc quiet + throw std::runtime_error("ESM4::PGRD::load numNodes mismatch"); + + mNodes.resize(numNodes); + for (std::size_t i = 0; i < numNodes; ++i) + { + reader.get(mNodes.at(i)); + + if (int(mNodes.at(i).z) % 2 == 0) + mNodes.at(i).priority = 0; + else + mNodes.at(i).priority = 1; + } + + break; + } + case ESM4::SUB_PGRR: + { + static PGRR link; + + for (std::size_t i = 0; i < std::size_t(mData); ++i) // keep gcc quiet + { + for (std::size_t j = 0; j < mNodes[i].numLinks; ++j) + { + link.startNode = std::int16_t(i); + + reader.get(link.endNode); + if (link.endNode == -1) + continue; + + // ICMarketDistrictTheBestDefenseBasement doesn't have a PGRR sub-record + // CELL formId 00049E2A + // PGRD formId 000304B7 + //if (mFormId == 0x0001C2C8) + //std::cout << link.startNode << "," << link.endNode << std::endl; + mLinks.push_back(link); + } + } + + break; + } + case ESM4::SUB_PGRI: + { + std::size_t numForeign = subHdr.dataSize / sizeof(PGRI); + mForeign.resize(numForeign); + for (std::size_t i = 0; i < numForeign; ++i) + { + reader.get(mForeign.at(i)); + mForeign.at(i).localNode;// &= 0xffff; // some have junk high bits (maybe flags?) + } + + break; + } + case ESM4::SUB_PGRL: + { + static PGRL objLink; + reader.get(objLink.object); + // object linkedNode + std::size_t numNodes = (subHdr.dataSize - sizeof(int32_t)) / sizeof(int32_t); + + objLink.linkedNodes.resize(numNodes); + for (std::size_t i = 0; i < numNodes; ++i) + reader.get(objLink.linkedNodes.at(i)); + + mObjects.push_back(objLink); + + break; + } + case ESM4::SUB_PGAG: + { +#if 0 + boost::scoped_array mDataBuf(new unsigned char[subHdr.dataSize]); + reader.get(&mDataBuf[0], subHdr.dataSize); + + std::ostringstream ss; + ss << mEditorId << " " << ESM::printName(subHdr.typeId) << ":size " << subHdr.dataSize << "\n"; + for (std::size_t i = 0; i < subHdr.dataSize; ++i) + { + //if (mDataBuf[i] > 64 && mDataBuf[i] < 91) // looks like printable ascii char + //ss << (char)(mDataBuf[i]) << " "; + //else + ss << std::setfill('0') << std::setw(2) << std::hex << (int)(mDataBuf[i]); + if ((i & 0x000f) == 0xf) // wrap around + ss << "\n"; + else if (i < subHdr.dataSize-1) + ss << " "; + } + std::cout << ss.str() << std::endl; +#else + //std::cout << "PGRD " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); +#endif + break; + } + default: + throw std::runtime_error("ESM4::PGRD::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Pathgrid::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Pathgrid::blank() +//{ +//} diff --git a/components/esm4/loadpgrd.hpp b/components/esm4/loadpgrd.hpp new file mode 100644 index 0000000000..9f522af5ca --- /dev/null +++ b/components/esm4/loadpgrd.hpp @@ -0,0 +1,96 @@ +/* + Copyright (C) 2020 - 2021 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_PGRD_H +#define ESM4_PGRD_H + +#include +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Pathgrid + { +#pragma pack(push, 1) + struct PGRP + { + float x; + float y; + float z; + std::uint8_t numLinks; + std::uint8_t priority; // probably padding, repurposing + std::uint16_t unknown; // probably padding + }; + + struct PGRR + { + std::int16_t startNode; + std::int16_t endNode; + }; + + struct PGRI + { + std::int32_t localNode; + float x; // foreign + float y; // foreign + float z; // foreign + }; +#pragma pack(pop) + + struct PGRL + { + FormId object; + std::vector linkedNodes; + }; + + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; // FIXME: no such record for PGRD, but keep here to avoid extra work for now + + std::int16_t mData; // number of nodes + std::vector mNodes; + std::vector mLinks; + std::vector mForeign; + std::vector mObjects; + + Pathgrid(); + virtual ~Pathgrid(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_PGRD_H diff --git a/components/esm4/loadpgre.cpp b/components/esm4/loadpgre.cpp new file mode 100644 index 0000000000..9c3e745ab9 --- /dev/null +++ b/components/esm4/loadpgre.cpp @@ -0,0 +1,105 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#include "loadpgre.hpp" + +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::PlacedGrenade::PlacedGrenade() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); +} + +ESM4::PlacedGrenade::~PlacedGrenade() +{ +} + +void ESM4::PlacedGrenade::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: + case ESM4::SUB_XEZN: + case ESM4::SUB_XRGD: + case ESM4::SUB_XRGB: + case ESM4::SUB_XPRD: + case ESM4::SUB_XPPA: + case ESM4::SUB_INAM: + case ESM4::SUB_TNAM: + case ESM4::SUB_XOWN: + case ESM4::SUB_XRNK: + case ESM4::SUB_XCNT: + case ESM4::SUB_XRDS: + case ESM4::SUB_XHLP: + case ESM4::SUB_XPWR: + case ESM4::SUB_XDCR: + case ESM4::SUB_XLKR: + case ESM4::SUB_XCLP: + case ESM4::SUB_XAPD: + case ESM4::SUB_XAPR: + case ESM4::SUB_XATO: + case ESM4::SUB_XESP: + case ESM4::SUB_XEMI: + case ESM4::SUB_XMBR: + case ESM4::SUB_XIBS: + case ESM4::SUB_XSCL: + case ESM4::SUB_DATA: + { + //std::cout << "PGRE " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + std::cout << "PGRE " << ESM::printName(subHdr.typeId) << " skipping..." + << subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + //throw std::runtime_error("ESM4::PGRE::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::PlacedGrenade::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::PlacedGrenade::blank() +//{ +//} diff --git a/components/esm4/loadpgre.hpp b/components/esm4/loadpgre.hpp new file mode 100644 index 0000000000..bb36acfc37 --- /dev/null +++ b/components/esm4/loadpgre.hpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#ifndef ESM4_PGRE_H +#define ESM4_PGRE_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct PlacedGrenade + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + PlacedGrenade(); + virtual ~PlacedGrenade(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_PGRE_H diff --git a/components/esm4/loadpwat.cpp b/components/esm4/loadpwat.cpp new file mode 100644 index 0000000000..a88adcf4c2 --- /dev/null +++ b/components/esm4/loadpwat.cpp @@ -0,0 +1,82 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#include "loadpwat.hpp" + +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::PlaceableWater::PlaceableWater() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); +} + +ESM4::PlaceableWater::~PlaceableWater() +{ +} + +void ESM4::PlaceableWater::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_OBND: + case ESM4::SUB_MODL: + case ESM4::SUB_DNAM: + { + //std::cout << "PWAT " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + std::cout << "PWAT " << ESM::printName(subHdr.typeId) << " skipping..." + << subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + //throw std::runtime_error("ESM4::PWAT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::PlaceableWater::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::PlaceableWater::blank() +//{ +//} diff --git a/components/esm4/loadpwat.hpp b/components/esm4/loadpwat.hpp new file mode 100644 index 0000000000..e1de91ab96 --- /dev/null +++ b/components/esm4/loadpwat.hpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#ifndef ESM4_PWAT_H +#define ESM4_PWAT_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct PlaceableWater + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + PlaceableWater(); + virtual ~PlaceableWater(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_PWAT_H diff --git a/components/esm4/loadqust.cpp b/components/esm4/loadqust.cpp new file mode 100644 index 0000000000..67e250ae76 --- /dev/null +++ b/components/esm4/loadqust.cpp @@ -0,0 +1,179 @@ +/* + Copyright (C) 2020-2021 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 "loadqust.hpp" + +#include +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Quest::Quest() : mFormId(0), mFlags(0), mQuestScript(0) +{ + mEditorId.clear(); + mQuestName.clear(); + mFileName.clear(); + + std::memset(&mScript.scriptHeader, 0, sizeof(ScriptHeader)); + mScript.scriptSource.clear(); + mScript.globReference = 0; +} + +ESM4::Quest::~Quest() +{ +} + +void ESM4::Quest::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(mQuestName); break; + case ESM4::SUB_ICON: reader.getZString(mFileName); break; // TES4 (none in FO3/FONV) + case ESM4::SUB_DATA: + { + if (subHdr.dataSize == 2) // TES4 + { + reader.get(&mData, 2); + mData.questDelay = 0.f; // unused in TES4 but keep it clean + + //if ((mData.flags & Flag_StartGameEnabled) != 0) + //std::cout << "start quest " << mEditorId << std::endl; + } + else + reader.get(mData); // FO3 + + break; + } + case ESM4::SUB_SCRI: reader.get(mQuestScript); break; + case ESM4::SUB_CTDA: // FIXME: how to detect if 1st/2nd param is a formid? + { + TargetCondition cond; + + if (subHdr.dataSize == 24) // TES4 + { + reader.get(&cond, 24); + cond.reference = 0; // unused in TES4 but keep it clean + } + else if (subHdr.dataSize == 28) + { + reader.get(cond); // FO3/FONV + if (cond.reference) + reader.adjustFormId(cond.reference); + } + else + { + // one record with size 20: EDID GenericSupMutBehemoth + reader.skipSubRecordData(); // FIXME + } + // FIXME: support TES5 + + mTargetConditions.push_back(cond); + + break; + } + case ESM4::SUB_SCHR: reader.get(mScript.scriptHeader); break; + case ESM4::SUB_SCDA: reader.skipSubRecordData(); break; // compiled script data + case ESM4::SUB_SCTX: reader.getString(mScript.scriptSource); break; + case ESM4::SUB_SCRO: reader.getFormId(mScript.globReference); break; + case ESM4::SUB_INDX: + case ESM4::SUB_QSDT: + case ESM4::SUB_CNAM: + case ESM4::SUB_QSTA: + case ESM4::SUB_NNAM: // FO3 + case ESM4::SUB_QOBJ: // FO3 + case ESM4::SUB_NAM0: // FO3 + case ESM4::SUB_ANAM: // TES5 + case ESM4::SUB_DNAM: // TES5 + case ESM4::SUB_ENAM: // TES5 + case ESM4::SUB_FNAM: // TES5 + case ESM4::SUB_NEXT: // TES5 + case ESM4::SUB_ALCA: // TES5 + case ESM4::SUB_ALCL: // TES5 + case ESM4::SUB_ALCO: // TES5 + case ESM4::SUB_ALDN: // TES5 + case ESM4::SUB_ALEA: // TES5 + case ESM4::SUB_ALED: // TES5 + case ESM4::SUB_ALEQ: // TES5 + case ESM4::SUB_ALFA: // TES5 + case ESM4::SUB_ALFC: // TES5 + case ESM4::SUB_ALFD: // TES5 + case ESM4::SUB_ALFE: // TES5 + case ESM4::SUB_ALFI: // TES5 + case ESM4::SUB_ALFL: // TES5 + case ESM4::SUB_ALFR: // TES5 + case ESM4::SUB_ALID: // TES5 + case ESM4::SUB_ALLS: // TES5 + case ESM4::SUB_ALNA: // TES5 + case ESM4::SUB_ALNT: // TES5 + case ESM4::SUB_ALPC: // TES5 + case ESM4::SUB_ALRT: // TES5 + case ESM4::SUB_ALSP: // TES5 + case ESM4::SUB_ALST: // TES5 + case ESM4::SUB_ALUA: // TES5 + case ESM4::SUB_CIS2: // TES5 + case ESM4::SUB_CNTO: // TES5 + case ESM4::SUB_COCT: // TES5 + case ESM4::SUB_ECOR: // TES5 + case ESM4::SUB_FLTR: // TES5 + case ESM4::SUB_KNAM: // TES5 + case ESM4::SUB_KSIZ: // TES5 + case ESM4::SUB_KWDA: // TES5 + case ESM4::SUB_QNAM: // TES5 + case ESM4::SUB_QTGL: // TES5 + case ESM4::SUB_SPOR: // TES5 + case ESM4::SUB_VMAD: // TES5 + case ESM4::SUB_VTCK: // TES5 + { + //std::cout << "QUST " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::QUST::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } + //if (mEditorId == "DAConversations") + //std::cout << mEditorId << std::endl; +} + +//void ESM4::Quest::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Quest::blank() +//{ +//} diff --git a/components/esm4/loadqust.hpp b/components/esm4/loadqust.hpp new file mode 100644 index 0000000000..905a5cf0fa --- /dev/null +++ b/components/esm4/loadqust.hpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2020-2021 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_QUST_H +#define ESM4_QUST_H + +#include + +#include "formid.hpp" +#include "script.hpp" // TargetCondition, ScriptDefinition + +namespace ESM4 +{ + class Reader; + class Writer; + +#pragma pack(push, 1) + struct QuestData + { + std::uint8_t flags; // Quest_Flags + std::uint8_t priority; + std::uint16_t padding; // FO3 + float questDelay; // FO3 + }; +#pragma pack(pop) + + struct Quest + { + // NOTE: these values are for TES4 + enum Quest_Flags + { + Flag_StartGameEnabled = 0x01, + Flag_AllowRepeatConvTopic = 0x04, + Flag_AllowRepeatStages = 0x08 + }; + + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + std::string mQuestName; + std::string mFileName; // texture file + FormId mQuestScript; + + QuestData mData; + + std::vector mTargetConditions; + + ScriptDefinition mScript; + + Quest(); + virtual ~Quest(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_QUST_H diff --git a/components/esm4/loadrace.cpp b/components/esm4/loadrace.cpp new file mode 100644 index 0000000000..79f635bd48 --- /dev/null +++ b/components/esm4/loadrace.cpp @@ -0,0 +1,715 @@ +/* + Copyright (C) 2016, 2018-2021 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 "loadrace.hpp" + +#include +#include +#include // FIXME: for debugging only +#include // FIXME: for debugging only + +#include "formid.hpp" +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Race::Race() : mFormId(0), mFlags(0), mIsTES5(false) + , mHeightMale(1.f), mHeightFemale(1.f), mWeightMale(1.f), mWeightFemale(1.f) + , mRaceFlags(0), mFaceGenMainClamp(0.f), mFaceGenFaceClamp(0.f), mNumKeywords(0) + , mSkin(0) +{ + mEditorId.clear(); + mFullName.clear(); + mDesc.clear(); + mModelMale.clear(); + mModelFemale.clear(); + + std::memset(&mAttribMale, 0, sizeof(AttributeValues)); + std::memset(&mAttribFemale, 0, sizeof(AttributeValues)); + + mVNAM.resize(2); + mDefaultHair.resize(2); + + mBodyTemplate.bodyPart = 0; + mBodyTemplate.flags = 0; + mBodyTemplate.type = 0; +} + +ESM4::Race::~Race() +{ +} + +void ESM4::Race::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + + std::uint32_t esmVer = reader.esmVersion(); + bool isTES4 = esmVer == ESM::VER_080 || esmVer == ESM::VER_100; + bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134; + bool isFO3 = false; + + bool isMale = false; + int curr_part = -1; // 0 = head, 1 = body, 2 = egt, 3 = hkx + std::uint32_t currentIndex = 0xffffffff; + + while (reader.getSubRecordHeader()) + { + const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + //std::cout << "RACE " << ESM::printName(subHdr.typeId) << std::endl; + switch (subHdr.typeId) + { + case ESM4::SUB_EDID: + { + reader.getZString(mEditorId); + // TES4 + // Sheogorath 0x0005308E + // GoldenSaint 0x0001208F + // DarkSeducer 0x0001208E + // VampireRace 0x00000019 + // Dremora 0x00038010 + // Argonian 0x00023FE9 + // Nord 0x000224FD + // Breton 0x000224FC + // WoodElf 0x000223C8 + // Khajiit 0x000223C7 + // DarkElf 0x000191C1 + // Orc 0x000191C0 + // HighElf 0x00019204 + // Redguard 0x00000D43 + // Imperial 0x00000907 + break; + } + case ESM4::SUB_FULL: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_DESC: + { + if (subHdr.dataSize == 1) // FO3? + { + reader.skipSubRecordData(); + break; + } + + reader.getLocalizedString(mDesc); break; + } + case ESM4::SUB_SPLO: // bonus spell formid (TES5 may have SPCT and multiple SPLO) + { + FormId magic; + reader.getFormId(magic); + mBonusSpells.push_back(magic); +// std::cout << "RACE " << printName(subHdr.typeId) << " " << formIdToString(magic) << std::endl; + + break; + } + case ESM4::SUB_DATA: // ?? different length for TES5 + { +// DATA:size 128 +// 0f 0f ff 00 ff 00 ff 00 ff 00 ff 00 ff 00 00 00 +// 9a 99 99 3f 00 00 80 3f 00 00 80 3f 00 00 80 3f +// 48 89 10 00 00 00 40 41 00 00 00 00 00 00 48 43 +// 00 00 48 43 00 00 80 3f 9a 99 19 3f 00 00 00 40 +// 01 00 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 +// ff ff ff ff 00 00 00 00 00 00 20 41 00 00 a0 40 +// 00 00 a0 40 00 00 80 42 ff ff ff ff 00 00 00 00 +// 00 00 00 00 9a 99 99 3e 00 00 a0 40 02 00 00 00 +#if 0 + unsigned char mDataBuf[256/*bufSize*/]; + reader.get(&mDataBuf[0], subHdr.dataSize); + + std::ostringstream ss; + ss << ESM::printName(subHdr.typeId) << ":size " << subHdr.dataSize << "\n"; + 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; +#else + if (subHdr.dataSize == 36) // TES4/FO3/FONV + { + if (!isTES4 && !isFONV && !mIsTES5) + isFO3 = true; + + std::uint8_t skill; + std::uint8_t bonus; + for (unsigned int i = 0; i < 8; ++i) + { + reader.get(skill); + reader.get(bonus); + mSkillBonus[static_cast(skill)] = bonus; + } + reader.get(mHeightMale); + reader.get(mHeightFemale); + reader.get(mWeightMale); + reader.get(mWeightFemale); + reader.get(mRaceFlags); + } + else if (subHdr.dataSize >= 128 && subHdr.dataSize <= 164) // TES5 + { + mIsTES5 = true; + + std::uint8_t skill; + std::uint8_t bonus; + for (unsigned int i = 0; i < 7; ++i) + { + reader.get(skill); + reader.get(bonus); + mSkillBonus[static_cast(skill)] = bonus; + } + std::uint16_t unknown; + reader.get(unknown); + + reader.get(mHeightMale); + reader.get(mHeightFemale); + reader.get(mWeightMale); + reader.get(mWeightFemale); + reader.get(mRaceFlags); + + // FIXME + float dummy; + reader.get(dummy); // starting health + reader.get(dummy); // starting magicka + reader.get(dummy); // starting stamina + reader.get(dummy); // base carry weight + reader.get(dummy); // base mass + reader.get(dummy); // accleration rate + reader.get(dummy); // decleration rate + + uint32_t dummy2; + reader.get(dummy2); // size + reader.get(dummy2); // head biped object + reader.get(dummy2); // hair biped object + reader.get(dummy); // injured health % (0.f..1.f) + reader.get(dummy2); // shield biped object + reader.get(dummy); // health regen + reader.get(dummy); // magicka regen + reader.get(dummy); // stamina regen + reader.get(dummy); // unarmed damage + reader.get(dummy); // unarmed reach + reader.get(dummy2); // body biped object + reader.get(dummy); // aim angle tolerance + reader.get(dummy2); // unknown + reader.get(dummy); // angular accleration rate + reader.get(dummy); // angular tolerance + reader.get(dummy2); // flags + + if (subHdr.dataSize > 128) + { + reader.get(dummy2); // unknown 1 + reader.get(dummy2); // unknown 2 + reader.get(dummy2); // unknown 3 + reader.get(dummy2); // unknown 4 + reader.get(dummy2); // unknown 5 + reader.get(dummy2); // unknown 6 + reader.get(dummy2); // unknown 7 + reader.get(dummy2); // unknown 8 + reader.get(dummy2); // unknown 9 + } + } + else + { + reader.skipSubRecordData(); + std::cout << "RACE " << ESM::printName(subHdr.typeId) << " skipping..." + << subHdr.dataSize << std::endl; + } +#endif + break; + } + case ESM4::SUB_DNAM: + { + reader.get(mDefaultHair[0]); // male + reader.get(mDefaultHair[1]); // female + + break; + } + case ESM4::SUB_CNAM: // Only in TES4? + // CNAM SNAM VNAM + // Sheogorath 0x0 0000 98 2b 10011000 00101011 + // Golden Saint 0x3 0011 26 46 00100110 01000110 + // Dark Seducer 0xC 1100 df 55 11011111 01010101 + // Vampire Race 0x0 0000 77 44 01110111 10001000 + // Dremora 0x7 0111 bf 32 10111111 00110010 + // Argonian 0x0 0000 dc 3c 11011100 00111100 + // Nord 0x5 0101 b6 03 10110110 00000011 + // Breton 0x5 0101 48 1d 01001000 00011101 00000000 00000907 (Imperial) + // Wood Elf 0xD 1101 2e 4a 00101110 01001010 00019204 00019204 (HighElf) + // khajiit 0x5 0101 54 5b 01010100 01011011 00023FE9 00023FE9 (Argonian) + // Dark Elf 0x0 0000 72 54 01110010 01010100 00019204 00019204 (HighElf) + // Orc 0xC 1100 74 09 01110100 00001001 000224FD 000224FD (Nord) + // High Elf 0xF 1111 e6 21 11100110 00100001 + // Redguard 0xD 1101 a9 61 10101001 01100001 + // Imperial 0xD 1101 8e 35 10001110 00110101 + { + reader.skipSubRecordData(); + break; + } + case ESM4::SUB_PNAM: reader.get(mFaceGenMainClamp); break; // 0x40A00000 = 5.f + case ESM4::SUB_UNAM: reader.get(mFaceGenFaceClamp); break; // 0x40400000 = 3.f + case ESM4::SUB_ATTR: // Only in TES4? + { + if (subHdr.dataSize == 2) // FO3? + { + reader.skipSubRecordData(); + break; + } + reader.get(mAttribMale.strength); + reader.get(mAttribMale.intelligence); + reader.get(mAttribMale.willpower); + reader.get(mAttribMale.agility); + reader.get(mAttribMale.speed); + reader.get(mAttribMale.endurance); + reader.get(mAttribMale.personality); + reader.get(mAttribMale.luck); + reader.get(mAttribFemale.strength); + reader.get(mAttribFemale.intelligence); + reader.get(mAttribFemale.willpower); + reader.get(mAttribFemale.agility); + reader.get(mAttribFemale.speed); + reader.get(mAttribFemale.endurance); + reader.get(mAttribFemale.personality); + reader.get(mAttribFemale.luck); + + break; + } + // [0..9]-> ICON + // NAM0 -> INDX -> MODL --+ + // ^ -> MODB | + // | | + // +-------------+ + // + case ESM4::SUB_NAM0: // start marker head data /* 1 */ + { + curr_part = 0; // head part + + if (isFO3 || isFONV) + { + mHeadParts.resize(8); + mHeadPartsFemale.resize(8); + } + else if (isTES4) + mHeadParts.resize(9); // assumed based on Construction Set + else + { + mHeadPartIdsMale.resize(5); + mHeadPartIdsFemale.resize(5); + } + + currentIndex = 0xffffffff; + break; + } + case ESM4::SUB_INDX: + { + reader.get(currentIndex); + // FIXME: below check is rather useless + //if (headpart) + //{ + // if (currentIndex > 8) + // throw std::runtime_error("ESM4::RACE::load - too many head part " + currentIndex); + //} + //else // bodypart + //{ + // if (currentIndex > 4) + // throw std::runtime_error("ESM4::RACE::load - too many body part " + currentIndex); + //} + + break; + } + case ESM4::SUB_MODL: + { + if (curr_part == 0) // head part + { + if (isMale || isTES4) + reader.getZString(mHeadParts[currentIndex].mesh); + else + reader.getZString(mHeadPartsFemale[currentIndex].mesh); // TODO: check TES4 + + // TES5 keeps head part formid in mHeadPartIdsMale and mHeadPartIdsFemale + } + else if (curr_part == 1) // body part + { + if (isMale) + reader.getZString(mBodyPartsMale[currentIndex].mesh); + else + reader.getZString(mBodyPartsFemale[currentIndex].mesh); + + // TES5 seems to have no body parts at all, instead keep EGT models + } + else if (curr_part == 2) // egt + { + //std::cout << mEditorId << " egt " << currentIndex << std::endl; // FIXME + reader.skipSubRecordData(); // FIXME TES5 egt + } + else + { + //std::cout << mEditorId << " hkx " << currentIndex << std::endl; // FIXME + reader.skipSubRecordData(); // FIXME TES5 hkx + } + + break; + } + case ESM4::SUB_MODB: reader.skipSubRecordData(); break; // always 0x0000? + case ESM4::SUB_ICON: + { + if (curr_part == 0) // head part + { + if (isMale || isTES4) + reader.getZString(mHeadParts[currentIndex].texture); + else + reader.getZString(mHeadPartsFemale[currentIndex].texture); // TODO: check TES4 + } + else if (curr_part == 1) // body part + { + if (isMale) + reader.getZString(mBodyPartsMale[currentIndex].texture); + else + reader.getZString(mBodyPartsFemale[currentIndex].texture); + } + else + reader.skipSubRecordData(); // FIXME TES5 + + break; + } + // + case ESM4::SUB_NAM1: // start marker body data /* 4 */ + { + + if (isFO3 || isFONV) + { + curr_part = 1; // body part + + mBodyPartsMale.resize(4); + mBodyPartsFemale.resize(4); + } + else if (isTES4) + { + curr_part = 1; // body part + + mBodyPartsMale.resize(5); // 0 = upper body, 1 = legs, 2 = hands, 3 = feet, 4 = tail + mBodyPartsFemale.resize(5); // 0 = upper body, 1 = legs, 2 = hands, 3 = feet, 4 = tail + } + else // TES5 + curr_part = 2; // for TES5 NAM1 indicates the start of EGT model + + if (isTES4) + currentIndex = 4; // FIXME: argonian tail mesh without preceeding INDX + else + currentIndex = 0xffffffff; + + break; + } + case ESM4::SUB_MNAM: isMale = true; break; /* 2, 5, 7 */ + case ESM4::SUB_FNAM: isMale = false; break; /* 3, 6, 8 */ + // + case ESM4::SUB_HNAM: + { + std::size_t numHairChoices = subHdr.dataSize / sizeof(FormId); + mHairChoices.resize(numHairChoices); + for (unsigned int i = 0; i < numHairChoices; ++i) + reader.get(mHairChoices.at(i)); + + break; + } + case ESM4::SUB_ENAM: + { + std::size_t numEyeChoices = subHdr.dataSize / sizeof(FormId); + mEyeChoices.resize(numEyeChoices); + for (unsigned int i = 0; i < numEyeChoices; ++i) + reader.get(mEyeChoices.at(i)); + + break; + } + case ESM4::SUB_FGGS: + { + if (isMale || isTES4) + { + mSymShapeModeCoefficients.resize(50); + for (std::size_t i = 0; i < 50; ++i) + reader.get(mSymShapeModeCoefficients.at(i)); + } + else + { + mSymShapeModeCoeffFemale.resize(50); + for (std::size_t i = 0; i < 50; ++i) + reader.get(mSymShapeModeCoeffFemale.at(i)); + } + + break; + } + case ESM4::SUB_FGGA: + { + if (isMale || isTES4) + { + mAsymShapeModeCoefficients.resize(30); + for (std::size_t i = 0; i < 30; ++i) + reader.get(mAsymShapeModeCoefficients.at(i)); + } + else + { + mAsymShapeModeCoeffFemale.resize(30); + for (std::size_t i = 0; i < 30; ++i) + reader.get(mAsymShapeModeCoeffFemale.at(i)); + } + + break; + } + case ESM4::SUB_FGTS: + { + if (isMale || isTES4) + { + mSymTextureModeCoefficients.resize(50); + for (std::size_t i = 0; i < 50; ++i) + reader.get(mSymTextureModeCoefficients.at(i)); + } + else + { + mSymTextureModeCoeffFemale.resize(50); + for (std::size_t i = 0; i < 50; ++i) + reader.get(mSymTextureModeCoeffFemale.at(i)); + } + + break; + } + // + case ESM4::SUB_SNAM: //skipping...2 // only in TES4? + { + reader.skipSubRecordData(); + break; + } + case ESM4::SUB_XNAM: + { + FormId race; + std::int32_t adjustment; + reader.get(race); + reader.get(adjustment); + mDisposition[race] = adjustment; + + break; + } + case ESM4::SUB_VNAM: + { + if (subHdr.dataSize == 8) // TES4 + { + reader.get(mVNAM[0]); // For TES4 seems to be 2 race formids + reader.get(mVNAM[1]); + } + else if (subHdr.dataSize == 4) // TES5 + { + // equipment type flags meant to be uint32 ??? GLOB reference? shows up in + // SCRO in sript records and CTDA in INFO records + uint32_t dummy; + reader.get(dummy); + } + else + { + reader.skipSubRecordData(); + std::cout << "RACE " << ESM::printName(subHdr.typeId) << " skipping..." + << subHdr.dataSize << std::endl; + } + + break; + } + // + case ESM4::SUB_ANAM: // TES5 + { + if (isMale) + reader.getZString(mModelMale); + else + reader.getZString(mModelFemale); + break; + } + case ESM4::SUB_KSIZ: reader.get(mNumKeywords); break; + case ESM4::SUB_KWDA: + { + std::uint32_t formid; + for (unsigned int i = 0; i < mNumKeywords; ++i) + reader.getFormId(formid); + break; + } + // + case ESM4::SUB_WNAM: // ARMO FormId + { + reader.getFormId(mSkin); + //std::cout << mEditorId << " skin " << formIdToString(mSkin) << std::endl; // FIXME + break; + } + case ESM4::SUB_BODT: // body template + { + reader.get(mBodyTemplate.bodyPart); + reader.get(mBodyTemplate.flags); + reader.get(mBodyTemplate.unknown1); // probably padding + reader.get(mBodyTemplate.unknown2); // probably padding + reader.get(mBodyTemplate.unknown3); // probably padding + reader.get(mBodyTemplate.type); + + break; + } + case ESM4::SUB_BOD2: // TES5 + { + reader.get(mBodyTemplate.bodyPart); + mBodyTemplate.flags = 0; + mBodyTemplate.unknown1 = 0; // probably padding + mBodyTemplate.unknown2 = 0; // probably padding + mBodyTemplate.unknown3 = 0; // probably padding + reader.get(mBodyTemplate.type); + + break; + } + case ESM4::SUB_HEAD: // TES5 + { + FormId formId; + reader.getFormId(formId); + + // FIXME: no order? head, mouth, eyes, brow, hair + if (isMale) + mHeadPartIdsMale[currentIndex] = formId; + else + mHeadPartIdsFemale[currentIndex] = formId; + + //std::cout << mEditorId << (isMale ? " male head " : " female head ") + //<< formIdToString(formId) << " " << currentIndex << std::endl; // FIXME + + break; + } + case ESM4::SUB_NAM3: // start of hkx model + { + curr_part = 3; // for TES5 NAM3 indicates the start of hkx model + + break; + } + // Not sure for what this is used - maybe to indicate which slots are in use? e.g.: + // + // ManakinRace HEAD + // ManakinRace Hair + // ManakinRace BODY + // ManakinRace Hands + // ManakinRace Forearms + // ManakinRace Amulet + // ManakinRace Ring + // ManakinRace Feet + // ManakinRace Calves + // ManakinRace SHIELD + // ManakinRace + // ManakinRace LongHair + // ManakinRace Circlet + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace DecapitateHead + // ManakinRace Decapitate + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace + // ManakinRace FX0 + case ESM4::SUB_NAME: // TES5 biped object names (x32) + { + std::string name; + reader.getZString(name); + //std::cout << mEditorId << " " << name << std::endl; + + break; + } + case ESM4::SUB_MTNM: // movement type + case ESM4::SUB_ATKD: // attack data + case ESM4::SUB_ATKE: // attach event + case ESM4::SUB_GNAM: // body part data + case ESM4::SUB_NAM4: // material type + case ESM4::SUB_NAM5: // unarmed impact? + case ESM4::SUB_LNAM: // close loot sound + case ESM4::SUB_QNAM: // equipment slot formid + case ESM4::SUB_HCLF: // default hair colour + case ESM4::SUB_UNES: // unarmed equipment slot formid + case ESM4::SUB_TINC: + case ESM4::SUB_TIND: + case ESM4::SUB_TINI: + case ESM4::SUB_TINL: + case ESM4::SUB_TINP: + case ESM4::SUB_TINT: + case ESM4::SUB_TINV: + case ESM4::SUB_TIRS: + case ESM4::SUB_PHWT: + case ESM4::SUB_AHCF: + case ESM4::SUB_AHCM: + case ESM4::SUB_MPAI: + case ESM4::SUB_MPAV: + case ESM4::SUB_DFTF: + case ESM4::SUB_DFTM: + case ESM4::SUB_FLMV: + case ESM4::SUB_FTSF: + case ESM4::SUB_FTSM: + case ESM4::SUB_MTYP: + case ESM4::SUB_NAM7: + case ESM4::SUB_NAM8: + case ESM4::SUB_PHTN: + case ESM4::SUB_RNAM: + case ESM4::SUB_RNMV: + case ESM4::SUB_RPRF: + case ESM4::SUB_RPRM: + case ESM4::SUB_SNMV: + case ESM4::SUB_SPCT: + case ESM4::SUB_SPED: + case ESM4::SUB_SWMV: + case ESM4::SUB_WKMV: + // + 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 " << ESM::printName(subHdr.typeId) << " skipping..." << subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::RACE::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Race::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Race::blank() +//{ +//} diff --git a/components/esm4/loadrace.hpp b/components/esm4/loadrace.hpp new file mode 100644 index 0000000000..0cede13b0b --- /dev/null +++ b/components/esm4/loadrace.hpp @@ -0,0 +1,174 @@ +/* + Copyright (C) 2016, 2018-2020 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 +#include + +#include "formid.hpp" +#include "actor.hpp" // AttributeValues, BodyTemplate + +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) + + enum SkillIndex + { + Skill_Armorer = 0x0C, + Skill_Athletics = 0x0D, + Skill_Blade = 0x0E, + Skill_Block = 0x0F, + Skill_Blunt = 0x10, + Skill_HandToHand = 0x11, + Skill_HeavyArmor = 0x12, + Skill_Alchemy = 0x13, + Skill_Alteration = 0x14, + Skill_Conjuration = 0x15, + Skill_Destruction = 0x16, + Skill_Illusion = 0x17, + Skill_Mysticism = 0x18, + Skill_Restoration = 0x19, + Skill_Acrobatics = 0x1A, + Skill_LightArmor = 0x1B, + Skill_Marksman = 0x1C, + Skill_Mercantile = 0x1D, + Skill_Security = 0x1E, + Skill_Sneak = 0x1F, + Skill_Speechcraft = 0x20, + Skill_None = 0xFF, + Skill_Unknown = 0x00 + }; + + enum HeadPartIndex // TES4 + { + Head = 0, + EarMale = 1, + EarFemale = 2, + Mouth = 3, + TeethLower = 4, + TeethUpper = 5, + Tongue = 6, + EyeLeft = 7, // no texture + EyeRight = 8, // no texture + NumHeadParts = 9 + }; + + enum BodyPartIndex // TES4 + { + UpperBody = 0, + LowerBody = 1, + Hands = 2, + Feet = 3, + Tail = 4, + NumBodyParts = 5 + }; + + struct BodyPart + { + std::string mesh; // can be empty for arms, hands, etc + std::string texture; // can be empty e.g. eye left, eye right + }; + + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + bool mIsTES5; + + std::string mEditorId; + std::string mFullName; + std::string mDesc; + std::string mModelMale; // TES5 skeleton (in TES4 skeleton is found in npc_) + std::string mModelFemale; // TES5 skeleton (in TES4 skeleton is found in npc_) + + AttributeValues mAttribMale; + AttributeValues mAttribFemale; + std::map mSkillBonus; + + // DATA + float mHeightMale; + float mHeightFemale; + float mWeightMale; + float mWeightFemale; + std::uint32_t mRaceFlags; // 0x0001 = playable? + + std::vector mHeadParts; // see HeadPartIndex + std::vector mHeadPartsFemale; // see HeadPartIndex + + std::vector mBodyPartsMale; // see BodyPartIndex + std::vector mBodyPartsFemale; // see BodyPartIndex + + std::vector mEyeChoices; // texture only + std::vector mHairChoices; // not for TES5 + + float mFaceGenMainClamp; + float mFaceGenFaceClamp; + std::vector mSymShapeModeCoefficients; // should be 50 + std::vector mSymShapeModeCoeffFemale; // should be 50 + std::vector mAsymShapeModeCoefficients; // should be 30 + std::vector mAsymShapeModeCoeffFemale; // should be 30 + std::vector mSymTextureModeCoefficients; // should be 50 + std::vector mSymTextureModeCoeffFemale; // should be 50 + + std::map mDisposition; // race adjustments + std::vector mBonusSpells; // race ability/power + std::vector mVNAM; // don't know what these are; 1 or 2 RACE FormIds + std::vector mDefaultHair; // male/female (HAIR FormId for TES4) + + std::uint32_t mNumKeywords; + + FormId mSkin; // TES5 + BodyTemplate mBodyTemplate; // TES5 + + // FIXME: there's no fixed order? + // head, mouth, eyes, brow, hair + std::vector mHeadPartIdsMale; // TES5 + std::vector mHeadPartIdsFemale; // TES5 + + Race(); + virtual ~Race(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_RACE diff --git a/components/esm4/loadrefr.cpp b/components/esm4/loadrefr.cpp new file mode 100644 index 0000000000..4921fd16ca --- /dev/null +++ b/components/esm4/loadrefr.cpp @@ -0,0 +1,349 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 "loadrefr.hpp" + +#include +#include // FIXME: debug only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Reference::Reference() : mParent(0), mFormId(0), mFlags(0), + mBaseObj(0), mScale(1.f), mOwner(0), mGlobal(0), mFactionRank(-1), + mInitiallyDisabled(false), mIsMapMarker(false), mMapMarker(0), mCount(1), + mAudioLocation(0), mIsLocked(false), mLockLevel(0), mKey(0), mTargetRef(0) +{ + mEditorId.clear(); + mFullName.clear(); + + //mPlacement. + + mEsp.parent = 0; + mEsp.flags = 0; + + mRadio.rangeRadius = 0.f; + mRadio.broadcastRange = 0; + mRadio.staticPercentage = 0.f; + mRadio.posReference = 0; + + mDoor.destDoor = 0; + //mDoor.destPos. + mDoor.flags = 0; +} + +ESM4::Reference::~Reference() +{ +} + +void ESM4::Reference::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + mParent = reader.currCell(); // NOTE: only for persistent refs? + + // TODO: Let the engine apply this? Saved games? + //mInitiallyDisabled = ((mFlags & ESM4::Rec_Disabled) != 0) ? true : false; + std::uint32_t esmVer = reader.esmVersion(); + bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134; + + FormId mid; + FormId sid; + + 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.getLocalizedString(mFullName); 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 + //if (mBaseObj == 0x20) // e.g. FO3 mFormId == 0x0007E90F + //if (mBaseObj == 0x17) + //std::cout << mEditorId << std::endl; + break; + } + case ESM4::SUB_DATA: reader.get(mPlacement); 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.getFormId(mDoor.destDoor); + reader.get(mDoor.destPos); + if (esmVer == ESM::VER_094 || esmVer == ESM::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) + { + float data, data2, data3; + reader.get(data); + reader.get(data2); + reader.get(data3); + //bool hasVisibleWhenDistantFlag = (mFlags & 0x00008000) != 0; // currently unused + // some are trees, e.g. 000E03B6, mBaseObj 00022F32, persistent, visible when distant + // some are doors, e.g. 000270F7, mBaseObj 000CD338, persistent, initially disabled + // (this particular one is an Oblivion Gate) + //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; + } + case ESM4::SUB_XRTM: // formId + { + // seems like another ref, e.g. 00064583 has base object 00000034 which is "XMarkerHeading" + // e.g. some are doors (prob. quest related) + // MS94OblivionGateRef XRTM : 00097C88 + // MQ11SkingradGate XRTM : 00064583 + // MQ11ChorrolGate XRTM : 00188770 + // MQ11LeyawiinGate XRTM : 0018AD7C + // MQ11AnvilGate XRTM : 0018D452 + // MQ11BravilGate XRTM : 0018AE1B + // e.g. some are XMarkerHeading + // XRTM : 000A4DD7 in OblivionRDCavesMiddleA05 (maybe spawn points?) + FormId marker; + reader.getFormId(marker); + //std::cout << "REFR " << mEditorId << " XRTM : " << formIdToString(marker) << std::endl;// FIXME + break; + } + case ESM4::SUB_TNAM: //reader.get(mMapMarker); break; + { + if (subHdr.dataSize != sizeof(mMapMarker)) + //reader.skipSubRecordData(); // FIXME: FO3 + reader.getFormId(mid); + else + reader.get(mMapMarker); // TES4 + + break; + } + case ESM4::SUB_XMRK: mIsMapMarker = true; break; // all have mBaseObj 0x00000010 "MapMarker" + case ESM4::SUB_FNAM: + { + //std::cout << "REFR " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + case ESM4::SUB_XTRG: // formId + { + reader.getFormId(mTargetRef); + //std::cout << "REFR XRTG : " << formIdToString(id) << std::endl;// FIXME + break; + } + case ESM4::SUB_CNAM: reader.getFormId(mAudioLocation); break; // FONV + case ESM4::SUB_XRDO: // FO3 + { + reader.get(mRadio.rangeRadius); + reader.get(mRadio.broadcastRange); + reader.get(mRadio.staticPercentage); + reader.getFormId(mRadio.posReference); + + break; + } + case ESM4::SUB_SCRO: // FO3 + { + reader.getFormId(sid); + //if (mFormId == 0x0016b74B) + //std::cout << "REFR SCRO : " << formIdToString(sid) << std::endl;// FIXME + break; + } + case ESM4::SUB_XLOC: + { + mIsLocked = true; + std::int8_t dummy; // FIXME: very poor code + + reader.get(mLockLevel); + reader.get(dummy); + reader.get(dummy); + reader.get(dummy); + reader.getFormId(mKey); + reader.get(dummy); // flag? + reader.get(dummy); + reader.get(dummy); + reader.get(dummy); + if (subHdr.dataSize == 16) + reader.skipSubRecordData(4); // Oblivion (sometimes), flag? + else if (subHdr.dataSize == 20) // Skyrim, FO3 + reader.skipSubRecordData(8); // flag? + + 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_XPCI: // formId + case ESM4::SUB_XLCM: + case ESM4::SUB_XCNT: + 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_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_RCLR: // FO3 + case ESM4::SUB_BNAM: // 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 + { + //if (mFormId == 0x0007e90f) // XPRM XPOD + //if (mBaseObj == 0x17) //XPRM XOCP occlusion plane data XMBO bound half extents + //std::cout << "REFR " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::REFR::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } + //if (mFormId == 0x0016B74B) // base is TACT vCasinoUltraLuxeRadio in cell ULCasino + //std::cout << "REFR SCRO " << formIdToString(sid) << std::endl; +} + +//void ESM4::Reference::save(ESM4::Writer& writer) const +//{ +//} + +void ESM4::Reference::blank() +{ +} diff --git a/components/esm4/loadrefr.hpp b/components/esm4/loadrefr.hpp new file mode 100644 index 0000000000..d9c96cbf9f --- /dev/null +++ b/components/esm4/loadrefr.hpp @@ -0,0 +1,119 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 + +#include "reference.hpp" // FormId, Placement, EnableParent + +namespace ESM4 +{ + class Reader; + class Writer; + + enum MapMarkerType + { + Map_None = 0x00, // ? + Map_Camp = 0x01, + Map_Cave = 0x02, + Map_City = 0x03, + Map_ElvenRuin = 0x04, + Map_FortRuin = 0x05, + Map_Mine = 0x06, + Map_Landmark = 0x07, + Map_Tavern = 0x08, + Map_Settlement = 0x09, + Map_DaedricShrine = 0x0A, + Map_OblivionGate = 0x0B, + Map_Unknown = 0x0C // ? (door icon) + }; + + struct TeleportDest + { + FormId destDoor; + Placement destPos; + std::uint32_t flags; // 0x01 no alarm (only in TES5) + }; + + struct RadioStationData + { + float rangeRadius; + // 0 radius, 1 everywhere, 2 worldspace and linked int, 3 linked int, 4 current cell only + std::uint32_t broadcastRange; + float staticPercentage; + FormId posReference; // only used if broadcastRange == 0 + }; + + struct Reference + { + FormId mParent; // cell FormId (currently persistent refs only), from the loading sequence + // NOTE: for exterior cells it will be the dummy cell FormId + + 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; + + Placement mPlacement; + float mScale; // default 1.f + FormId mOwner; + FormId mGlobal; + std::int32_t mFactionRank; + + bool mInitiallyDisabled; // TODO may need to check mFlags & 0x800 (initially disabled) + bool mIsMapMarker; + std::uint16_t mMapMarker; + + EnableParent mEsp; + + std::uint32_t mCount; // only if > 1 (default 1) + + FormId mAudioLocation; + + RadioStationData mRadio; + + TeleportDest mDoor; + bool mIsLocked; + std::int8_t mLockLevel; + FormId mKey; + + FormId mTargetRef; + + 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/components/esm4/loadregn.cpp b/components/esm4/loadregn.cpp new file mode 100644 index 0000000000..6f871e8471 --- /dev/null +++ b/components/esm4/loadregn.cpp @@ -0,0 +1,164 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 "loadregn.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +#include +#include + +//#include // FIXME: debug only +//#include "formid.hpp" + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Region::Region() : mFormId(0), mFlags(0), mWorldId(0), mEdgeFalloff(0) +{ + mEditorId.clear(); + mShader.clear(); + mMapName.clear(); +} + +ESM4::Region::~Region() +{ +} + +void ESM4::Region::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_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: reader.get(mData); break; + case ESM4::SUB_RDMP: + { + assert(mData.type == RDAT_Map && "REGN unexpected data type"); + reader.getLocalizedString(mMapName); break; + } + // FO3 only 2: DemoMegatonSound and DC01 (both 0 RDMD) + // FONV none + case ESM4::SUB_RDMD: // music type; 0 default, 1 public, 2 dungeon + { +#if 0 + int dummy; + reader.get(dummy); + std::cout << "REGN " << mEditorId << " " << dummy << std::endl; +#else + reader.skipSubRecordData(); +#endif + break; + } + case ESM4::SUB_RDMO: // not seen in FO3/FONV? + { + //std::cout << "REGN " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + case ESM4::SUB_RDSD: // Possibly the same as RDSA + { + //assert(mData.type == RDAT_Map && "REGN unexpected data type"); + if (mData.type != RDAT_Sound) + throw std::runtime_error("ESM4::REGN::load - unexpected data type " + + ESM::printName(subHdr.typeId)); + + std::size_t numSounds = subHdr.dataSize / sizeof(RegionSound); + mSounds.resize(numSounds); + for (std::size_t i = 0; i < numSounds; ++i) + reader.get(mSounds.at(i)); + + break; + } + case ESM4::SUB_RDGS: // Only in Oblivion? (ToddTestRegion1) // formId + 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 + case ESM4::SUB_NVMI: // TES5 + { + //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 " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); // FIXME: process the subrecord rather than skip + break; + } + default: + throw std::runtime_error("ESM4::REGN::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Region::save(ESM4::Writer& writer) const +//{ +//} + +void ESM4::Region::blank() +{ +} diff --git a/components/esm4/loadregn.hpp b/components/esm4/loadregn.hpp new file mode 100644 index 0000000000..36ed8eb9d5 --- /dev/null +++ b/components/esm4/loadregn.hpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2015-2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Region + { + enum RegionDataType + { + RDAT_None = 0x00, + RDAT_Objects = 0x02, + RDAT_Weather = 0x03, + RDAT_Map = 0x04, + RDAT_Landscape = 0x05, + RDAT_Grass = 0x06, + RDAT_Sound = 0x07, + RDAT_Imposter = 0x08 + }; + +#pragma pack(push, 1) + struct RegionData + { + std::uint32_t type; + std::uint8_t flag; + std::uint8_t priority; + std::uint16_t unknown; + }; + + struct RegionSound + { + FormId sound; + std::uint32_t flags; // 0 pleasant, 1 cloudy, 2 rainy, 3 snowy + std::uint32_t chance; + }; +#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::uint32_t mColour; // RGBA + FormId mWorldId; // worldspace formid + + std::string mShader; //?? ICON + std::string mMapName; + + std::uint32_t mEdgeFalloff; + std::vector mRPLD; // unknown, point data? + + RegionData mData; + std::vector mSounds; + + 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/components/esm4/loadroad.cpp b/components/esm4/loadroad.cpp new file mode 100644 index 0000000000..cb7742eb7c --- /dev/null +++ b/components/esm4/loadroad.cpp @@ -0,0 +1,123 @@ +/* + Copyright (C) 2020 - 2021 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 "loadroad.hpp" + +#include +//#include // FIXME: for debugging only + +#include "formid.hpp" // FIXME: for workaround +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Road::Road() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + + mNodes.clear(); + mLinks.clear(); +} + +ESM4::Road::~Road() +{ +} + +void ESM4::Road::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + mParent = reader.currWorld(); + + mEditorId = formIdToString(mFormId); // FIXME: quick workaround to use existing code + + while (reader.getSubRecordHeader()) + { + const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + switch (subHdr.typeId) + { + case ESM4::SUB_PGRP: + { + std::size_t numNodes = subHdr.dataSize / sizeof(PGRP); + + mNodes.resize(numNodes); + for (std::size_t i = 0; i < numNodes; ++i) + { + reader.get(mNodes.at(i)); + } + + break; + } + case ESM4::SUB_PGRR: + { + static PGRR link; + static RDRP linkPt; + + for (std::size_t i = 0; i < mNodes.size(); ++i) + { + for (std::size_t j = 0; j < mNodes[i].numLinks; ++j) + { + link.startNode = std::int16_t(i); + + reader.get(linkPt); + + // FIXME: instead of looping each time, maybe use a map? + bool found = false; + for (std::size_t k = 0; k < mNodes.size(); ++k) + { + if (linkPt.x != mNodes[k].x || linkPt.y != mNodes[k].y || linkPt.z != mNodes[k].z) + continue; + else + { + link.endNode = std::int16_t(k); + mLinks.push_back(link); + + found = true; + break; + } + } + + if (!found) + throw std::runtime_error("ESM4::ROAD::PGRR - Unknown link point " + + std::to_string(j) + "at node " + std::to_string(i) + "."); + } + } + + break; + } + default: + throw std::runtime_error("ESM4::ROAD::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Road::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Road::blank() +//{ +//} diff --git a/components/esm4/loadroad.hpp b/components/esm4/loadroad.hpp new file mode 100644 index 0000000000..dad92e858c --- /dev/null +++ b/components/esm4/loadroad.hpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2020 - 2021 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_ROAD_H +#define ESM4_ROAD_H + +#include +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Road + { +#pragma pack(push, 1) + // FIXME: duplicated from PGRD + struct PGRP + { + float x; + float y; + float z; + std::uint8_t numLinks; + std::uint8_t unknown1; + std::uint16_t unknown2; + }; + + // FIXME: duplicated from PGRD + struct PGRR + { + std::int16_t startNode; + std::int16_t endNode; + }; + + struct RDRP + { + float x; + float y; + float z; + }; +#pragma pack(pop) + FormId mParent; // world FormId, 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::vector mNodes; + std::vector mLinks; + + Road(); + virtual ~Road(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_ROAD_H diff --git a/components/esm4/loadsbsp.cpp b/components/esm4/loadsbsp.cpp new file mode 100644 index 0000000000..c9af5b856e --- /dev/null +++ b/components/esm4/loadsbsp.cpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2016, 2018, 2020 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 "loadsbsp.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 " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::SubSpace::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::SubSpace::blank() +//{ +//} diff --git a/components/esm4/loadsbsp.hpp b/components/esm4/loadsbsp.hpp new file mode 100644 index 0000000000..c0ca7c5ddf --- /dev/null +++ b/components/esm4/loadsbsp.hpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + + 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/components/esm4/loadscol.cpp b/components/esm4/loadscol.cpp new file mode 100644 index 0000000000..1990af5f4b --- /dev/null +++ b/components/esm4/loadscol.cpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#include "loadscol.hpp" + +#include +#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::StaticCollection::StaticCollection() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); +} + +ESM4::StaticCollection::~StaticCollection() +{ +} + +void ESM4::StaticCollection::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_OBND: + case ESM4::SUB_MODL: + case ESM4::SUB_MODT: + case ESM4::SUB_ONAM: + case ESM4::SUB_DATA: + { + //std::cout << "SCOL " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + std::cout << "SCOL " << ESM::printName(subHdr.typeId) << " skipping..." + << subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + //throw std::runtime_error("ESM4::SCOL::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::StaticCollection::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::StaticCollection::blank() +//{ +//} diff --git a/components/esm4/loadscol.hpp b/components/esm4/loadscol.hpp new file mode 100644 index 0000000000..c32cdd3ca9 --- /dev/null +++ b/components/esm4/loadscol.hpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2020 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#ifndef ESM4_SCOL_H +#define ESM4_SCOL_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct StaticCollection + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + StaticCollection(); + virtual ~StaticCollection(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_SCOL_H diff --git a/components/esm4/loadscpt.cpp b/components/esm4/loadscpt.cpp new file mode 100644 index 0000000000..cf4bdeb58a --- /dev/null +++ b/components/esm4/loadscpt.cpp @@ -0,0 +1,173 @@ +/* + Copyright (C) 2019-2021 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 "loadscpt.hpp" + +#include +#include // FIXME: debugging only +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Script::Script() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); +} + +ESM4::Script::~Script() +{ +} + +void ESM4::Script::load(ESM4::Reader& reader) +{ + mFormId = reader.hdr().record.id; + reader.adjustFormId(mFormId); + mFlags = reader.hdr().record.flags; + + static ScriptLocalVariableData localVar; + + while (reader.getSubRecordHeader()) + { + const ESM4::SubRecordHeader& subHdr = reader.subRecordHeader(); + switch (subHdr.typeId) + { + case ESM4::SUB_EDID: + { + reader.getZString(mEditorId); + break; + } + case ESM4::SUB_SCHR: + { + // 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; +#else + reader.get(mScript.scriptHeader); +#endif + break; + } + case ESM4::SUB_SCTX: reader.getString(mScript.scriptSource); + //if (mEditorId == "CTrapLogs01SCRIPT") + //std::cout << mScript.scriptSource << std::endl; + break; + case ESM4::SUB_SCDA: // compiled script data + { + // For debugging only +#if 0 + if (subHdr.dataSize >= 4096) + { + std::cout << "Skipping " << mEditorId << std::endl; + reader.skipSubRecordData(); + break; + } + + std::cout << mEditorId << std::endl; + + unsigned char mDataBuf[4096/*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 < 4096/*bufSize*/-1) + ss << " "; + } + std::cout << ss.str() << std::endl; +#else + reader.skipSubRecordData(); +#endif + break; + } + case ESM4::SUB_SCRO: reader.getFormId(mScript.globReference); break; + case ESM4::SUB_SLSD: + { + localVar.clear(); + reader.get(localVar.index); + reader.get(localVar.unknown1); + reader.get(localVar.unknown2); + reader.get(localVar.unknown3); + reader.get(localVar.type); + reader.get(localVar.unknown4); + // WARN: assumes SCVR will follow immediately + + break; + } + case ESM4::SUB_SCVR: // assumed always pair with SLSD + { + reader.getZString(localVar.variableName); + + mScript.localVarData.push_back(localVar); + + break; + } + case ESM4::SUB_SCRV: + { + std::uint32_t index; + reader.get(index); + + mScript.localRefVarIndex.push_back(index); + + break; + } + default: + //std::cout << "SCPT " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + //reader.skipSubRecordData(); + //break; + throw std::runtime_error("ESM4::SCPT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Script::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Script::blank() +//{ +//} diff --git a/components/esm4/loadscpt.hpp b/components/esm4/loadscpt.hpp new file mode 100644 index 0000000000..747f49444d --- /dev/null +++ b/components/esm4/loadscpt.hpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2019, 2020 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_SCPT_H +#define ESM4_SCPT_H + +#include + +#include "formid.hpp" +#include "script.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Script + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + ScriptDefinition mScript; + + Script(); + virtual ~Script(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_SCPT_H diff --git a/components/esm4/loadscrl.cpp b/components/esm4/loadscrl.cpp new file mode 100644 index 0000000000..c09147ce5e --- /dev/null +++ b/components/esm4/loadscrl.cpp @@ -0,0 +1,99 @@ +/* + Copyright (C) 2019-2021 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 "loadscrl.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Scroll::Scroll() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mText.clear(); + + mData.value = 0; + mData.weight = 0.f; +} + +ESM4::Scroll::~Scroll() +{ +} + +void ESM4::Scroll::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.getLocalizedString(mFullName); break; + case ESM4::SUB_DESC: reader.getLocalizedString(mText); break; + case ESM4::SUB_DATA: + { + reader.get(mData.value); + reader.get(mData.weight); + break; + } + case ESM4::SUB_MODL: reader.getZString(mModel); break; + //case ESM4::SUB_MODB: reader.get(mBoundRadius); break; + case ESM4::SUB_OBND: + case ESM4::SUB_CTDA: + case ESM4::SUB_EFID: + case ESM4::SUB_EFIT: + case ESM4::SUB_ETYP: + case ESM4::SUB_KSIZ: + case ESM4::SUB_KWDA: + case ESM4::SUB_MDOB: + case ESM4::SUB_MODT: + case ESM4::SUB_SPIT: + { + //std::cout << "SCRL " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::SCRL::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Scroll::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Scroll::blank() +//{ +//} diff --git a/components/esm4/loadscrl.hpp b/components/esm4/loadscrl.hpp new file mode 100644 index 0000000000..a88781a6f8 --- /dev/null +++ b/components/esm4/loadscrl.hpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2019, 2020 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_SCRL_H +#define ESM4_SCRL_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Scroll + { + struct Data + { + 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; + std::string mText; + + Data mData; + + Scroll(); + virtual ~Scroll(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_SCRL_H diff --git a/components/esm4/loadsgst.cpp b/components/esm4/loadsgst.cpp new file mode 100644 index 0000000000..b46c6bad29 --- /dev/null +++ b/components/esm4/loadsgst.cpp @@ -0,0 +1,118 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loadsgst.hpp" + +#include +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::SigilStone::SigilStone() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScriptId(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(mScriptId); 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: + { + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::SGST::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::SigilStone::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::SigilStone::blank() +//{ +//} diff --git a/components/esm4/loadsgst.hpp b/components/esm4/loadsgst.hpp new file mode 100644 index 0000000000..b0bbf9d800 --- /dev/null +++ b/components/esm4/loadsgst.hpp @@ -0,0 +1,75 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "effect.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 mScriptId; + 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/components/esm4/loadslgm.cpp b/components/esm4/loadslgm.cpp new file mode 100644 index 0000000000..482c817802 --- /dev/null +++ b/components/esm4/loadslgm.cpp @@ -0,0 +1,91 @@ +/* + Copyright (C) 2016, 2018, 2021 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 "loadslgm.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::SoulGem::SoulGem() : mFormId(0), mFlags(0), mBoundRadius(0.f), mScriptId(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: reader.getLocalizedString(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_SCRI: reader.getFormId(mScriptId); 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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::SLGM::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::SoulGem::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::SoulGem::blank() +//{ +//} diff --git a/components/esm4/loadslgm.hpp b/components/esm4/loadslgm.hpp new file mode 100644 index 0000000000..8724da13c1 --- /dev/null +++ b/components/esm4/loadslgm.hpp @@ -0,0 +1,76 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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 mScriptId; + 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/components/esm4/loadsndr.cpp b/components/esm4/loadsndr.cpp new file mode 100644 index 0000000000..25109e36b6 --- /dev/null +++ b/components/esm4/loadsndr.cpp @@ -0,0 +1,94 @@ +/* + Copyright (C) 2020 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 "loadsndr.hpp" + +#include +//#include // FIXME: for debugging only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::SoundReference::SoundReference() : mFormId(0), mFlags(0), mSoundCategory(0), mSoundId(0), mOutputModel(0) +{ + mEditorId.clear(); + mSoundFile.clear(); +} + +ESM4::SoundReference::~SoundReference() +{ +} + +void ESM4::SoundReference::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_CTDA: + { + reader.get(&mTargetCondition, 20); + reader.get(mTargetCondition.runOn); + reader.get(mTargetCondition.reference); + if (mTargetCondition.reference) + reader.adjustFormId(mTargetCondition.reference); + reader.skipSubRecordData(4); // unknown + + break; + } + case ESM4::SUB_GNAM: reader.getFormId(mSoundCategory); break; + case ESM4::SUB_SNAM: reader.getFormId(mSoundId); break; + case ESM4::SUB_ONAM: reader.getFormId(mOutputModel); break; + case ESM4::SUB_ANAM: reader.getZString(mSoundFile); break; + case ESM4::SUB_LNAM: reader.get(mLoopInfo); break; + case ESM4::SUB_BNAM: reader.get(mData); break; + case ESM4::SUB_CNAM: // CRC32 hash + case ESM4::SUB_FNAM: // unknown + { + //std::cout << "SNDR " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::SNDR::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::SoundReference::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::SoundReference::blank() +//{ +//} diff --git a/components/esm4/loadsndr.hpp b/components/esm4/loadsndr.hpp new file mode 100644 index 0000000000..17f0491136 --- /dev/null +++ b/components/esm4/loadsndr.hpp @@ -0,0 +1,86 @@ +/* + Copyright (C) 2020 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_SNDR_H +#define ESM4_SNDR_H + +#include +#include + +#include "formid.hpp" +#include "script.hpp" // TargetCondition + +namespace ESM4 +{ + class Reader; + class Writer; + +#pragma pack(push, 1) + struct LoopInfo + { + std::uint16_t flags; + std::uint8_t unknown; + std::uint8_t rumble; + }; + + struct SoundInfo + { + std::int8_t frequencyAdjustment; // %, signed + std::uint8_t frequencyVariance; // % + std::uint8_t priority; // default 128 + std::uint8_t dBVriance; + std::uint16_t staticAttenuation; // divide by 100 to get value in dB + }; +#pragma pack(pop) + + struct SoundReference + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + FormId mSoundCategory; // SNCT + FormId mSoundId; // another SNDR + FormId mOutputModel; // SOPM + + std::string mSoundFile; + LoopInfo mLoopInfo; + SoundInfo mData; + + TargetCondition mTargetCondition; + + SoundReference(); + virtual ~SoundReference(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_SNDR_H diff --git a/components/esm4/loadsoun.cpp b/components/esm4/loadsoun.cpp new file mode 100644 index 0000000000..6441d8a799 --- /dev/null +++ b/components/esm4/loadsoun.cpp @@ -0,0 +1,95 @@ +/* + Copyright (C) 2016, 2018, 2020 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 "loadsoun.hpp" + +#include +//#include // FIXME + +#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: + { + if (subHdr.dataSize == 8) + reader.get(&mData, 8); + else + { + reader.get(mData); + reader.get(mExtra); + } + + break; + } + 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 " << ESM::printName(subHdr.typeId) << " skipping..." + //<< subHdr.dataSize << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::SOUN::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Sound::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Sound::blank() +//{ +//} diff --git a/components/esm4/loadsoun.hpp b/components/esm4/loadsoun.hpp new file mode 100644 index 0000000000..845f38c380 --- /dev/null +++ b/components/esm4/loadsoun.hpp @@ -0,0 +1,101 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Sound + { + enum Flags + { + Flag_RandomFreqShift = 0x0001, + Flag_PlayAtRandom = 0x0002, + Flag_EnvIgnored = 0x0004, + Flag_RandomLocation = 0x0008, + Flag_Loop = 0x0010, + Flag_MenuSound = 0x0020, + Flag_2D = 0x0040, + Flag_360LFE = 0x0080 + }; + +#pragma pack(push, 1) + struct SNDX + { + std::uint8_t minAttenuation; // distance? + std::uint8_t maxAttenuation; // distance? + std::int8_t freqAdjustment; // %, signed + std::uint8_t unknown; // probably padding + std::uint16_t flags; + std::uint16_t unknown2; // probably padding + std::uint16_t staticAttenuation; // divide by 100 to get value in dB + std::uint8_t stopTime; // multiply by 1440/256 to get value in minutes + std::uint8_t startTime; // multiply by 1440/256 to get value in minutes + }; + + struct SoundData + { + std::int16_t attenuationPoint1; + std::int16_t attenuationPoint2; + std::int16_t attenuationPoint3; + std::int16_t attenuationPoint4; + std::int16_t attenuationPoint5; + std::int16_t reverbAttenuationControl; + std::int32_t priority; + std::int32_t x; + std::int32_t y; + }; +#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; + SoundData mExtra; + + 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/components/esm4/loadstat.cpp b/components/esm4/loadstat.cpp new file mode 100644 index 0000000000..de487147fe --- /dev/null +++ b/components/esm4/loadstat.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 "loadstat.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() == ESM::VER_094 || reader.esmVersion() == ESM::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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::STAT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Static::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Static::blank() +//{ +//} diff --git a/components/esm4/loadstat.hpp b/components/esm4/loadstat.hpp new file mode 100644 index 0000000000..966721ef37 --- /dev/null +++ b/components/esm4/loadstat.hpp @@ -0,0 +1,62 @@ +/* + Copyright (C) 2015-2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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/components/esm4/loadtact.cpp b/components/esm4/loadtact.cpp new file mode 100644 index 0000000000..c082048086 --- /dev/null +++ b/components/esm4/loadtact.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2019-2021 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 "loadtact.hpp" + +#include +#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::TalkingActivator::TalkingActivator() : mFormId(0), mFlags(0), mScriptId(0), mVoiceType(0), mLoopSound(0), + mRadioTemplate(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); +} + +ESM4::TalkingActivator::~TalkingActivator() +{ +} + +void ESM4::TalkingActivator::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.getLocalizedString(mFullName); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); break; + case ESM4::SUB_VNAM: reader.getFormId(mVoiceType); break; + case ESM4::SUB_SNAM: reader.getFormId(mLoopSound); break; + case ESM4::SUB_INAM: reader.getFormId(mRadioTemplate); break; // FONV + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_DEST: // FO3 destruction + case ESM4::SUB_DSTD: // FO3 destruction + case ESM4::SUB_DSTF: // FO3 destruction + case ESM4::SUB_FNAM: + case ESM4::SUB_PNAM: + case ESM4::SUB_MODT: // texture file hash? + case ESM4::SUB_OBND: + { + //std::cout << "TACT " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::TACT::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::TalkingActivator::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::TalkingActivator::blank() +//{ +//} diff --git a/components/esm4/loadtact.hpp b/components/esm4/loadtact.hpp new file mode 100644 index 0000000000..a1d15524be --- /dev/null +++ b/components/esm4/loadtact.hpp @@ -0,0 +1,76 @@ +/* + Copyright (C) 2019, 2020 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_TACT_H +#define ESM4_TACT_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + enum TalkingActivatorFlags + { + TACT_OnLocalMap = 0x00000200, + TACT_QuestItem = 0x00000400, + TACT_NoVoiceFilter = 0x00002000, + TACT_RandomAnimStart = 0x00010000, + TACT_RadioStation = 0x00020000, + TACT_NonProxy = 0x10000000, // only valid if Radio Station + TACT_ContBroadcast = 0x40000000 // only valid if Radio Station + }; + + struct TalkingActivator + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see above for details + + std::string mEditorId; + std::string mFullName; + + std::string mModel; + + FormId mScriptId; + FormId mVoiceType; // VTYP + FormId mLoopSound; // SOUN + FormId mRadioTemplate; // SOUN + + TalkingActivator(); + virtual ~TalkingActivator(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_TACT_H diff --git a/components/esm4/loadterm.cpp b/components/esm4/loadterm.cpp new file mode 100644 index 0000000000..68a436d87c --- /dev/null +++ b/components/esm4/loadterm.cpp @@ -0,0 +1,101 @@ +/* + Copyright (C) 2019-2021 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 "loadterm.hpp" + +#include +//#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Terminal::Terminal() : mFormId(0), mFlags(0), mScriptId(0), mPasswordNote(0), mSound(0) +{ + mEditorId.clear(); + mFullName.clear(); + mText.clear(); + + mModel.clear(); + mResultText.clear(); +} + +ESM4::Terminal::~Terminal() +{ +} + +void ESM4::Terminal::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.getLocalizedString(mFullName); break; + case ESM4::SUB_DESC: reader.getLocalizedString(mText); break; + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); break; + case ESM4::SUB_PNAM: reader.getFormId(mPasswordNote); break; + case ESM4::SUB_SNAM: reader.getFormId(mSound); break; + case ESM4::SUB_MODL: reader.getZString(mModel); break; + case ESM4::SUB_RNAM: reader.getZString(mResultText); break; + case ESM4::SUB_DNAM: // difficulty + case ESM4::SUB_ANAM: // flags + case ESM4::SUB_CTDA: + case ESM4::SUB_INAM: + case ESM4::SUB_ITXT: + case ESM4::SUB_MODT: // texture hash? + case ESM4::SUB_SCDA: + case ESM4::SUB_SCHR: + case ESM4::SUB_SCRO: + case ESM4::SUB_SCRV: + case ESM4::SUB_SCTX: + case ESM4::SUB_SCVR: + case ESM4::SUB_SLSD: + case ESM4::SUB_TNAM: + case ESM4::SUB_OBND: + case ESM4::SUB_MODS: // FONV + { + //std::cout << "TERM " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::TERM::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Terminal::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Terminal::blank() +//{ +//} diff --git a/components/esm4/loadterm.hpp b/components/esm4/loadterm.hpp new file mode 100644 index 0000000000..9fb0ee60a8 --- /dev/null +++ b/components/esm4/loadterm.hpp @@ -0,0 +1,66 @@ +/* + Copyright (C) 2019, 2020 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_TERM_H +#define ESM4_TERM_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Terminal + { + 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 mText; + + std::string mModel; + std::string mResultText; + + FormId mScriptId; + FormId mPasswordNote; + FormId mSound; + + Terminal(); + virtual ~Terminal(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_TERM_H diff --git a/components/esm4/loadtes4.cpp b/components/esm4/loadtes4.cpp new file mode 100644 index 0000000000..42c4acde4e --- /dev/null +++ b/components/esm4/loadtes4.cpp @@ -0,0 +1,113 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 "loadtes4.hpp" + +#ifdef NDEBUG // FIXME: debuggigng only +#undef NDEBUG +#endif + +#include +#include + +#include // FIXME: debugging only + +#include "common.hpp" +#include "formid.hpp" +#include "reader.hpp" +//#include "writer.hpp" + +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.getExact(mData.version) || !reader.getExact(mData.records) || !reader.getExact(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: reader.getZString(mAuthor); break; + case ESM4::SUB_SNAM: reader.getZString(mDesc); break; + case ESM4::SUB_MAST: // multiple + { + ESM::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.getExact(mMaster.back().size)) + throw std::runtime_error("TES4 DATA data read error"); + break; + } + case ESM4::SUB_ONAM: + { + mOverrides.resize(subHdr.dataSize/sizeof(FormId)); + for (unsigned int & mOverride : mOverrides) + { + if (!reader.getExact(mOverride)) + 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(mOverride) << 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 << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::Header::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Header::save(ESM4::Writer& writer) +//{ +//} diff --git a/components/esm4/loadtes4.hpp b/components/esm4/loadtes4.hpp new file mode 100644 index 0000000000..6d84fe360b --- /dev/null +++ b/components/esm4/loadtes4.hpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 "formid.hpp" +#include "../esm/common.hpp" // ESMVersion, MasterData + +namespace ESM4 +{ + class Reader; + class Writer; + +#pragma pack(push, 1) + struct Data + { + ESM::ESMVersion version; // File format version. + std::int32_t records; // Number of records + std::uint32_t nextObjectId; + }; +#pragma pack(pop) + + struct Header + { + 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 mModIndices; + + void load (Reader& reader); + //void save (Writer& writer); + }; +} + +#endif // ESM4_TES4_H diff --git a/components/esm4/loadtree.cpp b/components/esm4/loadtree.cpp new file mode 100644 index 0000000000..7029222758 --- /dev/null +++ b/components/esm4/loadtree.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 "loadtree.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 " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::TREE::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Tree::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Tree::blank() +//{ +//} diff --git a/components/esm4/loadtree.hpp b/components/esm4/loadtree.hpp new file mode 100644 index 0000000000..2069f93d95 --- /dev/null +++ b/components/esm4/loadtree.hpp @@ -0,0 +1,62 @@ +/* + Copyright (C) 2016, 2018, 2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + 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/components/esm4/loadtxst.cpp b/components/esm4/loadtxst.cpp new file mode 100644 index 0000000000..ce281d4848 --- /dev/null +++ b/components/esm4/loadtxst.cpp @@ -0,0 +1,92 @@ +/* + Copyright (C) 2019, 2020 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 "loadtxst.hpp" + +#include +#include // FIXME: testing only + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::TextureSet::TextureSet() : mFormId(0), mFlags(0) +{ + mEditorId.clear(); + mDiffuse.clear(); + mNormalMap.clear(); + mEnvMask.clear(); + mToneMap.clear(); + mDetailMap.clear(); + mEnvMap.clear(); + mUnknown.clear(); + mSpecular.clear(); +} + +ESM4::TextureSet::~TextureSet() +{ +} + +void ESM4::TextureSet::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_TX00: reader.getZString(mDiffuse); break; + case ESM4::SUB_TX01: reader.getZString(mNormalMap); break; + case ESM4::SUB_TX02: reader.getZString(mEnvMask); break; + case ESM4::SUB_TX03: reader.getZString(mToneMap); break; + case ESM4::SUB_TX04: reader.getZString(mDetailMap); break; + case ESM4::SUB_TX05: reader.getZString(mEnvMap); break; + case ESM4::SUB_TX06: reader.getZString(mUnknown); break; + case ESM4::SUB_TX07: reader.getZString(mSpecular); break; + case ESM4::SUB_DNAM: + case ESM4::SUB_DODT: + case ESM4::SUB_OBND: // object bounds + { + //std::cout << "TXST " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::TXST::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::TextureSet::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::TextureSet::blank() +//{ +//} diff --git a/components/esm4/loadtxst.hpp b/components/esm4/loadtxst.hpp new file mode 100644 index 0000000000..cc81923e1f --- /dev/null +++ b/components/esm4/loadtxst.hpp @@ -0,0 +1,66 @@ +/* + Copyright (C) 2019, 2020 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_TXST_H +#define ESM4_TXST_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct TextureSet + { + FormId mFormId; // from the header + std::uint32_t mFlags; // from the header, see enum type RecordFlag for details + + std::string mEditorId; + + std::string mDiffuse; // includes alpha info + std::string mNormalMap; // includes specular info (alpha channel) + std::string mEnvMask; + std::string mToneMap; + std::string mDetailMap; + std::string mEnvMap; + std::string mUnknown; + std::string mSpecular; + + TextureSet(); + virtual ~TextureSet(); + + virtual void load(ESM4::Reader& reader); + //virtual void save(ESM4::Writer& writer) const; + + //void blank(); + }; +} + +#endif // ESM4_TXST_H diff --git a/components/esm4/loadweap.cpp b/components/esm4/loadweap.cpp new file mode 100644 index 0000000000..b56e01171c --- /dev/null +++ b/components/esm4/loadweap.cpp @@ -0,0 +1,188 @@ +/* + Copyright (C) 2016, 2018-2021 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 "loadweap.hpp" + +#include + +#include "reader.hpp" +//#include "writer.hpp" + +ESM4::Weapon::Weapon() : mFormId(0), mFlags(0), mPickUpSound(0), mDropSound(0), mBoundRadius(0.f), mScriptId(0), + mEnchantmentPoints(0), mEnchantment(0) +{ + mEditorId.clear(); + mFullName.clear(); + mModel.clear(); + mText.clear(); + mIcon.clear(); + mMiniIcon.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 == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::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: reader.getLocalizedString(mFullName); break; + case ESM4::SUB_DATA: + { + //if (reader.esmVersion() == ESM::VER_094 || reader.esmVersion() == ESM::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_MICO: reader.getZString(mMiniIcon); break; // FO3 + case ESM4::SUB_SCRI: reader.getFormId(mScriptId); 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_DESC: reader.getLocalizedString(mText); break; + case ESM4::SUB_YNAM: reader.getFormId(mPickUpSound); break; + case ESM4::SUB_ZNAM: reader.getFormId(mDropSound); 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_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_REPL: // 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 ESM4::SUB_EFSD: // FONV DeadMoney + { + //std::cout << "WEAP " << ESM::printName(subHdr.typeId) << " skipping..." << std::endl; + reader.skipSubRecordData(); + break; + } + default: + throw std::runtime_error("ESM4::WEAP::load - Unknown subrecord " + ESM::printName(subHdr.typeId)); + } + } +} + +//void ESM4::Weapon::save(ESM4::Writer& writer) const +//{ +//} + +//void ESM4::Weapon::blank() +//{ +//} diff --git a/components/esm4/loadweap.hpp b/components/esm4/loadweap.hpp new file mode 100644 index 0000000000..dae2da1e7e --- /dev/null +++ b/components/esm4/loadweap.hpp @@ -0,0 +1,96 @@ +/* + Copyright (C) 2016, 2018-2020 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 + +#include "formid.hpp" + +namespace ESM4 +{ + class Reader; + class Writer; + + struct Weapon + { + struct Data + { + // type + // 0 = Blade One Hand + // 1 = Blade Two Hand + // 2 = Blunt One Hand + // 3 = Blunt Two Hand + // 4 = Staff + // 5 = Bow + 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 mText; + std::string mIcon; + std::string mMiniIcon; + + FormId mPickUpSound; + FormId mDropSound; + + float mBoundRadius; + + FormId mScriptId; + 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/components/esm4/loadwrld.cpp b/components/esm4/loadwrld.cpp new file mode 100644 index 0000000000..ec01556338 --- /dev/null +++ b/components/esm4/loadwrld.cpp @@ -0,0 +1,205 @@ +/* + Copyright (C) 2015-2016, 2018-2021 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 "loadwrld.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), + mLandLevel(0.f), mWaterLevel(0.f), // -2700.f and -14000.f for TES5 + mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mSound(0), mMusic(0), mParentUseFlags(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 + + std::uint32_t esmVer = reader.esmVersion(); + //bool isTES4 = (esmVer == ESM::VER_080 || esmVer == ESM::VER_100); + //bool isFONV = (esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134); + bool isTES5 = (esmVer == ESM::VER_094 || esmVer == ESM::VER_170); // WARN: FO3 is also VER_094 + bool usingDefaultLevels = true; + + 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.getLocalizedString(mFullName); 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_DNAM: // defaults + { + reader.get(mLandLevel); // -2700.f for TES5 + reader.get(mWaterLevel); // -14000.f for TES5 + usingDefaultLevels = false; + + break; + } + // Only a few worlds in FO3 have music (I'm guessing 00090908 "explore" is the default?) + // 00090906 public WRLD: 00000A74 MegatonWorld + // 00090CE7 base WRLD: 0001A25D DCWorld18 (Arlington National Cemeteray) + // 00090CE7 base WRLD: 0001A266 DCWorld09 (The Mall) + // 00090CE7 base WRLD: 0001A267 DCWorld08 (Pennsylvania Avenue) + // 000BAD30 tranquilitylane WRLD: 000244A7 TranquilityLane + // 00090CE7 base WRLD: 000271C0 MonumentWorld (The Washington Monument) + // 00090907 dungeon WRLD: 0004C4D1 MamaDolcesWorld (Mama Dolce's Loading Yard) + // + // FONV has only 3 (note the different format, also can't find the files?): + // 00119D2E freeside\freeside_01.mp3 0010BEEA FreesideWorld (Freeside) + // 00119D2E freeside\freeside_01.mp3 0012D94D FreesideNorthWorld (Freeside) + // 00119D2E freeside\freeside_01.mp3 0012D94E FreesideFortWorld (Old Mormon Fort) + // NOTE: FONV DefaultObjectManager has 00090908 "explore" as the default music + case ESM4::SUB_ZNAM: reader.getFormId(mMusic); break; + case ESM4::SUB_PNAM: reader.get(mParentUseFlags); 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_MODL: + case ESM4::SUB_NAMA: + case ESM4::SUB_ONAM: + case ESM4::SUB_TNAM: + case ESM4::SUB_UNAM: + 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 " << ESM::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.updateRecordRead(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 " + ESM::printName(subHdr.typeId)); + } + + if (isTES5 && usingDefaultLevels) + { + mLandLevel = -2700.f; + mWaterLevel = -14000.f; + } + } +} + +//void ESM4::World::save(ESM4::Writer& writer) const +//{ +//} diff --git a/components/esm4/loadwrld.hpp b/components/esm4/loadwrld.hpp new file mode 100644 index 0000000000..0105dc12f4 --- /dev/null +++ b/components/esm4/loadwrld.hpp @@ -0,0 +1,137 @@ +/* + Copyright (C) 2015-2016, 2018-2020 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 +#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; + float mLandLevel; + float mWaterLevel; + + 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; + + // ---------------------- + FormId mMusic; + + // 0x01 use Land data + // 0x02 use LOD data + // 0x04 use Map data + // 0x08 use Water data + // 0x10 use Climate data + // 0x20 use Image Space data (Climate for TES5) + // 0x40 use SkyCell (TES5) + // 0x80 needs water adjustment (this isn't for parent I think? FONV only set for wastelandnv) + std::uint16_t mParentUseFlags; // FO3/FONV + + // 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 diff --git a/components/esm4/reader.cpp b/components/esm4/reader.cpp new file mode 100644 index 0000000000..f4fe8def68 --- /dev/null +++ b/components/esm4/reader.cpp @@ -0,0 +1,639 @@ +/* + Copyright (C) 2015-2021 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" + +#ifdef NDEBUG // FIXME: debugging only +#undef NDEBUG +#endif + +#undef DEBUG_GROUPSTACK + +#include +#include +#include +#include // for debugging +#include // for debugging +#include // for debugging + +#if defined(_MSC_VER) + #pragma warning (push) + #pragma warning (disable : 4706) + #include + #pragma warning (pop) +#else + #include +#endif +#include +#include +#include +#include +#include + +#include + +#include "formid.hpp" + +namespace ESM4 +{ + +ReaderContext::ReaderContext() : modIndex(0), recHeaderSize(sizeof(RecordHeader)), + filePos(0), recordRead(0), currWorld(0), currCell(0), cellGridValid(false) +{ + currCellGrid.cellId = 0; + currCellGrid.grid.x = 0; + currCellGrid.grid.y = 0; +} + +Reader::Reader(Files::IStreamPtr esmStream, const std::string& filename) + : mEncoder(nullptr), mFileSize(0), mStream(esmStream) +{ + // used by ESMReader only? + mCtx.filename = filename; + + mCtx.fileRead = 0; + mStream->seekg(0, mStream->end); + mFileSize = mStream->tellg(); + mStream->seekg(20); // go to the start but skip the "TES4" record header + + mSavedStream.reset(); + + // determine header size + std::uint32_t subRecName = 0; + mStream->read((char*)&subRecName, sizeof(subRecName)); + if (subRecName == 0x52444548) // "HEDR" + mCtx.recHeaderSize = sizeof(RecordHeader) - 4; // TES4 header size is 4 bytes smaller than TES5 header + else + mCtx.recHeaderSize = sizeof(RecordHeader); + + // restart from the beginning (i.e. "TES4" record header) + mStream->seekg(0, mStream->beg); +#if 0 + unsigned int esmVer = mHeader.mData.version.ui; + bool isTes4 = esmVer == ESM::VER_080 || esmVer == ESM::VER_100; + //bool isTes5 = esmVer == ESM::VER_094 || esmVer == ESM::VER_170; + //bool isFONV = esmVer == ESM::VER_132 || esmVer == ESM::VER_133 || esmVer == ESM::VER_134; + + // TES4 header size is 4 bytes smaller than TES5 header + mCtx.recHeaderSize = isTes4 ? sizeof(ESM4::RecordHeader) - 4 : sizeof(ESM4::RecordHeader); +#endif + getRecordHeader(); + if (mCtx.recordHeader.record.typeId == REC_TES4) + { + mHeader.load(*this); + mCtx.fileRead += mCtx.recordHeader.record.dataSize; + + buildLStringIndex(); // for localised strings in Skyrim + } + else + fail("Unknown file format"); +} + +Reader::~Reader() +{ + close(); +} + +// 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. +ReaderContext Reader::getContext() +{ + mCtx.filePos = mStream->tellg(); + mCtx.filePos -= mCtx.recHeaderSize; // update file position + return mCtx; +} + +// NOTE: Assumes that the caller has reopened the file if necessary +bool Reader::restoreContext(const ReaderContext& ctx) +{ + if (mSavedStream) // TODO: doesn't seem to ever happen + { + mStream = mSavedStream; + mSavedStream.reset(); + } + + mCtx.groupStack.clear(); // probably not necessary since it will be overwritten + mCtx = ctx; + mStream->seekg(ctx.filePos); // update file position + + return getRecordHeader(); +} + +void Reader::close() +{ + mStream.reset(); + //clearCtx(); + //mHeader.blank(); +} + +void Reader::openRaw(Files::IStreamPtr esmStream, const std::string& filename) +{ + close(); + + mStream = esmStream; + mCtx.filename = filename; + mCtx.fileRead = 0; + mStream->seekg(0, mStream->end); + mFileSize = mStream->tellg(); + mStream->seekg(0, mStream->beg); + +} + +void Reader::open(Files::IStreamPtr esmStream, const std::string &filename) +{ + openRaw(esmStream, filename); + + // should at least have the size of ESM3 record header (20 or 24 bytes for ESM4) + assert (mFileSize >= 16); + std::uint32_t modVer = 0; + if (getExact(modVer)) // get the first 4 bytes of the record header only + { + // FIXME: need to setup header/context + if (modVer == REC_TES4) + { + } + else + { + } + } + + throw std::runtime_error("Unknown file format"); // can't yet use fail() as mCtx is not setup +} + +void Reader::setRecHeaderSize(const std::size_t size) +{ + mCtx.recHeaderSize = size; +} + +// FIXME: only "English" strings supported for now +void 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 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? + Files::IStreamPtr filestream = Files::IStreamPtr(Files::openConstrainedFileStream(stringFile.c_str())); + + filestream->seekg(0, std::ios::end); + std::size_t fileSize = filestream->tellg(); + filestream->seekg(0, std::ios::beg); + + 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::unknown localised string type"); + } + + filestream->read((char*)&numEntries, sizeof(numEntries)); + filestream->read((char*)&dataSize, sizeof(dataSize)); + std::size_t dataStart = fileSize - dataSize; + for (unsigned int i = 0; i < numEntries; ++i) + { + filestream->read((char*)&stringId, sizeof(stringId)); + filestream->read((char*)&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 Reader::getLocalizedString(std::string& str) +{ + if (!hasLocalizedStrings()) + return (void)getZString(str); + + std::uint32_t stringId; // FormId + get(stringId); + if (stringId) // TES5 FoxRace, BOOK + getLocalizedStringImpl(stringId, str); +} + +// FIXME: very messy and probably slow/inefficient +void Reader::getLocalizedStringImpl(const FormId stringId, std::string& str) +{ + const std::map::const_iterator it = mLStringIndex.find(stringId); + + if (it != mLStringIndex.end()) + { + Files::IStreamPtr filestream; + + switch (it->second.type) + { + case Type_Strings: // no string size provided + { + filestream = mStrings; + filestream->seekg(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 unknown string type"); + } + + // get ILStrings or DLStrings (they provide string size) + filestream->seekg(it->second.offset); + std::uint32_t size = 0; + filestream->read((char*)&size, sizeof(size)); + getStringImpl(str, size, filestream, mEncoder, true); // expect null terminated string + } + else + throw std::runtime_error("ESM4::Reader::getLocalizedString localized string not found"); +} + +bool Reader::getRecordHeader() +{ + // FIXME: this seems very hacky but we may have skipped subrecords from within an inflated data block + if (/*mStream->eof() && */mSavedStream) + { + mStream = mSavedStream; + mSavedStream.reset(); + } + + mStream->read((char*)&mCtx.recordHeader, mCtx.recHeaderSize); + std::size_t bytesRead = (std::size_t)mStream->gcount(); + + // keep track of data left to read from the file + mCtx.fileRead += mCtx.recHeaderSize; + + mCtx.recordRead = 0; // for keeping track of sub records + + // After reading the record header we can cache a WRLD or CELL formId for convenient access later. + // FIXME: currently currWorld and currCell are set manually when loading the WRLD and CELL records + + // HACK: mCtx.groupStack.back() is updated before the record data are read/skipped + // N.B. the data must be fully read/skipped for this to work + if (mCtx.recordHeader.record.typeId != REC_GRUP && !mCtx.groupStack.empty()) + { + mCtx.groupStack.back().second += (std::uint32_t)mCtx.recHeaderSize + mCtx.recordHeader.record.dataSize; + + // keep track of data left to read from the file + mCtx.fileRead += mCtx.recordHeader.record.dataSize; + } + + return bytesRead == mCtx.recHeaderSize; +} + +void Reader::getRecordData(bool dump) +{ + std::uint32_t uncompressedSize = 0; + + if ((mCtx.recordHeader.record.flags & Rec_Compressed) != 0) + { + mStream->read(reinterpret_cast(&uncompressedSize), sizeof(std::uint32_t)); + + std::size_t recordSize = mCtx.recordHeader.record.dataSize - sizeof(std::uint32_t); + Bsa::MemoryInputStream compressedRecord(recordSize); + mStream->read(compressedRecord.getRawData(), recordSize); + std::istream *fileStream = (std::istream*)&compressedRecord; + mSavedStream = mStream; + + mCtx.recordHeader.record.dataSize = uncompressedSize - sizeof(uncompressedSize); + + std::shared_ptr memoryStreamPtr + = std::make_shared(uncompressedSize); + + boost::iostreams::filtering_streambuf inputStreamBuf; + inputStreamBuf.push(boost::iostreams::zlib_decompressor()); + inputStreamBuf.push(*fileStream); + + boost::iostreams::basic_array_sink sr(memoryStreamPtr->getRawData(), uncompressedSize); + boost::iostreams::copy(inputStreamBuf, sr); + + // For debugging only +//#if 0 +if (dump) +{ + std::ostringstream ss; + char* data = memoryStreamPtr->getRawData(); + for (unsigned int i = 0; i < uncompressedSize; ++i) + { + if (data[i] > 64 && data[i] < 91) + ss << (char)(data[i]) << " "; + else + ss << std::setfill('0') << std::setw(2) << std::hex << (int)(data[i]); + if ((i & 0x000f) == 0xf) + ss << "\n"; + else if (i < uncompressedSize-1) + ss << " "; + } + std::cout << ss.str() << std::endl; +} +//#endif + mStream = std::shared_ptr(memoryStreamPtr, (std::istream*)memoryStreamPtr.get()); + } +} + +void Reader::skipRecordData() +{ + assert (mCtx.recordRead <= mCtx.recordHeader.record.dataSize && "Skipping after reading more than available"); + mStream->ignore(mCtx.recordHeader.record.dataSize - mCtx.recordRead); + mCtx.recordRead = mCtx.recordHeader.record.dataSize; // for getSubRecordHeader() +} + +bool 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 mCtx.recordRead via updateRecordRead() + // See ESM4::NavMesh and ESM4::World. + if (mCtx.recordHeader.record.dataSize - mCtx.recordRead >= sizeof(mCtx.subRecordHeader)) + { + result = getExact(mCtx.subRecordHeader); + // HACK: below assumes sub-record data will be read or skipped in full; + // this hack aims to avoid updating mCtx.recordRead each time anything is read + mCtx.recordRead += (sizeof(mCtx.subRecordHeader) + mCtx.subRecordHeader.dataSize); + } + else if (mCtx.recordRead > mCtx.recordHeader.record.dataSize) + { + // try to correct any overshoot, seek to the end of the expected data + // this will only work if mCtx.subRecordHeader.dataSize was fully read or skipped + // (i.e. it will only correct mCtx.subRecordHeader.dataSize being incorrect) + // TODO: not tested + std::uint32_t overshoot = (std::uint32_t)mCtx.recordRead - mCtx.recordHeader.record.dataSize; + + std::size_t pos = mStream->tellg(); + mStream->seekg(pos - overshoot); + + return false; + } + + return result; +} + +void Reader::skipSubRecordData() +{ + mStream->ignore(mCtx.subRecordHeader.dataSize); +} + +void Reader::skipSubRecordData(std::uint32_t size) +{ + mStream->ignore(size); +} + +void Reader::enterGroup() +{ +#ifdef DEBUG_GROUPSTACK + std::string padding = ""; // FIXME: debugging only + padding.insert(0, mCtx.groupStack.size()*2, ' '); + std::cout << padding << "Starting record group " + << printLabel(mCtx.recordHeader.group.label, mCtx.recordHeader.group.type) << std::endl; +#endif + // empty group if the group size is same as the header size + if (mCtx.recordHeader.group.groupSize == (std::uint32_t)mCtx.recHeaderSize) + { +#ifdef DEBUG_GROUPSTACK + std::cout << padding << "Ignoring record group " // FIXME: debugging only + << printLabel(mCtx.recordHeader.group.label, mCtx.recordHeader.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, exitGroupCheck() may not get called before recursing into this method + mCtx.groupStack.back().second += mCtx.recordHeader.group.groupSize; + exitGroupCheck(); + } + + return; // don't push an empty group, just return + } + + // push group + mCtx.groupStack.push_back(std::make_pair(mCtx.recordHeader.group, (std::uint32_t)mCtx.recHeaderSize)); +} + +void Reader::exitGroupCheck() +{ + if (mCtx.groupStack.empty()) + return; + + // pop finished groups (note reading too much is allowed here) + std::uint32_t lastGroupSize = mCtx.groupStack.back().first.groupSize; + while (mCtx.groupStack.back().second >= lastGroupSize) + { +#ifdef DEBUG_GROUPSTACK + GroupTypeHeader grp = mCtx.groupStack.back().first; // FIXME: grp is for debugging only +#endif + // try to correct any overshoot + // TODO: not tested + std::uint32_t overshoot = mCtx.groupStack.back().second - lastGroupSize; + if (overshoot > 0) + { + std::size_t pos = mStream->tellg(); + mStream->seekg(pos - overshoot); + } + + mCtx.groupStack.pop_back(); +#ifdef DEBUG_GROUPSTACK + std::string padding = ""; // FIXME: debugging only + padding.insert(0, mCtx.groupStack.size()*2, ' '); + std::cout << padding << "Finished record group " << printLabel(grp.label, grp.type) << std::endl; +#endif + // if the previous group was the final one no need to do below + if (mCtx.groupStack.empty()) + return; + + mCtx.groupStack.back().second += lastGroupSize; + lastGroupSize = mCtx.groupStack.back().first.groupSize; + + assert (lastGroupSize >= mCtx.groupStack.back().second && "Read more records than available"); +//#if 0 + if (mCtx.groupStack.back().second > lastGroupSize) // FIXME: debugging only + std::cerr << printLabel(mCtx.groupStack.back().first.label, + mCtx.groupStack.back().first.type) + << " read more records than available" << std::endl; +//#endif + } +} + +// WARNING: this method should be used after first calling enterGroup() +// else the method may try to dereference an element that does not exist +const GroupTypeHeader& 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 Reader::skipGroupData() +{ + assert (!mCtx.groupStack.empty() && "Skipping group with an empty stack"); + + // subtract what was already read/skipped + std::uint32_t skipSize = mCtx.groupStack.back().first.groupSize - mCtx.groupStack.back().second; + + mStream->ignore(skipSize); + + // keep track of data left to read from the file + mCtx.fileRead += skipSize; + + mCtx.groupStack.back().second = mCtx.groupStack.back().first.groupSize; +} + +void Reader::skipGroup() +{ +#ifdef DEBUG_GROUPSTACK + std::string padding = ""; // FIXME: debugging only + padding.insert(0, mCtx.groupStack.size()*2, ' '); + std::cout << padding << "Skipping record group " + << printLabel(mCtx.recordHeader.group.label, mCtx.recordHeader.group.type) << std::endl; +#endif + // subtract the size of header already read before skipping + std::uint32_t skipSize = mCtx.recordHeader.group.groupSize - (std::uint32_t)mCtx.recHeaderSize; + mStream->ignore(skipSize); + + // keep track of data left to read from the file + mCtx.fileRead += skipSize; + + // NOTE: mCtx.groupStack.back().second already has mCtx.recHeaderSize from enterGroup() + if (!mCtx.groupStack.empty()) + mCtx.groupStack.back().second += mCtx.recordHeader.group.groupSize; +} + +const CellGrid& Reader::currCellGrid() const +{ + // Maybe should throw an exception instead? + assert (mCtx.cellGridValid && "Attempt to use an invalid cell grid"); + + return mCtx.currCellGrid; +} + +// NOTE: the parameter 'files' must have the file names in the loaded order +void Reader::updateModIndices(const std::vector& files) +{ + if (files.size() >= 0xff) + throw std::runtime_error("ESM4::Reader::updateModIndices 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; + + mCtx.parentFileIndices.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()) + mCtx.parentFileIndices[i] = (std::uint32_t)((it->second << 24) & 0xff000000); + else + throw std::runtime_error("ESM4::Reader::updateModIndices required dependency file not loaded"); +#if 0 + std::cout << "Master Mod: " << mCtx.header.mMaster[i].name << ", " // FIXME: debugging only + << formIdToString(mCtx.parentFileIndices[i]) << std::endl; +#endif + } + + if (!mCtx.parentFileIndices.empty() && mCtx.parentFileIndices[0] != 0) + throw std::runtime_error("ESM4::Reader::updateModIndices base modIndex is not zero"); +} + +// ModIndex adjusted formId according to master file dependencies +// (see http://www.uesp.net/wiki/Tes4Mod:FormID_Fixup) +// NOTE: need to update modindex to parentFileIndices.size() before saving +// +// FIXME: probably should add a parameter to check for mCtx.header::mOverrides +// (ACHR, LAND, NAVM, PGRE, PHZD, REFR), but not sure what exactly overrides mean +// i.e. use the modindx of its master? +// FIXME: Apparently ModIndex '00' in an ESP means the object is defined in one of its masters. +// This means we may need to search multiple times to get the correct id. +// (see https://www.uesp.net/wiki/Tes4Mod:Formid#ModIndex_Zero) +void Reader::adjustFormId(FormId& id) +{ + if (mCtx.parentFileIndices.empty()) + return; + + std::size_t index = (id >> 24) & 0xff; + + if (index < mCtx.parentFileIndices.size()) + id = mCtx.parentFileIndices[index] | (id & 0x00ffffff); + else + id = mCtx.modIndex | (id & 0x00ffffff); +} + +bool Reader::getFormId(FormId& id) +{ + if (!getExact(id)) + return false; + + adjustFormId(id); + return true; +} + +void Reader::adjustGRUPFormId() +{ + adjustFormId(mCtx.recordHeader.group.label.value); +} + +[[noreturn]] void Reader::fail(const std::string& msg) +{ + std::stringstream ss; + + ss << "ESM Error: " << msg; + ss << "\n File: " << mCtx.filename; + ss << "\n Record: " << ESM::printName(mCtx.recordHeader.record.typeId); + ss << "\n Subrecord: " << ESM::printName(mCtx.subRecordHeader.typeId); + if (mStream.get()) + ss << "\n Offset: 0x" << std::hex << mStream->tellg(); + + throw std::runtime_error(ss.str()); +} + +} diff --git a/components/esm4/reader.hpp b/components/esm4/reader.hpp new file mode 100644 index 0000000000..b3f1070495 --- /dev/null +++ b/components/esm4/reader.hpp @@ -0,0 +1,298 @@ +/* + Copyright (C) 2015-2016, 2018, 2020-2021 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 "common.hpp" +#include "loadtes4.hpp" +#include "../esm/reader.hpp" + +namespace ESM4 { + // bytes read from group, updated by + // getRecordHeader() in advance + // | + // v + typedef std::vector > GroupStack; + + struct ReaderContext { + std::string filename; // in case we need to reopen to restore the context + std::uint32_t modIndex; // the sequential position of this file in the load order: + // 0x00 reserved, 0xFF in-game (see notes below) + + // position in the vector = mod index of master files above + // value = adjusted mod index based on all the files loaded so far + std::vector parentFileIndices; + + 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 + + // for keeping track of things + std::size_t fileRead; // number of bytes read, incl. the current record + + GroupStack groupStack; // keep track of bytes left to find when a group is done + RecordHeader recordHeader; // header of the current record or group being processed + SubRecordHeader subRecordHeader; // header of the current sub record being processed + std::uint32_t recordRead; // bytes read from the sub records, incl. the current one + + FormId currWorld; // formId of current world - for grouping CELL records + FormId currCell; // formId of current cell + // FIXME: try to get rid of these two members, seem like massive hacks + CellGrid currCellGrid; // TODO: should keep a map of cell formids + bool cellGridValid; + + ReaderContext(); + }; + + class Reader : public ESM::Reader + { + Header mHeader; // ESM4 header + + ReaderContext mCtx; + + ToUTF8::Utf8Encoder* mEncoder; + + std::size_t mFileSize; + + Files::IStreamPtr mStream; + Files::IStreamPtr mSavedStream; // mStream is saved here while using deflated memory stream + + Files::IStreamPtr mStrings; + Files::IStreamPtr mILStrings; + Files::IStreamPtr mDLStrings; + + enum LocalizedStringType + { + Type_Strings = 0, + Type_ILStrings = 1, + Type_DLStrings = 2 + }; + + struct LStringOffset + { + LocalizedStringType type; + std::uint32_t offset; + }; + + std::map mLStringIndex; + + void buildLStringIndex(const std::string& stringFile, LocalizedStringType stringType); + + inline bool hasLocalizedStrings() const { return (mHeader.mFlags & Rec_Localized) != 0; } + + void getLocalizedStringImpl(const FormId stringId, std::string& str); + + // Close the file, resets all information. + // After calling close() the structure may be reused to load a new file. + //void close(); + + // Raw opening. Opens the file and sets everything up but doesn't parse the header. + void openRaw(Files::IStreamPtr esmStream, const std::string& filename); + + // Load ES file from a new stream, parses the header. + // Closes the currently open file first, if any. + void open(Files::IStreamPtr esmStream, const std::string& filename); + + Reader() = default; + + public: + + Reader(Files::IStreamPtr esmStream, const std::string& filename); + ~Reader(); + + // FIXME: should be private but ESMTool uses it + void openRaw(const std::string& filename) { + openRaw(Files::openConstrainedFileStream(filename.c_str()), filename); + } + + void open(const std::string& filename) { + open(Files::openConstrainedFileStream (filename.c_str ()), filename); + } + + void close() final; + + inline bool isEsm4() const final { return true; } + + inline void setEncoder(ToUTF8::Utf8Encoder* encoder) final { mEncoder = encoder; }; + + const std::vector& getGameFiles() const final { return mHeader.mMaster; } + + inline int getRecordCount() const final { return mHeader.mData.records; } + inline const std::string getAuthor() const final { return mHeader.mAuthor; } + inline int getFormat() const final { return 0; }; // prob. not relevant for ESM4 + inline const std::string getDesc() const final { return mHeader.mDesc; } + + inline std::string getFileName() const final { return mCtx.filename; }; // not used + + inline bool hasMoreRecs() const final { return (mFileSize - mCtx.fileRead) > 0; } + + // Methods added for updating loading progress bars + inline std::size_t getFileSize() const { return mFileSize; } + inline std::size_t getFileOffset() const { return mStream->tellg(); } + + // Methods added for saving/restoring context + ReaderContext getContext(); // WARN: must be called immediately after reading the record header + + bool restoreContext(const ReaderContext& ctx); // returns the result of re-reading the header + + template + inline void get(T& t) { mStream->read((char*)&t, sizeof(T)); } + + template + bool getExact(T& t) { + mStream->read((char*)&t, sizeof(T)); + return mStream->gcount() == sizeof(T); // FIXME: try/catch block needed? + } + + // for arrays + inline bool get(void* p, std::size_t size) { + mStream->read((char*)p, size); + return mStream->gcount() == (std::streamsize)size; // FIXME: try/catch block needed? + } + + // NOTE: must be called before calling getRecordHeader() + void setRecHeaderSize(const std::size_t size); + + inline unsigned int esmVersion() const { return mHeader.mData.version.ui; } + inline unsigned int numRecords() const { return mHeader.mData.records; } + + void buildLStringIndex(); + void getLocalizedString(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 mCtx.recordHeader; } + + 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(std::uint32_t index) final { mCtx.modIndex = (index << 24) & 0xff000000; } + void updateModIndices(const std::vector& files); + + // Maybe should throw an exception if called when not valid? + const CellGrid& currCellGrid() const; + + inline bool hasCellGrid() const { return mCtx.cellGridValid; } + + // 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) { + mCtx.cellGridValid = true; + mCtx.currCellGrid = 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() { mCtx.cellGridValid = 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(bool dump = false); + + // Skip the data part of a record + // Note: assumes the header was read correctly (partial skip is allowed) + void skipRecordData(); + + // Skip the remaining part of the group + // Note: assumes the header was read correctly and group was pushed onto the stack + void skipGroupData(); + + // Skip the group without pushing onto the stack + // Note: assumes the header was read correctly and group was not pushed onto the stack + // (expected to be used during development only while some groups are not yet supported) + 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. increase) the bytes read after SUB_XXXX + inline void updateRecordRead(std::uint32_t subSize) { mCtx.recordRead += subSize; } + + inline const SubRecordHeader& subRecordHeader() const { return mCtx.subRecordHeader; } + + // 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 (!getExact(hdr) || (hdr.typeId != type) || (hdr.dataSize != sizeof(T))) + return false; + + return get(t); + } + + // ModIndex adjusted formId according to master file dependencies + void adjustFormId(FormId& id); + + bool getFormId(FormId& id); + + void adjustGRUPFormId(); + + // Note: uses the string size from the subrecord header rather than checking null termination + bool getZString(std::string& str) { + return getStringImpl(str, mCtx.subRecordHeader.dataSize, mStream, mEncoder, true); + } + bool getString(std::string& str) { + return getStringImpl(str, mCtx.subRecordHeader.dataSize, mStream, mEncoder); + } + + void enterGroup(); + void exitGroupCheck(); + + // for debugging only + size_t stackSize() const { return mCtx.groupStack.size(); } + + // Used for error handling + [[noreturn]] void fail(const std::string& msg); + }; +} + +#endif // ESM4_READER_H diff --git a/components/esm4/records.hpp b/components/esm4/records.hpp new file mode 100644 index 0000000000..d212091adb --- /dev/null +++ b/components/esm4/records.hpp @@ -0,0 +1,74 @@ +#ifndef ESM4_RECORDS_H +#define ESM4_RECORDS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ESM4_RECORDS_H diff --git a/components/esm4/reference.hpp b/components/esm4/reference.hpp new file mode 100644 index 0000000000..5ac94b8519 --- /dev/null +++ b/components/esm4/reference.hpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2020 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_REFERENCE_H +#define ESM4_REFERENCE_H + +#include +#include + +#include "formid.hpp" + +namespace ESM4 +{ +#pragma pack(push, 1) + struct Vector3 + { + float x; + float y; + float z; + }; + + // REFR, ACHR, ACRE + struct Placement + { + 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 + }; +#pragma pack(pop) + + struct LODReference + { + FormId baseObj; + Placement placement; + float scale; + }; +} + +#endif // ESM4_REFERENCE_H diff --git a/components/esm4/script.hpp b/components/esm4/script.hpp new file mode 100644 index 0000000000..8ba8c0025c --- /dev/null +++ b/components/esm4/script.hpp @@ -0,0 +1,384 @@ +/* + Copyright (C) 2020-2021 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. + + Also see https://tes5edit.github.io/fopdoc/ for FO3/FONV specific details. + +*/ +#ifndef ESM4_SCRIPT_H +#define ESM4_SCRIPT_H + +#include +#include +#include + +namespace ESM4 +{ + enum EmotionType + { + EMO_Neutral = 0, + EMO_Anger = 1, + EMO_Disgust = 2, + EMO_Fear = 3, + EMO_Sad = 4, + EMO_Happy = 5, + EMO_Surprise = 6, + EMO_Pained = 7 // FO3/FONV + }; + + enum ConditionTypeAndFlag + { + // flag + CTF_Combine = 0x01, + CTF_RunOnTarget = 0x02, + CTF_UseGlobal = 0x04, + // condition + CTF_EqualTo = 0x00, + CTF_NotEqualTo = 0x20, + CTF_GreaterThan = 0x40, + CTF_GrThOrEqTo = 0x60, + CTF_LessThan = 0x80, + CTF_LeThOrEqTo = 0xA0 + }; + + enum FunctionIndices + { + FUN_GetDistance = 1, + FUN_GetLocked = 5, + FUN_GetPos = 6, + FUN_GetAngle = 8, + FUN_GetStartingPos = 10, + FUN_GetStartingAngle = 11, + FUN_GetSecondsPassed = 12, + FUN_GetActorValue = 14, + FUN_GetCurrentTime = 18, + FUN_GetScale = 24, + FUN_IsMoving = 25, + FUN_IsTurning = 26, + FUN_GetLineOfSight = 27, + FUN_GetIsInSameCell = 32, + FUN_GetDisabled = 35, + FUN_GetMenuMode = 36, + FUN_GetDisease = 39, + FUN_GetVampire = 40, + FUN_GetClothingValue = 41, + FUN_SameFaction = 42, + FUN_SameRace = 43, + FUN_SameSex = 44, + FUN_GetDetected = 45, + FUN_GetDead = 46, + FUN_GetItemCount = 47, + FUN_GetGold = 48, + FUN_GetSleeping = 49, + FUN_GetTalkedToPC = 50, + FUN_GetScriptVariable = 53, + FUN_GetQuestRunning = 56, + FUN_GetStage = 58, + FUN_GetStageDone = 59, + FUN_GetFactionRankDifference = 60, + FUN_GetAlarmed = 61, + FUN_IsRaining = 62, + FUN_GetAttacked = 63, + FUN_GetIsCreature = 64, + FUN_GetLockLevel = 65, + FUN_GetShouldAttack = 66, + FUN_GetInCell = 67, + FUN_GetIsClass = 68, + FUN_GetIsRace = 69, + FUN_GetIsSex = 70, + FUN_GetInFaction = 71, + FUN_GetIsID = 72, + FUN_GetFactionRank = 73, + FUN_GetGlobalValue = 74, + FUN_IsSnowing = 75, + FUN_GetDisposition = 76, + FUN_GetRandomPercent = 77, + FUN_GetQuestVariable = 79, + FUN_GetLevel = 80, + FUN_GetArmorRating = 81, + FUN_GetDeadCount = 84, + FUN_GetIsAlerted = 91, + FUN_GetPlayerControlsDisabled = 98, + FUN_GetHeadingAngle = 99, + FUN_IsWeaponOut = 101, + FUN_IsTorchOut = 102, + FUN_IsShieldOut = 103, + FUN_IsFacingUp = 106, + FUN_GetKnockedState = 107, + FUN_GetWeaponAnimType = 108, + FUN_IsWeaponSkillType = 109, + FUN_GetCurrentAIPackage = 110, + FUN_IsWaiting = 111, + FUN_IsIdlePlaying = 112, + FUN_GetMinorCrimeCount = 116, + FUN_GetMajorCrimeCount = 117, + FUN_GetActorAggroRadiusViolated = 118, + FUN_GetCrime = 122, + FUN_IsGreetingPlayer = 123, + FUN_IsGuard = 125, + FUN_HasBeenEaten = 127, + FUN_GetFatiguePercentage = 128, + FUN_GetPCIsClass = 129, + FUN_GetPCIsRace = 130, + FUN_GetPCIsSex = 131, + FUN_GetPCInFaction = 132, + FUN_SameFactionAsPC = 133, + FUN_SameRaceAsPC = 134, + FUN_SameSexAsPC = 135, + FUN_GetIsReference = 136, + FUN_IsTalking = 141, + FUN_GetWalkSpeed = 142, + FUN_GetCurrentAIProcedure = 143, + FUN_GetTrespassWarningLevel = 144, + FUN_IsTrespassing = 145, + FUN_IsInMyOwnedCell = 146, + FUN_GetWindSpeed = 147, + FUN_GetCurrentWeatherPercent = 148, + FUN_GetIsCurrentWeather = 149, + FUN_IsContinuingPackagePCNear = 150, + FUN_CanHaveFlames = 153, + FUN_HasFlames = 154, + FUN_GetOpenState = 157, + FUN_GetSitting = 159, + FUN_GetFurnitureMarkerID = 160, + FUN_GetIsCurrentPackage = 161, + FUN_IsCurrentFurnitureRef = 162, + FUN_IsCurrentFurnitureObj = 163, + FUN_GetDayofWeek = 170, + FUN_GetTalkedToPCParam = 172, + FUN_IsPCSleeping = 175, + FUN_IsPCAMurderer = 176, + FUN_GetDetectionLevel = 180, + FUN_GetEquipped = 182, + FUN_IsSwimming = 185, + FUN_GetAmountSoldStolen = 190, + FUN_GetIgnoreCrime = 192, + FUN_GetPCExpelled = 193, + FUN_GetPCFactionMurder = 195, + FUN_GetPCEnemyofFaction = 197, + FUN_GetPCFactionAttack = 199, + FUN_GetDestroyed = 203, + FUN_HasMagicEffect = 214, + FUN_GetDefaultOpen = 215, + FUN_GetAnimAction = 219, + FUN_IsSpellTarget = 223, + FUN_GetVATSMode = 224, + FUN_GetPersuasionNumber = 225, + FUN_GetSandman = 226, + FUN_GetCannibal = 227, + FUN_GetIsClassDefault = 228, + FUN_GetClassDefaultMatch = 229, + FUN_GetInCellParam = 230, + FUN_GetVatsTargetHeight = 235, + FUN_GetIsGhost = 237, + FUN_GetUnconscious = 242, + FUN_GetRestrained = 244, + FUN_GetIsUsedItem = 246, + FUN_GetIsUsedItemType = 247, + FUN_GetIsPlayableRace = 254, + FUN_GetOffersServicesNow = 255, + FUN_GetUsedItemLevel = 258, + FUN_GetUsedItemActivate = 259, + FUN_GetBarterGold = 264, + FUN_IsTimePassing = 265, + FUN_IsPleasant = 266, + FUN_IsCloudy = 267, + FUN_GetArmorRatingUpperBody = 274, + FUN_GetBaseActorValue = 277, + FUN_IsOwner = 278, + FUN_IsCellOwner = 280, + FUN_IsHorseStolen = 282, + FUN_IsLeftUp = 285, + FUN_IsSneaking = 286, + FUN_IsRunning = 287, + FUN_GetFriendHit = 288, + FUN_IsInCombat = 289, + FUN_IsInInterior = 300, + FUN_IsWaterObject = 304, + FUN_IsActorUsingATorch = 306, + FUN_IsXBox = 309, + FUN_GetInWorldspace = 310, + FUN_GetPCMiscStat = 312, + FUN_IsActorEvil = 313, + FUN_IsActorAVictim = 314, + FUN_GetTotalPersuasionNumber = 315, + FUN_GetIdleDoneOnce = 318, + FUN_GetNoRumors = 320, + FUN_WhichServiceMenu = 323, + FUN_IsRidingHorse = 327, + FUN_IsInDangerousWater = 332, + FUN_GetIgnoreFriendlyHits = 338, + FUN_IsPlayersLastRiddenHorse = 339, + FUN_IsActor = 353, + FUN_IsEssential = 354, + FUN_IsPlayerMovingIntoNewSpace = 358, + FUN_GetTimeDead = 361, + FUN_GetPlayerHasLastRiddenHorse = 362, + FUN_IsChild = 365, + FUN_GetLastPlayerAction = 367, + FUN_IsPlayerActionActive = 368, + FUN_IsTalkingActivatorActor = 370, + FUN_IsInList = 372, + FUN_GetHasNote = 382, + FUN_GetHitLocation = 391, + FUN_IsPC1stPerson = 392, + FUN_GetCauseofDeath = 397, + FUN_IsLimbGone = 398, + FUN_IsWeaponInList = 399, + FUN_HasFriendDisposition = 403, + FUN_GetVATSValue = 408, + FUN_IsKiller = 409, + FUN_IsKillerObject = 410, + FUN_GetFactionCombatReaction = 411, + FUN_Exists = 415, + FUN_GetGroupMemberCount = 416, + FUN_GetGroupTargetCount = 417, + FUN_GetObjectiveCompleted = 420, + FUN_GetObjectiveDisplayed = 421, + FUN_GetIsVoiceType = 427, + FUN_GetPlantedExplosive = 428, + FUN_IsActorTalkingThroughActivator = 430, + FUN_GetHealthPercentage = 431, + FUN_GetIsObjectType = 433, + FUN_GetDialogueEmotion = 435, + FUN_GetDialogueEmotionValue = 436, + FUN_GetIsCreatureType = 438, + FUN_GetInZone = 446, + FUN_HasPerk = 449, + FUN_GetFactionRelation = 450, + FUN_IsLastIdlePlayed = 451, + FUN_GetPlayerTeammate = 454, + FUN_GetPlayerTeammateCount = 455, + FUN_GetActorCrimePlayerEnemy = 459, + FUN_GetActorFactionPlayerEnemy = 460, + FUN_IsPlayerTagSkill = 462, + FUN_IsPlayerGrabbedRef = 464, + FUN_GetDestructionStage = 471, + FUN_GetIsAlignment = 474, + FUN_GetThreatRatio = 478, + FUN_GetIsUsedItemEquipType = 480, + FUN_GetConcussed = 489, + FUN_GetMapMarkerVisible = 492, + FUN_GetPermanentActorValue = 495, + FUN_GetKillingBlowLimb = 496, + FUN_GetWeaponHealthPerc = 500, + FUN_GetRadiationLevel = 503, + FUN_GetLastHitCritical = 510, + FUN_IsCombatTarget = 515, + FUN_GetVATSRightAreaFree = 518, + FUN_GetVATSLeftAreaFree = 519, + FUN_GetVATSBackAreaFree = 520, + FUN_GetVATSFrontAreaFree = 521, + FUN_GetIsLockBroken = 522, + FUN_IsPS3 = 523, + FUN_IsWin32 = 524, + FUN_GetVATSRightTargetVisible = 525, + FUN_GetVATSLeftTargetVisible = 526, + FUN_GetVATSBackTargetVisible = 527, + FUN_GetVATSFrontTargetVisible = 528, + FUN_IsInCriticalStage = 531, + FUN_GetXPForNextLevel = 533, + FUN_GetQuestCompleted = 546, + FUN_IsGoreDisabled = 550, + FUN_GetSpellUsageNum = 555, + FUN_GetActorsInHigh = 557, + FUN_HasLoaded3D = 558, + FUN_GetReputation = 573, + FUN_GetReputationPct = 574, + FUN_GetReputationThreshold = 575, + FUN_IsHardcore = 586, + FUN_GetForceHitReaction = 601, + FUN_ChallengeLocked = 607, + FUN_GetCasinoWinningStage = 610, + FUN_PlayerInRegion = 612, + FUN_GetChallengeCompleted = 614, + FUN_IsAlwaysHardcore = 619 + }; + +#pragma pack(push, 1) + struct TargetResponseData + { + std::uint32_t emoType; // EmotionType + std::int32_t emoValue; + std::uint32_t unknown1; + std::uint32_t responseNo; // 1 byte + padding + // below FO3/FONV + FormId sound; // when 20 bytes usually 0 but there are exceptions (FO3 INFO FormId = 0x0002241f) + std::uint32_t flags; // 1 byte + padding (0x01 = use emotion anim) + }; + + struct TargetCondition + { + std::uint32_t condition; // ConditionTypeAndFlag + padding + float comparison; // WARN: can be GLOB FormId if flag set + std::uint32_t functionIndex; + std::uint32_t param1; // FIXME: if formid needs modindex adjustment or not? + std::uint32_t param2; + std::uint32_t runOn; // 0 subject, 1 target, 2 reference, 3 combat target, 4 linked reference + // below FO3/FONV/TES5 + FormId reference; + }; + + struct ScriptHeader + { + std::uint32_t unused; + std::uint32_t refCount; + std::uint32_t compiledSize; + std::uint32_t variableCount; + std::uint16_t type; // 0 object, 1 quest, 0x100 effect + std::uint16_t flag; // 0x01 enabled + }; +#pragma pack(pop) + + struct ScriptLocalVariableData + { + // SLSD + std::uint32_t index; + std::uint32_t unknown1; + std::uint32_t unknown2; + std::uint32_t unknown3; + std::uint32_t type; + std::uint32_t unknown4; + // SCVR + std::string variableName; + + void clear() { + index = 0; + type = 0; + variableName.clear(); + } + }; + + struct ScriptDefinition + { + ScriptHeader scriptHeader; + // SDCA compiled source + std::string scriptSource; + std::vector localVarData; + std::vector localRefVarIndex; + FormId globReference; + }; +} + +#endif // ESM4_SCRIPT_H diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index f7dc33fcbf..04edfda09d 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -84,6 +84,15 @@ std::string Utf8Encoder::getUtf8(const char* input, size_t size) // is also ok.) assert(input[size] == 0); + std::string inputString(input, size); + std::string output; + toUtf8(inputString, output, size); + + return output; +} + +void Utf8Encoder::toUtf8(std::string& input, std::string& output, size_t size) +{ // Note: The rest of this function is designed for single-character // input encodings only. It also assumes that the input encoding // shares its first 128 values (0-127) with ASCII. There are no plans @@ -93,29 +102,36 @@ std::string Utf8Encoder::getUtf8(const char* input, size_t size) // Compute output length, and check for pure ascii input at the same // time. bool ascii; - size_t outlen = getLength(input, ascii); + size_t outlen = getLength(input.c_str(), ascii); // If we're pure ascii, then don't bother converting anything. if(ascii) - return std::string(input, outlen); + { + std::swap(input, output); + return; + } // Make sure the output is large enough - resize(outlen); - char *out = &mOutput[0]; + if (output.size() <= outlen) + // Add some extra padding to reduce the chance of having to resize + // again later. + output.resize(3*outlen); + + // And make sure the string is zero terminated + output[outlen] = 0; + char *in = &input[0]; + char *out = &output[0]; // Translate - while (*input) - copyFromArray(*(input++), out); + while (*in) + copyFromArray(*(in++), out); // Make sure that we wrote the correct number of bytes - assert((out-&mOutput[0]) == (int)outlen); + assert((out-&output[0]) == (int)outlen); // And make extra sure the output is null terminated - assert(mOutput.size() > outlen); - assert(mOutput[outlen] == 0); - - // Return a string - return std::string(&mOutput[0], outlen); + assert(output.size() > outlen); + assert(output[outlen] == 0); } std::string Utf8Encoder::getLegacyEnc(const char *input, size_t size) diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index d8c9f09d5d..23dc09c06e 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -34,6 +34,9 @@ namespace ToUTF8 return getUtf8(str.c_str(), str.size()); } + // Convert input to UTF8 to the given output string + void toUtf8(std::string& input, std::string& output, size_t size); + std::string getLegacyEnc(const char *input, size_t size); inline std::string getLegacyEnc(const std::string &str) { diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index ef0f432075..37068fd70d 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -53,13 +53,14 @@ namespace Translation if (!line.empty()) { - line = mEncoder->getUtf8(line); + std::string utf8Line; + mEncoder->toUtf8(line, utf8Line, line.size()); - size_t tab_pos = line.find('\t'); - if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1) + size_t tab_pos = utf8Line.find('\t'); + if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < utf8Line.size() - 1) { - std::string key = line.substr(0, tab_pos); - std::string value = line.substr(tab_pos + 1); + std::string key = utf8Line.substr(0, tab_pos); + std::string value = utf8Line.substr(tab_pos + 1); if (!key.empty() && !value.empty()) container.insert(std::make_pair(key, value));