No Description

project.cpp 56KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529
  1. #include "parseutil.h"
  2. #include "project.h"
  3. #include "tile.h"
  4. #include "tileset.h"
  5. #include "event.h"
  6. #include <QDebug>
  7. #include <QDir>
  8. #include <QFile>
  9. #include <QTextStream>
  10. #include <QStandardItem>
  11. #include <QMessageBox>
  12. #include <QRegularExpression>
  13. Project::Project()
  14. {
  15. groupNames = new QStringList;
  16. map_groups = new QMap<QString, int>;
  17. mapNames = new QStringList;
  18. regionMapSections = new QStringList;
  19. itemNames = new QStringList;
  20. flagNames = new QStringList;
  21. varNames = new QStringList;
  22. movementTypes = new QStringList;
  23. mapTypes = new QStringList;
  24. mapBattleScenes = new QStringList;
  25. weatherNames = new QStringList;
  26. coordEventWeatherNames = new QStringList;
  27. secretBaseIds = new QStringList;
  28. bgEventFacingDirections = new QStringList;
  29. map_cache = new QMap<QString, Map*>;
  30. mapConstantsToMapNames = new QMap<QString, QString>;
  31. mapNamesToMapConstants = new QMap<QString, QString>;
  32. tileset_cache = new QMap<QString, Tileset*>;
  33. }
  34. QString Project::getProjectTitle() {
  35. if (!root.isNull()) {
  36. return root.section('/', -1);
  37. } else {
  38. return QString();
  39. }
  40. }
  41. Map* Project::loadMap(QString map_name) {
  42. Map *map;
  43. if (map_cache->contains(map_name)) {
  44. map = map_cache->value(map_name);
  45. // TODO: uncomment when undo/redo history is fully implemented for all actions.
  46. if (true/*map->hasUnsavedChanges()*/) {
  47. return map;
  48. }
  49. } else {
  50. map = new Map;
  51. map->setName(map_name);
  52. }
  53. readMapHeader(map);
  54. readMapLayout(map);
  55. readMapEvents(map);
  56. loadMapConnections(map);
  57. map->commit();
  58. map->history.save();
  59. map_cache->insert(map_name, map);
  60. return map;
  61. }
  62. void Project::loadMapConnections(Map *map) {
  63. if (!map->isPersistedToFile) {
  64. return;
  65. }
  66. map->connections.clear();
  67. if (!map->connections_label.isNull()) {
  68. QString path = root + QString("/data/maps/%1/connections.inc").arg(map->name);
  69. QString text = readTextFile(path);
  70. if (!text.isNull()) {
  71. QList<QStringList> *commands = parseAsm(text);
  72. QStringList *list = getLabelValues(commands, map->connections_label);
  73. //// Avoid using this value. It ought to be generated instead.
  74. //int num_connections = list->value(0).toInt(nullptr, 0);
  75. QString connections_list_label = list->value(1);
  76. QList<QStringList> *connections = getLabelMacros(commands, connections_list_label);
  77. for (QStringList command : *connections) {
  78. QString macro = command.value(0);
  79. if (macro == "connection") {
  80. Connection *connection = new Connection;
  81. connection->direction = command.value(1);
  82. connection->offset = command.value(2);
  83. QString mapConstant = command.value(3);
  84. if (mapConstantsToMapNames->contains(mapConstant)) {
  85. connection->map_name = mapConstantsToMapNames->value(mapConstant);
  86. map->connections.append(connection);
  87. } else {
  88. qDebug() << QString("Failed to find connected map for map constant '%1'").arg(mapConstant);
  89. }
  90. }
  91. }
  92. }
  93. }
  94. }
  95. void Project::setNewMapConnections(Map *map) {
  96. map->connections.clear();
  97. }
  98. QList<QStringList>* Project::getLabelMacros(QList<QStringList> *list, QString label) {
  99. bool in_label = false;
  100. QList<QStringList> *new_list = new QList<QStringList>;
  101. for (int i = 0; i < list->length(); i++) {
  102. QStringList params = list->value(i);
  103. QString macro = params.value(0);
  104. if (macro == ".label") {
  105. if (params.value(1) == label) {
  106. in_label = true;
  107. } else if (in_label) {
  108. // If nothing has been read yet, assume the label
  109. // we're looking for is in a stack of labels.
  110. if (new_list->length() > 0) {
  111. break;
  112. }
  113. }
  114. } else if (in_label) {
  115. new_list->append(params);
  116. }
  117. }
  118. return new_list;
  119. }
  120. // For if you don't care about filtering by macro,
  121. // and just want all values associated with some label.
  122. QStringList* Project::getLabelValues(QList<QStringList> *list, QString label) {
  123. list = getLabelMacros(list, label);
  124. QStringList *values = new QStringList;
  125. for (int i = 0; i < list->length(); i++) {
  126. QStringList params = list->value(i);
  127. QString macro = params.value(0);
  128. // Ignore .align
  129. if (macro == ".align")
  130. continue;
  131. if (macro == ".ifdef")
  132. continue;
  133. if (macro == ".ifndef")
  134. continue;
  135. for (int j = 1; j < params.length(); j++) {
  136. values->append(params.value(j));
  137. }
  138. }
  139. return values;
  140. }
  141. void Project::readMapHeader(Map* map) {
  142. if (!map->isPersistedToFile) {
  143. return;
  144. }
  145. QString label = map->name;
  146. ParseUtil *parser = new ParseUtil;
  147. QString header_text = readTextFile(root + "/data/maps/" + label + "/header.inc");
  148. if (header_text.isNull()) {
  149. return;
  150. }
  151. QStringList *header = getLabelValues(parser->parseAsm(header_text), label);
  152. map->layout_label = header->value(0);
  153. map->events_label = header->value(1);
  154. map->scripts_label = header->value(2);
  155. map->connections_label = header->value(3);
  156. map->song = header->value(4);
  157. map->layout_id = header->value(5);
  158. map->location = header->value(6);
  159. map->requiresFlash = header->value(7);
  160. map->weather = header->value(8);
  161. map->type = header->value(9);
  162. map->unknown = header->value(10);
  163. map->show_location = header->value(11);
  164. map->battle_scene = header->value(12);
  165. }
  166. void Project::setNewMapHeader(Map* map, int mapIndex) {
  167. map->layout_label = QString("%1_Layout").arg(map->name);
  168. map->events_label = QString("%1_MapEvents").arg(map->name);;
  169. map->scripts_label = QString("%1_MapScripts").arg(map->name);;
  170. map->connections_label = "0x0";
  171. map->song = "MUS_DAN02";
  172. map->layout_id = QString("%1").arg(mapIndex);
  173. map->location = "MAPSEC_LITTLEROOT_TOWN";
  174. map->requiresFlash = "FALSE";
  175. map->weather = "WEATHER_SUNNY";
  176. map->type = "MAP_TYPE_TOWN";
  177. map->unknown = "0";
  178. map->show_location = "TRUE";
  179. map->battle_scene = "MAP_BATTLE_SCENE_NORMAL";
  180. }
  181. void Project::saveMapHeader(Map *map) {
  182. QString label = map->name;
  183. QString header_path = root + "/data/maps/" + label + "/header.inc";
  184. QString text = "";
  185. text += QString("%1::\n").arg(label);
  186. text += QString("\t.4byte %1\n").arg(map->layout_label);
  187. text += QString("\t.4byte %1\n").arg(map->events_label);
  188. text += QString("\t.4byte %1\n").arg(map->scripts_label);
  189. if (map->connections.length() == 0) {
  190. map->connections_label = "0x0";
  191. } else {
  192. map->connections_label = QString("%1_MapConnections").arg(map->name);
  193. }
  194. text += QString("\t.4byte %1\n").arg(map->connections_label);
  195. text += QString("\t.2byte %1\n").arg(map->song);
  196. text += QString("\t.2byte %1\n").arg(map->layout_id);
  197. text += QString("\t.byte %1\n").arg(map->location);
  198. text += QString("\t.byte %1\n").arg(map->requiresFlash);
  199. text += QString("\t.byte %1\n").arg(map->weather);
  200. text += QString("\t.byte %1\n").arg(map->type);
  201. text += QString("\t.2byte %1\n").arg(map->unknown);
  202. text += QString("\t.byte %1\n").arg(map->show_location);
  203. text += QString("\t.byte %1\n").arg(map->battle_scene);
  204. saveTextFile(header_path, text);
  205. }
  206. void Project::saveMapConnections(Map *map) {
  207. QString path = root + "/data/maps/" + map->name + "/connections.inc";
  208. if (map->connections.length() > 0) {
  209. QString text = "";
  210. QString connectionsListLabel = QString("%1_MapConnectionsList").arg(map->name);
  211. int numValidConnections = 0;
  212. text += QString("%1::\n").arg(connectionsListLabel);
  213. for (Connection* connection : map->connections) {
  214. if (mapNamesToMapConstants->contains(connection->map_name)) {
  215. text += QString("\tconnection %1, %2, %3\n")
  216. .arg(connection->direction)
  217. .arg(connection->offset)
  218. .arg(mapNamesToMapConstants->value(connection->map_name));
  219. numValidConnections++;
  220. } else {
  221. qDebug() << QString("Failed to write map connection. %1 not a valid map name").arg(connection->map_name);
  222. }
  223. }
  224. text += QString("\n");
  225. text += QString("%1::\n").arg(map->connections_label);
  226. text += QString("\t.4byte %1\n").arg(numValidConnections);
  227. text += QString("\t.4byte %1\n").arg(connectionsListLabel);
  228. saveTextFile(path, text);
  229. } else {
  230. deleteFile(path);
  231. }
  232. updateMapsWithConnections(map);
  233. }
  234. void Project::updateMapsWithConnections(Map *map) {
  235. if (map->connections.length() > 0) {
  236. if (!mapsWithConnections.contains(map->name)) {
  237. mapsWithConnections.append(map->name);
  238. }
  239. } else {
  240. if (mapsWithConnections.contains(map->name)) {
  241. mapsWithConnections.removeOne(map->name);
  242. }
  243. }
  244. }
  245. void Project::readMapLayoutsTable() {
  246. int curIndex = 1;
  247. QString layoutsText = readTextFile(getMapLayoutsTableFilepath());
  248. QList<QStringList>* values = parseAsm(layoutsText);
  249. bool inLayoutPointers = false;
  250. for (int i = 0; i < values->length(); i++) {
  251. QStringList params = values->value(i);
  252. QString macro = params.value(0);
  253. if (macro == ".label") {
  254. if (inLayoutPointers) {
  255. break;
  256. }
  257. if (params.value(1) == "gMapLayouts") {
  258. inLayoutPointers = true;
  259. }
  260. } else if (macro == ".4byte" && inLayoutPointers) {
  261. QString layoutName = params.value(1);
  262. mapLayoutsTable.append(layoutName);
  263. }
  264. }
  265. // Deep copy
  266. mapLayoutsTableMaster = mapLayoutsTable;
  267. mapLayoutsTableMaster.detach();
  268. }
  269. void Project::saveMapLayoutsTable() {
  270. QString text = "";
  271. text += QString("\t.align 2\n");
  272. text += QString("gMapLayouts::\n");
  273. for (QString layoutName : mapLayoutsTableMaster) {
  274. text += QString("\t.4byte %1\n").arg(layoutName);
  275. }
  276. saveTextFile(getMapLayoutsTableFilepath(), text);
  277. }
  278. QString Project::getMapLayoutsTableFilepath() {
  279. return QString("%1/data/layouts_table.inc").arg(root);
  280. }
  281. QStringList* Project::readLayoutValues(QString layoutLabel) {
  282. ParseUtil *parser = new ParseUtil;
  283. QString layoutText = readTextFile(getMapLayoutFilepath(layoutLabel));
  284. if (layoutText.isNull()) {
  285. return NULL;
  286. }
  287. QStringList *layoutValues = getLabelValues(parser->parseAsm(layoutText), layoutLabel);
  288. QString borderLabel = layoutValues->value(2);
  289. QString blockdataLabel = layoutValues->value(3);
  290. QStringList *borderValues = getLabelValues(parser->parseAsm(layoutText), borderLabel);
  291. QString borderPath = borderValues->value(0).replace("\"", "");
  292. layoutValues->append(borderPath);
  293. QStringList *blockdataValues = getLabelValues(parser->parseAsm(layoutText), blockdataLabel);
  294. QString blockdataPath = blockdataValues->value(0).replace("\"", "");
  295. layoutValues->append(blockdataPath);
  296. if (layoutValues->size() != 8) {
  297. qDebug() << "Error: Unexpected number of properties in layout '" << layoutLabel << "'";
  298. return NULL;
  299. }
  300. return layoutValues;
  301. }
  302. void Project::readMapLayout(Map* map) {
  303. if (!map->isPersistedToFile) {
  304. return;
  305. }
  306. MapLayout *layout;
  307. if (!mapLayouts.contains(map->layout_label)) {
  308. QStringList *layoutValues = readLayoutValues(map->layout->label);
  309. if (layoutValues == NULL) {
  310. return;
  311. }
  312. layout = new MapLayout();
  313. mapLayouts.insert(map->layout_label, layout);
  314. layout->name = MapLayout::getNameFromLabel(map->layout_label);
  315. layout->label = map->layout_label;
  316. layout->width = layoutValues->value(0);
  317. layout->height = layoutValues->value(1);
  318. layout->border_label = layoutValues->value(2);
  319. layout->blockdata_label = layoutValues->value(3);
  320. layout->tileset_primary_label = layoutValues->value(4);
  321. layout->tileset_secondary_label = layoutValues->value(5);
  322. layout->border_path = layoutValues->value(6);
  323. layout->blockdata_path = layoutValues->value(7);
  324. map->layout = layout;
  325. } else {
  326. map->layout = mapLayouts[map->layout_label];
  327. }
  328. loadMapTilesets(map);
  329. loadBlockdata(map);
  330. loadMapBorder(map);
  331. }
  332. void Project::readAllMapLayouts() {
  333. mapLayouts.clear();
  334. for (int i = 0; i < mapLayoutsTable.size(); i++) {
  335. QString layoutLabel = mapLayoutsTable[i];
  336. QStringList *layoutValues = readLayoutValues(layoutLabel);
  337. if (layoutValues == NULL) {
  338. return;
  339. }
  340. MapLayout *layout = new MapLayout();
  341. layout->name = MapLayout::getNameFromLabel(layoutLabel);
  342. layout->label = layoutLabel;
  343. layout->index = i;
  344. layout->width = layoutValues->value(0);
  345. layout->height = layoutValues->value(1);
  346. layout->border_label = layoutValues->value(2);
  347. layout->blockdata_label = layoutValues->value(3);
  348. layout->tileset_primary_label = layoutValues->value(4);
  349. layout->tileset_secondary_label = layoutValues->value(5);
  350. layout->border_path = layoutValues->value(6);
  351. layout->blockdata_path = layoutValues->value(7);
  352. mapLayouts.insert(layoutLabel, layout);
  353. }
  354. // Deep copy
  355. mapLayoutsMaster = mapLayouts;
  356. mapLayoutsMaster.detach();
  357. }
  358. void Project::saveAllMapLayouts() {
  359. for (QString layoutName : mapLayoutsTableMaster) {
  360. MapLayout *layout = mapLayouts.value(layoutName);
  361. QString text = QString("%1::\n").arg(layout->border_label);
  362. text += QString("\t.incbin \"%1\"\n").arg(layout->border_path);
  363. text += QString("\n");
  364. text += QString("%1::\n").arg(layout->blockdata_label);
  365. text += QString("\t.incbin \"%1\"\n").arg(layout->blockdata_path);
  366. text += QString("\n");
  367. text += QString("\t.align 2\n");
  368. text += QString("%1::\n").arg(layoutName);
  369. text += QString("\t.4byte %1\n").arg(layout->width);
  370. text += QString("\t.4byte %1\n").arg(layout->height);
  371. text += QString("\t.4byte %1\n").arg(layout->border_label);
  372. text += QString("\t.4byte %1\n").arg(layout->blockdata_label);
  373. text += QString("\t.4byte %1\n").arg(layout->tileset_primary_label);
  374. text += QString("\t.4byte %1\n").arg(layout->tileset_secondary_label);
  375. text += QString("\n");
  376. saveTextFile(getMapLayoutFilepath(layout->label), text);
  377. }
  378. }
  379. QString Project::getMapLayoutFilepath(QString layoutLabel) {
  380. return QString("%1/data/layouts/%2/layout.inc").arg(root).arg(MapLayout::getNameFromLabel(layoutLabel));
  381. }
  382. void Project::setNewMapLayout(Map* map) {
  383. MapLayout *layout = new MapLayout();
  384. layout->label = QString("%1_Layout").arg(map->name);
  385. layout->name = MapLayout::getNameFromLabel(layout->label);
  386. layout->width = "20";
  387. layout->height = "20";
  388. layout->border_label = QString("%1_MapBorder").arg(map->name);
  389. layout->border_path = QString("data/layouts/%1/border.bin").arg(map->name);
  390. layout->blockdata_label = QString("%1_MapBlockdata").arg(map->name);
  391. layout->blockdata_path = QString("data/layouts/%1/map.bin").arg(map->name);
  392. layout->tileset_primary_label = "gTileset_General";
  393. layout->tileset_secondary_label = "gTileset_Petalburg";
  394. map->layout = layout;
  395. map->layout_label = layout->label;
  396. // Insert new entry into the global map layouts.
  397. mapLayouts.insert(layout->label, layout);
  398. mapLayoutsTable.append(layout->label);
  399. }
  400. void Project::saveMapGroupsTable() {
  401. QString text = "";
  402. int groupNum = 0;
  403. for (QStringList mapNames : groupedMapNames) {
  404. text += QString("\t.align 2\n");
  405. text += QString("gMapGroup%1::\n").arg(groupNum);
  406. for (QString mapName : mapNames) {
  407. text += QString("\t.4byte %1\n").arg(mapName);
  408. }
  409. text += QString("\n");
  410. groupNum++;
  411. }
  412. text += QString("\t.align 2\n");
  413. text += QString("gMapGroups::\n");
  414. for (int i = 0; i < groupNum; i++) {
  415. text += QString("\t.4byte gMapGroup%1\n").arg(i);
  416. }
  417. saveTextFile(root + "/data/maps/groups.inc", text);
  418. }
  419. void Project::saveMapConstantsHeader() {
  420. QString text = QString("#ifndef GUARD_CONSTANTS_MAPS_H\n");
  421. text += QString("#define GUARD_CONSTANTS_MAPS_H\n");
  422. text += QString("\n");
  423. int groupNum = 0;
  424. for (QStringList mapNames : groupedMapNames) {
  425. text += QString("// Map Group %1\n").arg(groupNum);
  426. int maxLength = 0;
  427. for (QString mapName : mapNames) {
  428. QString mapConstantName = mapNamesToMapConstants->value(mapName);
  429. if (mapConstantName.length() > maxLength)
  430. maxLength = mapConstantName.length();
  431. }
  432. int groupIndex = 0;
  433. for (QString mapName : mapNames) {
  434. QString mapConstantName = mapNamesToMapConstants->value(mapName);
  435. text += QString("#define %1%2(%3 | (%4 << 8))\n")
  436. .arg(mapConstantName)
  437. .arg(QString(" ").repeated(maxLength - mapConstantName.length() + 1))
  438. .arg(groupIndex)
  439. .arg(groupNum);
  440. groupIndex++;
  441. }
  442. text += QString("\n");
  443. groupNum++;
  444. }
  445. text += QString("\n");
  446. text += QString("#define MAP_NONE (0x7F | (0x7F << 8))\n");
  447. text += QString("#define MAP_UNDEFINED (0xFF | (0xFF << 8))\n\n\n");
  448. text += QString("#define MAP_GROUP(map) (MAP_##map >> 8)\n");
  449. text += QString("#define MAP_NUM(map) (MAP_##map & 0xFF)\n\n");
  450. text += QString("#endif // GUARD_CONSTANTS_MAPS_H\n");
  451. saveTextFile(root + "/include/constants/maps.h", text);
  452. }
  453. void Project::loadMapTilesets(Map* map) {
  454. if (map->layout->has_unsaved_changes) {
  455. return;
  456. }
  457. map->layout->tileset_primary = getTileset(map->layout->tileset_primary_label);
  458. map->layout->tileset_secondary = getTileset(map->layout->tileset_secondary_label);
  459. }
  460. Tileset* Project::loadTileset(QString label) {
  461. ParseUtil *parser = new ParseUtil;
  462. QString headers_text = readTextFile(root + "/data/tilesets/headers.inc");
  463. QStringList *values = getLabelValues(parser->parseAsm(headers_text), label);
  464. Tileset *tileset = new Tileset;
  465. tileset->name = label;
  466. tileset->is_compressed = values->value(0);
  467. tileset->is_secondary = values->value(1);
  468. tileset->padding = values->value(2);
  469. tileset->tiles_label = values->value(3);
  470. tileset->palettes_label = values->value(4);
  471. tileset->metatiles_label = values->value(5);
  472. tileset->metatile_attrs_label = values->value(6);
  473. tileset->callback_label = values->value(7);
  474. loadTilesetAssets(tileset);
  475. tileset_cache->insert(label, tileset);
  476. return tileset;
  477. }
  478. void Project::loadBlockdata(Map* map) {
  479. if (!map->isPersistedToFile || map->layout->has_unsaved_changes) {
  480. return;
  481. }
  482. QString path = QString("%1/%2").arg(root).arg(map->layout->blockdata_path);
  483. map->layout->blockdata = readBlockdata(path);
  484. }
  485. void Project::setNewMapBlockdata(Map* map) {
  486. Blockdata *blockdata = new Blockdata;
  487. for (int i = 0; i < map->getWidth() * map->getHeight(); i++) {
  488. blockdata->addBlock(qint16(0x3001));
  489. }
  490. map->layout->blockdata = blockdata;
  491. }
  492. void Project::loadMapBorder(Map *map) {
  493. if (!map->isPersistedToFile || map->layout->has_unsaved_changes) {
  494. return;
  495. }
  496. QString path = QString("%1/%2").arg(root).arg(map->layout->border_path);
  497. map->layout->border = readBlockdata(path);
  498. }
  499. void Project::setNewMapBorder(Map *map) {
  500. Blockdata *blockdata = new Blockdata;
  501. blockdata->addBlock(qint16(0x01D4));
  502. blockdata->addBlock(qint16(0x01D5));
  503. blockdata->addBlock(qint16(0x01DC));
  504. blockdata->addBlock(qint16(0x01DD));
  505. map->layout->border = blockdata;
  506. }
  507. void Project::saveMapBorder(Map *map) {
  508. QString path = QString("%1/%2").arg(root).arg(map->layout->border_path);
  509. writeBlockdata(path, map->layout->border);
  510. }
  511. void Project::saveBlockdata(Map* map) {
  512. QString path = QString("%1/%2").arg(root).arg(map->layout->blockdata_path);
  513. writeBlockdata(path, map->layout->blockdata);
  514. map->history.save();
  515. }
  516. void Project::writeBlockdata(QString path, Blockdata *blockdata) {
  517. QFile file(path);
  518. if (file.open(QIODevice::WriteOnly)) {
  519. QByteArray data = blockdata->serialize();
  520. file.write(data);
  521. } else {
  522. qDebug() << "Failed to open blockdata file for writing: '" << path << "'";
  523. }
  524. }
  525. void Project::saveAllMaps() {
  526. QList<QString> keys = map_cache->keys();
  527. for (int i = 0; i < keys.length(); i++) {
  528. QString key = keys.value(i);
  529. Map* map = map_cache->value(key);
  530. saveMap(map);
  531. }
  532. }
  533. void Project::saveMap(Map *map) {
  534. // Create/Modify a few collateral files for brand new maps.
  535. if (!map->isPersistedToFile) {
  536. QString newMapDataDir = QString(root + "/data/maps/%1").arg(map->name);
  537. if (!QDir::root().mkdir(newMapDataDir)) {
  538. qDebug() << "Error: failed to create directory for new map. " << newMapDataDir;
  539. }
  540. QString newLayoutDir = QString(root + "/data/layouts/%1").arg(map->name);
  541. if (!QDir::root().mkdir(newLayoutDir)) {
  542. qDebug() << "Error: failed to create directory for new layout. " << newLayoutDir;
  543. }
  544. // TODO: In the future, these files needs more structure to allow for proper parsing/saving.
  545. // Create file data/maps/<map_name>/scripts.inc
  546. QString text = QString("%1_MapScripts::\n\t.byte 0\n").arg(map->name);
  547. saveTextFile(root + "/data/maps/" + map->name + "/scripts.inc", text);
  548. // Create file data/maps/<map_name>/text.inc
  549. saveTextFile(root + "/data/maps/" + map->name + "/text.inc", "\n");
  550. // Simply append to data/event_scripts.s.
  551. text = QString("\n\t.include \"data/maps/%1/scripts.inc\"\n").arg(map->name);
  552. text += QString("\t.include \"data/maps/%1/text.inc\"\n").arg(map->name);
  553. appendTextFile(root + "/data/event_scripts.s", text);
  554. // Simply append to data/map_events.s.
  555. text = QString("\n\t.include \"data/maps/%1/events.inc\"\n").arg(map->name);
  556. appendTextFile(root + "/data/map_events.s", text);
  557. // Simply append to data/maps/headers.inc.
  558. text = QString("\t.include \"data/maps/%1/header.inc\"\n").arg(map->name);
  559. appendTextFile(root + "/data/maps/headers.inc", text);
  560. // Simply append to data/layouts.inc.
  561. text = QString("\t.include \"data/layouts/%1/layout.inc\"\n").arg(map->layout->name);
  562. appendTextFile(root + "/data/layouts.inc", text);
  563. }
  564. saveMapBorder(map);
  565. saveMapHeader(map);
  566. saveMapConnections(map);
  567. saveBlockdata(map);
  568. saveMapEvents(map);
  569. // Update global data structures with current map data.
  570. updateMapLayout(map);
  571. map->isPersistedToFile = true;
  572. map->layout->has_unsaved_changes = false;
  573. }
  574. void Project::updateMapLayout(Map* map) {
  575. if (!mapLayoutsTableMaster.contains(map->layout_label)) {
  576. mapLayoutsTableMaster.append(map->layout_label);
  577. }
  578. // Deep copy
  579. MapLayout *layout = mapLayouts.value(map->layout_label);
  580. MapLayout *newLayout = new MapLayout();
  581. *newLayout = *layout;
  582. mapLayoutsMaster.insert(map->layout_label, newLayout);
  583. }
  584. void Project::saveAllDataStructures() {
  585. saveMapLayoutsTable();
  586. saveAllMapLayouts();
  587. saveMapGroupsTable();
  588. saveMapConstantsHeader();
  589. saveMapsWithConnections();
  590. }
  591. void Project::loadTilesetAssets(Tileset* tileset) {
  592. ParseUtil* parser = new ParseUtil;
  593. QString category = (tileset->is_secondary == "TRUE") ? "secondary" : "primary";
  594. if (tileset->name.isNull()) {
  595. return;
  596. }
  597. QString dir_path = root + "/data/tilesets/" + category + "/" + tileset->name.replace("gTileset_", "").toLower();
  598. QString graphics_text = readTextFile(root + "/data/tilesets/graphics.inc");
  599. QList<QStringList> *graphics = parser->parseAsm(graphics_text);
  600. QStringList *tiles_values = getLabelValues(graphics, tileset->tiles_label);
  601. QStringList *palettes_values = getLabelValues(graphics, tileset->palettes_label);
  602. QString tiles_path;
  603. if (!tiles_values->isEmpty()) {
  604. tiles_path = root + "/" + tiles_values->value(0).section('"', 1, 1);
  605. } else {
  606. tiles_path = dir_path + "/tiles.4bpp";
  607. if (tileset->is_compressed == "TRUE") {
  608. tiles_path += ".lz";
  609. }
  610. }
  611. QStringList *palette_paths = new QStringList;
  612. if (!palettes_values->isEmpty()) {
  613. for (int i = 0; i < palettes_values->length(); i++) {
  614. QString value = palettes_values->value(i);
  615. palette_paths->append(root + "/" + value.section('"', 1, 1));
  616. }
  617. } else {
  618. QString palettes_dir_path = dir_path + "/palettes";
  619. for (int i = 0; i < 16; i++) {
  620. palette_paths->append(palettes_dir_path + "/" + QString("%1").arg(i, 2, 10, QLatin1Char('0')) + ".gbapal");
  621. }
  622. }
  623. QString metatiles_path;
  624. QString metatile_attrs_path;
  625. QString metatiles_text = readTextFile(root + "/data/tilesets/metatiles.inc");
  626. QList<QStringList> *metatiles_macros = parser->parseAsm(metatiles_text);
  627. QStringList *metatiles_values = getLabelValues(metatiles_macros, tileset->metatiles_label);
  628. if (!metatiles_values->isEmpty()) {
  629. metatiles_path = root + "/" + metatiles_values->value(0).section('"', 1, 1);
  630. } else {
  631. metatiles_path = dir_path + "/metatiles.bin";
  632. }
  633. QStringList *metatile_attrs_values = getLabelValues(metatiles_macros, tileset->metatile_attrs_label);
  634. if (!metatile_attrs_values->isEmpty()) {
  635. metatile_attrs_path = root + "/" + metatile_attrs_values->value(0).section('"', 1, 1);
  636. } else {
  637. metatile_attrs_path = dir_path + "/metatile_attributes.bin";
  638. }
  639. // tiles
  640. tiles_path = fixGraphicPath(tiles_path);
  641. QImage *image = new QImage(tiles_path);
  642. //image->setColor(0, qRgb(0xff, 0, 0)); // debug
  643. QList<QImage> *tiles = new QList<QImage>;
  644. int w = 8;
  645. int h = 8;
  646. for (int y = 0; y < image->height(); y += h)
  647. for (int x = 0; x < image->width(); x += w) {
  648. QImage tile = image->copy(x, y, w, h);
  649. tiles->append(tile);
  650. }
  651. tileset->tiles = tiles;
  652. // metatiles
  653. QFile metatiles_file(metatiles_path);
  654. if (metatiles_file.open(QIODevice::ReadOnly)) {
  655. QByteArray data = metatiles_file.readAll();
  656. int num_metatiles = data.length() / 16;
  657. int num_layers = 2;
  658. QList<Metatile*> *metatiles = new QList<Metatile*>;
  659. for (int i = 0; i < num_metatiles; i++) {
  660. Metatile *metatile = new Metatile;
  661. int index = i * (2 * 4 * num_layers);
  662. for (int j = 0; j < 4 * num_layers; j++) {
  663. uint16_t word = data[index++] & 0xff;
  664. word += (data[index++] & 0xff) << 8;
  665. Tile tile;
  666. tile.tile = word & 0x3ff;
  667. tile.xflip = (word >> 10) & 1;
  668. tile.yflip = (word >> 11) & 1;
  669. tile.palette = (word >> 12) & 0xf;
  670. metatile->tiles->append(tile);
  671. }
  672. metatiles->append(metatile);
  673. }
  674. tileset->metatiles = metatiles;
  675. } else {
  676. tileset->metatiles = new QList<Metatile*>;
  677. qDebug() << QString("Could not open '%1'").arg(metatiles_path);
  678. }
  679. QFile attrs_file(metatile_attrs_path);
  680. //qDebug() << metatile_attrs_path;
  681. if (attrs_file.open(QIODevice::ReadOnly)) {
  682. QByteArray data = attrs_file.readAll();
  683. int num_metatiles = data.length() / 2;
  684. for (int i = 0; i < num_metatiles; i++) {
  685. uint16_t word = data[i*2] & 0xff;
  686. word += (data[i*2 + 1] & 0xff) << 8;
  687. tileset->metatiles->value(i)->attr = word;
  688. }
  689. } else {
  690. qDebug() << QString("Could not open '%1'").arg(metatile_attrs_path);
  691. }
  692. // palettes
  693. QList<QList<QRgb>> *palettes = new QList<QList<QRgb>>;
  694. for (int i = 0; i < palette_paths->length(); i++) {
  695. QString path = palette_paths->value(i);
  696. // the palettes are not compressed. this should never happen. it's only a precaution.
  697. path = path.replace(QRegExp("\\.lz$"), "");
  698. // TODO default to .pal (JASC-PAL)
  699. // just use .gbapal for now
  700. QFile file(path);
  701. QList<QRgb> palette;
  702. if (file.open(QIODevice::ReadOnly)) {
  703. QByteArray data = file.readAll();
  704. for (int j = 0; j < 16; j++) {
  705. uint16_t word = data[j*2] & 0xff;
  706. word += (data[j*2 + 1] & 0xff) << 8;
  707. int red = word & 0x1f;
  708. int green = (word >> 5) & 0x1f;
  709. int blue = (word >> 10) & 0x1f;
  710. QRgb color = qRgb(red * 8, green * 8, blue * 8);
  711. palette.prepend(color);
  712. }
  713. } else {
  714. for (int j = 0; j < 16; j++) {
  715. palette.append(qRgb(j * 16, j * 16, j * 16));
  716. }
  717. qDebug() << QString("Could not open palette path '%1'").arg(path);
  718. }
  719. palettes->append(palette);
  720. }
  721. tileset->palettes = palettes;
  722. }
  723. Blockdata* Project::readBlockdata(QString path) {
  724. Blockdata *blockdata = new Blockdata;
  725. QFile file(path);
  726. if (file.open(QIODevice::ReadOnly)) {
  727. QByteArray data = file.readAll();
  728. for (int i = 0; (i + 1) < data.length(); i += 2) {
  729. uint16_t word = (data[i] & 0xff) + ((data[i + 1] & 0xff) << 8);
  730. blockdata->addBlock(word);
  731. }
  732. } else {
  733. qDebug() << "Failed to open blockdata path '" << path << "'";
  734. }
  735. return blockdata;
  736. }
  737. Map* Project::getMap(QString map_name) {
  738. if (map_cache->contains(map_name)) {
  739. return map_cache->value(map_name);
  740. } else {
  741. Map *map = loadMap(map_name);
  742. return map;
  743. }
  744. }
  745. Tileset* Project::getTileset(QString label) {
  746. if (tileset_cache->contains(label)) {
  747. return tileset_cache->value(label);
  748. } else {
  749. Tileset *tileset = loadTileset(label);
  750. return tileset;
  751. }
  752. }
  753. QString Project::readTextFile(QString path) {
  754. QFile file(path);
  755. if (!file.open(QIODevice::ReadOnly)) {
  756. //QMessageBox::information(0, "Error", QString("Could not open '%1': ").arg(path) + file.errorString());
  757. qDebug() << QString("Could not open '%1': ").arg(path) + file.errorString();
  758. return QString();
  759. }
  760. QTextStream in(&file);
  761. QString text = "";
  762. while (!in.atEnd()) {
  763. text += in.readLine() + "\n";
  764. }
  765. return text;
  766. }
  767. void Project::saveTextFile(QString path, QString text) {
  768. QFile file(path);
  769. if (file.open(QIODevice::WriteOnly)) {
  770. file.write(text.toUtf8());
  771. } else {
  772. qDebug() << QString("Could not open '%1' for writing: ").arg(path) + file.errorString();
  773. }
  774. }
  775. void Project::appendTextFile(QString path, QString text) {
  776. QFile file(path);
  777. if (file.open(QIODevice::Append)) {
  778. file.write(text.toUtf8());
  779. } else {
  780. qDebug() << QString("Could not open '%1' for appending: ").arg(path) + file.errorString();
  781. }
  782. }
  783. void Project::deleteFile(QString path) {
  784. QFile file(path);
  785. if (file.exists() && !file.remove()) {
  786. qDebug() << QString("Could not delete file '%1': ").arg(path) + file.errorString();
  787. }
  788. }
  789. void Project::readMapGroups() {
  790. QString text = readTextFile(root + "/data/maps/groups.inc");
  791. if (text.isNull()) {
  792. return;
  793. }
  794. ParseUtil *parser = new ParseUtil;
  795. QList<QStringList> *commands = parser->parseAsm(text);
  796. bool in_group_pointers = false;
  797. QStringList *groups = new QStringList;
  798. for (int i = 0; i < commands->length(); i++) {
  799. QStringList params = commands->value(i);
  800. QString macro = params.value(0);
  801. if (macro == ".label") {
  802. if (in_group_pointers) {
  803. break;
  804. }
  805. if (params.value(1) == "gMapGroups") {
  806. in_group_pointers = true;
  807. }
  808. } else if (macro == ".4byte") {
  809. if (in_group_pointers) {
  810. for (int j = 1; j < params.length(); j++) {
  811. groups->append(params.value(j));
  812. }
  813. }
  814. }
  815. }
  816. QList<QStringList> groupedMaps;
  817. for (int i = 0; i < groups->length(); i++) {
  818. groupedMaps.append(QStringList());
  819. }
  820. QStringList *maps = new QStringList;
  821. int group = -1;
  822. for (int i = 0; i < commands->length(); i++) {
  823. QStringList params = commands->value(i);
  824. QString macro = params.value(0);
  825. if (macro == ".label") {
  826. group = groups->indexOf(params.value(1));
  827. } else if (macro == ".4byte") {
  828. if (group != -1) {
  829. for (int j = 1; j < params.length(); j++) {
  830. QString mapName = params.value(j);
  831. groupedMaps[group].append(mapName);
  832. maps->append(mapName);
  833. map_groups->insert(mapName, group);
  834. // Build the mapping and reverse mapping between map constants and map names.
  835. QString mapConstant = Map::mapConstantFromName(mapName);
  836. mapConstantsToMapNames->insert(mapConstant, mapName);
  837. mapNamesToMapConstants->insert(mapName, mapConstant);
  838. }
  839. }
  840. }
  841. }
  842. groupNames = groups;
  843. groupedMapNames = groupedMaps;
  844. mapNames = maps;
  845. }
  846. Map* Project::addNewMapToGroup(QString mapName, int groupNum) {
  847. // Setup new map in memory, but don't write to file until map is actually saved later.
  848. mapNames->append(mapName);
  849. map_groups->insert(mapName, groupNum);
  850. groupedMapNames[groupNum].append(mapName);
  851. Map *map = new Map;
  852. map->isPersistedToFile = false;
  853. map->setName(mapName);
  854. mapConstantsToMapNames->insert(map->constantName, map->name);
  855. mapNamesToMapConstants->insert(map->name, map->constantName);
  856. setNewMapHeader(map, mapLayoutsTable.size() + 1);
  857. setNewMapLayout(map);
  858. loadMapTilesets(map);
  859. setNewMapBlockdata(map);
  860. setNewMapBorder(map);
  861. setNewMapEvents(map);
  862. setNewMapConnections(map);
  863. map->commit();
  864. map->history.save();
  865. map_cache->insert(mapName, map);
  866. return map;
  867. }
  868. QString Project::getNewMapName() {
  869. // Ensure default name doesn't already exist.
  870. int i = 0;
  871. QString newMapName;
  872. do {
  873. newMapName = QString("NewMap%1").arg(++i);
  874. } while (mapNames->contains(newMapName));
  875. return newMapName;
  876. }
  877. QList<QStringList>* Project::parseAsm(QString text) {
  878. ParseUtil *parser = new ParseUtil;
  879. return parser->parseAsm(text);
  880. }
  881. QStringList Project::getVisibilities() {
  882. // TODO
  883. QStringList names;
  884. for (int i = 0; i < 16; i++) {
  885. names.append(QString("%1").arg(i));
  886. }
  887. return names;
  888. }
  889. QMap<QString, QStringList> Project::getTilesets() {
  890. QMap<QString, QStringList> allTilesets;
  891. QStringList primaryTilesets;
  892. QStringList secondaryTilesets;
  893. allTilesets.insert("primary", primaryTilesets);
  894. allTilesets.insert("secondary", secondaryTilesets);
  895. QString headers_text = readTextFile(root + "/data/tilesets/headers.inc");
  896. QList<QStringList>* commands = parseAsm(headers_text);
  897. int i = 0;
  898. while (i < commands->length()) {
  899. if (commands->at(i).length() != 2)
  900. continue;
  901. if (commands->at(i).at(0) == ".label") {
  902. QString tilesetLabel = commands->at(i).at(1);
  903. // Advance to command specifying whether or not it is a secondary tileset
  904. i += 2;
  905. if (commands->at(i).at(0) != ".byte") {
  906. qDebug() << "Unexpected command found for secondary tileset flag. Expected '.byte', but found: " << commands->at(i).at(0);
  907. continue;
  908. }
  909. QString secondaryTilesetValue = commands->at(i).at(1);
  910. if (secondaryTilesetValue != "TRUE" && secondaryTilesetValue != "FALSE" && secondaryTilesetValue != "0" && secondaryTilesetValue != "1") {
  911. qDebug() << "Unexpected secondary tileset flag found. Expected \"TRUE\", \"FALSE\", \"0\", or \"1\", but found: " << secondaryTilesetValue;
  912. continue;
  913. }
  914. bool isSecondaryTileset = (secondaryTilesetValue == "TRUE" || secondaryTilesetValue == "1");
  915. if (isSecondaryTileset)
  916. allTilesets["secondary"].append(tilesetLabel);
  917. else
  918. allTilesets["primary"].append(tilesetLabel);
  919. }
  920. i++;
  921. }
  922. return allTilesets;
  923. }
  924. void Project::readRegionMapSections() {
  925. QString filepath = root + "/include/constants/region_map_sections.h";
  926. QStringList prefixes = (QStringList() << "MAPSEC_");
  927. readCDefinesSorted(filepath, prefixes, regionMapSections);
  928. }
  929. void Project::readItemNames() {
  930. QString filepath = root + "/include/constants/items.h";
  931. QStringList prefixes = (QStringList() << "ITEM_");
  932. readCDefinesSorted(filepath, prefixes, itemNames);
  933. }
  934. void Project::readFlagNames() {
  935. QString filepath = root + "/include/constants/flags.h";
  936. QStringList prefixes = (QStringList() << "FLAG_");
  937. readCDefinesSorted(filepath, prefixes, flagNames);
  938. }
  939. void Project::readVarNames() {
  940. QString filepath = root + "/include/constants/vars.h";
  941. QStringList prefixes = (QStringList() << "VAR_");
  942. readCDefinesSorted(filepath, prefixes, varNames);
  943. }
  944. void Project::readMovementTypes() {
  945. QString filepath = root + "/include/constants/event_object_movement_constants.h";
  946. QStringList prefixes = (QStringList() << "MOVEMENT_TYPE_");
  947. readCDefinesSorted(filepath, prefixes, movementTypes);
  948. }
  949. void Project::readMapTypes() {
  950. QString filepath = root + "/include/constants/map_types.h";
  951. QStringList prefixes = (QStringList() << "MAP_TYPE_");
  952. readCDefinesSorted(filepath, prefixes, mapTypes);
  953. }
  954. void Project::readMapBattleScenes() {
  955. QString filepath = root + "/include/constants/map_types.h";
  956. QStringList prefixes = (QStringList() << "MAP_BATTLE_SCENE_");
  957. readCDefinesSorted(filepath, prefixes, mapBattleScenes);
  958. }
  959. void Project::readWeatherNames() {
  960. QString filepath = root + "/include/constants/weather.h";
  961. QStringList prefixes = (QStringList() << "WEATHER_");
  962. readCDefinesSorted(filepath, prefixes, weatherNames);
  963. }
  964. void Project::readCoordEventWeatherNames() {
  965. QString filepath = root + "/include/constants/weather.h";
  966. QStringList prefixes = (QStringList() << "COORD_EVENT_WEATHER_");
  967. readCDefinesSorted(filepath, prefixes, coordEventWeatherNames);
  968. }
  969. void Project::readSecretBaseIds() {
  970. QString filepath = root + "/include/constants/secret_bases.h";
  971. QStringList prefixes = (QStringList() << "SECRET_BASE_");
  972. readCDefinesSorted(filepath, prefixes, secretBaseIds);
  973. }
  974. void Project::readBgEventFacingDirections() {
  975. QString filepath = root + "/include/constants/bg_event_constants.h";
  976. QStringList prefixes = (QStringList() << "BG_EVENT_PLAYER_FACING_");
  977. readCDefinesSorted(filepath, prefixes, bgEventFacingDirections);
  978. }
  979. void Project::readCDefinesSorted(QString filepath, QStringList prefixes, QStringList* definesToSet) {
  980. QString text = readTextFile(filepath);
  981. if (!text.isNull()) {
  982. QMap<QString, int> defines = readCDefines(text, prefixes);
  983. // The defines should to be sorted by their underlying value, not alphabetically.
  984. // Reverse the map and read out the resulting keys in order.
  985. QMultiMap<int, QString> definesInverse;
  986. for (QString defineName : defines.keys()) {
  987. definesInverse.insert(defines[defineName], defineName);
  988. }
  989. *definesToSet = definesInverse.values();
  990. } else {
  991. qDebug() << "Failed to read C defines file: " << filepath;
  992. }
  993. }
  994. void Project::readMapsWithConnections() {
  995. QString path = root + "/data/maps/connections.inc";
  996. QString text = readTextFile(path);
  997. if (text.isNull()) {
  998. return;
  999. }
  1000. mapsWithConnections.clear();
  1001. QRegularExpression re("data\\/maps\\/(?<mapName>\\w+)\\/connections.inc");
  1002. QList<QStringList>* includes = parseAsm(text);
  1003. for (QStringList values : *includes) {
  1004. if (values.length() != 2)
  1005. continue;
  1006. QRegularExpressionMatch match = re.match(values.value(1));
  1007. if (match.hasMatch()) {
  1008. QString mapName = match.captured("mapName");
  1009. mapsWithConnections.append(mapName);
  1010. }
  1011. }
  1012. }
  1013. void Project::saveMapsWithConnections() {
  1014. QString path = root + "/data/maps/connections.inc";
  1015. QString text = "";
  1016. for (QString mapName : mapsWithConnections) {
  1017. if (mapNamesToMapConstants->contains(mapName)) {
  1018. text += QString("\t.include \"data/maps/%1/connections.inc\"\n").arg(mapName);
  1019. } else {
  1020. qDebug() << QString("Failed to write connection include. %1 not a valid map name").arg(mapName);
  1021. }
  1022. }
  1023. saveTextFile(path, text);
  1024. }
  1025. QStringList Project::getSongNames() {
  1026. QStringList names;
  1027. QString text = readTextFile(root + "/include/constants/songs.h");
  1028. if (!text.isNull()) {
  1029. QStringList songDefinePrefixes;
  1030. songDefinePrefixes << "SE_" << "MUS_";
  1031. QMap<QString, int> songDefines = readCDefines(text, songDefinePrefixes);
  1032. names = songDefines.keys();
  1033. }
  1034. return names;
  1035. }
  1036. QMap<QString, int> Project::getEventObjGfxConstants() {
  1037. QMap<QString, int> constants;
  1038. QString text = readTextFile(root + "/include/constants/event_objects.h");
  1039. if (!text.isNull()) {
  1040. QStringList eventObjGfxPrefixes;
  1041. eventObjGfxPrefixes << "EVENT_OBJ_GFX_";
  1042. constants = readCDefines(text, eventObjGfxPrefixes);
  1043. }
  1044. return constants;
  1045. }
  1046. QString Project::fixGraphicPath(QString path) {
  1047. path = path.replace(QRegExp("\\.lz$"), "");
  1048. path = path.replace(QRegExp("\\.[1248]bpp$"), ".png");
  1049. return path;
  1050. }
  1051. void Project::loadEventPixmaps(QList<Event*> objects) {
  1052. bool needs_update = false;
  1053. for (Event *object : objects) {
  1054. if (object->pixmap.isNull()) {
  1055. needs_update = true;
  1056. break;
  1057. }
  1058. }
  1059. if (!needs_update) {
  1060. return;
  1061. }
  1062. QMap<QString, int> constants = getEventObjGfxConstants();
  1063. QString pointers_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics_info_pointers.h");
  1064. QString info_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics_info.h");
  1065. QString pic_text = readTextFile(root + "/src/data/field_event_obj/event_object_pic_tables.h");
  1066. QString assets_text = readTextFile(root + "/src/data/field_event_obj/event_object_graphics.h");
  1067. QStringList pointers = readCArray(pointers_text, "gEventObjectGraphicsInfoPointers");
  1068. for (Event *object : objects) {
  1069. if (!object->pixmap.isNull()) {
  1070. continue;
  1071. }
  1072. QString event_type = object->get("event_type");
  1073. if (event_type == EventType::Object) {
  1074. object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(0, 0, 16, 16);
  1075. } else if (event_type == EventType::Warp) {
  1076. object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(16, 0, 16, 16);
  1077. } else if (event_type == EventType::CoordScript || event_type == EventType::CoordWeather) {
  1078. object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(32, 0, 16, 16);
  1079. } else if (event_type == EventType::Sign || event_type == EventType::HiddenItem || event_type == EventType::SecretBase) {
  1080. object->pixmap = QPixmap(":/images/Entities_16x16.png").copy(48, 0, 16, 16);
  1081. }
  1082. if (event_type == EventType::Object) {
  1083. int sprite_id = constants.value(object->get("sprite"));
  1084. QString info_label = pointers.value(sprite_id).replace("&", "");
  1085. QString pic_label = readCArray(info_text, info_label).value(14);
  1086. QString gfx_label = readCArray(pic_text, pic_label).value(0);
  1087. gfx_label = gfx_label.section(QRegExp("[\\(\\)]"), 1, 1);
  1088. QString path = readCIncbin(assets_text, gfx_label);
  1089. if (!path.isNull()) {
  1090. path = fixGraphicPath(path);
  1091. QPixmap pixmap(root + "/" + path);
  1092. if (!pixmap.isNull()) {
  1093. object->pixmap = pixmap;
  1094. }
  1095. }
  1096. }
  1097. }
  1098. }
  1099. void Project::saveMapEvents(Map *map) {
  1100. QString path = root + QString("/data/maps/%1/events.inc").arg(map->name);
  1101. QString text = "";
  1102. QString objectEventsLabel = "0x0";
  1103. QString warpEventsLabel = "0x0";
  1104. QString coordEventsLabel = "0x0";
  1105. QString bgEventsLabel = "0x0";
  1106. if (map->events["object_event_group"].length() > 0) {
  1107. objectEventsLabel = Map::objectEventsLabelFromName(map->name);
  1108. text += QString("%1::\n").arg(objectEventsLabel);
  1109. for (int i = 0; i < map->events["object_event_group"].length(); i++) {
  1110. Event *object_event = map->events["object_event_group"].value(i);
  1111. text += object_event->buildObjectEventMacro(i);
  1112. }
  1113. text += "\n";
  1114. }
  1115. if (map->events["warp_event_group"].length() > 0) {
  1116. warpEventsLabel = Map::warpEventsLabelFromName(map->name);
  1117. text += QString("%1::\n").arg(warpEventsLabel);
  1118. for (Event *warp : map->events["warp_event_group"]) {
  1119. text += warp->buildWarpEventMacro(mapNamesToMapConstants);
  1120. }
  1121. text += "\n";
  1122. }
  1123. if (map->events["coord_event_group"].length() > 0) {
  1124. coordEventsLabel = Map::coordEventsLabelFromName(map->name);
  1125. text += QString("%1::\n").arg(coordEventsLabel);
  1126. for (Event *event : map->events["coord_event_group"]) {
  1127. QString event_type = event->get("event_type");
  1128. if (event_type == EventType::CoordScript) {
  1129. text += event->buildCoordScriptEventMacro();
  1130. } else if (event_type == EventType::CoordWeather) {
  1131. text += event->buildCoordWeatherEventMacro();
  1132. }
  1133. }
  1134. text += "\n";
  1135. }
  1136. if (map->events["bg_event_group"].length() > 0)
  1137. {
  1138. bgEventsLabel = Map::bgEventsLabelFromName(map->name);
  1139. text += QString("%1::\n").arg(bgEventsLabel);
  1140. for (Event *event : map->events["bg_event_group"]) {
  1141. QString event_type = event->get("event_type");
  1142. if (event_type == EventType::Sign) {
  1143. text += event->buildSignEventMacro();
  1144. } else if (event_type == EventType::HiddenItem) {
  1145. text += event->buildHiddenItemEventMacro();
  1146. } else if (event_type == EventType::SecretBase) {
  1147. text += event->buildSecretBaseEventMacro();
  1148. }
  1149. }
  1150. text += "\n";
  1151. }
  1152. text += QString("%1::\n").arg(map->events_label);
  1153. text += QString("\tmap_events %1, %2, %3, %4\n")
  1154. .arg(objectEventsLabel)
  1155. .arg(warpEventsLabel)
  1156. .arg(coordEventsLabel)
  1157. .arg(bgEventsLabel);
  1158. saveTextFile(path, text);
  1159. }
  1160. void Project::readMapEvents(Map *map) {
  1161. if (!map->isPersistedToFile) {
  1162. return;
  1163. }
  1164. // lazy
  1165. QString path = root + QString("/data/maps/%1/events.inc").arg(map->name);
  1166. QString text = readTextFile(path);
  1167. if (text.isNull()) {
  1168. return;
  1169. }
  1170. QStringList *labels = getLabelValues(parseAsm(text), map->events_label);
  1171. QString objectEventsLabel = labels->value(0);
  1172. QString warpEventsLabel = labels->value(1);
  1173. QString coordEventsLabel = labels->value(2);
  1174. QString bgEventsLabel = labels->value(3);
  1175. QList<QStringList> *object_events = getLabelMacros(parseAsm(text), objectEventsLabel);
  1176. map->events["object_event_group"].clear();
  1177. for (QStringList command : *object_events) {
  1178. if (command.value(0) == "object_event") {
  1179. Event *object = new Event;
  1180. object->put("map_name", map->name);
  1181. int i = 2;
  1182. object->put("sprite", command.value(i++));
  1183. object->put("replacement", command.value(i++));
  1184. object->put("x", command.value(i++).toInt(nullptr, 0));
  1185. object->put("y", command.value(i++).toInt(nullptr, 0));
  1186. object->put("elevation", command.value(i++));
  1187. object->put("movement_type", command.value(i++));
  1188. object->put("radius_x", command.value(i++).toInt(nullptr, 0));
  1189. object->put("radius_y", command.value(i++).toInt(nullptr, 0));
  1190. object->put("is_trainer", command.value(i++));
  1191. object->put("sight_radius_tree_id", command.value(i++));
  1192. object->put("script_label", command.value(i++));
  1193. object->put("event_flag", command.value(i++));
  1194. object->put("event_group_type", "object_event_group");
  1195. object->put("event_type", EventType::Object);
  1196. map->events["object_event_group"].append(object);
  1197. }
  1198. }
  1199. QList<QStringList> *warps = getLabelMacros(parseAsm(text), warpEventsLabel);
  1200. map->events["warp_event_group"].clear();
  1201. for (QStringList command : *warps) {
  1202. if (command.value(0) == "warp_def") {
  1203. Event *warp = new Event;
  1204. warp->put("map_name", map->name);
  1205. int i = 1;
  1206. warp->put("x", command.value(i++));
  1207. warp->put("y", command.value(i++));
  1208. warp->put("elevation", command.value(i++));
  1209. warp->put("destination_warp", command.value(i++));
  1210. // Ensure the warp destination map constant is valid before adding it to the warps.
  1211. QString mapConstant = command.value(i++);
  1212. if (mapConstantsToMapNames->contains(mapConstant)) {
  1213. warp->put("destination_map_name", mapConstantsToMapNames->value(mapConstant));
  1214. warp->put("event_group_type", "warp_event_group");
  1215. warp->put("event_type", EventType::Warp);
  1216. map->events["warp_event_group"].append(warp);
  1217. } else {
  1218. qDebug() << QString("Destination map constant '%1' is invalid for warp").arg(mapConstant);
  1219. }
  1220. }
  1221. }
  1222. QList<QStringList> *coords = getLabelMacros(parseAsm(text), coordEventsLabel);
  1223. map->events["coord_event_group"].clear();
  1224. for (QStringList command : *coords) {
  1225. if (command.value(0) == "coord_event") {
  1226. Event *coord = new Event;
  1227. coord->put("map_name", map->name);
  1228. int i = 1;
  1229. coord->put("x", command.value(i++));
  1230. coord->put("y", command.value(i++));
  1231. coord->put("elevation", command.value(i++));
  1232. coord->put("script_var", command.value(i++));
  1233. coord->put("script_var_value", command.value(i++));
  1234. coord->put("script_label", command.value(i++));
  1235. coord->put("event_group_type", "coord_event_group");
  1236. coord->put("event_type", EventType::CoordScript);
  1237. map->events["coord_event_group"].append(coord);
  1238. } else if (command.value(0) == "coord_weather_event") {
  1239. Event *coord = new Event;
  1240. coord->put("map_name", map->name);
  1241. int i = 1;
  1242. coord->put("x", command.value(i++));
  1243. coord->put("y", command.value(i++));
  1244. coord->put("elevation", command.value(i++));
  1245. coord->put("weather", command.value(i++));
  1246. coord->put("event_group_type", "coord_event_group");
  1247. coord->put("event_type", EventType::CoordWeather);
  1248. map->events["coord_event_group"].append(coord);
  1249. }
  1250. }
  1251. QList<QStringList> *bgs = getLabelMacros(parseAsm(text), bgEventsLabel);
  1252. map->events["bg_event_group"].clear();
  1253. for (QStringList command : *bgs) {
  1254. if (command.value(0) == "bg_event") {
  1255. Event *bg = new Event;
  1256. bg->put("map_name", map->name);
  1257. int i = 1;
  1258. bg->put("x", command.value(i++));
  1259. bg->put("y", command.value(i++));
  1260. bg->put("elevation", command.value(i++));
  1261. bg->put("player_facing_direction", command.value(i++));
  1262. bg->put("script_label", command.value(i++));
  1263. //sign_unknown7
  1264. bg->put("event_group_type", "bg_event_group");
  1265. bg->put("event_type", EventType::Sign);
  1266. map->events["bg_event_group"].append(bg);
  1267. } else if (command.value(0) == "bg_hidden_item_event") {
  1268. Event *bg = new Event;
  1269. bg->put("map_name", map->name);
  1270. int i = 1;
  1271. bg->put("x", command.value(i++));
  1272. bg->put("y", command.value(i++));
  1273. bg->put("elevation", command.value(i++));
  1274. bg->put("item", command.value(i++));
  1275. bg->put("flag", command.value(i++));
  1276. bg->put("event_group_type", "bg_event_group");
  1277. bg->put("event_type", EventType::HiddenItem);
  1278. map->events["bg_event_group"].append(bg);
  1279. } else if (command.value(0) == "bg_secret_base_event") {
  1280. Event *bg = new Event;
  1281. bg->put("map_name", map->name);
  1282. int i = 1;
  1283. bg->put("x", command.value(i++));
  1284. bg->put("y", command.value(i++));
  1285. bg->put("elevation", command.value(i++));
  1286. bg->put("secret_base_id", command.value(i++));
  1287. bg->put("event_group_type", "bg_event_group");
  1288. bg->put("event_type", EventType::SecretBase);
  1289. map->events["bg_event_group"].append(bg);
  1290. }
  1291. }
  1292. }
  1293. void Project::setNewMapEvents(Map *map) {
  1294. map->events["object_event_group"].clear();
  1295. map->events["warp_event_group"].clear();
  1296. map->events["coord_event_group"].clear();
  1297. map->events["bg_event_group"].clear();
  1298. }
  1299. QStringList Project::readCArray(QString text, QString label) {
  1300. QStringList list;
  1301. if (label.isNull()) {
  1302. return list;
  1303. }
  1304. QRegExp *re = new QRegExp(QString("\\b%1\\b\\s*\\[?\\s*\\]?\\s*=\\s*\\{([^\\}]*)\\}").arg(label));
  1305. int pos = re->indexIn(text);
  1306. if (pos != -1) {
  1307. QString body = re->cap(1);
  1308. body = body.replace(QRegExp("\\s*"), "");
  1309. list = body.split(',');
  1310. /*
  1311. QRegExp *inner = new QRegExp("&?\\b([A-Za-z0-9_\\(\\)]*)\\b,");
  1312. int pos = 0;
  1313. while ((pos = inner->indexIn(body, pos)) != -1) {
  1314. list << inner->cap(1);
  1315. pos += inner->matchedLength();
  1316. }
  1317. */
  1318. }
  1319. return list;
  1320. }
  1321. QString Project::readCIncbin(QString text, QString label) {
  1322. QString path;
  1323. if (label.isNull()) {
  1324. return path;
  1325. }
  1326. QRegExp *re = new QRegExp(QString(
  1327. "\\b%1\\b"
  1328. "\\s*\\[?\\s*\\]?\\s*=\\s*"
  1329. "INCBIN_[US][0-9][0-9]?"
  1330. "\\(\"([^\"]*)\"\\)").arg(label));
  1331. int pos = re->indexIn(text);
  1332. if (pos != -1) {
  1333. path = re->cap(1);
  1334. }
  1335. return path;
  1336. }
  1337. QMap<QString, int> Project::readCDefines(QString text, QStringList prefixes) {
  1338. ParseUtil parser;
  1339. QMap<QString, int> allDefines;
  1340. QMap<QString, int> filteredDefines;
  1341. QRegularExpression re("#define\\s+(?<defineName>\\w+)[^\\S\\n]+(?<defineValue>.+)");
  1342. QRegularExpressionMatchIterator iter = re.globalMatch(text);
  1343. while (iter.hasNext()) {
  1344. QRegularExpressionMatch match = iter.next();
  1345. QString name = match.captured("defineName");
  1346. QString expression = match.captured("defineValue");
  1347. expression.replace(QRegularExpression("//.*"), "");
  1348. int value = parser.evaluateDefine(expression, &allDefines);
  1349. allDefines.insert(name, value);
  1350. for (QString prefix : prefixes) {
  1351. if (name.startsWith(prefix)) {
  1352. filteredDefines.insert(name, value);
  1353. }
  1354. }
  1355. }
  1356. return filteredDefines;
  1357. }