From 63dbfe74b00be01fefb74777d4d7568d98679c84 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 09:16:43 -0700 Subject: [PATCH] fix(wom): cap top-level vert/index/tex counts on save Top-level WOM save was writing raw model.vertices/.indices/.texturePaths sizes; load enforces 1M / 4M / 1024 limits. A pathological model would emit a header rejected on load, leaking the rest of the file body. Cap each count at the load limit and iterate the WOM1 vertex block + texture-path block by index so the body matches the header. --- src/pipeline/wowee_model.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/pipeline/wowee_model.cpp b/src/pipeline/wowee_model.cpp index f365809f..71e53a2a 100644 --- a/src/pipeline/wowee_model.cpp +++ b/src/pipeline/wowee_model.cpp @@ -255,9 +255,14 @@ bool WoweeModelLoader::save(const WoweeModel& model, const std::string& basePath uint32_t magic = hasBatches ? WOM3_MAGIC : (hasAnim ? WOM2_MAGIC : WOM_MAGIC); f.write(reinterpret_cast(&magic), 4); - uint32_t vertCount = static_cast(model.vertices.size()); - uint32_t indexCount = static_cast(model.indices.size()); - uint32_t texCount = static_cast(model.texturePaths.size()); + // Cap top-level counts at the load-side limits so a pathological + // model can't write a file the loader rejects whole. + uint32_t vertCount = static_cast( + std::min(model.vertices.size(), 1'000'000)); + uint32_t indexCount = static_cast( + std::min(model.indices.size(), 4'000'000)); + uint32_t texCount = static_cast( + std::min(model.texturePaths.size(), 1024)); f.write(reinterpret_cast(&vertCount), 4); f.write(reinterpret_cast(&indexCount), 4); f.write(reinterpret_cast(&texCount), 4); @@ -291,7 +296,10 @@ bool WoweeModelLoader::save(const WoweeModel& model, const std::string& basePath f.write(reinterpret_cast(model.vertices.data()), vertCount * sizeof(WoweeModel::Vertex)); } else { - for (const auto& v : model.vertices) { + // WOM1: write only the capped vertCount entries so the body + // matches the header. + for (uint32_t vi = 0; vi < vertCount; vi++) { + const auto& v = model.vertices[vi]; f.write(reinterpret_cast(&v.position), 12); f.write(reinterpret_cast(&v.normal), 12); f.write(reinterpret_cast(&v.texCoord), 8); @@ -308,7 +316,8 @@ bool WoweeModelLoader::save(const WoweeModel& model, const std::string& basePath f.write(reinterpret_cast(sanIdx.data()), indexCount * 4); } - for (const auto& path : model.texturePaths) writeStr(path); + // Match header texCount on round-trip. + for (uint32_t ti = 0; ti < texCount; ti++) writeStr(model.texturePaths[ti]); // WOM2/WOM3: write bones and animations (always, even if empty for WOM3) if (hasAnim || hasBatches) {