summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/BlockTypePalette.cpp175
-rw-r--r--tests/BlockTypeRegistry/BlockTypePaletteTest.cpp4
2 files changed, 132 insertions, 47 deletions
diff --git a/src/BlockTypePalette.cpp b/src/BlockTypePalette.cpp
index b8f962f5d..b452023c4 100644
--- a/src/BlockTypePalette.cpp
+++ b/src/BlockTypePalette.cpp
@@ -7,6 +7,30 @@
+/** Returns the index into aString >= aStartIdx at which the next separator occurs.
+Separator is one of \t, \n or \r.
+Returns AString::npos if no such separator. */
+static size_t findNextSeparator(const AString & aString, size_t aStartIdx = 0)
+{
+ for (size_t i = aStartIdx, len = aString.length(); i < len; ++i)
+ {
+ switch (aString[i])
+ {
+ case '\t':
+ case '\n':
+ case '\r':
+ {
+ return i;
+ }
+ }
+ }
+ return AString::npos;
+}
+
+
+
+
+
BlockTypePalette::BlockTypePalette():
mMaxIndex(0)
{
@@ -196,89 +220,150 @@ void BlockTypePalette::loadFromJsonString(const AString & aJsonPalette)
void BlockTypePalette::loadFromTsv(const AString & aTsvPalette, bool aIsUpgrade)
{
- auto lines = StringSplitAndTrim(aTsvPalette, "\n");
+ static const AString hdrTsvRegular = "BlockTypePalette";
+ static const AString hdrTsvUpgrade = "UpgradeBlockTypePalette";
+
+ // Check the file signature:
+ auto idx = findNextSeparator(aTsvPalette);
+ if ((idx == AString::npos) || (aTsvPalette[idx] == '\t'))
+ {
+ throw LoadFailedException("Invalid signature");
+ }
+ auto signature = aTsvPalette.substr(0, idx);
+ bool isUpgrade = (signature == hdrTsvUpgrade);
+ if (!isUpgrade && (signature != hdrTsvRegular))
+ {
+ throw LoadFailedException("Unknown signature");
+ }
+ if (aTsvPalette[idx] == '\r') // CR of the CRLF pair, skip the LF:
+ {
+ idx += 1;
+ }
// Parse the header:
- int fileVersion = 0;
+ bool hasHadVersion = false;
AString commonPrefix;
- auto numLines = lines.size();
- for (size_t idx = 1; idx < numLines; ++idx)
+ int line = 2;
+ auto len = aTsvPalette.length();
+ while (true)
{
- const auto & line = lines[idx];
- if (line.empty())
+ auto keyStart = idx + 1;
+ auto keyEnd = findNextSeparator(aTsvPalette, idx + 1);
+ if (keyEnd == AString::npos)
+ {
+ throw LoadFailedException(Printf("Invalid header key format on line %u", line));
+ }
+ if (keyEnd == idx + 1) // Empty line, end of headers
{
- // End of headers, erase them from lines[] and go parse the data
- lines.erase(lines.begin(), lines.begin() + static_cast<AStringVector::difference_type>(idx) + 1);
+ if (aTsvPalette[keyEnd] == '\r') // CR of the CRLF pair, skip the LF:
+ {
+ ++keyEnd;
+ }
+ idx = keyEnd;
+ ++line;
break;
}
- auto s = StringSplit(line, "\t");
- if (s.size() != 2)
+ auto valueEnd = findNextSeparator(aTsvPalette, keyEnd + 1);
+ if ((valueEnd == AString::npos) || (aTsvPalette[valueEnd] == '\t'))
{
- throw LoadFailedException(Printf("Invalid header format on line %u", idx + 1));
+ throw LoadFailedException(Printf("Invalid header value format on line %u", line));
}
- if (s[0] == "FileVersion")
+ auto key = aTsvPalette.substr(keyStart, keyEnd - keyStart);
+ if (key == "FileVersion")
{
- try
+ unsigned version = 0;
+ auto value = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1);
+ if (!StringToInteger(value, version))
{
- fileVersion = std::stoi(s[1]);
+ throw LoadFailedException("Invalid FileVersion value");
}
- catch (const std::exception & exc)
+ else if (version != 1)
{
- throw LoadFailedException(Printf("Invalid file version: \"%d\" (%s)", s[1], exc.what()));
+ throw LoadFailedException(Printf("Unknown FileVersion: %u. Only version 1 is supported.", version));
}
+ hasHadVersion = true;
+ }
+ else if (key == "CommonPrefix")
+ {
+ commonPrefix = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1);
}
- else if (s[0] == "CommonPrefix")
+ idx = valueEnd;
+ if (aTsvPalette[idx] == '\r') // CR of the CRLF pair, skip the LF:
{
- commonPrefix = s[1];
+ ++idx;
}
+ ++line;
}
- if (fileVersion != 1)
+ if (!hasHadVersion)
{
- throw LoadFailedException(Printf("Unknown file version (%d), only version 1 is supported", fileVersion));
+ throw LoadFailedException("No FileVersion value");
}
// Parse the data:
- size_t minSplit = aIsUpgrade ? 3 : 2;
- for (const auto & line: lines)
+ while (idx + 1 < len)
{
- auto s = StringSplit(line, "\t");
- auto numSplit = s.size();
- if (numSplit < minSplit)
+ auto lineStart = idx + 1;
+ auto idEnd = findNextSeparator(aTsvPalette, lineStart);
+ if ((idEnd == AString::npos) || (aTsvPalette[idEnd] != '\t'))
{
- throw LoadFailedException(Printf("Not enough values on data line: \"%s\"", line));
+ throw LoadFailedException(Printf("Incomplete data on line %u (id)", line));
}
UInt32 id;
- try
+ if (!StringToInteger(aTsvPalette.substr(lineStart, idEnd - lineStart), id))
{
- id = static_cast<UInt32>(std::stoi(s[0]));
+ throw LoadFailedException(Printf("Failed to parse id on line %u", line));
}
- catch (const std::exception & exc)
+ size_t metaEnd = idEnd;
+ if (isUpgrade)
{
- throw LoadFailedException(Printf("Invalid block ID: \"%s\" (%s)", s[0], exc.what()));
+ metaEnd = findNextSeparator(aTsvPalette, idEnd + 1);
+ if ((metaEnd == AString::npos) || (aTsvPalette[metaEnd] != '\t'))
+ {
+ throw LoadFailedException(Printf("Incomplete data on line %u (meta)", line));
+ }
+ UInt32 meta = 0;
+ if (!StringToInteger(aTsvPalette.substr(idEnd + 1, metaEnd - idEnd - 1), meta))
+ {
+ throw LoadFailedException(Printf("Failed to parse meta on line %u", line));
+ }
+ if (meta > 15)
+ {
+ throw LoadFailedException(Printf("Invalid meta value on line %u: %u", line, meta));
+ }
+ id = (id * 16) | meta;
+ }
+ auto blockTypeEnd = findNextSeparator(aTsvPalette, metaEnd + 1);
+ if (blockTypeEnd == AString::npos)
+ {
+ throw LoadFailedException(Printf("Incomplete data on line %u (blockTypeName)", line));
}
- size_t idx = 1;
- if (aIsUpgrade)
+ auto blockTypeName = aTsvPalette.substr(metaEnd + 1, blockTypeEnd - metaEnd - 1);
+ auto blockStateEnd = blockTypeEnd;
+ AStringMap blockState;
+ while (aTsvPalette[blockStateEnd] == '\t')
{
- id = id * 16;
- try
+ auto keyEnd = findNextSeparator(aTsvPalette, blockStateEnd + 1);
+ if ((keyEnd == AString::npos) || (aTsvPalette[keyEnd] != '\t'))
{
- id = id + static_cast<UInt32>(Clamp(std::stoi(s[1]), 0, 15));
+ throw LoadFailedException(Printf("Incomplete data on line %u (blockState key)", line));
}
- catch (const std::exception & exc)
+ auto valueEnd = findNextSeparator(aTsvPalette, keyEnd + 1);
+ if (valueEnd == AString::npos)
{
- throw LoadFailedException(Printf("Invalid block meta: \"%s\" (%s)", s[1], exc.what()));
+ throw LoadFailedException(Printf("Incomplete data on line %u (blockState value)", line));
}
- idx = 2;
+ auto key = aTsvPalette.substr(blockStateEnd + 1, keyEnd - blockStateEnd - 1);
+ auto value = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1);
+ blockState[key] = value;
+ blockStateEnd = valueEnd;
}
- const auto & blockTypeName = s[idx];
- idx += 1;
- std::map<AString, AString> state;
- while (idx + 1 < numSplit)
+ addMapping(id, commonPrefix + blockTypeName, std::move(blockState));
+ ++line;
+ if (aTsvPalette[blockStateEnd] == '\r') // CR of the CRLF pair, skip the LF:
{
- state[s[idx]] = s[idx + 1];
- idx += 2;
+ ++blockStateEnd;
}
- addMapping(id, commonPrefix + blockTypeName, state);
+ idx = blockStateEnd;
}
}
diff --git a/tests/BlockTypeRegistry/BlockTypePaletteTest.cpp b/tests/BlockTypeRegistry/BlockTypePaletteTest.cpp
index 995552085..1a2341bd9 100644
--- a/tests/BlockTypeRegistry/BlockTypePaletteTest.cpp
+++ b/tests/BlockTypeRegistry/BlockTypePaletteTest.cpp
@@ -229,8 +229,8 @@ static void testLoadTsvRegular(void)
auto str = "\
BlockTypePalette\r\n\
FileVersion\t1\n\
-CommonPrefix\tminecraft:\r\n\
-\n\
+CommonPrefix\tminecraft:\n\
+\r\n\
0\tair\r\n\
1\tstone\n\
2\tgrass\tsnow_covered\t0\n\