46 =welder::weld_as(
"WDT"),
49 A whole map description, abstract over the client version — the main
50 .wdt file and its era's satellite files (_occ/_lgt/_fogs/_mpv) as one
51 entity. Construct the concrete version with WDT.for_version(expansion),
52 then read()/write(); the per-version WDT* classes are subclasses. See
53 https://wowdev.wiki/WDT.)")
62 template <ClientVersion V>
65 "The _occ.wdt occlusion satellite (WoD+); default-empty when "
66 "the file does not exist.")]]
70 "The _lgt.wdt lights satellite (WoD+); default-empty when the "
71 "file does not exist.")]]
76 template <ClientVersion V>
78 [[=welder::doc(
"The _fogs.wdt volumetric-fog satellite (Legion 7.2.5+); "
79 "default-empty when the file does not exist.")]]
84 template <ClientVersion V>
85 struct SatellitesBfa {
86 [[=welder::doc(
"The _mpv.wdt particulate-volume satellite (BfA+); "
87 "default-empty when the file does not exist.")]]
107 template <ClientVersion V>
111 A whole map description for one client version: the main .wdt file
112 and its era's satellite files (_occ/_lgt since WoD, _fogs since
113 Legion 7.2.5, _mpv since BfA) as one entity. Satellites locate by
114 the "{map}_occ.wdt" naming convention up to 8.1 and by the MPHD
115 FileDataIDs after; a missing satellite stays default-empty. An
116 entity read from a client and left unmodified rewrites
117 byte-for-byte. See https://wowdev.wiki/WDT.)")
125 [[=welder::doc(
"The main file contents.")]]
136 "Load the WDT and every satellite file present from a client "
137 "filesystem, replacing this entity's contents.")]]
139 const FileKey& key [[=welder::doc(
"the main file identity (path and/or FileDataID)")]]);
142 =welder::doc(
"Serialize and store the WDT (main file and every engaged "
143 "satellite) through the filesystem's project overlay; satellite "
144 "file names are derived from the main key, which must resolve "
147 const FileKey& key [[=welder::doc(
"the main file identity; must resolve to a path")]])
const;
150 Check the logical integrity contracts this object must satisfy to
151 LOAD in the client — across the main file AND every engaged
152 satellite — which write() deliberately never enforces. Call it
153 before writing when you want to know the files will load. An object
154 read from a client and left unmodified reports no errors.)"),
155 =welder::returns(R"(every violated contract, each with its member path
156 ("root..." / "occlusion..." / "lights..." /
157 "fogs..." / "particulates..."))")]]
161 [[=welder::doc(
"Validate and raise on the first error instead of "
162 "returning a report — the assert-style face of "
164 =welder::returns(
"nothing; raises when validate() finds any error")]]
175 static std::string _satellitePath(std::string_view rootPath, std::string_view suffix);
182 template <ClientVersion V>
191 template <ClientVersion V>
192 std::string detail::WDT<V>::_satellitePath(std::string_view rootPath, std::string_view suffix) {
193 std::string_view stem = rootPath;
194 if (stem.ends_with(
".wdt")) stem.remove_suffix(4);
195 return std::format(
"{}_{}.wdt", stem, suffix);
198 template <ClientVersion V>
200 const auto rootData =
fs.readFile(key);
201 if (!rootData)
return std::unexpected{rootData.error()};
205 if (
auto r =
root.read(*rootData); !r)
return std::unexpected{r.error()};
210 if constexpr (
requires { this->
occlusion; }) {
213 constexpr bool byFdid =
requires { this->
root.header.occFdid; };
215 std::string rootPath;
216 if constexpr (!byFdid) {
217 const FileKey resolved = fs.
resolve(key);
220 "satellite files need the main path (pre-8.1 clients have no "
221 "satellite FileDataIDs and the main key has no resolvable path)");
222 rootPath = *resolved.path;
225 const auto load = [&](
auto& satellite, std::uint32_t fdid, std::string_view suffix) ->
Result<void> {
226 if constexpr (byFdid)
227 if (fdid == 0)
return {};
231 if (!
fs.exists(satelliteKey))
return {};
232 const auto data = fs.
readFile(satelliteKey);
234 return makeError(data.error().code, std::format(
"_{} satellite: {}", suffix, data.error().message),
235 data.error().nativeError);
236 if (
auto r = satellite.read(*data); !r)
237 return makeError(r.error().code, std::format(
"_{} satellite: {}", suffix, r.error().message),
238 r.error().nativeError);
242 const auto headerFdid = [&](
auto pick) -> std::uint32_t {
243 if constexpr (byFdid)
return pick(
root.header);
247 if (
auto r = load(this->
occlusion, headerFdid([](
const auto& h) {
return h.occFdid; }),
"occ"); !r)
return r;
248 if (
auto r = load(this->
lights, headerFdid([](
const auto& h) {
return h.lgtFdid; }),
"lgt"); !r)
return r;
249 if constexpr (
requires { this->
fogs; })
250 if (
auto r = load(this->
fogs, headerFdid([](
const auto& h) {
254 if constexpr (
requires { this->particulates; })
255 if (
auto r = load(this->particulates, headerFdid([](
const auto& h) {
return h.mpvFdid; }),
"mpv"); !r)
return
261 template <ClientVersion V>
263 ValidationReport report;
265 const std::size_t mark = report.size();
267 report.prefixFrom(mark,
"root");
271 const auto validatePart = [&report](
const auto& part, std::string_view name) {
272 const std::size_t mark = report.size();
274 report.prefixFrom(mark, std::string{name});
276 if constexpr (
requires { this->
occlusion; }) validatePart(this->
occlusion,
"occlusion");
277 if constexpr (
requires { this->
lights; }) validatePart(this->
lights,
"lights");
278 if constexpr (
requires { this->
fogs; }) validatePart(this->
fogs,
"fogs");
279 if constexpr (
requires { this->particulates; }) validatePart(this->particulates,
"particulates");
283 template <ClientVersion V>
285 return validate().toResult();
288 template <ClientVersion V>
290 const FileKey resolved = fs.resolve(key);
294 const auto rootData =
root.write();
295 if (!rootData)
return std::unexpected{rootData.error()};
296 if (
auto r = fs.addFile(*resolved.path, *rootData); !r)
return std::unexpected{r.error()};
298 if constexpr (
requires { this->
occlusion; }) {
301 const auto store = [&](
const auto& satellite, std::string_view suffix) ->
Result<void> {
303 const auto data = satellite.write();
305 return makeError(data.error().code, std::format(
"_{} satellite: {}", suffix, data.error().message),
306 data.error().nativeError);
307 if (
auto r = fs.addFile(_satellitePath(*resolved.path, suffix), *data); !r)
308 return makeError(r.error().code, std::format(
"_{} satellite: {}", suffix, r.error().message),
309 r.error().nativeError);
313 if (
auto r = store(this->
occlusion,
"occ"); !r)
return r;
314 if (
auto r = store(this->
lights,
"lgt"); !r)
return r;
315 if constexpr (
requires { this->
fogs; })
316 if (
auto r = store(this->
fogs,
"fogs"); !r)
return r;
317 if constexpr (
requires { this->particulates; })
318 if (
auto r = store(this->particulates,
"mpv"); !r)
return r;
FileKey resolve(const FileKey &key) const
Result< FileBuffer > readFile(const FileKey &key)
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...
The error-handling vocabulary: ErrorCode, Error and the Result<T> alias every fallible wowlib operati...
File identity types: the strong FileDataID and the FileKey a read request travels as.
The runtime facade over the static compositions — the primary welder binding surface of the fs layer.
The _fogs.wdt volumetric-fog satellite entity (namespace wowlib::formats::wdt::fogs),...
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.
The _lgt.wdt lights satellite entity (namespace wowlib::formats::wdt::lights), WoD+: freely placed ma...
The _mpv.wdt particulate-volume satellite entity (namespace wowlib::formats::wdt::mpv),...
constexpr welder::lang Cs
C#/.NET — the welder-csharp rod's identity (user-range slot 0), respelled for wowlib's annotation sit...
std::expected< T, Error > Result
Every fallible wowlib operation returns Result<T>; bindings translate the error branch into a target-...
std::unexpected< Error > makeError(ErrorCode code, std::string message, std::uint32_t nativeError=0)
Shorthand for constructing the error branch of a Result.
@ PathNotResolvable
No FileDataID is known for the given path (listfile miss).
@ FormatVersionMismatch
The file's version chunk disagrees with the requested version.
The _occ.wdt occlusion satellite entity (namespace wowlib::formats::wdt::occlusion),...
std::optional< std::string > path
The canonical client-internal path, if known.
The conditional-base mechanism that gives a versioned chunked entity exactly the fields its client ve...
WDT version grids and per-family canonicalization pivots.
The WDT main-file entity (namespace wowlib::formats::wdt::root): the map header, the 64x64 tile table...