diff --git a/dCommon/ZoneFile.cpp b/dCommon/ZoneFile.cpp index 12825e3dc..4e9b9ee34 100644 --- a/dCommon/ZoneFile.cpp +++ b/dCommon/ZoneFile.cpp @@ -35,6 +35,7 @@ void ZoneFile::ReadHeader(std::istream& file) { for (uint32_t i = 0; i < sceneCount; ++i) { ReadScene(file, i); } + KeepLoadedScenes(); ReadZoneBoundaries(file); @@ -106,6 +107,21 @@ void ZoneFile::ReadScene(std::istream& file, uint32_t index) { scenes.push_back(std::move(scene)); } +void ZoneFile::KeepLoadedScenes() { + if (fileFormatVersion < FileFormatVersion::PrePreAlpha) return; // ResolveSceneTable does this for those + // LuzReader::ReadScenes keeps the scenes by scene ID and layer, a later one replacing an earlier one. + // ZoneLoader::ReadZoneFile then goes through them by scene ID: a scene ID with no General layer is left out, and + // before Latest only the General layer is loaded + std::map, ZoneScene> byID; + for (auto& scene : scenes) byID.insert_or_assign({ scene.id, scene.sceneType }, std::move(scene)); + scenes.clear(); + for (auto& [key, scene] : byID) { + if (!byID.contains({ key.first, eSceneType::General })) continue; + if (key.second != eSceneType::General && fileFormatVersion < FileFormatVersion::Latest) continue; + scenes.push_back(std::move(scene)); + } +} + void ZoneFile::ResolveSceneTable(const std::function(uint32_t)>& sceneName) { if (fileFormatVersion >= FileFormatVersion::PrePreAlpha) return; // ZoneLoader::ReadZoneFile: the scenes go by their SceneTable ID; each one with a row there gets the next index diff --git a/dCommon/ZoneFile.h b/dCommon/ZoneFile.h index e560a93f4..341501f8f 100644 --- a/dCommon/ZoneFile.h +++ b/dCommon/ZoneFile.h @@ -256,6 +256,7 @@ struct ZoneFile { private: void ReadScene(std::istream& file, uint32_t index); + void KeepLoadedScenes(); void ReadZoneBoundaries(std::istream& file); void ReadSceneTransition(std::istream& file); SceneTransitionInfo ReadSceneTransitionInfo(std::istream& file); diff --git a/tests/dCommonTests/ZoneFileTests.cpp b/tests/dCommonTests/ZoneFileTests.cpp index 646741034..3a2df7596 100644 --- a/tests/dCommonTests/ZoneFileTests.cpp +++ b/tests/dCommonTests/ZoneFileTests.cpp @@ -31,7 +31,7 @@ namespace { w.Put(41).Put(3).Put(1150); // version, revision, world w.Point(1, 2, 3).Put(1.0f).Put(0.0f).Put(0.0f).Put(0.0f); // spawn point and rotation w.Put(1); - w.Text("scene.lvl").Put(7).Put(2).Text("Global").Put(1).Put(2).Put(3); + w.Text("scene.lvl").Put(7).Put(0).Text("Global").Put(1).Put(2).Put(3); w.Put(0).Text("zone.raw").Text("Name").Text("Description"); // no zone boundaries w.Put(1); for (int i = 0; i < 2; i++) w.Put(1).Point(0, 0, 0); @@ -65,7 +65,7 @@ TEST(ZoneFileTests, ReadsScenesAndPaths) { ASSERT_EQ(zone.scenes.size(), 1u); EXPECT_EQ(zone.scenes[0].filename, "scene.lvl"); EXPECT_EQ(zone.scenes[0].id, 7u); - EXPECT_EQ(zone.scenes[0].sceneType, eSceneType::FX); + EXPECT_EQ(zone.scenes[0].sceneType, eSceneType::General); EXPECT_EQ(zone.zoneRawPath, "zone.raw"); ASSERT_EQ(zone.sceneTransitions.size(), 1u); EXPECT_EQ(zone.sceneTransitions[0].points.size(), 2u); @@ -183,6 +183,35 @@ TEST(ZoneFileTests, EarlyScenesAreNumberedInOrder) { EXPECT_EQ(zone.scenes[2].filename, "c.lvl"); } +// The scenes the client loads: by scene ID and layer, a later one replacing an earlier one; none without a General +// layer; before version 41 only the General layer +TEST(ZoneFileTests, KeepsTheScenesTheClientLoads) { + for (const uint32_t version : { 40u, 41u }) { + ZoneBytes w; + w.Put(version).Put(1).Put(1100); + w.Point(0, 0, 0).Put(1.0f).Put(0.0f).Put(0.0f).Put(0.0f); + w.Put(5); + const auto scene = [&w](const std::string& file, uint32_t id, uint32_t layer) { + w.Text(file).Put(id).Put(layer).Text("n").Put(0).Put(0).Put(0); + }; + scene("b.lvl", 2, 0); + scene("b_audio.lvl", 2, 1); + scene("orphan_audio.lvl", 3, 1); + scene("a.lvl", 1, 0); + scene("b2.lvl", 2, 0); + w.Put(0).Text("zone.raw").Text("Name").Text("Description"); + w.Put(0).Put(0).Put(1).Put(0); + std::istringstream stream(w.data); + ZoneFile zone; + zone.Read(stream); + EXPECT_FALSE(stream.fail()); + std::vector files; + for (const auto& read : zone.scenes) files.push_back(read.filename); + if (version == 41) EXPECT_EQ(files, (std::vector{ "a.lvl", "b2.lvl", "b_audio.lvl" })); + else EXPECT_EQ(files, (std::vector{ "a.lvl", "b2.lvl" })); + } +} + // A PrePreAlpha (30) file ends at its terrain file's name: no zone name, description, transitions or paths TEST(ZoneFileTests, PrePreAlphaHasNoZoneName) { ZoneBytes w;