/src/dfinstance.cpp

https://code.google.com/p/dwarftherapist/ · C++ · 965 lines · 755 code · 117 blank · 93 comment · 141 complexity · bb5e7e844c816cd686e2543ef34d7209 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 <QtGui>
  21. #include <QtDebug>
  22. #include "defines.h"
  23. #include "dfinstance.h"
  24. #include "dwarf.h"
  25. #include "squad.h"
  26. #include "word.h"
  27. #include "utils.h"
  28. #include "gamedatareader.h"
  29. #include "memorylayout.h"
  30. #include "cp437codec.h"
  31. #include "dwarftherapist.h"
  32. #include "memorysegment.h"
  33. #include "truncatingfilelogger.h"
  34. #include "mainwindow.h"
  35. #ifdef Q_WS_WIN
  36. #define LAYOUT_SUBDIR "windows"
  37. #include "dfinstancewindows.h"
  38. #else
  39. #ifdef Q_WS_X11
  40. #define LAYOUT_SUBDIR "linux"
  41. #include "dfinstancelinux.h"
  42. #else
  43. #ifdef Q_WS_MAC
  44. #define LAYOUT_SUBDIR "osx"
  45. #include "dfinstanceosx.h"
  46. #endif
  47. #endif
  48. #endif
  49. DFInstance::DFInstance(QObject* parent)
  50. : QObject(parent)
  51. , m_pid(0)
  52. , m_memory_correction(0)
  53. , m_stop_scan(false)
  54. , m_is_ok(true)
  55. , m_bytes_scanned(0)
  56. , m_layout(0)
  57. , m_attach_count(0)
  58. , m_heartbeat_timer(new QTimer(this))
  59. , m_memory_remap_timer(new QTimer(this))
  60. , m_scan_speed_timer(new QTimer(this))
  61. , m_dwarf_race_id(0)
  62. {
  63. connect(m_scan_speed_timer, SIGNAL(timeout()),
  64. SLOT(calculate_scan_rate()));
  65. connect(m_memory_remap_timer, SIGNAL(timeout()),
  66. SLOT(map_virtual_memory()));
  67. m_memory_remap_timer->start(20000); // 20 seconds
  68. // let subclasses start the heartbeat timer, since we don't want to be
  69. // checking before we're connected
  70. connect(m_heartbeat_timer, SIGNAL(timeout()), SLOT(heartbeat()));
  71. // We need to scan for memory layout files to get a list of DF versions this
  72. // DT version can talk to. Start by building up a list of search paths
  73. QDir working_dir = QDir::current();
  74. QStringList search_paths;
  75. search_paths << working_dir.path();
  76. QString subdir = LAYOUT_SUBDIR;
  77. search_paths << QString("etc/memory_layouts/%1").arg(subdir);
  78. TRACE << "Searching for MemoryLayout ini files in the following directories";
  79. foreach(QString path, search_paths) {
  80. TRACE<< path;
  81. QDir d(path);
  82. d.setNameFilters(QStringList() << "*.ini");
  83. d.setFilter(QDir::NoDotAndDotDot | QDir::Readable | QDir::Files);
  84. d.setSorting(QDir::Name | QDir::Reversed);
  85. QFileInfoList files = d.entryInfoList();
  86. foreach(QFileInfo info, files) {
  87. MemoryLayout *temp = new MemoryLayout(info.absoluteFilePath());
  88. if (temp && temp->is_valid()) {
  89. LOGD << "adding valid layout" << temp->game_version()
  90. << temp->checksum();
  91. m_memory_layouts.insert(temp->checksum().toLower(), temp);
  92. }
  93. }
  94. }
  95. // if no memory layouts were found that's a critical error
  96. if (m_memory_layouts.size() < 1) {
  97. LOGE << "No valid memory layouts found in the following directories..."
  98. << QDir::searchPaths("memory_layouts");
  99. qApp->exit(ERROR_NO_VALID_LAYOUTS);
  100. }
  101. }
  102. DFInstance::~DFInstance() {
  103. LOGD << "DFInstance baseclass virtual dtor!";
  104. foreach(MemoryLayout *l, m_memory_layouts) {
  105. delete(l);
  106. }
  107. m_memory_layouts.clear();
  108. }
  109. DFInstance * DFInstance::newInstance() {
  110. #ifdef Q_WS_WIN
  111. return new DFInstanceWindows();
  112. #else
  113. #ifdef Q_WS_MAC
  114. return new DFInstanceOSX();
  115. #else
  116. #ifdef Q_WS_X11
  117. return new DFInstanceLinux();
  118. #endif
  119. #endif
  120. #endif
  121. }
  122. BYTE DFInstance::read_byte(const VIRTADDR &addr) {
  123. QByteArray out;
  124. read_raw(addr, sizeof(BYTE), out);
  125. return out.at(0);
  126. }
  127. WORD DFInstance::read_word(const VIRTADDR &addr) {
  128. QByteArray out;
  129. read_raw(addr, sizeof(WORD), out);
  130. return decode_word(out);
  131. }
  132. VIRTADDR DFInstance::read_addr(const VIRTADDR &addr) {
  133. QByteArray out;
  134. read_raw(addr, sizeof(VIRTADDR), out);
  135. return decode_dword(out);
  136. }
  137. qint16 DFInstance::read_short(const VIRTADDR &addr) {
  138. QByteArray out;
  139. read_raw(addr, sizeof(qint16), out);
  140. return decode_short(out);
  141. }
  142. qint32 DFInstance::read_int(const VIRTADDR &addr) {
  143. QByteArray out;
  144. read_raw(addr, sizeof(qint32), out);
  145. return decode_int(out);
  146. }
  147. QVector<VIRTADDR> DFInstance::scan_mem(const QByteArray &needle, const uint start_addr, const uint end_addr) {
  148. // progress reporting
  149. m_scan_speed_timer->start(500);
  150. m_memory_remap_timer->stop(); // don't remap segments while scanning
  151. int total_bytes = 0;
  152. m_bytes_scanned = 0; // for global timings
  153. int bytes_scanned = 0; // for progress calcs
  154. foreach(MemorySegment *seg, m_regions) {
  155. total_bytes += seg->size;
  156. }
  157. int report_every_n_bytes = total_bytes / 1000;
  158. emit scan_total_steps(1000);
  159. emit scan_progress(0);
  160. m_stop_scan = false;
  161. QVector<VIRTADDR> addresses; //! return value
  162. QByteArrayMatcher matcher(needle);
  163. int step_size = 0x1000;
  164. QByteArray buffer(step_size, 0);
  165. QByteArray back_buffer(step_size * 2, 0);
  166. QTime timer;
  167. timer.start();
  168. attach();
  169. foreach(MemorySegment *seg, m_regions) {
  170. int step = step_size;
  171. int steps = seg->size / step;
  172. if (seg->size % step)
  173. steps++;
  174. if( seg->end_addr < start_addr ) {
  175. continue;
  176. }
  177. if( seg->start_addr > end_addr ) {
  178. break;
  179. }
  180. for(VIRTADDR ptr = seg->start_addr; ptr < seg->end_addr; ptr += step) {
  181. if( ptr < start_addr ) {
  182. continue;
  183. }
  184. if( ptr > end_addr ) {
  185. m_stop_scan = true;
  186. break;
  187. }
  188. step = step_size;
  189. if (ptr + step > seg->end_addr) {
  190. step = seg->end_addr - ptr;
  191. }
  192. // move the last thing we read to the front of the back_buffer
  193. back_buffer.replace(0, step, buffer);
  194. // fill the main read buffer
  195. int bytes_read = read_raw(ptr, step, buffer);
  196. if (bytes_read < step && !seg->is_guarded) {
  197. if (m_layout->is_complete()) {
  198. LOGW << "tried to read" << step << "bytes starting at" <<
  199. hexify(ptr) << "but only got" << dec << bytes_read;
  200. }
  201. continue;
  202. }
  203. bytes_scanned += bytes_read;
  204. m_bytes_scanned += bytes_read;
  205. // put the main buffer on the end of the back_buffer
  206. back_buffer.replace(step, step, buffer);
  207. int idx = -1;
  208. forever {
  209. idx = matcher.indexIn(back_buffer, idx+1);
  210. if (idx == -1) {
  211. break;
  212. } else {
  213. VIRTADDR hit = ptr + idx - step;
  214. if (!addresses.contains(hit)) {
  215. // backbuffer may cause duplicate hits
  216. addresses << hit;
  217. }
  218. }
  219. }
  220. if (m_stop_scan)
  221. break;
  222. emit scan_progress(bytes_scanned / report_every_n_bytes);
  223. }
  224. DT->processEvents();
  225. if (m_stop_scan)
  226. break;
  227. }
  228. detach();
  229. m_memory_remap_timer->start(20000); // start the remapper again
  230. LOGD << QString("Scanned %L1MB in %L2ms").arg(bytes_scanned / 1024 * 1024)
  231. .arg(timer.elapsed());
  232. return addresses;
  233. }
  234. bool DFInstance::looks_like_vector_of_pointers(const VIRTADDR &addr) {
  235. int start = read_int(addr + 0x4);
  236. int end = read_int(addr + 0x8);
  237. int entries = (end - start) / sizeof(int);
  238. LOGD << "LOOKS LIKE VECTOR? unverified entries:" << entries;
  239. return start >=0 &&
  240. end >=0 &&
  241. end >= start &&
  242. (end-start) % 4 == 0 &&
  243. start % 4 == 0 &&
  244. end % 4 == 0 &&
  245. entries < 10000;
  246. }
  247. void DFInstance::read_raws() {
  248. emit progress_message(tr("Reading raws"));
  249. LOGI << "Reading some game raws...";
  250. GameDataReader::ptr()->read_raws(m_df_dir);
  251. }
  252. QVector<Dwarf*> DFInstance::load_dwarves() {
  253. map_virtual_memory();
  254. QVector<Dwarf*> dwarves;
  255. if (!m_is_ok) {
  256. LOGW << "not connected";
  257. detach();
  258. return dwarves;
  259. }
  260. // we're connected, make sure we have good addresses
  261. VIRTADDR creature_vector = m_layout->address("creature_vector");
  262. creature_vector += m_memory_correction;
  263. VIRTADDR dwarf_race_index = m_layout->address("dwarf_race_index");
  264. dwarf_race_index += m_memory_correction;
  265. if (!is_valid_address(creature_vector)) {
  266. LOGW << "Active Memory Layout" << m_layout->filename() << "("
  267. << m_layout->game_version() << ")" << "contains an invalid"
  268. << "creature_vector address. Either you are scanning a new "
  269. << "DF version or your config files are corrupted.";
  270. return dwarves;
  271. }
  272. if (!is_valid_address(dwarf_race_index)) {
  273. LOGW << "Active Memory Layout" << m_layout->filename() << "("
  274. << m_layout->game_version() << ")" << "contains an invalid"
  275. << "dwarf_race_index address. Either you are scanning a new "
  276. << "DF version or your config files are corrupted.";
  277. return dwarves;
  278. }
  279. // both necessary addresses are valid, so let's try to read the creatures
  280. LOGD << "loading creatures from " << hexify(creature_vector) <<
  281. hexify(creature_vector - m_memory_correction) << "(UNCORRECTED)";
  282. LOGD << "dwarf race index" << hexify(dwarf_race_index) <<
  283. hexify(dwarf_race_index - m_memory_correction) << "(UNCORRECTED)";
  284. emit progress_message(tr("Loading Dwarves"));
  285. attach();
  286. // which race id is dwarven?
  287. m_dwarf_race_id = read_word(dwarf_race_index);
  288. LOGD << "dwarf race:" << hexify(m_dwarf_race_id);
  289. QVector<VIRTADDR> entries = enumerate_vector(creature_vector);
  290. emit progress_range(0, entries.size()-1);
  291. TRACE << "FOUND" << entries.size() << "creatures";
  292. if (!entries.empty()) {
  293. Dwarf *d = 0;
  294. int i = 0;
  295. foreach(VIRTADDR creature_addr, entries) {
  296. d = Dwarf::get_dwarf(this, creature_addr);
  297. if (d) {
  298. dwarves.append(d);
  299. LOGD << "FOUND DWARF" << hexify(creature_addr)
  300. << d->nice_name();
  301. } else {
  302. TRACE << "FOUND OTHER CREATURE" << hexify(creature_addr);
  303. }
  304. emit progress_value(i++);
  305. }
  306. } else {
  307. // we lost the fort!
  308. m_is_ok = false;
  309. }
  310. detach();
  311. LOGI << "found" << dwarves.size() << "dwarves out of" << entries.size()
  312. << "creatures";
  313. return dwarves;
  314. }
  315. QVector<Squad*> DFInstance::load_squads() {
  316. QVector<Squad*> squads;
  317. if (!m_is_ok) {
  318. LOGW << "not connected";
  319. detach();
  320. return squads;
  321. }
  322. // we're connected, make sure we have good addresses
  323. VIRTADDR squad_vector = m_layout->address("squad_vector");
  324. if(squad_vector == 0xFFFFFFFF) {
  325. LOGI << "Squads not supported for this version of Dwarf Fortress";
  326. return squads;
  327. }
  328. squad_vector += m_memory_correction;
  329. if (!is_valid_address(squad_vector)) {
  330. LOGW << "Active Memory Layout" << m_layout->filename() << "("
  331. << m_layout->game_version() << ")" << "contains an invalid"
  332. << "squad_vector address. Either you are scanning a new "
  333. << "DF version or your config files are corrupted.";
  334. return squads;
  335. }
  336. // both necessary addresses are valid, so let's try to read the creatures
  337. LOGD << "loading squads from " << hexify(squad_vector) <<
  338. hexify(squad_vector - m_memory_correction) << "(UNCORRECTED)";
  339. emit progress_message(tr("Loading Squads"));
  340. attach();
  341. QVector<VIRTADDR> entries = enumerate_vector(squad_vector);
  342. TRACE << "FOUND" << entries.size() << "squads";
  343. if (!entries.empty()) {
  344. emit progress_range(0, entries.size()-1);
  345. Squad *s = NULL;
  346. int i = 0;
  347. foreach(VIRTADDR squad_addr, entries) {
  348. s = Squad::get_squad(this, squad_addr);
  349. if (s) {
  350. TRACE << "FOUND SQUAD" << hexify(squad_addr) << s->name();
  351. squads << s;
  352. }
  353. emit progress_value(i++);
  354. }
  355. }
  356. detach();
  357. LOGI << "Found" << squads.size() << "squads out of" << entries.size();
  358. return squads;
  359. }
  360. void DFInstance::heartbeat() {
  361. // simple read attempt that will fail if the DF game isn't running a fort,
  362. // or isn't running at all
  363. QVector<VIRTADDR> creatures = enumerate_vector(
  364. m_layout->address("creature_vector") + m_memory_correction);
  365. if (creatures.size() < 1) {
  366. // no game loaded, or process is gone
  367. emit connection_interrupted();
  368. }
  369. }
  370. bool DFInstance::is_valid_address(const VIRTADDR &addr) {
  371. bool valid = false;
  372. foreach(MemorySegment *seg, m_regions) {
  373. if (seg->contains(addr)) {
  374. valid = true;
  375. break;
  376. }
  377. }
  378. return valid;
  379. }
  380. QByteArray DFInstance::get_data(const VIRTADDR &addr, int size) {
  381. QByteArray ret_val(size, 0); // 0 filled to proper length
  382. int bytes_read = read_raw(addr, size, ret_val);
  383. if (bytes_read != size) {
  384. ret_val.clear();
  385. }
  386. return ret_val;
  387. }
  388. //! ahhh convenience
  389. QString DFInstance::pprint(const VIRTADDR &addr, int size) {
  390. return pprint(get_data(addr, size), addr);
  391. }
  392. QString DFInstance::pprint(const QByteArray &ba, const VIRTADDR &start_addr) {
  393. QString out = " ADDR | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F | TEXT\n";
  394. out.append("------------------------------------------------------------------------\n");
  395. int lines = ba.size() / 16;
  396. if (ba.size() % 16)
  397. lines++;
  398. if (lines < 1)
  399. lines = 0;
  400. for(int i = 0; i < lines; ++i) {
  401. VIRTADDR offset = start_addr + i * 16;
  402. out.append(hexify(offset));
  403. out.append(" | ");
  404. for (int c = 0; c < 16; ++c) {
  405. out.append(ba.mid(i*16 + c, 1).toHex());
  406. out.append(" ");
  407. }
  408. out.append("| ");
  409. for (int c = 0; c < 16; ++c) {
  410. QByteArray tmp = ba.mid(i*16 + c, 1);
  411. if (tmp.at(0) == 0)
  412. out.append(".");
  413. else if (tmp.at(0) <= 126 && tmp.at(0) >= 32)
  414. out.append(tmp);
  415. else
  416. out.append(tmp.toHex());
  417. }
  418. //out.append(ba.mid(i*16, 16).toPercentEncoding());
  419. out.append("\n");
  420. }
  421. return out;
  422. }
  423. Word * DFInstance::read_dwarf_word(const VIRTADDR &addr) {
  424. Word * result = NULL;
  425. uint word_id = read_int(addr);
  426. if(word_id != 0xFFFFFFFF) {
  427. result = DT->get_word(word_id);
  428. }
  429. return result;
  430. }
  431. QString DFInstance::read_dwarf_name(const VIRTADDR &addr) {
  432. QString result = "The";
  433. //7 parts e.g. ffffffff ffffffff 000006d4
  434. // ffffffff ffffffff 000002b1 ffffffff
  435. //Unknown
  436. Word * word = read_dwarf_word(addr);
  437. if(word)
  438. result.append(" " + capitalize(word->base()));
  439. //Unknown
  440. word = read_dwarf_word(addr + 0x04);
  441. if(word)
  442. result.append(" " + capitalize(word->base()));
  443. //Verb
  444. word = read_dwarf_word(addr + 0x08);
  445. if(word) {
  446. result.append(" " + capitalize(word->adjective()));
  447. }
  448. //Unknown
  449. word = read_dwarf_word(addr + 0x0C);
  450. if(word)
  451. result.append(" " + capitalize(word->base()));
  452. //Unknown
  453. word = read_dwarf_word(addr + 0x10);
  454. if(word)
  455. result.append(" " + capitalize(word->base()));
  456. //Noun
  457. word = read_dwarf_word(addr + 0x14);
  458. bool singular = false;
  459. if(word) {
  460. if(word->plural_noun().isEmpty()) {
  461. result.append(" " + capitalize(word->noun()));
  462. singular = true;
  463. } else {
  464. result.append(" " + capitalize(word->plural_noun()));
  465. }
  466. }
  467. //of verb(noun)
  468. word = read_dwarf_word(addr + 0x18);
  469. if(word) {
  470. if( !word->verb().isEmpty() ) {
  471. if(singular) {
  472. result.append(" of " + capitalize(word->verb()));
  473. } else {
  474. result.append(" of " + capitalize(word->present_participle_verb()));
  475. }
  476. } else {
  477. if(singular) {
  478. result.append(" of " + capitalize(word->noun()));
  479. } else {
  480. result.append(" of " + capitalize(word->plural_noun()));
  481. }
  482. }
  483. }
  484. return result.trimmed();
  485. }
  486. QVector<VIRTADDR> DFInstance::find_vectors_in_range(const int &max_entries,
  487. const VIRTADDR &start_address,
  488. const int &range_length) {
  489. QByteArray data = get_data(start_address, range_length);
  490. QVector<VIRTADDR> vectors;
  491. VIRTADDR int1 = 0; // holds the start val
  492. VIRTADDR int2 = 0; // holds the end val
  493. for (int i = 0; i < range_length; i += 4) {
  494. memcpy(&int1, data.data() + i, 4);
  495. memcpy(&int2, data.data() + i + 4, 4);
  496. if (int2 >= int1 && is_valid_address(int1) && is_valid_address(int2)) {
  497. int bytes = int2 - int1;
  498. int entries = bytes / 4;
  499. if (entries > 0 && entries <= max_entries) {
  500. VIRTADDR vector_addr = start_address + i - VECTOR_POINTER_OFFSET;
  501. QVector<VIRTADDR> addrs = enumerate_vector(vector_addr);
  502. bool all_valid = true;
  503. foreach(VIRTADDR vec_entry, addrs) {
  504. if (!is_valid_address(vec_entry)) {
  505. all_valid = false;
  506. break;
  507. }
  508. }
  509. if (all_valid) {
  510. vectors << vector_addr;
  511. }
  512. }
  513. }
  514. }
  515. return vectors;
  516. }
  517. QVector<VIRTADDR> DFInstance::find_vectors(int num_entries, int fuzz/* =0 */,
  518. int entry_size/* =4 */) {
  519. /*
  520. glibc++ does vectors like so...
  521. |4bytes | 4bytes | 4bytes
  522. START_ADDRESS|END_ADDRESS|END_ALLOCATOR
  523. MSVC++ does vectors like so...
  524. | 4bytes | 4bytes | 4 bytes | 4bytes
  525. ALLOCATOR |START_ADDRESS|END_ADDRESS|END_ALLOCATOR
  526. */
  527. m_stop_scan = false; //! if ever set true, bail from the inner loop
  528. QVector<VIRTADDR> vectors; //! return value collection of vectors found
  529. VIRTADDR int1 = 0; // holds the start val
  530. VIRTADDR int2 = 0; // holds the end val
  531. // progress reporting
  532. m_scan_speed_timer->start(500);
  533. m_memory_remap_timer->stop(); // don't remap segments while scanning
  534. int total_bytes = 0;
  535. m_bytes_scanned = 0; // for global timings
  536. int bytes_scanned = 0; // for progress calcs
  537. foreach(MemorySegment *seg, m_regions) {
  538. total_bytes += seg->size;
  539. }
  540. int report_every_n_bytes = total_bytes / 1000;
  541. emit scan_total_steps(1000);
  542. emit scan_progress(0);
  543. int scan_step_size = 0x10000;
  544. QByteArray buffer(scan_step_size, '\0');
  545. QTime timer;
  546. timer.start();
  547. attach();
  548. foreach(MemorySegment *seg, m_regions) {
  549. //TRACE << "SCANNING REGION" << hex << seg->start_addr << "-"
  550. // << seg->end_addr << "BYTES:" << dec << seg->size;
  551. if ((int)seg->size <= scan_step_size) {
  552. scan_step_size = seg->size;
  553. }
  554. int scan_steps = seg->size / scan_step_size;
  555. if (seg->size % scan_step_size) {
  556. scan_steps++;
  557. }
  558. VIRTADDR addr = 0; // the ptr we will read from
  559. for(int step = 0; step < scan_steps; ++step) {
  560. addr = seg->start_addr + (scan_step_size * step);
  561. //LOGD << "starting scan for vectors at" << hex << addr << "step"
  562. // << dec << step << "of" << scan_steps;
  563. int bytes_read = read_raw(addr, scan_step_size, buffer);
  564. if (bytes_read < scan_step_size) {
  565. continue;
  566. }
  567. for(int offset = 0; offset < scan_step_size; offset += entry_size) {
  568. int1 = decode_int(buffer.mid(offset, entry_size));
  569. int2 = decode_int(buffer.mid(offset + entry_size, entry_size));
  570. if (int1 && int2 && int2 >= int1
  571. && int1 % 4 == 0
  572. && int2 % 4 == 0
  573. //&& is_valid_address(int1)
  574. //&& is_valid_address(int2)
  575. ) {
  576. int bytes = int2 - int1;
  577. int entries = bytes / entry_size;
  578. int diff = entries - num_entries;
  579. if (qAbs(diff) <= fuzz) {
  580. VIRTADDR vector_addr = addr + offset -
  581. VECTOR_POINTER_OFFSET;
  582. QVector<VIRTADDR> addrs = enumerate_vector(vector_addr);
  583. diff = addrs.size() - num_entries;
  584. if (qAbs(diff) <= fuzz) {
  585. vectors << vector_addr;
  586. }
  587. }
  588. }
  589. m_bytes_scanned += entry_size;
  590. bytes_scanned += entry_size;
  591. if (m_stop_scan)
  592. break;
  593. }
  594. emit scan_progress(bytes_scanned / report_every_n_bytes);
  595. DT->processEvents();
  596. if (m_stop_scan)
  597. break;
  598. }
  599. }
  600. detach();
  601. m_memory_remap_timer->start(20000); // start the remapper again
  602. m_scan_speed_timer->stop();
  603. LOGD << QString("Scanned %L1MB in %L2ms").arg(bytes_scanned / 1024 * 1024)
  604. .arg(timer.elapsed());
  605. emit scan_progress(100);
  606. return vectors;
  607. }
  608. QVector<VIRTADDR> DFInstance::find_vectors_ext(int num_entries, const char op,
  609. const uint start_addr, const uint end_addr, int entry_size/* =4 */) {
  610. /*
  611. glibc++ does vectors like so...
  612. |4bytes | 4bytes | 4bytes
  613. START_ADDRESS|END_ADDRESS|END_ALLOCATOR
  614. MSVC++ does vectors like so...
  615. | 4bytes | 4bytes | 4 bytes | 4bytes
  616. ALLOCATOR |START_ADDRESS|END_ADDRESS|END_ALLOCATOR
  617. */
  618. m_stop_scan = false; //! if ever set true, bail from the inner loop
  619. QVector<VIRTADDR> vectors; //! return value collection of vectors found
  620. VIRTADDR int1 = 0; // holds the start val
  621. VIRTADDR int2 = 0; // holds the end val
  622. // progress reporting
  623. m_scan_speed_timer->start(500);
  624. m_memory_remap_timer->stop(); // don't remap segments while scanning
  625. int total_bytes = 0;
  626. m_bytes_scanned = 0; // for global timings
  627. int bytes_scanned = 0; // for progress calcs
  628. foreach(MemorySegment *seg, m_regions) {
  629. total_bytes += seg->size;
  630. }
  631. int report_every_n_bytes = total_bytes / 1000;
  632. emit scan_total_steps(1000);
  633. emit scan_progress(0);
  634. int scan_step_size = 0x10000;
  635. QByteArray buffer(scan_step_size, '\0');
  636. QTime timer;
  637. timer.start();
  638. attach();
  639. foreach(MemorySegment *seg, m_regions) {
  640. //TRACE << "SCANNING REGION" << hex << seg->start_addr << "-"
  641. // << seg->end_addr << "BYTES:" << dec << seg->size;
  642. if ((int)seg->size <= scan_step_size) {
  643. scan_step_size = seg->size;
  644. }
  645. int scan_steps = seg->size / scan_step_size;
  646. if (seg->size % scan_step_size) {
  647. scan_steps++;
  648. }
  649. if( seg->end_addr < start_addr ) {
  650. continue;
  651. }
  652. if( seg->start_addr > end_addr ) {
  653. break;
  654. }
  655. VIRTADDR addr = 0; // the ptr we will read from
  656. for(int step = 0; step < scan_steps; ++step) {
  657. addr = seg->start_addr + (scan_step_size * step);
  658. //LOGD << "starting scan for vectors at" << hex << addr << "step"
  659. // << dec << step << "of" << scan_steps;
  660. int bytes_read = read_raw(addr, scan_step_size, buffer);
  661. if (bytes_read < scan_step_size) {
  662. continue;
  663. }
  664. for(int offset = 0; offset < scan_step_size; offset += entry_size) {
  665. VIRTADDR vector_addr = addr + offset - VECTOR_POINTER_OFFSET;
  666. if( vector_addr < start_addr ) {
  667. continue;
  668. }
  669. if( vector_addr > end_addr ) {
  670. m_stop_scan = true;
  671. break;
  672. }
  673. int1 = decode_int(buffer.mid(offset, entry_size));
  674. int2 = decode_int(buffer.mid(offset + entry_size, entry_size));
  675. if (int1 && int2 && int2 >= int1
  676. && int1 % 4 == 0
  677. && int2 % 4 == 0
  678. //&& is_valid_address(int1)
  679. //&& is_valid_address(int2)
  680. ) {
  681. int bytes = int2 - int1;
  682. int entries = bytes / entry_size;
  683. if (entries > 0 && entries < 1000) {
  684. QVector<VIRTADDR> addrs = enumerate_vector(vector_addr);
  685. if( (op == '=' && addrs.size() == num_entries)
  686. || (op == '<' && addrs.size() < num_entries)
  687. || (op == '>' && addrs.size() > num_entries) ) {
  688. vectors << vector_addr;
  689. }
  690. }
  691. }
  692. m_bytes_scanned += entry_size;
  693. bytes_scanned += entry_size;
  694. if (m_stop_scan)
  695. break;
  696. }
  697. emit scan_progress(bytes_scanned / report_every_n_bytes);
  698. DT->processEvents();
  699. if (m_stop_scan)
  700. break;
  701. }
  702. }
  703. detach();
  704. m_memory_remap_timer->start(20000); // start the remapper again
  705. m_scan_speed_timer->stop();
  706. LOGD << QString("Scanned %L1MB in %L2ms").arg(bytes_scanned / 1024 * 1024)
  707. .arg(timer.elapsed());
  708. emit scan_progress(100);
  709. return vectors;
  710. }
  711. QVector<VIRTADDR> DFInstance::find_vectors(int num_entries, const QVector<VIRTADDR> & search_set,
  712. int fuzz/* =0 */, int entry_size/* =4 */) {
  713. m_stop_scan = false; //! if ever set true, bail from the inner loop
  714. QVector<VIRTADDR> vectors; //! return value collection of vectors found
  715. // progress reporting
  716. m_scan_speed_timer->start(500);
  717. m_memory_remap_timer->stop(); // don't remap segments while scanning
  718. int total_vectors = vectors.size();
  719. m_bytes_scanned = 0; // for global timings
  720. int vectors_scanned = 0; // for progress calcs
  721. emit scan_total_steps(total_vectors);
  722. emit scan_progress(0);
  723. QTime timer;
  724. timer.start();
  725. attach();
  726. int vector_size = 8 + VECTOR_POINTER_OFFSET;
  727. QByteArray buffer(vector_size, '\0');
  728. foreach(VIRTADDR addr, search_set) {
  729. int bytes_read = read_raw(addr, vector_size, buffer);
  730. if (bytes_read < vector_size) {
  731. continue;
  732. }
  733. VIRTADDR int1 = 0; // holds the start val
  734. VIRTADDR int2 = 0; // holds the end val
  735. int1 = decode_int(buffer.mid(VECTOR_POINTER_OFFSET, sizeof(VIRTADDR)));
  736. int2 = decode_int(buffer.mid(VECTOR_POINTER_OFFSET+ sizeof(VIRTADDR), sizeof(VIRTADDR)));
  737. if (int1 && int2 && int2 >= int1
  738. && int1 % 4 == 0
  739. && int2 % 4 == 0) {
  740. int bytes = int2 - int1;
  741. int entries = bytes / entry_size;
  742. int diff = entries - num_entries;
  743. if (qAbs(diff) <= fuzz) {
  744. QVector<VIRTADDR> addrs = enumerate_vector(addr);
  745. diff = addrs.size() - num_entries;
  746. if (qAbs(diff) <= fuzz) {
  747. vectors << addr;
  748. }
  749. }
  750. }
  751. vectors_scanned++;
  752. if(vectors_scanned % 100 == 0) {
  753. emit scan_progress(vectors_scanned);
  754. DT->processEvents();
  755. }
  756. if (m_stop_scan)
  757. break;
  758. }
  759. detach();
  760. m_memory_remap_timer->start(20000); // start the remapper again
  761. m_scan_speed_timer->stop();
  762. LOGD << QString("Scanned %L1 vectors in %L2ms").arg(vectors_scanned)
  763. .arg(timer.elapsed());
  764. emit scan_progress(100);
  765. return vectors;
  766. }
  767. MemoryLayout *DFInstance::get_memory_layout(QString checksum, bool) {
  768. checksum = checksum.toLower();
  769. LOGD << "DF's checksum is:" << checksum;
  770. MemoryLayout *ret_val = NULL;
  771. ret_val = m_memory_layouts.value(checksum, NULL);
  772. m_is_ok = ret_val != NULL && ret_val->is_valid();
  773. if(!m_is_ok) {
  774. LOGD << "Could not find layout for checksum" << checksum;
  775. DT->get_main_window()->check_for_layout(checksum);
  776. }
  777. if (m_is_ok) {
  778. LOGI << "Detected Dwarf Fortress version"
  779. << ret_val->game_version() << "using MemoryLayout from"
  780. << ret_val->filename();
  781. }
  782. return ret_val;
  783. }
  784. bool DFInstance::add_new_layout(const QString & version, QFile & file) {
  785. QString newFileName = version;
  786. newFileName.replace("(", "").replace(")", "").replace(" ", "_");
  787. newFileName += ".ini";
  788. QFileInfo newFile(QDir(QString("etc/memory_layouts/%1").arg(LAYOUT_SUBDIR)), newFileName);
  789. newFileName = newFile.absoluteFilePath();
  790. if(!file.exists()) {
  791. LOGW << "Layout file" << file.fileName() << "does not exist!";
  792. return false;
  793. }
  794. LOGD << "Copying: " << file.fileName() << " to " << newFileName;
  795. if(!file.copy(newFileName)) {
  796. LOGW << "Error renaming layout file!";
  797. return false;
  798. }
  799. MemoryLayout *temp = new MemoryLayout(newFileName);
  800. if (temp && temp->is_valid()) {
  801. LOGD << "adding valid layout" << temp->game_version() << temp->checksum();
  802. m_memory_layouts.insert(temp->checksum().toLower(), temp);
  803. }
  804. return true;
  805. }
  806. void DFInstance::layout_not_found(const QString & checksum) {
  807. QString supported_vers;
  808. // TODO: Replace this with a rich dialog at some point that
  809. // is also accessible from the help menu. For now, remove the
  810. // extra path information as the dialog is getting way too big.
  811. // And make a half-ass attempt at sorting
  812. QList<MemoryLayout *> layouts = m_memory_layouts.values();
  813. qSort(layouts);
  814. foreach(MemoryLayout * l, layouts) {
  815. supported_vers.append(
  816. QString("<li><b>%1</b>(<font color=\"#444444\">%2"
  817. "</font>)</li>")
  818. .arg(l->game_version())
  819. .arg(l->checksum()));
  820. }
  821. QMessageBox *mb = new QMessageBox(qApp->activeWindow());
  822. mb->setIcon(QMessageBox::Critical);
  823. mb->setWindowTitle(tr("Unidentified Game Version"));
  824. mb->setText(tr("I'm sorry but I don't know how to talk to this "
  825. "version of Dwarf Fortress! (checksum:%1)<br><br> <b>Supported "
  826. "Versions:</b><ul>%2</ul>").arg(checksum).arg(supported_vers));
  827. mb->setInformativeText(tr("<a href=\"%1\">Click Here to find out "
  828. "more online</a>.")
  829. .arg(URL_SUPPORTED_GAME_VERSIONS));
  830. /*
  831. mb->setDetailedText(tr("Failed to locate a memory layout file for "
  832. "Dwarf Fortress exectutable with checksum '%1'").arg(checksum));
  833. */
  834. mb->exec();
  835. LOGE << tr("unable to identify version from checksum:") << checksum;
  836. }
  837. void DFInstance::calculate_scan_rate() {
  838. float rate = (m_bytes_scanned / 1024.0f / 1024.0f) /
  839. (m_scan_speed_timer->interval() / 1000.0f);
  840. QString msg = QString("%L1MB/s").arg(rate);
  841. emit scan_message(msg);
  842. m_bytes_scanned = 0;
  843. }