- #include <cstdint>
- #include <vector>
- #include <iostream>
- #include <cstdio>
- template<typename T>
- struct M2Array
- {
- uint32_t number;
- uint32_t offset_elements;
- T* get (void* data) const
- {
- return (T*)((char*)data + offset_elements);
- }
- };
- template<typename T>
- struct M2Track
- {
- uint16_t interpolation_type;
- uint16_t global_sequence;
- M2Array<M2Array<uint32_t>> timestamps;
- M2Array<M2Array<T>> values;
- };
- struct C3Vector
- {
- float x;
- float y;
- float z;
- C3Vector (float _x, float _y, float _z)
- : x (_x), y (_y), z (_z)
- {}
- };
- struct M2CompQuat
- {
- uint16_t val[4]; // maps to C4Quaternion. See Quaternion values and 2.x.
- };
- struct M2CompBone
- {
- int32_t key_bone_id; // Back-reference to the key bone lookup table. -1 if this is no key bone.
- enum
- {
- spherical_billboard = 0x8,
- cylindrical_billboard = 0x40,
- transformed = 0x200,
- kinematic_bone = 0x400, // MoP+: allow physics to influence this bone
- helmet_anim_scaled = 0x1000, // set blend_modificator to helmetAnimScalingRec.m_amount for this bone
- };
- uint32_t flags;
- int16_t parent_bone; // Parent bone ID or -1 if there is none.
- uint16_t submesh_id; // Mesh part ID
- uint16_t _unknown[2];
- M2Track<C3Vector> translation;
- M2Track<M2CompQuat> rotation; // compressed values, see Quaternion values and 2.x. Default is (32767,32767,32767,65535) == (0,0,0,1) == identity
- M2Track<C3Vector> scale;
- C3Vector pivot; // The pivot point of that bone.
- };
- template<typename T>
- struct m2_array_iter
- {
- M2Array<T>& _array;
- void* _data;
- m2_array_iter (M2Array<T>& array, void* data)
- : _array (array)
- , _data (data)
- {}
- using value_type = T&;
- T* begin() const { return _array.get (_data); }
- T* end() const { return _array.get (_data) + _array.number; }
- };
- template<typename T>
- m2_array_iter<T> iter (M2Array<T>& array, void* data)
- {
- return m2_array_iter<T> (array, data);
- }
- struct m2_header_partially
- {
- uint32_t magic;
- uint32_t version;
- M2Array<char> name;
- uint32_t global_flags;
- M2Array<uint32_t> global_sequences;
- M2Array<void> animations;
- M2Array<void> animation_lookup;
- M2Array<M2CompBone> bones;
- // ...
- };
- namespace key_bone { enum
- {
- ArmL,
- ArmR,
- ShoulderL,
- ShoulderR,
- SpineLow,
- Waist,
- Head,
- Jaw,
- IndexFingerR,
- MiddleFingerR,
- PinkyFingerR,
- RingFingerR,
- ThumbR,
- IndexFingerL,
- MiddleFingerL,
- PinkyFingerL,
- RingFingerL,
- ThumbL,
- BTH,
- CSR,
- CSL,
- _Breath,
- _Name,
- _NameMount,
- CHD,
- CCH,
- Root,
- Wheel1,
- Wheel2,
- Wheel3,
- Wheel4,
- Wheel5,
- Wheel6,
- Wheel7,
- Wheel8,
- };}
- struct IffChunk
- {
- uint32_t magic;
- uint32_t size;
- char data[];
- };
- struct fake_animation_block_data_with_one_c3vector
- {
- uint32_t timestamp;
- C3Vector value;
- M2Array<uint32_t> timestamps;
- M2Array<C3Vector> values;
- fake_animation_block_data_with_one_c3vector (float scale_factor, uint32_t base_offset, uint32_t* timestamps_offset, uint32_t* values_offset)
- : timestamp (0)
- , value (scale_factor, scale_factor, scale_factor)
- {
- timestamps.number = 1;
- timestamps.offset_elements = base_offset + offsetof (fake_animation_block_data_with_one_c3vector, timestamp);
- values.number = 1;
- values.offset_elements = base_offset + offsetof (fake_animation_block_data_with_one_c3vector, value);
- *timestamps_offset = base_offset + offsetof (fake_animation_block_data_with_one_c3vector, timestamps);
- *values_offset = base_offset + offsetof (fake_animation_block_data_with_one_c3vector, values);
- }
- };
- namespace
- {
- void* find_base_file (void* data)
- {
- IffChunk* chunk ((IffChunk*)data);
- if (chunk->magic == '02DM')
- {
- return chunk;
- }
- else
- {
- while (chunk->magic != '12DM')
- {
- chunk = (IffChunk*)(chunk->data + chunk->size);
- }
- return chunk->data;
- }
- }
- }
- namespace sys
- {
- FILE* fopen (char const* name, char const* mode)
- {
- auto r (::fopen (name, mode));
- if (!r) { throw std::runtime_error ("fopen (" + std::string (name) + ")"); }
- return r;
- }
- void fclose (FILE* file)
- {
- if (::fclose (file)) throw std::runtime_error ("fclose");
- }
- void fread (void* ptr, size_t size, size_t count, FILE* file)
- {
- auto r (::fread (ptr, size, count, file));
- if (r != count) { throw std::runtime_error ("fread"); }
- }
- void fwrite (void* ptr, size_t size, size_t count, FILE* file)
- {
- auto r (::fwrite (ptr, size, count, file));
- if (r != count) { throw std::runtime_error ("fwrite"); }
- }
- void fseek (FILE* file, size_t offset, int mode)
- {
- if (::fseek (file, offset, mode)) throw std::runtime_error ("fseek");
- }
- }
- int main (int argc, char** argv)
- {
- bool is_legion (true);
- float scale_factor (2.0f);
- for (size_t i (1); i < argc; ++i)
- try
- {
- FILE* file (sys::fopen (argv[i], "rb+"));
- sys::fseek (file, 0, SEEK_END);
- std::size_t file_size (ftell (file));
- std::vector<char> file_data (file_size);
- sys::fseek (file, 0, SEEK_SET);
- sys::fread (file_data.data(), file_data.size(), 1, file);
- sys::fclose (file);
- uint32_t fake_timestamps_offset;
- uint32_t fake_values_offset;
- {
- uint32_t fake_animation_block_data_with_one_c3vector_offset_in_file (0);
- uint32_t fake_animation_block_data_with_one_c3vector_offset_in_base_file (0);
- IffChunk* chunk ((IffChunk*)file_data.data());
- if (chunk->magic == '02DM')
- {
- fake_animation_block_data_with_one_c3vector_offset_in_file = file_size;
- fake_animation_block_data_with_one_c3vector_offset_in_base_file = file_size;
- }
- else
- {
- while (chunk->magic != '12DM')
- {
- chunk = (IffChunk*)(chunk->data + chunk->size);
- }
- fake_animation_block_data_with_one_c3vector_offset_in_file = chunk->data + chunk->size - (char*)(file_data.data());
- fake_animation_block_data_with_one_c3vector_offset_in_base_file = chunk->size;
- chunk->size += sizeof (fake_animation_block_data_with_one_c3vector);
- }
- file_data.insert ( file_data.begin() + fake_animation_block_data_with_one_c3vector_offset_in_file
- , sizeof (fake_animation_block_data_with_one_c3vector)
- , 0
- );
- new (file_data.data() + fake_animation_block_data_with_one_c3vector_offset_in_file) fake_animation_block_data_with_one_c3vector
- ( scale_factor
- , fake_animation_block_data_with_one_c3vector_offset_in_base_file
- , &fake_timestamps_offset
- , &fake_values_offset
- );
- }
- void* const data (find_base_file (file_data.data()));
- m2_header_partially* header ((m2_header_partially*)data);
- for (M2CompBone& bone : iter (header->bones, data))
- {
- if (bone.key_bone_id == key_bone::Head)
- {
- if (bone.scale.values.number != 0)
- {
- throw std::runtime_error ("appears broken, thus disabled");
- for (auto& values : iter (bone.scale.values, data))
- {
- for (C3Vector& value : iter (values, data))
- {
- value.x *= scale_factor;
- value.y *= scale_factor;
- value.z *= scale_factor;
- }
- }
- }
- else
- {
- bone.scale.timestamps.number = 1;
- bone.scale.timestamps.offset_elements = fake_timestamps_offset;
- bone.scale.values.number = 1;
- bone.scale.values.offset_elements = fake_values_offset;
- }
- }
- }
- file = sys::fopen (argv[i], "w+");
- sys::fwrite (file_data.data(), file_data.size(), 1, file);
- sys::fclose (file);
- }
- catch (std::exception const& ex)
- {
- std::cerr << argv[i] << ": " << ex.what() << std::endl;
- }
- return 0;
- }