mix_file.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /*
  2. XCC Utilities and Library
  3. Copyright (C) 2000 Olaf van der Spek <[email protected]>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. #include <stdafx.h>
  16. #include "mix_file.h"
  17. #ifndef NO_FT_SUPPORT
  18. #include "big_file.h"
  19. #endif
  20. #include "blowfish.h"
  21. #include "crc.h"
  22. #include "id_log.h"
  23. #ifndef NO_FT_SUPPORT
  24. #include "mix_cache.h"
  25. #endif
  26. #include "mix_decode.h"
  27. #ifndef NO_FT_SUPPORT
  28. #include "mix_rg_file.h"
  29. #include "pak_file.h"
  30. #endif
  31. #include "string_conversion.h"
  32. #include "xcc_lmd_file.h"
  33. bool Cmix_file::m_ft_support = false;
  34. Cmix_file::Cmix_file() :
  35. Ccc_file(false)
  36. {
  37. }
  38. bool Cmix_file::is_valid()
  39. {
  40. const byte* data = get_data();
  41. if (!data)
  42. return false;
  43. const t_mix_header& header = *reinterpret_cast<const t_mix_header*>(data);
  44. int size = get_size();
  45. if (sizeof(t_mix_header) > size)
  46. return false;
  47. if (header.c_files && sizeof(t_mix_header) + header.c_files * sizeof(t_mix_index_entry) + header.size == size)
  48. return true;
  49. if (header.flags & ~(mix_encrypted | mix_checksum))
  50. return false;
  51. m_has_checksum = header.flags & mix_checksum;
  52. m_is_encrypted = header.flags & mix_encrypted;
  53. if (m_is_encrypted)
  54. {
  55. Cblowfish bf;
  56. std::array<byte, cb_mix_key> key;
  57. get_blowfish_key(data + 4, key);
  58. bf.set_key(key);
  59. byte e[8];
  60. bf.decipher(data + 84, e, 8);
  61. t_mix_header* header = reinterpret_cast<t_mix_header*>(e);
  62. if (!header->c_files || 84 + (sizeof(t_mix_header) + header->c_files * sizeof(t_mix_index_entry) + 7 & ~7) + header->size + (m_has_checksum ? 20 : 0) != size)
  63. return false;
  64. }
  65. else
  66. {
  67. const t_mix_header* header = reinterpret_cast<const t_mix_header*>(data + 4);
  68. if (!header->c_files || 4 + sizeof(t_mix_header) + header->c_files * sizeof(t_mix_index_entry) + header->size + (m_has_checksum ? 20 : 0) != size)
  69. return false;
  70. }
  71. return true;
  72. }
  73. #define test_fail(res) { int v = res; if (v) { close(); return v; }}
  74. int Cmix_file::post_open()
  75. {
  76. #ifndef NO_FT_SUPPORT
  77. bool index_read = false;
  78. if (h())
  79. {
  80. Cmix_rg_file f;
  81. if (get_data())
  82. f.load(vdata());
  83. else
  84. f.open(h());
  85. if (f.is_open() && f.is_valid())
  86. {
  87. m_game = game_rg;
  88. m_is_encrypted = m_has_checksum = false;
  89. int c_files = f.get_c_files();
  90. m_index.resize(c_files);
  91. for (int i = 0; i < c_files; i++)
  92. {
  93. string name = f.get_name(i);
  94. mix_database::add_name(m_game, name, "-");
  95. m_index[i] = t_mix_index_entry(get_id(get_game(), name), f.get_offset(name), f.get_size(name));
  96. }
  97. index_read = true;
  98. }
  99. else
  100. {
  101. Cbig_file f;
  102. if (get_data())
  103. f.load(vdata());
  104. else
  105. f.open(h());
  106. if (f.is_open() && f.is_valid())
  107. {
  108. m_game = game_gr;
  109. m_is_encrypted = m_has_checksum = false;
  110. int c_files = f.get_c_files();
  111. m_index.resize(c_files);
  112. for (int i = 0; i < c_files; i++)
  113. {
  114. string name = f.get_name(i);
  115. mix_database::add_name(m_game, name, "-");
  116. m_index[i] = t_mix_index_entry(get_id(get_game(), name), f.get_offset(name), f.get_size(name));
  117. }
  118. index_read = true;
  119. }
  120. }
  121. }
  122. if (!index_read)
  123. {
  124. Cpak_file f;
  125. if (get_data())
  126. f.load(vdata());
  127. else
  128. {
  129. int size = min<int>(get_size(), 64 << 10);
  130. Cvirtual_binary data;
  131. test_fail(read(data.write_start(size), size));
  132. f.load(data, get_size());
  133. }
  134. if (f.is_valid())
  135. {
  136. m_game = game_dune2;
  137. m_is_encrypted = m_has_checksum = false;
  138. int c_files = f.get_c_files();
  139. if (c_files >> 12)
  140. test_fail(1);
  141. m_index.resize(c_files);
  142. for (int i = 0; i < c_files; i++)
  143. {
  144. string name = f.get_name(i);
  145. mix_database::add_name(m_game, name, "-");
  146. m_index[i] = t_mix_index_entry(get_id(get_game(), name), f.get_offset(name), f.get_size(name));
  147. }
  148. index_read = true;
  149. }
  150. }
  151. if (!index_read)
  152. #endif
  153. {
  154. t_mix_header header;
  155. seek(0);
  156. test_fail(read(&header, sizeof(t_mix_header)));
  157. if (header.c_files)
  158. {
  159. m_game = game_td;
  160. m_is_encrypted = m_has_checksum = false;
  161. int c_files = header.c_files;
  162. int cb_index = c_files * sizeof(t_mix_index_entry);
  163. if (header.c_files >> 12 || get_size() != 6 + cb_index + header.size)
  164. test_fail(1);
  165. m_index.resize(c_files);
  166. test_fail(read(&m_index[0], cb_index));
  167. for (int i = 0; i < c_files; i++)
  168. m_index[i].offset += 6 + cb_index;
  169. }
  170. else
  171. {
  172. m_has_checksum = header.flags & mix_checksum;
  173. m_is_encrypted = header.flags & mix_encrypted;
  174. bool aligned = true;
  175. Cblowfish bf;
  176. seek(4);
  177. if (m_is_encrypted)
  178. {
  179. byte key_source[cb_mix_key_source];
  180. read(key_source, cb_mix_key_source);
  181. std::array<byte, cb_mix_key> key;
  182. get_blowfish_key(key_source, key);
  183. bf.set_key(key);
  184. byte e[8];
  185. read(e, 8);
  186. bf.decipher(e, e, 8);
  187. memcpy(&header, e, sizeof(t_mix_header));
  188. int c_files = header.c_files;
  189. const int cb_index = c_files * sizeof(t_mix_index_entry);
  190. const int cb_f = cb_index + 5 & ~7;
  191. if (get_size() != 92 + cb_f + header.size + (m_has_checksum ? 20 : 0))
  192. test_fail(1);
  193. if (c_files)
  194. {
  195. Cvirtual_binary f;
  196. read(f.write_start(cb_f), cb_f);
  197. bf.decipher(f.data_edit(), f.data_edit(), cb_f);
  198. m_index.resize(c_files);
  199. memcpy(&m_index[0], e + 6, 2);
  200. memcpy(reinterpret_cast<byte*>(&m_index[0]) + 2, f.data(), cb_index - 2);
  201. for (int i = 0; i < c_files; i++)
  202. {
  203. if (m_index[i].offset & 0xf)
  204. aligned = false;
  205. m_index[i].offset += 92 + cb_f;
  206. }
  207. }
  208. }
  209. else
  210. {
  211. read(&header, sizeof(header));
  212. int c_files = header.c_files;
  213. const int cb_index = c_files * sizeof(t_mix_index_entry);
  214. if (get_size() != 4 + sizeof(t_mix_header) + cb_index + header.size + (m_has_checksum ? 20 : 0))
  215. test_fail(1);
  216. m_index.resize(c_files);
  217. read(&m_index[0], cb_index);
  218. for (int i = 0; i < c_files; i++)
  219. {
  220. if (m_index[i].offset & 0xf)
  221. aligned = false;
  222. m_index[i].offset += 4 + sizeof(t_mix_header) + cb_index;
  223. }
  224. }
  225. m_game = is_encrypted() ? game_ra : game_ts;
  226. for (int i = 0; i < get_c_files(); i++)
  227. {
  228. if (get_id(i) == 0x763c81dd)
  229. {
  230. m_game = game_ts;
  231. break;
  232. }
  233. }
  234. if (m_game == game_ra && aligned)
  235. m_game = game_ts;
  236. }
  237. }
  238. for (int i = 0; i < get_c_files(); i++)
  239. m_id_index[get_id(i)] = i;
  240. #ifndef NO_FT_SUPPORT
  241. if (m_ft_support)
  242. {
  243. switch (m_game)
  244. {
  245. case game_dune2:
  246. case game_rg:
  247. break;
  248. default:
  249. int count[game_unknown] = {0};
  250. for (int i = 0; i < get_c_files(); i++)
  251. {
  252. int id = get_id(i);
  253. for (int game = game_td; game < game_unknown; game++)
  254. count[game] += mix_database::get_name(static_cast<t_game>(game), id).empty();
  255. }
  256. int min = count[0];
  257. for (int game = 0; game < game_unknown; game++)
  258. {
  259. if (count[game] < min)
  260. {
  261. m_game = static_cast<t_game>(game);
  262. min = count[game];
  263. }
  264. }
  265. }
  266. if (vdata().size() == get_size())
  267. {
  268. int crc = compute_crc(&m_index[0], get_c_files() * sizeof(t_mix_index_entry));
  269. Cvirtual_binary s = mix_cache::get_data(crc);
  270. m_index_ft.resize(get_c_files());
  271. if (s.size() == get_c_files() * sizeof(t_file_type))
  272. memcpy(&m_index_ft[0], s.data(), get_c_files() * sizeof(t_file_type));
  273. else
  274. {
  275. using t_block_map = multimap<int, int>;
  276. t_block_map block_map;
  277. for (int i = 0; i < get_c_files(); i++)
  278. block_map.insert(t_block_map::value_type(get_offset(get_id(i)), i));
  279. for (auto& i : block_map)
  280. {
  281. Ccc_file f(false);
  282. f.open(get_id(i.second), *this);
  283. m_index_ft[i.second] = f.get_file_type();
  284. }
  285. mix_cache::set_data(crc, Cvirtual_binary(&m_index_ft[0], get_c_files() * sizeof(t_file_type)));
  286. }
  287. for (int i = 0; i < get_c_files(); i++)
  288. {
  289. int id = get_id(i);
  290. Cxcc_lmd_file f;
  291. if (get_type(id) != ft_xcc_lmd || f.open(id, *this) || !f.is_valid())
  292. continue;
  293. m_game = f.get_game();
  294. int count = f.get_c_fnames();
  295. const char* r = f.get_fnames();
  296. while (count--)
  297. {
  298. string name = r;
  299. r += name.length() + 1;
  300. mix_database::add_name(m_game, name, "-");
  301. }
  302. }
  303. }
  304. }
  305. #endif
  306. if (m_mix_expansion)
  307. {
  308. int c_files = get_c_files();
  309. for (int i = 0; i < c_files; i++)
  310. {
  311. if (get_type(m_index[i].id) != ft_mix)
  312. continue;
  313. Cmix_file f;
  314. f.open(m_index[i].id, *this);
  315. int c_files = get_c_files();
  316. int new_c_files = f.get_c_files();
  317. m_index.resize(c_files + new_c_files);
  318. for (int j = 0; j < new_c_files; j++)
  319. {
  320. int id = f.get_id(j);
  321. m_index[c_files + j] = t_mix_index_entry(id, f.get_offset(id) + get_offset(m_index[i].id), f.get_size(id));
  322. m_id_index[id] = c_files + j;
  323. }
  324. m_index_ft.resize(c_files + new_c_files);
  325. memcpy(&m_index_ft[c_files], &f.m_index_ft[0], new_c_files * sizeof(t_file_type));
  326. }
  327. }
  328. return 0;
  329. }
  330. void Cmix_file::close()
  331. {
  332. m_id_index.clear();
  333. m_index_ft.clear();
  334. m_index.clear();
  335. Ccc_file::close();
  336. }
  337. string Cmix_file::get_name(int id)
  338. {
  339. #ifdef NO_FT_SUPPORT
  340. return "";
  341. #else
  342. return mix_database::get_name(get_game(), id);
  343. #endif
  344. }
  345. int Cmix_file::get_id(t_game game, string name)
  346. {
  347. boost::to_upper(name);
  348. std::replace(name.begin(), name.end(), '/', '\\');
  349. switch (game)
  350. {
  351. case game_ts:
  352. case game_ra2:
  353. case game_ra2_yr:
  354. {
  355. const int l = name.length();
  356. int a = l & ~3;
  357. if (l & 3)
  358. {
  359. name += static_cast<char>(l - a);
  360. name.append(3 - (l & 3), name[a]);
  361. }
  362. return compute_crc(name.c_str(), name.length());
  363. }
  364. case game_rg:
  365. case game_gr:
  366. case game_gr_zh:
  367. return compute_crc(name.c_str(), name.length());
  368. default:
  369. int i = 0;
  370. unsigned int id = 0;
  371. int l = name.length();
  372. while (i < l)
  373. {
  374. unsigned int a = 0;
  375. for (int j = 0; j < 4; j++)
  376. {
  377. a >>= 8;
  378. if (i < l)
  379. a |= static_cast<unsigned int>(name[i++]) << 24;
  380. }
  381. id = (id << 1 | id >> 31) + a;
  382. }
  383. return id;
  384. }
  385. }
  386. int Cmix_file::get_index(unsigned int id) const
  387. {
  388. auto i = find_ptr(m_id_index, id);
  389. return i ? *i : -1;
  390. }
  391. Cvirtual_binary Cmix_file::get_vdata(int id)
  392. {
  393. if (get_index(id) == -1)
  394. return Cvirtual_binary();
  395. if (get_data())
  396. return vdata().sub_bin(get_offset(id), get_size(id));
  397. Cvirtual_binary d;
  398. seek(get_offset(id));
  399. int size = get_size(id);
  400. if (read(d.write_start(size), size))
  401. d.clear();
  402. return d;
  403. }
  404. Cvirtual_binary Cmix_file::get_vdata(const string& name)
  405. {
  406. return get_vdata(get_id(m_game, name));
  407. }