innoextract.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. * innoextract.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "innoextract.h"
  12. #ifdef ENABLE_INNOEXTRACT
  13. #include "cli/extract.hpp"
  14. #include "setup/version.hpp"
  15. #endif
  16. QString Innoextract::extract(QString installer, QString outDir, std::function<void (float percent)> cb)
  17. {
  18. QString errorText{};
  19. #ifdef ENABLE_INNOEXTRACT
  20. ::extract_options o;
  21. o.extract = true;
  22. // standard settings
  23. o.gog_galaxy = true;
  24. o.codepage = 0U;
  25. o.output_dir = outDir.toStdString();
  26. o.extract_temp = true;
  27. o.extract_unknown = true;
  28. o.filenames.set_expand(true);
  29. o.preserve_file_times = true; // also correctly closes file -> without it: on Windows the files are not written completely
  30. try
  31. {
  32. process_file(installer.toStdString(), o, cb);
  33. }
  34. catch(const std::ios_base::failure & e)
  35. {
  36. errorText = tr("Stream error while extracting files!\nerror reason: ");
  37. errorText += e.what();
  38. }
  39. catch(const format_error & e)
  40. {
  41. errorText = e.what();
  42. }
  43. catch(const std::runtime_error & e)
  44. {
  45. errorText = e.what();
  46. }
  47. catch(const setup::version_error &)
  48. {
  49. errorText = tr("Not a supported Inno Setup installer!");
  50. }
  51. #else
  52. errorText = tr("VCMI was compiled without innoextract support, which is needed to extract exe files!");
  53. #endif
  54. return errorText;
  55. }
  56. QString Innoextract::getHashError(QString exeFile, QString binFile, QString exeFileOriginal, QString binFileOriginal)
  57. {
  58. enum filetype
  59. {
  60. H3_COMPLETE, CHR
  61. };
  62. struct data
  63. {
  64. filetype type;
  65. std::string language;
  66. int exeSize;
  67. int binSize;
  68. std::string exe;
  69. std::string bin;
  70. };
  71. struct fileinfo {
  72. std::string hash;
  73. int size = 0;
  74. };
  75. std::vector<data> knownHashes = {
  76. // { H3_COMPLETE, "english", 973162040, 0, "7cf1ecec73e8c2f2c2619415cd16749be5641942", "" }, // setup_homm_3_complete_4.0_(10665).exe
  77. // { H3_COMPLETE, "french", ???, 0, "7e5a737c51530a1888033d188ab0635825ee622f", "" }, // setup_homm_3_complete_french_4.0_(10665).exe
  78. { H3_COMPLETE, "english", 822520, 1005040617, "66646a353b06417fa12c6384405688c84a315cc1", "c624e2071f4e35386765ab044ad5860ac245b7f4" }, // setup_heroes_of_might_and_magic_3_complete_4.0_(28740).exe
  79. { H3_COMPLETE, "french", 824960, 997305870, "072f1d4466ff16444d8c7949c6530448a9c53cfa", "9b6b451d2bd2f8b4be159e62fa6d32e87ee10455" }, // setup_heroes_of_might_and_magic_3_complete_4.0_(french)_(28740).exe
  80. { H3_COMPLETE, "polish", 822288, 849286313, "74ffde00156dd5a8e237668f87213387f0dd9c7c", "2523cf9943043ae100186f89e4ebf7c28be09804" }, // setup_heroes_of_might_and_magic_3_complete_4.0_(polish)_(28740).exe
  81. { H3_COMPLETE, "russian", 821608, 980398466, "88ccae41e66da58ba4ad62024d97dfe69084f825", "58f1b3c813a1953992bba1f9855c47d01c897db8" }, // setup_heroes_of_might_and_magic_3_complete_4.0_(russian)_(28740).exe
  82. { H3_COMPLETE, "english", 820288, 1006275333, "ca68adb8c2d8c6b3afa17a595ad70c2cec062b5a", "2715e10e91919d05377d39fd879d43f9f0cb9f87" }, // setup_heroes_of_might_and_magic_3_complete_4.0_(3.2)_gog_0.1_(77075).exe
  83. { H3_COMPLETE, "french", 822688, 998540653, "fbb300eeef52f5d81a571a178723b19313e3856d", "4f4d90ff2f60968616766237664744bc54754500" }, // setup_heroes_of_might_and_magic_3_complete_4.0_(3.2)_gog_0.1_(french)_(77075).exe
  84. { H3_COMPLETE, "polish", 819904, 851750601, "a413b0b9f3d5ca3e1a57e84a42de28c67d77b1a7", "fd9fe58bcbb8b442e8cfc299d90f1d503f281d40" }, // setup_heroes_of_might_and_magic_3_complete_4.0_(3.2)_gog_0.1_(polish)_(77075).exe
  85. { H3_COMPLETE, "russian", 819416, 981633128, "e84eedf62fe2e5f9171a7e1ce6e99315a09ce41f", "49cc683395c0cf80830bfa66e42bb5dfdb7aa124" }, // setup_heroes_of_might_and_magic_3_complete_4.0_(3.2)_gog_0.1_(russian)_(77075).exe
  86. { CHR, "english", 689384280, 0, "81f6e306103cca7dd413c4476eeda65eb790e9e6", "" }, // setup_heroes_chronicles_2.0.0.38.exe
  87. { CHR, "english", 485694752, 0, "44e4fc2c38261a1c2a57d5198f44493210e8fc1a", "" }, // setup_heroes_chronicles_chapter1_2.1.0.42.exe
  88. { CHR, "english", 493102840, 0, "b479a3272cf4b57a6b7fc499df5eafb624dcd6de", "" }, // setup_heroes_chronicles_chapter2_2.1.0.43.exe
  89. { CHR, "english", 470364128, 0, "5ad36d822e1700c9ecf93b78652900a52518146b", "" }, // setup_heroes_chronicles_chapter3_2.1.0.41.exe
  90. { CHR, "english", 469211296, 0, "5deb374a2e188ed14e8f74ad1284c45e46adf760", "" }, // setup_heroes_chronicles_chapter4_2.1.0.42.exe
  91. { CHR, "english", 447497560, 0, "a6daa6ed56c840f3be7ad6ad920a2f9f2439acc8", "" }, // setup_heroes_chronicles_chapter5_2.1.0.42.exe
  92. { CHR, "english", 447430456, 0, "93a42dd24453f36e7020afc61bca05b8461a3f04", "" }, // setup_heroes_chronicles_chapter6_2.1.0.42.exe
  93. { CHR, "english", 481583720, 0, "d74b042015f3c5b667821c5d721ac3d2fdbf43fc", "" }, // setup_heroes_chronicles_chapter7_2.1.0.42.exe
  94. { CHR, "english", 462976008, 0, "9039050e88b9dabcdb3ffa74b33e6aa86a20b7d9", "" }, // setup_heroes_chronicles_chapter8_2.1.0.42.exe
  95. { CHR, "english", 500308840, 0, "bc96759fcd4098a846dc83c76e9175c2cb047d66", "" }, // setup_heroes_chronicles_chapter_1_-_warlords_of_the_wasteland_1.0_gog_v2_(83337).exe
  96. { CHR, "english", 503877256, 0, "e64e09b4d4a558c4b435f45dd5a5025812d2418f", "" }, // setup_heroes_chronicles_chapter_2_-_conquest_of_the_underworld_1.0_gog_v2_(83337).exe
  97. { CHR, "english", 484422880, 0, "c2c5c596367def0835c6401bcd0b8949892c74b6", "" }, // setup_heroes_chronicles_chapter_3_-_masters_of_the_elements_1.0_gog_v2_(83337).exe
  98. { CHR, "english", 479835408, 0, "5df9e25a20a91fd51c6ae109e8a652bbe33fb85b", "" }, // setup_heroes_chronicles_chapter_4_-_clash_of_the_dragons_1.0_gog_v2_(83337).exe
  99. { CHR, "english", 460531888, 0, "d2af77493045543945bea7c1d2da35c4c8c2d8a4", "" }, // setup_heroes_chronicles_chapter_5_-_the_world_tree_1.0_gog_v2_(83337).exe
  100. { CHR, "english", 462570376, 0, "143f778c640bbe719f27a6551c6f9732766ec8d1", "" }, // setup_heroes_chronicles_chapter_6_-_the_fiery_moon_1.0_gog_v2_(83337).exe
  101. { CHR, "english", 497063264, 0, "0b2784a07417245b1f8caa3f5f128bc1e3c1a18c", "" }, // setup_heroes_chronicles_chapter_7_-_revolt_of_the_beastmasters_1.0_gog_v2_(83337).exe
  102. { CHR, "english", 477021344, 0, "bd7db8f8a31f06960f8997548b7ed5fd3bf7cab2", "" }, // setup_heroes_chronicles_chapter_8_-_the_sword_of_frost_1.0_gog_v2_(83337).exe
  103. };
  104. auto doHash = [](QFile f){
  105. fileinfo tmp;
  106. if(f.open(QFile::ReadOnly)) {
  107. QCryptographicHash hash(QCryptographicHash::Algorithm::Sha1);
  108. if(hash.addData(&f))
  109. tmp.hash = hash.result().toHex().toLower().toStdString();
  110. tmp.size = f.size();
  111. }
  112. return tmp;
  113. };
  114. fileinfo exeInfo;
  115. fileinfo binInfo;
  116. fileinfo exeInfoOriginal;
  117. fileinfo binInfoOriginal;
  118. exeInfo = doHash(QFile(exeFile));
  119. if(!binFile.isEmpty())
  120. binInfo = doHash(QFile(binFile));
  121. exeInfoOriginal = doHash(QFile(exeFileOriginal));
  122. if(!binFileOriginal.isEmpty())
  123. binInfoOriginal = doHash(QFile(binFileOriginal));
  124. if(exeInfo.hash.empty() || (!binFile.isEmpty() && binInfo.hash.empty()))
  125. return QString{}; // hashing not possible -> previous error is enough
  126. QString hashOutput = tr("SHA1 hash of provided files:\nExe (%n bytes):\n%1", "param is hash", exeInfo.size).arg(QString::fromStdString(exeInfo.hash));
  127. if(!binInfo.hash.empty())
  128. hashOutput += tr("\nBin (%n bytes):\n%1", "param is hash", binInfo.size).arg(QString::fromStdString(binInfo.hash));
  129. if((!exeInfoOriginal.hash.empty() && exeInfo.hash != exeInfoOriginal.hash) || (!binInfoOriginal.hash.empty() && !binFile.isEmpty() && !binFileOriginal.isEmpty() && binInfo.hash != binInfoOriginal.hash))
  130. return tr("Internal copy process failed. Enough space on device?\n\n%1").arg(hashOutput);
  131. QString foundKnown;
  132. QString exeLang;
  133. QString binLang;
  134. auto find = [exeInfo, binInfo](const data & d) { return (!d.exe.empty() && d.exe == exeInfo.hash) || (!d.bin.empty() && d.bin == binInfo.hash);};
  135. auto it = std::find_if(knownHashes.begin(), knownHashes.end(), find);
  136. while(it != knownHashes.end()){
  137. auto lang = QString::fromStdString((*it).language);
  138. foundKnown += "\n" + (exeInfo.hash == (*it).exe ? tr("Exe") : tr("Bin")) + " - " + lang;
  139. if(exeInfo.hash == (*it).exe)
  140. exeLang = lang;
  141. else
  142. binLang = lang;
  143. it = std::find_if(++it, knownHashes.end(), find);
  144. }
  145. if(!exeLang.isEmpty() && !binLang.isEmpty() && exeLang != binLang && !binFile.isEmpty())
  146. return tr("Language mismatch!\n%1\n\n%2").arg(foundKnown, hashOutput);
  147. else if((!exeLang.isEmpty() || !binLang.isEmpty()) && !binFile.isEmpty())
  148. return tr("Only one file known! Maybe files are corrupted? Please download again.\n%1\n\n%2").arg(foundKnown, hashOutput);
  149. else if(!exeLang.isEmpty() && binFile.isEmpty())
  150. return QString{};
  151. else if(!exeLang.isEmpty() && !binFile.isEmpty() && exeLang == binLang)
  152. return QString{};
  153. return tr("Unknown files! Maybe files are corrupted? Please download again.\n\n%1").arg(hashOutput);
  154. }