#include "tes4.hpp" #include "arguments.hpp" #include "labels.hpp" #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 HasFormId : std::false_type {}; template struct HasFormId> : std::true_type {}; template constexpr bool hasFormId = HasFormId::value; template > struct HasFlags : std::false_type {}; template struct HasFlags> : std::true_type {}; template constexpr bool hasFlags = HasFlags::value; 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 (hasFormId) std::cout << ' ' << value.mFormId; if constexpr (hasFlags) std::cout << "\n Record flags: " << recordFlags(value.mFlags); std::cout << '\n'; } void readRecord(const Params& params, ESM4::Reader& reader) { switch (static_cast(reader.hdr().record.typeId)) { case ESM4::REC_AACT: break; case ESM4::REC_ACHR: return readTypedRecord(params, reader); case ESM4::REC_ACRE: return readTypedRecord(params, reader); case ESM4::REC_ACTI: return readTypedRecord(params, reader); case ESM4::REC_ADDN: break; case ESM4::REC_ALCH: return readTypedRecord(params, reader); case ESM4::REC_ALOC: return readTypedRecord(params, reader); case ESM4::REC_AMMO: return readTypedRecord(params, reader); case ESM4::REC_ANIO: return readTypedRecord(params, reader); case ESM4::REC_APPA: return readTypedRecord(params, reader); case ESM4::REC_ARMA: return readTypedRecord(params, reader); case ESM4::REC_ARMO: return readTypedRecord(params, reader); case ESM4::REC_ARTO: break; case ESM4::REC_ASPC: return readTypedRecord(params, reader); case ESM4::REC_ASTP: break; case ESM4::REC_AVIF: break; case ESM4::REC_BOOK: return readTypedRecord(params, reader); case ESM4::REC_BPTD: return readTypedRecord(params, reader); case ESM4::REC_CAMS: break; case ESM4::REC_CCRD: break; case ESM4::REC_CELL: return readTypedRecord(params, reader); case ESM4::REC_CLAS: return readTypedRecord(params, reader); case ESM4::REC_CLFM: return readTypedRecord(params, reader); case ESM4::REC_CLMT: break; case ESM4::REC_CLOT: return readTypedRecord(params, reader); case ESM4::REC_CMNY: break; case ESM4::REC_COBJ: break; case ESM4::REC_COLL: break; case ESM4::REC_CONT: return readTypedRecord(params, reader); case ESM4::REC_CPTH: break; case ESM4::REC_CREA: return readTypedRecord(params, reader); case ESM4::REC_CSTY: break; case ESM4::REC_DEBR: break; case ESM4::REC_DIAL: return readTypedRecord(params, reader); case ESM4::REC_DLBR: break; case ESM4::REC_DLVW: break; case ESM4::REC_DOBJ: return readTypedRecord(params, reader); case ESM4::REC_DOOR: return readTypedRecord(params, reader); 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: return readTypedRecord(params, reader); case ESM4::REC_FACT: break; case ESM4::REC_FLOR: return readTypedRecord(params, reader); case ESM4::REC_FLST: return readTypedRecord(params, reader); case ESM4::REC_FSTP: break; case ESM4::REC_FSTS: break; case ESM4::REC_FURN: return readTypedRecord(params, reader); case ESM4::REC_GLOB: return readTypedRecord(params, reader); case ESM4::REC_GMST: break; case ESM4::REC_GRAS: return readTypedRecord(params, reader); case ESM4::REC_GRUP: break; case ESM4::REC_HAIR: return readTypedRecord(params, reader); case ESM4::REC_HAZD: break; case ESM4::REC_HDPT: return readTypedRecord(params, reader); case ESM4::REC_IDLE: // FIXME: ESM4::IdleAnimation::load does not work with Oblivion.esm // return readTypedRecord(params, reader); break; case ESM4::REC_IDLM: return readTypedRecord(params, reader); case ESM4::REC_IMAD: break; case ESM4::REC_IMGS: break; case ESM4::REC_IMOD: return readTypedRecord(params, reader); case ESM4::REC_INFO: return readTypedRecord(params, reader); case ESM4::REC_INGR: return readTypedRecord(params, reader); case ESM4::REC_IPCT: break; case ESM4::REC_IPDS: break; case ESM4::REC_KEYM: return readTypedRecord(params, reader); case ESM4::REC_KYWD: break; case ESM4::REC_LAND: return readTypedRecord(params, reader); case ESM4::REC_LCRT: break; case ESM4::REC_LCTN: break; case ESM4::REC_LGTM: return readTypedRecord(params, reader); case ESM4::REC_LIGH: return readTypedRecord(params, reader); case ESM4::REC_LSCR: break; case ESM4::REC_LTEX: return readTypedRecord(params, reader); case ESM4::REC_LVLC: return readTypedRecord(params, reader); case ESM4::REC_LVLI: return readTypedRecord(params, reader); case ESM4::REC_LVLN: return readTypedRecord(params, reader); case ESM4::REC_LVSP: break; case ESM4::REC_MATO: return readTypedRecord(params, reader); case ESM4::REC_MATT: break; case ESM4::REC_MESG: break; case ESM4::REC_MGEF: break; case ESM4::REC_MISC: return readTypedRecord(params, reader); case ESM4::REC_MOVT: break; case ESM4::REC_MSET: return readTypedRecord(params, reader); case ESM4::REC_MSTT: return readTypedRecord(params, reader); case ESM4::REC_MUSC: return readTypedRecord(params, reader); case ESM4::REC_MUST: break; case ESM4::REC_NAVI: return readTypedRecord(params, reader); case ESM4::REC_NAVM: return readTypedRecord(params, reader); case ESM4::REC_NOTE: return readTypedRecord(params, reader); case ESM4::REC_NPC_: return readTypedRecord(params, reader); case ESM4::REC_OTFT: return readTypedRecord(params, reader); case ESM4::REC_PACK: return readTypedRecord(params, reader); case ESM4::REC_PERK: break; case ESM4::REC_PGRD: return readTypedRecord(params, reader); case ESM4::REC_PGRE: return readTypedRecord(params, reader); case ESM4::REC_PHZD: break; case ESM4::REC_PROJ: break; case ESM4::REC_PWAT: return readTypedRecord(params, reader); case ESM4::REC_QUST: return readTypedRecord(params, reader); case ESM4::REC_RACE: return readTypedRecord(params, reader); case ESM4::REC_REFR: return readTypedRecord(params, reader); case ESM4::REC_REGN: return readTypedRecord(params, reader); case ESM4::REC_RELA: break; case ESM4::REC_REVB: break; case ESM4::REC_RFCT: break; case ESM4::REC_ROAD: return readTypedRecord(params, reader); case ESM4::REC_SBSP: return readTypedRecord(params, reader); case ESM4::REC_SCEN: break; case ESM4::REC_SCOL: return readTypedRecord(params, reader); case ESM4::REC_SCPT: return readTypedRecord(params, reader); case ESM4::REC_SCRL: return readTypedRecord(params, reader); case ESM4::REC_SGST: return readTypedRecord(params, reader); case ESM4::REC_SHOU: break; case ESM4::REC_SLGM: return readTypedRecord(params, reader); case ESM4::REC_SMBN: break; case ESM4::REC_SMEN: break; case ESM4::REC_SMQN: break; case ESM4::REC_SNCT: break; case ESM4::REC_SNDR: return readTypedRecord(params, reader); case ESM4::REC_SOPM: break; case ESM4::REC_SOUN: return readTypedRecord(params, reader); case ESM4::REC_SPEL: break; case ESM4::REC_SPGD: break; case ESM4::REC_STAT: return readTypedRecord(params, reader); case ESM4::REC_TACT: return readTypedRecord(params, reader); case ESM4::REC_TERM: return readTypedRecord(params, reader); case ESM4::REC_TES4: return readTypedRecord(params, reader); case ESM4::REC_TREE: return readTypedRecord(params, reader); case ESM4::REC_TXST: return readTypedRecord(params, reader); case ESM4::REC_VTYP: break; case ESM4::REC_WATR: break; case ESM4::REC_WEAP: return readTypedRecord(params, reader); case ESM4::REC_WOOP: break; case ESM4::REC_WRLD: return readTypedRecord(params, reader); case ESM4::REC_WTHR: break; } if (!params.mQuite) std::cout << "\n Unsupported record: " << ESM::NAME(reader.hdr().record.typeId).toStringView() << '\n'; reader.skipRecordData(); } bool readItem(const Params& params, ESM4::Reader& reader); bool readGroup(const Params& params, ESM4::Reader& reader) { const ESM4::RecordHeader& header = reader.hdr(); if (!params.mQuite) std::cout << "\nGroup: " << toString(static_cast(header.group.type)) << " " << ESM::NAME(header.group.typeId).toStringView() << '\n'; switch (static_cast(header.group.type)) { case ESM4::Grp_RecordType: case ESM4::Grp_InteriorCell: case ESM4::Grp_InteriorSubCell: case ESM4::Grp_ExteriorCell: case ESM4::Grp_ExteriorSubCell: reader.enterGroup(); return readItem(params, reader); case ESM4::Grp_WorldChild: case ESM4::Grp_CellChild: case ESM4::Grp_TopicChild: case ESM4::Grp_CellPersistentChild: case ESM4::Grp_CellTemporaryChild: case ESM4::Grp_CellVisibleDistChild: reader.adjustGRUPFormId(); reader.enterGroup(); if (!reader.hasMoreRecs()) return false; return readItem(params, reader); } reader.skipGroup(); return true; } bool readItem(const Params& params, ESM4::Reader& reader) { if (!reader.getRecordHeader() || !reader.hasMoreRecs()) return false; const ESM4::RecordHeader& header = reader.hdr(); if (header.record.typeId == ESM4::REC_GRUP) return readGroup(params, reader); readRecord(params, reader); return true; } } 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); 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& masterData = reader.getGameFiles(); !masterData.empty()) { std::cout << "Masters:" << '\n'; for (const auto& master : masterData) std::cout << " " << master.name << ", " << master.size << " bytes\n"; } } while (reader.hasMoreRecs()) { reader.exitGroupCheck(); if (!readItem(params, reader)) break; } } catch (const std::exception& e) { std::cout << "\nERROR:\n\n " << e.what() << std::endl; return -1; } return 0; } }