Browse Source

Installer: Offsets for battle animations properly extracted.

Other changes:
 - Battle entities can now be reoriented during battle, and enemies by default face the party.
 - Correch battle models for party characters.
Iñigo Valentin 3 năm trước cách đây
mục cha
commit
d6f3d5406c

+ 7 - 7
src/FF7Character.h

@@ -40,22 +40,22 @@ class Character{
             /**
              * Cloud's ID.
              */
-            CLOUD   = 0,
+            CLOUD = 0,
 
             /**
-             * Tifa's ID.
+             * Barret's ID.
              */
-            TIFA = 1,
+            BARRET = 1,
 
             /**
-             * Aerith's ID.
+             * Tifa's ID.
              */
-            AERIS = 2,
+            TIFA = 2,
 
             /**
-             * Barret's ID.
+             * Aerith's ID.
              */
-            BARRET = 3,
+            AERIS = 3,
 
             /**
              * Red XIII's ID.

+ 4 - 1
src/core/BattleManager.cpp

@@ -100,7 +100,6 @@ void BattleManager::StartBattle(const unsigned int id){
         return;
     }
     SetBattleModule();
-    SetFormationId(id);
     AudioManager::getSingleton().SetBattleModule();
     CameraManager::getSingleton().SetBattleModule();
     DialogsManager::getSingleton().SetBattleModule();
@@ -109,6 +108,7 @@ void BattleManager::StartBattle(const unsigned int id){
     InputManager::getSingleton().SetBattleModule();
     ScriptManager::getSingleton().SetBattleModule();
     UiManager::getSingleton().SetBattleModule();
+    SetFormationId(id);
 
     // Load the formation XML file (name is dddd.xml)
     std::string filename = std::to_string(formation_id_);
@@ -177,6 +177,9 @@ void BattleManager::AddEnemy(
       "enemies/" + enemy->GetModel() + ".mesh", enemy->GetPos(), Ogre::Degree(1),
       Ogre::Vector3(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE), Ogre::Quaternion(1, 1, 0, 0), id
     );
+    EntityManager::getSingleton().GetEntity(
+      enemy->GetName() + "_" + std::to_string(enemies_.size() - 1)
+    )->SetRotation(Ogre::Degree(180));
 }
 
 void BattleManager::AddCamera(

+ 7 - 2
src/core/Entity.cpp

@@ -18,6 +18,7 @@
 #include "core/ConfigVar.h"
 #include "core/DebugDraw.h"
 #include "core/Entity.h"
+#include "core/EntityManager.h"
 #include "core/Logger.h"
 
 ConfigVar cv_debug_entity("debug_entity", "Draw entity debug info", "0");
@@ -237,7 +238,9 @@ void Entity::SetRotation(const Ogre::Degree& rotation){
       = rotation.valueDegrees() - Ogre::Math::Floor(rotation.valueDegrees() / 360.0f) * 360.0f;
     if (angle < 0) angle = 360 + angle;
     Ogre::Quaternion q;
-    Ogre::Vector3 vec = Ogre::Vector3::UNIT_Z;
+    // On battle module, rotate on Y axis.
+    Ogre::Vector3 vec = EntityManager::getSingleton().IsBattleModule()
+      ? Ogre::Vector3::UNIT_Y : Ogre::Vector3::UNIT_Z;
     q.FromAngleAxis(Ogre::Radian(Ogre::Degree(angle)), vec);
     model_root_node_->setOrientation(q);
     direction_node_->setOrientation(q);
@@ -249,7 +252,9 @@ Ogre::Degree Entity::GetRotation() const{
     assert(model_root_node_);
     Ogre::Quaternion q = model_root_node_->getOrientation();
     Ogre::Degree temp;
-    Ogre::Vector3 vec = Ogre::Vector3::UNIT_Z;
+    // On battle module, rotate on Y axis.
+    Ogre::Vector3 vec = EntityManager::getSingleton().IsBattleModule()
+      ? Ogre::Vector3::UNIT_Z : Ogre::Vector3::UNIT_Z;
     q.ToAngleAxis(temp, vec);
     return temp;
 }

+ 5 - 2
src/core/EntityManager.cpp

@@ -387,7 +387,7 @@ void EntityManager::SetBackground3D(const Ogre::String& name, const Ogre::String
     }
     Ogre::SceneNode* node = scene_node_->createChildSceneNode("bg_" + name);
     background_3d_ = new EntityModel(name, file_name, node);
-    background_3d_->SetPosition(Ogre::Vector3(0, 0, -2));
+    background_3d_->SetPosition(Ogre::Vector3(0, 0, 0));
     background_3d_->SetRotation(Ogre::Degree(0));
     background_3d_->setScale(Ogre::Vector3(SCENE_SCALE, SCENE_SCALE, SCENE_SCALE));
     background_3d_->SetIndex(IsBattleModule() ? BATTLE_BACKGROUND_ID : WORLD_MAP_BACKGROUND_ID);
@@ -459,7 +459,10 @@ void EntityManager::ClearBattle(){
     battle_entity_.clear();
     if (background_3d_ != nullptr){
         scene_node_->removeAndDestroyChild("bg_" + background_3d_->GetName());
-        delete background_3d_;
+        ScriptManager::getSingleton().RemoveEntity(
+          ScriptManager::ENTITY, background_3d_->GetName()
+        );
+        background_3d_ = nullptr;
     }
 }
 

+ 134 - 197
src/installer/data/DaFile.cpp

@@ -18,11 +18,10 @@
 #include <cstdint>
 #include "data/DaFile.h"
 
-DaFile::DaFile(File file): da_file_(file){
+const float DaFile::OFFSET_SCALE = 0.0070312473867f;
 
-    //std::cout << "[DAFILE] " << file.GetFileName() << std::endl;
+DaFile::DaFile(File file): da_file_(file){
     animations_.clear();
-
     // Save file contents as a byte array.
     file.SetOffset(0);
     da_bytes_ = new u8[file.GetFileSize()];
@@ -32,61 +31,57 @@ DaFile::DaFile(File file): da_file_(file){
     Read();
 }
 
-int DaFile::GetBitsU(int bits){
-    int value = 0;
-    int base_byte, bi, res, num_bytes, unaligned_by_bits;
-    int first_aligned_byte, last_aligned_byte, end_bits;
-    bool is_aligned, clean_end;
-    if (bits < 1) return value;
-    base_byte = bit_offset_ / 8;
-    unaligned_by_bits = bit_offset_ % 8;
-
-    if (unaligned_by_bits + bits > 8){
-        is_aligned = (unaligned_by_bits == 0);
-        end_bits = (bit_offset_ + bits) % 8;
-        clean_end = (end_bits == 0);
-        num_bytes =
-          (bits - (is_aligned ? 0 : 8 - unaligned_by_bits) - (clean_end ? 0 : end_bits)) / 8
-          + (is_aligned ? 0 : 1) + (clean_end ? 0 : 1);
-        last_aligned_byte = num_bytes - (clean_end ? 0 : 1) - 1;
-        first_aligned_byte = 0;
-
-        res = 0;
-        //  Unaligned prefix
-        //  Stored at the begining of the byte
-        if (!is_aligned){
-            res = da_bytes_[base_byte];
-            res &= (int) (pow(2, (8 - unaligned_by_bits)) - 1);
-            first_aligned_byte = 1;
-        }
-
-        //  Aligned bytes
-        for (bi = first_aligned_byte; bi <= last_aligned_byte; bi ++){
-            res *= 256;
-            res |= da_bytes_[base_byte + bi];
-        }
-
-        //  Sufix
-        //  Stored at the end of the byte
-        if (!clean_end){
-            res *= (int) pow(2, end_bits);
-            res |= (
-              (da_bytes_[base_byte + last_aligned_byte + 1]) / (int) (pow(2, 8 - end_bits))
-              & (int) (pow(2, end_bits) - 1)
-            );
+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 ++){
+        /*
+         * .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 offset,
+         * and after them, the bone rotation data.
+         */
+        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);
+        int bone_count = animations_[anim].bone_count - 1;
+        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*>(&bone_count), 4);
+        a.write(reinterpret_cast<const char*>(&rotation_order), 4);
+        a.write(reinterpret_cast<const char*>(&zero), 4); // Runtime data, skip 5 bytes.
+        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 ++){
+            // Root rotation (always 0).
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            a.write(reinterpret_cast<const char*>(&zero), 4);
+            // Root offset.
+            a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].offset.f_x), 4);
+            a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].offset.f_y), 4);
+            a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].offset.f_z), 4);
+            // Bones.
+            for (int b = 1; b < animations_[anim].frames[f].bones.size(); b ++){
+                a.write(
+                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_x), 4
+                );
+                a.write(
+                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_y), 4
+                );
+                a.write(
+                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_z), 4
+                );
+            }
         }
