Bladeren bron

Installer: The battle data installer can now extract full battle animations.

Iñigo Valentin 3 jaren geleden
bovenliggende
commit
345e66682b

+ 7 - 4
src/core/BattleManager.cpp

@@ -42,7 +42,9 @@ template<>BattleManager *Ogre::Singleton<BattleManager>::msSingleton = nullptr;
 ConfigVar cv_debug_battle_grid("debug_battle_grid", "Draw debug battle grid", "false");
 ConfigVar cv_debug_battle_axis("debug_battle_axis", "Draw debug battle axis", "false");
 
-const float BattleManager::MODEL_SCALE = 0.0032f;
+const float BattleManager::ENEMY_SCALE = 0.0032f;
+
+const float BattleManager::PARTY_SCALE = 0.0039f;
 
 const float BattleManager::SCENE_SCALE = 0.0012f;
 
@@ -172,8 +174,8 @@ void BattleManager::AddEnemy(
     enemies_.push_back(*enemy);
     EntityManager::getSingleton().AddEntity(
       enemy->GetName() + "_" + std::to_string(enemies_.size() - 1),
-      "enemies/" + enemy->GetModel() + ".mesh", enemy->GetPos(), Ogre::Degree(0),
-      Ogre::Vector3(MODEL_SCALE, MODEL_SCALE, MODEL_SCALE), Ogre::Quaternion::IDENTITY, id
+      "enemies/" + enemy->GetModel() + ".mesh", enemy->GetPos(), Ogre::Degree(1),
+      Ogre::Vector3(ENEMY_SCALE, ENEMY_SCALE, ENEMY_SCALE), Ogre::Quaternion(1, 1, 0, 0), id
     );
 }
 
@@ -331,7 +333,8 @@ void BattleManager::LoadParty(){
         }
         EntityManager::getSingleton().AddEntity(
           name, model, position, Ogre::Degree(0),
-          Ogre::Vector3(MODEL_SCALE, MODEL_SCALE, MODEL_SCALE), Ogre::Quaternion::IDENTITY, 100 + i
+          Ogre::Vector3(PARTY_SCALE, PARTY_SCALE, PARTY_SCALE),
+          Ogre::Quaternion(1, 1, 0, 0), 100 + i
         );
 
         // Next position in Y axis

+ 7 - 2
src/core/BattleManager.h

