summaryrefslogtreecommitdiff
path: root/src/factories/ac/KeyFrameAnimFactory.cpp
blob: fc9f8aa5c1e60674588c5450e7124454ae571b0c (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
#include "KeyFrameAnimFactory.h"
#include "ACUtils.h"

#include <bit>

namespace AC {

KeyFrameCounts CountKeyFrameTables(const std::vector<uint8_t>& flags) {
    uint32_t keys = 0;
    if (!flags.empty()) {
        keys += std::popcount((uint32_t)(flags[0] & 0x38));
    }
    for (const auto f : flags) {
        keys += std::popcount((uint32_t)(f & 0x07));
    }
    const uint32_t total = 3 + 3 * (uint32_t)flags.size();
    return { keys, total - keys };
}

KeyFrameAnimData ReadKeyFrameAnim(uint32_t addr, YAML::Node& node, const std::string& name) {
    const auto joints = GetSafeNode<uint32_t>(node, "joints");
    if (joints == 0 || joints > 255) {
        throw std::runtime_error(name + ": joints must be 1..255");
    }
    Cursor c(ReadBytes(addr, 0x14, name), name);
    const uint32_t flagPtr = c.U32();
    const uint32_t dataPtr = c.U32();
    const uint32_t keyPtr = c.U32();
    const uint32_t fixedPtr = c.U32();

    KeyFrameAnimData anim;
    anim.mPad = c.S16();
    anim.mFrames = c.S16();
    anim.mFlags = ReadBytes(flagPtr, joints, name + " flag_table");

    const auto counts = CountKeyFrameTables(anim.mFlags);
    Cursor keys(ReadBytes(keyPtr, counts.keys * 2, name + " key_table"), name);
    uint32_t total = 0;
    for (uint32_t i = 0; i < counts.keys; i++) {
        const int16_t n = keys.S16();
        if (n < 0) {
            throw std::runtime_error(name + ": negative key count in key_table");
        }
        anim.mKeyCounts.push_back(n);
        total += n;
    }
    Cursor data(ReadBytes(dataPtr, total * 6, name + " data_table"), name);
    for (uint32_t i = 0; i < total; i++) {
        KeyFrame k;
        k.frame = data.S16();
        k.value = data.S16();
        k.velocity = data.S16();
        anim.mKeyFrames.push_back(k);
    }
    Cursor fixed(ReadBytes(fixedPtr, counts.fixed * 2, name + " fixed_table"), name);
    for (uint32_t i = 0; i < counts.fixed; i++) {
        anim.mFixedValues.push_back(fixed.S16());
    }

    CheckCount(node, "flags", joints, name);
    CheckCount(node, "keys", counts.keys, name);
    CheckCount(node, "fixed", counts.fixed, name);
    CheckCount(node, "data", total * 3, name);
    return anim;
}

void WriteKeyFrameAnimBody(LUS::BinaryWriter& writer, const KeyFrameAnimData& anim) {
    writer.Write((uint8_t)0);
    writer.Write((uint32_t)anim.mFlags.size());
    for (const auto f : anim.mFlags) {
        writer.Write(f);
    }
    writer.Write((uint32_t)anim.mKeyFrames.size());
    for (const auto& k : anim.mKeyFrames) {
        writer.Write(k.frame);
        writer.Write(k.value);
        writer.Write(k.velocity);
    }
    writer.Write((uint32_t)anim.mKeyCounts.size());
    for (const auto k : anim.mKeyCounts) {
        writer.Write(k);
    }
    writer.Write((uint32_t)anim.mFixedValues.size());
    for (const auto v : anim.mFixedValues) {
        writer.Write(v);
    }
    writer.Write((uint16_t)anim.mPad);
    writer.Write(anim.mFrames);
}

ExportResult KeyFrameAnimBinaryExporter::Export(std::ostream& write, std::shared_ptr<IParsedData> raw,
                                                std::string& entryName, YAML::Node& node, std::string* replacement) {
    auto anim = std::static_pointer_cast<KeyFrameAnimData>(raw);
    auto writer = LUS::BinaryWriter();
    WriteHeader(writer, Torch::ResourceType::ACKeyFrameAnim, 0);
    WriteKeyFrameAnimBody(writer, *anim);
    writer.Finish(write);
    return std::nullopt;
}

std::optional<std::shared_ptr<IParsedData>> KeyFrameAnimFactory::parse(std::vector<uint8_t>& buffer,
                                                                       YAML::Node& node) {
    const auto offset = GetSafeNode<uint32_t>(node, "offset");
    return std::make_shared<KeyFrameAnimData>(ReadKeyFrameAnim(offset, node, NameOf(node)));
}

} // namespace AC