wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
roundtrip_wmo.cpp
Go to the documentation of this file.
1
5
6#include <cstddef>
7#include <format>
8#include <string>
9
12
13namespace {
14 using namespace wowlib;
15 using namespace wowlib::audit;
16 using namespace wowlib::formats;
17
25 template <ClientVersion V>
26 RoundtripReport roundtripWmo(fs::FileSystem& fs, const std::string& path) {
27 RoundtripReport report;
28
29 const auto rootData = fs.readFile(FileKey{path});
30 if (auto outcome = audit::detail::classifyRead(report, "root read", rootData)) return *outcome;
31
33 if (auto r = root.read(*rootData); !r) return audit::detail::failWith(report, "root parse", r.error().message);
34 audit::detail::tallyUnknown(report, root);
35
36 const auto rewrittenRoot = root.write();
37 if (!rewrittenRoot)
38 return audit::detail::failWith(report, "root write", rewrittenRoot.error().message);
39 if (auto divergence = audit::detail::firstDivergenceChunked(*rootData, *rewrittenRoot)) return
40 audit::detail::failWith(report, "root compare", *divergence);
41
42 // group identity: GFID when present (Legion+), name derivation otherwise.
43 // The GFID member only exists on versions that have it, so guard the
44 // access. The classifier guarantees the path ends with ".wmo".
45 const std::size_t nGroups = root.header.nGroups;
46 bool byFdid = false;
47 if constexpr (requires { root.groupFdids; }) byFdid = root.groupFdids.size() >= nGroups;
48
49 for (std::size_t i = 0; i < nGroups; ++i) {
50 const FileKey groupKey = [&]() -> FileKey {
51 if constexpr (requires { root.groupFdids; })
52 if (byFdid) return FileKey{FileDataID{root.groupFdids[i]}};
53 return FileKey{std::format("{}_{:03}.wmo", path.substr(0, path.size() - 4), i)};
54 }();
55 const auto groupData = fs.readFile(groupKey);
56 if (auto outcome = audit::detail::classifyRead(report, std::format("group {} read", i), groupData)) return *
57 outcome;
58
60 if (auto r = group.read(*groupData); !r)
61 return audit::detail::failWith(report, std::format("group {} parse", i), r.error().message);
62 audit::detail::tallyUnknown(report, group);
63 audit::detail::tallyUnknown(report, group.body);
64
65 const auto rewritten = group.write();
66 if (!rewritten)
67 return audit::detail::failWith(report, std::format("group {} write", i), rewritten.error().message);
68 if (auto divergence = audit::detail::firstDivergenceChunked(*groupData, *rewritten))
69 return audit::detail::failWith(report, std::format("group {} compare", i), *divergence);
70 }
71 return report;
72 }
73}
74
75namespace wowlib::audit::detail {
77 RoundtripReport report = skipped("unsupported-version");
78 const bool matched = withVersion(version, [&]<ClientVersion V>() {
79 if constexpr (versionSupported(formats::wmo::WmoVersions, V)) report = guarded([&] {
80 return roundtripWmo<V>(fs, path);
81 });
82 });
83 if (!matched) return unrecognizedVersion(version);
84 return report;
85 }
86}
static RoundtripReport wmo(fs::FileSystem &fs, const std::string &path, ClientVersion version)
Byte-perfect round-trip of one root WMO and all its group files (GFID-located on Legion+,...
Internals of the round-trip audit (namespace wowlib::audit::detail): the per-format driver class — on...
RoundtripReport failWith(RoundtripReport &report, std::string stage, std::string error)
Mark report failed in place and hand it back — keeps the unknown-chunk tally gathered before the fail...
Definition detail.hpp:57
std::optional< RoundtripReport > classifyRead(RoundtripReport &report, std::string stage, const Result< FileBuffer > &result)
Classify a client-file read for the audit: content behind an unknown TACT key and zero-byte archive e...
Definition detail.hpp:72
bool withVersion(const ClientVersion v, F &&f)
Invoke f with the targeted release constant matching v as a compile-time template argument — the runt...
Definition detail.hpp:166
constexpr bool versionSupported(std::span< const ClientVersion > grid, const ClientVersion v)
Whether a format's instantiation grid carries v — the per-format if constexpr guard of the version di...
Definition detail.hpp:193
RoundtripReport skipped(std::string_view reason)
A skipped outcome — ok, tagged with the reason.
Definition detail.hpp:45
std::optional< std::string > firstDivergenceChunked(const FileBuffer &original, const FileBuffer &rewritten)
The first byte divergence between an original chunk stream and its rewrite, located within the enclos...
Definition detail.hpp:134
RoundtripReport guarded(F &&f)
Run one file's driver, converting any escaping exception into a failed outcome — one pathological fil...
Definition detail.hpp:100
void tallyUnknown(RoundtripReport &report, const formats::ChunkExtras &extras, formats::FourCCEndian endian=formats::FourCCEndian::Reversed)
Append every unmodeled chunk of a parsed entity to the report's tally, one fourcc spelling per occurr...
Definition detail.hpp:87
RoundtripReport unrecognizedVersion(const ClientVersion v)
The dispatch-failure outcome for a version outside the targeted releases.
Definition detail.hpp:202
group::detail::WMOGroup< canonicalVersion(V, WmoGroupPivots, WmoVersions)> WMOGroup
One WMO group file — the canonicalizing face of detail::WMOGroup (same pivots as the body it contains...
Definition group.hpp:756
constexpr std::array WmoVersions
The versions WMO is instantiated (and welded) for: every targeted last-minor-of-major release,...
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
Result< FileBuffer > write() const
Serialize this entity.
Result< void > read(std::span< const std::byte > data)
Deserialize file bytes into this entity, replacing its contents.
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
SMOHeader header
The root header (MOHD).
Definition root.hpp:192
The WMO entity (namespace wowlib::formats::wmo): a v17 world map object with its root file (wmo::root...