mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-26 09:35:28 +00:00
077cf97bc4
to avoid copy pasting code, readerutils gives functions that take visitors as params to decide how a record must be handled Check encoder exists, and get value of stateless encoder. fixes code formatting conventions Fixed output of record with RefId also fixed readTypedRecord and readRecord to have the proper return types Check if the type has a sRecordId
617 lines
22 KiB
C++
617 lines
22 KiB
C++
#include "tes4.hpp"
|
|
#include "arguments.hpp"
|
|
#include "labels.hpp"
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <type_traits>
|
|
|
|
#include <components/esm/esmcommon.hpp>
|
|
#include <components/esm4/reader.hpp>
|
|
#include <components/esm4/readerutils.hpp>
|
|
#include <components/esm4/records.hpp>
|
|
#include <components/to_utf8/to_utf8.hpp>
|
|
|
|
namespace EsmTool
|
|
{
|
|
namespace
|
|
{
|
|
struct Params
|
|
{
|
|
const bool mQuite;
|
|
|
|
explicit Params(const Arguments& info)
|
|
: mQuite(info.quiet_given || info.mode == "clone")
|
|
{
|
|
}
|
|
};
|
|
|
|
std::string toString(ESM4::GroupType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case ESM4::Grp_RecordType:
|
|
return "RecordType";
|
|
case ESM4::Grp_WorldChild:
|
|
return "WorldChild";
|
|
case ESM4::Grp_InteriorCell:
|
|
return "InteriorCell";
|
|
case ESM4::Grp_InteriorSubCell:
|
|
return "InteriorSubCell";
|
|
case ESM4::Grp_ExteriorCell:
|
|
return "ExteriorCell";
|
|
case ESM4::Grp_ExteriorSubCell:
|
|
return "ExteriorSubCell";
|
|
case ESM4::Grp_CellChild:
|
|
return "CellChild";
|
|
case ESM4::Grp_TopicChild:
|
|
return "TopicChild";
|
|
case ESM4::Grp_CellPersistentChild:
|
|
return "CellPersistentChild";
|
|
case ESM4::Grp_CellTemporaryChild:
|
|
return "CellTemporaryChild";
|
|
case ESM4::Grp_CellVisibleDistChild:
|
|
return "CellVisibleDistChild";
|
|
}
|
|
|
|
return "Unknown (" + std::to_string(type) + ")";
|
|
}
|
|
|
|
template <class T, class = std::void_t<>>
|
|
struct HasFormId : std::false_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
struct HasFormId<T, std::void_t<decltype(T::mFormId)>> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool hasFormId = HasFormId<T>::value;
|
|
|
|
template <class T, class = std::void_t<>>
|
|
struct HasRefId : std::false_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
struct HasRefId<T, std::void_t<decltype(T::mId)>> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool hasRefId = HasRefId<T>::value;
|
|
|
|
template <class T, class = std::void_t<>>
|
|
struct HasFlags : std::false_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
struct HasFlags<T, std::void_t<decltype(T::mFlags)>> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool hasFlags = HasFlags<T>::value;
|
|
|
|
template <class T, class = std::void_t<>>
|
|
struct HasEditorId : std::false_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
struct HasEditorId<T, std::void_t<decltype(T::mEditorId)>> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool hasEditorId = HasEditorId<T>::value;
|
|
|
|
template <class T, class = std::void_t<>>
|
|
struct HasModel : std::false_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
struct HasModel<T, std::void_t<decltype(T::mModel)>> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool hasModel = HasModel<T>::value;
|
|
|
|
template <class T, class = std::void_t<>>
|
|
struct HasNif : std::false_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
struct HasNif<T, std::void_t<decltype(T::mNif)>> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool hasNif = HasNif<T>::value;
|
|
|
|
template <class T, class = std::void_t<>>
|
|
struct HasKf : std::false_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
struct HasKf<T, std::void_t<decltype(T::mKf)>> : std::true_type
|
|
{
|
|
};
|
|
|
|
template <class T>
|
|
constexpr bool hasKf = HasKf<T>::value;
|
|
|
|
template <class T>
|
|
struct WriteArray
|
|
{
|
|
std::string_view mPrefix;
|
|
const T& mValue;
|
|
|
|
explicit WriteArray(std::string_view prefix, const T& value)
|
|
: mPrefix(prefix)
|
|
, mValue(value)
|
|
{
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
std::ostream& operator<<(std::ostream& stream, const WriteArray<T>& write)
|
|
{
|
|
for (const auto& value : write.mValue)
|
|
stream << write.mPrefix << value;
|
|
return stream;
|
|
}
|
|
|
|
template <class T>
|
|
void readTypedRecord(const Params& params, ESM4::Reader& reader)
|
|
{
|
|
reader.getRecordData();
|
|
|
|
T value;
|
|
value.load(reader);
|
|
|
|
if (params.mQuite)
|
|
return;
|
|
|
|
std::cout << "\n Record: " << ESM::NAME(reader.hdr().record.typeId).toStringView();
|
|
if constexpr (hasFormId<T>)
|
|
std::cout << "\n FormId: " << value.mFormId;
|
|
if constexpr (hasRefId<T>)
|
|
std::cout << "\n RefId: " << value.mId;
|
|
if constexpr (hasFlags<T>)
|
|
std::cout << "\n Record flags: " << recordFlags(value.mFlags);
|
|
if constexpr (hasEditorId<T>)
|
|
std::cout << "\n EditorId: " << value.mEditorId;
|
|
if constexpr (hasModel<T>)
|
|
std::cout << "\n Model: " << value.mModel;
|
|
if constexpr (hasNif<T>)
|
|
std::cout << "\n Nif:" << WriteArray("\n - ", value.mNif);
|
|
if constexpr (hasKf<T>)
|
|
std::cout << "\n Kf:" << WriteArray("\n - ", value.mKf);
|
|
std::cout << '\n';
|
|
return;
|
|
}
|
|
|
|
bool readRecord(const Params& params, ESM4::Reader& reader)
|
|
{
|
|
switch (static_cast<ESM4::RecordTypes>(reader.hdr().record.typeId))
|
|
{
|
|
case ESM4::REC_AACT:
|
|
break;
|
|
case ESM4::REC_ACHR:
|
|
readTypedRecord<ESM4::ActorCharacter>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ACRE:
|
|
readTypedRecord<ESM4::ActorCreature>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ACTI:
|
|
readTypedRecord<ESM4::Activator>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ADDN:
|
|
break;
|
|
case ESM4::REC_ALCH:
|
|
readTypedRecord<ESM4::Potion>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ALOC:
|
|
readTypedRecord<ESM4::MediaLocationController>(params, reader);
|
|
return true;
|
|
case ESM4::REC_AMMO:
|
|
readTypedRecord<ESM4::Ammunition>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ANIO:
|
|
readTypedRecord<ESM4::AnimObject>(params, reader);
|
|
return true;
|
|
case ESM4::REC_APPA:
|
|
readTypedRecord<ESM4::Apparatus>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ARMA:
|
|
readTypedRecord<ESM4::ArmorAddon>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ARMO:
|
|
readTypedRecord<ESM4::Armor>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ARTO:
|
|
break;
|
|
case ESM4::REC_ASPC:
|
|
readTypedRecord<ESM4::AcousticSpace>(params, reader);
|
|
return true;
|
|
case ESM4::REC_ASTP:
|
|
break;
|
|
case ESM4::REC_AVIF:
|
|
break;
|
|
case ESM4::REC_BOOK:
|
|
readTypedRecord<ESM4::Book>(params, reader);
|
|
return true;
|
|
case ESM4::REC_BPTD:
|
|
readTypedRecord<ESM4::BodyPartData>(params, reader);
|
|
return true;
|
|
case ESM4::REC_CAMS:
|
|
break;
|
|
case ESM4::REC_CCRD:
|
|
break;
|
|
case ESM4::REC_CELL:
|
|
readTypedRecord<ESM4::Cell>(params, reader);
|
|
return true;
|
|
case ESM4::REC_CLAS:
|
|
readTypedRecord<ESM4::Class>(params, reader);
|
|
return true;
|
|
case ESM4::REC_CLFM:
|
|
readTypedRecord<ESM4::Colour>(params, reader);
|
|
return true;
|
|
case ESM4::REC_CLMT:
|
|
break;
|
|
case ESM4::REC_CLOT:
|
|
readTypedRecord<ESM4::Clothing>(params, reader);
|
|
return true;
|
|
case ESM4::REC_CMNY:
|
|
break;
|
|
case ESM4::REC_COBJ:
|
|
break;
|
|
case ESM4::REC_COLL:
|
|
break;
|
|
case ESM4::REC_CONT:
|
|
readTypedRecord<ESM4::Container>(params, reader);
|
|
return true;
|
|
case ESM4::REC_CPTH:
|
|
break;
|
|
case ESM4::REC_CREA:
|
|
readTypedRecord<ESM4::Creature>(params, reader);
|
|
return true;
|
|
case ESM4::REC_CSTY:
|
|
break;
|
|
case ESM4::REC_DEBR:
|
|
break;
|
|
case ESM4::REC_DIAL:
|
|
readTypedRecord<ESM4::Dialogue>(params, reader);
|
|
return true;
|
|
case ESM4::REC_DLBR:
|
|
break;
|
|
case ESM4::REC_DLVW:
|
|
break;
|
|
case ESM4::REC_DOBJ:
|
|
readTypedRecord<ESM4::DefaultObj>(params, reader);
|
|
return true;
|
|
case ESM4::REC_DOOR:
|
|
readTypedRecord<ESM4::Door>(params, reader);
|
|
return true;
|
|
case ESM4::REC_DUAL:
|
|
break;
|
|
case ESM4::REC_ECZN:
|
|
break;
|
|
case ESM4::REC_EFSH:
|
|
break;
|
|
case ESM4::REC_ENCH:
|
|
break;
|
|
case ESM4::REC_EQUP:
|
|
break;
|
|
case ESM4::REC_EXPL:
|
|
break;
|
|
case ESM4::REC_EYES:
|
|
readTypedRecord<ESM4::Eyes>(params, reader);
|
|
return true;
|
|
case ESM4::REC_FACT:
|
|
break;
|
|
case ESM4::REC_FLOR:
|
|
readTypedRecord<ESM4::Flora>(params, reader);
|
|
return true;
|
|
case ESM4::REC_FLST:
|
|
readTypedRecord<ESM4::FormIdList>(params, reader);
|
|
return true;
|
|
case ESM4::REC_FSTP:
|
|
break;
|
|
case ESM4::REC_FSTS:
|
|
break;
|
|
case ESM4::REC_FURN:
|
|
readTypedRecord<ESM4::Furniture>(params, reader);
|
|
return true;
|
|
case ESM4::REC_GLOB:
|
|
readTypedRecord<ESM4::GlobalVariable>(params, reader);
|
|
return true;
|
|
case ESM4::REC_GMST:
|
|
break;
|
|
case ESM4::REC_GRAS:
|
|
readTypedRecord<ESM4::Grass>(params, reader);
|
|
return true;
|
|
case ESM4::REC_GRUP:
|
|
break;
|
|
case ESM4::REC_HAIR:
|
|
readTypedRecord<ESM4::Hair>(params, reader);
|
|
return true;
|
|
case ESM4::REC_HAZD:
|
|
break;
|
|
case ESM4::REC_HDPT:
|
|
readTypedRecord<ESM4::HeadPart>(params, reader);
|
|
return true;
|
|
case ESM4::REC_IDLE:
|
|
// FIXME: ESM4::IdleAnimation::load does not work with Oblivion.esm
|
|
// readTypedRecord<ESM4::IdleAnimation>(params, reader);
|
|
return true;
|
|
break;
|
|
case ESM4::REC_IDLM:
|
|
readTypedRecord<ESM4::IdleMarker>(params, reader);
|
|
return true;
|
|
case ESM4::REC_IMAD:
|
|
break;
|
|
case ESM4::REC_IMGS:
|
|
break;
|
|
case ESM4::REC_IMOD:
|
|
readTypedRecord<ESM4::ItemMod>(params, reader);
|
|
return true;
|
|
case ESM4::REC_INFO:
|
|
readTypedRecord<ESM4::DialogInfo>(params, reader);
|
|
return true;
|
|
case ESM4::REC_INGR:
|
|
readTypedRecord<ESM4::Ingredient>(params, reader);
|
|
return true;
|
|
case ESM4::REC_IPCT:
|
|
break;
|
|
case ESM4::REC_IPDS:
|
|
break;
|
|
case ESM4::REC_KEYM:
|
|
readTypedRecord<ESM4::Key>(params, reader);
|
|
return true;
|
|
case ESM4::REC_KYWD:
|
|
break;
|
|
case ESM4::REC_LAND:
|
|
readTypedRecord<ESM4::Land>(params, reader);
|
|
return true;
|
|
case ESM4::REC_LCRT:
|
|
break;
|
|
case ESM4::REC_LCTN:
|
|
break;
|
|
case ESM4::REC_LGTM:
|
|
readTypedRecord<ESM4::LightingTemplate>(params, reader);
|
|
return true;
|
|
case ESM4::REC_LIGH:
|
|
readTypedRecord<ESM4::Light>(params, reader);
|
|
return true;
|
|
case ESM4::REC_LSCR:
|
|
break;
|
|
case ESM4::REC_LTEX:
|
|
readTypedRecord<ESM4::LandTexture>(params, reader);
|
|
return true;
|
|
case ESM4::REC_LVLC:
|
|
readTypedRecord<ESM4::LevelledCreature>(params, reader);
|
|
return true;
|
|
case ESM4::REC_LVLI:
|
|
readTypedRecord<ESM4::LevelledItem>(params, reader);
|
|
return true;
|
|
case ESM4::REC_LVLN:
|
|
readTypedRecord<ESM4::LevelledNpc>(params, reader);
|
|
return true;
|
|
case ESM4::REC_LVSP:
|
|
break;
|
|
case ESM4::REC_MATO:
|
|
readTypedRecord<ESM4::Material>(params, reader);
|
|
return true;
|
|
case ESM4::REC_MATT:
|
|
break;
|
|
case ESM4::REC_MESG:
|
|
break;
|
|
case ESM4::REC_MGEF:
|
|
break;
|
|
case ESM4::REC_MISC:
|
|
readTypedRecord<ESM4::MiscItem>(params, reader);
|
|
return true;
|
|
case ESM4::REC_MOVT:
|
|
break;
|
|
case ESM4::REC_MSET:
|
|
readTypedRecord<ESM4::MediaSet>(params, reader);
|
|
return true;
|
|
case ESM4::REC_MSTT:
|
|
readTypedRecord<ESM4::MovableStatic>(params, reader);
|
|
return true;
|
|
case ESM4::REC_MUSC:
|
|
readTypedRecord<ESM4::Music>(params, reader);
|
|
return true;
|
|
case ESM4::REC_MUST:
|
|
break;
|
|
case ESM4::REC_NAVI:
|
|
readTypedRecord<ESM4::Navigation>(params, reader);
|
|
return true;
|
|
case ESM4::REC_NAVM:
|
|
readTypedRecord<ESM4::NavMesh>(params, reader);
|
|
return true;
|
|
case ESM4::REC_NOTE:
|
|
readTypedRecord<ESM4::Note>(params, reader);
|
|
return true;
|
|
case ESM4::REC_NPC_:
|
|
readTypedRecord<ESM4::Npc>(params, reader);
|
|
return true;
|
|
case ESM4::REC_OTFT:
|
|
readTypedRecord<ESM4::Outfit>(params, reader);
|
|
return true;
|
|
case ESM4::REC_PACK:
|
|
readTypedRecord<ESM4::AIPackage>(params, reader);
|
|
return true;
|
|
case ESM4::REC_PERK:
|
|
break;
|
|
case ESM4::REC_PGRD:
|
|
readTypedRecord<ESM4::Pathgrid>(params, reader);
|
|
return true;
|
|
case ESM4::REC_PGRE:
|
|
readTypedRecord<ESM4::PlacedGrenade>(params, reader);
|
|
return true;
|
|
case ESM4::REC_PHZD:
|
|
break;
|
|
case ESM4::REC_PROJ:
|
|
break;
|
|
case ESM4::REC_PWAT:
|
|
readTypedRecord<ESM4::PlaceableWater>(params, reader);
|
|
return true;
|
|
case ESM4::REC_QUST:
|
|
readTypedRecord<ESM4::Quest>(params, reader);
|
|
return true;
|
|
case ESM4::REC_RACE:
|
|
readTypedRecord<ESM4::Race>(params, reader);
|
|
return true;
|
|
case ESM4::REC_REFR:
|
|
readTypedRecord<ESM4::Reference>(params, reader);
|
|
return true;
|
|
case ESM4::REC_REGN:
|
|
readTypedRecord<ESM4::Region>(params, reader);
|
|
return true;
|
|
case ESM4::REC_RELA:
|
|
break;
|
|
case ESM4::REC_REVB:
|
|
break;
|
|
case ESM4::REC_RFCT:
|
|
break;
|
|
case ESM4::REC_ROAD:
|
|
readTypedRecord<ESM4::Road>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SBSP:
|
|
readTypedRecord<ESM4::SubSpace>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SCEN:
|
|
break;
|
|
case ESM4::REC_SCOL:
|
|
readTypedRecord<ESM4::StaticCollection>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SCPT:
|
|
readTypedRecord<ESM4::Script>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SCRL:
|
|
readTypedRecord<ESM4::Scroll>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SGST:
|
|
readTypedRecord<ESM4::SigilStone>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SHOU:
|
|
break;
|
|
case ESM4::REC_SLGM:
|
|
readTypedRecord<ESM4::SoulGem>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SMBN:
|
|
break;
|
|
case ESM4::REC_SMEN:
|
|
break;
|
|
case ESM4::REC_SMQN:
|
|
break;
|
|
case ESM4::REC_SNCT:
|
|
break;
|
|
case ESM4::REC_SNDR:
|
|
readTypedRecord<ESM4::SoundReference>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SOPM:
|
|
break;
|
|
case ESM4::REC_SOUN:
|
|
readTypedRecord<ESM4::Sound>(params, reader);
|
|
return true;
|
|
case ESM4::REC_SPEL:
|
|
break;
|
|
case ESM4::REC_SPGD:
|
|
break;
|
|
case ESM4::REC_STAT:
|
|
readTypedRecord<ESM4::Static>(params, reader);
|
|
return true;
|
|
case ESM4::REC_TACT:
|
|
readTypedRecord<ESM4::TalkingActivator>(params, reader);
|
|
return true;
|
|
case ESM4::REC_TERM:
|
|
readTypedRecord<ESM4::Terminal>(params, reader);
|
|
return true;
|
|
case ESM4::REC_TES4:
|
|
readTypedRecord<ESM4::Header>(params, reader);
|
|
return true;
|
|
case ESM4::REC_TREE:
|
|
readTypedRecord<ESM4::Tree>(params, reader);
|
|
return true;
|
|
case ESM4::REC_TXST:
|
|
readTypedRecord<ESM4::TextureSet>(params, reader);
|
|
return true;
|
|
case ESM4::REC_VTYP:
|
|
break;
|
|
case ESM4::REC_WATR:
|
|
break;
|
|
case ESM4::REC_WEAP:
|
|
readTypedRecord<ESM4::Weapon>(params, reader);
|
|
return true;
|
|
case ESM4::REC_WOOP:
|
|
break;
|
|
case ESM4::REC_WRLD:
|
|
readTypedRecord<ESM4::World>(params, reader);
|
|
return true;
|
|
case ESM4::REC_WTHR:
|
|
break;
|
|
}
|
|
|
|
if (!params.mQuite)
|
|
std::cout << "\n Unsupported record: " << ESM::NAME(reader.hdr().record.typeId).toStringView() << '\n';
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
int loadTes4(const Arguments& info, std::unique_ptr<std::ifstream>&& stream)
|
|
{
|
|
std::cout << "Loading TES4 file: " << info.filename << '\n';
|
|
|
|
try
|
|
{
|
|
const ToUTF8::StatelessUtf8Encoder encoder(ToUTF8::calculateEncoding(info.encoding));
|
|
ESM4::Reader reader(std::move(stream), info.filename);
|
|
reader.setEncoder(&encoder);
|
|
const Params params(info);
|
|
|
|
if (!params.mQuite)
|
|
{
|
|
std::cout << "Author: " << reader.getAuthor() << '\n'
|
|
<< "Description: " << reader.getDesc() << '\n'
|
|
<< "File format version: " << reader.esmVersion() << '\n';
|
|
|
|
if (const std::vector<ESM::MasterData>& masterData = reader.getGameFiles(); !masterData.empty())
|
|
{
|
|
std::cout << "Masters:" << '\n';
|
|
for (const auto& master : masterData)
|
|
std::cout << " " << master.name << ", " << master.size << " bytes\n";
|
|
}
|
|
}
|
|
|
|
auto visitorRec = [¶ms](ESM4::Reader& reader) { return readRecord(params, reader); };
|
|
auto visistorGroup = [¶ms](ESM4::Reader& reader) {
|
|
if (params.mQuite)
|
|
return;
|
|
auto groupType = static_cast<ESM4::GroupType>(reader.hdr().group.type);
|
|
std::cout << "\nGroup: " << toString(groupType) << " "
|
|
<< ESM::NAME(reader.hdr().group.typeId).toStringView() << '\n';
|
|
};
|
|
ESM4::ReaderUtils::readAll(reader, visitorRec, visistorGroup);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
std::cout << "\nERROR:\n\n " << e.what() << std::endl;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|