wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
root.hpp
Go to the documentation of this file.
1#pragma once
2
13
14#include <array>
15#include <cstdint>
16#include <cstring>
17#include <format>
18#include <span>
19#include <string_view>
20#include <vector>
21
24#include <wowlib/core/lang.hpp>
37
39 using namespace wowlib::formats::wmo::root::chunks;
40
50 struct [[
51 =welder::weld,
52 =welder::weld_as("WMORoot"),
54 =welder::doc(R"(
55 A WMO root file, abstract over the client version. The root file lists the
56 object's shared data (materials, doodads, portals, lights, fog and the
57 group table); the geometry lives in the separate group files. Construct a
58 concrete version with WMORoot.for_version(expansion); the per-version
59 WMORoot* classes are subclasses. See https://wowdev.wiki/WMO.)")
60 ]] WMORootBase {};
61
62 namespace detail {
63 // --- version-range trait bases (unwelded) ---------------------------------
64 // One struct per availability range; members keep their chunk/since/until/doc/
65 // marks (read off the declaring class, so flattening preserves them). Member
66 // order within a trait is for readability; the serialization order is the
67 // entity's ChunkOrder table, not the flatten order.
70 struct RootLegion {
71 [[
72 =chunk("GFID"),
75 =welder::mark::no_reassign,
76 =welder::doc(R"(Group file FileDataIDs (GFID, Legion+), in group order;
77 repeated per LOD level for LOD WMOs.)")]]
78 std::vector<std::uint32_t> groupFdids;
79 };
80
82 struct Root73 {
83 [[
84 =chunk("MOUV"),
87 =formats::countMatches("materials"),
88 =welder::mark::no_reassign,
89 =welder::doc(
90 R"(Texture-coordinate translation animations (MOUV, 7.3+), one
91 per material.)")]]
92 std::vector<UVAnimation> uvAnimations;
93 };
100 struct RootPre83 {
101 [[
102 =chunk("MOTX"),
105 =welder::doc(
106 R"(Texture filenames (MOTX); the material texture fields index
107 into this block. Pre-8.1 it is the primary reference; in
108 8.1/8.2 its presence marks the fallback mode (MOMT texture
109 fields are MOTX offsets, not FileDataIDs). Removed at 8.3,
110 when the client dropped name-based texture loading.)")]]
112
113 [[
114 =chunk("MODN"),
117 =welder::doc(
118 R"(Doodad (M2) filenames (MODN); MODD entries index into this
119 block. Pre-8.1 primary, 8.1/8.2 fallback (see textures).
120 Removed at 8.3 alongside MOTX; MODI (doodad FileDataIDs)
121 replaces it.)")]]
123 };
124
126 struct Root81 {
127 [[
128 =chunk("MOSI"),
131 =welder::mark::no_reassign,
132 =welder::doc("Skybox FileDataID (MOSI, 8.1+).")]]
133 std::vector<std::uint32_t> skyboxFdid;
134
135 [[
136 =chunk("MODI"),
139 =welder::mark::no_reassign,
140 =welder::doc(
141 "Doodad FileDataIDs (MODI, 8.1+; replaces doodad_names).")]]
142 std::vector<std::uint32_t> doodadFdids;
143 };
146 struct Root83 {
147 [[
148 =chunk("MDDI"),
151 =formats::countMatches("doodadDefs"),
152 =welder::mark::no_reassign,
153 =welder::doc(
154 R"(Per-doodad color multipliers (MDDI, 8.3+), applied to the
155 MODD color.)")]]
156 std::vector<float> doodadColorMults;
157
158 [[
159 =chunk("MPVD"),
162 =welder::doc(
163 R"(Particulate volume data (MPVD, 8.3+); undocumented layout,
164 kept opaque.)")]]
166
167 [[
168 =chunk("MAVG"),
171 =welder::mark::no_reassign,
172 =welder::doc(
173 R"(Global ambient volumes (MAVG, 8.3+); position and radii are
174 zero, selected by doodad set.)")]]
175 std::vector<AmbientVolume> globalAmbientVolumes;
176
177 [[
178 =chunk("MAVD"),
181 =welder::mark::no_reassign,
182 =welder::doc(
183 R"(Ambient volumes (MAVD, 8.3+), overriding the header ambient
184 color inside their radius.)")]]
185 std::vector<AmbientVolume> ambientVolumes;
186
187 [[
188 =chunk("MBVD"),
191 =welder::mark::no_reassign,
192 =welder::doc(
193 R"(Box ambient volumes (MBVD, 8.3+); read only when MAVG/MAVD is
194 present.)")]]
195 std::vector<AmbientBoxVolume> ambientBoxVolumes;
196 };
197
199 struct Root90 {
200 [[
201 =chunk("MFED"),
205 =welder::mark::no_reassign,
206 =welder::doc("Fog extra data (MFED, 9.0+); same count as MFOG.")]]
207 std::vector<FogExtra> fogExtras;
208
209 [[
210 =chunk("MGI2"),
213 =formats::countMatches("groupInfos"),
214 =welder::mark::no_reassign,
215 =welder::doc(
216 R"(Group info v2 (MGI2, 9.0+); same count as MOGI, overrides LOD
217 selection.)")]]
218 std::vector<GroupInfo2> groupInfos2;
220 [[
221 =chunk("MNLD"),
224 =welder::mark::no_reassign,
225 =welder::doc(R"(Dynamic lights (MNLD, 9.0+): torch fires, window light
226 projection and the like.)")]]
227 std::vector<NewLight> newLights;
228
229 [[
230 =chunk("MDDL"),
233 =welder::doc(R"(Detail (ground-effect) doodad layers (MDDL, 9.0+);
234 variable-length RLE layout, kept opaque.)")]]
236 };
237
239 struct Root91 {
240 [[
241 =chunk("MOLV"),
244 =welder::mark::no_reassign,
245 =welder::doc(
246 R"(Directional-gradient light extensions (MOLV, 9.1+); entries
247 reference lights by index.)")]]
248 std::vector<LightExtension> lightExtensions;
249 };
250
252 struct Root110 {
253 [[
254 =chunk("MOM3"),
257 =welder::doc(
258 R"(M3 materials (MOM3, 11.0+); when present, MOMT is ignored. An
259 m3SI blob, kept opaque.)")]]
261 };
262
264 struct Root111 {
265 [[
266 =chunk("MOPE"),
269 =welder::mark::no_reassign,
270 =welder::doc("Portal extra data (MOPE, 11.1+).")]]
271 std::vector<PortalExtra> portalExtras;
272 };
273 }
274
275 namespace detail {
276 // The annotated entity; instantiate through the canonicalizing alias
277 // below, never directly. The trait bases share this namespace, so they
278 // need no qualifier (a bare detail:: would be ambiguous against
279 // chunks::detail via the using-directive).
291 template <ClientVersion V>
292 struct [[
293 =welder::weld,
294 =welder::doc(R"(
295 A WMO root file for one client version. The root lists the object's shared
296 data — materials and textures, doodad (M2) sets and placements, portals,
297 lights, fog, culling volumes and the per-group table — while the 3D
298 geometry lives in the separate group files (see WMOGroup). An instance read
299 from a client file rewrites byte-for-byte until modified. See
300 https://wowdev.wiki/WMO.)")
301 ]] WMORoot
313 static constexpr ClientVersion Version = V;
314
315 [[
316 =chunk("MVER"),
318 =welder::doc("The WMO format version; 17 for every supported client.")]]
319 std::uint32_t mver = WmoVersionV17;
320
321 [[
322 =chunk("MOHD"),
323 =welder::doc("The root header (MOHD).")]]
325
326 [[
327 =chunk("MOMT"),
329 =welder::mark::no_reassign,
330 =welder::doc("Materials (MOMT).")]]
331 std::vector<SMOMaterial> materials;
332
333 [[
334 =chunk("MOGN"),
336 =welder::doc("Group names (MOGN), referenced by byte offset.")]]
337 StringBlock groupNames;
338
339 [[
340 =chunk("MOGI"),
342 =welder::mark::no_reassign,
343 =welder::doc("Per-group info (MOGI).")]]
344 std::vector<SMOGroupInfo> groupInfos;
345
346 [[
347 =chunk("MOSB"),
349 =welder::doc(
350 R"(Skybox filename (MOSB); raw bytes — files pad it to 4-byte
351 alignment. skybox_fdid (MOSI, 8.1+) functionally replaces
352 it, but the chunk never left the corpus: a 9.2.7 client
353 survey (2026-08-08) found MOSB in 5404 of 8148 roots —
354 always empty, never alongside MOSI — so it stays on every
355 version as the no-skybox marker exporters still emit.)")
356 ]]
357 ChunkBlob skyboxName;
358
359 [[
360 =chunk("MOPV"),
362 =welder::mark::no_reassign,
363 =welder::doc("Portal vertices (MOPV).")]]
364 std::vector<C3Vector> portalVertices;
365
366 [[
367 =chunk("MOPT"),
369 =welder::mark::no_reassign,
370 =welder::doc("Portals (MOPT).")]]
371 std::vector<SMOPortal> portals;
372
373 [[
374 =chunk("MOPR"),
376 =welder::mark::no_reassign,
377 =welder::doc("Portal references from groups (MOPR).")]]
378 std::vector<SMOPortalRef> portalRefs;
379
380 [[
381 =chunk("MOVV"),
383 =welder::mark::no_reassign,
384 =welder::doc("Visible block vertices (MOVV).")]]
385 std::vector<C3Vector> visibleBlockVertices;
386
387 [[
388 =chunk("MOVB"),
390 =welder::mark::no_reassign,
391 =welder::doc("Visible blocks (MOVB).")]]
392 std::vector<SMOVisibleBlock> visibleBlocks;
393
394 [[
395 =chunk("MOLT"),
397 =welder::mark::no_reassign,
398 =welder::doc("Lights (MOLT).")]]
399 std::vector<SMOLight> lights;
400
401 [[
402 =chunk("MODS"),
404 =welder::mark::no_reassign,
405 =welder::doc("Doodad sets (MODS).")]]
406 std::vector<SMODoodadSet> doodadSets;
407
408 [[
409 =chunk("MODD"),
411 =welder::mark::no_reassign,
412 =welder::doc("Doodad placements (MODD).")]]
413 std::vector<SMODoodadDef> doodadDefs;
414
415 [[
416 =chunk("MFOG"),
418 =welder::mark::no_reassign,
419 =welder::doc("Fog volumes (MFOG).")]]
420 std::vector<SMOFog> fogs;
421
422 [[
423 =chunk("MCVP"),
425 =welder::mark::no_reassign,
426 =welder::doc("Convex volume planes (MCVP); transports mostly.")]]
427 std::vector<C4Plane> convexVolumePlanes;
428
429 [[
430 =chunk("MOMX"),
432 =welder::doc("MOMX (11.x, undocumented purpose); preserved opaque.")]]
433 ChunkBlob momx;
434
441 static constexpr std::array ChunkOrder = {
442 fourcc("MVER"),
443 fourcc("MOHD"),
444 fourcc("MOTX"),
445 fourcc("MOMT"),
446 fourcc("MOM3"),
447 fourcc("MOUV"),
448 fourcc("MOGN"),
449 fourcc("MOGI"),
450 fourcc("MOSB"),
451 fourcc("MOSI"),
452 fourcc("MOPV"),
453 fourcc("MOPT"),
454 fourcc("MOPR"),
455 fourcc("MOPE"),
456 fourcc("MOVV"),
457 fourcc("MOVB"),
458 fourcc("MOLT"),
459 fourcc("MOLV"),
460 fourcc("MODS"),
461 fourcc("MODN"),
462 fourcc("MODI"),
463 fourcc("MODD"),
464 fourcc("MFOG"),
465 fourcc("MCVP"),
466 fourcc("GFID"),
467 fourcc("MDDI"),
468 fourcc("MPVD"),
469 fourcc("MAVG"),
470 fourcc("MAVD"),
471 fourcc("MBVD"),
472 fourcc("MFED"),
473 fourcc("MGI2"),
474 fourcc("MNLD"),
475 fourcc("MDDL"),
476 fourcc("MOMX"),
477 };
478
486 [[=welder::mark::exclude]]
487 void patchChunk(std::uint32_t fourccMagic, std::span<std::byte> payload) const {
488 if (fourccMagic != fourcc("MOHD") || payload.size() < sizeof(SMOHeader)) return;
489 SMOHeader h;
490 std::memcpy(&h, payload.data(), sizeof h);
491 h.nGroups = static_cast<std::uint32_t>(groupInfos.size());
492 h.nPortals = static_cast<std::uint32_t>(portals.size());
493 h.nDoodadSets = static_cast<std::uint32_t>(doodadSets.size());
494 std::memcpy(payload.data(), &h, sizeof h);
495 }
496
507 [[=welder::mark::exclude]]
508 void validateExtra(ValidationReport& report) const {
509 // MODS: (startIndex, count) ranges into MODD
510 for (std::size_t i = 0; i < doodadSets.size(); ++i)
511 if (doodadSets[i].startIndex + doodadSets[i].count > doodadDefs.size())
512 report.addError(std::format("doodad_sets[{}]", i),
513 std::format("range [{}, {}) overruns the {} doodad placements", doodadSets[i].startIndex,
514 doodadSets[i].startIndex + doodadSets[i].count, doodadDefs.size()));
515
516 // MODD name references: MODN byte offsets pre-8.3, MODI indices once
517 // the names block is gone (8.1/8.2 carry both - an engaged MODN marks
518 // the by-name fallback mode, so it stays authoritative)
519 constexpr std::size_t maxReported = 8;
520 std::size_t badNames = 0;
521 for (std::size_t i = 0; i < doodadDefs.size(); ++i) {
522 const std::uint32_t name = doodadDefs[i].nameIndex();
523 bool resolvable = false;
524 std::string_view space;
525 std::size_t bound = 0;
526 if constexpr (requires { this->doodadNames; })
527 if (!this->doodadNames.empty()) {
528 resolvable = name < this->doodadNames.size();
529 space = "doodad_names blob byte";
530 bound = this->doodadNames.size();
531 }
532 if constexpr (requires { this->doodadFdids; })
533 if (space.empty()) {
534 resolvable = name < this->doodadFdids.size();
535 space = "doodad FileDataID";
536 bound = this->doodadFdids.size();
537 }
538 if (space.empty()) {
539 resolvable = false;
540 space = "doodad name source (none engaged)";
541 }
542 if (!resolvable && ++badNames <= maxReported)
543 report.addError(std::format("doodadDefs[{}]", i),
544 std::format("name reference {} does not resolve: {} count {}", name, space, bound));
545 }
546 if (badNames > maxReported)
547 report.addError("doodadDefs", std::format(
548 "... and {} more unresolvable name " "references", badNames - maxReported));
549
550 // MOPT: vertex ranges into MOPV
551 for (std::size_t i = 0; i < portals.size(); ++i)
552 if (portals[i].startVertex + portals[i].count > portalVertices.size())
553 report.addError(std::format("portals[{}]", i),
554 std::format("vertex range [{}, {}) overruns the {} portal vertices",
555 portals[i].startVertex, portals[i].startVertex + portals[i].count,
556 portalVertices.size()));
557
558 // MOPR: indices into MOPT and MOGI
559 for (std::size_t i = 0; i < portalRefs.size(); ++i) {
560 if (portalRefs[i].portalIndex >= portals.size())
561 report.addError(std::format("portal_refs[{}]", i),
562 std::format("portal_index {} out of range: {} portals", portalRefs[i].portalIndex,
563 portals.size()));
564 if (portalRefs[i].groupIndex >= groupInfos.size())
565 report.addError(std::format("portal_refs[{}]", i),
566 std::format("group_index {} out of range: {} groups", portalRefs[i].groupIndex,
567 groupInfos.size()));
568 }
569
570 // MOVB: vertex ranges into MOVV
571 for (std::size_t i = 0; i < visibleBlocks.size(); ++i)
572 if (visibleBlocks[i].firstVertex + visibleBlocks[i].count > visibleBlockVertices.size())
573 report.addError(std::format("visible_blocks[{}]", i), std::format(
574 "vertex range [{}, {}) overruns the {} visible block " "vertices",
575 visibleBlocks[i].firstVertex, visibleBlocks[i].firstVertex + visibleBlocks[i].count,
576 visibleBlockVertices.size()));
577
578 // MOGI: group-name offsets into MOGN (-1 marks the unnamed group)
579 for (std::size_t i = 0; i < groupInfos.size(); ++i)
580 if (groupInfos[i].nameOffset >= 0 && static_cast<std::size_t>(groupInfos[i].nameOffset) >= groupNames.
581 size())
582 report.addError(std::format("groupInfos[{}]", i),
583 std::format("name offset {} out of range: {} blob bytes", groupInfos[i].nameOffset,
584 groupNames.size()));
585
586 // MOMT texture references are MOTX byte offsets while the names block
587 // is engaged (pre-8.1 always; 8.1/8.2 fallback mode). Only texture1
588 // is validated: real vanilla/TBC-era files ship raw float garbage in
589 // the texture2/texture3 slots (corpus: Stormwind.wmo, Subway.wmo),
590 // which the client evidently never dereferences.
591 if constexpr (requires { this->textures; })
592 if (!this->textures.empty())
593 for (std::size_t i = 0; i < materials.size(); ++i)
594 if (materials[i].texture1 >= this->textures.size())
595 report.addError(std::format("materials[{}]", i),
596 std::format("texture_1 offset {} out of range: {} blob bytes", materials[i].texture1,
597 this->textures.size()));
598
599 // MOLV (9.1+): entries reference lights by index
600 if constexpr (requires { this->lightExtensions; })
601 for (std::size_t i = 0; i < this->lightExtensions.size(); ++i)
602 if (const auto index = this->lightExtensions[i].lightIndex; index < 0 || static_cast<std::size_t>(index)
603 >= lights.size())
604 report.addError(std::format("light_extensions[{}]", i),
605 std::format("light_index {} out of range: {} lights", index, lights.size()));
606 }
607 };
608 }
609
614 template <ClientVersion V>
616}
The chunk framework, vocabulary and engine in one header.
void addError(std::string path, std::string message)
Record an error finding (see add()).
Named ClientVersion constants for the exact client builds format features appeared (or vanished) at —...
Client version identity, the flavor axis that separates a client's CONTENT version from the engine ge...
WMO doodad sets and placements (MODS, MODD) (namespace wowlib::formats::wmo::root::chunks).
WMO fog and ambient volumes (MFOG, MAVD/MAVG/MBVD) (namespace wowlib::formats::wmo::root::chunks).
FourCC chunk identifiers: compile-time conversion of the four-letter codes to the host integers chunk...
Binding-language identities welder's core does not name.
#define WOWLIB_CS_FAMILY_SURFACE
The C# rod's family-surface opt-in, spellable in every build.
Definition lang.hpp:44
constexpr ClientVersion Legion_Alpha
legion alpha, 7.0.1 build 20740 — the first chunked-format build (the .m2 MD21 container,...
constexpr ClientVersion TWW_Alpha
The War Within alpha, 11.0.0 build 54210.
constexpr ClientVersion Legion_ShadowsOfArgus_24473
legion, Shadows of Argus PTR, 7.3.0 build 24473.
constexpr ClientVersion BfA_VisionsOfNzoth_32044
BfA, Visions of N'Zoth PTR, 8.3.0 build 32044.
constexpr ClientVersion SL_Alpha_33978
shadowlands alpha, 9.0.1 build 33978 (WFV3/EDGF/NERF/DBOC; PFDC is dated here by wowdev but reached l...
constexpr ClientVersion BfA_TidesOfVengeance
BfA, Tides of Vengeance PTR, 8.1.0 build 27826 (RPID/GPID, WMO MOSI/MODI).
constexpr ClientVersion SL_ChainsOfDomination
shadowlands, Chains of Domination PTR, 9.1.0 build 39015.
constexpr ClientVersion TWW_Undermined
TWW, Undermine(d) PTR, 11.1.0 build 58221.
The WMO root-file entity: WMORoot and its per-version classes.
Definition doodad.hpp:17
detail::WMORoot< canonicalVersion(V, WmoRootPivots, WmoVersions)> WMORoot
A WMO root file — the canonicalizing face of detail::WMORoot: every client version maps to its range'...
Definition root.hpp:431
constexpr std::uint32_t WmoVersionV17
The WMO format version every supported client uses (MVER payload).
constexpr std::array WmoRootPivots
WMORoot: every trait-slot boundary and chunk-introduction build the root file carries (GFID/MOUV/MOSI...
constexpr std::array WmoVersions
The versions WMO is instantiated (and welded) for: every targeted last-minor-of-major release,...
constexpr std::uint32_t fourcc(const char(&cc)[5], FourCCEndian endian=FourCCEndian::Reversed)
The host integer a scanned chunk id compares equal to for code cc.
Definition fourcc.hpp:33
constexpr ClientVersion canonicalVersion(ClientVersion v, std::span< const ClientVersion > pivots, std::span< const ClientVersion > grid)
The canonical version v collapses to: the FIRST grid version in v's range.
constexpr detail::OptionalSpec Optional
Mark a chunk member the format does not require: absence on read is fine.
consteval detail::UntilSpec until(ClientVersion v)
Restrict a member to entity versions < v (exclusive).
consteval detail::ExpectedValueSpec expectedValue(std::uint32_t value)
Declare an exact-value contract on an integral data member (format version fields: WMO MVER is always...
consteval detail::ChunkSpec chunk(const char(&cc)[5], FourCCEndian endian=FourCCEndian::Reversed)
Declare the chunk a member maps to.
consteval detail::SinceSpec since(ClientVersion v)
Restrict a member to entity versions >= v (inclusive).
consteval detail::CountMatchesSpec countMatches(std::string_view name, std::uint32_t scale=1)
Declare a companion-count contract: when this member is engaged (non-empty), its element count times ...
std::conditional_t<(V.formatLineage() >=Since &&V.formatLineage()< Until), Trait, Absent< Trait > > Slot
A version-gated base: the entity inherits Trait (flattening its chunk members in) iff Since <= V < Un...
WMO lights and their extensions (MOLT, MOLV, MNLD) (namespace wowlib::formats::wmo::root::chunks).
StringBlock — the decoded representation of a chunk of zero-terminated strings (MOTX,...
WMO group info, portals and visible blocks (namespace wowlib::formats::wmo::root::chunks).
An unparsed chunk payload, preserved verbatim for round-trip.
The serialization face of a chunked entity, mixed in CRTP-style: an entity struct E : ChunkedFile<E> ...
The version-agnostic base of every WMORoot<V> (welded as "WMORoot").
Definition root.hpp:56
The MOHD root header: entity counts, ambient color, bounds and root-wide flags.
Definition header.hpp:34
std::uint32_t nDoodadSets
Number of doodad sets; derived from the MODS table on write and hidden from bindings.
Definition header.hpp:62
std::uint32_t nGroups
Number of group files; derived from the MOGI group-info table on write and hidden from bindings.
Definition header.hpp:47
std::uint32_t nPortals
Number of portals; derived from the MOPT table on write and hidden from bindings.
Definition header.hpp:50
std::vector< PortalExtra > portalExtras
Portal extra data (MOPE, 11.1+).
Definition root.hpp:151
std::vector< UVAnimation > uvAnimations
Definition root.hpp:74
8.1+ root chunks (the FileDataID chunks that replace name lookups).
Definition root.hpp:91
std::vector< std::uint32_t > skyboxFdid
Skybox FileDataID (MOSI, 8.1+).
Definition root.hpp:94
std::vector< std::uint32_t > doodadFdids
Doodad FileDataIDs (MODI, 8.1+; replaces doodad_names).
Definition root.hpp:98
8.3+ root chunks (per-doodad colour + the volume family).
Definition root.hpp:102
std::vector< float > doodadColorMults
Definition root.hpp:104
std::vector< AmbientBoxVolume > ambientBoxVolumes
Definition root.hpp:116
std::vector< AmbientVolume > ambientVolumes
Definition root.hpp:113
std::vector< AmbientVolume > globalAmbientVolumes
Definition root.hpp:110
9.0+ root chunks (fog/group v2, dynamic lights, detail doodads).
Definition root.hpp:120
std::vector< FogExtra > fogExtras
Fog extra data (MFED, 9.0+); same count as MFOG.
Definition root.hpp:123
std::vector< GroupInfo2 > groupInfos2
Definition root.hpp:126
std::vector< NewLight > newLights
Definition root.hpp:129
std::vector< LightExtension > lightExtensions
Definition root.hpp:138
legion+ (7.0.1) root chunks.
Definition root.hpp:66
std::vector< std::uint32_t > groupFdids
Definition root.hpp:68
Pre-8.3 root chunks: the by-name file-reference blocks.
Definition root.hpp:82
A WMO (world map object) root file for one client version.
Definition root.hpp:183
void patchChunk(std::uint32_t fourccMagic, std::span< std::byte > payload) const
Serializer hook (see writeEntity): stamp the derived MOHD counts into the just-written header payload...
Definition root.hpp:304
void validateExtra(ValidationReport &report) const
Validation hook (see detail::validateEntity): the root contracts the annotations cannot express — doo...
Definition root.hpp:324
The binary-level math and color primitives shared across WoW file formats (wowdev....
The conditional-base mechanism that gives a versioned chunked entity exactly the fields its client ve...
WMO version pivots that change a binary-struct layout (not just chunk presence).
WMO root header (MOHD) and its flag bits (namespace wowlib::formats::wmo::root::chunks).
WMO materials and UV animation (MOMT, MOUV) (namespace wowlib::formats::wmo::root::chunks).