whiteoutlib 0.1.5

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

#pragma once

#include <whiteout/models/m2/types.h>
#include "../../common/binary_reader.h"
#include "../../common/streams.h"
#include "internal_structures.h"
#include "traits.h"

#include <type_traits>

namespace whiteout {
namespace m2 {

class WoWFileSystem;

class BinaryParseVisitor {
public:
    explicit BinaryParseVisitor(common::BinaryReader& reader, WoWFileSystem* wfs = nullptr,
                                i32 maxSize_ = -1);

    template <typename T>
    void read(T& header) {
        start();
        visit(header);
    }

    template <typename T>
    void read(T& header, BaseFile& file) {
        start();
        visit(header, file);
    }

    void setVersion(u32 v) {
        version = v;
    }

    u32 getVersion() const {
        return version;
    }

protected:
    void start();

    void visit(GlobalFlags& flags);
    void visit(GlobalSequence& seq);
    void visit(Sequence& seq);
    void visit(Vertex& vertex);
    void visit(Bone& bone);
    void visit(Texture& texture);
    void visit(Material& material);
    void visit(TextureWeight& weight);
    void visit(TextureTransform& transform);
    void visit(ColorAnimation& color);
    void visit(Light& light);
    void visit(CameraSpline& spline);
    void visit(Camera& camera);
    void visit(Attachment& attachment);
    void visit(RibbonEmitter& emitter);
    void visit(ParticleEmitter& emitter);
    void visit(Event& event);
    void visit(Model& header);
    void visit(MD20Header& header);

    void visit(LodProfile& chunk);
    void visit(ParticleEmitterExtension& chunk);
    void visit(TXACChunk& chunk, BaseFile& file);

    void visit(PFIDChunk& chunk, BaseFile& file);
    void visit(SFIDChunk& chunk, BaseFile& file);
    void visit(AFIDEntry& entry);
    void visit(AFIDChunk& chunk);
    void visit(BFIDChunk& chunk);

    void visit(EXPTEntry& entry);
    void visit(EXPTChunk& chunk);
    void visit(EXP2Chunk& chunk);
    void visit(PABCChunk& chunk);
    void visit(PADCChunk& chunk);
    void visit(PSBCChunk& chunk);
    void visit(PEDCChunk& chunk);
    void visit(SKIDChunk& chunk);
    void visit(TXIDChunk& chunk);

    void visit(M2RPIDEntry& entry);
    void visit(M2RPIDChunk& chunk);
    void visit(GPIDEntry& entry);
    void visit(GPIDChunk& chunk);
    void visit(ParticleGeosetData& entry);
    void visit(PGD1Chunk& chunk);
    void visit(WaterfallData& data);
    void visit(WFV3Chunk& chunk);
    void visit(PFDCChunk& chunk);
    void visit(EdgeFadeData& entry);
    void visit(EDGFChunk& chunk);
    void visit(DistanceFadeData& entry);
    void visit(NERFChunk& chunk);
    void visit(DetailedLightData& entry);
    void visit(DETLChunk& chunk);
    void visit(DebugOcclusionData& entry);
    void visit(DBOCChunk& chunk);
    void visit(AFRAChunk& chunk);
    void visit(PCOLChunk& chunk);
    void visit(DPIVChunk& chunk);
    void visit(TexturedLightData& entry);
    void visit(TEXLChunk& chunk);

    void visit(PHYSHeader& header);
    void visit(PHYTEntry& entry);
    void visit(BODYEntry& entry);
    void visit(BDY2Entry& entry);
    void visit(BDY3Entry& entry);
    void visit(BDY4Entry& entry);
    void visit(SHAPEntry& entry);
    void visit(SHP2Entry& entry);
    void visit(BOXSEntry& entry);
    void visit(CAPSEntry& entry);
    void visit(SPHSEntry& entry);
    void visit(PLYTNode& node);
    void visit(PLYTData& data, const PLYTHeader& header);
    void visit(PLYTEntry& entry);
    void visit(JOINEntry& entry);
    void visit(Matrix3x4& matrix);
    void visit(WELJEntry& entry);
    void visit(WLJ2Entry& entry);
    void visit(WLJ3Entry& entry);
    void visit(SPHJEntry& entry);
    void visit(SHOJEntry& entry);
    void visit(SHJ2Entry& entry);
    void visit(PRSJEntry& entry);
    void visit(PRS2Entry& entry);
    void visit(REVJEntry& entry);
    void visit(REV2Entry& entry);
    void visit(DSTJEntry& entry);
    void visit(PHYVEntry& entry);

