From e4440a75980f7981f7d3be077623166a82921b2d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Skutnik Date: Sat, 13 Nov 2021 14:38:09 -0800 Subject: [PATCH] Creates files for version compatibility functions --- src/CMakeLists.txt | 3 + src/chunk.cpp | 59 +----- src/chunk_format_versions/assert.cpp | 47 +++++ src/chunk_format_versions/assert.hpp | 19 ++ src/chunk_format_versions/get_section.cpp | 15 ++ src/chunk_format_versions/get_section.hpp | 11 ++ src/chunk_format_versions/section_format.cpp | 176 ++++++++++++++++++ src/chunk_format_versions/section_format.hpp | 26 +++ src/section.cpp | 179 +------------------ 9 files changed, 306 insertions(+), 229 deletions(-) create mode 100644 src/chunk_format_versions/assert.cpp create mode 100644 src/chunk_format_versions/assert.hpp create mode 100644 src/chunk_format_versions/get_section.cpp create mode 100644 src/chunk_format_versions/get_section.hpp create mode 100644 src/chunk_format_versions/section_format.cpp create mode 100644 src/chunk_format_versions/section_format.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4bc893ec..e6151ab3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,9 @@ SET(SOURCES ${SOURCES} section.cpp settings.cpp worldloader.cpp + chunk_format_versions/assert.cpp + chunk_format_versions/get_section.cpp + chunk_format_versions/section_format.cpp VERSION) ADD_LIBRARY(mcmap_core STATIC ${SOURCES}) diff --git a/src/chunk.cpp b/src/chunk.cpp index d258e0bf..ce8ba1be 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -1,74 +1,23 @@ #include "./chunk.h" +#include "./chunk_format_versions/assert.hpp" +#include "./chunk_format_versions/get_section.hpp" #include #include namespace mcmap { namespace versions { -namespace assert_versions { -#ifdef SNAPSHOT_SUPPORT -bool v2844(const nbt::NBT &chunk) { - // Snapshot 21w43a - return chunk.contains("sections") // No sections mean no blocks - && chunk.contains("Status") // Ensure the status is `full` - && chunk["Status"].get() == "full"; -} - -bool v2840(const nbt::NBT &chunk) { - // Snapshot 21w42a - return chunk.contains("Level") && // Level data is required - chunk["Level"].contains("Sections") // No sections mean no blocks - && chunk["Level"].contains("Status") // Ensure the status is `full` - && chunk["Level"]["Status"].get() == "full"; -} -#endif - -bool v1976(const nbt::NBT &chunk) { - // From 1.14 onwards - return chunk.contains("Level") // Level data is required - && chunk["Level"].contains("Sections") // No sections mean no blocks - && chunk["Level"].contains("Status") // Ensure the status is `full` - && chunk["Level"]["Status"].get() == "full"; -} - -bool v1628(const nbt::NBT &chunk) { - // From 1.13 onwards - return chunk.contains("Level") // Level data is required - && chunk["Level"].contains("Sections") // No sections mean no blocks - && chunk["Level"].contains("Status") // Ensure the status is `full` - && chunk["Level"]["Status"].get() == - "postprocessed"; -} - -bool catchall(const nbt::NBT &chunk) { - logger::deep_debug("Unsupported DataVersion: {}\n", - chunk["DataVersion"].get()); - return false; -} -} // namespace assert_versions - -namespace sections_versions { -nbt::NBT v2844(const nbt::NBT &chunk) { return chunk["sections"]; } -nbt::NBT v1628(const nbt::NBT &chunk) { return chunk["Level"]["Sections"]; } -nbt::NBT catchall(const nbt::NBT &chunk) { - logger::deep_debug("Unsupported DataVersion: {}\n", - chunk["DataVersion"].get()); - return nbt::NBT(std::vector()); -} -} // namespace sections_versions - std::map> assert = { + {2844, assert_versions::v2844}, #ifdef SNAPSHOT_SUPPORT - {2844, assert_versions::v2844}, {2840, assert_versions::v2840}, + {2840, assert_versions::v2840}, #endif {1976, assert_versions::v1976}, {1628, assert_versions::v1628}, {0, assert_versions::catchall}, }; std::map> sections = { -#ifdef SNAPSHOT_SUPPORT {2844, sections_versions::v2844}, -#endif {1628, sections_versions::v1628}, {0, sections_versions::catchall}, }; diff --git a/src/chunk_format_versions/assert.cpp b/src/chunk_format_versions/assert.cpp new file mode 100644 index 00000000..7e0b0e38 --- /dev/null +++ b/src/chunk_format_versions/assert.cpp @@ -0,0 +1,47 @@ +#include "./assert.hpp" + +namespace mcmap { +namespace versions { +namespace assert_versions { +bool v2844(const nbt::NBT &chunk) { + // Snapshot 21w43a + return chunk.contains("sections") // No sections mean no blocks + && chunk.contains("Status") // Ensure the status is `full` + && chunk["Status"].get() == "full"; +} + +#ifdef SNAPSHOT_SUPPORT +bool v2840(const nbt::NBT &chunk) { + // Snapshot 21w42a + return chunk.contains("Level") && // Level data is required + chunk["Level"].contains("Sections") // No sections mean no blocks + && chunk["Level"].contains("Status") // Ensure the status is `full` + && chunk["Level"]["Status"].get() == "full"; +} +#endif + +bool v1976(const nbt::NBT &chunk) { + // From 1.14 onwards + return chunk.contains("Level") // Level data is required + && chunk["Level"].contains("Sections") // No sections mean no blocks + && chunk["Level"].contains("Status") // Ensure the status is `full` + && chunk["Level"]["Status"].get() == "full"; +} + +bool v1628(const nbt::NBT &chunk) { + // From 1.13 onwards + return chunk.contains("Level") // Level data is required + && chunk["Level"].contains("Sections") // No sections mean no blocks + && chunk["Level"].contains("Status") // Ensure the status is `full` + && chunk["Level"]["Status"].get() == + "postprocessed"; +} + +bool catchall(const nbt::NBT &chunk) { + logger::deep_debug("Unsupported DataVersion: {}\n", + chunk["DataVersion"].get()); + return false; +} +} // namespace assert_versions +} // namespace versions +} // namespace mcmap diff --git a/src/chunk_format_versions/assert.hpp b/src/chunk_format_versions/assert.hpp new file mode 100644 index 00000000..8792bf39 --- /dev/null +++ b/src/chunk_format_versions/assert.hpp @@ -0,0 +1,19 @@ +#include + +namespace mcmap { +namespace versions { +namespace assert_versions { +bool v2844(const nbt::NBT &chunk); + +#ifdef SNAPSHOT_SUPPORT +bool v2840(const nbt::NBT &chunk); +#endif + +bool v1976(const nbt::NBT &chunk); + +bool v1628(const nbt::NBT &chunk); + +bool catchall(const nbt::NBT &chunk); +} // namespace assert_versions +} // namespace versions +} // namespace mcmap diff --git a/src/chunk_format_versions/get_section.cpp b/src/chunk_format_versions/get_section.cpp new file mode 100644 index 00000000..41196b76 --- /dev/null +++ b/src/chunk_format_versions/get_section.cpp @@ -0,0 +1,15 @@ +#include "get_section.hpp" + +namespace mcmap { +namespace versions { +namespace sections_versions { +nbt::NBT v2844(const nbt::NBT &chunk) { return chunk["sections"]; } +nbt::NBT v1628(const nbt::NBT &chunk) { return chunk["Level"]["Sections"]; } +nbt::NBT catchall(const nbt::NBT &chunk) { + logger::deep_debug("Unsupported DataVersion: {}\n", + chunk["DataVersion"].get()); + return nbt::NBT(std::vector()); +} +} // namespace sections_versions +} // namespace versions +} // namespace mcmap diff --git a/src/chunk_format_versions/get_section.hpp b/src/chunk_format_versions/get_section.hpp new file mode 100644 index 00000000..e016f364 --- /dev/null +++ b/src/chunk_format_versions/get_section.hpp @@ -0,0 +1,11 @@ +#include + +namespace mcmap { +namespace versions { +namespace sections_versions { +nbt::NBT v2844(const nbt::NBT &); +nbt::NBT v1628(const nbt::NBT &); +nbt::NBT catchall(const nbt::NBT &); +} // namespace sections_versions +} // namespace versions +} // namespace mcmap diff --git a/src/chunk_format_versions/section_format.cpp b/src/chunk_format_versions/section_format.cpp new file mode 100644 index 00000000..634f0c31 --- /dev/null +++ b/src/chunk_format_versions/section_format.cpp @@ -0,0 +1,176 @@ +#include "section_format.hpp" + +namespace mcmap { +namespace versions { +namespace block_states_versions { +void post116(const uint8_t index_length, + const std::vector *blockStates, + Section::block_array &buffer) { + // NEW in 1.16, longs are padded by 0s when a block cannot fit, so no more + // overflow to deal with ! + + for (uint16_t index = 0; index < 4096; index++) { + // Determine how many indexes each long holds + const uint8_t blocksPerLong = 64 / index_length; + + // Calculate where in the long array is the long containing the right index. + const uint16_t longIndex = index / blocksPerLong; + + // Once we located a long, we have to know where in the 64 bits + // the relevant block is located. + const uint8_t padding = (index - longIndex * blocksPerLong) * index_length; + + // Bring the data to the first bits of the long, then extract it by bitwise + // comparison + const uint16_t blockIndex = ((*blockStates)[longIndex] >> padding) & + ((uint64_t(1) << index_length) - 1); + + buffer[index] = blockIndex; + } +} + +void pre116(const uint8_t index_length, const std::vector *blockStates, + Section::block_array &buffer) { + // The `BlockStates` array contains data on the section's blocks. You have to + // extract it by understanfing its structure. + // + // Although it is a array of long values, one must see it as an array of block + // indexes, whose element size depends on the size of the Palette. This + // routine locates the necessary long, extracts the block with bit + // comparisons. + // + // The length of a block index has to be coded on the minimal possible size, + // which is the logarithm in base2 of the size of the palette, or 4 if the + // logarithm is smaller. + + for (uint16_t index = 0; index < 4096; index++) { + + // We skip the `position` first blocks, of length `size`, then divide by 64 + // to get the number of longs to skip from the array + const uint16_t skip_longs = (index * index_length) >> 6; + + // Once we located the data in a long, we have to know where in the 64 bits + // it is located. This is the remaining of the previous operation + const int8_t padding = (index * index_length) & 63; + + // Sometimes the data of an index does not fit entirely into a long, so we + // check if there is overflow + const int8_t overflow = + (padding + index_length > 64 ? padding + index_length - 64 : 0); + + // This complicated expression extracts the necessary bits from the current + // long. + // + // Lets say we need the following bits in a long (not to scale): + // 10011100111001110011100 + // ^^^^^ + // We do this by shifting (>>) the data by padding, to get the relevant bits + // on the end of the long: + // ???????????????10011100 + // ^^^^^ + // We then apply a mask to get only the relevant bits: + // ???????????????10011100 + // 00000000000000000011111 & + // 00000000000000000011100 <- result + // + // The mask is made at the size of the data, using the formula (1 << n) - 1, + // the resulting bitset is of the following shape: 0...01...1 with n 1s. + // + // If there is an overflow, the mask size is reduced, as not to catch noise + // from the padding (ie the interrogation points earlier) that appear on + // ARM32. + uint16_t lower_data = ((*blockStates)[skip_longs] >> padding) & + ((uint64_t(1) << (index_length - overflow)) - 1); + + if (overflow > 0) { + // The exact same process is used to catch the overflow from the next long + const uint16_t upper_data = + ((*blockStates)[skip_longs + 1]) & ((uint64_t(1) << overflow) - 1); + // We then associate both values to create the final value + lower_data = lower_data | (upper_data << (index_length - overflow)); + } + + // lower_data now contains the index in the palette + buffer[index] = lower_data; + } +} +} // namespace block_states_versions + +namespace init_versions { +void v1628(Section *target, const nbt::NBT &raw_section) { + if (raw_section.contains("BlockStates") && raw_section.contains("Palette")) { + target->palette = + *raw_section["Palette"].get *>(); + const nbt::NBT::tag_long_array_t *blockStates = + raw_section["BlockStates"].get(); + + // Remove the air that is default-constructed + target->colors.clear(); + // Anticipate the color input from the palette's size + target->colors.reserve(target->palette.size()); + + // The length in bits of a block is the log2 of the palette's size or 4, + // whichever is greatest. Ranges from 4 to 12. + const uint8_t blockBitLength = + std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4)); + + // Parse the blockstates for block info + block_states_versions::pre116(blockBitLength, blockStates, target->blocks); + } +} + +void v2534(Section *target, const nbt::NBT &raw_section) { + if (raw_section.contains("BlockStates") && raw_section.contains("Palette")) { + target->palette = + *raw_section["Palette"].get *>(); + const nbt::NBT::tag_long_array_t *blockStates = + raw_section["BlockStates"].get(); + + // Remove the air that is default-constructed + target->colors.clear(); + // Anticipate the color input from the palette's size + target->colors.reserve(target->palette.size()); + + // The length in bits of a block is the log2 of the palette's size or 4, + // whichever is greatest. Ranges from 4 to 12. + const uint8_t blockBitLength = + std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4)); + + // Parse the blockstates for block info + block_states_versions::post116(blockBitLength, blockStates, target->blocks); + } +} + +#ifdef SNAPSHOT_SUPPORT +void v2840(Section *target, const nbt::NBT &raw_section) { + if (raw_section.contains("block_states") && + raw_section["block_states"].contains("data") && + raw_section["block_states"].contains("palette")) { + target->palette = *raw_section["block_states"]["palette"] + .get *>(); + const nbt::NBT::tag_long_array_t *blockStates = + raw_section["block_states"]["data"] + .get(); + + // Remove the air that is default-constructed + target->colors.clear(); + // Anticipate the color input from the palette's size + target->colors.reserve(target->palette.size()); + + // The length in bits of a block is the log2 of the palette's size or 4, + // whichever is greatest. Ranges from 4 to 12. + const uint8_t blockBitLength = + std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4)); + + // Parse the blockstates for block info + block_states_versions::post116(blockBitLength, blockStates, target->blocks); + } +} +#endif + +void catchall(Section *, const nbt::NBT &) { + logger::deep_debug("Unsupported DataVersion\n"); +} +} // namespace init_versions +} // namespace versions +} // namespace mcmap diff --git a/src/chunk_format_versions/section_format.hpp b/src/chunk_format_versions/section_format.hpp new file mode 100644 index 00000000..75a32fb8 --- /dev/null +++ b/src/chunk_format_versions/section_format.hpp @@ -0,0 +1,26 @@ +#include "../section.h" +#include + +namespace mcmap { +namespace versions { +namespace block_states_versions { +void post116(const uint8_t, const std::vector *, + Section::block_array &); + +void pre116(const uint8_t, const std::vector *, + Section::block_array &); +} // namespace block_states_versions + +namespace init_versions { +void v1628(Section *, const nbt::NBT &); + +void v2534(Section *, const nbt::NBT &); + +#ifdef SNAPSHOT_SUPPORT +void v2840(Section *, const nbt::NBT &); +#endif + +void catchall(Section *, const nbt::NBT &); +} // namespace init_versions +} // namespace versions +} // namespace mcmap diff --git a/src/section.cpp b/src/section.cpp index 63c59c2e..9bc60772 100644 --- a/src/section.cpp +++ b/src/section.cpp @@ -1,179 +1,10 @@ #include "./section.h" +#include "./chunk_format_versions/section_format.hpp" #include #include +namespace mcmap { namespace versions { -namespace block_states_versions { -void post116(const uint8_t index_length, - const std::vector *blockStates, - Section::block_array &buffer) { - // NEW in 1.16, longs are padded by 0s when a block cannot fit, so no more - // overflow to deal with ! - - for (uint16_t index = 0; index < 4096; index++) { - // Determine how many indexes each long holds - const uint8_t blocksPerLong = 64 / index_length; - - // Calculate where in the long array is the long containing the right index. - const uint16_t longIndex = index / blocksPerLong; - - // Once we located a long, we have to know where in the 64 bits - // the relevant block is located. - const uint8_t padding = (index - longIndex * blocksPerLong) * index_length; - - // Bring the data to the first bits of the long, then extract it by bitwise - // comparison - const uint16_t blockIndex = ((*blockStates)[longIndex] >> padding) & - ((uint64_t(1) << index_length) - 1); - - buffer[index] = blockIndex; - } -} - -void pre116(const uint8_t index_length, const std::vector *blockStates, - Section::block_array &buffer) { - // The `BlockStates` array contains data on the section's blocks. You have to - // extract it by understanfing its structure. - // - // Although it is a array of long values, one must see it as an array of block - // indexes, whose element size depends on the size of the Palette. This - // routine locates the necessary long, extracts the block with bit - // comparisons. - // - // The length of a block index has to be coded on the minimal possible size, - // which is the logarithm in base2 of the size of the palette, or 4 if the - // logarithm is smaller. - - for (uint16_t index = 0; index < 4096; index++) { - - // We skip the `position` first blocks, of length `size`, then divide by 64 - // to get the number of longs to skip from the array - const uint16_t skip_longs = (index * index_length) >> 6; - - // Once we located the data in a long, we have to know where in the 64 bits - // it is located. This is the remaining of the previous operation - const int8_t padding = (index * index_length) & 63; - - // Sometimes the data of an index does not fit entirely into a long, so we - // check if there is overflow - const int8_t overflow = - (padding + index_length > 64 ? padding + index_length - 64 : 0); - - // This complicated expression extracts the necessary bits from the current - // long. - // - // Lets say we need the following bits in a long (not to scale): - // 10011100111001110011100 - // ^^^^^ - // We do this by shifting (>>) the data by padding, to get the relevant bits - // on the end of the long: - // ???????????????10011100 - // ^^^^^ - // We then apply a mask to get only the relevant bits: - // ???????????????10011100 - // 00000000000000000011111 & - // 00000000000000000011100 <- result - // - // The mask is made at the size of the data, using the formula (1 << n) - 1, - // the resulting bitset is of the following shape: 0...01...1 with n 1s. - // - // If there is an overflow, the mask size is reduced, as not to catch noise - // from the padding (ie the interrogation points earlier) that appear on - // ARM32. - uint16_t lower_data = ((*blockStates)[skip_longs] >> padding) & - ((uint64_t(1) << (index_length - overflow)) - 1); - - if (overflow > 0) { - // The exact same process is used to catch the overflow from the next long - const uint16_t upper_data = - ((*blockStates)[skip_longs + 1]) & ((uint64_t(1) << overflow) - 1); - // We then associate both values to create the final value - lower_data = lower_data | (upper_data << (index_length - overflow)); - } - - // lower_data now contains the index in the palette - buffer[index] = lower_data; - } -} -} // namespace block_states_versions - -namespace init_versions { -void v1628(Section *target, const nbt::NBT &raw_section) { - if (raw_section.contains("BlockStates") && raw_section.contains("Palette")) { - target->palette = - *raw_section["Palette"].get *>(); - const nbt::NBT::tag_long_array_t *blockStates = - raw_section["BlockStates"].get(); - - // Remove the air that is default-constructed - target->colors.clear(); - // Anticipate the color input from the palette's size - target->colors.reserve(target->palette.size()); - - // The length in bits of a block is the log2 of the palette's size or 4, - // whichever is greatest. Ranges from 4 to 12. - const uint8_t blockBitLength = - std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4)); - - // Parse the blockstates for block info - block_states_versions::pre116(blockBitLength, blockStates, target->blocks); - } -} - -void v2534(Section *target, const nbt::NBT &raw_section) { - if (raw_section.contains("BlockStates") && raw_section.contains("Palette")) { - target->palette = - *raw_section["Palette"].get *>(); - const nbt::NBT::tag_long_array_t *blockStates = - raw_section["BlockStates"].get(); - - // Remove the air that is default-constructed - target->colors.clear(); - // Anticipate the color input from the palette's size - target->colors.reserve(target->palette.size()); - - // The length in bits of a block is the log2 of the palette's size or 4, - // whichever is greatest. Ranges from 4 to 12. - const uint8_t blockBitLength = - std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4)); - - // Parse the blockstates for block info - block_states_versions::post116(blockBitLength, blockStates, target->blocks); - } -} - -#ifdef SNAPSHOT_SUPPORT -void v2840(Section *target, const nbt::NBT &raw_section) { - if (raw_section.contains("block_states") && - raw_section["block_states"].contains("data") && - raw_section["block_states"].contains("palette")) { - target->palette = *raw_section["block_states"]["palette"] - .get *>(); - const nbt::NBT::tag_long_array_t *blockStates = - raw_section["block_states"]["data"] - .get(); - - // Remove the air that is default-constructed - target->colors.clear(); - // Anticipate the color input from the palette's size - target->colors.reserve(target->palette.size()); - - // The length in bits of a block is the log2 of the palette's size or 4, - // whichever is greatest. Ranges from 4 to 12. - const uint8_t blockBitLength = - std::max(uint8_t(ceil(log2(target->palette.size()))), uint8_t(4)); - - // Parse the blockstates for block info - block_states_versions::post116(blockBitLength, blockStates, target->blocks); - } -} -#endif - -void catchall(Section *, const nbt::NBT &) { - logger::deep_debug("Unsupported DataVersion\n"); -} -} // namespace init_versions - std::map> init = { #ifdef SNAPSHOT_SUPPORT {2840, init_versions::v2840}, @@ -182,8 +13,8 @@ std::map> init = { {1628, init_versions::v1628}, {0, init_versions::catchall}, }; - } // namespace versions +} // namespace mcmap const Colors::Block _void; @@ -224,9 +55,9 @@ Section::Section(const nbt::NBT &raw_section, const int dataVersion) // Get data from the NBT Y = raw_section["Y"].get(); - auto init_it = compatible(versions::init, dataVersion); + auto init_it = compatible(mcmap::versions::init, dataVersion); - if (init_it != versions::init.end()) { + if (init_it != mcmap::versions::init.end()) { init_it->second(this, raw_section); }