31#include <welder/vocabulary.hpp>
57 =welder::doc(
"How a BLP's pixel payload is encoded (the header's "
58 "colorEncoding byte).")
60 Jpeg [[=welder::doc(
"JPEG-compressed content (Warcraft III heritage; never "
61 "shipped in WoW clients — wowlib preserves but cannot "
63 Palettized [[=welder::doc(
"256-color palette indices, one byte per pixel, "
64 "followed by a separate alpha plane of "
65 "alphaDepth bits per pixel.")]] = 1,
66 Dxt [[=welder::doc(
"DXT/S3TC block compression; the variant (BC1/BC2/BC3/"
67 "BC5) follows from preferred_format and "
69 Bgra [[=welder::doc(
"Raw 32-bit BGRA pixels (Cataclysm+; terrain cube "
71 BgraAlt [[=welder::doc(
"Raw 32-bit BGRA under a different client-side "
72 "PIXEL_FORMAT; identical file content to "
78 =welder::doc(
"The client-side pixel format hint (the header's "
79 "preferredFormat byte). For DXT-encoded files it selects the "
80 "block format: Dxt1 -> BC1, Dxt3 -> BC2, Dxt5 -> BC3, "
83 Dxt1 [[=welder::doc(
"BC1: 8-byte blocks, optional 1-bit punch-through "
85 Dxt3 [[=welder::doc(
"BC2: 16-byte blocks, explicit 4-bit alpha.")]] = 1,
86 Argb8888 [[=welder::doc(
"Raw 32-bit upload hint (used by Bgra-encoded "
88 Argb1555 [[=welder::doc(
"16-bit 1555 upload hint; never a file "
89 "content layout.")]] = 3,
90 Argb4444 [[=welder::doc(
"16-bit 4444 upload hint; never a file "
91 "content layout.")]] = 4,
92 Rgb565 [[=welder::doc(
"16-bit 565 upload hint; never a file content "
94 A8 [[=welder::doc(
"Alpha-only upload hint; never a file content "
96 Dxt5 [[=welder::doc(
"BC3: 16-byte blocks, interpolated 8-bit alpha.")]] = 7,
97 Unspecified [[=welder::doc(
"No preference recorded (typical for palettized "
99 Argb2565 [[=welder::doc(
"Component-texture upload hint; never a file "
100 "content layout.")]] = 9,
101 Bc5 [[=welder::doc(
"BC5: two interpolated channels (normal maps, later "
116 std::uint32_t
width = 0;
118 std::array<std::uint32_t, BlpMaxMips>
mipOffsets{};
120 std::array<std::uint32_t, BlpMaxMips>
mipSizes{};
124 static_assert(
sizeof(
BLPHeader) == 0x94);
125 static_assert(std::is_trivially_copyable_v<BLPHeader>);
133 struct StoredLayout {
143 std::array<std::uint32_t, BlpMaxMips>
offsets{};
145 std::array<std::uint32_t, BlpMaxMips>
sizes{};
148 std::vector<Run>
gaps;
151 bool operator==(
const StoredLayout&)
const =
default;
158 A decoded texture surface: 8-bit RGBA pixels in row-major order, row 0
159 at the top. pixels holds width * height * 4 bytes (r, g, b, a per
160 pixel) and maps to NumPy zero-copy; reshape to (height, width, 4).)")
162 [[=welder::doc("The width in pixels.")]]
165 [[=welder::doc(
"The height in pixels.")]]
168 [[=welder::mark::no_reassign,
169 =welder::doc(
"The RGBA8 pixel bytes, width * height * 4, row-major from "
171 std::vector<std::uint8_t> pixels;
179 How encode() should build the file. The defaults produce what the
180 client ships most: DXT compression with the block format chosen from
181 the alpha depth (0 -> BC1, 1 -> BC1 punch-through, 4 -> BC2,
182 8 -> BC3), with a full generated mip chain.)")
184 [[=welder::doc("The payload encoding to produce (Palettized, Dxt or "
185 "Bgra; Jpeg is not supported).")]]
188 [[=welder::doc(
"The DXT block format for Dxt encoding (Dxt1, Dxt3, Dxt5 "
189 "or Bc5). Unspecified picks from alphaDepth: 0/1 -> Dxt1, "
190 "4 -> Dxt3, 8 -> Dxt5. Ignored for Palettized/Bgra.")]]
193 [[=welder::doc(
"Alpha bits per pixel: 0, 1, 4 or 8. Selects the alpha "
194 "plane depth for Palettized files and the block format for "
195 "Dxt when format is Unspecified.")]]
198 [[=welder::doc(
"Whether to generate the full mip chain down to 1x1 "
199 "(box-filtered). Off: the file holds only level 0.")]]
212 A BLP2 texture file — every WoW client release reads the same layout,
213 so the class carries no client-version axis. read()/write() move the
214 file whole with a byte-perfect round-trip while the mip payloads are
215 unmodified; decode(level) produces an RGBA8 Image from a stored level
216 (palettized, DXT1/3/5, BC5 and raw BGRA all decode); encode(image,
217 settings) rebuilds the palette/compression/mip chain from one. Raw
218 payload access goes through mip()/set_mip(). See
219 https://wowdev.wiki/BLP.)")
221 [[=welder::doc("The header version field; 1 in every shipped file.")]]
224 [[=welder::doc(
"How the pixel payload is encoded.")]]
227 [[=welder::doc(
"Alpha bits per pixel (0, 1, 4 or 8): the alpha plane "
228 "depth for Palettized files, and a selection hint for "
230 std::uint8_t alphaDepth = 8;
232 [[=welder::doc(
"The client-side pixel format hint; selects the DXT block "
233 "format for Dxt-encoded files.")]]
236 [[=welder::doc(
"The header's mip byte: 0 = level 0 only, 1 = generated "
237 "mips, 2 = handmade mips (plus rare high flag bits, "
238 "preserved verbatim).")]]
239 std::uint8_t mipFlags = 1;
241 [[=welder::doc(
"The level-0 width in pixels.")]]
242 std::uint32_t
width = 0;
244 [[=welder::doc(
"The level-0 height in pixels.")]]
247 [[=welder::doc(
"The 256-entry color table of Palettized files (b, g, r + "
248 "a padding byte, preserved verbatim). Present but unused "
249 "for Dxt/Bgra files.")]]
250 std::array<CImVector, BlpPaletteSize>
palette{};
255 [[=welder::mark::exclude]]
256 std::vector<FileBuffer>
mips;
259 [[=welder::mark::exclude]]
264 [[=welder::doc(
"Parse a BLP2 file from memory, replacing this entity's "
266 Result<void> read(std::span<const std::byte> data [[=welder::doc(
"the complete file bytes")]]);
269 [[=welder::doc(
"Serialize this entity. While the mip payloads are "
270 "unmodified the original file's exact layout is replayed, "
271 "so an unmodified read rewrites byte-for-byte."),
272 =welder::returns(
"the file bytes")]]
275 [[=welder::doc(
"Load the BLP from a client filesystem, replacing this "
276 "entity's contents.")]]
278 const FileKey& key [[=welder::doc(
"the file identity (path and/or FileDataID)")]]);
280 [[=welder::doc(
"Serialize and store the BLP through the filesystem's "
281 "project overlay.")]]
283 const FileKey& key [[=welder::doc(
"the file identity; must resolve to a path")]])
const;
288 [[=welder::doc(
"Decode mip level 0 to an RGBA8 Image."),
289 =welder::returns(
"the decoded image")]]
293 [[=welder::doc(
"Decode one stored mip level to an RGBA8 Image."),
294 =welder::returns(
"the decoded image")]]
295 Result<Image> decode(std::uint32_t level [[=welder::doc(
"the mip level to decode")]])
const;
297 [[=welder::doc(
"Rebuild the whole texture from an RGBA8 image with the "
298 "default settings (DXT, alpha depth 8 -> BC3, full mip "
301 [[=welder::doc(
"the level-0 image; width * height * 4 "
304 [[=welder::doc(
"Rebuild the whole texture from an RGBA8 image: sets the "
305 "header fields, quantizes/compresses every level and "
306 "generates the mip chain per the settings.")]]
308 [[=welder::doc(
"the level-0 image; width * height * 4 "
311 [[=welder::doc(
"encoding, block format, alpha depth "
312 "and mip generation choices")]]);
317 =welder::doc(
"The number of stored mip levels (level indices 0 .. "
322 [[=welder::doc(
"One level's raw payload bytes (palette indices + alpha "
323 "plane, DXT blocks, or BGRA pixels, per colorEncoding)."),
324 =welder::returns(
"a copy of the payload bytes")]]
327 [[=welder::doc(
"Replace one level's raw payload bytes verbatim. The "
328 "caller owns their consistency with the header fields; "
329 "changing a payload's size switches write() to the "
330 "canonical contiguous layout.")]]
331 Result<void> setMip(std::uint32_t level [[=welder::doc(
"the mip level")]],
332 std::span<const std::byte> data [[=welder::doc(
"the payload bytes")]]);
335 [[=welder::doc(
"The pixel width of a mip level (level 0 halves per step, "
337 std::uint32_t mipWidth(std::uint32_t level [[=welder::doc(
"the mip level")]])
const {
338 return std::max<std::uint32_t>(1, width >> level);
342 [[=welder::doc(
"The pixel height of a mip level (level 0 halves per step, "
344 std::uint32_t mipHeight(std::uint32_t level [[=welder::doc(
"the mip level")]])
const {
345 return std::max<std::uint32_t>(1, height >> level);
350 Check the logical integrity contracts this texture must satisfy for the
351 client to decode it — the base level's presence, the dimensions, and
352 every stored level covering the pixels its size implies. write() never
353 runs this; call it before writing when you want to know the result will
355 =welder::returns("every violated contract, in level order")]]
356 ValidationReport validate()
const;
359 [[=welder::doc(
"Validate and raise on the first error instead of returning "
360 "a report — the assert-style face of validate()."),
361 =welder::returns(
"nothing; raises when validate() finds any error")]]
362 Result<void> ensureValid()
const {
return validate().toResult(); }
364 bool operator==(
const BLP&)
const =
default;
The owning byte buffer file contents are read into.
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.
FourCC chunk identifiers: compile-time conversion of the four-letter codes to the host integers chunk...
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::vector< std::byte > FileBuffer
Owning byte buffer for file contents read out of a client storage.
The binary-level math and color primitives shared across WoW file formats (wowdev....
The validation vocabulary: the severity scale, the single finding and the report validate() fills.