mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 16:03:52 +00:00
Merge branch 'master' of https://github.com/Kelsidavis/WoWee
This commit is contained in:
commit
0c188b55bc
35 changed files with 24925 additions and 148 deletions
24
.github/workflows/build.yml
vendored
24
.github/workflows/build.yml
vendored
|
|
@ -46,6 +46,9 @@ jobs:
|
|||
libglm-dev \
|
||||
libssl-dev \
|
||||
zlib1g-dev \
|
||||
libvulkan-dev \
|
||||
vulkan-tools \
|
||||
glslc \
|
||||
libavformat-dev \
|
||||
libavcodec-dev \
|
||||
libswscale-dev \
|
||||
|
|
@ -89,14 +92,14 @@ jobs:
|
|||
- name: Install dependencies
|
||||
run: |
|
||||
brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg unicorn \
|
||||
stormlib dylibbundler || true
|
||||
stormlib vulkan-loader vulkan-headers shaderc dylibbundler || true
|
||||
# dylibbundler may not be in all brew mirrors; install separately to not block others
|
||||
brew install dylibbundler 2>/dev/null || true
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
BREW=$(brew --prefix)
|
||||
export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig"
|
||||
export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix vulkan-loader)/lib/pkgconfig:$(brew --prefix shaderc)/lib/pkgconfig"
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH="$BREW" \
|
||||
|
|
@ -170,7 +173,7 @@ jobs:
|
|||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: CLANGARM64
|
||||
update: false
|
||||
update: true
|
||||
install: >-
|
||||
mingw-w64-clang-aarch64-cmake
|
||||
mingw-w64-clang-aarch64-clang
|
||||
|
|
@ -182,8 +185,16 @@ jobs:
|
|||
mingw-w64-clang-aarch64-openssl
|
||||
mingw-w64-clang-aarch64-zlib
|
||||
mingw-w64-clang-aarch64-ffmpeg
|
||||
mingw-w64-clang-aarch64-unicorn
|
||||
mingw-w64-clang-aarch64-vulkan-loader
|
||||
mingw-w64-clang-aarch64-vulkan-headers
|
||||
mingw-w64-clang-aarch64-shaderc
|
||||
git
|
||||
|
||||
- name: Install optional packages
|
||||
shell: msys2 {0}
|
||||
run: pacman -S --noconfirm --needed mingw-w64-clang-aarch64-stormlib || true
|
||||
|
||||
- name: Configure
|
||||
shell: msys2 {0}
|
||||
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||
|
|
@ -237,9 +248,16 @@ jobs:
|
|||
mingw-w64-x86_64-zlib
|
||||
mingw-w64-x86_64-ffmpeg
|
||||
mingw-w64-x86_64-unicorn
|
||||
mingw-w64-x86_64-vulkan-loader
|
||||
mingw-w64-x86_64-vulkan-headers
|
||||
mingw-w64-x86_64-shaderc
|
||||
mingw-w64-x86_64-nsis
|
||||
git
|
||||
|
||||
- name: Install optional packages
|
||||
shell: msys2 {0}
|
||||
run: pacman -S --noconfirm --needed mingw-w64-x86_64-stormlib || true
|
||||
|
||||
- name: Configure
|
||||
shell: msys2 {0}
|
||||
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||
|
|
|
|||
6
.github/workflows/security.yml
vendored
6
.github/workflows/security.yml
vendored
|
|
@ -36,6 +36,9 @@ jobs:
|
|||
libglm-dev \
|
||||
libssl-dev \
|
||||
zlib1g-dev \
|
||||
libvulkan-dev \
|
||||
vulkan-tools \
|
||||
glslc \
|
||||
libavformat-dev \
|
||||
libavcodec-dev \
|
||||
libswscale-dev \
|
||||
|
|
@ -105,6 +108,9 @@ jobs:
|
|||
libglm-dev \
|
||||
libssl-dev \
|
||||
zlib1g-dev \
|
||||
libvulkan-dev \
|
||||
vulkan-tools \
|
||||
glslc \
|
||||
libavformat-dev \
|
||||
libavcodec-dev \
|
||||
libswscale-dev \
|
||||
|
|
|
|||
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -42,10 +42,12 @@ wowee
|
|||
*~
|
||||
.DS_Store
|
||||
|
||||
# External dependencies (except submodules)
|
||||
# External dependencies (except submodules and vendored headers)
|
||||
extern/*
|
||||
!extern/.gitkeep
|
||||
!extern/imgui
|
||||
!extern/vk-bootstrap
|
||||
!extern/vk_mem_alloc.h
|
||||
|
||||
# ImGui state
|
||||
imgui.ini
|
||||
|
|
@ -64,7 +66,7 @@ cache/
|
|||
saves/
|
||||
wowee_[0-9][0-9][0-9][0-9]
|
||||
|
||||
# Extracted assets (run ./extract_assets.sh to generate)
|
||||
# Extracted assets (run ./extract_assets.sh or .\extract_assets.ps1 to generate)
|
||||
Data/db/
|
||||
Data/character/
|
||||
Data/creature/
|
||||
|
|
@ -87,6 +89,9 @@ Data/expansions/*/overlay/
|
|||
Data/hd/
|
||||
ingest/
|
||||
|
||||
# Asset pipeline state and texture packs
|
||||
asset_pipeline/
|
||||
|
||||
# Local texture dumps / extracted art should never be committed
|
||||
assets/textures/
|
||||
node_modules/
|
||||
|
|
|
|||
4
.gitmodules
vendored
4
.gitmodules
vendored
|
|
@ -2,3 +2,7 @@
|
|||
path = extern/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
shallow = true
|
||||
[submodule "extern/vk-bootstrap"]
|
||||
path = extern/vk-bootstrap
|
||||
url = https://github.com/charles-lunarg/vk-bootstrap.git
|
||||
shallow = true
|
||||
|
|
|
|||
|
|
@ -10,7 +10,13 @@ This document provides platform-specific build instructions for WoWee.
|
|||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake pkg-config git libsdl2-dev libglew-dev libglm-dev libssl-dev zlib1g-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libunicorn-dev libstorm-dev
|
||||
sudo apt install -y \
|
||||
build-essential cmake pkg-config git \
|
||||
libsdl2-dev libglew-dev libglm-dev \
|
||||
libssl-dev zlib1g-dev \
|
||||
libvulkan-dev vulkan-tools glslc \
|
||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
|
||||
libunicorn-dev libstorm-dev libx11-dev
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -20,7 +26,11 @@ sudo apt install -y build-essential cmake pkg-config git libsdl2-dev libglew
|
|||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
sudo pacman -S --needed base-devel cmake pkgconf git sdl2 glew glm openssl zlib ffmpeg unicorn stormlib
|
||||
sudo pacman -S --needed \
|
||||
base-devel cmake pkgconf git \
|
||||
sdl2 glew glm openssl zlib \
|
||||
vulkan-devel vulkan-tools shaderc \
|
||||
ffmpeg unicorn stormlib
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -49,15 +59,119 @@ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|||
cmake --build build -j"$(nproc)"
|
||||
```
|
||||
|
||||
### Asset Extraction (Linux)
|
||||
|
||||
After building, extract assets from your WoW client:
|
||||
|
||||
```bash
|
||||
./extract_assets.sh /path/to/WoW/Data wotlk
|
||||
```
|
||||
|
||||
Supports `classic`, `tbc`, `wotlk` targets (auto-detected if omitted).
|
||||
|
||||
---
|
||||
|
||||
## 🍎 macOS
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
Vulkan on macOS is provided via MoltenVK (a Vulkan-to-Metal translation layer),
|
||||
which is included in the `vulkan-loader` Homebrew package.
|
||||
|
||||
```bash
|
||||
brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg unicorn \
|
||||
stormlib vulkan-loader vulkan-headers shaderc
|
||||
```
|
||||
|
||||
Optional (for creating redistributable `.app` bundles):
|
||||
|
||||
```bash
|
||||
brew install dylibbundler
|
||||
```
|
||||
|
||||
### Clone & Build
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/Kelsidavis/WoWee.git
|
||||
cd WoWee
|
||||
|
||||
BREW=$(brew --prefix)
|
||||
export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix vulkan-loader)/lib/pkgconfig:$(brew --prefix shaderc)/lib/pkgconfig"
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH="$BREW" \
|
||||
-DOPENSSL_ROOT_DIR="$(brew --prefix openssl@3)"
|
||||
cmake --build build -j"$(sysctl -n hw.logicalcpu)"
|
||||
```
|
||||
|
||||
### Asset Extraction (macOS)
|
||||
|
||||
The script will auto-build `asset_extract` if needed (requires `stormlib`).
|
||||
It automatically detects Homebrew and passes the correct paths to CMake.
|
||||
|
||||
```bash
|
||||
./extract_assets.sh /path/to/WoW/Data wotlk
|
||||
```
|
||||
|
||||
Supports `classic`, `tbc`, `wotlk` targets (auto-detected if omitted).
|
||||
|
||||
---
|
||||
|
||||
## 🪟 Windows (MSYS2 — Recommended)
|
||||
|
||||
MSYS2 provides all dependencies as pre-built packages.
|
||||
|
||||
### Install MSYS2
|
||||
|
||||
Download and install from <https://www.msys2.org/>, then open a **MINGW64** shell.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
pacman -S --needed \
|
||||
mingw-w64-x86_64-cmake \
|
||||
mingw-w64-x86_64-gcc \
|
||||
mingw-w64-x86_64-ninja \
|
||||
mingw-w64-x86_64-pkgconf \
|
||||
mingw-w64-x86_64-SDL2 \
|
||||
mingw-w64-x86_64-glew \
|
||||
mingw-w64-x86_64-glm \
|
||||
mingw-w64-x86_64-openssl \
|
||||
mingw-w64-x86_64-zlib \
|
||||
mingw-w64-x86_64-ffmpeg \
|
||||
mingw-w64-x86_64-unicorn \
|
||||
mingw-w64-x86_64-vulkan-loader \
|
||||
mingw-w64-x86_64-vulkan-headers \
|
||||
mingw-w64-x86_64-shaderc \
|
||||
mingw-w64-x86_64-stormlib \
|
||||
git
|
||||
```
|
||||
|
||||
### Clone & Build
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/Kelsidavis/WoWee.git
|
||||
cd WoWee
|
||||
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build --parallel $(nproc)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🪟 Windows (Visual Studio 2022)
|
||||
|
||||
For users who prefer Visual Studio over MSYS2.
|
||||
|
||||
### Install
|
||||
|
||||
- Visual Studio 2022
|
||||
- Desktop development with C++
|
||||
- CMake tools for Windows
|
||||
- Visual Studio 2022 with **Desktop development with C++** workload
|
||||
- CMake tools for Windows (included in VS workload)
|
||||
- [LunarG Vulkan SDK](https://vulkan.lunarg.com/) (provides Vulkan headers, loader, and glslc)
|
||||
|
||||
### vcpkg Dependencies
|
||||
|
||||
```powershell
|
||||
vcpkg install sdl2 glew glm openssl zlib ffmpeg stormlib --triplet x64-windows
|
||||
```
|
||||
|
||||
### Clone
|
||||
|
||||
|
|
@ -68,16 +182,29 @@ cd WoWee
|
|||
|
||||
### Build
|
||||
|
||||
Open the folder in Visual Studio (it will detect CMake automatically)
|
||||
Open the folder in Visual Studio (it will detect CMake automatically)
|
||||
or build from Developer PowerShell:
|
||||
|
||||
```powershell
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="[vcpkg root]/scripts/buildsystems/vcpkg.cmake"
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🪟 Asset Extraction (Windows)
|
||||
|
||||
After building (via either MSYS2 or Visual Studio), extract assets from your WoW client:
|
||||
|
||||
```powershell
|
||||
.\extract_assets.ps1 "C:\Games\WoW-3.3.5a\Data"
|
||||
```
|
||||
|
||||
Or double-click `extract_assets.bat` and provide the path when prompted.
|
||||
You can also specify an expansion: `.\extract_assets.ps1 "C:\Games\WoW\Data" wotlk`
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Notes
|
||||
|
||||
- Case matters on Linux (`WoWee` not `wowee`).
|
||||
|
|
|
|||
|
|
@ -35,7 +35,30 @@ endif()
|
|||
|
||||
# Find required packages
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(Vulkan REQUIRED)
|
||||
find_package(Vulkan QUIET)
|
||||
if(NOT Vulkan_FOUND)
|
||||
# Fallback: some distros / CMake versions need pkg-config to locate Vulkan.
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PkgConfig_FOUND)
|
||||
pkg_check_modules(VULKAN_PKG vulkan)
|
||||
if(VULKAN_PKG_FOUND)
|
||||
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
|
||||
set_target_properties(Vulkan::Vulkan PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${VULKAN_PKG_INCLUDE_DIRS}"
|
||||
INTERFACE_LINK_LIBRARIES "${VULKAN_PKG_LIBRARIES}"
|
||||
)
|
||||
if(VULKAN_PKG_LIBRARY_DIRS)
|
||||
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
||||
INTERFACE_LINK_DIRECTORIES "${VULKAN_PKG_LIBRARY_DIRS}")
|
||||
endif()
|
||||
set(Vulkan_FOUND TRUE)
|
||||
message(STATUS "Found Vulkan via pkg-config: ${VULKAN_PKG_LIBRARIES}")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT Vulkan_FOUND)
|
||||
message(FATAL_ERROR "Could not find Vulkan. Install libvulkan-dev (Linux), vulkan-loader (macOS), or the Vulkan SDK (Windows).")
|
||||
endif()
|
||||
endif()
|
||||
# GL/GLEW kept temporarily for unconverted sub-renderers during Vulkan migration.
|
||||
# These files compile against GL types but their code is never called — the Vulkan
|
||||
# path is the only active rendering backend. Remove in Phase 7 when all renderers
|
||||
|
|
@ -445,6 +468,9 @@ endif()
|
|||
target_include_directories(wowee PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
)
|
||||
# Vendored headers as SYSTEM to suppress third-party warnings
|
||||
target_include_directories(wowee SYSTEM PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/extern
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/extern/vk-bootstrap/src
|
||||
)
|
||||
|
|
@ -522,7 +548,7 @@ endif()
|
|||
if(MSVC)
|
||||
target_compile_options(wowee PRIVATE /W4)
|
||||
else()
|
||||
target_compile_options(wowee PRIVATE -Wall -Wextra -Wpedantic)
|
||||
target_compile_options(wowee PRIVATE -Wall -Wextra -Wpedantic -Wno-missing-field-initializers)
|
||||
endif()
|
||||
|
||||
# Debug build flags
|
||||
|
|
@ -594,12 +620,18 @@ add_custom_command(TARGET wowee POST_BUILD
|
|||
# which does NOT include System32. Copy vulkan-1.dll into the output directory so
|
||||
# SDL_Vulkan_LoadLibrary can locate it without needing a full system PATH search.
|
||||
if(WIN32)
|
||||
add_custom_command(TARGET wowee POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"$ENV{SystemRoot}/System32/vulkan-1.dll"
|
||||
"$<TARGET_FILE_DIR:wowee>/vulkan-1.dll"
|
||||
COMMENT "Copying vulkan-1.dll to output directory"
|
||||
)
|
||||
find_file(VULKAN_DLL vulkan-1.dll
|
||||
PATHS "$ENV{SystemRoot}/System32" "$ENV{VULKAN_SDK}/Bin"
|
||||
NO_DEFAULT_PATH)
|
||||
if(VULKAN_DLL)
|
||||
add_custom_command(TARGET wowee POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${VULKAN_DLL}" "$<TARGET_FILE_DIR:wowee>/vulkan-1.dll"
|
||||
COMMENT "Copying vulkan-1.dll to output directory"
|
||||
)
|
||||
else()
|
||||
message(STATUS " vulkan-1.dll not found — skipping copy (MSYS2 provides it via PATH)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Install targets
|
||||
|
|
@ -661,7 +693,14 @@ if(STORMLIB_LIBRARY AND STORMLIB_INCLUDE_DIR)
|
|||
Threads::Threads
|
||||
)
|
||||
if(WIN32)
|
||||
target_link_libraries(asset_extract PRIVATE wininet bz2)
|
||||
find_library(WININET_LIB wininet)
|
||||
find_library(BZ2_LIB bz2)
|
||||
if(WININET_LIB)
|
||||
target_link_libraries(asset_extract PRIVATE ${WININET_LIB})
|
||||
endif()
|
||||
if(BZ2_LIB)
|
||||
target_link_libraries(asset_extract PRIVATE ${BZ2_LIB})
|
||||
endif()
|
||||
endif()
|
||||
set_target_properties(asset_extract PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
||||
|
|
|
|||
24
README.md
24
README.md
|
|
@ -36,7 +36,7 @@ Protocol Compatible with **Vanilla (Classic) 1.12 + TBC 2.4.3 + WotLK 3.3.5a**.
|
|||
### Asset Pipeline
|
||||
- Extracted loose-file **`Data/`** tree indexed by **`manifest.json`** (fast lookup + caching)
|
||||
- Optional **overlay layers** for multi-expansion asset deduplication
|
||||
- `asset_extract` + `extract_assets.sh` for MPQ extraction (StormLib tooling)
|
||||
- `asset_extract` + `extract_assets.sh` (Linux/macOS) / `extract_assets.ps1` (Windows) for MPQ extraction (StormLib tooling)
|
||||
- File formats: **BLP** (DXT1/3/5), **ADT**, **M2**, **WMO**, **DBC** (Spell/Item/Faction/etc.)
|
||||
|
||||
### Gameplay Systems
|
||||
|
|
@ -84,6 +84,14 @@ sudo pacman -S sdl2 glm openssl \
|
|||
ffmpeg zlib cmake base-devel libx11 \
|
||||
unicorn # optional: Warden module execution
|
||||
# StormLib: install from AUR for asset_extract tool
|
||||
|
||||
# macOS (Homebrew)
|
||||
brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg \
|
||||
vulkan-loader vulkan-headers shaderc \
|
||||
unicorn \
|
||||
stormlib
|
||||
# unicorn is optional (Warden module execution)
|
||||
# stormlib is optional (asset_extract tool)
|
||||
```
|
||||
|
||||
### Container build
|
||||
|
|
@ -98,11 +106,18 @@ This project requires WoW client data that you extract from your own legally obt
|
|||
|
||||
Wowee loads assets via an extracted loose-file tree indexed by `manifest.json` (it does not read MPQs at runtime).
|
||||
|
||||
For a cross-platform GUI workflow (extraction + texture pack management + active override state), see:
|
||||
- [Asset Pipeline GUI](docs/asset-pipeline-gui.md)
|
||||
|
||||
#### 1) Extract MPQs into `./Data/`
|
||||
|
||||
```bash
|
||||
# WotLK 3.3.5a example
|
||||
# Linux / macOS
|
||||
./extract_assets.sh /path/to/WoW/Data wotlk
|
||||
|
||||
# Windows (PowerShell)
|
||||
.\extract_assets.ps1 "C:\Games\WoW-3.3.5a\Data" wotlk
|
||||
# Or double-click extract_assets.bat
|
||||
```
|
||||
|
||||
```
|
||||
|
|
@ -117,7 +132,7 @@ Data/
|
|||
Notes:
|
||||
|
||||
- `StormLib` is required to build/run the extractor (`asset_extract`), but the main client does not require StormLib at runtime.
|
||||
- `extract_assets.sh` supports `classic`, `tbc`, `wotlk` targets.
|
||||
- `extract_assets.sh` / `extract_assets.ps1` support `classic`, `tbc`, `wotlk` targets.
|
||||
|
||||
#### 2) Point wowee at the extracted data
|
||||
|
||||
|
|
@ -184,6 +199,7 @@ make -j$(nproc)
|
|||
- [Project Status](docs/status.md) -- Current code state, limitations, and near-term direction
|
||||
- [Quick Start](docs/quickstart.md) -- Installation and first steps
|
||||
- [Build Instructions](BUILD_INSTRUCTIONS.md) -- Detailed dependency, build, and run guide
|
||||
- [Asset Pipeline GUI](docs/asset-pipeline-gui.md) -- Python GUI for extraction, pack installs, ordering, and override rebuilds
|
||||
|
||||
### Technical Documentation
|
||||
- [Architecture](docs/architecture.md) -- System design and module overview
|
||||
|
|
@ -206,7 +222,7 @@ make -j$(nproc)
|
|||
|
||||
## Technical Details
|
||||
|
||||
- **Platform**: Linux (primary), C++20, CMake 3.15+
|
||||
- **Platform**: Linux (primary), Windows (MSYS2/MSVC), macOS — C++20, CMake 3.15+
|
||||
- **Dependencies**: SDL2, Vulkan, GLM, OpenSSL, ImGui, FFmpeg, Unicorn Engine (StormLib for asset extraction tooling)
|
||||
- **Architecture**: Modular design with clear separation (core, rendering, networking, game logic, asset pipeline, UI, audio)
|
||||
- **Networking**: Non-blocking TCP, SRP6a authentication, RC4 encryption, WoW 3.3.5a protocol
|
||||
|
|
|
|||
5
build.sh
5
build.sh
|
|
@ -16,8 +16,9 @@ echo "Configuring with CMake..."
|
|||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
# Build with all cores
|
||||
echo "Building with $(nproc) cores..."
|
||||
cmake --build . --parallel $(nproc)
|
||||
NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)
|
||||
echo "Building with $NPROC cores..."
|
||||
cmake --build . --parallel "$NPROC"
|
||||
|
||||
# Ensure Data symlink exists in bin directory
|
||||
cd bin
|
||||
|
|
|
|||
194
docs/asset-pipeline-gui.md
Normal file
194
docs/asset-pipeline-gui.md
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
# Asset Pipeline GUI
|
||||
|
||||
WoWee includes a Python GUI for extraction and texture-pack management:
|
||||
|
||||
```bash
|
||||
python3 tools/asset_pipeline_gui.py
|
||||
```
|
||||
|
||||
The script is also executable directly: `./tools/asset_pipeline_gui.py`
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- Linux
|
||||
- macOS
|
||||
- Windows
|
||||
|
||||
The app uses Python's built-in `tkinter` module. If `tkinter` is missing, install the platform package:
|
||||
|
||||
- Linux (Debian/Ubuntu): `sudo apt install python3-tk`
|
||||
- Fedora: `sudo dnf install python3-tkinter`
|
||||
- Arch: `sudo pacman -S tk`
|
||||
- macOS: use the official Python.org installer (includes Tk)
|
||||
- Windows: use the official Python installer and enable Tcl/Tk support
|
||||
|
||||
## What It Does
|
||||
|
||||
- Runs `asset_extract` (or shell/PowerShell script fallback) to extract MPQ data
|
||||
- Saves extraction config in `asset_pipeline/state.json`
|
||||
- Installs texture packs from ZIP or folders (with zip-slip protection)
|
||||
- Lets users activate/deactivate packs and reorder active pack priority
|
||||
- Rebuilds `Data/override` from active pack order (runs in background thread)
|
||||
- Shows current data state (`manifest.json`, entry count, override file count, last runs)
|
||||
- Browses extracted assets with inline previews (images, 3D wireframes, data tables, text, hex dumps)
|
||||
|
||||
## Configuration Tab
|
||||
|
||||
### Path Settings
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| **WoW Data (MPQ source)** | Path to your WoW client's `Data/` folder containing `.MPQ` files |
|
||||
| **Output Data directory** | Where extracted assets land. Defaults to `<project root>/Data` |
|
||||
| **Extractor binary/script** | Optional. Leave blank for auto-detection (see below) |
|
||||
|
||||
### Extractor Auto-Detection
|
||||
|
||||
When no extractor path is configured, the GUI searches in order:
|
||||
|
||||
1. `build/bin/asset_extract` — CMake build with bin subdirectory
|
||||
2. `build/asset_extract` — CMake build without bin subdirectory
|
||||
3. `bin/asset_extract` — standalone binary
|
||||
4. **Windows only**: `extract_assets.ps1` — invoked via `powershell -ExecutionPolicy Bypass -File`
|
||||
5. **Linux/macOS only**: `extract_assets.sh` — invoked via `bash`
|
||||
|
||||
On Windows, `.exe` is appended to binary candidates automatically.
|
||||
|
||||
### Extraction Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| **Expansion** | `auto`, `classic`, `turtle`, `tbc`, or `wotlk`. Read-only dropdown. |
|
||||
| **Locale** | `auto`, `enUS`, `enGB`, `deDE`, `frFR`, etc. Read-only dropdown. |
|
||||
| **Threads** | Worker thread count. 0 = auto (uses all cores). |
|
||||
| **Skip DBC extraction** | Skip database client files (faster if you only want textures). |
|
||||
| **Generate DBC CSV** | Output human-readable CSV alongside binary DBC files. |
|
||||
| **Verify CRC** | Check file integrity during extraction (slower but safer). |
|
||||
| **Verbose output** | More detail in the Logs tab. |
|
||||
|
||||
### Buttons
|
||||
|
||||
| Button | Action |
|
||||
|--------|--------|
|
||||
| **Save Configuration** | Writes all settings to `asset_pipeline/state.json`. |
|
||||
| **Run Extraction** | Starts the extractor in a background thread. Output streams to the Logs tab. |
|
||||
| **Cancel Extraction** | Terminates a running extraction. Grayed out when idle, active during extraction. |
|
||||
| **Refresh State** | Reloads the Current State tab. |
|
||||
|
||||
## Texture Packs Tab
|
||||
|
||||
### Installing Packs
|
||||
|
||||
- **Install ZIP**: Opens a file picker for `.zip` archives. Each member path is validated against zip-slip attacks before extraction.
|
||||
- **Install Folder**: Opens a folder picker and copies the entire folder into the pipeline's internal pack storage.
|
||||
|
||||
### Managing Packs
|
||||
|
||||
| Button | Action |
|
||||
|--------|--------|
|
||||
| **Activate** | Adds the selected pack to the active override list. |
|
||||
| **Deactivate** | Removes the selected pack from the active list (stays installed). |
|
||||
| **Move Up / Move Down** | Changes priority order. Pack #1 is the base layer; higher numbers override lower. |
|
||||
| **Rebuild Override** | Merges all active packs into `Data/override/` in a background thread. UI stays responsive. |
|
||||
| **Uninstall** | Removes the pack from disk after confirmation. |
|
||||
|
||||
Pack list selection is preserved across refreshes — you can activate a pack and immediately reorder it without re-selecting.
|
||||
|
||||
## Pack Format
|
||||
|
||||
Supported pack layouts:
|
||||
|
||||
1. `PackName/Data/...`
|
||||
2. `PackName/data/...`
|
||||
3. `PackName/...` where top folders include game folders (`Interface`, `World`, `Character`, `Textures`, `Sound`)
|
||||
4. Single wrapper directory containing any of the above
|
||||
|
||||
When multiple active packs contain the same file path, **later packs in active order win**.
|
||||
|
||||
## Asset Browser Tab
|
||||
|
||||
Browse and preview every extracted asset visually. Requires a completed extraction with a `manifest.json` in the output directory.
|
||||
|
||||
### Layout
|
||||
|
||||
- **Top bar**: Search entry, file type filter dropdown, Search/Reset buttons, result count
|
||||
- **Left panel** (~30%): Directory tree built lazily from `manifest.json`
|
||||
- **Right panel** (~70%): Preview area that adapts to the selected file type
|
||||
- **Bottom bar**: File path, size, and CRC from manifest
|
||||
|
||||
### Search and Filtering
|
||||
|
||||
Type a substring into the search box and/or pick a file type from the dropdown, then click **Search**. The tree repopulates with matching results (capped at 5000 entries). Click **Reset** to restore the full tree.
|
||||
|
||||
File type filters: All, BLP, M2, WMO, DBC, ADT, Audio, Text.
|
||||
|
||||
### Preview Types
|
||||
|
||||
| Type | What You See |
|
||||
|------|--------------|
|
||||
| **BLP** | Converted to PNG via `blp_convert --to-png`, displayed as an image. Cached in `asset_pipeline/preview_cache/`. |
|
||||
| **M2** | Wireframe rendering of model vertices and triangles on a Canvas. Drag to rotate, scroll to zoom. |
|
||||
| **WMO** | Wireframe of group geometry (MOVT/MOVI chunks). Root WMOs auto-load the `_000` group file. |
|
||||
| **CSV** (DBC exports) | Scrollable table with column names from `dbc_layouts.json`. First 500 rows loaded, click "Load more" for the rest. |
|
||||
| **ADT** | Colored heightmap grid parsed from MCNK chunks. |
|
||||
| **Text** (XML, LUA, JSON, HTML, TOC) | Syntax-highlighted scrollable text view. |
|
||||
| **Audio** (WAV, MP3, OGG) | Metadata display — format, channels, sample rate, duration (WAV). |
|
||||
| **Other** | Hex dump of the first 512 bytes. |
|
||||
|
||||
### Wireframe Controls
|
||||
|
||||
- **Left-click drag**: Rotate the model (azimuth + elevation)
|
||||
- **Scroll wheel**: Zoom in/out
|
||||
- Depth coloring: closer geometry renders brighter
|
||||
|
||||
### Optional Dependencies
|
||||
|
||||
| Dependency | Required For | Fallback |
|
||||
|------------|-------------|----------|
|
||||
| [Pillow](https://pypi.org/project/Pillow/) (`pip install Pillow`) | BLP image preview | Shows install instructions |
|
||||
| `blp_convert` (built with project) | BLP → PNG conversion | Shows "not found" message |
|
||||
|
||||
All other previews (wireframe, table, text, hex) work without any extra dependencies.
|
||||
|
||||
### Cache
|
||||
|
||||
BLP previews are cached as PNG files in `asset_pipeline/preview_cache/` keyed by path and file size. Delete this directory to clear the cache.
|
||||
|
||||
## Current State Tab
|
||||
|
||||
Shows a summary of pipeline state:
|
||||
|
||||
- Output directory existence and `manifest.json` entry count
|
||||
- Override folder file count and last build timestamp
|
||||
- Installed and active pack counts with priority order
|
||||
- Last extraction time, success/failure, and the exact command used
|
||||
- Paths to the state file and packs directory
|
||||
|
||||
Click **Refresh** to reload, or it auto-refreshes after operations.
|
||||
|
||||
## Logs Tab
|
||||
|
||||
All extraction output, override rebuild messages, cancellations, and errors stream here in real time via a log queue polled every 120ms. Click **Clear Logs** to reset.
|
||||
|
||||
## State Files and Folders
|
||||
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `asset_pipeline/state.json` | All configuration, pack metadata, and extraction history |
|
||||
| `asset_pipeline/packs/<pack-id>/` | Installed pack contents (one directory per pack) |
|
||||
| `asset_pipeline/preview_cache/` | Cached BLP → PNG conversions for the Asset Browser |
|
||||
| `<Output Data>/override/` | Merged output from active packs |
|
||||
|
||||
The `asset_pipeline/` directory is gitignored.
|
||||
|
||||
## Typical Workflow
|
||||
|
||||
1. Launch: `python3 tools/asset_pipeline_gui.py`
|
||||
2. **Configuration tab**: Browse to your WoW `Data/` folder, pick expansion, click **Save Configuration**.
|
||||
3. Click **Run Extraction** — watch progress in the **Logs** tab. Cancel with **Cancel Extraction** if needed.
|
||||
4. Switch to **Texture Packs** tab. Click **Install ZIP** and pick a texture pack.
|
||||
5. Select the pack and click **Activate**.
|
||||
6. (Optional) Install more packs, activate them, and use **Move Up/Down** to set priority.
|
||||
7. Click **Rebuild Override** — the status bar shows progress, and the result appears in Logs.
|
||||
8. (Optional) Switch to **Asset Browser** to explore extracted files — preview textures, inspect models, browse DBC tables.
|
||||
9. Run wowee — it loads override textures on top of the extracted base assets.
|
||||
1
extern/vk-bootstrap
vendored
Submodule
1
extern/vk-bootstrap
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 2e7c8e8323c0fe7efc96dc241610fcba35d8f530
|
||||
19845
extern/vk_mem_alloc.h
vendored
Normal file
19845
extern/vk_mem_alloc.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
4
extract_assets.bat
Normal file
4
extract_assets.bat
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
@echo off
|
||||
REM Convenience wrapper — launches the PowerShell asset extraction script.
|
||||
REM Usage: extract_assets.bat C:\Games\WoW\Data [expansion]
|
||||
powershell -ExecutionPolicy Bypass -File "%~dp0extract_assets.ps1" %*
|
||||
120
extract_assets.ps1
Normal file
120
extract_assets.ps1
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Extracts WoW MPQ archives for use with wowee (Windows equivalent of extract_assets.sh).
|
||||
|
||||
.DESCRIPTION
|
||||
Builds the asset_extract tool (if needed) and runs it against a WoW Data directory.
|
||||
|
||||
.PARAMETER MpqDir
|
||||
Path to the WoW client's Data directory containing .MPQ files.
|
||||
|
||||
.PARAMETER Expansion
|
||||
Expansion hint: classic, turtle, tbc, wotlk. Auto-detected if omitted.
|
||||
|
||||
.EXAMPLE
|
||||
.\extract_assets.ps1 "C:\Games\WoW-3.3.5a\Data"
|
||||
.\extract_assets.ps1 "C:\Games\WoW-1.12\Data" classic
|
||||
.\extract_assets.ps1 "D:\TurtleWoW\Data" turtle
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true, Position=0)]
|
||||
[string]$MpqDir,
|
||||
|
||||
[Parameter(Position=1)]
|
||||
[string]$Expansion = "auto"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$BuildDir = Join-Path $ScriptDir "build"
|
||||
$Binary = Join-Path $BuildDir "bin\asset_extract.exe"
|
||||
$OutputDir = Join-Path $ScriptDir "Data"
|
||||
|
||||
# --- Validate arguments ---
|
||||
if (-not (Test-Path $MpqDir -PathType Container)) {
|
||||
Write-Error "Error: Directory not found: $MpqDir"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check for .MPQ files
|
||||
$mpqFiles = Get-ChildItem -Path $MpqDir -Filter "*.MPQ" -ErrorAction SilentlyContinue
|
||||
$mpqFilesLower = Get-ChildItem -Path $MpqDir -Filter "*.mpq" -ErrorAction SilentlyContinue
|
||||
if (-not $mpqFiles -and -not $mpqFilesLower) {
|
||||
Write-Error "Error: No .MPQ files found in: $MpqDir`nMake sure this is the WoW Data/ directory (not the WoW root)."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Note about existing CSV DBCs
|
||||
if (Test-Path (Join-Path $OutputDir "expansions")) {
|
||||
$csvPattern = $null
|
||||
if ($Expansion -ne "auto") {
|
||||
$csvPattern = Join-Path $OutputDir "expansions\$Expansion\db\*.csv"
|
||||
} else {
|
||||
$csvPattern = Join-Path $OutputDir "expansions\*\db\*.csv"
|
||||
}
|
||||
if ($csvPattern -and (Get-ChildItem -Path $csvPattern -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "Note: Found CSV DBCs. DBC extraction is optional; visual assets are still required.`n"
|
||||
}
|
||||
}
|
||||
|
||||
# --- Build asset_extract if needed ---
|
||||
if (-not (Test-Path $Binary)) {
|
||||
Write-Host "Building asset_extract..."
|
||||
|
||||
# Check for cmake
|
||||
if (-not (Get-Command cmake -ErrorAction SilentlyContinue)) {
|
||||
Write-Error "Error: cmake not found. Install CMake and ensure it is on your PATH."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not (Test-Path $BuildDir)) {
|
||||
& cmake -S $ScriptDir -B $BuildDir -DCMAKE_BUILD_TYPE=Release
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
}
|
||||
|
||||
$numProcs = $env:NUMBER_OF_PROCESSORS
|
||||
if (-not $numProcs) { $numProcs = 4 }
|
||||
& cmake --build $BuildDir --target asset_extract --parallel $numProcs
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
if (-not (Test-Path $Binary)) {
|
||||
Write-Error "Error: Failed to build asset_extract"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Run extraction ---
|
||||
Write-Host "Extracting assets from: $MpqDir"
|
||||
Write-Host "Output directory: $OutputDir"
|
||||
Write-Host ""
|
||||
|
||||
$extraArgs = @("--mpq-dir", $MpqDir, "--output", $OutputDir)
|
||||
|
||||
if ($Expansion -ne "auto") {
|
||||
$extraArgs += "--expansion"
|
||||
$extraArgs += $Expansion
|
||||
}
|
||||
|
||||
# Skip DBC extraction if CSVs already present
|
||||
$skipDbc = $false
|
||||
if ($Expansion -ne "auto") {
|
||||
$csvPath = Join-Path $OutputDir "expansions\$Expansion\db\*.csv"
|
||||
if (Get-ChildItem -Path $csvPath -ErrorAction SilentlyContinue) { $skipDbc = $true }
|
||||
} else {
|
||||
$csvPath = Join-Path $OutputDir "expansions\*\db\*.csv"
|
||||
if (Get-ChildItem -Path $csvPath -ErrorAction SilentlyContinue) { $skipDbc = $true }
|
||||
}
|
||||
if ($skipDbc) {
|
||||
$extraArgs += "--skip-dbc"
|
||||
}
|
||||
|
||||
& $Binary @extraArgs
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Done! Assets extracted to $OutputDir"
|
||||
Write-Host "You can now run wowee."
|
||||
|
|
@ -63,18 +63,39 @@ fi
|
|||
# --- Build asset_extract if needed ---
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
# --- Check for StormLib (only required to build) ---
|
||||
if ! ldconfig -p 2>/dev/null | grep -qi stormlib; then
|
||||
STORMLIB_FOUND=false
|
||||
if ldconfig -p 2>/dev/null | grep -qi stormlib; then
|
||||
STORMLIB_FOUND=true
|
||||
elif pkg-config --exists stormlib 2>/dev/null; then
|
||||
STORMLIB_FOUND=true
|
||||
elif [ -f "$(brew --prefix 2>/dev/null)/lib/libstorm.dylib" ] 2>/dev/null; then
|
||||
STORMLIB_FOUND=true
|
||||
fi
|
||||
if [ "$STORMLIB_FOUND" = false ]; then
|
||||
echo "Error: StormLib not found."
|
||||
echo "Install it with: sudo apt install libstormlib-dev"
|
||||
echo " or build from source: https://github.com/ladislav-zezula/StormLib"
|
||||
echo " Ubuntu/Debian: sudo apt install libstormlib-dev"
|
||||
echo " macOS: brew install stormlib"
|
||||
echo " From source: https://github.com/ladislav-zezula/StormLib"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building asset_extract..."
|
||||
if [ ! -d "$BUILD_DIR" ]; then
|
||||
cmake -S "$SCRIPT_DIR" -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release
|
||||
CMAKE_EXTRA_ARGS=()
|
||||
# On macOS, Homebrew installs to a non-default prefix that CMake
|
||||
# can't find automatically — pass it explicitly.
|
||||
if command -v brew &>/dev/null; then
|
||||
BREW="$(brew --prefix)"
|
||||
CMAKE_EXTRA_ARGS+=(
|
||||
"-DCMAKE_PREFIX_PATH=$BREW"
|
||||
"-DOPENSSL_ROOT_DIR=$(brew --prefix openssl@3)"
|
||||
)
|
||||
export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix vulkan-loader 2>/dev/null)/lib/pkgconfig:$(brew --prefix shaderc 2>/dev/null)/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
|
||||
fi
|
||||
cmake -S "$SCRIPT_DIR" -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release "${CMAKE_EXTRA_ARGS[@]}"
|
||||
fi
|
||||
cmake --build "$BUILD_DIR" --target asset_extract -- -j"$(nproc)"
|
||||
NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)
|
||||
cmake --build "$BUILD_DIR" --target asset_extract -- -j"$NPROC"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ inline ProcessHandle spawnProcess(const std::vector<std::string>& args) {
|
|||
if (pid == 0) {
|
||||
// Child process
|
||||
setpgid(0, 0);
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
if (!freopen("/dev/null", "w", stdout)) { _exit(1); }
|
||||
if (!freopen("/dev/null", "w", stderr)) { _exit(1); }
|
||||
|
||||
// Build argv for exec
|
||||
std::vector<const char*> argv;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ AllocatedBuffer uploadBuffer(VkContext& ctx, const void* data, VkDeviceSize size
|
|||
VkBufferUsageFlags usage);
|
||||
|
||||
// Check VkResult and log on failure
|
||||
inline bool vkCheck(VkResult result, const char* msg) {
|
||||
inline bool vkCheck(VkResult result, [[maybe_unused]] const char* msg) {
|
||||
if (result != VK_SUCCESS) {
|
||||
// Caller should log the message
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ echo "Configuring with CMake..."
|
|||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
# Build with all cores
|
||||
echo "Building with $(nproc) cores..."
|
||||
cmake --build . --parallel $(nproc)
|
||||
NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)
|
||||
echo "Building with $NPROC cores..."
|
||||
cmake --build . --parallel "$NPROC"
|
||||
|
||||
# Create Data symlink in bin directory
|
||||
echo "Creating Data symlink..."
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ void ActivitySoundManager::shutdown() {
|
|||
assetManager = nullptr;
|
||||
}
|
||||
|
||||
void ActivitySoundManager::update(float deltaTime) {
|
||||
void ActivitySoundManager::update([[maybe_unused]] float deltaTime) {
|
||||
reapProcesses();
|
||||
|
||||
// Play swimming stroke sounds periodically when swimming and moving
|
||||
|
|
@ -168,7 +168,7 @@ void ActivitySoundManager::rebuildJumpClipsForProfile(const std::string& raceFol
|
|||
}
|
||||
}
|
||||
|
||||
void ActivitySoundManager::rebuildSwimLoopClipsForProfile(const std::string& raceFolder, const std::string& raceBase, bool male) {
|
||||
void ActivitySoundManager::rebuildSwimLoopClipsForProfile([[maybe_unused]] const std::string& raceFolder, [[maybe_unused]] const std::string& raceBase, [[maybe_unused]] bool male) {
|
||||
swimLoopClips.clear();
|
||||
|
||||
// WoW 3.3.5a doesn't have dedicated swim loop sounds
|
||||
|
|
|
|||
|
|
@ -117,10 +117,10 @@ bool AmbientSoundManager::initialize(pipeline::AssetManager* assets) {
|
|||
bool forestNightLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\ForestNormalNight.wav", forestNormalNightSounds_[0], assets);
|
||||
|
||||
forestSnowDaySounds_.resize(1);
|
||||
bool forestSnowDayLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\ForestSnowDay.wav", forestSnowDaySounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\ForestSnowDay.wav", forestSnowDaySounds_[0], assets);
|
||||
|
||||
forestSnowNightSounds_.resize(1);
|
||||
bool forestSnowNightLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\ForestSnowNight.wav", forestSnowNightSounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\ForestSnowNight.wav", forestSnowNightSounds_[0], assets);
|
||||
|
||||
beachDaySounds_.resize(1);
|
||||
bool beachDayLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\BeachDay.wav", beachDaySounds_[0], assets);
|
||||
|
|
@ -129,34 +129,34 @@ bool AmbientSoundManager::initialize(pipeline::AssetManager* assets) {
|
|||
bool beachNightLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\BeachNight.wav", beachNightSounds_[0], assets);
|
||||
|
||||
grasslandsDaySounds_.resize(1);
|
||||
bool grasslandsDayLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\GrasslandsDay.wav", grasslandsDaySounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\GrasslandsDay.wav", grasslandsDaySounds_[0], assets);
|
||||
|
||||
grasslandsNightSounds_.resize(1);
|
||||
bool grasslandsNightLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\GrassLandsNight.wav", grasslandsNightSounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\GrassLandsNight.wav", grasslandsNightSounds_[0], assets);
|
||||
|
||||
jungleDaySounds_.resize(1);
|
||||
bool jungleDayLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\JungleDay.wav", jungleDaySounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\JungleDay.wav", jungleDaySounds_[0], assets);
|
||||
|
||||
jungleNightSounds_.resize(1);
|
||||
bool jungleNightLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\JungleNight.wav", jungleNightSounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\JungleNight.wav", jungleNightSounds_[0], assets);
|
||||
|
||||
marshDaySounds_.resize(1);
|
||||
bool marshDayLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\MarshDay.wav", marshDaySounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\MarshDay.wav", marshDaySounds_[0], assets);
|
||||
|
||||
marshNightSounds_.resize(1);
|
||||
bool marshNightLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\MarshNight.wav", marshNightSounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\MarshNight.wav", marshNightSounds_[0], assets);
|
||||
|
||||
desertCanyonDaySounds_.resize(1);
|
||||
bool desertCanyonDayLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\CanyonDesertDay.wav", desertCanyonDaySounds_[0], assets);
|
||||
|
||||
desertCanyonNightSounds_.resize(1);
|
||||
bool desertCanyonNightLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\CanyonDesertNight.wav", desertCanyonNightSounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\CanyonDesertNight.wav", desertCanyonNightSounds_[0], assets);
|
||||
|
||||
desertPlainsDaySounds_.resize(1);
|
||||
bool desertPlainsDayLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\PlainsDesertDay.wav", desertPlainsDaySounds_[0], assets);
|
||||
|
||||
desertPlainsNightSounds_.resize(1);
|
||||
bool desertPlainsNightLoaded = loadSound("Sound\\Ambience\\ZoneAmbience\\PlainsDesertNight.wav", desertPlainsNightSounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\ZoneAmbience\\PlainsDesertNight.wav", desertPlainsNightSounds_[0], assets);
|
||||
|
||||
// Load city ambience sounds (day and night where available)
|
||||
stormwindDaySounds_.resize(1);
|
||||
|
|
@ -169,10 +169,10 @@ bool AmbientSoundManager::initialize(pipeline::AssetManager* assets) {
|
|||
bool ironforgeLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\Ironforge.wav", ironforgeSounds_[0], assets);
|
||||
|
||||
darnassusDaySounds_.resize(1);
|
||||
bool darnassusDayLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\DarnassusDay.wav", darnassusDaySounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\WMOAmbience\\DarnassusDay.wav", darnassusDaySounds_[0], assets);
|
||||
|
||||
darnassusNightSounds_.resize(1);
|
||||
bool darnassusNightLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\DarnassusNight.wav", darnassusNightSounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\WMOAmbience\\DarnassusNight.wav", darnassusNightSounds_[0], assets);
|
||||
|
||||
orgrimmarDaySounds_.resize(1);
|
||||
bool orgrimmarDayLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\OrgrimmarDay.wav", orgrimmarDaySounds_[0], assets);
|
||||
|
|
@ -181,13 +181,13 @@ bool AmbientSoundManager::initialize(pipeline::AssetManager* assets) {
|
|||
bool orgrimmarNightLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\OrgrimmarNight.wav", orgrimmarNightSounds_[0], assets);
|
||||
|
||||
undercitySounds_.resize(1);
|
||||
bool undercityLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\Undercity.wav", undercitySounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\WMOAmbience\\Undercity.wav", undercitySounds_[0], assets);
|
||||
|
||||
thunderbluffDaySounds_.resize(1);
|
||||
bool thunderbluffDayLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\ThunderBluffDay.wav", thunderbluffDaySounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\WMOAmbience\\ThunderBluffDay.wav", thunderbluffDaySounds_[0], assets);
|
||||
|
||||
thunderbluffNightSounds_.resize(1);
|
||||
bool thunderbluffNightLoaded = loadSound("Sound\\Ambience\\WMOAmbience\\ThunderBluffNight.wav", thunderbluffNightSounds_[0], assets);
|
||||
loadSound("Sound\\Ambience\\WMOAmbience\\ThunderBluffNight.wav", thunderbluffNightSounds_[0], assets);
|
||||
|
||||
// Load bell toll sounds
|
||||
bellAllianceSounds_.resize(1);
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ void CombatSoundManager::playWeaponMiss(bool twoHanded) {
|
|||
}
|
||||
}
|
||||
|
||||
void CombatSoundManager::playImpact(WeaponSize weaponSize, ImpactType impactType, bool isCrit) {
|
||||
void CombatSoundManager::playImpact([[maybe_unused]] WeaponSize weaponSize, ImpactType impactType, bool isCrit) {
|
||||
// Select appropriate impact sound library
|
||||
const std::vector<CombatSample>* normalLibrary = nullptr;
|
||||
const std::vector<CombatSample>* critLibrary = nullptr;
|
||||
|
|
|
|||
|
|
@ -34,16 +34,16 @@ bool UiSoundManager::initialize(pipeline::AssetManager* assets) {
|
|||
bool charSheetCloseLoaded = loadSound("Sound\\Interface\\iAbilitiesCloseA.wav", characterSheetCloseSounds_[0], assets);
|
||||
|
||||
auctionOpenSounds_.resize(1);
|
||||
bool auctionOpenLoaded = loadSound("Sound\\Interface\\AuctionWindowOpen.wav", auctionOpenSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\AuctionWindowOpen.wav", auctionOpenSounds_[0], assets);
|
||||
|
||||
auctionCloseSounds_.resize(1);
|
||||
bool auctionCloseLoaded = loadSound("Sound\\Interface\\AuctionWindowClose.wav", auctionCloseSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\AuctionWindowClose.wav", auctionCloseSounds_[0], assets);
|
||||
|
||||
guildBankOpenSounds_.resize(1);
|
||||
bool guildBankOpenLoaded = loadSound("Sound\\Interface\\GuildVaultOpen.wav", guildBankOpenSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\GuildVaultOpen.wav", guildBankOpenSounds_[0], assets);
|
||||
|
||||
guildBankCloseSounds_.resize(1);
|
||||
bool guildBankCloseLoaded = loadSound("Sound\\Interface\\GuildVaultClose.wav", guildBankCloseSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\GuildVaultClose.wav", guildBankCloseSounds_[0], assets);
|
||||
|
||||
// Load button sounds
|
||||
buttonClickSounds_.resize(1);
|
||||
|
|
@ -63,7 +63,7 @@ bool UiSoundManager::initialize(pipeline::AssetManager* assets) {
|
|||
bool questFailedLoaded = loadSound("Sound\\Interface\\igQuestFailed.wav", questFailedSounds_[0], assets);
|
||||
|
||||
questUpdateSounds_.resize(1);
|
||||
bool questUpdateLoaded = loadSound("Sound\\Interface\\iQuestUpdate.wav", questUpdateSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\iQuestUpdate.wav", questUpdateSounds_[0], assets);
|
||||
|
||||
// Load loot sounds
|
||||
lootCoinSmallSounds_.resize(1);
|
||||
|
|
@ -86,13 +86,13 @@ bool UiSoundManager::initialize(pipeline::AssetManager* assets) {
|
|||
bool pickupBookLoaded = loadSound("Sound\\Interface\\PickUp\\PickUpBook.wav", pickupBookSounds_[0], assets);
|
||||
|
||||
pickupClothSounds_.resize(1);
|
||||
bool pickupClothLoaded = loadSound("Sound\\Interface\\PickUp\\PickUpCloth_Leather01.wav", pickupClothSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\PickUp\\PickUpCloth_Leather01.wav", pickupClothSounds_[0], assets);
|
||||
|
||||
pickupFoodSounds_.resize(1);
|
||||
bool pickupFoodLoaded = loadSound("Sound\\Interface\\PickUp\\PickUpFoodGeneric.wav", pickupFoodSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\PickUp\\PickUpFoodGeneric.wav", pickupFoodSounds_[0], assets);
|
||||
|
||||
pickupGemSounds_.resize(1);
|
||||
bool pickupGemLoaded = loadSound("Sound\\Interface\\PickUp\\PickUpGems.wav", pickupGemSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\PickUp\\PickUpGems.wav", pickupGemSounds_[0], assets);
|
||||
|
||||
// Load eating/drinking sounds
|
||||
eatingSounds_.resize(1);
|
||||
|
|
@ -107,13 +107,13 @@ bool UiSoundManager::initialize(pipeline::AssetManager* assets) {
|
|||
|
||||
// Load error/feedback sounds
|
||||
errorSounds_.resize(1);
|
||||
bool errorLoaded = loadSound("Sound\\Interface\\Error.wav", errorSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\Error.wav", errorSounds_[0], assets);
|
||||
|
||||
selectTargetSounds_.resize(1);
|
||||
bool selectTargetLoaded = loadSound("Sound\\Interface\\iSelectTarget.wav", selectTargetSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\iSelectTarget.wav", selectTargetSounds_[0], assets);
|
||||
|
||||
deselectTargetSounds_.resize(1);
|
||||
bool deselectTargetLoaded = loadSound("Sound\\Interface\\iDeselectTarget.wav", deselectTargetSounds_[0], assets);
|
||||
loadSound("Sound\\Interface\\iDeselectTarget.wav", deselectTargetSounds_[0], assets);
|
||||
|
||||
LOG_INFO("UISoundManager: Window sounds - Bag: ", (bagOpenLoaded && bagCloseLoaded) ? "YES" : "NO",
|
||||
", QuestLog: ", (questLogOpenLoaded && questLogCloseLoaded) ? "YES" : "NO",
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ bool WardenEmulator::initialize(const void* moduleCode, size_t moduleSize, uint3
|
|||
|
||||
uint32_t WardenEmulator::hookAPI(const std::string& dllName,
|
||||
const std::string& functionName,
|
||||
std::function<uint32_t(WardenEmulator&, const std::vector<uint32_t>&)> handler) {
|
||||
[[maybe_unused]] std::function<uint32_t(WardenEmulator&, const std::vector<uint32_t>&)> handler) {
|
||||
// Allocate address for this API stub
|
||||
static uint32_t nextStubAddr = API_STUB_BASE;
|
||||
uint32_t stubAddr = nextStubAddr;
|
||||
|
|
@ -239,7 +239,7 @@ std::string WardenEmulator::readString(uint32_t address, size_t maxLen) {
|
|||
return std::string(buffer.data());
|
||||
}
|
||||
|
||||
uint32_t WardenEmulator::allocateMemory(size_t size, uint32_t protection) {
|
||||
uint32_t WardenEmulator::allocateMemory(size_t size, [[maybe_unused]] uint32_t protection) {
|
||||
// Align to 4KB
|
||||
size = (size + 0xFFF) & ~0xFFF;
|
||||
|
||||
|
|
@ -315,7 +315,7 @@ uint32_t WardenEmulator::apiVirtualFree(WardenEmulator& emu, const std::vector<u
|
|||
return emu.freeMemory(lpAddress) ? 1 : 0;
|
||||
}
|
||||
|
||||
uint32_t WardenEmulator::apiGetTickCount(WardenEmulator& emu, const std::vector<uint32_t>& args) {
|
||||
uint32_t WardenEmulator::apiGetTickCount([[maybe_unused]] WardenEmulator& emu, [[maybe_unused]] const std::vector<uint32_t>& args) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
|
||||
uint32_t ticks = static_cast<uint32_t>(ms & 0xFFFFFFFF);
|
||||
|
|
@ -324,7 +324,7 @@ uint32_t WardenEmulator::apiGetTickCount(WardenEmulator& emu, const std::vector<
|
|||
return ticks;
|
||||
}
|
||||
|
||||
uint32_t WardenEmulator::apiSleep(WardenEmulator& emu, const std::vector<uint32_t>& args) {
|
||||
uint32_t WardenEmulator::apiSleep([[maybe_unused]] WardenEmulator& emu, const std::vector<uint32_t>& args) {
|
||||
if (args.size() < 1) return 0;
|
||||
uint32_t dwMilliseconds = args[0];
|
||||
|
||||
|
|
@ -333,12 +333,12 @@ uint32_t WardenEmulator::apiSleep(WardenEmulator& emu, const std::vector<uint32_
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint32_t WardenEmulator::apiGetCurrentThreadId(WardenEmulator& emu, const std::vector<uint32_t>& args) {
|
||||
uint32_t WardenEmulator::apiGetCurrentThreadId([[maybe_unused]] WardenEmulator& emu, [[maybe_unused]] const std::vector<uint32_t>& args) {
|
||||
std::cout << "[WinAPI] GetCurrentThreadId() = 1234" << '\n';
|
||||
return 1234; // Fake thread ID
|
||||
}
|
||||
|
||||
uint32_t WardenEmulator::apiGetCurrentProcessId(WardenEmulator& emu, const std::vector<uint32_t>& args) {
|
||||
uint32_t WardenEmulator::apiGetCurrentProcessId([[maybe_unused]] WardenEmulator& emu, [[maybe_unused]] const std::vector<uint32_t>& args) {
|
||||
std::cout << "[WinAPI] GetCurrentProcessId() = 5678" << '\n';
|
||||
return 5678; // Fake process ID
|
||||
}
|
||||
|
|
@ -347,7 +347,7 @@ uint32_t WardenEmulator::apiReadProcessMemory(WardenEmulator& emu, const std::ve
|
|||
// ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead)
|
||||
if (args.size() < 5) return 0;
|
||||
|
||||
uint32_t hProcess = args[0];
|
||||
[[maybe_unused]] uint32_t hProcess = args[0];
|
||||
uint32_t lpBaseAddress = args[1];
|
||||
uint32_t lpBuffer = args[2];
|
||||
uint32_t nSize = args[3];
|
||||
|
|
@ -377,13 +377,11 @@ uint32_t WardenEmulator::apiReadProcessMemory(WardenEmulator& emu, const std::ve
|
|||
// Unicorn Callbacks
|
||||
// ============================================================================
|
||||
|
||||
void WardenEmulator::hookCode(uc_engine* uc, uint64_t address, uint32_t size, void* userData) {
|
||||
WardenEmulator* emu = static_cast<WardenEmulator*>(userData);
|
||||
void WardenEmulator::hookCode([[maybe_unused]] uc_engine* uc, uint64_t address, [[maybe_unused]] uint32_t size, [[maybe_unused]] void* userData) {
|
||||
std::cout << "[Trace] 0x" << std::hex << address << std::dec << '\n';
|
||||
}
|
||||
|
||||
void WardenEmulator::hookMemInvalid(uc_engine* uc, int type, uint64_t address, int size, int64_t value, void* userData) {
|
||||
WardenEmulator* emu = static_cast<WardenEmulator*>(userData);
|
||||
void WardenEmulator::hookMemInvalid([[maybe_unused]] uc_engine* uc, int type, uint64_t address, int size, [[maybe_unused]] int64_t value, [[maybe_unused]] void* userData) {
|
||||
|
||||
const char* typeStr = "UNKNOWN";
|
||||
switch (type) {
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ bool WardenModule::load(const std::vector<uint8_t>& moduleData,
|
|||
}
|
||||
|
||||
bool WardenModule::processCheckRequest(const std::vector<uint8_t>& checkData,
|
||||
std::vector<uint8_t>& responseOut) {
|
||||
[[maybe_unused]] std::vector<uint8_t>& responseOut) {
|
||||
if (!loaded_) {
|
||||
std::cerr << "[WardenModule] Module not loaded, cannot process checks" << '\n';
|
||||
return false;
|
||||
|
|
@ -198,7 +198,7 @@ bool WardenModule::processCheckRequest(const std::vector<uint8_t>& checkData,
|
|||
return false;
|
||||
}
|
||||
|
||||
uint32_t WardenModule::tick(uint32_t deltaMs) {
|
||||
uint32_t WardenModule::tick([[maybe_unused]] uint32_t deltaMs) {
|
||||
if (!loaded_ || !funcList_.tick) {
|
||||
return 0; // No tick needed
|
||||
}
|
||||
|
|
@ -209,7 +209,7 @@ uint32_t WardenModule::tick(uint32_t deltaMs) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void WardenModule::generateRC4Keys(uint8_t* packet) {
|
||||
void WardenModule::generateRC4Keys([[maybe_unused]] uint8_t* packet) {
|
||||
if (!loaded_ || !funcList_.generateRC4Keys) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -633,9 +633,11 @@ bool WardenModule::applyRelocations() {
|
|||
currentOffset += delta;
|
||||
|
||||
if (currentOffset + 4 <= moduleSize_) {
|
||||
uint32_t* ptr = reinterpret_cast<uint32_t*>(
|
||||
static_cast<uint8_t*>(moduleMemory_) + currentOffset);
|
||||
*ptr += moduleBase_;
|
||||
uint8_t* addr = static_cast<uint8_t*>(moduleMemory_) + currentOffset;
|
||||
uint32_t val;
|
||||
std::memcpy(&val, addr, sizeof(uint32_t));
|
||||
val += moduleBase_;
|
||||
std::memcpy(addr, &val, sizeof(uint32_t));
|
||||
relocCount++;
|
||||
} else {
|
||||
std::cerr << "[WardenModule] Relocation offset " << currentOffset
|
||||
|
|
@ -755,16 +757,16 @@ bool WardenModule::initializeModule() {
|
|||
void (*logMessage)(const char* msg);
|
||||
};
|
||||
|
||||
// Setup client callbacks
|
||||
ClientCallbacks callbacks = {};
|
||||
// Setup client callbacks (used when calling module entry point below)
|
||||
[[maybe_unused]] ClientCallbacks callbacks = {};
|
||||
|
||||
// Stub callbacks (would need real implementations)
|
||||
callbacks.sendPacket = [](uint8_t* data, size_t len) {
|
||||
callbacks.sendPacket = []([[maybe_unused]] uint8_t* data, size_t len) {
|
||||
std::cout << "[WardenModule Callback] sendPacket(" << len << " bytes)" << '\n';
|
||||
// TODO: Send CMSG_WARDEN_DATA packet
|
||||
};
|
||||
|
||||
callbacks.validateModule = [](uint8_t* hash) {
|
||||
callbacks.validateModule = []([[maybe_unused]] uint8_t* hash) {
|
||||
std::cout << "[WardenModule Callback] validateModule()" << '\n';
|
||||
// TODO: Validate module hash
|
||||
};
|
||||
|
|
@ -779,7 +781,7 @@ bool WardenModule::initializeModule() {
|
|||
free(ptr);
|
||||
};
|
||||
|
||||
callbacks.generateRC4 = [](uint8_t* seed) {
|
||||
callbacks.generateRC4 = []([[maybe_unused]] uint8_t* seed) {
|
||||
std::cout << "[WardenModule Callback] generateRC4()" << '\n';
|
||||
// TODO: Re-key RC4 cipher
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4276,6 +4276,7 @@ network::Packet AuctionListItemsPacket::build(
|
|||
p.writeUInt32(quality);
|
||||
p.writeUInt8(usableOnly);
|
||||
p.writeUInt8(0); // getAll (0 = normal search)
|
||||
p.writeUInt8(exactMatch);
|
||||
// Sort columns (0 = none)
|
||||
p.writeUInt8(0);
|
||||
return p;
|
||||
|
|
|
|||
|
|
@ -30,34 +30,39 @@ BLPImage BLPLoader::load(const std::vector<uint8_t>& blpData) {
|
|||
}
|
||||
|
||||
BLPImage BLPLoader::loadBLP1(const uint8_t* data, size_t size) {
|
||||
// BLP1 header has all uint32 fields (different layout from BLP2)
|
||||
const BLP1Header* header = reinterpret_cast<const BLP1Header*>(data);
|
||||
// Copy header to stack to avoid unaligned reinterpret_cast (UB on strict platforms)
|
||||
if (size < sizeof(BLP1Header)) {
|
||||
LOG_ERROR("BLP1 data too small for header");
|
||||
return BLPImage();
|
||||
}
|
||||
BLP1Header header;
|
||||
std::memcpy(&header, data, sizeof(BLP1Header));
|
||||
|
||||
BLPImage image;
|
||||
image.format = BLPFormat::BLP1;
|
||||
image.width = header->width;
|
||||
image.height = header->height;
|
||||
image.width = header.width;
|
||||
image.height = header.height;
|
||||
image.channels = 4;
|
||||
image.mipLevels = header->hasMips ? 16 : 1;
|
||||
image.mipLevels = header.hasMips ? 16 : 1;
|
||||
|
||||
// BLP1 compression: 0=JPEG (not used in WoW), 1=palette/indexed
|
||||
// BLP1 does NOT support DXT — only palette with optional alpha
|
||||
if (header->compression == 1) {
|
||||
if (header.compression == 1) {
|
||||
image.compression = BLPCompression::PALETTE;
|
||||
} else if (header->compression == 0) {
|
||||
} else if (header.compression == 0) {
|
||||
LOG_WARNING("BLP1 JPEG compression not supported");
|
||||
return BLPImage();
|
||||
} else {
|
||||
LOG_WARNING("BLP1 unknown compression: ", header->compression);
|
||||
LOG_WARNING("BLP1 unknown compression: ", header.compression);
|
||||
return BLPImage();
|
||||
}
|
||||
|
||||
LOG_DEBUG("Loading BLP1: ", image.width, "x", image.height, " ",
|
||||
getCompressionName(image.compression), " alpha=", header->alphaBits);
|
||||
getCompressionName(image.compression), " alpha=", header.alphaBits);
|
||||
|
||||
// Get first mipmap (full resolution)
|
||||
uint32_t offset = header->mipOffsets[0];
|
||||
uint32_t mipSize = header->mipSizes[0];
|
||||
uint32_t offset = header.mipOffsets[0];
|
||||
uint32_t mipSize = header.mipSizes[0];
|
||||
|
||||
if (offset + mipSize > size) {
|
||||
LOG_ERROR("BLP1 mipmap data out of bounds (offset=", offset, " size=", mipSize, " fileSize=", size, ")");
|
||||
|
|
@ -70,45 +75,50 @@ BLPImage BLPLoader::loadBLP1(const uint8_t* data, size_t size) {
|
|||
int pixelCount = image.width * image.height;
|
||||
image.data.resize(pixelCount * 4); // RGBA8
|
||||
|
||||
decompressPalette(mipData, image.data.data(), header->palette,
|
||||
image.width, image.height, static_cast<uint8_t>(header->alphaBits));
|
||||
decompressPalette(mipData, image.data.data(), header.palette,
|
||||
image.width, image.height, static_cast<uint8_t>(header.alphaBits));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
BLPImage BLPLoader::loadBLP2(const uint8_t* data, size_t size) {
|
||||
// BLP2 header has uint8 fields for compression/alpha/encoding
|
||||
const BLP2Header* header = reinterpret_cast<const BLP2Header*>(data);
|
||||
// Copy header to stack to avoid unaligned reinterpret_cast (UB on strict platforms)
|
||||
if (size < sizeof(BLP2Header)) {
|
||||
LOG_ERROR("BLP2 data too small for header");
|
||||
return BLPImage();
|
||||
}
|
||||
BLP2Header header;
|
||||
std::memcpy(&header, data, sizeof(BLP2Header));
|
||||
|
||||
BLPImage image;
|
||||
image.format = BLPFormat::BLP2;
|
||||
image.width = header->width;
|
||||
image.height = header->height;
|
||||
image.width = header.width;
|
||||
image.height = header.height;
|
||||
image.channels = 4;
|
||||
image.mipLevels = header->hasMips ? 16 : 1;
|
||||
image.mipLevels = header.hasMips ? 16 : 1;
|
||||
|
||||
// BLP2 compression types:
|
||||
// 1 = palette/uncompressed
|
||||
// 2 = DXTC (DXT1/DXT3/DXT5 based on alphaDepth + alphaEncoding)
|
||||
// 3 = plain A8R8G8B8
|
||||
if (header->compression == 1) {
|
||||
if (header.compression == 1) {
|
||||
image.compression = BLPCompression::PALETTE;
|
||||
} else if (header->compression == 2) {
|
||||
} else if (header.compression == 2) {
|
||||
// BLP2 DXTC format selection based on alphaDepth + alphaEncoding:
|
||||
// alphaDepth=0 → DXT1 (no alpha)
|
||||
// alphaDepth>0, alphaEncoding=0 → DXT1 (1-bit alpha)
|
||||
// alphaDepth>0, alphaEncoding=1 → DXT3 (explicit 4-bit alpha)
|
||||
// alphaDepth>0, alphaEncoding=7 → DXT5 (interpolated alpha)
|
||||
if (header->alphaDepth == 0 || header->alphaEncoding == 0) {
|
||||
if (header.alphaDepth == 0 || header.alphaEncoding == 0) {
|
||||
image.compression = BLPCompression::DXT1;
|
||||
} else if (header->alphaEncoding == 1) {
|
||||
} else if (header.alphaEncoding == 1) {
|
||||
image.compression = BLPCompression::DXT3;
|
||||
} else if (header->alphaEncoding == 7) {
|
||||
} else if (header.alphaEncoding == 7) {
|
||||
image.compression = BLPCompression::DXT5;
|
||||
} else {
|
||||
image.compression = BLPCompression::DXT1;
|
||||
}
|
||||
} else if (header->compression == 3) {
|
||||
} else if (header.compression == 3) {
|
||||
image.compression = BLPCompression::ARGB8888;
|
||||
} else {
|
||||
image.compression = BLPCompression::ARGB8888;
|
||||
|
|
@ -116,13 +126,13 @@ BLPImage BLPLoader::loadBLP2(const uint8_t* data, size_t size) {
|
|||
|
||||
LOG_DEBUG("Loading BLP2: ", image.width, "x", image.height, " ",
|
||||
getCompressionName(image.compression),
|
||||
" (comp=", (int)header->compression, " alphaDepth=", (int)header->alphaDepth,
|
||||
" alphaEnc=", (int)header->alphaEncoding, " mipOfs=", header->mipOffsets[0],
|
||||
" mipSize=", header->mipSizes[0], ")");
|
||||
" (comp=", (int)header.compression, " alphaDepth=", (int)header.alphaDepth,
|
||||
" alphaEnc=", (int)header.alphaEncoding, " mipOfs=", header.mipOffsets[0],
|
||||
" mipSize=", header.mipSizes[0], ")");
|
||||
|
||||
// Get first mipmap (full resolution)
|
||||
uint32_t offset = header->mipOffsets[0];
|
||||
uint32_t mipSize = header->mipSizes[0];
|
||||
uint32_t offset = header.mipOffsets[0];
|
||||
uint32_t mipSize = header.mipSizes[0];
|
||||
|
||||
if (offset + mipSize > size) {
|
||||
LOG_ERROR("BLP2 mipmap data out of bounds");
|
||||
|
|
@ -149,8 +159,8 @@ BLPImage BLPLoader::loadBLP2(const uint8_t* data, size_t size) {
|
|||
break;
|
||||
|
||||
case BLPCompression::PALETTE:
|
||||
decompressPalette(mipData, image.data.data(), header->palette,
|
||||
image.width, image.height, header->alphaDepth);
|
||||
decompressPalette(mipData, image.data.data(), header.palette,
|
||||
image.width, image.height, header.alphaDepth);
|
||||
break;
|
||||
|
||||
case BLPCompression::ARGB8888:
|
||||
|
|
|
|||
|
|
@ -42,19 +42,20 @@ bool DBCFile::load(const std::vector<uint8_t>& dbcData) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Read header
|
||||
const DBCHeader* header = reinterpret_cast<const DBCHeader*>(dbcData.data());
|
||||
// Read header safely (avoid unaligned reinterpret_cast — UB on strict platforms)
|
||||
DBCHeader header;
|
||||
std::memcpy(&header, dbcData.data(), sizeof(DBCHeader));
|
||||
|
||||
// Verify magic
|
||||
if (std::memcmp(header->magic, "WDBC", 4) != 0) {
|
||||
LOG_ERROR("Invalid DBC magic: ", std::string(header->magic, 4));
|
||||
if (std::memcmp(header.magic, "WDBC", 4) != 0) {
|
||||
LOG_ERROR("Invalid DBC magic: ", std::string(header.magic, 4));
|
||||
return false;
|
||||
}
|
||||
|
||||
recordCount = header->recordCount;
|
||||
fieldCount = header->fieldCount;
|
||||
recordSize = header->recordSize;
|
||||
stringBlockSize = header->stringBlockSize;
|
||||
recordCount = header.recordCount;
|
||||
fieldCount = header.fieldCount;
|
||||
recordSize = header.recordSize;
|
||||
stringBlockSize = header.stringBlockSize;
|
||||
|
||||
// Validate sizes
|
||||
uint32_t expectedSize = sizeof(DBCHeader) + (recordCount * recordSize) + stringBlockSize;
|
||||
|
|
@ -111,8 +112,9 @@ uint32_t DBCFile::getUInt32(uint32_t recordIndex, uint32_t fieldIndex) const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
const uint32_t* field = reinterpret_cast<const uint32_t*>(record + (fieldIndex * 4));
|
||||
return *field;
|
||||
uint32_t value;
|
||||
std::memcpy(&value, record + (fieldIndex * 4), sizeof(uint32_t));
|
||||
return value;
|
||||
}
|
||||
|
||||
int32_t DBCFile::getInt32(uint32_t recordIndex, uint32_t fieldIndex) const {
|
||||
|
|
@ -129,8 +131,9 @@ float DBCFile::getFloat(uint32_t recordIndex, uint32_t fieldIndex) const {
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
const float* field = reinterpret_cast<const float*>(record + (fieldIndex * 4));
|
||||
return *field;
|
||||
float value;
|
||||
std::memcpy(&value, record + (fieldIndex * 4), sizeof(float));
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string DBCFile::getString(uint32_t recordIndex, uint32_t fieldIndex) const {
|
||||
|
|
|
|||
|
|
@ -1456,9 +1456,7 @@ bool M2Loader::loadSkin(const std::vector<uint8_t>& skinData, M2Model& model) {
|
|||
if (header.nSubmeshes > 0 && header.ofsSubmeshes > 0) {
|
||||
submeshes = readArray<M2SkinSubmesh>(skinData, header.ofsSubmeshes, header.nSubmeshes);
|
||||
core::Logger::getInstance().debug(" Submeshes: ", submeshes.size());
|
||||
for (size_t i = 0; i < submeshes.size(); i++) {
|
||||
const auto& sm = submeshes[i];
|
||||
}
|
||||
(void)submeshes;
|
||||
}
|
||||
|
||||
// Read batches with proper submesh references
|
||||
|
|
|
|||
|
|
@ -1610,7 +1610,7 @@ glm::mat4 CharacterRenderer::getBoneTransform(const pipeline::M2Bone& bone, floa
|
|||
|
||||
// --- Rendering ---
|
||||
|
||||
void CharacterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const Camera& camera) {
|
||||
void CharacterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, [[maybe_unused]] const Camera& camera) {
|
||||
if (instances.empty() || !opaquePipeline_) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2381,11 +2381,11 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
}
|
||||
}
|
||||
|
||||
// Bind material descriptor set (set 1)
|
||||
if (batch.materialSet) {
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipelineLayout_, 1, 1, &batch.materialSet, 0, nullptr);
|
||||
}
|
||||
// Bind material descriptor set (set 1) — skip batch if missing
|
||||
// to avoid inheriting a stale descriptor set from a prior renderer
|
||||
if (!batch.materialSet) continue;
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipelineLayout_, 1, 1, &batch.materialSet, 0, nullptr);
|
||||
|
||||
// Push constants
|
||||
M2PushConstants pc;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ bool VkRenderTarget::create(VkContext& ctx, uint32_t width, uint32_t height,
|
|||
|
||||
// Create color image (multisampled if MSAA)
|
||||
colorImage_ = createImage(device, allocator, width, height, format,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | (useMSAA ? VkImageUsageFlags(0) : VK_IMAGE_USAGE_SAMPLED_BIT),
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | (useMSAA ? static_cast<VkImageUsageFlags>(0) : static_cast<VkImageUsageFlags>(VK_IMAGE_USAGE_SAMPLED_BIT)),
|
||||
msaaSamples);
|
||||
|
||||
if (!colorImage_.image) {
|
||||
|
|
|
|||
|
|
@ -3766,7 +3766,6 @@ void GameScreen::renderBagBar(game::GameHandler& gameHandler) {
|
|||
|
||||
// Track bag slot screen rects for drop detection
|
||||
ImVec2 bagSlotMins[4], bagSlotMaxs[4];
|
||||
VkDescriptorSet bagIcons[4] = {};
|
||||
|
||||
// Slots 1-4: Bag slots (leftmost)
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
|
|
@ -3780,8 +3779,6 @@ void GameScreen::renderBagBar(game::GameHandler& gameHandler) {
|
|||
if (!bagItem.empty() && bagItem.item.displayInfoId != 0) {
|
||||
bagIcon = inventoryScreen.getItemIcon(bagItem.item.displayInfoId);
|
||||
}
|
||||
bagIcons[i] = bagIcon;
|
||||
|
||||
// Render the slot as an invisible button so we control all interaction
|
||||
ImVec2 cpos = ImGui::GetCursorScreenPos();
|
||||
ImGui::InvisibleButton("##bagSlot", ImVec2(slotSize, slotSize));
|
||||
|
|
@ -7483,7 +7480,6 @@ void GameScreen::renderBankWindow(game::GameHandler& gameHandler) {
|
|||
if (slot.empty()) {
|
||||
ImGui::Button("##bank", ImVec2(42, 42));
|
||||
} else {
|
||||
auto* info = gameHandler.getItemInfo(slot.item.itemId);
|
||||
ImVec4 qc = InventoryScreen::getQualityColor(slot.item.quality);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(qc.x * 0.3f, qc.y * 0.3f, qc.z * 0.3f, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(qc.x * 0.5f, qc.y * 0.5f, qc.z * 0.5f, 0.9f));
|
||||
|
|
|
|||
|
|
@ -202,7 +202,6 @@ void TalentScreen::renderTalent(game::GameHandler& gameHandler,
|
|||
auto* assetManager = core::Application::getInstance().getAssetManager();
|
||||
|
||||
uint8_t currentRank = gameHandler.getTalentRank(talent.talentId);
|
||||
uint8_t nextRank = currentRank + 1;
|
||||
|
||||
// Check if can learn
|
||||
bool canLearn = currentRank < talent.maxRank &&
|
||||
|
|
@ -297,7 +296,6 @@ void TalentScreen::renderTalent(game::GameHandler& gameHandler,
|
|||
|
||||
// Rank indicator overlay
|
||||
if (talent.maxRank > 1) {
|
||||
ImVec2 pMin = ImGui::GetItemRectMin();
|
||||
ImVec2 pMax = ImGui::GetItemRectMax();
|
||||
auto* drawList = ImGui::GetWindowDrawList();
|
||||
|
||||
|
|
|
|||
2198
tools/asset_pipeline_gui.py
Executable file
2198
tools/asset_pipeline_gui.py
Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -50,7 +50,8 @@ if command -v zstd &>/dev/null; then
|
|||
tar cf - -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")" | zstd -T0 -3 -o "$ARCHIVE"
|
||||
elif command -v pigz &>/dev/null; then
|
||||
ARCHIVE="$BACKUP_DIR/wowee_assets_$TIMESTAMP.tar.gz"
|
||||
tar cf - -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")" | pigz -p "$(nproc)" > "$ARCHIVE"
|
||||
NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)
|
||||
tar cf - -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")" | pigz -p "$NPROC" > "$ARCHIVE"
|
||||
else
|
||||
ARCHIVE="$BACKUP_DIR/wowee_assets_$TIMESTAMP.tar.gz"
|
||||
tar czf "$ARCHIVE" -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")"
|
||||
|
|
|
|||
2170
tools/m2_viewer.py
Normal file
2170
tools/m2_viewer.py
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue