From 03c700b0304f60508c943a7d8345afcae63e83bc Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 12:44:09 -0700 Subject: [PATCH] feat(editor): add --info-blp inspector for proprietary BLP textures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The --info-* suite covers wowee open formats and their JSON/PNG sidecars. BLP itself was the gap — the proprietary source format that asset_extract converts FROM. Useful for verifying source files before --convert-blp-png: wowee_editor --info-blp Texture.blp BLP: Texture.blp (BLP2) size : 128 x 128 channels : 4 format : BLP2 compression: DXT5 mip levels : 16 file bytes : 23044 decoded RGBA bytes: 65536 Magic-checks the first 4 bytes (BLP1 or BLP2) before invoking the loader so feeding a non-BLP gets a clear 'not a BLP1/BLP2 file' message instead of a confusing 'invalid' from the decoder. Reports both file size (compressed-on-disk) and decoded RGBA byte count (width*height*4) so users can see the compression ratio at a glance. JSON mode emits the same fields for CI/scripting. Verified on a real WoW BLP (pvp-banner-emblem-1.blp): correctly identifies as BLP2 / DXT5 / 128x128 / 16 mips. Total format- inspector lineup is now complete (BLP/PNG/DBC-bin/DBC-json plus all 5 wowee binary formats). --- tools/editor/main.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index a05e7b20..837e7ec2 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -484,6 +484,8 @@ static void printUsage(const char* argv0) { std::printf(" Walk extracted asset tree and report open-format coverage and exit\n"); std::printf(" --info-png [--json]\n"); std::printf(" Print PNG header (width, height, channels, bit depth) and exit\n"); + std::printf(" --info-blp [--json]\n"); + std::printf(" Print BLP texture header (format, compression, mips, dimensions) and exit\n"); std::printf(" --info-jsondbc [--json]\n"); std::printf(" Print JSON DBC sidecar metadata (records, fields, source) and exit\n"); std::printf(" --list-missing-sidecars [--json]\n"); @@ -531,7 +533,7 @@ int main(int argc, char* argv[]) { "--data", "--info", "--info-wob", "--info-woc", "--info-wot", "--info-creatures", "--info-objects", "--info-quests", "--info-extract", "--list-missing-sidecars", - "--info-png", "--info-jsondbc", + "--info-png", "--info-jsondbc", "--info-blp", "--info-zone", "--info-wcp", "--list-wcp", "--list-creatures", "--list-objects", "--list-quests", "--list-quest-objectives", "--list-quest-rewards", @@ -1106,6 +1108,65 @@ int main(int argc, char* argv[]) { colorName, channels, channels == 1 ? "" : "s"); std::printf(" file bytes: %llu\n", static_cast(fsz)); return 0; + } else if (std::strcmp(argv[i], "--info-blp") == 0 && i + 1 < argc) { + // Inspect a BLP texture: format/compression/mips/dimensions. + // Loads the full image (which decompresses pixels) since we + // also report channel count and decoded byte size — useful + // for verifying the source before --convert-blp-png. + std::string path = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + std::ifstream in(path, std::ios::binary); + if (!in) { + std::fprintf(stderr, "info-blp: cannot open %s\n", path.c_str()); + return 1; + } + std::vector bytes((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + // Quick magic check before full decode — saves a confusing + // 'invalid' from the loader when the user feeds a non-BLP. + if (bytes.size() < 4 || + !(bytes[0] == 'B' && bytes[1] == 'L' && bytes[2] == 'P' && + (bytes[3] == '1' || bytes[3] == '2'))) { + std::fprintf(stderr, "info-blp: %s is not a BLP1/BLP2 file\n", + path.c_str()); + return 1; + } + std::string magicVer = std::string(bytes.begin(), bytes.begin() + 4); + auto img = wowee::pipeline::BLPLoader::load(bytes); + if (!img.isValid()) { + std::fprintf(stderr, "info-blp: failed to decode %s\n", path.c_str()); + return 1; + } + std::error_code ec; + uint64_t fsz = std::filesystem::file_size(path, ec); + const char* fmtName = wowee::pipeline::BLPLoader::getFormatName(img.format); + const char* compName = wowee::pipeline::BLPLoader::getCompressionName(img.compression); + if (jsonOut) { + nlohmann::json j; + j["blp"] = path; + j["magic"] = magicVer; + j["width"] = img.width; + j["height"] = img.height; + j["channels"] = img.channels; + j["mipLevels"] = img.mipLevels; + j["format"] = fmtName; + j["compression"] = compName; + j["decodedBytes"] = img.data.size(); + j["fileSize"] = fsz; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("BLP: %s (%s)\n", path.c_str(), magicVer.c_str()); + std::printf(" size : %d x %d\n", img.width, img.height); + std::printf(" channels : %d\n", img.channels); + std::printf(" format : %s\n", fmtName); + std::printf(" compression: %s\n", compName); + std::printf(" mip levels : %d\n", img.mipLevels); + std::printf(" file bytes : %llu\n", static_cast(fsz)); + std::printf(" decoded RGBA bytes: %zu\n", img.data.size()); + return 0; } else if (std::strcmp(argv[i], "--info-jsondbc") == 0 && i + 1 < argc) { // Inspect a JSON DBC sidecar (the JSON output of asset_extract // --emit-json-dbc). Reports recordCount, fieldCount, source