Project Soon

Re-implementing from old program

While it was totally unnecessary, I felt like re-implementing beta format 1, and because the internal NBT format is basically alpha format 2, I implemented that too. I had several reasons doing so, like supporting down to 1.0, check how much faster this version of PixelMap is compared to its previous version, and updating the versatility of the library itself, as I found parts of it being limited to the current save formats implementation. In the process I made some parts of the code a bit smarter and easier to use, while also reducing both memory footprint slightly and added more move semantics where needed.

Alpha format is very not optimized, and while most players will not play that, if not for nostalgia, it is good to provide a whole package, when it is rather easy to support it. Do note that the format is very different from the current format, but the biggest slowdown is not the conversion to the new format, but actually reading each individual file, as each file consist of one chunk only. But as said previously, it also made me look a bit more on the system and fix some places where I could either optimize or refactor so it makes more sense how things are made. It was quite a pleasant experience, except for the horrendous conversion.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Block ID
for (auto x = 0; x < SECTION_X; ++x)
  for (auto y = 0; y < SECTION_Y; ++y)
    for (auto z = 0; z < SECTION_Z; ++z)
      b[(y << 8) | (z << 4) | x] = (b[(y << 8) | (z << 4) | x] & 0xFF00) | uint16_t(source[(x << 11) | (z << 7) | (y + (section_i << 4))]);
// [...]
// Data ID
for (auto x = 0; x < SECTION_X; ++x)
  for (auto y = 0; y < SECTION_Y; ++y)
    for (auto z = 0; z < SECTION_Z; ++z)
      b[(y << 8) | (z << 4) | x] = (b[(y << 8) | (z << 4) | x] & 0x0FFF) | uint16_t(nibble4(source, (x << 11) | (z << 7) | (y + (section_i << 4))) << 12);
// [...]
// Block/sky-light
for (auto x = 0; x < SECTION_X; ++x)
  for (auto y = 0; y < SECTION_Y; ++y)
    for (auto z = 0; z < SECTION_Z; ++z)
      b[(y << 8) | (z << 4) | x] = nibble4(source, (x << 11) | (z << 7) | (y + (section_i << 4)));

The worst part is not that it looks ugly, but when I initially wrote this I found it difficult to deduplicate them into a single function. However, after some more tinkering I found a way.

1
2
3
4
5
6
7
8
9
template<typename T>
void convert_alpha([[maybe_unused]] const NBT::NBTByteArray & src, std::vector<T> & dst, std::size_t n, const std::function<T(T, std::size_t)> & convert)
{
  assert(src.size() == dst.size());
  for (auto x = 0; x < SECTION_X; ++x)
    for (auto y = 0; y < SECTION_Y; ++y)
      for (auto z = 0; z < SECTION_Z; ++z)
        dst[(y << 8) | (z << 4) | x] = convert(dst[(y << 8) | (z << 4) | x], (x << 11) | (z << 7) | (y + (n << 4)));
}

And that reduce the previous blocks to the following, making it a lot easier to locate a bug of a specific problem instead having to separate it from duplicate code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Block ID
convert_alpha<uint16_t>(source, destination, section_i, [&source](uint16_t dst, std::size_t i) -> uint16_t {
  return (dst & 0xFF00) | uint16_t(source[i]);
});
// [...]
// Data ID
convert_alpha<uint16_t>(source, destination, section_i, [&source](uint16_t dst, std::size_t i) -> uint16_t {
  return (dst & 0x0FFF) | uint16_t(nibble4(source, i) << 12);
});
// [...]
// Block/sky-light
convert_alpha<uint8_t>(source, dest, section_i, [&source](uint8_t, std::size_t i) -> uint8_t {
  return nibble4(source, i);
});

It is not perfect, but at least it is better managed. Splitting these into two functions, or refactoring it further may give even better result, but this is fine for now. My biggest concern right now is that while I am using STL containers, I do not use STL generics to generalize explain what my templated functions actually needs. It is a subject of its own, but it would allow me to remove the specific use of std::vector (even if it is very efficient), to a more generalized format that allows any type of container that follows the specific requirements.


  1. https://minecraft.wiki/w/Region_file_format ↩︎

  2. https://minecraft.wiki/w/Java_Edition_Alpha_level_format ↩︎