mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 19:13:52 +00:00
feat(pipeline): add WOMX (Wowee World Map index) format
Novel open replacement for Blizzard's WDT (top-level world
definition table). The 9th open format added to the editor.
A WOMX file holds the manifest of which terrain tiles exist
within a world plus a tiny bit of map-level metadata. The
runtime consults it before attempting to load any individual
tile (so missing tiles produce a clean "no data" result
instead of a file-not-found error).
Format:
• magic "WMPX", version 1, little-endian
• mapName + worldType (continent/instance/battleground/arena)
• gridSize 1..128 (typically 64 for continents)
• defaultLightId / defaultWeatherId (atmosphere preset
refs, 0 if none — wires into the WOL/WOW pair)
• packed bitmap, 1 bit per tile, row-major
• A 64x64 manifest is exactly 512 bytes of bitmap
API: WoweeWorldMapLoader::save / load / exists; presets
makeContinent (64x64 full), makeInstance (4x4 full),
makeArena (1x1 full).
CLI added (5 flags, 456 total now):
--gen-world-map <base> [name] (continent)
--gen-world-map-instance <base> [name] (4x4)
--gen-world-map-arena <base> [name] (1x1)
--info-womx <base> [--json]
--validate-womx <base> [--json]
Round-trip verified: continent + instance + arena presets
all save / load / re-validate to byte-identical state with
correct tile counts.
This commit is contained in:
parent
6c3f5cb33f
commit
db47f00657
8 changed files with 527 additions and 0 deletions
|
|
@ -596,6 +596,7 @@ set(WOWEE_SOURCES
|
|||
src/pipeline/wowee_collision.cpp
|
||||
src/pipeline/wowee_light.cpp
|
||||
src/pipeline/wowee_weather.cpp
|
||||
src/pipeline/wowee_world_map.cpp
|
||||
src/pipeline/custom_zone_discovery.cpp
|
||||
src/pipeline/dbc_layout.cpp
|
||||
|
||||
|
|
@ -1338,6 +1339,7 @@ add_executable(wowee_editor
|
|||
tools/editor/cli_info_density.cpp
|
||||
tools/editor/cli_info_audio.cpp
|
||||
tools/editor/cli_world_info.cpp
|
||||
tools/editor/cli_world_map.cpp
|
||||
tools/editor/cli_quest_objective.cpp
|
||||
tools/editor/cli_quest_reward.cpp
|
||||
tools/editor/cli_clone.cpp
|
||||
|
|
@ -1412,6 +1414,7 @@ add_executable(wowee_editor
|
|||
src/pipeline/wowee_collision.cpp
|
||||
src/pipeline/wowee_light.cpp
|
||||
src/pipeline/wowee_weather.cpp
|
||||
src/pipeline/wowee_world_map.cpp
|
||||
src/pipeline/custom_zone_discovery.cpp
|
||||
src/pipeline/terrain_mesh.cpp
|
||||
|
||||
|
|
|
|||
82
include/pipeline/wowee_world_map.hpp
Normal file
82
include/pipeline/wowee_world_map.hpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
// Wowee Open World Map index (.womx) — novel replacement for
|
||||
// Blizzard's WDT (top-level world definition table). A WOMX file
|
||||
// holds the manifest of which terrain tiles exist within a world,
|
||||
// plus a tiny bit of map-level metadata. The runtime consults it
|
||||
// before attempting to load any individual tile (so missing tiles
|
||||
// produce a clean "no data" instead of a file-not-found error).
|
||||
//
|
||||
// The grid is a square of size N×N where N is typically 64 (the
|
||||
// historical WoW value), but the format permits any N up to 128.
|
||||
// Tile presence is stored as a packed bitmap (1 bit per tile,
|
||||
// row-major), so a 64×64 manifest is only 512 bytes.
|
||||
//
|
||||
// Binary layout (little-endian):
|
||||
// magic[4] = "WMPX"
|
||||
// version (uint32) = current 1
|
||||
// nameLen (uint32) + name bytes
|
||||
// worldType (uint8) = 0=continent, 1=instance, 2=battleground, 3=arena
|
||||
// gridSize (uint8) = N (1..128)
|
||||
// pad[2]
|
||||
// defaultLightId (uint32) -- 0 if no atmosphere preset
|
||||
// defaultWeatherId (uint32) -- 0 if no atmosphere preset
|
||||
// reserved[3] (uint32 each)
|
||||
// bitmapBytes (uint32) = ceil(N*N/8)
|
||||
// bitmap (bitmapBytes)
|
||||
struct WoweeWorldMap {
|
||||
enum WorldType : uint8_t {
|
||||
Continent = 0,
|
||||
Instance = 1,
|
||||
Battleground = 2,
|
||||
Arena = 3,
|
||||
};
|
||||
|
||||
std::string name;
|
||||
uint8_t worldType = Continent;
|
||||
uint8_t gridSize = 64;
|
||||
uint32_t defaultLightId = 0;
|
||||
uint32_t defaultWeatherId = 0;
|
||||
|
||||
// Packed row-major bitmap: bit (y * gridSize + x) is set
|
||||
// when a tile exists at column x, row y.
|
||||
std::vector<uint8_t> tileBitmap;
|
||||
|
||||
bool isValid() const { return gridSize > 0 && gridSize <= 128; }
|
||||
|
||||
bool hasTile(uint32_t x, uint32_t y) const;
|
||||
void setTile(uint32_t x, uint32_t y, bool present);
|
||||
|
||||
// Count of set bits in the bitmap (= number of present tiles).
|
||||
uint32_t countTiles() const;
|
||||
|
||||
static const char* worldTypeName(uint8_t t);
|
||||
};
|
||||
|
||||
class WoweeWorldMapLoader {
|
||||
public:
|
||||
static bool save(const WoweeWorldMap& map,
|
||||
const std::string& basePath);
|
||||
static WoweeWorldMap load(const std::string& basePath);
|
||||
static bool exists(const std::string& basePath);
|
||||
|
||||
// Preset emitters used by --gen-world-map* variants.
|
||||
//
|
||||
// makeContinent — full 64×64 grid with all tiles present
|
||||
// (continent-style world map, ~4.3 km²)
|
||||
// makeInstance — small 4×4 grid for dungeon-scale worlds
|
||||
// makeArena — 1×1 single-tile arena
|
||||
static WoweeWorldMap makeContinent(const std::string& mapName);
|
||||
static WoweeWorldMap makeInstance(const std::string& mapName);
|
||||
static WoweeWorldMap makeArena(const std::string& mapName);
|
||||
};
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
207
src/pipeline/wowee_world_map.cpp
Normal file
207
src/pipeline/wowee_world_map.cpp
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
#include "pipeline/wowee_world_map.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kMagic[4] = {'W', 'M', 'P', 'X'};
|
||||
constexpr uint32_t kVersion = 1;
|
||||
|
||||
template <typename T>
|
||||
void writePOD(std::ofstream& os, const T& v) {
|
||||
os.write(reinterpret_cast<const char*>(&v), sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool readPOD(std::ifstream& is, T& v) {
|
||||
is.read(reinterpret_cast<char*>(&v), sizeof(T));
|
||||
return is.gcount() == static_cast<std::streamsize>(sizeof(T));
|
||||
}
|
||||
|
||||
std::string normalizePath(std::string base) {
|
||||
if (base.size() < 5 || base.substr(base.size() - 5) != ".womx") {
|
||||
base += ".womx";
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
size_t bitmapBytesFor(uint32_t gridSize) {
|
||||
return (static_cast<size_t>(gridSize) * gridSize + 7) / 8;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WoweeWorldMap::hasTile(uint32_t x, uint32_t y) const {
|
||||
if (x >= gridSize || y >= gridSize) return false;
|
||||
size_t bit = static_cast<size_t>(y) * gridSize + x;
|
||||
size_t byte = bit / 8;
|
||||
if (byte >= tileBitmap.size()) return false;
|
||||
return (tileBitmap[byte] >> (bit & 7)) & 1;
|
||||
}
|
||||
|
||||
void WoweeWorldMap::setTile(uint32_t x, uint32_t y, bool present) {
|
||||
if (x >= gridSize || y >= gridSize) return;
|
||||
size_t bit = static_cast<size_t>(y) * gridSize + x;
|
||||
size_t byte = bit / 8;
|
||||
if (tileBitmap.size() <= byte) tileBitmap.resize(byte + 1, 0);
|
||||
uint8_t mask = static_cast<uint8_t>(1u << (bit & 7));
|
||||
if (present) tileBitmap[byte] |= mask;
|
||||
else tileBitmap[byte] &= static_cast<uint8_t>(~mask);
|
||||
}
|
||||
|
||||
uint32_t WoweeWorldMap::countTiles() const {
|
||||
uint32_t n = 0;
|
||||
size_t totalBits = static_cast<size_t>(gridSize) * gridSize;
|
||||
for (size_t bit = 0; bit < totalBits; ++bit) {
|
||||
size_t byte = bit / 8;
|
||||
if (byte >= tileBitmap.size()) break;
|
||||
if ((tileBitmap[byte] >> (bit & 7)) & 1) n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
const char* WoweeWorldMap::worldTypeName(uint8_t t) {
|
||||
switch (t) {
|
||||
case Continent: return "continent";
|
||||
case Instance: return "instance";
|
||||
case Battleground: return "battleground";
|
||||
case Arena: return "arena";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool WoweeWorldMapLoader::save(const WoweeWorldMap& m,
|
||||
const std::string& basePath) {
|
||||
if (m.gridSize == 0 || m.gridSize > 128) return false;
|
||||
std::ofstream os(normalizePath(basePath), std::ios::binary);
|
||||
if (!os) return false;
|
||||
os.write(kMagic, 4);
|
||||
writePOD(os, kVersion);
|
||||
uint32_t nameLen = static_cast<uint32_t>(m.name.size());
|
||||
writePOD(os, nameLen);
|
||||
if (nameLen > 0) os.write(m.name.data(), nameLen);
|
||||
writePOD(os, m.worldType);
|
||||
writePOD(os, m.gridSize);
|
||||
uint16_t pad = 0;
|
||||
writePOD(os, pad);
|
||||
writePOD(os, m.defaultLightId);
|
||||
writePOD(os, m.defaultWeatherId);
|
||||
uint32_t reserved = 0;
|
||||
writePOD(os, reserved);
|
||||
writePOD(os, reserved);
|
||||
writePOD(os, reserved);
|
||||
size_t expectedBytes = bitmapBytesFor(m.gridSize);
|
||||
uint32_t bitmapBytes = static_cast<uint32_t>(expectedBytes);
|
||||
writePOD(os, bitmapBytes);
|
||||
// Pad bitmap up to expectedBytes if caller under-sized it
|
||||
// (setTile may not have grown it to the full grid coverage).
|
||||
if (m.tileBitmap.size() >= expectedBytes) {
|
||||
os.write(reinterpret_cast<const char*>(m.tileBitmap.data()),
|
||||
expectedBytes);
|
||||
} else {
|
||||
if (!m.tileBitmap.empty()) {
|
||||
os.write(reinterpret_cast<const char*>(m.tileBitmap.data()),
|
||||
m.tileBitmap.size());
|
||||
}
|
||||
std::vector<uint8_t> tail(expectedBytes - m.tileBitmap.size(), 0);
|
||||
os.write(reinterpret_cast<const char*>(tail.data()), tail.size());
|
||||
}
|
||||
return os.good();
|
||||
}
|
||||
|
||||
WoweeWorldMap WoweeWorldMapLoader::load(const std::string& basePath) {
|
||||
WoweeWorldMap out;
|
||||
std::ifstream is(normalizePath(basePath), std::ios::binary);
|
||||
if (!is) return out;
|
||||
char magic[4];
|
||||
is.read(magic, 4);
|
||||
if (std::memcmp(magic, kMagic, 4) != 0) return out;
|
||||
uint32_t version = 0;
|
||||
if (!readPOD(is, version) || version != kVersion) return out;
|
||||
uint32_t nameLen = 0;
|
||||
if (!readPOD(is, nameLen)) return out;
|
||||
if (nameLen > 0) {
|
||||
out.name.resize(nameLen);
|
||||
is.read(out.name.data(), nameLen);
|
||||
if (is.gcount() != static_cast<std::streamsize>(nameLen)) {
|
||||
out.name.clear();
|
||||
return out;
|
||||
}
|
||||
}
|
||||
if (!readPOD(is, out.worldType)) return out;
|
||||
if (!readPOD(is, out.gridSize)) return out;
|
||||
uint16_t pad = 0;
|
||||
if (!readPOD(is, pad)) return out;
|
||||
if (!readPOD(is, out.defaultLightId)) return out;
|
||||
if (!readPOD(is, out.defaultWeatherId)) return out;
|
||||
uint32_t reserved = 0;
|
||||
if (!readPOD(is, reserved)) return out;
|
||||
if (!readPOD(is, reserved)) return out;
|
||||
if (!readPOD(is, reserved)) return out;
|
||||
uint32_t bitmapBytes = 0;
|
||||
if (!readPOD(is, bitmapBytes)) return out;
|
||||
// Cap to a sane upper bound in case the file is corrupted —
|
||||
// a 128×128 grid is 2048 bytes, so anything > 4 KiB is a sign
|
||||
// of trouble.
|
||||
if (bitmapBytes > 4096) {
|
||||
out.gridSize = 0;
|
||||
return out;
|
||||
}
|
||||
out.tileBitmap.resize(bitmapBytes);
|
||||
if (bitmapBytes > 0) {
|
||||
is.read(reinterpret_cast<char*>(out.tileBitmap.data()), bitmapBytes);
|
||||
if (is.gcount() != static_cast<std::streamsize>(bitmapBytes)) {
|
||||
out.tileBitmap.clear();
|
||||
out.gridSize = 0;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool WoweeWorldMapLoader::exists(const std::string& basePath) {
|
||||
std::ifstream is(normalizePath(basePath), std::ios::binary);
|
||||
return is.good();
|
||||
}
|
||||
|
||||
WoweeWorldMap WoweeWorldMapLoader::makeContinent(const std::string& mapName) {
|
||||
WoweeWorldMap m;
|
||||
m.name = mapName;
|
||||
m.worldType = WoweeWorldMap::Continent;
|
||||
m.gridSize = 64;
|
||||
m.tileBitmap.assign(bitmapBytesFor(64), 0xFF);
|
||||
// Last byte may have spare bits past 64*64 — but 64*64 is
|
||||
// a multiple of 8 (4096), so this is exact and no masking
|
||||
// is needed.
|
||||
return m;
|
||||
}
|
||||
|
||||
WoweeWorldMap WoweeWorldMapLoader::makeInstance(const std::string& mapName) {
|
||||
WoweeWorldMap m;
|
||||
m.name = mapName;
|
||||
m.worldType = WoweeWorldMap::Instance;
|
||||
m.gridSize = 4;
|
||||
m.tileBitmap.assign(bitmapBytesFor(4), 0);
|
||||
for (uint32_t y = 0; y < 4; ++y)
|
||||
for (uint32_t x = 0; x < 4; ++x)
|
||||
m.setTile(x, y, true);
|
||||
return m;
|
||||
}
|
||||
|
||||
WoweeWorldMap WoweeWorldMapLoader::makeArena(const std::string& mapName) {
|
||||
WoweeWorldMap m;
|
||||
m.name = mapName;
|
||||
m.worldType = WoweeWorldMap::Arena;
|
||||
m.gridSize = 1;
|
||||
m.tileBitmap.assign(bitmapBytesFor(1), 0);
|
||||
m.setTile(0, 0, true);
|
||||
return m;
|
||||
}
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
|
|
@ -21,6 +21,9 @@ const char* const kArgRequired[] = {
|
|||
"--export-wow-json", "--import-wow-json",
|
||||
"--info-wow", "--validate-wow",
|
||||
"--validate-wom",
|
||||
"--gen-world-map", "--gen-world-map-instance",
|
||||
"--gen-world-map-arena",
|
||||
"--info-womx", "--validate-womx",
|
||||
"--gen-weather-temperate", "--gen-weather-arctic",
|
||||
"--gen-weather-desert", "--gen-weather-stormy",
|
||||
"--gen-zone-atmosphere",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include "cli_info_density.hpp"
|
||||
#include "cli_info_audio.hpp"
|
||||
#include "cli_world_info.hpp"
|
||||
#include "cli_world_map.hpp"
|
||||
#include "cli_quest_objective.hpp"
|
||||
#include "cli_quest_reward.hpp"
|
||||
#include "cli_clone.hpp"
|
||||
|
|
@ -113,6 +114,7 @@ constexpr DispatchFn kDispatchTable[] = {
|
|||
handleInfoDensity,
|
||||
handleInfoAudio,
|
||||
handleWorldInfo,
|
||||
handleWorldMap,
|
||||
handleQuestObjective,
|
||||
handleQuestReward,
|
||||
handleClone,
|
||||
|
|
|
|||
|
|
@ -817,6 +817,16 @@ void printUsage(const char* argv0) {
|
|||
std::printf(" Walk every WOW entry; check typeId / intensity bounds [0,1] / weight > 0 / duration min ≤ max\n");
|
||||
std::printf(" --validate-wom <wom-base> [--json]\n");
|
||||
std::printf(" Static sanity checks on .wom: index range, bone refs, bound box, batch coverage, animation track count\n");
|
||||
std::printf(" --gen-world-map <womx-base> [mapName]\n");
|
||||
std::printf(" Emit .womx world-tile manifest: 64x64 continent grid with all tiles present (open WDT replacement)\n");
|
||||
std::printf(" --gen-world-map-instance <womx-base> [mapName]\n");
|
||||
std::printf(" Emit .womx world-tile manifest: 4x4 instance grid (small-world / dungeon scale)\n");
|
||||
std::printf(" --gen-world-map-arena <womx-base> [mapName]\n");
|
||||
std::printf(" Emit .womx world-tile manifest: 1x1 single-tile arena (smallest valid world)\n");
|
||||
std::printf(" --info-womx <womx-base> [--json]\n");
|
||||
std::printf(" Print WOMX manifest (worldType / gridSize / tilesPresent / defaultLightId / defaultWeatherId)\n");
|
||||
std::printf(" --validate-womx <womx-base> [--json]\n");
|
||||
std::printf(" Static checks on .womx: gridSize 1..128, worldType in range, tileBitmap matches expected size\n");
|
||||
std::printf(" --gen-weather-temperate <wow-base> [zoneName]\n");
|
||||
std::printf(" Emit .wow weather schedule: clear-dominant + occasional rain + fog (forest / grassland)\n");
|
||||
std::printf(" --gen-weather-arctic <wow-base> [zoneName]\n");
|
||||
|
|
|
|||
209
tools/editor/cli_world_map.cpp
Normal file
209
tools/editor/cli_world_map.cpp
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
#include "cli_world_map.hpp"
|
||||
|
||||
#include "pipeline/wowee_world_map.hpp"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace wowee {
|
||||
namespace editor {
|
||||
namespace cli {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string stripWomxExt(std::string base) {
|
||||
if (base.size() >= 5 && base.substr(base.size() - 5) == ".womx")
|
||||
base = base.substr(0, base.size() - 5);
|
||||
return base;
|
||||
}
|
||||
|
||||
bool saveOrError(const wowee::pipeline::WoweeWorldMap& m,
|
||||
const std::string& base, const char* cmd) {
|
||||
if (!wowee::pipeline::WoweeWorldMapLoader::save(m, base)) {
|
||||
std::fprintf(stderr, "%s: failed to save %s.womx\n",
|
||||
cmd, base.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void printGenSummary(const wowee::pipeline::WoweeWorldMap& m,
|
||||
const std::string& base) {
|
||||
std::printf("Wrote %s.womx\n", base.c_str());
|
||||
std::printf(" name : %s\n", m.name.c_str());
|
||||
std::printf(" worldType : %u (%s)\n", m.worldType,
|
||||
wowee::pipeline::WoweeWorldMap::worldTypeName(m.worldType));
|
||||
std::printf(" gridSize : %ux%u tiles\n", m.gridSize, m.gridSize);
|
||||
std::printf(" tilesPresent: %u / %u\n",
|
||||
m.countTiles(),
|
||||
static_cast<uint32_t>(m.gridSize) * m.gridSize);
|
||||
}
|
||||
|
||||
int handleGenContinent(int& i, int argc, char** argv) {
|
||||
std::string base = argv[++i];
|
||||
std::string mapName = "Continent";
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') mapName = argv[++i];
|
||||
base = stripWomxExt(base);
|
||||
auto m = wowee::pipeline::WoweeWorldMapLoader::makeContinent(mapName);
|
||||
if (!saveOrError(m, base, "gen-world-map")) return 1;
|
||||
printGenSummary(m, base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleGenInstance(int& i, int argc, char** argv) {
|
||||
std::string base = argv[++i];
|
||||
std::string mapName = "Instance";
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') mapName = argv[++i];
|
||||
base = stripWomxExt(base);
|
||||
auto m = wowee::pipeline::WoweeWorldMapLoader::makeInstance(mapName);
|
||||
if (!saveOrError(m, base, "gen-world-map-instance")) return 1;
|
||||
printGenSummary(m, base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleGenArena(int& i, int argc, char** argv) {
|
||||
std::string base = argv[++i];
|
||||
std::string mapName = "Arena";
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') mapName = argv[++i];
|
||||
base = stripWomxExt(base);
|
||||
auto m = wowee::pipeline::WoweeWorldMapLoader::makeArena(mapName);
|
||||
if (!saveOrError(m, base, "gen-world-map-arena")) return 1;
|
||||
printGenSummary(m, base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleInfo(int& i, int argc, char** argv) {
|
||||
std::string base = argv[++i];
|
||||
bool jsonOut = (i + 1 < argc &&
|
||||
std::strcmp(argv[i + 1], "--json") == 0);
|
||||
if (jsonOut) i++;
|
||||
base = stripWomxExt(base);
|
||||
if (!wowee::pipeline::WoweeWorldMapLoader::exists(base)) {
|
||||
std::fprintf(stderr, "WOMX not found: %s.womx\n", base.c_str());
|
||||
return 1;
|
||||
}
|
||||
auto m = wowee::pipeline::WoweeWorldMapLoader::load(base);
|
||||
uint32_t total = static_cast<uint32_t>(m.gridSize) * m.gridSize;
|
||||
uint32_t present = m.countTiles();
|
||||
if (jsonOut) {
|
||||
nlohmann::json j;
|
||||
j["womx"] = base + ".womx";
|
||||
j["name"] = m.name;
|
||||
j["worldType"] = m.worldType;
|
||||
j["worldTypeName"] =
|
||||
wowee::pipeline::WoweeWorldMap::worldTypeName(m.worldType);
|
||||
j["gridSize"] = m.gridSize;
|
||||
j["totalTiles"] = total;
|
||||
j["tilesPresent"] = present;
|
||||
j["defaultLightId"] = m.defaultLightId;
|
||||
j["defaultWeatherId"] = m.defaultWeatherId;
|
||||
std::printf("%s\n", j.dump(2).c_str());
|
||||
return 0;
|
||||
}
|
||||
std::printf("WOMX: %s.womx\n", base.c_str());
|
||||
std::printf(" name : %s\n", m.name.c_str());
|
||||
std::printf(" worldType : %u (%s)\n", m.worldType,
|
||||
wowee::pipeline::WoweeWorldMap::worldTypeName(m.worldType));
|
||||
std::printf(" gridSize : %ux%u (%u total tiles)\n",
|
||||
m.gridSize, m.gridSize, total);
|
||||
std::printf(" tilesPresent : %u (%.1f%%)\n",
|
||||
present,
|
||||
total ? (100.0f * present / total) : 0.0f);
|
||||
std::printf(" defaultLightId : %u\n", m.defaultLightId);
|
||||
std::printf(" defaultWeatherId : %u\n", m.defaultWeatherId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleValidate(int& i, int argc, char** argv) {
|
||||
std::string base = argv[++i];
|
||||
bool jsonOut = (i + 1 < argc &&
|
||||
std::strcmp(argv[i + 1], "--json") == 0);
|
||||
if (jsonOut) i++;
|
||||
base = stripWomxExt(base);
|
||||
if (!wowee::pipeline::WoweeWorldMapLoader::exists(base)) {
|
||||
std::fprintf(stderr, "validate-womx: WOMX not found: %s.womx\n",
|
||||
base.c_str());
|
||||
return 1;
|
||||
}
|
||||
auto m = wowee::pipeline::WoweeWorldMapLoader::load(base);
|
||||
std::vector<std::string> errors;
|
||||
std::vector<std::string> warnings;
|
||||
if (m.gridSize == 0 || m.gridSize > 128) {
|
||||
errors.push_back("gridSize " + std::to_string(m.gridSize) +
|
||||
" not in range 1..128");
|
||||
}
|
||||
if (m.worldType > wowee::pipeline::WoweeWorldMap::Arena) {
|
||||
errors.push_back("worldType " + std::to_string(m.worldType) +
|
||||
" not in known range 0..3");
|
||||
}
|
||||
size_t expectedBytes =
|
||||
(static_cast<size_t>(m.gridSize) * m.gridSize + 7) / 8;
|
||||
if (m.tileBitmap.size() != expectedBytes) {
|
||||
errors.push_back("tileBitmap size " +
|
||||
std::to_string(m.tileBitmap.size()) +
|
||||
" != expected " + std::to_string(expectedBytes) +
|
||||
" for grid " + std::to_string(m.gridSize) + "x" +
|
||||
std::to_string(m.gridSize));
|
||||
}
|
||||
if (m.countTiles() == 0) {
|
||||
warnings.push_back("no tiles present in bitmap (empty world)");
|
||||
}
|
||||
bool ok = errors.empty();
|
||||
if (jsonOut) {
|
||||
nlohmann::json j;
|
||||
j["womx"] = base + ".womx";
|
||||
j["ok"] = ok;
|
||||
j["errors"] = errors;
|
||||
j["warnings"] = warnings;
|
||||
std::printf("%s\n", j.dump(2).c_str());
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
std::printf("validate-womx: %s.womx\n", base.c_str());
|
||||
if (ok && warnings.empty()) {
|
||||
std::printf(" OK — %ux%u grid, %u/%u tiles present\n",
|
||||
m.gridSize, m.gridSize,
|
||||
m.countTiles(),
|
||||
static_cast<uint32_t>(m.gridSize) * m.gridSize);
|
||||
return 0;
|
||||
}
|
||||
if (!warnings.empty()) {
|
||||
std::printf(" warnings (%zu):\n", warnings.size());
|
||||
for (const auto& w : warnings)
|
||||
std::printf(" - %s\n", w.c_str());
|
||||
}
|
||||
if (!errors.empty()) {
|
||||
std::printf(" ERRORS (%zu):\n", errors.size());
|
||||
for (const auto& e : errors)
|
||||
std::printf(" - %s\n", e.c_str());
|
||||
}
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool handleWorldMap(int& i, int argc, char** argv, int& outRc) {
|
||||
if (std::strcmp(argv[i], "--gen-world-map") == 0 && i + 1 < argc) {
|
||||
outRc = handleGenContinent(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--gen-world-map-instance") == 0 && i + 1 < argc) {
|
||||
outRc = handleGenInstance(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--gen-world-map-arena") == 0 && i + 1 < argc) {
|
||||
outRc = handleGenArena(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--info-womx") == 0 && i + 1 < argc) {
|
||||
outRc = handleInfo(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--validate-womx") == 0 && i + 1 < argc) {
|
||||
outRc = handleValidate(i, argc, argv); return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace cli
|
||||
} // namespace editor
|
||||
} // namespace wowee
|
||||
11
tools/editor/cli_world_map.hpp
Normal file
11
tools/editor/cli_world_map.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
namespace wowee {
|
||||
namespace editor {
|
||||
namespace cli {
|
||||
|
||||
bool handleWorldMap(int& i, int argc, char** argv, int& outRc);
|
||||
|
||||
} // namespace cli
|
||||
} // namespace editor
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue