Ver Fonte

Better extractor for battle models in the battle installer

Iñigo Valentin há 3 anos atrás
pai
commit
a881f7a41f

+ 21 - 4
src/installer/BattleDataInstaller.cpp

@@ -26,6 +26,7 @@
 #include "TexFile.h"
 #include "data/VGearsHRCFileManager.h"
 #include "data/VGearsAFileManager.h"
+#include "data/DaFile.h"
 #include "common/VGearsStringUtil.h"
 #include "common/FinalFantasy7/FF7NameLookup.h"
 
@@ -256,9 +257,24 @@ unsigned int BattleDataInstaller::ConvertModel(){
     if (next_model_to_convert_ >= models_.size()) return models_.size();
     Model model = models_[next_model_to_convert_];
     try{
-        DecompileHrc(File(output_dir_ + "temp/battle_models/" + model.hrc + "bin"), model, output_dir_ + "temp/battle_models/" + model.hrc);
+        DecompileHrc(
+          File(output_dir_ + "temp/battle_models/" + model.hrc + "bin"),
+          model,
+          output_dir_ + "temp/battle_models/" + model.hrc
+        );
         GenerateRsdFiles(model, output_dir_ + "temp/battle_models/");
-        ExtractAFilesFromDAFile(File(output_dir_ + "temp/battle_models/" + model.anim), &model, output_dir_ + "temp/battle_models/");
+        //ExtractAFilesFromDAFile(
+        //  File(output_dir_ + "temp/battle_models/" + model.anim),
+        //  &model,
+        //  output_dir_ + "temp/battle_models/"
+        //);
+
+        DaFile da(File(output_dir_ + "temp/battle_models/" + model.anim));
+        std::vector<std::string> a_files = da.GenerateAFiles(
+          model.id, output_dir_ + "temp/battle_models/"
+        );
+        for (std::string file_name : a_files) model.a.push_back(file_name);
+
         Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(model.hrc, "FFVII");
         Ogre::String base_name;
         VGears::StringUtil::splitBase(model.hrc, base_name);
@@ -761,7 +777,9 @@ void BattleDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshP
                                     VGears::StringUtil::splitBase(
                                       unit->getTextureName(), base_name
                                     );
-                                    unit->setTextureName(base_name + ".png");
+                                    unit->setTextureName(
+                                      "models/battle/entities/" + base_name + ".png"
+                                    );
                                     textures.insert(unit->getTextureName());
                                 }
                             }
@@ -770,7 +788,6 @@ void BattleDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshP
                 }
             }
             if (std::count(materials_.begin(), materials_.end(), sub_mesh->getMaterialName()) == 0){
-                 << sub_mesh->getMaterialName() << std::endl;
                 mat_ser.queueForExport(mat);
                 materials_.push_back(sub_mesh->getMaterialName());
             }

+ 1 - 0
src/installer/CMakeLists.txt

