diff --git a/CMakeLists.txt b/CMakeLists.txt index 51298998..62cafdb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -597,6 +597,7 @@ set(WOWEE_SOURCES src/pipeline/wowee_light.cpp src/pipeline/wowee_weather.cpp src/pipeline/wowee_world_map.cpp + src/pipeline/wowee_sound.cpp src/pipeline/custom_zone_discovery.cpp src/pipeline/dbc_layout.cpp @@ -1340,6 +1341,7 @@ add_executable(wowee_editor tools/editor/cli_info_audio.cpp tools/editor/cli_world_info.cpp tools/editor/cli_world_map.cpp + tools/editor/cli_sound_catalog.cpp tools/editor/cli_quest_objective.cpp tools/editor/cli_quest_reward.cpp tools/editor/cli_clone.cpp @@ -1415,6 +1417,7 @@ add_executable(wowee_editor src/pipeline/wowee_light.cpp src/pipeline/wowee_weather.cpp src/pipeline/wowee_world_map.cpp + src/pipeline/wowee_sound.cpp src/pipeline/custom_zone_discovery.cpp src/pipeline/terrain_mesh.cpp diff --git a/include/pipeline/wowee_sound.hpp b/include/pipeline/wowee_sound.hpp new file mode 100644 index 00000000..8da6420a --- /dev/null +++ b/include/pipeline/wowee_sound.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include + +namespace wowee { +namespace pipeline { + +// Wowee Open Sound Catalog (.wsnd) — novel replacement for +// Blizzard's SoundEntries.dbc + SoundEntriesAdvanced.dbc. +// One file holds all sound metadata for a zone or feature: UI +// clicks, ambient loops, spell SFX, voice clips, combat hits. +// The runtime keys lookups by soundId; the catalog name is +// just a label for debugging / tooling. +// +// 3D positional sounds are described by minDistance (the +// radius within which the sound plays at full volume) and +// maxDistance (beyond which it is fully attenuated). Loop +// flag, stream flag, and 3D flag are packed into the flags +// field so a future format extension can reuse other bits. +// +// Binary layout (little-endian): +// magic[4] = "WSND" +// version (uint32) = current 1 +// nameLen (uint32) + name bytes -- catalog label +// entryCount (uint32) +// entries (each): +// soundId (uint32) +// kind (uint8) +// pad[3] +// flags (uint32) +// volume (float) +// minDistance (float) +// maxDistance (float) +// filePathLen (uint32) + filePath bytes +// labelLen (uint32) + label bytes +struct WoweeSound { + enum Kind : uint8_t { + Sfx = 0, + Music = 1, + Ambient = 2, + Ui = 3, + Voice = 4, + Spell = 5, + Combat = 6, + }; + + enum Flags : uint32_t { + Loop = 0x01, + Is3D = 0x02, + Stream = 0x04, + }; + + struct Entry { + uint32_t soundId = 0; + uint8_t kind = Sfx; + uint32_t flags = 0; + float volume = 1.0f; + float minDistance = 5.0f; + float maxDistance = 30.0f; + std::string filePath; + std::string label; + }; + + std::string name; + std::vector entries; + + bool isValid() const { return !entries.empty(); } + + // Lookup by soundId — returns nullptr if not present. + const Entry* findById(uint32_t soundId) const; + + static const char* kindName(uint8_t k); +}; + +class WoweeSoundLoader { +public: + static bool save(const WoweeSound& cat, + const std::string& basePath); + static WoweeSound load(const std::string& basePath); + static bool exists(const std::string& basePath); + + // Preset emitters used by --gen-sound-catalog* variants. + // + // makeStarter — one entry per kind covering the common + // sound categories (sfx / music / ambient + // / ui / voice / spell / combat). Useful + // as a template for hand-edit. + // makeAmbient — wilderness-style: bird-loop ambient + + // wind ambient + footstep variants. + // makeTavern — interior: crackling-fire ambient + crowd + // murmur + drink-clink + door-creak. + static WoweeSound makeStarter(const std::string& catalogName); + static WoweeSound makeAmbient(const std::string& catalogName); + static WoweeSound makeTavern(const std::string& catalogName); +}; + +} // namespace pipeline +} // namespace wowee diff --git a/src/pipeline/wowee_sound.cpp b/src/pipeline/wowee_sound.cpp new file mode 100644 index 00000000..d52b0d9e --- /dev/null +++ b/src/pipeline/wowee_sound.cpp @@ -0,0 +1,186 @@ +#include "pipeline/wowee_sound.hpp" + +#include +#include +#include + +namespace wowee { +namespace pipeline { + +namespace { + +constexpr char kMagic[4] = {'W', 'S', 'N', 'D'}; +constexpr uint32_t kVersion = 1; + +template +void writePOD(std::ofstream& os, const T& v) { + os.write(reinterpret_cast(&v), sizeof(T)); +} + +template +bool readPOD(std::ifstream& is, T& v) { + is.read(reinterpret_cast(&v), sizeof(T)); + return is.gcount() == static_cast(sizeof(T)); +} + +void writeStr(std::ofstream& os, const std::string& s) { + uint32_t n = static_cast(s.size()); + writePOD(os, n); + if (n > 0) os.write(s.data(), n); +} + +bool readStr(std::ifstream& is, std::string& s) { + uint32_t n = 0; + if (!readPOD(is, n)) return false; + if (n > (1u << 20)) return false; // 1 MiB sanity cap + s.resize(n); + if (n > 0) { + is.read(s.data(), n); + if (is.gcount() != static_cast(n)) { + s.clear(); + return false; + } + } + return true; +} + +std::string normalizePath(std::string base) { + if (base.size() < 5 || base.substr(base.size() - 5) != ".wsnd") { + base += ".wsnd"; + } + return base; +} + +} // namespace + +const WoweeSound::Entry* WoweeSound::findById(uint32_t soundId) const { + for (const auto& e : entries) { + if (e.soundId == soundId) return &e; + } + return nullptr; +} + +const char* WoweeSound::kindName(uint8_t k) { + switch (k) { + case Sfx: return "sfx"; + case Music: return "music"; + case Ambient: return "ambient"; + case Ui: return "ui"; + case Voice: return "voice"; + case Spell: return "spell"; + case Combat: return "combat"; + default: return "unknown"; + } +} + +bool WoweeSoundLoader::save(const WoweeSound& cat, + const std::string& basePath) { + std::ofstream os(normalizePath(basePath), std::ios::binary); + if (!os) return false; + os.write(kMagic, 4); + writePOD(os, kVersion); + writeStr(os, cat.name); + uint32_t entryCount = static_cast(cat.entries.size()); + writePOD(os, entryCount); + for (const auto& e : cat.entries) { + writePOD(os, e.soundId); + writePOD(os, e.kind); + uint8_t pad[3] = {0, 0, 0}; + os.write(reinterpret_cast(pad), 3); + writePOD(os, e.flags); + writePOD(os, e.volume); + writePOD(os, e.minDistance); + writePOD(os, e.maxDistance); + writeStr(os, e.filePath); + writeStr(os, e.label); + } + return os.good(); +} + +WoweeSound WoweeSoundLoader::load(const std::string& basePath) { + WoweeSound 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; + if (!readStr(is, out.name)) return out; + uint32_t entryCount = 0; + if (!readPOD(is, entryCount)) return out; + if (entryCount > (1u << 20)) return out; // 1M cap + out.entries.resize(entryCount); + for (auto& e : out.entries) { + if (!readPOD(is, e.soundId)) { out.entries.clear(); return out; } + if (!readPOD(is, e.kind)) { out.entries.clear(); return out; } + uint8_t pad[3]; + is.read(reinterpret_cast(pad), 3); + if (is.gcount() != 3) { out.entries.clear(); return out; } + if (!readPOD(is, e.flags)) { out.entries.clear(); return out; } + if (!readPOD(is, e.volume)) { out.entries.clear(); return out; } + if (!readPOD(is, e.minDistance)) { out.entries.clear(); return out; } + if (!readPOD(is, e.maxDistance)) { out.entries.clear(); return out; } + if (!readStr(is, e.filePath)) { out.entries.clear(); return out; } + if (!readStr(is, e.label)) { out.entries.clear(); return out; } + } + return out; +} + +bool WoweeSoundLoader::exists(const std::string& basePath) { + std::ifstream is(normalizePath(basePath), std::ios::binary); + return is.good(); +} + +WoweeSound WoweeSoundLoader::makeStarter(const std::string& catalogName) { + WoweeSound c; + c.name = catalogName; + c.entries.push_back({1, WoweeSound::Sfx, 0, 1.0f, 5.0f, 30.0f, "Sound/Sfx/footstep_grass.ogg", "Footstep (grass)"}); + c.entries.push_back({2, WoweeSound::Music, WoweeSound::Stream, 0.7f, 0.0f, 0.0f, "Sound/Music/main_theme.ogg", "Main theme"}); + c.entries.push_back({3, WoweeSound::Ambient, + WoweeSound::Loop | WoweeSound::Is3D, 0.5f, 10.0f, 60.0f, "Sound/Ambient/forest_loop.ogg", "Forest ambience"}); + c.entries.push_back({4, WoweeSound::Ui, 0, 1.0f, 0.0f, 0.0f, "Sound/Ui/button_click.ogg", "UI button click"}); + c.entries.push_back({5, WoweeSound::Voice, WoweeSound::Is3D, 1.0f, 3.0f, 20.0f, "Sound/Voice/vendor_greet.ogg", "Vendor greeting"}); + c.entries.push_back({6, WoweeSound::Spell, WoweeSound::Is3D, 0.9f, 4.0f, 40.0f, "Sound/Spell/fireball_cast.ogg", "Fireball cast"}); + c.entries.push_back({7, WoweeSound::Combat, WoweeSound::Is3D, 0.9f, 3.0f, 25.0f, "Sound/Combat/sword_clang.ogg", "Sword clang"}); + return c; +} + +WoweeSound WoweeSoundLoader::makeAmbient(const std::string& catalogName) { + WoweeSound c; + c.name = catalogName; + c.entries.push_back({100, WoweeSound::Ambient, + WoweeSound::Loop | WoweeSound::Is3D, 0.4f, 12.0f, 80.0f, + "Sound/Ambient/birds_loop.ogg", "Birds (loop)"}); + c.entries.push_back({101, WoweeSound::Ambient, + WoweeSound::Loop | WoweeSound::Is3D, 0.3f, 20.0f, 120.0f, + "Sound/Ambient/wind_loop.ogg", "Wind (loop)"}); + c.entries.push_back({102, WoweeSound::Sfx, WoweeSound::Is3D, 0.8f, 4.0f, 25.0f, + "Sound/Sfx/footstep_grass.ogg", "Footstep grass"}); + c.entries.push_back({103, WoweeSound::Sfx, WoweeSound::Is3D, 0.8f, 4.0f, 25.0f, + "Sound/Sfx/footstep_dirt.ogg", "Footstep dirt"}); + c.entries.push_back({104, WoweeSound::Sfx, WoweeSound::Is3D, 0.8f, 4.0f, 25.0f, + "Sound/Sfx/footstep_leaves.ogg", "Footstep leaves"}); + return c; +} + +WoweeSound WoweeSoundLoader::makeTavern(const std::string& catalogName) { + WoweeSound c; + c.name = catalogName; + c.entries.push_back({200, WoweeSound::Ambient, + WoweeSound::Loop | WoweeSound::Is3D, 0.5f, 6.0f, 40.0f, + "Sound/Ambient/fire_crackle.ogg", "Fire crackle (loop)"}); + c.entries.push_back({201, WoweeSound::Ambient, + WoweeSound::Loop, 0.4f, 0.0f, 0.0f, + "Sound/Ambient/crowd_murmur.ogg", "Crowd murmur (loop)"}); + c.entries.push_back({202, WoweeSound::Sfx, WoweeSound::Is3D, 0.9f, 3.0f, 15.0f, + "Sound/Sfx/drink_clink.ogg", "Drink clink"}); + c.entries.push_back({203, WoweeSound::Sfx, WoweeSound::Is3D, 0.7f, 4.0f, 20.0f, + "Sound/Sfx/door_creak.ogg", "Door creak"}); + c.entries.push_back({204, WoweeSound::Music, WoweeSound::Stream, 0.6f, 0.0f, 0.0f, + "Sound/Music/tavern_lute.ogg", "Tavern lute"}); + return c; +} + +} // namespace pipeline +} // namespace wowee diff --git a/tools/editor/cli_arg_required.cpp b/tools/editor/cli_arg_required.cpp index bd4a7fde..943862db 100644 --- a/tools/editor/cli_arg_required.cpp +++ b/tools/editor/cli_arg_required.cpp @@ -25,6 +25,9 @@ const char* const kArgRequired[] = { "--gen-world-map-arena", "--info-womx", "--validate-womx", "--export-womx-json", "--import-womx-json", + "--gen-sound-catalog", "--gen-sound-catalog-ambient", + "--gen-sound-catalog-tavern", + "--info-wsnd", "--validate-wsnd", "--gen-weather-temperate", "--gen-weather-arctic", "--gen-weather-desert", "--gen-weather-stormy", "--gen-zone-atmosphere", diff --git a/tools/editor/cli_dispatch.cpp b/tools/editor/cli_dispatch.cpp index a99c3b3d..8aa8e206 100644 --- a/tools/editor/cli_dispatch.cpp +++ b/tools/editor/cli_dispatch.cpp @@ -37,6 +37,7 @@ #include "cli_info_audio.hpp" #include "cli_world_info.hpp" #include "cli_world_map.hpp" +#include "cli_sound_catalog.hpp" #include "cli_quest_objective.hpp" #include "cli_quest_reward.hpp" #include "cli_clone.hpp" @@ -115,6 +116,7 @@ constexpr DispatchFn kDispatchTable[] = { handleInfoAudio, handleWorldInfo, handleWorldMap, + handleSoundCatalog, handleQuestObjective, handleQuestReward, handleClone, diff --git a/tools/editor/cli_help.cpp b/tools/editor/cli_help.cpp index 1e9df634..e146469c 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -835,6 +835,16 @@ void printUsage(const char* argv0) { std::printf(" Export binary .womx to a human-editable JSON sidecar (rows of '1'/'0' strings, easy to hand-edit)\n"); std::printf(" --import-womx-json [out-base]\n"); std::printf(" Import a .womx.json sidecar back into binary .womx (round-trip with --export-womx-json)\n"); + std::printf(" --gen-sound-catalog [name]\n"); + std::printf(" Emit .wsnd starter catalog (one entry per kind: sfx/music/ambient/ui/voice/spell/combat)\n"); + std::printf(" --gen-sound-catalog-ambient [name]\n"); + std::printf(" Emit .wsnd wilderness catalog: looped birds + wind + 3 footstep variants\n"); + std::printf(" --gen-sound-catalog-tavern [name]\n"); + std::printf(" Emit .wsnd tavern catalog: fire-crackle + crowd murmur + drink-clink + door-creak + lute music\n"); + std::printf(" --info-wsnd [--json]\n"); + std::printf(" Print WSND catalog entries (id / kind / flags / volume / 3D distances / file path / label)\n"); + std::printf(" --validate-wsnd [--json]\n"); + std::printf(" Static checks: kind in 0..6, finite distances, 3D needs max>min>=0, no duplicate sound IDs\n"); std::printf(" --gen-weather-temperate [zoneName]\n"); std::printf(" Emit .wow weather schedule: clear-dominant + occasional rain + fog (forest / grassland)\n"); std::printf(" --gen-weather-arctic [zoneName]\n"); diff --git a/tools/editor/cli_sound_catalog.cpp b/tools/editor/cli_sound_catalog.cpp new file mode 100644 index 00000000..0a1a3912 --- /dev/null +++ b/tools/editor/cli_sound_catalog.cpp @@ -0,0 +1,251 @@ +#include "cli_sound_catalog.hpp" + +#include "pipeline/wowee_sound.hpp" +#include + +#include +#include +#include +#include +#include +#include + +namespace wowee { +namespace editor { +namespace cli { + +namespace { + +std::string stripWsndExt(std::string base) { + if (base.size() >= 5 && base.substr(base.size() - 5) == ".wsnd") + base = base.substr(0, base.size() - 5); + return base; +} + +void appendFlagsStr(std::string& s, uint32_t flags) { + if (flags & wowee::pipeline::WoweeSound::Loop) s += "loop "; + if (flags & wowee::pipeline::WoweeSound::Is3D) s += "3d "; + if (flags & wowee::pipeline::WoweeSound::Stream) s += "stream "; + if (s.empty()) s = "-"; + else if (s.back() == ' ') s.pop_back(); +} + +bool saveOrError(const wowee::pipeline::WoweeSound& c, + const std::string& base, const char* cmd) { + if (!wowee::pipeline::WoweeSoundLoader::save(c, base)) { + std::fprintf(stderr, "%s: failed to save %s.wsnd\n", + cmd, base.c_str()); + return false; + } + return true; +} + +void printGenSummary(const wowee::pipeline::WoweeSound& c, + const std::string& base) { + std::printf("Wrote %s.wsnd\n", base.c_str()); + std::printf(" catalog : %s\n", c.name.c_str()); + std::printf(" entries : %zu\n", c.entries.size()); +} + +int handleGenStarter(int& i, int argc, char** argv) { + std::string base = argv[++i]; + std::string name = "StarterCatalog"; + if (i + 1 < argc && argv[i + 1][0] != '-') name = argv[++i]; + base = stripWsndExt(base); + auto c = wowee::pipeline::WoweeSoundLoader::makeStarter(name); + if (!saveOrError(c, base, "gen-sound-catalog")) return 1; + printGenSummary(c, base); + return 0; +} + +int handleGenAmbient(int& i, int argc, char** argv) { + std::string base = argv[++i]; + std::string name = "AmbientCatalog"; + if (i + 1 < argc && argv[i + 1][0] != '-') name = argv[++i]; + base = stripWsndExt(base); + auto c = wowee::pipeline::WoweeSoundLoader::makeAmbient(name); + if (!saveOrError(c, base, "gen-sound-catalog-ambient")) return 1; + printGenSummary(c, base); + return 0; +} + +int handleGenTavern(int& i, int argc, char** argv) { + std::string base = argv[++i]; + std::string name = "TavernCatalog"; + if (i + 1 < argc && argv[i + 1][0] != '-') name = argv[++i]; + base = stripWsndExt(base); + auto c = wowee::pipeline::WoweeSoundLoader::makeTavern(name); + if (!saveOrError(c, base, "gen-sound-catalog-tavern")) return 1; + printGenSummary(c, 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 = stripWsndExt(base); + if (!wowee::pipeline::WoweeSoundLoader::exists(base)) { + std::fprintf(stderr, "WSND not found: %s.wsnd\n", base.c_str()); + return 1; + } + auto c = wowee::pipeline::WoweeSoundLoader::load(base); + if (jsonOut) { + nlohmann::json j; + j["wsnd"] = base + ".wsnd"; + j["name"] = c.name; + j["count"] = c.entries.size(); + nlohmann::json arr = nlohmann::json::array(); + for (const auto& e : c.entries) { + std::string fs; + appendFlagsStr(fs, e.flags); + arr.push_back({ + {"soundId", e.soundId}, + {"kind", e.kind}, + {"kindName", wowee::pipeline::WoweeSound::kindName(e.kind)}, + {"flags", e.flags}, + {"flagsStr", fs}, + {"volume", e.volume}, + {"minDistance", e.minDistance}, + {"maxDistance", e.maxDistance}, + {"filePath", e.filePath}, + {"label", e.label}, + }); + } + j["entries"] = arr; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("WSND: %s.wsnd\n", base.c_str()); + std::printf(" catalog : %s\n", c.name.c_str()); + std::printf(" entries : %zu\n", c.entries.size()); + if (c.entries.empty()) return 0; + std::printf(" id kind flags vol min max label / file\n"); + for (const auto& e : c.entries) { + std::string fs; + appendFlagsStr(fs, e.flags); + std::printf(" %4u %-7s %-12s %4.2f %5.1f %5.1f %s\n", + e.soundId, + wowee::pipeline::WoweeSound::kindName(e.kind), + fs.c_str(), + e.volume, e.minDistance, e.maxDistance, + e.label.empty() ? e.filePath.c_str() + : (e.label + " (" + e.filePath + ")").c_str()); + } + 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 = stripWsndExt(base); + if (!wowee::pipeline::WoweeSoundLoader::exists(base)) { + std::fprintf(stderr, + "validate-wsnd: WSND not found: %s.wsnd\n", base.c_str()); + return 1; + } + auto c = wowee::pipeline::WoweeSoundLoader::load(base); + std::vector errors; + std::vector warnings; + if (c.entries.empty()) { + warnings.push_back("catalog has zero entries"); + } + // Per-entry checks plus a duplicate-soundId scan. + std::vector idsSeen; + idsSeen.reserve(c.entries.size()); + for (size_t k = 0; k < c.entries.size(); ++k) { + const auto& e = c.entries[k]; + std::string ctx = "entry " + std::to_string(k) + + " (id=" + std::to_string(e.soundId) + ")"; + if (e.kind > wowee::pipeline::WoweeSound::Combat) { + errors.push_back(ctx + ": kind " + std::to_string(e.kind) + + " not in known range 0..6"); + } + // Reject NaN/inf early — these crash audio engines. + if (!std::isfinite(e.volume) || + !std::isfinite(e.minDistance) || + !std::isfinite(e.maxDistance)) { + errors.push_back(ctx + ": volume/min/max distance not finite"); + } + if (e.volume < 0.0f || e.volume > 4.0f) { + warnings.push_back(ctx + ": volume " + + std::to_string(e.volume) + + " outside typical 0..4 range"); + } + // 3D sounds need min < max; non-3D sounds usually have + // both at 0 (positional fields ignored at runtime). + if (e.flags & wowee::pipeline::WoweeSound::Is3D) { + if (e.minDistance < 0 || e.maxDistance <= e.minDistance) { + errors.push_back(ctx + + ": 3d sound needs minDistance >= 0 and " + "maxDistance > minDistance"); + } + } + if (e.filePath.empty()) { + errors.push_back(ctx + ": filePath is empty"); + } + for (uint32_t prev : idsSeen) { + if (prev == e.soundId) { + errors.push_back(ctx + + ": soundId already used by an earlier entry"); + break; + } + } + idsSeen.push_back(e.soundId); + } + bool ok = errors.empty(); + if (jsonOut) { + nlohmann::json j; + j["wsnd"] = base + ".wsnd"; + 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-wsnd: %s.wsnd\n", base.c_str()); + if (ok && warnings.empty()) { + std::printf(" OK — %zu entries, all sound IDs unique\n", + c.entries.size()); + 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 handleSoundCatalog(int& i, int argc, char** argv, int& outRc) { + if (std::strcmp(argv[i], "--gen-sound-catalog") == 0 && i + 1 < argc) { + outRc = handleGenStarter(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--gen-sound-catalog-ambient") == 0 && i + 1 < argc) { + outRc = handleGenAmbient(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--gen-sound-catalog-tavern") == 0 && i + 1 < argc) { + outRc = handleGenTavern(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--info-wsnd") == 0 && i + 1 < argc) { + outRc = handleInfo(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--validate-wsnd") == 0 && i + 1 < argc) { + outRc = handleValidate(i, argc, argv); return true; + } + return false; +} + +} // namespace cli +} // namespace editor +} // namespace wowee diff --git a/tools/editor/cli_sound_catalog.hpp b/tools/editor/cli_sound_catalog.hpp new file mode 100644 index 00000000..dad3ada3 --- /dev/null +++ b/tools/editor/cli_sound_catalog.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace wowee { +namespace editor { +namespace cli { + +bool handleSoundCatalog(int& i, int argc, char** argv, int& outRc); + +} // namespace cli +} // namespace editor +} // namespace wowee