    void visit(BONEHeader& header);
    void visit(Matrix44f& matrix);
    void visit(BIDAChunk& chunk);
    void visit(BOMTChunk& chunk);

    void visit(AFM2Chunk& chunk);
    void visit(AFSAChunk& chunk);
    void visit(AFSBChunk& chunk);
    void visit(AnimFile& file);

    void visit(SKL1Chunk& chunk);
    void visit(SKA1Chunk& chunk);
    void visit(SKB1Chunk& chunk);
    void visit(SKS1Chunk& chunk);
    void visit(SKPDChunk& chunk);

    void visit(SkinSection& section);
    void visit(Batch& batch);
    void visit(ShadowBatch& batch);
    void visit(SkinProfile& profile);
    void visit(SkinFile& file);

    template <typename T>
    void visit(std::vector<T>& array);

    template <typename T>
    void parse_chunked_vector(std::vector<T>& array);

    void visit(std::string& str);

    template <typename T>
    void visit(AnimationTrack<T>& track);
    void visit(AnimationTrackBase& track);

    template <typename T>
    void visit(ParticleAnimationTrack<T>& track);

    template <typename T>
    void readAnimationVector(std::vector<std::vector<T>>& keys);

    u32 version = 0;
    GlobalFlag globalFlags = GlobalFlag::None;
    common::BinaryReader& reader;
    WoWFileSystem* wfs = nullptr;
    u32 baseOffset = 0;
    i32 maxSize = -1;
    std::vector<std::span<const u8>> animBuffers;
    std::vector<AnimProfile> animProfiles;
};

template <typename T>
void BinaryParseVisitor::visit(std::vector<T>& array) {
    const auto count = reader.read<u32>();
    const auto offset = reader.read<u32>();
    if (count == 0) {
        array.clear();
        return;
    }

    const auto currentPos = reader.getPosition();
    reader.setPosition(offset + baseOffset);

    if constexpr (bulk_copyable_v<T>) {
        array = reader.read<std::vector<T>>(count);
    } else {
        array.clear();
        array.reserve(count);
        for (size_t i = 0; i < count; ++i) {
            T element;
            this->visit(element);
            array.push_back(std::move(element));
        }
    }

    reader.setPosition(currentPos);
}

template <typename T>
void BinaryParseVisitor::parse_chunked_vector(std::vector<T>& array) {
    if (maxSize <= 0) {
        array.clear();
        return;
    }
    const size_t totalSize = maxSize / sizeof(T);
    array = reader.read<std::vector<T>>(totalSize);
}

template <typename T>
void BinaryParseVisitor::readAnimationVector(std::vector<std::vector<T>>& keys) {
    struct Reference {
        u32 count;
        u32 offset;
    };
    Reference ref = reader.read<Reference>();
    if (ref.count == 0) {
        keys.clear();
        return;
    }

    const auto currentPos = reader.getPosition();
    reader.setPosition(ref.offset + baseOffset);
    keys.resize(ref.count);
    for (size_t i = 0; i < ref.count; ++i) {
        const auto ref = reader.read<Reference>();
        const auto& buffer = animBuffers[i];
        if (buffer.empty()) {
            const auto savePos = reader.getPosition();
            reader.setPosition(ref.offset + baseOffset);
            keys[i] = reader.read<std::vector<T>>(ref.count);
            reader.setPosition(savePos);
            continue;
        }
        common::span_streambuf sbuf(buffer);
        common::BinaryReader animReader(sbuf);
        animReader.setPosition(ref.offset);
        keys[i] = animReader.read<std::vector<T>>(ref.count);
    }
    reader.setPosition(currentPos);
}

template <typename T>
void BinaryParseVisitor::visit(AnimationTrack<T>& track) {
    track.interpolationType = static_cast<InterpolationType>(reader.read<u16>());
    track.globalSequenceId = reader.read<u16>();

    visit(track.timestamps);
    visit(track.values);
}

template <typename T>
void BinaryParseVisitor::visit(ParticleAnimationTrack<T>& track) {
    visit(track.timestamps);
    visit(track.values);
}

} // namespace m2
} // namespace whiteout