@@ -29,6 +29,7 @@ set(INSTALLER_SOURCE_FILES
     common/TimToVram.cpp
     common/Vram.cpp
     data/BattleSceneFile.cpp
+    data/DaFile.cpp
     decompiler/CodeGenerator.cpp
     decompiler/ControlFlow.cpp
     decompiler/DecompilerException.cpp

+ 355 - 0
src/installer/data/DaFile.cpp

@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <iostream>
+#include "data/DaFile.h"
+
+DaFile::DaFile(File file){
+    animations_.clear();
+    file.SetOffset(0);
+    Read(file);
+}
+
+std::vector<std::string> DaFile::GenerateAFiles(std::string model_id, std::string path){
+    std::vector<std::string> file_list;
+    u32 zero = 0x00000000;
+    u32 version = 0x00000001;
+    u32 rotation_order = 0x00020001;
+    for (int anim = 0; anim < animations_.size(); anim ++){
+        std::string file_index_name = std::to_string(anim);
+        while (file_index_name.size() < 2) file_index_name = "0" + file_index_name;
+        std::string file_name = path + model_id + "_" + file_index_name + ".a";
+        std::ofstream a(file_name, std::ios::out | std::ios::binary);
+        a.write(reinterpret_cast<const char*>(&version), 4);
+        a.write(reinterpret_cast<const char*>(&animations_[anim].frame_count), 4);
+        a.write(reinterpret_cast<const char*>(&animations_[anim].bone_count), 4);
+        a.write(reinterpret_cast<const char*>(&rotation_order), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4);
+        for (int f = 0; f < animations_[anim].frames.size(); f ++){
+            /*
+             * TODO #1
+             * .a files have a 36 byte header header, and after that, each animation frame. Each
+             * animation frame has three floats (of 4 bytes) for root rotation, three for root
+             * position, and after them, the bone rotation data. In most .a files (in char.lgp, for
+             * example), root rotation is all zeros, but the root location isn't. I don't know how
+             * to get those values, so I'm setting them all to zero. With this, the first frame
+             * works, but the others are all meshed up. Setting root location values give very
+             * strange results, like the model being way offset-ed from it's position. It's likely
+             * that Frame.position_offset has something to do with this.
+             */
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            /*
+            a.write(
+              reinterpret_cast<const char*>(&animations_[anim].frames[f].position_offset.x), 4
+            );
+            a.write(
+              reinterpret_cast<const char*>(&animations_[anim].frames[f].position_offset.y), 4
+            );
+            a.write(
+              reinterpret_cast<const char*>(&animations_[anim].frames[f].position_offset.z), 4
+            );
+            */
+            for (int b = 0; b < animations_[anim].frames[f].rotations.size(); b ++){
+                a.write(
+                  reinterpret_cast<const char*>(&animations_[anim].frames[f].rotations[b].x), 4
+                );
+                a.write(
+                  reinterpret_cast<const char*>(&animations_[anim].frames[f].rotations[b].y), 4
+                );
+                a.write(
+                  reinterpret_cast<const char*>(&animations_[anim].frames[f].rotations[b].z), 4
+                );
+            }
+        }
+        a.close();
+        file_list.push_back(file_name);
+    }
+    return file_list;
+}
+
+void DaFile::Read(File file){
+    int num_animations = file.readU32LE();
+    for (int a = 0; a < num_animations; a ++){
+        Animation animation;
+        AnimationHeader header;
+        header.bone_count = file.readU32LE();
+        header.frame_count = file.readU32LE();
+        animation.frame_count = header.frame_count;
+        animation.bone_count = header.bone_count - 1;
+        header.data_size = file.readU32LE();
+        int next_offset = file.GetCurrentOffset() + header.data_size;
+        if (header.data_size < 11){
+            file.SetOffset(next_offset);
+            continue;
+        }
+        u8* animation_data = new u8[header.data_size];
+        for (int b = 0; b < header.data_size; b ++) animation_data[b] = file.readU8();
+
+        // This will be the buffer to hold one frame. Only one frame will be stored at a time.
+        Frame frame;
+        frame.SetBones(header.bone_count);
+        int bits = 0;
+        for (int f = 0; f < header.frame_count; f ++){
+            FrameData frm;
+            bits = LoadFrames(&frame, header.bone_count, bits, animation_data);
+            // Reverse the Y offset (required).
+            frame.position_offset.f_y = 0.0f - frame.position_offset.f_y;
+            frm.position_offset.x = frame.position_offset.f_x;
+            frm.position_offset.y = frame.position_offset.f_y;
+            frm.position_offset.z = frame.position_offset.f_z;
+
+            // Calculate final rotations
+            for (int b = 0; b < header.bone_count - 1; b ++){
+                frame.rotations[b].f_x
+                  = static_cast<float>(frame.rotations[b].i_x) / 4096.0f * 360.0f;
+                frame.rotations[b].f_y
+                  = static_cast<float>(frame.rotations[b].i_y) / 4096.0f * 360.0f;
+                frame.rotations[b].f_z
+                  = static_cast<float>(frame.rotations[b].i_z) / 4096.0f * 360.0f;
+                Rotation rotation;
+                rotation.x = frame.rotations[b].f_x;
+                rotation.y = frame.rotations[b].f_y;
+                rotation.z = frame.rotations[b].f_z;
+                frm.rotations.push_back(rotation);
+            }
+            animation.frames.push_back(frm);
+
+        }
+        animations_.push_back(animation);
+        delete [] animation_data;
+        file.SetOffset(next_offset);
+    }
+}
+
+u16 DaFile::GetValueFromStream(u8* bytes, u32 &stream_bit_offset){
+    /*
+     * TODO #2 I think this is doing some weird things
+     */
+    u16 value; // The return value;
+    // The number of whole bytes already consumed in the stream.
+    u32 stream_byte_offset = stream_bit_offset / 8;
+    // The number of bits already consumed in the current stream byte.
+    u32 stream_current_byte_bits_eaten = stream_bit_offset % 8;
+    // The distance from next_stream_bytes' LSB to the key bit.
+    u32 type_bit_shift = 7 - stream_current_byte_bits_eaten;
+    // A copy of the next two bytes in the stream (from big-endian).
+    u32 next_stream_bytes = bytes[stream_byte_offset] << 8 | bytes[stream_byte_offset + 1];
+    // Test the first bit (the 'type' bit) to determine the size of the value.
+    if (next_stream_bytes & (1 << (type_bit_shift + 8))){ // Sixteen-bit value:
+        // Collect one more byte from the stream.
+        next_stream_bytes = next_stream_bytes << 8 | bytes[stream_byte_offset + 2];
+        // Shift the delta value into place.
+        value = (next_stream_bytes << (stream_current_byte_bits_eaten + 1)) >> 8;
+        // Update the stream offset.
+        stream_bit_offset += 17;
+    }
+    else{ // Seven-bit value
+        // Shift the delta value into place (taking care to preserve the sign).
+        value = ((short)(next_stream_bytes << (stream_current_byte_bits_eaten + 1))) >> 9;
+        // Update the stream offset.
+        stream_bit_offset += 8;
+    }
+
+    // Return the value.
+    return value;
+}
+
+u16 DaFile::ReadU16LE(u8* bytes, u32 &stream_bit_offset){
+    u32 byte_offset = stream_bit_offset / 8;
+    u16 data = (bytes + byte_offset)[0] | ((bytes + byte_offset)[1] << 8);
+    stream_bit_offset += 16;
+    return data;
+}
+
+int DaFile::GetBitsFromStream(u8* bytes, u32 &stream_bit_offset, int bits){
+    int value = 0;
+    unsigned int byte_offset = stream_bit_offset / 8;
+    unsigned int bit_offset = stream_bit_offset % 8;
+    //std::cout << "GetBitsFromStream: " << bits << " bits from " << stream_bit_offset
+    //  << ": (" << (int)bytes[byte_offset] << " " << (int)bytes[byte_offset + 1]
+    //  << "):\t ";
+    for (int b = 0; b < bits; b ++){
+        u8 byte = bytes[byte_offset + (bit_offset + b) / 8];
+        int bit = (byte >> ((bit_offset + 7 - b) % 8) & 0x01);
+        //std::cout << " ["<< (int)byte <<":" << (int)((bit_offset + 7 - b) % 8) << "]" << bit;
+        value += bit * (pow(2, bits - 1 - b));
+        stream_bit_offset += 1;
+    }
+    //std::cout << "\n";
+    return value;
+}
+
+u16 DaFile::GetCompressedDeltaFromStream(
+  u8* bytes, u32& stream_bit_offset, int lowered_precision_bits
+){
+    unsigned int bits;
+    int first_bit = GetBitsFromStream(bytes, stream_bit_offset, 1);
+    if (first_bit){
+        unsigned int key = GetBitsFromStream(bytes, stream_bit_offset, 3) & 7;
+        int temp;
+        switch (key){
+            case 0: // Return the smallest possible decrement delta (at given precision).
+                return (-1 << lowered_precision_bits);
+            case 1: case 2: case 3: case 4: case 5: case 6:
+                // Read a corresponding number of bits from the stream.
+                temp = GetBitsFromStream(bytes, stream_bit_offset, bits);
+                // Transform the value into the full seven-bit value, using the bit length
+                // as part of the encoding scheme (see notes).
+                if (temp < 0) temp -= 1 << (bits - 1);
+                else temp += 1 << (bits - 1);
+                // Adapt to the requested precision and return.
+                return (temp << lowered_precision_bits);
+            case 7:
+                // Read an uncompressed value from the stream (at requested precision),
+                // and return.
+                temp = GetBitsFromStream(
+                  bytes, stream_bit_offset, 12 - lowered_precision_bits
+                );
+                return (temp << lowered_precision_bits);
+        }
+    }
+    // Default/error: return zero.
+    return 0;
+}
+
+u32 DaFile::LoadFrames(Frame* frame, int bones, int bit_start, u8* animation_buffer){
+    // Get backups of the information we need.
+    u32 orig_bit_start = bit_start;
+    int orig_bones = bones;
+    u8* orig_animation_buffer = animation_buffer;
+    AnimationMiniHeader* mini_header = (AnimationMiniHeader*) animation_buffer;
+    u16 data_size = mini_header->data_size;
+    u8 key = mini_header->key;
+
+    // Skip the first 5 bytes because they are part of the frame header.
+    orig_animation_buffer += sizeof(AnimationMiniHeader);
+
+    if (bit_start == 0){ // First frame?
+
+        // Skip the next 6 bytes. Why? I don't know what they are.
+        orig_bit_start += 8 * 8;
+
+        // The first frame is uncompressed and each value is the actual rotation.
+        //std::cout << "FIRSR TRAME READING AT " << orig_bit_start;
+        frame->position_offset.s_x = GetBitsFromStream(orig_animation_buffer, orig_bit_start, 16);
+          //= ReadU16LE(orig_animation_buffer, orig_bit_start);
+        //std::cout << ", " << orig_bit_start;
+        frame->position_offset.s_y = GetBitsFromStream(orig_animation_buffer, orig_bit_start, 16);
+          //= ReadU16LE(orig_animation_buffer, orig_bit_start);
+        //std::cout << ", " << orig_bit_start;
+        frame->position_offset.s_z = GetBitsFromStream(orig_animation_buffer, orig_bit_start, 16);
+          //= ReadU16LE(orig_animation_buffer, orig_bit_start);
+        //std::cout << " END AT " << orig_bit_start << "\n";
+
+        // This function will set the FLOAT values for the positions.
+        // Any scaling that needs to be done would be done here.
+        frame->position_offset.f_x = static_cast<float>(frame->position_offset.s_x);
+        frame->position_offset.f_y = static_cast<float>(frame->position_offset.s_y);
+        frame->position_offset.f_z = static_cast<float>(frame->position_offset.s_z);
+
+        // TODO: For first bone, go back 2*3 bytes, and reread them, but this time as
+        // little endian for the rotations.
+        orig_bit_start -= (2 * 3 * 8);
+
+        frame->rotations[0].s_x = ReadU16LE(orig_animation_buffer, orig_bit_start);
+        frame->rotations[0].s_y = ReadU16LE(orig_animation_buffer, orig_bit_start);
+        frame->rotations[0].s_z = ReadU16LE(orig_animation_buffer, orig_bit_start);
+        frame->rotations[0].i_x = (frame->rotations[0].s_x < 0) ?
+          frame->rotations[0].s_x + 0x1000 : frame->rotations[0].s_x;
+        frame->rotations[0].i_y = (frame->rotations[0].s_y < 0) ?
+          frame->rotations[0].s_y + 0x1000 : frame->rotations[0].s_y;
+        frame->rotations[0].i_z = (frame->rotations[0].s_z < 0) ?
+          frame->rotations[0].s_z + 0x1000 : frame->rotations[0].s_z;
+
+        //std::cout << "FIRST FRAME FIRST BONE: "
+        //  << frame->rotations[0].i_x << " (" <<  (int) frame->rotations[0].s_x << "), "
+        //  << frame->rotations[0].i_y << " (" <<  (int) frame->rotations[0].s_y << "), "
+        //  << frame->rotations[0].i_z << " (" <<  (int) frame->rotations[0].s_z << ")\n";
+
+        for (int i = 1; i < orig_bones - 1; i++){
+            // Now get each bone rotation (the first bone is actually the root, not part of
+            // the skeleton). During the first frame, the rotations are always (12 - key).
+            // bits. The values are shifted by key to align it to 12 bits.
+            frame->rotations[i].s_x
+              = GetBitsFromStream(orig_animation_buffer, orig_bit_start, 12 - key) << key;
+            frame->rotations[i].s_y
+              = GetBitsFromStream(orig_animation_buffer, orig_bit_start, 12 - key) << key;
+            frame->rotations[i].s_z
+              = GetBitsFromStream(orig_animation_buffer, orig_bit_start, 12 - key) << key;
+
+            // Store the INT version as the absolute value of the SHORT version.
+            frame->rotations[i].i_x = (frame->rotations[i].s_x < 0) ?
+              frame->rotations[i].s_x + 0x1000 : frame->rotations[i].s_x;
+            frame->rotations[i].i_y = (frame->rotations[i].s_y < 0) ?
+              frame->rotations[i].s_y + 0x1000 : frame->rotations[i].s_y;
+            frame->rotations[i].i_z = (frame->rotations[i].s_z < 0) ?
+              frame->rotations[i].s_z + 0x1000 : frame->rotations[i].s_z;
+
+            /*if (i < 3){
+                std::cout << "FIRST FRAME BONE " << i << ": "
+                  << frame->rotations[i].i_x << " (" <<  (int) frame->rotations[i].s_x << "), "
+                  << frame->rotations[i].i_y << " (" <<  (int) frame->rotations[i].s_y << "), "
+                  << frame->rotations[i].i_z << " (" <<  (int) frame->rotations[i].s_z << ")\n";
+            }*/
+        }
+    }
+    else { // All other frames.
+        u16 x, y, z; // Get the positional offsets.
+        x = GetValueFromStream(orig_animation_buffer, orig_bit_start);
+        y = GetValueFromStream(orig_animation_buffer, orig_bit_start);
+        z = GetValueFromStream(orig_animation_buffer, orig_bit_start);
+
+        // When reaching this area of the function, frame will have the previous frame
+        // still stored in it.  Just add the offsets.
+        frame->position_offset.s_x += x;
+        frame->position_offset.s_y += y;
+        frame->position_offset.s_z += z;
+        frame->position_offset.f_x = static_cast<float>(frame->position_offset.s_x);
+        frame->position_offset.f_y = static_cast<float>(frame->position_offset.s_y);
+        frame->position_offset.f_z = static_cast<float>(frame->position_offset.s_z);
+        for (int i = 0; i < orig_bones - 1; i++){
+            // The same applies here.  Add the offsets and convert to INT form, adding
+            // 0x1000 if it is less than 0.
+            // When Final Fantasy VII loads these animations, it is possible for the value
+            // to sneak up above the 4095 boundary through a series of positive offsets.
+            x = GetCompressedDeltaFromStream(orig_animation_buffer, orig_bit_start, key);
+            y = GetCompressedDeltaFromStream(orig_animation_buffer, orig_bit_start, key);
+            z = GetCompressedDeltaFromStream(orig_animation_buffer, orig_bit_start, key);
+            frame->rotations[i].s_x += x;
+            frame->rotations[i].s_y += y;
+            frame->rotations[i].s_z += z;
+            frame->rotations[i].i_x = (frame->rotations[i].s_x < 0) ?
+              frame->rotations[i].s_x + 0x1000 : frame->rotations[i].s_z;
+            frame->rotations[i].i_y = (frame->rotations[i].s_y < 0) ?
+              frame->rotations[i].s_y + 0x1000 : frame->rotations[i].s_y;
+            frame->rotations[i].i_z = (frame->rotations[i].s_z < 0) ?
+              frame->rotations[i].s_z + 0x1000 : frame->rotations[i].s_z;
+        }
+    }
+
+    // If not as many bits as there are in the frame are read, return the location where
+    // the bits should start for the next frame. Otherwise, return 0.
+    if (orig_bit_start / 8 < data_size) return orig_bit_start;
+    return 0;
+}

+ 351 - 0
src/installer/data/DaFile.h

@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+
+#include <vector>
+#include "common/File.h"
+#include "common/TypeDefine.h"
+
+/**
+ * Represents a compiled animations file.
+ *
+ * Da files are bundled into battle.lgp. They contain information about a skeleton animation. They
+ * are heavily compressed, and involve a lot of bit reading to decompile.
+ */
+class DaFile{
+
+    public:
+
+        /**
+         * Constructor.
+         *
+         * Reads the file.
+         *
+         * @param[in, out] file The file to read from. The file contents will not be altered, but
+         * it's offset will be changed while reading it.
+         */
+        DaFile(File file);
+
+        /**
+         * Generates .a files from the da file.
+         *
+         * A file will be generated for each animation in the da file. See {@see VGearsAFile.h} for
+         * more info about .a files. Files will be created in the specified path, and will be named
+         * like the model id, followed by an underscore, followed a two-digit animation index
+         * (starting from 0) followed by the ".a extension". For example, if path is
+         * "/home/user/.v-gears", the model id is "ac", and the da file contains three animations,
+         * the following files will be generated: "/home/user/.v-gears/ac_00.a",
+         * "/home/user/.v-gears/ac_01.a" and "/home/user/.v-gears/ac_02.a".
+         *
+         * @param[in] model_id ID of the model the animation belongs to, usually a two letter
+         * character. Used to generate the file name.
+         * @param[in] path Path to the directory where the files will be saved.
+         * @return A list of the generated files. They include the path and the extension.
+         */
+        std::vector<std::string> GenerateAFiles(std::string model_id, std::string path);
+
+    private:
+
+        /**
+         * A bone rotation, with all it's values calculated.
+         */
+        struct Rotation{
+
+            /**
+             * Rotation in the X axis, in degrees.
+             */
+            float x;
+
+            /**
+             * Rotation in the Y axis, in degrees.
+             */
+            float y;
+
+            /**
+             * Rotation in the Z axis, in degrees.
+             */
+            float z;
+        };
+
+        /**
+         * Processed frame data, to be written to .a files.
+         */
+        struct FrameData{
+
+            /**
+             * Position offset for the frame.
+             */
+            Rotation position_offset;
+
+            /**
+             * List of bone rotations, one per bone.
+             */
+            std::vector<Rotation> rotations;
+        };
+
+        /**
+         * Processed animation data, to be written to.a files.
+         */
+        struct Animation{
+
+            /**
+             * Number of frames in the animation.
+             */
+            unsigned int frame_count;
+
+            /**
+             * Number of bones involved in the animation.
+             */
+            unsigned int bone_count;
+
+            /**
+             * Each frame in the animation.
+             */
+            std::vector<FrameData> frames;
+        };
+
+        /**
+         * Header for animations. 12 bytes, at the start of each animation.
+         */
+        struct AnimationHeader {
+
+            /**
+             * Number of bones + 1 in the animation.
+             *
+             * It's not always reliable, the number of bones in the skeleton is the real one. If
+             * dealing with a weapon animation, the value will always be 1.
+             */
+            u32 bone_count;
+
+            /**
+             * Number of frames in the animation.
+             */
+            u32 frame_count;
+
+            /**
+             * Size, in bytes, of the animation data.
+             */
+            u32 data_size;
+        };
+
+        /**
+         * Mini header for each animation. 5 bytes, immediately after each main header.
+         */
+        struct AnimationMiniHeader {
+
+            /**
+             * Number of frames in the animation.
+             *
+             * It may not match {@see AnimationHeader.frame_count}.
+             */
+            u16 frame_count;
+
+            /**
+             * Size of the animation data.
+             *
+             * It should be 6 bytes less than {@see AnimationHeader.data_size}.
+             */
+            u16 data_size;
+
+            /**
+             * Key used for decoding bone data.
+             *
+             * It indicates how many bits are to be read to get a bone rotation. Not to be used in
+             * the first frame, which is not compressed.
+             *
+             * 0: 12 bits per bone rotation, 36 bits per bone.
+             * 2: 10 bits per bone rotation, 30 bits per bone.
+             * 4: 8 bits per bone rotation, 30 bits per bone.
+             */
+            u8 key;
+        };
+
+        /**
+         * Bone rotation.
+         */
+        struct BoneRotation {
+
+            /**
+             * X axis rotation, as read from the da file.
+             */
+            u16 s_x;
+
+            /**
+             * Y axis rotation, as read from the da file.
+             */
+            u16 s_y;
+
+            /**
+             * Z axis rotation, as read from the da file.
+             */
+            u16 s_z;     // Signed short versions.   0x00
+
+            /**
+             * X axis rotation, converted to integer. Rotation between 0 and 4096.
+             */
+            int i_x;
+
+            /**
+             * Y axis rotation, converted to integer. Rotation between 0 and 4096.
+             */
+            int i_y;
+
+            /**
+             * Z axis rotation, converted to integer. Rotation between 0 and 4096.
+             */
+            int i_z;
+
+            /**
+             * X axis rotation, in degrees.
+             */
+            float f_x;
+
+            /**
+             * Y axis rotation, in degrees.
+             */
+            float f_y;
+
+            /**
+             * Z axis rotation, in degrees.
+             */
+            float f_z;
+        };
+
+        /**
+         * A frame of an animation.
+         *
+         * Stores all bone rotations for a frame.
+         */
+        struct Frame {
+
+            /**
+             * Number of bones in the animation frame.
+             */
+            u32 bone_count;
+
+            /**
+             * Position offset.
+             *
+             * @todo Verify if it's the previuos frame?
+             */
+            BoneRotation position_offset;
+
+            /**
+             * Bone rotations for the frame.
+             */
+            BoneRotation* rotations;
+
+            /**
+             * Constructor. Initializes data.
+             */
+            Frame() {
+                bone_count = 0;
+                rotations = NULL;
+            }
+
+            /**
+             * Destructor.
+             */
+            ~Frame() {
+                bone_count = 0;
+                delete [] rotations;
+                rotations = NULL;
+            }
+
+            /**
+             * Sets the number of bones and initializes data arrays.
+             */
+            void SetBones(u32 bones ) {
+                // Delete the old.
+                bone_count = 0;
+                delete [] rotations;
+                // Create the new.
+                rotations = new BoneRotation[bones];
+                if (rotations != NULL) bone_count = bones;
+            }
+        };
+
+        /**
+         * Reads the Da file and extracts all the data.
+         *
+         * @param[in, out] file The file to read from. The file contents will not be altered, but
+         * it's offset will be changed while reading it.
+         */
+        void Read(File file);
+
+        /**
+         * Retrieves a value of dynamic size from the byte stream.
+         *
+         * The first read bit will determine the length of the read data. If it's 1, 7 more bits of
+         * data will be read and returned. It it isn't, 16 more bits of data will be read and
+         * returned. In total, either 17 or 8 bits of data will be read.
+         *
+         * @param[in] bytes The byte stream to read from.
+         * @param[in,out] stream_bit_offset The bit at which to start reading. For each bit read,
+         * it will be advanced by one.
+         * @return The read value.
+         */
+        u16 GetValueFromStream(u8* bytes, u32 &stream_bit_offset);
+
+        /**
+         * Reads 2 bytes from the stream as a little endian value.
+         *
+         * @param[in] bytes The byte stream to read from.
+         * @param[in,out] stream_bit_offset The bit at which to start reading. It will be advanced
+         * by 16.
+         * @return The read value.
+         */
+        u16 ReadU16LE(u8* bytes, u32 &stream_bit_offset);
+
+        /**
+         * Reads an arbitrary length of bits from the stream.
+         *
+         * @param[in] bytes The byte stream to read from.
+         * @param[in,out] stream_bit_offset The bit at which to start reading. It will be advanced
+         * by one per read bit.
+         * @return The read value.
+         */
+        int GetBitsFromStream(u8* bytes, u32 &stream_bit_offset, int bits);
+
+        /**
+         * Decodes a delta rotation read from the stream.
+         *
+         * @param[in] bytes The byte stream to read from.
+         * @param[in,out] stream_bit_offset The bit at which to start reading. It will be advanced
+         * by one per read bit.
+         * @param How many bits to sift the read value.
+         * @return The decoded value.
+         */
+        u16 GetCompressedDeltaFromStream(
+          u8* bytes, u32& stream_bit_offset, int lowered_precision_bits
+        );
+
+        /**
+         * Loads the frames in an animation.
+         *
+         * @param[out] Read frame data will be set here.
+         * @param[in] bones The number of bones in the animation.
+         * @param[in] bit_start Bit to start reading from animation_buffer at.
+         * @param[in] animation_buffer The animation data.
+         */
+        u32 LoadFrames(Frame* frame, int bones, int bit_start, u8* animation_buffer);
+
+        /**
+         * List of animations.
+         */
+        std::vector<Animation> animations_;
+
+};