summaryrefslogtreecommitdiff
path: root/src/factories/oot/OoTAnimationFactory.cpp
blob: a2e8eddf83881b2e883b905006d4cb9226903683 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include "OoTAnimationFactory.h"
#include "spdlog/spdlog.h"
#include "Companion.h"
#include "utils/Decompressor.h"

namespace OoT {

std::optional<std::shared_ptr<IParsedData>> OoTAnimationFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
    // Check for legacy animation type (data stays in ROM segment, not extracted)
    auto animType = GetSafeNode<std::string>(node, "anim_type", "normal");
    if (animType == "legacy") {
        auto anim = std::make_shared<OoTNormalAnimationData>();
        anim->frameCount = 0;
        anim->limit = 0;
        anim->isLegacy = true;
        return anim;
    }

    auto [_, segment] = Decompressor::AutoDecode(node, buffer, 0x10);
    LUS::BinaryReader reader(segment.data, segment.size);
    reader.SetEndianness(Torch::Endianness::Big);

    // ROM layout: AnimationHeader (16 bytes)
    // +0x00: int16 frameCount
    // +0x02: int16 padding
    // +0x04: segptr rotationValues
    // +0x08: segptr rotationIndices
    // +0x0C: int16 limit
    // +0x0E: int16 padding
    int16_t frameCount = reader.ReadInt16();
    reader.ReadInt16(); // padding
    uint32_t rawRotValues = reader.ReadUInt32();
    uint32_t rawRotIndices = reader.ReadUInt32();
    int16_t limit = reader.ReadInt16();

    uint32_t rotValuesAddr = Companion::Instance->PatchVirtualAddr(rawRotValues);
    uint32_t rotIndicesAddr = Companion::Instance->PatchVirtualAddr(rawRotIndices);

    auto anim = std::make_shared<OoTNormalAnimationData>();
    anim->frameCount = frameCount;
    anim->limit = limit;

    // Translate segmented addresses to file offsets
    uint32_t rotValuesOffset = Decompressor::TranslateAddr(rotValuesAddr);
    uint32_t rotIndicesOffset = Decompressor::TranslateAddr(rotIndicesAddr);
    uint32_t animHeaderOffset = Decompressor::TranslateAddr(Companion::Instance->PatchVirtualAddr(GetSafeNode<uint32_t>(node, "offset")));

    // Read rotation values: array of uint16 from rotValues to rotIndices
    uint32_t rotValuesCount = (rotIndicesOffset - rotValuesOffset) / 2;
    if (rotValuesCount > 0 && rotValuesOffset < rotIndicesOffset) {
        YAML::Node rvNode;
        rvNode["offset"] = rotValuesAddr;
        auto rvRaw = Decompressor::AutoDecode(rvNode, buffer, rotValuesCount * 2);
        LUS::BinaryReader rvReader(rvRaw.segment.data, rvRaw.segment.size);
        rvReader.SetEndianness(Torch::Endianness::Big);

        for (uint32_t i = 0; i < rotValuesCount; i++) {
            anim->rotationValues.push_back(rvReader.ReadUInt16());
        }
    }

    // Read rotation indices: array of {x,y,z} uint16 from rotIndices to animHeader
    uint32_t rotIndicesCount = (animHeaderOffset - rotIndicesOffset) / 6;
    if (rotIndicesCount > 0 && rotIndicesOffset < animHeaderOffset) {
        YAML::Node riNode;
        riNode["offset"] = rotIndicesAddr;
        auto riRaw = Decompressor::AutoDecode(riNode, buffer, rotIndicesCount * 6);
        LUS::BinaryReader riReader(riRaw.segment.data, riRaw.segment.size);
        riReader.SetEndianness(Torch::Endianness::Big);

        for (uint32_t i = 0; i < rotIndicesCount; i++) {
            RotationIndex ri;
            ri.x = riReader.ReadUInt16();
            ri.y = riReader.ReadUInt16();
            ri.z = riReader.ReadUInt16();
            anim->rotationIndices.push_back(ri);
        }
    }

    return anim;
}

ExportResult OoTAnimationBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw,
                                                 std::string& entryName, YAML::Node& node,
                                                 std::string* replacement) {
    auto writer = LUS::BinaryWriter();
    auto anim = std::static_pointer_cast<OoTNormalAnimationData>(raw);

    WriteHeader(writer, Torch::ResourceType::OoTAnimation, 0);

    if (anim->isLegacy) {
        writer.Write(static_cast<uint32_t>(OoTAnimationType::Legacy));
        writer.Finish(write);
        return std::nullopt;
    }

    writer.Write(static_cast<uint32_t>(OoTAnimationType::Normal));
    writer.Write(anim->frameCount);

    writer.Write(static_cast<uint32_t>(anim->rotationValues.size()));
    for (auto& val : anim->rotationValues) {
        writer.Write(val);
    }

    writer.Write(static_cast<uint32_t>(anim->rotationIndices.size()));
    for (auto& ri : anim->rotationIndices) {
        writer.Write(ri.x);
        writer.Write(ri.y);
        writer.Write(ri.z);
    }

    writer.Write(anim->limit);

    writer.Finish(write);
    return std::nullopt;
}

} // namespace OoT