@@ -340,9 +340,14 @@ class BattleManager : public Manager, public Ogre::Singleton<BattleManager>{
         void UpdateWorld() override;
 
         /**
-         * Scale factor for battle character and enemy models.
+         * Scale factor for battle enemy models.
          */
-        static const float MODEL_SCALE;
+        static const float ENEMY_SCALE;
+
+        /**
+         * Scale factor for battle character models.
+         */
+        static const float PARTY_SCALE;
 
         /**
          * Scale factor for battle background models.

+ 2 - 0
src/core/CameraManager.cpp

@@ -199,6 +199,8 @@ void CameraManager::StartBattleCamera(
     //CameraManager::getSingleton().GetCurrentCamera()->setFixedYawAxis(true, Ogre::Vector3::UNIT_Y);
     camera_->lookAt(orientation);
     camera_->roll(Ogre::Angle(90));
+    // Fix: compensate for the battle UI covered section of the screen.
+    camera_->move(Ogre::Vector3(0, 0, -4));
 
     /*Ogre::SceneNode* rootScene =
               Ogre::Root::getSingleton().getSceneManager("Scene") ->getRootSceneNode();

+ 7 - 6
src/core/EntityManager.cpp

@@ -330,21 +330,22 @@ void EntityManager::AddFieldOrWorldMapEntity(
 
 void EntityManager::AddBattleEntity(
   const Ogre::String& name, const Ogre::String& file_name, const Ogre::Vector3& position,
-  const Ogre::Degree& orientation, const Ogre::Vector3& scale, const int index
+  const Ogre::Degree& rotation, const Ogre::Vector3& scale,
+  const Ogre::Quaternion& root_orientation, const int index
 ){
     if (module_ != Module::BATTLE){
         LOG_ERROR("Tried to add battle Entity but the EntityManager is not in battle mode.");
         return;
     }
     Ogre::SceneNode* node = scene_node_->createChildSceneNode("Battle_" + name);
+    // Battle models must be reoriented.
+    node->setOrientation(root_orientation);
     EntityModel* entity = new EntityModel(name, file_name, node);
     entity->SetPosition(position);
-    entity->SetRotation(orientation);
+    entity->SetRotation(rotation);
     entity->setScale(scale);
     entity->SetIndex(index);
     entity->SetVisible(true);
-    // Battle models must be reoriented.
-    node->setOrientation(1, 1, 0, 0);
     battle_entity_.push_back(entity);
     ScriptManager::getSingleton().AddEntity(ScriptManager::BATTLE, entity->GetName(), entity);
 
@@ -365,7 +366,7 @@ void EntityManager::AddBattleEntity(
      * where XX is the first two characters of file_name, without the path.
      */
     Ogre::String anim = file_name.substr(file_name.find_last_of("/\\") + 1).substr(0, 2) + "_00";
-    std::cout << "    Playing " << anim << std::endl;
+    //std::cout << "    Playing " << anim << std::endl;
     entity->ScriptSetDefaultAnimation(anim.c_str());
 
 
@@ -486,7 +487,7 @@ void EntityManager::AddEntity(
   const Ogre::Quaternion& root_orientation, const int index
 ){
     if (IsBattleModule())
-        AddBattleEntity(name, file_name, position, rotation, scale, index);
+        AddBattleEntity(name, file_name, position, rotation, scale, root_orientation, index);
     else
         AddFieldOrWorldMapEntity(
           name, file_name, position, rotation, scale, root_orientation, index

+ 3 - 2
src/core/EntityManager.h

@@ -467,14 +467,15 @@ class EntityManager : public Manager, public Ogre::Singleton<EntityManager>{
          * @param[in] name Entity name.
          * @param[in] file_name Path to the entity model file.
          * @param[in] position Entity position.
-         * @param[in] orientation Entity face direction.
+         * @param[in] rotation Entity face direction.
          * @param[in] scale Entity scale.
+         * @param[in] root_orientation The node orientation.
          * @param[in] index Index of the entity.
          */
         void AddBattleEntity(
           const Ogre::String& name, const Ogre::String& file_name,
           const Ogre::Vector3& position, const Ogre::Degree& orientation,
-          const Ogre::Vector3& scale, const int index
+          const Ogre::Vector3& scale, const Ogre::Quaternion& root_orientation, const int index
         );
 
         /**

+ 281 - 305
src/installer/data/DaFile.cpp

@@ -15,12 +15,244 @@
 
 #include <iostream>
 #include <bitset>
+#include <cstdint>
 #include "data/DaFile.h"
 
-DaFile::DaFile(File file){
+DaFile::DaFile(File file): da_file_(file){
+
+    //std::cout << "[DAFILE] " << file.GetFileName() << std::endl;
     animations_.clear();
+
+    // Save file contents as a byte array.
+    file.SetOffset(0);
+    da_bytes_ = new u8[file.GetFileSize()];
+    file.GetFileBuffer(da_bytes_, 0, file.GetFileSize());
+    bit_offset_ = 0;
     file.SetOffset(0);
-    Read(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)
+            );
+        }
+    }
+    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::ExtendSignInteger(int val, int len){
+    int result, aux;
+    if ((val & (int) pow(2, (len - 1))) != 0){
+        aux = (int) pow(2, 16) - 1;
+        aux ^= (int) (pow(2, len) - 1);
+        aux |= val;
+        result = aux;
+    }
+    else result = val;
+    return result;
+}
+
+int DaFile::GetSignExtendedShort(int val, int len){
+    int result = 0;
+    if (len > 0){
+        if (len < 16) result = ExtendSignInteger(val, len);
+        else result = val;
+    }
+    return result;
+}
+
+int DaFile::GetBitsS(int bits){
+    int tmp = GetBitsU(bits);
+    return GetSignExtendedShort(tmp, bits);
+}
+
+
+
+void DaFile::Read(){
+    int num_animations = da_file_.readU32LE();
+    bit_offset_ += 32;
+    for (int a = 0; a < num_animations; a ++){
+        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.
+        da_file_.SetOffset(start_offset + anim.size + 12);
+
+    }
+}
+
+DaFile::Animation DaFile::ReadAnimation(){
+    Animation anim;
+    // Read the animation headers.
+    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;
+    anim.key = da_file_.readU8();
+    bit_offset_ += 8;
+    Frame frame = ReadFirstFrame(anim.bone_count, anim.key);
+    anim.frames.push_back(frame);
+    for (int i = 1; i < anim.frame_count; i ++){
+        frame = ReadFrame(anim.bone_count, anim.key, frame);
+        anim.frames.push_back(frame);
+    }
+    return anim;
+}
+
+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);
+
+    // 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;
+
+    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;
+
+        // 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;
+        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;
+}
+
+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;
+
+    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
+        // 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;
+        bone.s_z = GetCompressedDeltaFromStream(key) + prev.bones[i].s_z;
+        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;
+        bone.f_x = static_cast<float>(bone.i_x) / pow(2, 12 - key) * 360.0f;
+        bone.f_y = static_cast<float>(bone.i_y) / pow(2, 12 - key) * 360.0f;
+        bone.f_z = static_cast<float>(bone.i_z) / pow(2, 12 - key) * 360.0f;
+        frame.bones.push_back(bone);
+    }
+    return frame;
 }
 
 std::vector<std::string> DaFile::GenerateAFiles(std::string model_id, std::string path){
@@ -33,11 +265,12 @@ std::vector<std::string> DaFile::GenerateAFiles(std::string model_id, std::strin
         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*>(&animations_[anim].bone_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);
+        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);
@@ -54,37 +287,23 @@ std::vector<std::string> DaFile::GenerateAFiles(std::string model_id, std::strin
              * 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 ++){
-                /*if (anim == 0 && b == 0)
-                    std::cout << "    FRAME " << f << " BONE 0: "
-                      << animations_[anim].frames[f].rotations[b].x << ", "
-                      << animations_[anim].frames[f].rotations[b].y << ", "
-                      << animations_[anim].frames[f].rotations[b].z << std::endl;*/
+            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].rotations[b].x), 4
+                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_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].bones[b].f_y), 4
                 );
-                a.write(reinterpret_cast<const char*>(
-                  &animations_[anim].frames[f].rotations[b].z), 4
+                a.write(
+                  reinterpret_cast<const char*>(&animations_[anim].frames[f].bones[b].f_z), 4
                 );
             }
         }
@@ -94,306 +313,63 @@ std::vector<std::string> DaFile::GenerateAFiles(std::string model_id, std::strin
     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 prev_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);
-            // Set relative rotations to the previous frame
-
-            // 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;
-                /*std::cout << "PASS " << f << ", " << b << ". prev is null? " <<
-                   (prev_frame.rotations == nullptr ? "YES" : "NO") << std::endl;
-                if (f > 0){
-                    std::cout << "    COMPENSATING START" << std::endl;
-                    rotation.x += prev_frame.rotations[b].f_x;
-                    rotation.y += prev_frame.rotations[b].f_y;
-                    rotation.z += prev_frame.rotations[b].f_z;
-                    std::cout << "    COMPENSATING END" << std::endl;
-                }*/
-                frm.rotations.push_back(rotation);
-            }
-            //prev_frame = frame;
-            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
-     */
-    //std::cout << "GetValueFromStream: ";
-    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;
-        //std::cout << " 17 bits: " << value << std::endl;
-    }
-    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;
-        //std::cout << " 8 bits: " << value << std::endl;
-    }
-
-    // Return the value.
+u16 DaFile::GetDynamicOffsetFromStream(){
+    u16 value;
+    int control_bit = GetBitsS(1);
+    if (control_bit == 1) value = GetBitsS(16);
+    else value = GetBitsS(7);
     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;
+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(u8* bytes, u32 &stream_bit_offset, int bits){
+int DaFile::GetBitsFromStream(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 ";
-
+    unsigned int byte_offset = bit_offset_ / 8;
+    unsigned int bit_offset = bit_offset_ % 8;
     for (int b = 0; b < bits; b ++){
-        u8 byte = bytes[byte_offset + (bit_offset + b) / 8];
+        u8 byte = da_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;
+        bit_offset_ += 1;
     }
     return value;
 }
 
-u16 DaFile::GetCompressedDeltaFromStream(
-  u8* bytes, u32& stream_bit_offset, int lowered_precision_bits
-){
+int DaFile::GetCompressedDeltaFromStream(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;
+    int first_bit = GetBitsU(1);
+    if (first_bit == 1){
+        unsigned int key = GetBitsU(3);// & 7;
+        int temp, sign, orig_temp;
+        unsigned int msb_mask;
         switch (key){
             case 0: // Return the smallest possible decrement delta (at given precision).
-                //std::cout << "  GCDFS 0: " << (-1 << lowered_precision_bits) << std::endl;
                 return (-1 << lowered_precision_bits);
             case 1: case 2: case 3: case 4: case 5: case 6:
                 bits = key;
                 // Read a corresponding number of bits from the stream.
-                temp = GetBitsFromStream(bytes, stream_bit_offset, bits);
-                //std::cout << "  GCDFS " << key << ": ";
-                // 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);
-                //std::cout << temp << " -> ";
-                // Adapt to the requested precision and return.
-                //std::cout << (temp << lowered_precision_bits) << std::endl;
-                return (temp << lowered_precision_bits);
+                temp = (int16_t) GetBitsS(bits);
+                //  Invert the value of the last bit
+                sign = (int) pow(2, bits - 1);
+
+                if (temp < 0) temp -= sign;
+                else temp += sign;
+                // Convert to 12-bits value
+                temp *= (int) pow(2, lowered_precision_bits);
+                return temp;
             case 7:
                 // Read an uncompressed value from the stream (at requested precision),
                 // and return.
-                temp = GetBitsFromStream(
-                  bytes, stream_bit_offset, 12 - lowered_precision_bits
-                );
-                //std::cout << "  GCDFS 7: " << temp << " -> " << (temp << lowered_precision_bits)
-                //  << std::endl;
+                temp = (int16_t) GetBitsS(12 - lowered_precision_bits);
                 return (temp << lowered_precision_bits);
-            //default:
-                //std::cout << "  GCDFS " << key << " UNKNOWN " << std::endl;
         }
     }
     // Default/error: return zero.
-    //std::cout << "  GCDFS 0 --"<< std::endl;
-    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; 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.
-
-            // TODO: This is NOT a fix. third parameter should be key, not 12 - key.
-            // The reason this looks better is because the rotations are now made so small they are
-            // imperceptible
-            x = GetCompressedDeltaFromStream(orig_animation_buffer, orig_bit_start, 12 - key);
-            y = GetCompressedDeltaFromStream(orig_animation_buffer, orig_bit_start, 12 - key);
-            z = GetCompressedDeltaFromStream(orig_animation_buffer, orig_bit_start, 12 - key);
-
-            frame->rotations[i].s_x += x;
-            frame->rotations[i].s_y += y;
-            frame->rotations[i].s_z += z;
-            //if (i == 0)
-            //    std::cout << "    ADDED ROTATION: " << frame->rotations[i].s_x << ", "
-            //      << frame->rotations[i].s_y << ", " << frame->rotations[i].s_z << std::endl;
-            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 == 0)
-            //    std::cout << "    PROCESSED ROTATION: " << frame->rotations[i].i_x << ", "
-            //      << frame->rotations[i].i_y << ", " << frame->rotations[i].i_z << std::endl;
-        }
-    }
-
-    // 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;
 }

