fix: read and keep a zone file's scenes as the client does

LuzFile::ReadLUZFile, LuzReader::ReadScenes and ZoneLoader::ReadZoneFile
(1.10.64):
- a version older than 20 is read as 20;
- before version 30 a scene is a bare u32 SceneTable ID: in ID order,
  each one with a SceneTable row gets the next index (-1 past 255) as
  its ID and the row's sceneName as its file; the others are left out;
- from 30 to 32 the file has no scene IDs: a scene's ID is its index in
  the file (they all had ID 0, colliding);
- a later scene with the same ID and layer replaces an earlier one, a
  scene ID with no layer 0 scene is left out, and before version 41
  only the layer 0 scenes are loaded.

No 1.10.64 zone file has a duplicate scene, a scene without a layer 0
scene or another layer before version 41, so what live zones load is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-10-26 21:29:58 -05:00
parent 0ff5d2bf87
commit 1c5c615670
5 changed files with 230 additions and 12 deletions

View File

@@ -51,14 +51,14 @@ protected:
file.write(data.data(), data.size());
}
// Scenes 0 (global), 1, 2 and 3 (general) and 5 (audio, layer 1); transitions 1 <-> 2 and 2 <-> 3
// Scenes 0 (global), 1, 2 and 3 (general) and 3 (audio, layer 1); transitions 1 <-> 2 and 2 <-> 3
void WriteZone(const std::string& rawName = "test.raw", uint8_t boundaryCount = 2, uint32_t version = 41) const {
const std::vector<TestScene> scenes = {
{ "global.lvl", 0, 0, "Global", 10, 20, 30 },
{ "one.lvl", 1, 0, "Scene One", 255, 0, 0 },
{ "two.lvl", 2, 0, "Scene Two", 0, 255, 0 },
{ "three.lvl", 3, 0, "Scene Three", 0, 0, 255 },
{ "audio.lvl", 5, 1, "Audio", 0, 0, 0 },
{ "audio.lvl", 3, 1, "Audio", 0, 0, 0 },
};
WriteFile("test.luz", WriteLuz(TEST_MAP, scenes, boundaryCount, rawName, {
{ { 1, 0, NiPoint3(1.0f, 0.0f, 0.0f) }, { 2, 0, NiPoint3(2.0f, 0.0f, 0.0f) } },
@@ -123,7 +123,7 @@ TEST_F(ZoneSceneTest, ReadsScenesBoundariesAndTransitions) {
EXPECT_FLOAT_EQ(global->color.m_Red, 10 / 255.0f);
EXPECT_FLOAT_EQ(global->color.m_Green, 20 / 255.0f);
EXPECT_FLOAT_EQ(global->color.m_Blue, 30 / 255.0f);
const SceneRef* audio = zone->GetScene(LWOSCENEID(5, 1));
const SceneRef* audio = zone->GetScene(LWOSCENEID(3, 1));
ASSERT_NE(audio, nullptr);
EXPECT_EQ(audio->sceneType, eSceneType::Audio);
EXPECT_EQ(zone->GetScene(LWOSCENEID(4, 0)), nullptr);
@@ -151,7 +151,7 @@ TEST_F(ZoneSceneTest, ReadsOlderZoneVersions) {
WriteZone("test.raw", 2, version);
WriteFile("test.raw", MakeRaw());
const Zone* zone = LoadZone();
EXPECT_EQ(zone->GetScenes().size(), 5) << version;
EXPECT_EQ(zone->GetScenes().size(), 4) << version; // before Latest only layer 0 scenes are loaded
EXPECT_EQ(zone->GetBoundaries().size(), 2) << version;
EXPECT_EQ(zone->GetZoneRawPath(), "test.raw") << version;
EXPECT_EQ(zone->GetZoneName(), "Test Zone") << version;
@@ -185,7 +185,7 @@ TEST_F(ZoneSceneTest, SceneGraphFromTransitions) {
const auto& graph = Game::zoneManager->GetSceneAdjacencyList();
EXPECT_EQ(graph.size(), 4); // the audio scene is left out
EXPECT_FALSE(graph.contains(LWOSCENEID(5, 1)));
EXPECT_FALSE(graph.contains(LWOSCENEID(3, 1)));
const auto one = Game::zoneManager->GetAdjacentScenes(LWOSCENEID(1, 0));
EXPECT_EQ(one.size(), 2);