+        a.close();
+        file_list.push_back(file_name);
     }
-    else{
-        res = da_bytes_[base_byte];
-        res /= (int) pow(2, 8 - (unaligned_by_bits + bits));
-        res &= (int) (pow(2, bits) - 1);
-    }
-
-    value = (short)res;
-
-    bit_offset_ += bits;
-
-    return value;
+    return file_list;
 }
 
 int DaFile::ExtendSignInteger(int val, int len){
@@ -110,48 +105,36 @@ int DaFile::GetSignExtendedShort(int val, int len){
     return result;
 }
 
-int DaFile::GetBitsS(int bits){
-    int tmp = GetBitsU(bits);
-    return GetSignExtendedShort(tmp, bits);
-}
-
-
-
 void DaFile::Read(){
+    bit_offset_ = 0;
+    da_file_.SetOffset(0);
     int num_animations = da_file_.readU32LE();
     bit_offset_ += 32;
     for (int a = 0; a < num_animations; a ++){
+    //for (int a = 0; a < 1; a ++){ // TODO DEBUG
         int start_offset = bit_offset_ / 8; // In bytes.
-        //std::cout << "    ANIMATION " << a << "/" << num_animations
-        //  << " START: " << start_offset;
         Animation anim = ReadAnimation();
         if (anim.frame_count > 0) animations_.push_back(anim);
         // Manually go to the next animation
-        bit_offset_ = (start_offset + anim.size + 12) * 8; // + 12 is for skipping the naim header.
+        bit_offset_ = (start_offset + anim.size + 12) * 8; // + 12 is for skipping the main header.
         da_file_.SetOffset(start_offset + anim.size + 12);
-
     }
 }
 
 DaFile::Animation DaFile::ReadAnimation(){
     Animation anim;
     // Read the animation headers.
+    da_file_.SetOffset(bit_offset_ / 8);
     anim.bone_count = da_file_.readU32LE();
     bit_offset_ += 32;
     anim.frame_count = da_file_.readU32LE();
     bit_offset_ += 32;
     anim.size = da_file_.readU32LE();
-    //std::cout << " Bones: " << anim.bone_count << " Frames: " << anim.bone_count << " Size: "
-    //  << anim.size;
     // Skip small (< 11 bytes) animations.
     if (anim.size < 11){
-        //std::cout << " SKIP" << std::endl;
-        //da_file_.SetOffset(da_file_.GetCurrentOffset() + anim.size);
-        //bit_offset_ += anim.size;
         anim.frame_count = 0;
         return anim;
     }
-    //std::cout << " READ" << std::endl;
     bit_offset_ += 32;
     da_file_.readU32LE(); // Duplicated Frames + Size
     bit_offset_ += 32;
@@ -168,47 +151,31 @@ DaFile::Animation DaFile::ReadAnimation(){
 
 DaFile::Frame DaFile::ReadFirstFrame(u32 bones, u8 key){
     Frame frame;
-
-    // Read root offset. In the first frame, always 16 bytes each.
-    frame.offset.s_x = GetBitsS(16);
-    frame.offset.s_y = GetBitsS(16);
-    frame.offset.s_z = GetBitsS(16);
-
+    // Read root offset. In the first frame, always 16 bytes each, as short.
+    frame.offset.s_x = (int16_t) GetBitsS(16);
+    frame.offset.s_y = (int16_t) GetBitsS(16);
+    frame.offset.s_z = (int16_t) GetBitsS(16);
     // Turn offset to float. Any scaling that needs to be done would be done here.
-    frame.offset.f_x = 0;//static_cast<float>(frame.offset.s_x);
-    frame.offset.f_y = 0;//static_cast<float>(frame.offset.s_y) * -1;
-    frame.offset.f_z = 0;//static_cast<float>(frame.offset.s_z);
-
-    // Read root rotation
-    frame.rotation.s_x = 0;//GetBitsFromStream(12 - key) << key;
-    frame.rotation.s_y = 0;//GetBitsFromStream(12 - key) << key;
-    frame.rotation.s_z = 0;//GetBitsFromStream(12 - key) << key;
-
-    // convert rotation to integer.
-    frame.rotation.i_x = frame.rotation.s_x < 0 ? frame.rotation.s_x + 0x1000 : frame.rotation.s_x;
-    frame.rotation.i_y = frame.rotation.s_y < 0 ? frame.rotation.s_y + 0x1000 : frame.rotation.s_y;
-    frame.rotation.i_z = frame.rotation.s_z < 0 ? frame.rotation.s_z + 0x1000 : frame.rotation.s_z;
-    frame.rotation.f_x = static_cast<float>(frame.rotation.i_x) / (12 - key) * 360.0f;
-    frame.rotation.f_y = static_cast<float>(frame.rotation.i_y) / (12 - key) * 360.0f;
-    frame.rotation.f_z = static_cast<float>(frame.rotation.i_z) / (12 - key) * 360.0f;
-
+    frame.offset.f_x = static_cast<float>(frame.offset.s_x) * OFFSET_SCALE;
+    frame.offset.f_y = static_cast<float>(frame.offset.s_y) * OFFSET_SCALE;
+    frame.offset.f_z = static_cast<float>(frame.offset.s_z) * OFFSET_SCALE;
+    // Read bones.
     for (int i = 0; i < bones; i ++){
         TriValue bone;
         // 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.
-        bone.s_x = GetBitsFromStream(12 - key) << key;
-        bone.s_y = GetBitsFromStream(12 - key) << key;
-        bone.s_z = GetBitsFromStream(12 - key) << key;
-
+        bone.s_x = GetBitsS(12 - key) << key;
+        bone.s_y = GetBitsS(12 - key) << key;
+        bone.s_z = GetBitsS(12 - key) << key;
         // Store the INT version as the absolute value of the SHORT version.
         bone.i_x = (bone.s_x < 0) ? bone.s_x + 0x1000 : bone.s_x;
         bone.i_y = (bone.s_y < 0) ? bone.s_y + 0x1000 : bone.s_y;
         bone.i_z = (bone.s_z < 0) ? bone.s_z + 0x1000 : bone.s_z;
+        // Convert to float.
         bone.f_x = static_cast<float>(bone.i_x) / 4096.0f * 360.0f;
         bone.f_y = static_cast<float>(bone.i_y) / 4096.0f * 360.0f;
         bone.f_z = static_cast<float>(bone.i_z) / 4096.0f * 360.0f;
-
         frame.bones.push_back(bone);
     }
     return frame;
@@ -218,28 +185,21 @@ DaFile::Frame DaFile::ReadFrame(u32 bones, u8 key, Frame prev){
     Frame frame;
 
     // Get dynamic root offset and add the previous frame values.
-    frame.offset.s_x = GetDynamicOffsetFromStream() + prev.offset.s_x;
-    frame.offset.s_y = GetDynamicOffsetFromStream() + prev.offset.s_y;
-    frame.offset.s_z = GetDynamicOffsetFromStream() + prev.offset.s_z;
-    frame.offset.f_x = 0.0f;//static_cast<float>(frame.offset.s_x);
-    frame.offset.f_y = 0.0f;//static_cast<float>(frame.offset.s_y) * -1;
-    frame.offset.f_z = 0.0f;//static_cast<float>(frame.offset.s_z);
-
-    frame.rotation.s_x = 0;//GetCompressedDeltaFromStream(key) + prev.rotation.s_x;
-    frame.rotation.s_y = 0;//GetCompressedDeltaFromStream(key) + prev.rotation.s_y;
-    frame.rotation.s_z = 0;//GetCompressedDeltaFromStream(key) + prev.rotation.s_z;
-    frame.rotation.i_x = 0;//frame.rotation.s_x < 0 ? frame.rotation.s_x + 0x1000 : frame.rotation.s_x;
-    frame.rotation.i_y = 0;//frame.rotation.s_y < 0 ? frame.rotation.s_y + 0x1000 : frame.rotation.s_y;
-    frame.rotation.i_z = 0;//frame.rotation.s_z < 0 ? frame.rotation.s_z + 0x1000 : frame.rotation.s_z;
-    frame.rotation.f_x = static_cast<float>(frame.rotation.i_x) / pow(2, 12 - key) * 360.0f;
-    frame.rotation.f_y = static_cast<float>(frame.rotation.i_y) / pow(2, 12 - key) * 360.0f;
-    frame.rotation.f_z = static_cast<float>(frame.rotation.i_z) / pow(2, 12 - key) * 360.0f;
+    // Note: in compressed frames, offset delta is a signed integer, so skip the short value
+    frame.offset.s_x = 0;
+    frame.offset.s_y = 0;
+    frame.offset.s_z = 0;
+    frame.offset.i_x = GetDynamicOffsetFromStream();
+    frame.offset.i_y = GetDynamicOffsetFromStream();
+    frame.offset.i_z = GetDynamicOffsetFromStream();
+    frame.offset.f_x = static_cast<float>(frame.offset.i_x) + prev.offset.f_x;
+    frame.offset.f_y = static_cast<float>(frame.offset.i_y) * -1 + prev.offset.f_y; // Invert Y.
+    frame.offset.f_z = static_cast<float>(frame.offset.i_z) + prev.offset.f_z;
 
     for (int i = 0; i < bones; i ++){
         TriValue bone;
-        // 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
+        // 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.
         bone.s_x = GetCompressedDeltaFromStream(key) + prev.bones[i].s_x;
         bone.s_y = GetCompressedDeltaFromStream(key) + prev.bones[i].s_y;
@@ -255,97 +215,74 @@ DaFile::Frame DaFile::ReadFrame(u32 bones, u8 key, Frame prev){
     return frame;
 }
 
-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);
-        int bone_count = animations_[anim].bone_count;
-        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*>(&bone_count), 4);
-        a.write(reinterpret_cast<const char*>(&rotation_order), 4);
-        a.write(reinterpret_cast<const char*>(&zero), 4); // Runtime data, skip
-        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*>(&animations_[anim].frames[f].rotation.i_x), 4);
-            a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].rotation.i_y), 4);
-            a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].rotation.i_z), 4);
-
-            a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].offset.f_x), 4);
-            a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].offset.f_y), 4);
-            a.write(reinterpret_cast<const char*>(&animations_[anim].frames[f].offset.f_z), 4);
-
-            for (int b = 1; b < animations_[anim].frames[f].bones.size() + 1; b ++){
-                a.write(
-                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_x), 4
-                );
-                a.write(
-                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_y), 4
-                );
-                a.write(
-                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_z), 4
-                );
-            }
-        }
-        a.close();
-        file_list.push_back(file_name);
-    }
-    return file_list;
-}
-
-u16 DaFile::GetDynamicOffsetFromStream(){
-    u16 value;
+int DaFile::GetDynamicOffsetFromStream(){
+    int value;
     int control_bit = GetBitsS(1);
     if (control_bit == 1) value = GetBitsS(16);
     else value = GetBitsS(7);
+    if (value > 0x7fff) value = (value - 0xffff);
     return value;
 }
 
-u16 DaFile::ReadU16LE(){
-    u32 byte_offset = bit_offset_ / 8;
-    u16 data = (da_bytes_ + byte_offset)[0] | ((da_bytes_ + byte_offset)[1] << 8);
-    bit_offset_ += 16;
-    return data;
-}
-
-int DaFile::GetBitsFromStream(int bits){
+int DaFile::GetBitsU(int bits){
     int value = 0;
-    unsigned int byte_offset = bit_offset_ / 8;
-    unsigned int bit_offset = bit_offset_ % 8;
-    for (int b = 0; b < bits; b ++){
-        u8 byte = da_bytes_[byte_offset + (bit_offset + b) / 8];
-        int bit = (byte >> ((bit_offset + 7 - b) % 8) & 0x01);
-        value += bit * (pow(2, bits - 1 - b));
-        bit_offset_ += 1;
+    int base_byte, bi, res, num_bytes, unaligned_by_bits;
+    int first_aligned_byte, last_aligned_byte, end_bits;
+    bool is_aligned, clean_end;
+    if (bits < 1) return value;
+    base_byte = bit_offset_ / 8;
+    unaligned_by_bits = bit_offset_ % 8;
+
+    if (unaligned_by_bits + bits > 8){
+        is_aligned = (unaligned_by_bits == 0);
+        end_bits = (bit_offset_ + bits) % 8;
+        clean_end = (end_bits == 0);
+        num_bytes =
+          (bits - (is_aligned ? 0 : 8 - unaligned_by_bits) - (clean_end ? 0 : end_bits)) / 8
+          + (is_aligned ? 0 : 1) + (clean_end ? 0 : 1);
+        last_aligned_byte = num_bytes - (clean_end ? 0 : 1) - 1;
+        first_aligned_byte = 0;
+        res = 0;
+        // Unaligned prefix: stored at the beginning of the byte
+        if (!is_aligned){
+            res = da_bytes_[base_byte];
+            res &= (int) (pow(2, (8 - unaligned_by_bits)) - 1);
+            first_aligned_byte = 1;
+        }
+        // Aligned bytes
+        for (bi = first_aligned_byte; bi <= last_aligned_byte; bi ++){
+            res *= 256;
+            res |= da_bytes_[base_byte + bi];
+        }
+        // Suffix: Stored at the end of the byte
+        if (!clean_end){
+            res *= (int) pow(2, end_bits);
+            res |= (
+              (da_bytes_[base_byte + last_aligned_byte + 1]) / (int) (pow(2, 8 - end_bits))
+              & (int) (pow(2, end_bits) - 1)
+            );
+        }
+    }
+    else{
+        res = da_bytes_[base_byte];
+        res /= (int) pow(2, 8 - (unaligned_by_bits + bits));
+        res &= (int) (pow(2, bits) - 1);
     }
+    value = (short)res;
+    bit_offset_ += bits;
     return value;
 }
 
+int DaFile::GetBitsS(int bits){
+    int tmp = GetBitsU(bits);
+    return GetSignExtendedShort(tmp, bits);
+}
+
 int DaFile::GetCompressedDeltaFromStream(int lowered_precision_bits){
     unsigned int bits;
     int first_bit = GetBitsU(1);
     if (first_bit == 1){
-        unsigned int key = GetBitsU(3);// & 7;
+        unsigned int key = GetBitsU(3);
         int temp, sign, orig_temp;
         unsigned int msb_mask;
         switch (key){
@@ -357,7 +294,7 @@ int DaFile::GetCompressedDeltaFromStream(int lowered_precision_bits){
                 temp = (int16_t) GetBitsS(bits);
                 //  Invert the value of the last bit
                 sign = (int) pow(2, bits - 1);
-
+                // Set sign.
                 if (temp < 0) temp -= sign;
                 else temp += sign;
                 // Convert to 12-bits value

+ 50 - 39
src/installer/data/DaFile.h

@@ -51,7 +51,7 @@ class DaFile{
          * "/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.
+         * code. 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.
          */
@@ -59,6 +59,11 @@ class DaFile{
 
     private:
 
+        /**
+         * Scale for bone offsets.
+         */
+        static const float OFFSET_SCALE;
+
         /**
          * A three-coordinate value.
          *
@@ -123,11 +128,6 @@ class DaFile{
              */
             TriValue offset;
 
-            /**
-             * Root rotation.
-             */
-            TriValue rotation;
-
             /**
              * Each of the bone rotations.
              */
@@ -167,6 +167,7 @@ class DaFile{
          *
          * @param[in] val The value to extend.
          * @param[in] len Number of bytes  to extend the value to.
+         * @return The signed integer.
          */
         static int ExtendSignInteger(int val, int len);
 
@@ -175,46 +176,70 @@ class DaFile{
          *
          * @param[in] val The value to extend.
          * @param[in] len Number of bytes  to extend the value to.
+         * @return The signed integer.
          */
         static int GetSignExtendedShort(int val, int len);
 
-
         /**
          * Reads the Da file and extracts all the data.
-         *
          */
         void Read();
 
+        /**
+         * Reads an animation from the *da file.
+         *
+         * Requires {@see bit_offset_} to indicate the bit at which the animation begins.
+         *
+         * If the read animation is invalid (i.e. with a size less than 11 bytes), the
+         * {@see Animation.frame_count} will be 0 and the rest of the data in the animation is not
+         * to be trusted, and the animation should not be used.
+         *
+         * @return The animation read.
+         */
         Animation ReadAnimation();
 
+        /**
+         * Reads the first, uncompressed frame.
+         *
+         * Requires {@see bit_offset_} to indicate the bit at which the frame begins.
+         *
+         * @param[in] bones The number of bones involved in the animation.
+         * @param[in] key Key for decoding the animation (fifth byte in the animation second
+         * header).
+         * @return The first frame.
+         */
         Frame ReadFirstFrame(u32 bones, u8 key);
 
-        Frame ReadFrame(u32 bones, u8 key, Frame prev);
-
         /**
-         * Retrieves a value of dynamic size from the byte stream.
+         * Reads a compressed frame.
          *
-         * 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.
+         * Requires {@see bit_offset_} to indicate the bit at which the frame begins.
          *
-         * @return The read value.
+         * @param[in] bones The number of bones involved in the animation.
+         * @param[in] key Key for decoding the animation (fifth byte in the animation second
+         * header).
+         * @param[in] prev Previous frame.
+         * @return The first frame.
          */
-        u16 GetDynamicOffsetFromStream();
+        Frame ReadFrame(u32 bones, u8 key, Frame prev);
 
         /**
-         * Reads 2 bytes from the stream as a little endian value.
+         * Retrieves a positional offset value of dynamic size from the file.
+         *
+         * Requires {@see bit_offset_} to indicate the bit at which the frame begins. 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. It will be advanced
-         * by 16.
          * @return The read value.
          */
-        u16 ReadU16LE();
+        int GetDynamicOffsetFromStream();
 
         /**
          * Read an arbitrary number of bits from the da contents as unsigned.
          *
+         * Bits will be read from {@see bit_offset_}.
+         *
          * @param[in] bits Number of bits.
          * @return Unsigned value.
          */
@@ -223,37 +248,23 @@ class DaFile{
         /**
          * Read an arbitrary number of bits from the da contents as signed.
          *
+         * Bits will be read from {@see bit_offset_}.
+         *
          * @param[in] bits Number of bits.
          * @return Signed value.
          */
         int GetBitsS(int bits);
 
-        /**
-         * Reads an arbitrary length of bits from the stream.
-         *
-         * @param[in] bits Number of bits to read.
-         * @return The read value.
-         */
-        int GetBitsFromStream(int bits);
-
         /**
          * Decodes a delta rotation read from the stream.
          *
+         * Requires {@see bit_offset_} to indicate the bit at which the delta begins
+         *
          * @param lowered_precision_bits How many bits to sift the read value. The animation key.
          * @return The decoded value.
          */
         int GetCompressedDeltaFromStream(int lowered_precision_bits);
 
-        /**
-         * Loads the frames in an animation.
-         *
-         * @param[out] frame 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);
-
         /**
          * The da file.
          */