+ 96 - 173
src/installer/data/DaFile.h

@@ -60,233 +60,136 @@ class DaFile{
     private:
 
         /**
-         * A bone rotation, with all it's values calculated.
+         * A three-coordinate value.
+         *
+         * Can be used to store bone rotations, offsets, translations... Each coordinate has short,
+         * integer and float formats.
          */
-        struct Rotation{
-
-            /**
-             * Rotation in the X axis, in degrees.
-             */
-            float x;
-
-            /**
-             * Rotation in the Y axis, in degrees.
-             */
-            float y;
+        struct TriValue{
 
             /**
-             * Rotation in the Z axis, in degrees.
+             * X value, short format.
              */
-            float z;
-        };
-
-        /**
-         * Processed frame data, to be written to .a files.
-         */
-        struct FrameData{
+            char16_t s_x;
 
             /**
-             * Position offset for the frame.
+             * Y value, short format.
              */
-            Rotation position_offset;
+            char16_t s_y;
 
             /**
-             * List of bone rotations, one per bone.
+             * Z value, short format
              */
-            std::vector<Rotation> rotations;
-        };
-
-        /**
-         * Processed animation data, to be written to.a files.
-         */
-        struct Animation{
+            char16_t s_z;
 
             /**
-             * Number of frames in the animation.
+             * X value, integer format.
              */
-            unsigned int frame_count;
+            int i_x;
 
             /**
-             * Number of bones involved in the animation.
+             * Y value, integer format.
              */
-            unsigned int bone_count;
+            int i_y;
 
             /**
-             * Each frame in the animation.
+             * Z value, integer format.
              */
-            std::vector<FrameData> frames;
-        };
-
-        /**
-         * Header for animations. 12 bytes, at the start of each animation.
-         */
-        struct AnimationHeader {
+            int i_z;
 
             /**
-             * 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.
+             * X value, float_format.
              */
-            u32 bone_count;
+            float f_x;
 
             /**
-             * Number of frames in the animation.
+             * Y value, float format.
              */
-            u32 frame_count;
+            float f_y;
 
             /**
-             * Size, in bytes, of the animation data.
+             * Z value, float format.
              */
-            u32 data_size;
+            float f_z;
         };
 
         /**
-         * Mini header for each animation. 5 bytes, immediately after each main header.
+         * Information for a frame.
          */
-        struct AnimationMiniHeader {
+        struct Frame{
 
             /**
-             * Number of frames in the animation.
-             *
-             * It may not match {@see AnimationHeader.frame_count}.
+             * Root offset.
              */
-            u16 frame_count;
+            TriValue offset;
 
             /**
-             * Size of the animation data.
-             *
-             * It should be 6 bytes less than {@see AnimationHeader.data_size}.
+             * Root rotation.
              */
-            u16 data_size;
+            TriValue rotation;
 
             /**
-             * 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.
+             * Each of the bone rotations.
              */
-            u8 key;
+            std::vector<TriValue> bones;
         };
 
-        /**
-         * 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;
+        struct Animation{
 
             /**
-             * Y axis rotation, converted to integer. Rotation between 0 and 4096.
+             * Number of bones in the animation.
              */
-            int i_y;
+            u32 bone_count;
 
             /**
-             * Z axis rotation, converted to integer. Rotation between 0 and 4096.
+             * Number of frames in the animation.
              */
-            int i_z;
+            u32 frame_count;
 
             /**
-             * X axis rotation, in degrees.
+             * Size of the animation data.
              */
-            float f_x;
+            u32 size;
 
             /**
-             * Y axis rotation, in degrees.
+             * Key for decoding values in an animation.
              */
-            float f_y;
+            u8 key;
 
             /**
-             * Z axis rotation, in degrees.
+             * List of frames in the animation.
              */
-            float f_z;
+            std::vector<Frame> frames;
         };
 
         /**
-         * A frame of an animation.
+         * Extends the sign for an integer value for a specified bit length.
          *
-         * Stores all bone rotations for a frame.
+         * @param[in] val The value to extend.
+         * @param[in] len Number of bytes  to extend the value to.
          */
-        struct Frame{
-
-            /**
-             * Number of bones in the animation frame.
-             */
-            u32 bone_count;
+        static int ExtendSignInteger(int val, int len);
 
-            /**
-             * Position offset.
-             *
-             * @todo Verify if it's the previous 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;
-            }
+        /**
+         * Extends the sign for a short value for a specified bit length.
+         *
+         * @param[in] val The value to extend.
+         * @param[in] len Number of bytes  to extend the value to.
+         */
+        static int GetSignExtendedShort(int val, int len);
 
-            /**
-             * Sets the number of bones and initializes data arrays.
-             *
-             * @param[in] bones The number of bones.
-             */
-            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);
+        void Read();
+
+        Animation ReadAnimation();
+
+        Frame ReadFirstFrame(u32 bones, u8 key);
+
+        Frame ReadFrame(u32 bones, u8 key, Frame prev);
 
         /**
          * Retrieves a value of dynamic size from the byte stream.
@@ -295,12 +198,9 @@ class DaFile{
          * 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);
+        u16 GetDynamicOffsetFromStream();
 
         /**
          * Reads 2 bytes from the stream as a little endian value.
@@ -310,31 +210,39 @@ class DaFile{
          * by 16.
          * @return The read value.
          */
-        u16 ReadU16LE(u8* bytes, u32 &stream_bit_offset);
+        u16 ReadU16LE();
+
+        /**
+         * Read an arbitrary number of bits from the da contents as unsigned.
+         *
+         * @param[in] bits Number of bits.
+         * @return Unsigned value.
+         */
+        int GetBitsU(int bits);
+
+        /**
+         * Read an arbitrary number of bits from the da contents as signed.
+         *
+         * @param[in] bits Number of bits.
+         * @return Signed value.
+         */
+        int GetBitsS(int bits);
 
         /**
          * 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.
          * @param[in] bits Number of bits to read.
          * @return The read value.
          */
-        int GetBitsFromStream(u8* bytes, u32 &stream_bit_offset, int bits);
+        int GetBitsFromStream(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 lowered_precision_bits How many bits to sift the read value.
+         * @param lowered_precision_bits How many bits to sift the read value. The animation key.
          * @return The decoded value.
          */
-        u16 GetCompressedDeltaFromStream(
-          u8* bytes, u32& stream_bit_offset, int lowered_precision_bits
-        );
+        int GetCompressedDeltaFromStream(int lowered_precision_bits);
 
         /**
          * Loads the frames in an animation.
@@ -346,9 +254,24 @@ class DaFile{
          */
         u32 LoadFrames(Frame* frame, int bones, int bit_start, u8* animation_buffer);
 
+        /**
+         * The da file.
+         */
+        File da_file_;
+
+        /**
+         * Contents of the da file as a byte array.
+         */
+        u8* da_bytes_;
+
         /**
          * List of animations.
          */
         std::vector<Animation> animations_;
 
+        /**
+         * Number of bits already read from the da file.
+         */
+        u32 bit_offset_;
+
 };