/src/scanner.cpp

https://code.google.com/p/dwarftherapist/ · C++ · 471 lines · 387 code · 59 blank · 25 comment · 39 complexity · da0bea3ba6f3885dff20684349bf9d4d MD5 · raw file

  1. /*
  2. Dwarf Therapist
  3. Copyright (c) 2009 Trey Stout (chmod)
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. #include <QThread>
  21. #include "scanner.h"
  22. #include "dfinstance.h"
  23. #include "gamedatareader.h"
  24. #include "dwarftherapist.h"
  25. #include "scannerthread.h"
  26. #include "defines.h"
  27. #include "selectparentlayoutdialog.h"
  28. #include "layoutcreator.h"
  29. #include "word.h"
  30. Scanner::Scanner(DFInstance *df, MainWindow *parent)
  31. : QDialog(parent)
  32. , m_df(df)
  33. , m_thread(0)
  34. , ui(new Ui::ScannerDialog)
  35. , m_stop_scanning(false)
  36. {
  37. ui->setupUi(this);
  38. set_ui_enabled(true);
  39. }
  40. void Scanner::cancel_scan() {
  41. m_stop_scanning = true;
  42. m_df->cancel_scan();
  43. }
  44. void Scanner::set_ui_enabled(bool enabled) {
  45. m_stop_scanning = enabled;
  46. ui->gb_scan_targets->setEnabled(enabled);
  47. ui->gb_search->setEnabled(enabled);
  48. ui->gb_brute_force->setEnabled(enabled);
  49. ui->gb_progress->setEnabled(!enabled);
  50. ui->btn_cancel_scan->setEnabled(!enabled);
  51. ui->lbl_scan_progress->setText(tr("Not Scanning"));
  52. ui->pb_main->reset();
  53. ui->pb_sub->reset();
  54. }
  55. void Scanner::report_address(const QString &msg, const quint32 &addr) {
  56. VIRTADDR corrected_addr = addr - m_df->get_memory_correction();
  57. QString out = QString("<b>%1\t= <font color=blue>%2</font> "
  58. "(uncorrected:%3)\n")
  59. .arg(msg)
  60. .arg(hexify(corrected_addr))
  61. .arg(hexify(addr));
  62. ui->text_output->append(out);
  63. LOGD << "ADDRESS FOR:" << msg << hexify(corrected_addr) << "UNCORRECTED:" <<
  64. hexify(addr);
  65. }
  66. void Scanner::report_offset(const QString &msg, const int &addr) {
  67. QString out = QString("<b>%1\t= <font color=blue>%2</font></b>\n")
  68. .arg(msg)
  69. .arg(hexify(addr));
  70. ui->text_output->append(out);
  71. LOGD << "OFFSET FOR:" << msg << hexify(addr);
  72. }
  73. void Scanner::get_brute_force_address_range(uint &start_addr, uint &end_addr) {
  74. QString start = ui->le_start_address->text();
  75. QString end = ui->le_end_address->text();
  76. bool ok;
  77. start_addr = start.toUInt(&ok, 16);
  78. if( !ok ) {
  79. start_addr = 0;
  80. }
  81. end_addr = end.toUInt(&ok, 16);
  82. if( !ok ) {
  83. end_addr = 0xFFFFFFFF;
  84. }
  85. }
  86. void Scanner::prepare_new_thread(SCANNER_JOB_TYPE type) {
  87. if (m_thread && m_thread->isRunning()) {
  88. m_thread->terminate();
  89. m_thread->wait(3000);
  90. m_thread->deleteLater();
  91. m_thread = 0;
  92. }
  93. m_thread = new ScannerThread(m_df);
  94. m_thread->set_job(type);
  95. connect(m_thread, SIGNAL(main_scan_total_steps(int)),
  96. ui->pb_main, SLOT(setMaximum(int)));
  97. connect(m_thread, SIGNAL(main_scan_progress(int)),
  98. ui->pb_main, SLOT(setValue(int)));
  99. connect(m_thread, SIGNAL(sub_scan_total_steps(int)),
  100. ui->pb_sub, SLOT(setMaximum(int)));
  101. connect(m_thread, SIGNAL(sub_scan_progress(int)),
  102. ui->pb_sub, SLOT(setValue(int)));
  103. connect(m_thread, SIGNAL(scan_message(const QString&)),
  104. ui->lbl_scan_progress, SLOT(setText(const QString&)));
  105. connect(m_thread, SIGNAL(found_address(const QString&, const quint32&)),
  106. SLOT(report_address(const QString&, const quint32&)));
  107. connect(m_thread, SIGNAL(found_offset(const QString&, const int&)),
  108. SLOT(report_offset(const QString&, const int&)));
  109. }
  110. void Scanner::run_thread_and_wait() {
  111. if (!m_thread) {
  112. LOGW << "can't run a thread that was never set up! (m_thread == 0)";
  113. return;
  114. }
  115. m_thread->start();
  116. while (!m_thread->wait(200)) {
  117. if (m_stop_scanning || !m_thread->isRunning() || m_thread->isFinished())
  118. break;
  119. //ui->text_output->append("waiting on thread...");
  120. DT->processEvents();
  121. }
  122. m_thread->terminate();
  123. if (m_thread->wait(5000)) {
  124. delete m_thread;
  125. } else {
  126. LOGE << "Scanning thread failed to stop for 5 seconds after killed!";
  127. }
  128. m_thread = 0;
  129. }
  130. void Scanner::find_translations_vector() {
  131. set_ui_enabled(false);
  132. prepare_new_thread(FIND_TRANSLATIONS_VECTOR);
  133. run_thread_and_wait();
  134. set_ui_enabled(true);
  135. }
  136. void Scanner::find_dwarf_race_index() {
  137. set_ui_enabled(false);
  138. prepare_new_thread(FIND_DWARF_RACE_INDEX);
  139. run_thread_and_wait();
  140. set_ui_enabled(true);
  141. }
  142. void Scanner::find_creature_vector() {
  143. set_ui_enabled(false);
  144. prepare_new_thread(FIND_CREATURE_VECTOR);
  145. run_thread_and_wait();
  146. set_ui_enabled(true);
  147. }
  148. void Scanner::find_vector_by_length() {
  149. VectorSearchParams params;
  150. set_ui_enabled(false);
  151. uint target_count = ui->sb_vector_entries->value();
  152. QString op = ui->btn_find_vector_operator->text();
  153. ui->text_output->append(tr("Vectors %1 %2 entries").arg(op).arg(target_count));
  154. prepare_new_thread(FIND_VECTORS_OF_SIZE);
  155. get_brute_force_address_range(params.start_addr, params.end_addr);
  156. params.target_count = target_count;
  157. params.op = op.at(0).toAscii();
  158. QByteArray needle((const char *)&params, sizeof(params));
  159. m_thread->set_meta(needle);
  160. run_thread_and_wait();
  161. set_ui_enabled(true);
  162. }
  163. void Scanner::find_stone_vector() {
  164. set_ui_enabled(false);
  165. prepare_new_thread(FIND_STONE_VECTOR);
  166. run_thread_and_wait();
  167. set_ui_enabled(true);
  168. }
  169. void Scanner::find_metal_vector() {
  170. set_ui_enabled(false);
  171. set_ui_enabled(true);
  172. }
  173. void Scanner::find_position_vector() {
  174. set_ui_enabled(false);
  175. prepare_new_thread(FIND_POSITION_VECTOR);
  176. run_thread_and_wait();
  177. set_ui_enabled(true);
  178. }
  179. void Scanner::create_memory_layout() {
  180. set_ui_enabled(false);
  181. SelectParentLayoutDialog dlg(m_df, this);
  182. int ret = dlg.exec();
  183. if(QDialog::Accepted == ret)
  184. {
  185. GameDataReader * gdr = GameDataReader::ptr();
  186. QString out = QString("<b><font color=blue>Make sure that your first dwarf from embark has a nickname of '%1' and a custom profession of '%2'</font></b>\n")
  187. .arg(gdr->get_string_for_key("ram_guesser/dwarf_nickname"))
  188. .arg(gdr->get_string_for_key("ram_guesser/dwarf_custom_profession"));
  189. ui->text_output->append(out);
  190. MemoryLayout * parent = dlg.get_layout();
  191. LOGD << "Attempting to create layout from " << parent->game_version() << " for version "
  192. << dlg.get_version_name() << " and filename " << dlg.get_file_name();
  193. LayoutCreator * creator = new LayoutCreator(m_df, parent, dlg.get_file_name(), dlg.get_version_name());
  194. m_df->set_memory_layout(parent);
  195. ScannerJob::m_layout_override_checksum = parent->checksum();
  196. prepare_new_thread(FIND_DWARF_RACE_INDEX);
  197. connect(m_thread, SIGNAL(found_address(const QString&, const quint32&)), creator,
  198. SLOT(report_address(const QString&, const quint32&)));
  199. run_thread_and_wait();
  200. prepare_new_thread(FIND_TRANSLATIONS_VECTOR);
  201. connect(m_thread, SIGNAL(found_address(const QString&, const quint32&)), creator,
  202. SLOT(report_address(const QString&, const quint32&)));
  203. run_thread_and_wait();
  204. prepare_new_thread(FIND_CREATURE_VECTOR);
  205. connect(m_thread, SIGNAL(found_address(const QString&, const quint32&)), creator,
  206. SLOT(report_address(const QString&, const quint32&)));
  207. run_thread_and_wait();
  208. prepare_new_thread(FIND_CURRENT_YEAR);
  209. connect(m_thread, SIGNAL(found_address(const QString&, const quint32&)), creator,
  210. SLOT(report_address(const QString&, const quint32&)));
  211. run_thread_and_wait();
  212. prepare_new_thread(FIND_SQUADS_VECTOR);
  213. connect(m_thread, SIGNAL(found_address(const QString&, const quint32&)), creator,
  214. SLOT(report_address(const QString&, const quint32&)));
  215. run_thread_and_wait();
  216. ScannerJob::m_layout_override_checksum = "";
  217. LOGD << "Finished reading layouts, writing to disk.";
  218. if(creator->write_file()) {
  219. out = QString("<b><font color=green>Finished. Created new file: %1</font></b>\n").arg(dlg.get_file_name());
  220. ui->text_output->append(out);
  221. out = QString("<b><font color=red>Please restart DwarfTherapist!</font></b>\n");
  222. ui->text_output->append(out);
  223. LOGD << "Finished writing file " << dlg.get_file_name() << " to disk.";
  224. } else {
  225. out = QString("<b><font color=red>Unable to write to file, please check your disk!</font></b>\n");
  226. ui->text_output->append(out);
  227. }
  228. delete creator;
  229. }
  230. set_ui_enabled(true);
  231. }
  232. void Scanner::find_std_string() {
  233. set_ui_enabled(false);
  234. prepare_new_thread(FIND_STD_STRING);
  235. QByteArray needle = ui->le_null_terminated_string->text().toAscii();
  236. m_thread->set_meta(needle);
  237. run_thread_and_wait();
  238. set_ui_enabled(true);
  239. }
  240. void Scanner::find_null_terminated_string() {
  241. NullTerminatedStringSearchParams params;
  242. set_ui_enabled(false);
  243. prepare_new_thread(FIND_NULL_TERMINATED_STRING);
  244. QByteArray text = ui->le_null_terminated_string->text().toLocal8Bit();
  245. get_brute_force_address_range(params.start_addr, params.end_addr);
  246. params.size = qMin((size_t)text.size(), sizeof(params.data));
  247. memcpy(params.data, text.data(), params.size);
  248. QByteArray needle((const char *)&params, sizeof(params));
  249. m_thread->set_meta(needle);
  250. run_thread_and_wait();
  251. set_ui_enabled(true);
  252. }
  253. void Scanner::find_number_or_address() {
  254. NullTerminatedStringSearchParams params;
  255. set_ui_enabled(false);
  256. //re-use the basic bit searcher
  257. prepare_new_thread(FIND_NULL_TERMINATED_STRING);
  258. bool ok;
  259. QByteArray text = encode(ui->le_find_address->text().
  260. toUInt(&ok, ui->rb_hex->isChecked() ? 16 : 10));
  261. get_brute_force_address_range(params.start_addr, params.end_addr);
  262. params.size = qMin((size_t)text.size(), sizeof(params.data));
  263. memcpy(params.data, text.data(), params.size);
  264. QByteArray needle((const char *)&params, sizeof(params));
  265. m_thread->set_meta(needle);
  266. run_thread_and_wait();
  267. set_ui_enabled(true);
  268. }
  269. void Scanner::brute_force_read() {
  270. set_ui_enabled(false);
  271. bool ok; // for base conversions
  272. VIRTADDR addr = ui->le_address->text().toUInt(&ok, 16);
  273. if (m_df && m_df->is_ok()) {
  274. switch(ui->cb_interpret_as_type->currentIndex()) {
  275. case 0: // std::string
  276. ui->le_read_output->setText(m_df->read_string(addr));
  277. break;
  278. case 1: // null terminated string
  279. {
  280. QByteArray str(512, 0);
  281. m_df->read_raw(addr, 512, str);
  282. ui->le_read_output->setText(QString(str));
  283. }
  284. break;
  285. case 2: // int
  286. ui->le_read_output->setText(QString::number(m_df->read_int(addr)));
  287. break;
  288. case 3: // uint
  289. ui->le_read_output->setText(QString::number(m_df->read_addr(addr)));
  290. break;
  291. case 4: // short
  292. ui->le_read_output->setText(QString::number(m_df->read_short(addr)));
  293. break;
  294. case 5: // ushort
  295. ui->le_read_output->setText(QString::number(m_df->read_word(addr)));
  296. break;
  297. case 6: // std::vector<void*>
  298. {
  299. QVector<uint> addresses = m_df->enumerate_vector(addr);
  300. ui->text_output->append(QString("Vector at %1 contains %2 "
  301. "entries...").arg(hexify(addr))
  302. .arg(addresses.size()));
  303. foreach(uint a, addresses) {
  304. ui->text_output->append(hexify(a));
  305. }
  306. }
  307. break;
  308. case 7: // raw
  309. ui->text_output->append(m_df->pprint(addr, 0xA00));
  310. break;
  311. case 8: // word
  312. {
  313. Word * word = m_df->read_dwarf_word(addr);
  314. if( word ) {
  315. ui->le_read_output->setText(word->base());
  316. } else {
  317. ui->le_read_output->setText(("<unknown>"));
  318. }
  319. }
  320. break;
  321. case 9: // name
  322. {
  323. QString name = m_df->read_dwarf_name(addr);
  324. ui->le_read_output->setText(name);
  325. }
  326. break;
  327. }
  328. } else {
  329. LOGE << "Cannot brute-force read. DF Connection is not ok.";
  330. }
  331. set_ui_enabled(true);
  332. }
  333. void Scanner::find_narrowing() {
  334. set_ui_enabled(false);
  335. uint target_count = ui->le_narrowing_value->text().toInt();
  336. prepare_new_thread(FIND_NARROWING_VECTORS_OF_SIZE);
  337. QByteArray needle = QString("%1").arg(target_count).toAscii();
  338. m_thread->set_meta(needle);
  339. if(ui->lbl_narrowing_result->text() != "nil") {
  340. m_thread->set_search_vector(m_narrow);
  341. }
  342. m_thread->start();
  343. while (!m_thread->wait(200)) {
  344. if (m_stop_scanning || !m_thread->isRunning() || m_thread->isFinished())
  345. break;
  346. //ui->text_output->append("waiting on thread...");
  347. DT->processEvents();
  348. }
  349. m_thread->terminate();
  350. if (!m_thread->wait(5000)) {
  351. LOGE << "Scanning thread failed to stop for 5 seconds after killed!";
  352. return;
  353. }
  354. QVector<VIRTADDR> * result = (QVector<VIRTADDR> *)m_thread->get_result();
  355. if(result == NULL) {
  356. ui->lbl_narrowing_result->setText(tr("0"));
  357. m_narrow.clear();
  358. } else {
  359. ui->lbl_narrowing_result->setText(QString("%1").arg(result->size()));
  360. m_narrow = *result;
  361. m_thread->clear_result();
  362. delete result;
  363. }
  364. delete m_thread;
  365. m_thread = 0;
  366. set_ui_enabled(true);
  367. }
  368. void Scanner::reset_narrowing() {
  369. LOGD << "Reset narrowing search";
  370. m_narrow.clear();
  371. ui->lbl_narrowing_result->setText(tr("nil"));
  372. ui->le_narrowing_value->setText("");
  373. }
  374. void Scanner::print_narrowing() {
  375. if(m_narrow.size() > 200) {
  376. QString out = QString("<b><font color=red>There are a total of %1 vectors, only printing 200.</font></b>\n")
  377. .arg(m_narrow.size());
  378. ui->text_output->append(out);
  379. }
  380. int i = 0;
  381. foreach(uint addr, m_narrow) {
  382. report_address("vector found at", addr);
  383. if(i++ >= 200)
  384. break;
  385. }
  386. }
  387. void Scanner::find_squad_vector() {
  388. set_ui_enabled(false);
  389. prepare_new_thread(FIND_SQUADS_VECTOR);
  390. run_thread_and_wait();
  391. set_ui_enabled(true);
  392. }
  393. void Scanner::change_operator() {
  394. QString op = ui->btn_find_vector_operator->text();
  395. if(op == "=") {
  396. op = "<";
  397. } else if(op == "<") {
  398. op = ">";
  399. } else {
  400. op = "=";
  401. }
  402. ui->btn_find_vector_operator->setText(op);
  403. }
  404. void Scanner::find_current_year() {
  405. set_ui_enabled(false);
  406. prepare_new_thread(FIND_CURRENT_YEAR);
  407. run_thread_and_wait();
  408. set_ui_enabled(true);
  409. }