From 04852fce7e0791bb26a37b56899a90727eedca70 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 29 Sep 2026 04:24:21 -0500 Subject: [PATCH] fix(zone): zone files of version 37 have a u32 scene count The scene count is a u8 before version 37 (LateAlpha) and a u32 from 37 on (LuzFile::ReadLUZFile, 1.10.64: revision < 37 reads a byte). The reader took the u8 for 37 too, so version 37 files misread from their scenes on and failed. No live zone is version 37; four older LUP zones now read in full. Every other zone file on disk reads the same as before. Check in game: nothing to check on live worlds (none is version 37). Co-Authored-By: Claude Opus 5.5 --- dCommon/ZoneFile.cpp | 3 ++- tests/dCommonTests/ZoneFileTests.cpp | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/dCommon/ZoneFile.cpp b/dCommon/ZoneFile.cpp index cfa61b665..a1c95f1c9 100644 --- a/dCommon/ZoneFile.cpp +++ b/dCommon/ZoneFile.cpp @@ -20,8 +20,9 @@ void ZoneFile::ReadHeader(std::istream& file) { BinaryIO::BinaryRead(file, spawnpointRotation); } + // A u8 before LateAlpha, a u32 from it on (LuzFile::ReadLUZFile) uint32_t sceneCount = 0; - if (fileFormatVersion <= FileFormatVersion::LateAlpha) { + if (fileFormatVersion < FileFormatVersion::LateAlpha) { uint8_t count; BinaryIO::BinaryRead(file, count); sceneCount = count; diff --git a/tests/dCommonTests/ZoneFileTests.cpp b/tests/dCommonTests/ZoneFileTests.cpp index ee5b34600..1b60a06ee 100644 --- a/tests/dCommonTests/ZoneFileTests.cpp +++ b/tests/dCommonTests/ZoneFileTests.cpp @@ -119,6 +119,25 @@ TEST(ZoneFileTests, ReadsZoneBoundaries) { EXPECT_EQ(zone.zoneDesc, "Description"); } +// From LateAlpha (37) on the scene count is a u32 (some LUP zones are 37) +TEST(ZoneFileTests, LateAlphaSceneCountIsAU32) { + ZoneBytes w; + w.Put(37).Put(1).Put(20022); // version, revision, world; no spawn point before 38 + w.Put(1); + w.Text("scene.lvl").Put(5).Put(0).Text("Global Scene").Put(0).Put(0).Put(0); + w.Put(0).Text("zone.raw").Text("Name").Text("Description"); + w.Put(0).Put(8).Put(1).Put(0); // no transitions, no paths + + std::istringstream stream(w.data); + ZoneFile zone; + zone.Read(stream); + EXPECT_FALSE(stream.fail()); + ASSERT_EQ(zone.scenes.size(), 1u); + EXPECT_EQ(zone.scenes[0].filename, "scene.lvl"); + EXPECT_EQ(zone.scenes[0].id, 5u); + EXPECT_EQ(zone.zoneRawPath, "zone.raw"); +} + TEST(ZoneFileTests, ShortFilesThrowOrFail) { const auto zone = SampleZone(); for (const size_t length : { size_t{ 3 }, size_t{ 40 }, zone.size() / 2, zone.size() - 1 }) {