whiteoutlib 0.1.3

Read and write Blizzard game assets from Rust: models (MDX, M2, M3), textures (BLP, DDS, PNG, JPEG, BMP, TGA, TIFF, GIF) and archives (CASC, MPQ).
Documentation
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Fernando Sahmkow

namespace detail {

template <typename T, typename = void>
struct has_get_version : std::false_type {};

template <typename T>
struct has_get_version<T, std::void_t<decltype(std::declval<T&>().getVersion())>>
    : std::true_type {};

template <typename T>
u32 getStructureVersion(const T& value) {
    if constexpr (has_get_version<T>::value) {
        return value.getVersion();
    }
    return ChunkTagTraits<T>::max_version;
}

} // namespace detail

template <typename T>
void BinaryWriterVisitor::visit(const AnimBlock<T>& block, u32 version) {
    (void)version;
    visit(block.timestamps);
    writer.write(block.flags);
    writer.write(block.endFrame);
    visit(block.keys);
}

template <typename T>
void BinaryWriterVisitor::visit(const std::vector<T>& container) {
    Reference ref = {};
    if (container.empty()) {
        writer.write(ref);
        return;
    }

    auto ref_position = writer.getPosition();
    writer.write(ref);
    currentLevelWrites.push_back([this, ref_position, &container]() {
        auto new_entry_index = indexTable.size();
        auto version = detail::getStructureVersion(*container.begin());
        const auto currentOffset = writer.getPosition();
        indexTable.push_back({ChunkTagTraits<T>::value, currentOffset,
                              static_cast<u32>(container.size()), version});
        auto& entry = indexTable[new_entry_index];
        writer.setPosition(ref_position);
        Reference ref = {};
        ref.entries = static_cast<u32>(container.size());
        ref.index = static_cast<u32>(new_entry_index);
        writer.write(ref);
        writer.setPosition(entry.offset);
        if constexpr (ChunkTagTraits<T>::is_trivial) {
            writer.write(container);
        } else {
            for (auto& element : container) {
                visit(element, entry.version);
                transferDeferredWrites();
            }
        }
        writer.AlignTo(16, 0xAA);
    });
}

template <typename T>
void BinaryWriterVisitor::visit(const std::optional<T>& container) {
    Reference ref = {};
    if (!container.has_value()) {
        writer.write(ref);
        return;
    }

    auto ref_position = writer.getPosition();
    writer.write(ref);
    currentLevelWrites.push_back([this, ref_position, &container]() {
        auto new_entry_index = indexTable.size();
        auto version = detail::getStructureVersion(*container);
        const auto currentOffset = writer.getPosition();
        indexTable.push_back({ChunkTagTraits<T>::value, currentOffset, 1, version});
        auto& entry = indexTable[new_entry_index];
        writer.setPosition(ref_position);
        Reference ref = {};
        ref.entries = 1;
        ref.index = static_cast<u32>(new_entry_index);
        writer.write(ref);
        writer.setPosition(entry.offset);
        if constexpr (ChunkTagTraits<T>::is_trivial) {
            writer.write(*container);
        } else {
            visit(*container, entry.version);
            transferDeferredWrites();
        }
        writer.AlignTo(16, 0xAA);
    });
}