1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-26 09:35:28 +00:00
OpenMW/apps/esmtool/tes4.cpp
florent.teppe 077cf97bc4 The esm4 reader logic is mutualised
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
2023-01-01 19:22:27 +01:00

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 = [&params](ESM4::Reader& reader) { return readRecord(params, reader); };
auto visistorGroup = [&params](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;
}
}