diff --git a/dCommon/LevelFile.cpp b/dCommon/LevelFile.cpp index f3d7fa2ff..140c82b63 100644 --- a/dCommon/LevelFile.cpp +++ b/dCommon/LevelFile.cpp @@ -1,5 +1,6 @@ #include "LevelFile.h" +#include #include #include @@ -198,8 +199,15 @@ void LevelFile::ReadSceneObjectDataChunk(std::istream& file, uint32_t version) { } if (file.fail()) throw std::runtime_error("Failed to read from istream."); - for (const auto& token : GeneralUtils::SplitString(GeneralUtils::UTF16ToWTF8(ldfString), '\n')) { - obj.settings.ParseInsert(token); + // LDF_FROM_STRING (0x010fa220): entries are split at every comma and line break, empty ones and ones without a + // '=' are left out + const auto config = GeneralUtils::UTF16ToWTF8(ldfString); + size_t start = 0; + while (start <= config.size()) { + const auto end = std::min(config.find_first_of(",\n", start), config.size()); + const auto entry = config.substr(start, end - start); + if (entry.find('=') != std::string::npos) obj.settings.ParseInsert(entry); + start = end + 1; } ApplyClientConfigFixups(obj, version); diff --git a/tests/dCommonTests/ZoneFileTests.cpp b/tests/dCommonTests/ZoneFileTests.cpp index 67e05fe9e..6a53294a0 100644 --- a/tests/dCommonTests/ZoneFileTests.cpp +++ b/tests/dCommonTests/ZoneFileTests.cpp @@ -1,9 +1,11 @@ #include +#include #include #include #include "Game.h" +#include "GeneralUtils.h" #include "LevelFile.h" #include "Logger.h" #include "ZoneFile.h" @@ -535,6 +537,25 @@ TEST(LevelFileTests, FixesUpConfigAsTheClient) { EXPECT_EQ(value(3, u"DisableModelBehaviors"), "DisableModelBehaviors=7:0"); } +// An object's config splits at commas as well as line breaks; entries without a '=' are left out +TEST(LevelFileTests, SplitsConfigAsTheClient) { + ZoneBytes w; + w.Put(30).Put(0); + w.data.append(48 + 12, '\0'); + w.Put(0).Put(1); + w.Object(30, 2, 0, "a=0:x,b=1:4\n\nstray,c=0:y"); + + std::istringstream stream(w.Done()); + LevelFile level; + level.Read(stream); + ASSERT_EQ(level.objects.size(), 1u); + const auto& settings = level.objects[0].settings; + std::vector keys; + for (const auto& [key, value] : settings) keys.push_back(GeneralUtils::UTF16ToWTF8(key)); + std::ranges::sort(keys); + EXPECT_EQ(keys, (std::vector{ "a", "b", "c" })); +} + TEST(LevelFileTests, DamagedFilesKeepWhatWasRead) { ZoneBytes w; w.Put(30).Put(0);