#include "tes4.hpp" #include "arguments.hpp" #include "labels.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include 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 struct WriteArray { std::string_view mPrefix; const T& mValue; explicit WriteArray(std::string_view prefix, const T& value) : mPrefix(prefix) , mValue(value) { } }; template struct WriteData { const T& mValue; explicit WriteData(const T& value) : mValue(value) { } }; template std::ostream& operator<<(std::ostream& stream, const WriteArray& write) { for (const auto& value : write.mValue) stream << write.mPrefix << value; return stream; } template std::ostream& operator<<(std::ostream& stream, const WriteData& /*write*/) { return stream << " ?"; } std::ostream& operator<<(std::ostream& stream, const std::monostate&) { return stream << "[none]"; } std::ostream& operator<<(std::ostream& stream, const WriteData& write) { std::visit([&](const auto& v) { stream << v; }, write.mValue); return stream; } struct WriteCellFlags { std::uint16_t mValue; }; using CellFlagString = Debug::FlagString; constexpr std::array cellFlags{ CellFlagString{ ESM4::CELL_Interior, "Interior" }, CellFlagString{ ESM4::CELL_HasWater, "HasWater" }, CellFlagString{ ESM4::CELL_NoTravel, "NoTravel" }, CellFlagString{ ESM4::CELL_HideLand, "HideLand" }, CellFlagString{ ESM4::CELL_Public, "Public" }, CellFlagString{ ESM4::CELL_HandChgd, "HandChgd" }, CellFlagString{ ESM4::CELL_QuasiExt, "QuasiExt" }, CellFlagString{ ESM4::CELL_SkyLight, "SkyLight" }, }; std::ostream& operator<<(std::ostream& stream, const WriteCellFlags& write) { return Debug::writeFlags(stream, write.mValue, cellFlags); } template 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 (ESM::hasId) std::cout << "\n Id: " << value.mId; if constexpr (ESM4::hasFlags) std::cout << "\n Record flags: " << recordFlags(value.mFlags); if constexpr (ESM4::hasParent) std::cout << "\n Parent: " << value.mParent; if constexpr (ESM4::hasEditorId) std::cout << "\n EditorId: " << value.mEditorId; if constexpr (ESM4::hasFullName) std::cout << "\n FullName: " << value.mFullName; if constexpr (ESM4::hasCellFlags) std::cout << "\n CellFlags: " << WriteCellFlags{ value.mCellFlags }; if constexpr (ESM4::hasX) std::cout << "\n X: " << value.mX; if constexpr (ESM4::hasY) std::cout << "\n Y: " << value.mY; if constexpr (ESM::hasModel) std::cout << "\n Model: " << value.mModel; if constexpr (ESM4::hasNif) std::cout << "\n Nif:" << WriteArray("\n - ", value.mNif); if constexpr (ESM4::hasKf) std::cout << "\n Kf:" << WriteArray("\n - ", value.mKf); if constexpr (ESM4::hasType) std::cout << "\n Type: " << value.mType; if constexpr (ESM4::hasValue) std::cout << "\n Value: " << value.mValue; if constexpr (ESM4::hasData) std::cout << "\n Data: " << WriteData(value.mData); std::cout << '\n'; } bool readRecord(const Params& params, ESM4::Reader& reader) { switch (static_cast(reader.hdr().record.typeId)) { case ESM4::REC_AACT: break; case ESM4::REC_ACHR: readTypedRecord(params, reader); return true; case ESM4::REC_ACRE: readTypedRecord(params, reader); return true; case ESM4::REC_ACTI: readTypedRecord(params, reader); return true; case ESM4::REC_ADDN: break; case ESM4::REC_ALCH: readTypedRecord(params, reader); return true; case ESM4::REC_ALOC: readTypedRecord(params, reader); return true; case ESM4::REC_AMMO: readTypedRecord(params, reader); return true; case ESM4::REC_ANIO: readTypedRecord(params, reader); return true; case ESM4::REC_APPA: readTypedRecord(params, reader); return true; case ESM4::REC_ARMA: readTypedRecord(params, reader); return true; case ESM4::REC_ARMO: readTypedRecord(params, reader); return true; case ESM4::REC_ARTO: break; case ESM4::REC_ASPC: readTypedRecord(params, reader); return true; case ESM4::REC_ASTP: break; case ESM4::REC_AVIF: break; case ESM4::REC_BOOK: readTypedRecord(params, reader); return true; case ESM4::REC_BPTD: readTypedRecord(params, reader); return true; case ESM4::REC_CAMS: break; case ESM4::REC_CCRD: break; case ESM4::REC_CELL: readTypedRecord(params, reader); return true; case ESM4::REC_CLAS: readTypedRecord(params, reader); return true; case ESM4::REC_CLFM: readTypedRecord(params, reader); return true; case ESM4::REC_CLMT: break; case ESM4::REC_CLOT: readTypedRecord(params, reader); return true; case ESM4::REC_CMNY: break; case ESM4::REC_COBJ: break; case ESM4::REC_COLL: break; case ESM4::REC_CONT: readTypedRecord(params, reader); return true; case ESM4::REC_CPTH: break; case ESM4::REC_CREA: readTypedRecord(params, reader); return true; case ESM4::REC_CSTY: break; case ESM4::REC_DEBR: break; case ESM4::REC_DIAL: readTypedRecord(params, reader); return true; case ESM4::REC_DLBR: break; case ESM4::REC_DLVW: break; case ESM4::REC_DOBJ: readTypedRecord(params, reader); return true; case ESM4::REC_DOOR: readTypedRecord(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(params, reader); return true; case ESM4::REC_FACT: break; case ESM4::REC_FLOR: readTypedRecord(params, reader); return true; case ESM4::REC_FLST: readTypedRecord(params, reader); return true; case ESM4::REC_FSTP: break; case ESM4::REC_FSTS: break; case ESM4::REC_FURN: readTypedRecord(params, reader); return true; case ESM4::REC_GLOB: readTypedRecord(params, reader); return true; case ESM4::REC_GMST: readTypedRecord(params, reader); return true; case ESM4::REC_GRAS: readTypedRecord(params, reader); return true; case ESM4::REC_GRUP: break; case ESM4::REC_HAIR: readTypedRecord(params, reader); return true; case ESM4::REC_HAZD: break; case ESM4::REC_HDPT: readTypedRecord(params, reader); return true; case ESM4::REC_IDLE: readTypedRecord(params, reader); return true; break; case ESM4::REC_IDLM: readTypedRecord(params, reader); return true; case ESM4::REC_IMAD: break; case ESM4::REC_IMGS: break; case ESM4::REC_IMOD: readTypedRecord(params, reader); return true; case ESM4::REC_INFO: readTypedRecord(params, reader); return true; case ESM4::REC_INGR: readTypedRecord(params, reader); return true; case ESM4::REC_IPCT: break; case ESM4::REC_IPDS: break; case ESM4::REC_KEYM: readTypedRecord(params, reader); return true; case ESM4::REC_KYWD: break; case ESM4::REC_LAND: readTypedRecord(params, reader); return true; case ESM4::REC_LCRT: break; case ESM4::REC_LCTN: break; case ESM4::REC_LGTM: readTypedRecord(params, reader); return true; case ESM4::REC_LIGH: readTypedRecord(params, reader); return true; case ESM4::REC_LSCR: break; case ESM4::REC_LTEX: readTypedRecord(params, reader); return true; case ESM4::REC_LVLC: readTypedRecord(params, reader); return true; case ESM4::REC_LVLI: readTypedRecord(params, reader); return true; case ESM4::REC_LVLN: readTypedRecord(params, reader); return true; case ESM4::REC_LVSP: break; case ESM4::REC_MATO: readTypedRecord(params, reader); return true; case ESM4::REC_MATT: break; case ESM4::REC_MESG: break; case ESM4::REC_MGEF: break; case ESM4::REC_MISC: readTypedRecord(params, reader); return true; case ESM4::REC_MOVT: break; case ESM4::REC_MSET: readTypedRecord(params, reader); return true; case ESM4::REC_MSTT: readTypedRecord(params, reader); return true; case ESM4::REC_MUSC: readTypedRecord(params, reader); return true; case ESM4::REC_MUST: break; case ESM4::REC_NAVI: readTypedRecord(params, reader); return true; case ESM4::REC_NAVM: readTypedRecord(params, reader); return true; case ESM4::REC_NOTE: readTypedRecord(params, reader); return true; case ESM4::REC_NPC_: readTypedRecord(params, reader); return true; case ESM4::REC_OTFT: readTypedRecord(params, reader); return true; case ESM4::REC_PACK: readTypedRecord(params, reader); return true; case ESM4::REC_PERK: break; case ESM4::REC_PGRD: readTypedRecord(params, reader); return true; case ESM4::REC_PGRE: readTypedRecord(params, reader); return true; case ESM4::REC_PHZD: break; case ESM4::REC_PROJ: break; case ESM4::REC_PWAT: readTypedRecord(params, reader); return true; case ESM4::REC_QUST: readTypedRecord(params, reader); return true; case ESM4::REC_RACE: readTypedRecord(params, reader); return true; case ESM4::REC_REFR: readTypedRecord(params, reader); return true; case ESM4::REC_REGN: readTypedRecord(params, reader); return true; case ESM4::REC_RELA: break; case ESM4::REC_REVB: break; case ESM4::REC_RFCT: break; case ESM4::REC_ROAD: readTypedRecord(params, reader); return true; case ESM4::REC_SBSP: readTypedRecord(params, reader); return true; case ESM4::REC_SCEN: break; case ESM4::REC_SCOL: readTypedRecord(params, reader); return true; case ESM4::REC_SCPT: readTypedRecord(params, reader); return true; case ESM4::REC_SCRL: readTypedRecord(params, reader); return true; case ESM4::REC_SGST: readTypedRecord(params, reader); return true; case ESM4::REC_SHOU: break; case ESM4::REC_SLGM: readTypedRecord(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(params, reader); return true; case ESM4::REC_SOPM: break; case ESM4::REC_SOUN: readTypedRecord(params, reader); return true; case ESM4::REC_SPEL: break; case ESM4::REC_SPGD: break; case ESM4::REC_STAT: readTypedRecord(params, reader); return true; case ESM4::REC_TACT: readTypedRecord(params, reader); return true; case ESM4::REC_TERM: readTypedRecord(params, reader); return true; case ESM4::REC_TES4: readTypedRecord(params, reader); return true; case ESM4::REC_TREE: readTypedRecord(params, reader); return true; case ESM4::REC_TXST: readTypedRecord(params, reader); return true; case ESM4::REC_VTYP: break; case ESM4::REC_WATR: break; case ESM4::REC_WEAP: readTypedRecord(params, reader); return true; case ESM4::REC_WOOP: break; case ESM4::REC_WRLD: readTypedRecord(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&& 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, nullptr, &encoder, true); const Params params(info); if (!params.mQuite) { std::cout << "Author: " << reader.getAuthor() << '\n' << "Description: " << reader.getDesc() << '\n' << "File format version: " << reader.esmVersionF() << '\n'; if (const std::vector& 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 visitorGroup = [¶ms](ESM4::Reader& reader) { if (params.mQuite) return; auto groupType = static_cast(reader.hdr().group.type); std::cout << "\nGroup: " << toString(groupType) << " " << ESM::NAME(reader.hdr().group.typeId).toStringView() << '\n'; }; ESM4::ReaderUtils::readAll(reader, visitorRec, visitorGroup); } catch (const std::exception& e) { std::cout << "\nERROR:\n\n " << e.what() << std::endl; return -1; } return 0; } }