1. #include <cstdint>
  2. #include <vector>
  3. #include <iostream>
  4. #include <cstdio>
  5. template<typename T>
  6. struct M2Array
  7. {
  8. uint32_t number;
  9. uint32_t offset_elements;
  10. T* get (void* data) const
  11. {
  12. return (T*)((char*)data + offset_elements);
  13. }
  14. };
  15. template<typename T>
  16. struct M2Track
  17. {
  18. uint16_t interpolation_type;
  19. uint16_t global_sequence;
  20. M2Array<M2Array<uint32_t>> timestamps;
  21. M2Array<M2Array<T>> values;
  22. };
  23. struct C3Vector
  24. {
  25. float x;
  26. float y;
  27. float z;
  28. C3Vector (float _x, float _y, float _z)
  29. : x (_x), y (_y), z (_z)
  30. {}
  31. };
  32. struct M2CompQuat
  33. {
  34. uint16_t val[4]; // maps to C4Quaternion. See Quaternion values and 2.x.
  35. };
  36. struct M2CompBone
  37. {
  38. int32_t key_bone_id; // Back-reference to the key bone lookup table. -1 if this is no key bone.
  39. enum
  40. {
  41. spherical_billboard = 0x8,
  42. cylindrical_billboard = 0x40,
  43. transformed = 0x200,
  44. kinematic_bone = 0x400, // MoP+: allow physics to influence this bone
  45. helmet_anim_scaled = 0x1000, // set blend_modificator to helmetAnimScalingRec.m_amount for this bone
  46. };
  47. uint32_t flags;
  48. int16_t parent_bone; // Parent bone ID or -1 if there is none.
  49. uint16_t submesh_id; // Mesh part ID
  50. uint16_t _unknown[2];
  51. M2Track<C3Vector> translation;
  52. M2Track<M2CompQuat> rotation; // compressed values, see Quaternion values and 2.x. Default is (32767,32767,32767,65535) == (0,0,0,1) == identity
  53. M2Track<C3Vector> scale;
  54. C3Vector pivot; // The pivot point of that bone.
  55. };
  56. template<typename T>
  57. struct m2_array_iter
  58. {
  59. M2Array<T>& _array;
  60. void* _data;
  61. m2_array_iter (M2Array<T>& array, void* data)
  62. : _array (array)
  63. , _data (data)
  64. {}
  65. using value_type = T&;
  66. T* begin() const { return _array.get (_data); }
  67. T* end() const { return _array.get (_data) + _array.number; }
  68. };
  69. template<typename T>
  70. m2_array_iter<T> iter (M2Array<T>& array, void* data)
  71. {
  72. return m2_array_iter<T> (array, data);
  73. }
  74. struct m2_header_partially
  75. {
  76. uint32_t magic;
  77. uint32_t version;
  78. M2Array<char> name;
  79. uint32_t global_flags;
  80. M2Array<uint32_t> global_sequences;
  81. M2Array<void> animations;
  82. M2Array<void> animation_lookup;
  83. M2Array<M2CompBone> bones;
  84. // ...
  85. };
  86. namespace key_bone { enum
  87. {
  88. ArmL,
  89. ArmR,
  90. ShoulderL,
  91. ShoulderR,
  92. SpineLow,
  93. Waist,
  94. Head,
  95. Jaw,
  96. IndexFingerR,
  97. MiddleFingerR,
  98. PinkyFingerR,
  99. RingFingerR,
  100. ThumbR,
  101. IndexFingerL,
  102. MiddleFingerL,
  103. PinkyFingerL,
  104. RingFingerL,
  105. ThumbL,
  106. BTH,
  107. CSR,
  108. CSL,
  109. _Breath,
  110. _Name,
  111. _NameMount,
  112. CHD,
  113. CCH,
  114. Root,
  115. Wheel1,
  116. Wheel2,
  117. Wheel3,
  118. Wheel4,
  119. Wheel5,
  120. Wheel6,
  121. Wheel7,
  122. Wheel8,
  123. };}
  124. struct IffChunk
  125. {
  126. uint32_t magic;
  127. uint32_t size;
  128. char data[];
  129. };
  130. struct fake_animation_block_data_with_one_c3vector
  131. {
  132. uint32_t timestamp;
  133. C3Vector value;
  134. M2Array<uint32_t> timestamps;
  135. M2Array<C3Vector> values;
  136. fake_animation_block_data_with_one_c3vector (float scale_factor, uint32_t base_offset, uint32_t* timestamps_offset, uint32_t* values_offset)
  137. : timestamp (0)
  138. , value (scale_factor, scale_factor, scale_factor)
  139. {
  140. timestamps.number = 1;
  141. timestamps.offset_elements = base_offset + offsetof (fake_animation_block_data_with_one_c3vector, timestamp);
  142. values.number = 1;
  143. values.offset_elements = base_offset + offsetof (fake_animation_block_data_with_one_c3vector, value);
  144. *timestamps_offset = base_offset + offsetof (fake_animation_block_data_with_one_c3vector, timestamps);
  145. *values_offset = base_offset + offsetof (fake_animation_block_data_with_one_c3vector, values);
  146. }
  147. };
  148. namespace
  149. {
  150. void* find_base_file (void* data)
  151. {
  152. IffChunk* chunk ((IffChunk*)data);
  153. if (chunk->magic == '02DM')
  154. {
  155. return chunk;
  156. }
  157. else
  158. {
  159. while (chunk->magic != '12DM')
  160. {
  161. chunk = (IffChunk*)(chunk->data + chunk->size);
  162. }
  163. return chunk->data;
  164. }
  165. }
  166. }
  167. namespace sys
  168. {
  169. FILE* fopen (char const* name, char const* mode)
  170. {
  171. auto r (::fopen (name, mode));
  172. if (!r) { throw std::runtime_error ("fopen (" + std::string (name) + ")"); }
  173. return r;
  174. }
  175. void fclose (FILE* file)
  176. {
  177. if (::fclose (file)) throw std::runtime_error ("fclose");
  178. }
  179. void fread (void* ptr, size_t size, size_t count, FILE* file)
  180. {
  181. auto r (::fread (ptr, size, count, file));
  182. if (r != count) { throw std::runtime_error ("fread"); }
  183. }
  184. void fwrite (void* ptr, size_t size, size_t count, FILE* file)
  185. {
  186. auto r (::fwrite (ptr, size, count, file));
  187. if (r != count) { throw std::runtime_error ("fwrite"); }
  188. }
  189. void fseek (FILE* file, size_t offset, int mode)
  190. {
  191. if (::fseek (file, offset, mode)) throw std::runtime_error ("fseek");
  192. }
  193. }
  194. int main (int argc, char** argv)
  195. {
  196. bool is_legion (true);
  197. float scale_factor (2.0f);
  198. for (size_t i (1); i < argc; ++i)
  199. try
  200. {
  201. FILE* file (sys::fopen (argv[i], "rb+"));
  202. sys::fseek (file, 0, SEEK_END);
  203. std::size_t file_size (ftell (file));
  204. std::vector<char> file_data (file_size);
  205. sys::fseek (file, 0, SEEK_SET);
  206. sys::fread (file_data.data(), file_data.size(), 1, file);
  207. sys::fclose (file);
  208. uint32_t fake_timestamps_offset;
  209. uint32_t fake_values_offset;
  210. {
  211. uint32_t fake_animation_block_data_with_one_c3vector_offset_in_file (0);
  212. uint32_t fake_animation_block_data_with_one_c3vector_offset_in_base_file (0);
  213. IffChunk* chunk ((IffChunk*)file_data.data());
  214. if (chunk->magic == '02DM')
  215. {
  216. fake_animation_block_data_with_one_c3vector_offset_in_file = file_size;
  217. fake_animation_block_data_with_one_c3vector_offset_in_base_file = file_size;
  218. }
  219. else
  220. {
  221. while (chunk->magic != '12DM')
  222. {
  223. chunk = (IffChunk*)(chunk->data + chunk->size);
  224. }
  225. fake_animation_block_data_with_one_c3vector_offset_in_file = chunk->data + chunk->size - (char*)(file_data.data());
  226. fake_animation_block_data_with_one_c3vector_offset_in_base_file = chunk->size;
  227. chunk->size += sizeof (fake_animation_block_data_with_one_c3vector);
  228. }
  229. file_data.insert ( file_data.begin() + fake_animation_block_data_with_one_c3vector_offset_in_file
  230. , sizeof (fake_animation_block_data_with_one_c3vector)
  231. , 0
  232. );
  233. new (file_data.data() + fake_animation_block_data_with_one_c3vector_offset_in_file) fake_animation_block_data_with_one_c3vector
  234. ( scale_factor
  235. , fake_animation_block_data_with_one_c3vector_offset_in_base_file
  236. , &fake_timestamps_offset
  237. , &fake_values_offset
  238. );
  239. }
  240. void* const data (find_base_file (file_data.data()));
  241. m2_header_partially* header ((m2_header_partially*)data);
  242. for (M2CompBone& bone : iter (header->bones, data))
  243. {
  244. if (bone.key_bone_id == key_bone::Head)
  245. {
  246. if (bone.scale.values.number != 0)
  247. {
  248. throw std::runtime_error ("appears broken, thus disabled");
  249. for (auto& values : iter (bone.scale.values, data))
  250. {
  251. for (C3Vector& value : iter (values, data))
  252. {
  253. value.x *= scale_factor;
  254. value.y *= scale_factor;
  255. value.z *= scale_factor;
  256. }
  257. }
  258. }
  259. else
  260. {
  261. bone.scale.timestamps.number = 1;
  262. bone.scale.timestamps.offset_elements = fake_timestamps_offset;
  263. bone.scale.values.number = 1;
  264. bone.scale.values.offset_elements = fake_values_offset;
  265. }
  266. }
  267. }
  268. file = sys::fopen (argv[i], "w+");
  269. sys::fwrite (file_data.data(), file_data.size(), 1, file);
  270. sys::fclose (file);
  271. }
  272. catch (std::exception const& ex)
  273. {
  274. std::cerr << argv[i] << ": " << ex.what() << std::endl;
  275. }
  276. return 0;
  277. }