yaze 0.2.0
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
overworld.cc
Go to the documentation of this file.
1#include "overworld.h"
2
3#include <algorithm>
4#include <fstream>
5#include <future>
6#include <memory>
7#include <unordered_map>
8#include <vector>
9
10#include "absl/status/status.h"
11#include "app/core/constants.h"
12#include "app/gfx/bitmap.h"
13#include "app/gfx/compression.h"
14#include "app/gfx/snes_tile.h"
15#include "app/rom.h"
18
19namespace yaze {
20namespace app {
21namespace zelda3 {
22namespace overworld {
23
24absl::Status Overworld::Load(Rom &rom) {
25 rom_ = rom;
26
30
31 const bool load_custom_overworld = flags()->overworld.kLoadCustomOverworld;
32 for (int map_index = 0; map_index < kNumOverworldMaps; ++map_index)
33 overworld_maps_.emplace_back(map_index, rom_, load_custom_overworld);
34
41
42 is_loaded_ = true;
43 return absl::OkStatus();
44}
45
47 for (int i = 128; i < 145; i++) {
48 overworld_maps_[i].SetAsSmallMap(0);
49 }
50
51 overworld_maps_[129].SetAsLargeMap(129, 0);
52 overworld_maps_[130].SetAsLargeMap(129, 1);
53 overworld_maps_[137].SetAsLargeMap(129, 2);
54 overworld_maps_[138].SetAsLargeMap(129, 3);
55 overworld_maps_[136].SetAsSmallMap();
56
57 std::vector<bool> map_checked;
58 map_checked.reserve(0x40);
59 for (int i = 0; i < 64; i++) {
60 map_checked[i] = false;
61 }
62 int xx = 0;
63 int yy = 0;
64 while (true) {
65 if (int i = xx + (yy * 8); map_checked[i] == false) {
66 if (overworld_maps_[i].is_large_map()) {
67 map_checked[i] = true;
68 overworld_maps_[i].SetAsLargeMap(i, 0);
69 overworld_maps_[i + 64].SetAsLargeMap(i + 64, 0);
70
71 map_checked[i + 1] = true;
72 overworld_maps_[i + 1].SetAsLargeMap(i, 1);
73 overworld_maps_[i + 65].SetAsLargeMap(i + 64, 1);
74
75 map_checked[i + 8] = true;
76 overworld_maps_[i + 8].SetAsLargeMap(i, 2);
77 overworld_maps_[i + 72].SetAsLargeMap(i + 64, 2);
78
79 map_checked[i + 9] = true;
80 overworld_maps_[i + 9].SetAsLargeMap(i, 3);
81 overworld_maps_[i + 73].SetAsLargeMap(i + 64, 3);
82 xx++;
83 } else {
84 overworld_maps_[i].SetAsSmallMap();
85 overworld_maps_[i + 64].SetAsSmallMap();
86 map_checked[i] = true;
87 }
88 }
89
90 xx++;
91 if (xx >= 8) {
92 xx = 0;
93 yy += 1;
94 if (yy >= 8) {
95 break;
96 }
97 }
98 }
99}
100
101absl::StatusOr<uint16_t> Overworld::GetTile16ForTile32(
102 int index, int quadrant, int dimension, const uint32_t *map32address) {
103 ASSIGN_OR_RETURN(auto arg1,
104 rom_.ReadByte(map32address[dimension] + quadrant + (index)));
105 ASSIGN_OR_RETURN(auto arg2, rom_.ReadWord(map32address[dimension] + (index) +
106 (quadrant <= 1 ? 4 : 5)));
107 return (uint16_t)(arg1 +
108 (((arg2 >> (quadrant % 2 == 0 ? 4 : 0)) & 0x0F) * 256));
109}
110
111constexpr int kMap32TilesLength = 0x33F0;
112
114 int num_tile32 = kMap32TilesLength;
115 uint32_t map32address[4] = {rom_.version_constants().kMap32TileTL,
119 if (rom()->data()[0x01772E] != 0x04) {
120 map32address[0] = rom_.version_constants().kMap32TileTL;
121 map32address[1] = kMap32TileTRExpanded;
122 map32address[2] = kMap32TileBLExpanded;
123 map32address[3] = kMap32TileBRExpanded;
124 num_tile32 = kMap32TileCountExpanded;
125 expanded_tile32_ = true;
126 }
127
128 // Loop through each 32x32 pixel tile in the rom
129 for (int i = 0; i < kMap32TilesLength; i += 6) {
130 // Loop through each quadrant of the 32x32 pixel tile.
131 for (int k = 0; k < 4; k++) {
132 // Generate the 16-bit tile for the current quadrant of the current
133 // 32x32 pixel tile.
134 ASSIGN_OR_RETURN(uint16_t tl,
136 map32address));
137 ASSIGN_OR_RETURN(uint16_t tr,
139 map32address));
140 ASSIGN_OR_RETURN(uint16_t bl,
142 map32address));
143 ASSIGN_OR_RETURN(uint16_t br,
145 map32address));
146
147 // Add the generated 16-bit tiles to the tiles32 vector.
148 tiles32_unique_.emplace_back(gfx::Tile32(tl, tr, bl, br));
149 }
150 }
151
152 map_tiles_.light_world.resize(0x200);
153 map_tiles_.dark_world.resize(0x200);
154 map_tiles_.special_world.resize(0x200);
155 for (int i = 0; i < 0x200; i++) {
156 map_tiles_.light_world[i].resize(0x200);
157 map_tiles_.dark_world[i].resize(0x200);
158 map_tiles_.special_world[i].resize(0x200);
159 }
160
161 return absl::OkStatus();
162}
163
165 int tpos = kMap16Tiles;
166 int num_tile16 = kNumTile16Individual;
167 if (rom()->data()[0x02FD28] != 0x0F) {
168 tpos = kMap16TilesExpanded;
169 num_tile16 = NumberOfMap16Ex;
170 expanded_tile16_ = true;
171 }
172
173 for (int i = 0; i < num_tile16; i += 1) {
174 gfx::TileInfo t0 = gfx::GetTilesInfo(rom()->toint16(tpos));
175 tpos += 2;
176 gfx::TileInfo t1 = gfx::GetTilesInfo(rom()->toint16(tpos));
177 tpos += 2;
178 gfx::TileInfo t2 = gfx::GetTilesInfo(rom()->toint16(tpos));
179 tpos += 2;
180 gfx::TileInfo t3 = gfx::GetTilesInfo(rom()->toint16(tpos));
181 tpos += 2;
182 tiles16_.emplace_back(t0, t1, t2, t3);
183 }
184}
185
186void Overworld::AssignWorldTiles(int x, int y, int sx, int sy, int tpos,
187 OWBlockset &world) {
188 int position_x1 = (x * 2) + (sx * 32);
189 int position_y1 = (y * 2) + (sy * 32);
190 int position_x2 = (x * 2) + 1 + (sx * 32);
191 int position_y2 = (y * 2) + 1 + (sy * 32);
192 world[position_x1][position_y1] = tiles32_unique_[tpos].tile0_;
193 world[position_x2][position_y1] = tiles32_unique_[tpos].tile1_;
194 world[position_x1][position_y2] = tiles32_unique_[tpos].tile2_;
195 world[position_x2][position_y2] = tiles32_unique_[tpos].tile3_;
196}
197
198void Overworld::OrganizeMapTiles(std::vector<uint8_t> &bytes,
199 std::vector<uint8_t> &bytes2, int i, int sx,
200 int sy, int &ttpos) {
201 for (int y = 0; y < 16; y++) {
202 for (int x = 0; x < 16; x++) {
203 auto tidD = (uint16_t)((bytes2[ttpos] << 8) + bytes[ttpos]);
204 if (int tpos = tidD; tpos < tiles32_unique_.size()) {
205 if (i < 64) {
206 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.light_world);
207 } else if (i < 128 && i >= 64) {
208 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.dark_world);
209 } else {
210 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.special_world);
211 }
212 }
213 ttpos += 1;
214 }
215 }
216}
217
219 const auto get_ow_map_gfx_ptr = [this](int index, uint32_t map_ptr) {
220 int p = (rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
221 (rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
222 (rom()->data()[map_ptr + (3 * index)]);
223 return core::SnesToPc(p);
224 };
225
226 uint32_t lowest = 0x0FFFFF;
227 uint32_t highest = 0x0F8000;
228 int sx = 0;
229 int sy = 0;
230 int c = 0;
231 for (int i = 0; i < 160; i++) {
232 auto p1 = get_ow_map_gfx_ptr(
233 i, rom()->version_constants().kCompressedAllMap32PointersHigh);
234 auto p2 = get_ow_map_gfx_ptr(
235 i, rom()->version_constants().kCompressedAllMap32PointersLow);
236
237 int ttpos = 0;
238
239 if (p1 >= highest) highest = p1;
240 if (p2 >= highest) highest = p2;
241
242 if (p1 <= lowest && p1 > 0x0F8000) lowest = p1;
243 if (p2 <= lowest && p2 > 0x0F8000) lowest = p2;
244
245 std::vector<uint8_t> bytes, bytes2;
246 int size1, size2;
247 auto decomp = gfx::lc_lz2::Uncompress(rom()->data() + p2, &size1, 1);
248 bytes.resize(size1);
249 for (int j = 0; j < size1; j++) {
250 bytes[j] = decomp[j];
251 }
252 free(decomp);
253 decomp = gfx::lc_lz2::Uncompress(rom()->data() + p1, &size2, 1);
254 bytes2.resize(size2);
255 for (int j = 0; j < size2; j++) {
256 bytes2[j] = decomp[j];
257 }
258 free(decomp);
259
260 OrganizeMapTiles(bytes, bytes2, i, sx, sy, ttpos);
261
262 sx++;
263 if (sx >= 8) {
264 sy++;
265 sx = 0;
266 }
267
268 c++;
269 if (c >= 64) {
270 sx = 0;
271 sy = 0;
272 c = 0;
273 }
274 }
275 return absl::OkStatus();
276}
277
279 auto size = tiles16_.size();
280 std::vector<std::future<absl::Status>> futures;
281 for (int i = 0; i < kNumOverworldMaps; ++i) {
282 int world_type = 0;
283 if (i >= 64 && i < 0x80) {
284 world_type = 1;
285 } else if (i >= 0x80) {
286 world_type = 2;
287 }
288 auto task_function = [this, i, size, world_type]() {
289 return overworld_maps_[i].BuildMap(size, game_state_, world_type,
290 tiles16_, GetMapTiles(world_type));
291 };
292 futures.emplace_back(std::async(std::launch::async, task_function));
293 }
294
295 // Wait for all tasks to complete and check their results
296 for (auto &future : futures) {
297 absl::Status status = future.get();
298 if (!status.ok()) {
299 return status;
300 }
301 }
302 return absl::OkStatus();
303}
304
306 for (int i = 0; i < 0x200; i++) {
308 rom()->data()[rom()->version_constants().overworldTilesType + i];
309 }
310}
311
313 int ow_entrance_map_ptr = OWEntranceMap;
314 int ow_entrance_pos_ptr = OWEntrancePos;
315 int ow_entrance_id_ptr = OWEntranceEntranceId;
316 int num_entrances = 129;
317 if (rom()->data()[0x0DB895] != 0xB8) {
318 ow_entrance_map_ptr = 0x0DB55F;
319 ow_entrance_pos_ptr = 0x0DB35F;
320 ow_entrance_id_ptr = 0x0DB75F;
321 expanded_entrances_ = true;
322 }
323
324 for (int i = 0; i < num_entrances; i++) {
325 short map_id = rom()->toint16(ow_entrance_map_ptr + (i * 2));
326 uint16_t map_pos = rom()->toint16(ow_entrance_pos_ptr + (i * 2));
327 uint8_t entrance_id = rom_[ow_entrance_id_ptr + i];
328 int p = map_pos >> 1;
329 int x = (p % 64);
330 int y = (p >> 6);
331 bool deleted = false;
332 if (map_pos == 0xFFFF) {
333 deleted = true;
334 }
335 all_entrances_.emplace_back(
336 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
337 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id, map_pos,
338 deleted);
339 }
340
341 for (int i = 0; i < 0x13; i++) {
342 auto map_id = (short)((rom_[OWHoleArea + (i * 2) + 1] << 8) +
343 (rom_[OWHoleArea + (i * 2)]));
344 auto map_pos = (short)((rom_[OWHolePos + (i * 2) + 1] << 8) +
345 (rom_[OWHolePos + (i * 2)]));
346 uint8_t entrance_id = (rom_[OWHoleEntrance + i]);
347 int p = (map_pos + 0x400) >> 1;
348 int x = (p % 64);
349 int y = (p >> 6);
350 all_holes_.emplace_back(
351 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
352 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id,
353 (uint16_t)(map_pos + 0x400), true);
354 }
355}
356
357absl::Status Overworld::LoadExits() {
358 const int NumberOfOverworldExits = 0x4F;
359 std::vector<OverworldExit> exits;
360 for (int i = 0; i < NumberOfOverworldExits; i++) {
361 auto rom_data = rom()->data();
362
363 uint16_t exit_room_id;
364 uint16_t exit_map_id;
365 uint16_t exit_vram;
366 uint16_t exit_y_scroll;
367 uint16_t exit_x_scroll;
368 uint16_t exit_y_player;
369 uint16_t exit_x_player;
370 uint16_t exit_y_camera;
371 uint16_t exit_x_camera;
372 uint16_t exit_scroll_mod_y;
373 uint16_t exit_scroll_mod_x;
374 uint16_t exit_door_type_1;
375 uint16_t exit_door_type_2;
376 RETURN_IF_ERROR(rom()->ReadTransaction(
377 exit_room_id, (OWExitRoomId + (i * 2)), exit_map_id, OWExitMapId + i,
378 exit_vram, OWExitVram + (i * 2), exit_y_scroll, OWExitYScroll + (i * 2),
379 exit_x_scroll, OWExitXScroll + (i * 2), exit_y_player,
380 OWExitYPlayer + (i * 2), exit_x_player, OWExitXPlayer + (i * 2),
381 exit_y_camera, OWExitYCamera + (i * 2), exit_x_camera,
382 OWExitXCamera + (i * 2), exit_scroll_mod_y, OWExitUnk1 + i,
383 exit_scroll_mod_x, OWExitUnk2 + i, exit_door_type_1,
384 OWExitDoorType1 + (i * 2), exit_door_type_2,
385 OWExitDoorType2 + (i * 2)));
386
387 uint16_t py = (uint16_t)((rom_data[OWExitYPlayer + (i * 2) + 1] << 8) +
388 rom_data[OWExitYPlayer + (i * 2)]);
389 uint16_t px = (uint16_t)((rom_data[OWExitXPlayer + (i * 2) + 1] << 8) +
390 rom_data[OWExitXPlayer + (i * 2)]);
391
392 if (rom()->flags()->kLogToConsole) {
393 std::cout << "Exit: " << i << " RoomID: " << exit_room_id
394 << " MapID: " << exit_map_id << " VRAM: " << exit_vram
395 << " YScroll: " << exit_y_scroll
396 << " XScroll: " << exit_x_scroll << " YPlayer: " << py
397 << " XPlayer: " << px << " YCamera: " << exit_y_camera
398 << " XCamera: " << exit_x_camera
399 << " ScrollModY: " << exit_scroll_mod_y
400 << " ScrollModX: " << exit_scroll_mod_x
401 << " DoorType1: " << exit_door_type_1
402 << " DoorType2: " << exit_door_type_2 << std::endl;
403 }
404
405 exits.emplace_back(exit_room_id, exit_map_id, exit_vram, exit_y_scroll,
406 exit_x_scroll, py, px, exit_y_camera, exit_x_camera,
407 exit_scroll_mod_y, exit_scroll_mod_x, exit_door_type_1,
408 exit_door_type_2, (px & py) == 0xFFFF);
409 }
411 return absl::OkStatus();
412}
413
414absl::Status Overworld::LoadItems() {
415 ASSIGN_OR_RETURN(uint32_t pointer,
417 uint32_t pointer_pc = core::SnesToPc(pointer); // 1BC2F9 -> 0DC2F9
418 for (int i = 0; i < 128; i++) {
419 ASSIGN_OR_RETURN(uint16_t word_address,
420 rom()->ReadWord(pointer_pc + i * 2));
421 uint32_t addr = (pointer & 0xFF0000) | word_address; // 1B F9 3C
422 addr = core::SnesToPc(addr);
423
424 if (overworld_maps_[i].is_large_map()) {
425 if (overworld_maps_[i].parent() != (uint8_t)i) {
426 continue;
427 }
428 }
429
430 while (true) {
431 ASSIGN_OR_RETURN(uint8_t b1, rom()->ReadByte(addr));
432 ASSIGN_OR_RETURN(uint8_t b2, rom()->ReadByte(addr + 1));
433 ASSIGN_OR_RETURN(uint8_t b3, rom()->ReadByte(addr + 2));
434
435 if (b1 == 0xFF && b2 == 0xFF) {
436 break;
437 }
438
439 int p = (((b2 & 0x1F) << 8) + b1) >> 1;
440
441 int x = p % 64;
442 int y = p >> 6;
443
444 int fakeID = i;
445 if (fakeID >= 64) {
446 fakeID -= 64;
447 }
448
449 int sy = fakeID / 8;
450 int sx = fakeID - (sy * 8);
451
452 all_items_.emplace_back(b3, (uint16_t)i, (x * 16) + (sx * 512),
453 (y * 16) + (sy * 512), false);
454 auto size = all_items_.size();
455
456 all_items_[size - 1].game_x_ = (uint8_t)x;
457 all_items_[size - 1].game_y_ = (uint8_t)y;
458 addr += 3;
459 }
460 }
461 return absl::OkStatus();
462}
463
465 for (int i = 0; i < 3; i++) {
466 all_sprites_.emplace_back();
467 }
468
472 return absl::OkStatus();
473}
474
475absl::Status Overworld::LoadSpritesFromMap(int sprites_per_gamestate_ptr,
476 int num_maps_per_gamestate,
477 int game_state) {
478 for (int i = 0; i < num_maps_per_gamestate; i++) {
479 if (map_parent_[i] != i) continue;
480
481 int current_spr_ptr = sprites_per_gamestate_ptr + (i * 2);
482 ASSIGN_OR_RETURN(auto word_addr, rom()->ReadWord(current_spr_ptr));
483 int sprite_address = core::SnesToPc((0x09 << 0x10) | word_addr);
484 while (true) {
485 ASSIGN_OR_RETURN(uint8_t b1, rom()->ReadByte(sprite_address));
486 ASSIGN_OR_RETURN(uint8_t b2, rom()->ReadByte(sprite_address + 1));
487 ASSIGN_OR_RETURN(uint8_t b3, rom()->ReadByte(sprite_address + 2));
488 if (b1 == 0xFF) break;
489
490 int editor_map_index = i;
491 if (game_state != 0) {
492 if (editor_map_index >= 128)
493 editor_map_index -= 128;
494 else if (editor_map_index >= 64)
495 editor_map_index -= 64;
496 }
497 int mapY = (editor_map_index / 8);
498 int mapX = (editor_map_index % 8);
499
500 int realX = ((b2 & 0x3F) * 16) + mapX * 512;
501 int realY = ((b1 & 0x3F) * 16) + mapY * 512;
502 auto current_gfx = overworld_maps_[i].current_graphics();
503 all_sprites_[game_state].emplace_back(current_gfx, (uint8_t)i, b3,
504 (uint8_t)(b2 & 0x3F),
505 (uint8_t)(b1 & 0x3F), realX, realY);
506 all_sprites_[game_state][i].Draw();
507
508 sprite_address += 3;
509 }
510 }
511
512 return absl::OkStatus();
513}
514
515// ---------------------------------------------------------------------------
516
528
530 core::Logger::log("Saving Overworld Maps");
531
532 // Initialize map pointers
533 std::fill(map_pointers1_id.begin(), map_pointers1_id.end(), -1);
534 std::fill(map_pointers2_id.begin(), map_pointers2_id.end(), -1);
535
536 // Compress and save each map
537 int pos = 0x058000;
538 for (int i = 0; i < 160; i++) {
539 std::vector<uint8_t> single_map_1(512);
540 std::vector<uint8_t> single_map_2(512);
541
542 // Copy tiles32 data to single_map_1 and single_map_2
543 int npos = 0;
544 for (int y = 0; y < 16; y++) {
545 for (int x = 0; x < 16; x++) {
546 auto packed = tiles32_list_[npos + (i * 256)];
547 single_map_1[npos] = packed & 0xFF; // Lower 8 bits
548 single_map_2[npos] = (packed >> 8) & 0xFF; // Next 8 bits
549 npos++;
550 }
551 }
552
553 std::vector<uint8_t> a, b;
554 int size_a, size_b;
555 // Compress single_map_1 and single_map_2
556 auto a_char = gfx::lc_lz2::Compress(single_map_1.data(), 256, &size_a, 1);
557 auto b_char = gfx::lc_lz2::Compress(single_map_2.data(), 256, &size_b, 1);
558 if (a_char == nullptr || b_char == nullptr) {
559 return absl::AbortedError("Error compressing map gfx.");
560 }
561 // Copy the compressed data to a and b
562 a.resize(size_a);
563 b.resize(size_b);
564 // Copy the arrays manually
565 for (int k = 0; k < size_a; k++) {
566 a[k] = a_char[k];
567 }
568 for (int k = 0; k < size_b; k++) {
569 b[k] = b_char[k];
570 }
571
572 // Save compressed data and pointers
573 map_data_p1[i] = std::vector<uint8_t>(size_a);
574 map_data_p2[i] = std::vector<uint8_t>(size_b);
575
576 if ((pos + size_a) >= 0x5FE70 && (pos + size_a) <= 0x60000) {
577 pos = 0x60000;
578 }
579
580 if ((pos + size_a) >= 0x6411F && (pos + size_a) <= 0x70000) {
581 core::Logger::log("Pos set to overflow region for map " +
582 std::to_string(i) + " at " +
584 pos = OverworldMapDataOverflow; // 0x0F8780;
585 }
586
587 auto compareArray = [](const std::vector<uint8_t> &array1,
588 const std::vector<uint8_t> &array2) -> bool {
589 if (array1.size() != array2.size()) {
590 return false;
591 }
592
593 for (size_t i = 0; i < array1.size(); i++) {
594 if (array1[i] != array2[i]) {
595 return false;
596 }
597 }
598
599 return true;
600 };
601
602 for (int j = 0; j < i; j++) {
603 if (compareArray(a, map_data_p1[j])) {
604 // Reuse pointer id j for P1 (a)
605 map_pointers1_id[i] = j;
606 }
607
608 if (compareArray(b, map_data_p2[j])) {
609 map_pointers2_id[i] = j;
610 // Reuse pointer id j for P2 (b)
611 }
612 }
613
614 if (map_pointers1_id[i] == -1) {
615 // Save compressed data and pointer for map1
616 std::copy(a.begin(), a.end(), map_data_p1[i].begin());
617 int snes_pos = core::PcToSnes(pos);
618 map_pointers1[i] = snes_pos;
619 core::Logger::log("Saving map pointers1 and compressed data for map " +
620 core::UppercaseHexByte(i) + " at " +
621 core::UppercaseHexLong(snes_pos));
622 RETURN_IF_ERROR(rom()->WriteLong(
623 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
624 snes_pos));
625 RETURN_IF_ERROR(rom()->WriteVector(pos, a));
626 pos += size_a;
627 } else {
628 // Save pointer for map1
629 int snes_pos = map_pointers1[map_pointers1_id[i]];
630 core::Logger::log("Saving map pointers1 for map " +
631 core::UppercaseHexByte(i) + " at " +
632 core::UppercaseHexLong(snes_pos));
633 RETURN_IF_ERROR(rom()->WriteLong(
634 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
635 snes_pos));
636 }
637
638 if ((pos + b.size()) >= 0x5FE70 && (pos + b.size()) <= 0x60000) {
639 pos = 0x60000;
640 }
641
642 if ((pos + b.size()) >= 0x6411F && (pos + b.size()) <= 0x70000) {
643 core::Logger::log("Pos set to overflow region for map " +
644 core::UppercaseHexByte(i) + " at " +
647 }
648
649 if (map_pointers2_id[i] == -1) {
650 // Save compressed data and pointer for map2
651 std::copy(b.begin(), b.end(), map_data_p2[i].begin());
652 int snes_pos = core::PcToSnes(pos);
653 map_pointers2[i] = snes_pos;
654 core::Logger::log("Saving map pointers2 and compressed data for map " +
655 core::UppercaseHexByte(i) + " at " +
656 core::UppercaseHexLong(snes_pos));
657 RETURN_IF_ERROR(rom()->WriteLong(
658 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
659 snes_pos));
660 RETURN_IF_ERROR(rom()->WriteVector(pos, b));
661 pos += size_b;
662 } else {
663 // Save pointer for map2
664 int snes_pos = map_pointers2[map_pointers2_id[i]];
665 core::Logger::log("Saving map pointers2 for map " +
666 core::UppercaseHexByte(i) + " at " +
667 core::UppercaseHexLong(snes_pos));
668 RETURN_IF_ERROR(rom()->WriteLong(
669 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
670 snes_pos));
671 }
672 }
673
674 // Check if too many maps data
675 if (pos > 0x137FFF) {
676 std::cerr << "Too many maps data " << std::hex << pos << std::endl;
677 return absl::AbortedError("Too many maps data " + std::to_string(pos));
678 }
679
680 // Save large maps
682
683 return absl::OkStatus();
684}
685
687 core::Logger::log("Saving Large Maps");
688 std::vector<uint8_t> checked_map;
689
690 for (int i = 0; i < 0x40; i++) {
691 int y_pos = i / 8;
692 int x_pos = i % 8;
693 int parent_y_pos = overworld_maps_[i].parent() / 8;
694 int parent_x_pos = overworld_maps_[i].parent() % 8;
695
696 // Always write the map parent since it should not matter
698 rom()->Write(overworldMapParentId + i, overworld_maps_[i].parent()))
699
700 if (std::find(checked_map.begin(), checked_map.end(), i) !=
701 checked_map.end()) {
702 continue;
703 }
704
705 // If it's large then save parent pos *
706 // 0x200 otherwise pos * 0x200
707 if (overworld_maps_[i].is_large_map()) {
708 const uint8_t large_map_offsets[] = {0, 1, 8, 9};
709 for (const auto &offset : large_map_offsets) {
710 // Check 1
711 RETURN_IF_ERROR(rom()->WriteByte(overworldMapSize + i + offset, 0x20));
712 // Check 2
714 rom()->WriteByte(overworldMapSizeHighByte + i + offset, 0x03));
715 // Check 3
717 rom()->WriteByte(overworldScreenSize + i + offset, 0x00));
719 rom()->WriteByte(overworldScreenSize + i + offset + 64, 0x00));
720 // Check 4
722 rom()->WriteByte(OverworldScreenSizeForLoading + i + offset, 0x04));
723 RETURN_IF_ERROR(rom()->WriteByte(
724 OverworldScreenSizeForLoading + i + offset + 64, 0x04));
725 RETURN_IF_ERROR(rom()->WriteByte(
726 OverworldScreenSizeForLoading + i + offset + 128, 0x04));
727 }
728
729 // Check 5 and 6
731 rom()->WriteShort(transition_target_north + (i * 2),
732 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
734 rom()->WriteShort(transition_target_west + (i * 2),
735 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
736
738 rom()->WriteShort(transition_target_north + (i * 2) + 2,
739 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
741 rom()->WriteShort(transition_target_west + (i * 2) + 2,
742 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
743
745 rom()->WriteShort(transition_target_north + (i * 2) + 16,
746 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
748 rom()->WriteShort(transition_target_west + (i * 2) + 16,
749 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
750
752 rom()->WriteShort(transition_target_north + (i * 2) + 18,
753 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
755 rom()->WriteShort(transition_target_west + (i * 2) + 18,
756 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
757
758 // Check 7 and 8
759 RETURN_IF_ERROR(rom()->WriteShort(overworldTransitionPositionX + (i * 2),
760 (parent_x_pos * 0x200)));
761 RETURN_IF_ERROR(rom()->WriteShort(overworldTransitionPositionY + (i * 2),
762 (parent_y_pos * 0x200)));
763
764 RETURN_IF_ERROR(rom()->WriteShort(
765 overworldTransitionPositionX + (i * 2) + 02, (parent_x_pos * 0x200)));
766 RETURN_IF_ERROR(rom()->WriteShort(
767 overworldTransitionPositionY + (i * 2) + 02, (parent_y_pos * 0x200)));
768
769 // problematic
770 RETURN_IF_ERROR(rom()->WriteShort(
771 overworldTransitionPositionX + (i * 2) + 16, (parent_x_pos * 0x200)));
772 RETURN_IF_ERROR(rom()->WriteShort(
773 overworldTransitionPositionY + (i * 2) + 16, (parent_y_pos * 0x200)));
774
775 RETURN_IF_ERROR(rom()->WriteShort(
776 overworldTransitionPositionX + (i * 2) + 18, (parent_x_pos * 0x200)));
777 RETURN_IF_ERROR(rom()->WriteShort(
778 overworldTransitionPositionY + (i * 2) + 18, (parent_y_pos * 0x200)));
779
780 // Check 9
781 RETURN_IF_ERROR(rom()->WriteShort(
782 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 00, 0x0060));
783 RETURN_IF_ERROR(rom()->WriteShort(
784 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 02, 0x0060));
785
786 // If parentX == 0 then lower submaps == 0x0060 too
787 if (parent_x_pos == 0) {
788 RETURN_IF_ERROR(rom()->WriteShort(
789 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 16, 0x0060));
790 RETURN_IF_ERROR(rom()->WriteShort(
791 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 18, 0x0060));
792 } else {
793 // Otherwise lower submaps == 0x1060
794 RETURN_IF_ERROR(rom()->WriteShort(
795 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 16, 0x1060));
796 RETURN_IF_ERROR(rom()->WriteShort(
797 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 18, 0x1060));
798
799 // If the area to the left is a large map, we don't need to add an
800 // offset to it. otherwise leave it the same. Just to make sure where
801 // don't try to read outside of the array.
802 if ((i - 1) >= 0) {
803 // If the area to the left is a large area.
804 if (overworld_maps_[i - 1].is_large_map()) {
805 // If the area to the left is the bottom right of a large area.
806 if (overworld_maps_[i - 1].large_index() == 1) {
807 RETURN_IF_ERROR(rom()->WriteShort(
809 0x0060));
810 }
811 }
812 }
813 }
814
815 // Always 0x0080
816 RETURN_IF_ERROR(rom()->WriteShort(
817 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 00, 0x0080));
818 RETURN_IF_ERROR(rom()->WriteShort(
819 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 2, 0x0080));
820 // Lower always 0x1080
821 RETURN_IF_ERROR(rom()->WriteShort(
822 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 16, 0x1080));
823 RETURN_IF_ERROR(rom()->WriteShort(
824 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 18, 0x1080));
825
826 // If the area to the right is a large map, we don't need to add an offset
827 // to it. otherwise leave it the same. Just to make sure where don't try
828 // to read outside of the array.
829 if ((i + 2) < 64) {
830 // If the area to the right is a large area.
831 if (overworld_maps_[i + 2].is_large_map()) {
832 // If the area to the right is the top left of a large area.
833 if (overworld_maps_[i + 2].large_index() == 0) {
834 RETURN_IF_ERROR(rom()->WriteShort(
835 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 18, 0x0080));
836 }
837 }
838 }
839
840 // Always 0x1800
841 RETURN_IF_ERROR(rom()->WriteShort(
842 OverworldScreenTileMapChangeByScreen3 + (i * 2), 0x1800));
843 RETURN_IF_ERROR(rom()->WriteShort(
844 OverworldScreenTileMapChangeByScreen3 + (i * 2) + 16, 0x1800));
845 // Right side is always 0x1840
846 RETURN_IF_ERROR(rom()->WriteShort(
847 OverworldScreenTileMapChangeByScreen3 + (i * 2) + 2, 0x1840));
848 RETURN_IF_ERROR(rom()->WriteShort(
849 OverworldScreenTileMapChangeByScreen3 + (i * 2) + 18, 0x1840));
850
851 // If the area above is a large map, we don't need to add an offset to it.
852 // otherwise leave it the same.
853 // Just to make sure where don't try to read outside of the array.
854 if (i - 8 >= 0) {
855 // If the area just above us is a large area.
856 if (overworld_maps_[i - 8].is_large_map()) {
857 // If the area just above us is the bottom left of a large area.
858 if (overworld_maps_[i - 8].large_index() == 2) {
859 RETURN_IF_ERROR(rom()->WriteShort(
860 OverworldScreenTileMapChangeByScreen3 + (i * 2) + 02, 0x1800));
861 }
862 }
863 }
864
865 // Always 0x2000
866 RETURN_IF_ERROR(rom()->WriteShort(
867 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 00, 0x2000));
868 RETURN_IF_ERROR(rom()->WriteShort(
869 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 16, 0x2000));
870 // Right side always 0x2040
871 RETURN_IF_ERROR(rom()->WriteShort(
872 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 2, 0x2040));
873 RETURN_IF_ERROR(rom()->WriteShort(
874 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 18, 0x2040));
875
876 // If the area below is a large map, we don't need to add an offset to it.
877 // otherwise leave it the same.
878 // Just to make sure where don't try to read outside of the array.
879 if (i + 16 < 64) {
880 // If the area just below us is a large area.
881 if (overworld_maps_[i + 16].is_large_map()) {
882 // If the area just below us is the top left of a large area.
883 if (overworld_maps_[i + 16].large_index() == 0) {
884 RETURN_IF_ERROR(rom()->WriteShort(
885 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 18, 0x2000));
886 }
887 }
888 }
889
890 checked_map.emplace_back(i);
891 checked_map.emplace_back((i + 1));
892 checked_map.emplace_back((i + 8));
893 checked_map.emplace_back((i + 9));
894
895 } else {
896 RETURN_IF_ERROR(rom()->WriteByte(overworldMapSize + i, 0x00));
897 RETURN_IF_ERROR(rom()->WriteByte(overworldMapSizeHighByte + i, 0x01));
898
899 RETURN_IF_ERROR(rom()->WriteByte(overworldScreenSize + i, 0x01));
900 RETURN_IF_ERROR(rom()->WriteByte(overworldScreenSize + i + 64, 0x01));
901
903 rom()->WriteByte(OverworldScreenSizeForLoading + i, 0x02));
905 rom()->WriteByte(OverworldScreenSizeForLoading + i + 64, 0x02));
907 rom()->WriteByte(OverworldScreenSizeForLoading + i + 128, 0x02));
908
909 RETURN_IF_ERROR(rom()->WriteShort(
910 OverworldScreenTileMapChangeByScreen1 + (i * 2), 0x0060));
911
912 // If the area to the left is a large map, we don't need to add an offset
913 // to it. otherwise leave it the same.
914 // Just to make sure where don't try to read outside of the array.
915 if (i - 1 >= 0 && parent_x_pos != 0) {
916 if (overworld_maps_[i - 1].is_large_map()) {
917 if (overworld_maps_[i - 1].large_index() == 3) {
918 RETURN_IF_ERROR(rom()->WriteShort(
919 OverworldScreenTileMapChangeByScreen1 + (i * 2), 0xF060));
920 }
921 }
922 }
923
924 RETURN_IF_ERROR(rom()->WriteShort(
925 OverworldScreenTileMapChangeByScreen2 + (i * 2), 0x0040));
926
927 if (i + 1 < 64 && parent_x_pos != 7) {
928 if (overworld_maps_[i + 1].is_large_map()) {
929 if (overworld_maps_[i + 1].large_index() == 2) {
930 RETURN_IF_ERROR(rom()->WriteShort(
931 OverworldScreenTileMapChangeByScreen2 + (i * 2), 0xF040));
932 }
933 }
934 }
935
936 RETURN_IF_ERROR(rom()->WriteShort(
937 OverworldScreenTileMapChangeByScreen3 + (i * 2), 0x1800));
938
939 // If the area above is a large map, we don't need to add an offset to it.
940 // otherwise leave it the same.
941 // Just to make sure where don't try to read outside of the array.
942 if (i - 8 >= 0) {
943 // If the area just above us is a large area.
944 if (overworld_maps_[i - 8].is_large_map()) {
945 // If we are under the bottom right of the large area.
946 if (overworld_maps_[i - 8].large_index() == 3) {
947 RETURN_IF_ERROR(rom()->WriteShort(
948 OverworldScreenTileMapChangeByScreen3 + (i * 2), 0x17C0));
949 }
950 }
951 }
952
953 RETURN_IF_ERROR(rom()->WriteShort(
954 OverworldScreenTileMapChangeByScreen4 + (i * 2), 0x1000));
955
956 // If the area below is a large map, we don't need to add an offset to it.
957 // otherwise leave it the same.
958 // Just to make sure where don't try to read outside of the array.
959 if (i + 8 < 64) {
960 // If the area just below us is a large area.
961 if (overworld_maps_[i + 8].is_large_map()) {
962 // If we are on top of the top right of the large area.
963 if (overworld_maps_[i + 8].large_index() == 1) {
964 RETURN_IF_ERROR(rom()->WriteShort(
965 OverworldScreenTileMapChangeByScreen4 + (i * 2), 0x0FC0));
966 }
967 }
968 }
969
970 RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2),
971 (uint16_t)((y_pos * 0x200) - 0xE0)));
972 RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2),
973 (uint16_t)((x_pos * 0x200) - 0x100)));
974
975 RETURN_IF_ERROR(rom()->WriteShort(overworldTransitionPositionX + (i * 2),
976 (x_pos * 0x200)));
977 RETURN_IF_ERROR(rom()->WriteShort(overworldTransitionPositionY + (i * 2),
978 (y_pos * 0x200)));
979
980 checked_map.emplace_back(i);
981 }
982 }
983
984 constexpr int OverworldScreenTileMapChangeMask = 0x1262C;
985
987 rom()->WriteShort(OverworldScreenTileMapChangeMask + 0, 0x1F80));
989 rom()->WriteShort(OverworldScreenTileMapChangeMask + 2, 0x1F80));
991 rom()->WriteShort(OverworldScreenTileMapChangeMask + 4, 0x007F));
993 rom()->WriteShort(OverworldScreenTileMapChangeMask + 6, 0x007F));
994
995 return absl::OkStatus();
996}
997
998namespace {
999std::vector<uint64_t> GetAllTile16(OWMapTiles &map_tiles_) {
1000 std::vector<uint64_t> all_tile_16; // Ensure it's 64 bits
1001
1002 int sx = 0;
1003 int sy = 0;
1004 int c = 0;
1005 OWBlockset tiles_used;
1006 for (int i = 0; i < kNumOverworldMaps; i++) {
1007 if (i < 64) {
1008 tiles_used = map_tiles_.light_world;
1009 } else if (i < 128 && i >= 64) {
1010 tiles_used = map_tiles_.dark_world;
1011 } else {
1012 tiles_used = map_tiles_.special_world;
1013 }
1014
1015 for (int y = 0; y < 32; y += 2) {
1016 for (int x = 0; x < 32; x += 2) {
1017 gfx::Tile32 current_tile(
1018 tiles_used[x + (sx * 32)][y + (sy * 32)],
1019 tiles_used[x + 1 + (sx * 32)][y + (sy * 32)],
1020 tiles_used[x + (sx * 32)][y + 1 + (sy * 32)],
1021 tiles_used[x + 1 + (sx * 32)][y + 1 + (sy * 32)]);
1022
1023 all_tile_16.emplace_back(current_tile.GetPackedValue());
1024 }
1025 }
1026
1027 sx++;
1028 if (sx >= 8) {
1029 sy++;
1030 sx = 0;
1031 }
1032
1033 c++;
1034 if (c >= 64) {
1035 sx = 0;
1036 sy = 0;
1037 c = 0;
1038 }
1039 }
1040
1041 return all_tile_16;
1042}
1043} // namespace
1044
1046 tiles32_unique_.clear();
1047 tiles32_list_.clear();
1048
1049 // Get all tiles16 and packs them into tiles32
1050 std::vector<uint64_t> all_tile_16 = GetAllTile16(map_tiles_);
1051
1052 // Convert to set then back to vector
1053 std::set<uint64_t> unique_tiles_set(all_tile_16.begin(), all_tile_16.end());
1054
1055 std::vector<uint64_t> unique_tiles(all_tile_16);
1056 unique_tiles.assign(unique_tiles_set.begin(), unique_tiles_set.end());
1057
1058 // Create the indexed tiles list
1059 std::unordered_map<uint64_t, uint16_t> all_tiles_indexed;
1060 for (size_t tile32_id = 0; tile32_id < unique_tiles.size(); tile32_id++) {
1061 all_tiles_indexed.insert(
1062 {unique_tiles[tile32_id], static_cast<uint16_t>(tile32_id)});
1063 }
1064
1065 // Add all tiles32 from all maps.
1066 // Convert all tiles32 non-unique IDs into unique array of IDs.
1067 for (int j = 0; j < NumberOfMap32; j++) {
1068 tiles32_list_.emplace_back(all_tiles_indexed[all_tile_16[j]]);
1069 }
1070
1071 // Create the unique tiles list
1072 for (size_t i = 0; i < unique_tiles.size(); ++i) {
1073 tiles32_unique_.emplace_back(gfx::Tile32(unique_tiles[i]));
1074 }
1075
1076 while (tiles32_unique_.size() % 4 != 0) {
1077 gfx::Tile32 padding_tile(0, 0, 0, 0);
1078 tiles32_unique_.emplace_back(padding_tile.GetPackedValue());
1079 }
1080
1081 if (tiles32_unique_.size() > LimitOfMap32) {
1082 return absl::InternalError(absl::StrFormat(
1083 "Number of unique Tiles32: %d Out of: %d\nUnique Tile32 count exceed "
1084 "the limit\nThe ROM Has not been saved\nYou can fill maps with grass "
1085 "tiles to free some space\nOr use the option Clear DW Tiles in the "
1086 "Overworld Menu",
1087 unique_tiles.size(), LimitOfMap32));
1088 }
1089
1090 if (flags()->kLogToConsole) {
1091 std::cout << "Number of unique Tiles32: " << tiles32_unique_.size()
1092 << " Saved:" << tiles32_unique_.size()
1093 << " Out of: " << LimitOfMap32 << std::endl;
1094 }
1095
1096 int v = tiles32_unique_.size();
1097 for (int i = v; i < LimitOfMap32; i++) {
1098 gfx::Tile32 padding_tile(420, 420, 420, 420);
1099 tiles32_unique_.emplace_back(padding_tile.GetPackedValue());
1100 }
1101
1102 return absl::OkStatus();
1103}
1104
1106 int bottomLeft = kMap32TileBLExpanded;
1107 int bottomRight = kMap32TileBRExpanded;
1108 int topRight = kMap32TileTRExpanded;
1109 int limit = 0x8A80;
1110
1111 // Updates the pointers too for the tile32
1112 // Top Right
1114 rom()->WriteLong(0x0176EC, core::PcToSnes(kMap32TileTRExpanded)));
1116 rom()->WriteLong(0x0176F3, core::PcToSnes(kMap32TileTRExpanded + 1)));
1118 rom()->WriteLong(0x0176FA, core::PcToSnes(kMap32TileTRExpanded + 2)));
1120 rom()->WriteLong(0x017701, core::PcToSnes(kMap32TileTRExpanded + 3)));
1122 rom()->WriteLong(0x017708, core::PcToSnes(kMap32TileTRExpanded + 4)));
1124 rom()->WriteLong(0x01771A, core::PcToSnes(kMap32TileTRExpanded + 5)));
1125
1126 // BottomLeft
1128 rom()->WriteLong(0x01772C, core::PcToSnes(kMap32TileBLExpanded)));
1130 rom()->WriteLong(0x017733, core::PcToSnes(kMap32TileBLExpanded + 1)));
1132 rom()->WriteLong(0x01773A, core::PcToSnes(kMap32TileBLExpanded + 2)));
1134 rom()->WriteLong(0x017741, core::PcToSnes(kMap32TileBLExpanded + 3)));
1136 rom()->WriteLong(0x017748, core::PcToSnes(kMap32TileBLExpanded + 4)));
1138 rom()->WriteLong(0x01775A, core::PcToSnes(kMap32TileBLExpanded + 5)));
1139
1140 // BottomRight
1142 rom()->WriteLong(0x01776C, core::PcToSnes(kMap32TileBRExpanded)));
1144 rom()->WriteLong(0x017773, core::PcToSnes(kMap32TileBRExpanded + 1)));
1146 rom()->WriteLong(0x01777A, core::PcToSnes(kMap32TileBRExpanded + 2)));
1148 rom()->WriteLong(0x017781, core::PcToSnes(kMap32TileBRExpanded + 3)));
1150 rom()->WriteLong(0x017788, core::PcToSnes(kMap32TileBRExpanded + 4)));
1152 rom()->WriteLong(0x01779A, core::PcToSnes(kMap32TileBRExpanded + 5)));
1153 return absl::OkStatus();
1154}
1155
1157 core::Logger::log("Saving Map32 Tiles");
1158 constexpr int kMaxUniqueTiles = 0x4540;
1159 constexpr int kTilesPer32x32Tile = 6;
1160
1161 int unique_tile_index = 0;
1162 int num_unique_tiles = tiles32_unique_.size();
1163
1164 for (int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
1165 if (unique_tile_index >= kMaxUniqueTiles) {
1166 return absl::AbortedError("Too many unique tile32 definitions.");
1167 }
1168
1169 // Top Left.
1170 auto top_left = rom()->version_constants().kMap32TileTL;
1171
1172 RETURN_IF_ERROR(rom()->WriteByte(
1173 top_left + i,
1174 (uint8_t)(tiles32_unique_[unique_tile_index].tile0_ & 0xFF)));
1175 RETURN_IF_ERROR(rom()->WriteByte(
1176 top_left + (i + 1),
1177 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile0_ & 0xFF)));
1178 RETURN_IF_ERROR(rom()->WriteByte(
1179 top_left + (i + 2),
1180 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile0_ & 0xFF)));
1181 RETURN_IF_ERROR(rom()->WriteByte(
1182 top_left + (i + 3),
1183 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile0_ & 0xFF)));
1184
1185 RETURN_IF_ERROR(rom()->WriteByte(
1186 top_left + (i + 4),
1187 (uint8_t)(((tiles32_unique_[unique_tile_index].tile0_ >> 4) & 0xF0) +
1188 ((tiles32_unique_[unique_tile_index + 1].tile0_ >> 8) &
1189 0x0F))));
1190 RETURN_IF_ERROR(rom()->WriteByte(
1191 top_left + (i + 5),
1192 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile0_ >> 4) &
1193 0xF0) +
1194 ((tiles32_unique_[unique_tile_index + 3].tile0_ >> 8) &
1195 0x0F))));
1196
1197 // Top Right.
1198 auto top_right = rom()->version_constants().kMap32TileTR;
1199 RETURN_IF_ERROR(rom()->WriteByte(
1200 top_right + i,
1201 (uint8_t)(tiles32_unique_[unique_tile_index].tile1_ & 0xFF)));
1202 RETURN_IF_ERROR(rom()->WriteByte(
1203 top_right + (i + 1),
1204 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile1_ & 0xFF)));
1205 RETURN_IF_ERROR(rom()->WriteByte(
1206 top_right + (i + 2),
1207 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile1_ & 0xFF)));
1208 RETURN_IF_ERROR(rom()->WriteByte(
1209 top_right + (i + 3),
1210 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile1_ & 0xFF)));
1211
1212 RETURN_IF_ERROR(rom()->WriteByte(
1213 top_right + (i + 4),
1214 (uint8_t)(((tiles32_unique_[unique_tile_index].tile1_ >> 4) & 0xF0) |
1215 ((tiles32_unique_[unique_tile_index + 1].tile1_ >> 8) &
1216 0x0F))));
1217 RETURN_IF_ERROR(rom()->WriteByte(
1218 top_right + (i + 5),
1219 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile1_ >> 4) &
1220 0xF0) |
1221 ((tiles32_unique_[unique_tile_index + 3].tile1_ >> 8) &
1222 0x0F))));
1223
1224 // Bottom Left.
1225 const auto map32TilesBL = rom()->version_constants().kMap32TileBL;
1226 RETURN_IF_ERROR(rom()->WriteByte(
1227 map32TilesBL + i,
1228 (uint8_t)(tiles32_unique_[unique_tile_index].tile2_ & 0xFF)));
1229 RETURN_IF_ERROR(rom()->WriteByte(
1230 map32TilesBL + (i + 1),
1231 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile2_ & 0xFF)));
1232 RETURN_IF_ERROR(rom()->WriteByte(
1233 map32TilesBL + (i + 2),
1234 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile2_ & 0xFF)));
1235 RETURN_IF_ERROR(rom()->WriteByte(
1236 map32TilesBL + (i + 3),
1237 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile2_ & 0xFF)));
1238
1239 RETURN_IF_ERROR(rom()->WriteByte(
1240 map32TilesBL + (i + 4),
1241 (uint8_t)(((tiles32_unique_[unique_tile_index].tile2_ >> 4) & 0xF0) |
1242 ((tiles32_unique_[unique_tile_index + 1].tile2_ >> 8) &
1243 0x0F))));
1244 RETURN_IF_ERROR(rom()->WriteByte(
1245 map32TilesBL + (i + 5),
1246 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile2_ >> 4) &
1247 0xF0) |
1248 ((tiles32_unique_[unique_tile_index + 3].tile2_ >> 8) &
1249 0x0F))));
1250
1251 // Bottom Right.
1252 const auto map32TilesBR = rom()->version_constants().kMap32TileBR;
1253 RETURN_IF_ERROR(rom()->WriteByte(
1254 map32TilesBR + i,
1255 (uint8_t)(tiles32_unique_[unique_tile_index].tile3_ & 0xFF)));
1256 RETURN_IF_ERROR(rom()->WriteByte(
1257 map32TilesBR + (i + 1),
1258 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile3_ & 0xFF)));
1259 RETURN_IF_ERROR(rom()->WriteByte(
1260 map32TilesBR + (i + 2),
1261 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile3_ & 0xFF)));
1262 RETURN_IF_ERROR(rom()->WriteByte(
1263 map32TilesBR + (i + 3),
1264 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile3_ & 0xFF)));
1265
1266 RETURN_IF_ERROR(rom()->WriteByte(
1267 map32TilesBR + (i + 4),
1268 (uint8_t)(((tiles32_unique_[unique_tile_index].tile3_ >> 4) & 0xF0) |
1269 ((tiles32_unique_[unique_tile_index + 1].tile3_ >> 8) &
1270 0x0F))));
1271 RETURN_IF_ERROR(rom()->WriteByte(
1272 map32TilesBR + (i + 5),
1273 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile3_ >> 4) &
1274 0xF0) |
1275 ((tiles32_unique_[unique_tile_index + 3].tile3_ >> 8) &
1276 0x0F))));
1277
1278 unique_tile_index += 4;
1279 num_unique_tiles += 2;
1280 }
1281
1282 return absl::OkStatus();
1283}
1284
1286 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x008865),
1288 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x0EDE4F),
1290 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x0EDEE9),
1292
1293 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BBC2D),
1295 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BBC4C),
1297 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BBCC2),
1299 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BBCCB),
1301
1302 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BBEF6),
1304 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BBF23),
1306 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BC041),
1308 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BC9B3),
1310
1311 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BC9BA),
1313 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BC9C1),
1315 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BC9C8),
1317
1318 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BCA40),
1320 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BCA47),
1322 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BCA4E),
1324 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x1BCA55),
1326
1327 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x02F457),
1329 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x02F45E),
1331 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x02F467),
1333 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x02F46E),
1335 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x02F51F),
1337 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x02F526),
1339 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x02F52F),
1341 RETURN_IF_ERROR(rom()->WriteLong(core::SnesToPc(0x02F536),
1343
1344 RETURN_IF_ERROR(rom()->WriteShort(core::SnesToPc(0x02FE1C),
1346 RETURN_IF_ERROR(rom()->WriteShort(core::SnesToPc(0x02FE23),
1348 RETURN_IF_ERROR(rom()->WriteShort(core::SnesToPc(0x02FE2C),
1350 RETURN_IF_ERROR(rom()->WriteShort(core::SnesToPc(0x02FE33),
1352
1353 RETURN_IF_ERROR(rom()->Write(
1354 core::SnesToPc(0x02FD28),
1355 static_cast<uint8_t>(core::PcToSnes(kMap16TilesExpanded) >> 16)));
1356 RETURN_IF_ERROR(rom()->Write(
1357 core::SnesToPc(0x02FD39),
1358 static_cast<uint8_t>(core::PcToSnes(kMap16TilesExpanded) >> 16)));
1359
1360 return absl::OkStatus();
1361}
1362
1364 core::Logger::log("Saving Map16 Tiles");
1365 int tpos = kMap16Tiles;
1366 // 3760
1367 for (int i = 0; i < NumberOfMap16; i += 1) {
1369 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile0_)))
1370 tpos += 2;
1372 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile1_)))
1373 tpos += 2;
1375 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile2_)))
1376 tpos += 2;
1378 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile3_)))
1379 tpos += 2;
1380 }
1381 return absl::OkStatus();
1382}
1383
1385 core::Logger::log("Saving Entrances");
1386 for (int i = 0; i < 129; i++) {
1388 rom()->WriteShort(OWEntranceMap + (i * 2), all_entrances_[i].map_id_))
1390 rom()->WriteShort(OWEntrancePos + (i * 2), all_entrances_[i].map_pos_))
1391 RETURN_IF_ERROR(rom()->WriteByte(OWEntranceEntranceId + i,
1392 all_entrances_[i].entrance_id_))
1393 }
1394
1395 for (int i = 0; i < 0x13; i++) {
1397 rom()->WriteShort(OWHoleArea + (i * 2), all_holes_[i].map_id_))
1399 rom()->WriteShort(OWHolePos + (i * 2), all_holes_[i].map_pos_))
1401 rom()->WriteByte(OWHoleEntrance + i, all_holes_[i].entrance_id_))
1402 }
1403
1404 return absl::OkStatus();
1405}
1406
1407absl::Status Overworld::SaveExits() {
1408 core::Logger::log("Saving Exits");
1409 for (int i = 0; i < 0x4F; i++) {
1411 rom()->WriteShort(OWExitRoomId + (i * 2), all_exits_[i].room_id_));
1412 RETURN_IF_ERROR(rom()->Write(OWExitMapId + i, all_exits_[i].map_id_));
1414 rom()->WriteShort(OWExitVram + (i * 2), all_exits_[i].map_pos_));
1416 rom()->WriteShort(OWExitYScroll + (i * 2), all_exits_[i].y_scroll_));
1418 rom()->WriteShort(OWExitXScroll + (i * 2), all_exits_[i].x_scroll_));
1420 rom()->WriteByte(OWExitYPlayer + (i * 2), all_exits_[i].y_player_));
1422 rom()->WriteByte(OWExitXPlayer + (i * 2), all_exits_[i].x_player_));
1424 rom()->WriteByte(OWExitYCamera + (i * 2), all_exits_[i].y_camera_));
1426 rom()->WriteByte(OWExitXCamera + (i * 2), all_exits_[i].x_camera_));
1428 rom()->WriteByte(OWExitUnk1 + i, all_exits_[i].scroll_mod_y_));
1430 rom()->WriteByte(OWExitUnk2 + i, all_exits_[i].scroll_mod_x_));
1431 RETURN_IF_ERROR(rom()->WriteShort(OWExitDoorType1 + (i * 2),
1432 all_exits_[i].door_type_1_));
1433 RETURN_IF_ERROR(rom()->WriteShort(OWExitDoorType2 + (i * 2),
1434 all_exits_[i].door_type_2_));
1435 }
1436
1437 return absl::OkStatus();
1438}
1439
1440namespace {
1441
1442bool compareItemsArrays(std::vector<OverworldItem> item_array1,
1443 std::vector<OverworldItem> item_array2) {
1444 if (item_array1.size() != item_array2.size()) {
1445 return false;
1446 }
1447
1448 bool match;
1449 for (size_t i = 0; i < item_array1.size(); i++) {
1450 match = false;
1451 for (size_t j = 0; j < item_array2.size(); j++) {
1452 // Check all sprite in 2nd array if one match
1453 if (item_array1[i].x_ == item_array2[j].x_ &&
1454 item_array1[i].y_ == item_array2[j].y_ &&
1455 item_array1[i].id_ == item_array2[j].id_) {
1456 match = true;
1457 break;
1458 }
1459 }
1460
1461 if (!match) {
1462 return false;
1463 }
1464 }
1465
1466 return true;
1467}
1468
1469} // namespace
1470
1471absl::Status Overworld::SaveItems() {
1472 std::vector<std::vector<OverworldItem>> room_items(128);
1473
1474 for (int i = 0; i < 128; i++) {
1475 room_items[i] = std::vector<OverworldItem>();
1476 for (const OverworldItem &item : all_items_) {
1477 if (item.room_map_id_ == i) {
1478 room_items[i].emplace_back(item);
1479 if (item.id_ == 0x86) {
1480 RETURN_IF_ERROR(rom()->WriteWord(
1481 0x16DC5 + (i * 2), (item.game_x_ + (item.game_y_ * 64)) * 2));
1482 }
1483 }
1484 }
1485 }
1486
1487 int data_pos = overworldItemsPointers + 0x100;
1488
1489 int item_pointers[128];
1490 int item_pointers_reuse[128];
1491 int empty_pointer = 0;
1492
1493 for (int i = 0; i < 128; i++) {
1494 item_pointers_reuse[i] = -1;
1495 for (int ci = 0; ci < i; ci++) {
1496 if (room_items[i].empty()) {
1497 item_pointers_reuse[i] = -2;
1498 break;
1499 }
1500
1501 // Copy into separator vectors from i to ci, then ci to end
1502 if (compareItemsArrays(
1503 std::vector<OverworldItem>(room_items[i].begin(),
1504 room_items[i].end()),
1505 std::vector<OverworldItem>(room_items[ci].begin(),
1506 room_items[ci].end()))) {
1507 item_pointers_reuse[i] = ci;
1508 break;
1509 }
1510 }
1511 }
1512
1513 for (int i = 0; i < 128; i++) {
1514 if (item_pointers_reuse[i] == -1) {
1515 item_pointers[i] = data_pos;
1516 for (const OverworldItem &item : room_items[i]) {
1517 short map_pos =
1518 static_cast<short>(((item.game_y_ << 6) + item.game_x_) << 1);
1519
1520 uint32_t data = static_cast<uint8_t>(map_pos & 0xFF) |
1521 static_cast<uint8_t>(map_pos >> 8) |
1522 static_cast<uint8_t>(item.id_);
1523 RETURN_IF_ERROR(rom()->WriteLong(data_pos, data));
1524 data_pos += 3;
1525 }
1526
1527 empty_pointer = data_pos;
1528 RETURN_IF_ERROR(rom()->WriteWord(data_pos, 0xFFFF));
1529 data_pos += 2;
1530 } else if (item_pointers_reuse[i] == -2) {
1531 item_pointers[i] = empty_pointer;
1532 } else {
1533 item_pointers[i] = item_pointers[item_pointers_reuse[i]];
1534 }
1535
1536 int snesaddr = core::PcToSnes(item_pointers[i]);
1538 rom()->WriteWord(overworldItemsPointers + (i * 2), snesaddr));
1539 }
1540
1541 if (data_pos > overworldItemsEndData) {
1542 return absl::AbortedError("Too many items");
1543 }
1544
1545 if (flags()->kLogToConsole) {
1546 std::cout << "End of Items : " << data_pos << std::endl;
1547 }
1548
1549 return absl::OkStatus();
1550}
1551
1553 core::Logger::log("Saving Map Properties");
1554 for (int i = 0; i < 64; i++) {
1555 RETURN_IF_ERROR(rom()->WriteByte(kAreaGfxIdPtr + i,
1556 overworld_maps_[i].area_graphics()));
1558 overworld_maps_[i].area_palette()));
1559 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + i,
1560 overworld_maps_[i].sprite_graphics(0)));
1561 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + 64 + i,
1562 overworld_maps_[i].sprite_graphics(1)));
1563 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + 128 + i,
1564 overworld_maps_[i].sprite_graphics(2)));
1566 overworld_maps_[i].sprite_palette(0)));
1567 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 64 + i,
1568 overworld_maps_[i].sprite_palette(1)));
1569 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 128 + i,
1570 overworld_maps_[i].sprite_palette(2)));
1571 }
1572
1573 for (int i = 64; i < 128; i++) {
1574 RETURN_IF_ERROR(rom()->WriteByte(kAreaGfxIdPtr + i,
1575 overworld_maps_[i].area_graphics()));
1576 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + i,
1577 overworld_maps_[i].sprite_graphics(0)));
1578 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + 64 + i,
1579 overworld_maps_[i].sprite_graphics(1)));
1580 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + 128 + i,
1581 overworld_maps_[i].sprite_graphics(2)));
1583 overworld_maps_[i].area_palette()));
1584 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 64 + i,
1585 overworld_maps_[i].sprite_palette(0)));
1586 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 128 + i,
1587 overworld_maps_[i].sprite_palette(1)));
1588 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 192 + i,
1589 overworld_maps_[i].sprite_palette(2)));
1590 }
1591
1592 return absl::OkStatus();
1593}
1594
1595} // namespace overworld
1596} // namespace zelda3
1597} // namespace app
1598} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:136
VersionConstants version_constants() const
Definition rom.h:482
absl::StatusOr< uint16_t > ReadWord(int offset)
Definition rom.h:257
absl::StatusOr< uint8_t > ReadByte(int offset)
Definition rom.h:249
static void log(std::string message)
Definition common.h:218
Tile composition of four 16x16 tiles.
Definition snes_tile.h:81
uint64_t GetPackedValue() const
Definition snes_tile.h:111
SNES 16-bit tile metadata container.
Definition snes_tile.h:50
std::vector< std::vector< Sprite > > all_sprites_
Definition overworld.h:605
OWBlockset & GetMapTiles(int world_type)
Definition overworld.h:511
std::vector< OverworldItem > all_items_
Definition overworld.h:604
std::vector< OverworldEntrance > all_entrances_
Definition overworld.h:601
std::vector< gfx::Tile32 > tiles32_unique_
Definition overworld.h:599
std::vector< OverworldExit > all_exits_
Definition overworld.h:603
void OrganizeMapTiles(std::vector< uint8_t > &bytes, std::vector< uint8_t > &bytes2, int i, int sx, int sy, int &ttpos)
Definition overworld.cc:198
std::vector< gfx::Tile16 > tiles16_
Definition overworld.h:596
void AssignWorldTiles(int x, int y, int sx, int sy, int tpos, OWBlockset &world)
Definition overworld.cc:186
absl::StatusOr< uint16_t > GetTile16ForTile32(int index, int quadrant, int dimension, const uint32_t *map32address)
Definition overworld.cc:101
std::vector< OverworldEntrance > all_holes_
Definition overworld.h:602
std::vector< OverworldMap > overworld_maps_
Definition overworld.h:600
std::vector< std::vector< uint8_t > > map_data_p2
Definition overworld.h:611
std::vector< std::vector< uint8_t > > map_data_p1
Definition overworld.h:609
absl::Status LoadSpritesFromMap(int spriteStart, int spriteCount, int spriteIndex)
Definition overworld.cc:475
std::vector< uint16_t > tiles32_list_
Definition overworld.h:598
#define RETURN_IF_ERROR(expression)
Definition constants.h:62
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition constants.h:70
uint32_t PcToSnes(uint32_t addr)
Definition common.h:234
std::string UppercaseHexLong(uint32_t dword)
Definition common.cc:119
uint32_t SnesToPc(uint32_t addr) noexcept
Definition common.h:226
std::string UppercaseHexByte(uint8_t byte, bool leading)
Definition common.cc:103
uint8_t * Uncompress(uint8_t const *src, int *const size, int const p_big_endian)
uint8_t * Compress(uint8_t const *const src, int const oldsize, int *const size, int const flag)
TileInfo GetTilesInfo(uint16_t tile)
Definition snes_tile.cc:377
bool compareItemsArrays(std::vector< OverworldItem > item_array1, std::vector< OverworldItem > item_array2)
std::vector< uint64_t > GetAllTile16(OWMapTiles &map_tiles_)
Definition overworld.cc:999
constexpr int overworldTransitionPositionY
Definition overworld.h:420
constexpr int kMap32TileCountExpanded
Definition overworld.h:443
constexpr int kOverworldItemsAddress
Definition overworld.h:71
constexpr int overworldMapParentId
Definition overworld.h:419
constexpr int kMap32TileBLExpanded
Definition overworld.h:441
constexpr int overworldSpriteset
Definition overworld.h:389
constexpr int kMap32TileTRExpanded
Definition overworld.h:440
constexpr int overworldItemsEndData
Definition overworld.h:73
constexpr int OverworldScreenSizeForLoading
Definition overworld.h:423
constexpr int OverworldScreenTileMapChangeByScreen2
Definition overworld.h:427
constexpr int kOverworldMapPaletteIds
Definition overworld.h:385
constexpr int overworldSpritesZelda
Definition overworld.h:394
constexpr int OverworldMapDataOverflow
Definition overworld.h:431
constexpr int OWEntranceEntranceId
Definition overworld.h:316
constexpr int kNumTile16Individual
Definition overworld.h:447
constexpr int kMap32TilesLength
Definition overworld.cc:111
constexpr int NumberOfMap16Ex
Definition overworld.h:450
constexpr int kNumOverworldMaps
Definition overworld.h:446
constexpr int overworldMapSize
Definition overworld.h:408
constexpr int overworldScreenSize
Definition overworld.h:422
constexpr int kMap16TilesExpanded
Definition overworld.h:439
constexpr int OWExitDoorType2
Definition overworld.h:139
constexpr int kOverworldSpritePaletteIds
Definition overworld.h:386
constexpr int OverworldScreenTileMapChangeByScreen3
Definition overworld.h:428
constexpr int transition_target_north
Definition overworld.h:433
constexpr int overworldMapSizeHighByte
Definition overworld.h:411
constexpr int overworldSpritesBegining
Definition overworld.h:392
constexpr int transition_target_west
Definition overworld.h:434
constexpr int kMap32TileBRExpanded
Definition overworld.h:442
constexpr int OverworldScreenTileMapChangeByScreen4
Definition overworld.h:429
constexpr int overworldTransitionPositionX
Definition overworld.h:421
constexpr int overworldSpritesAgahnim
Definition overworld.h:393
constexpr int OverworldScreenTileMapChangeByScreen1
Definition overworld.h:426
constexpr int OWExitDoorType1
Definition overworld.h:138
constexpr int overworldItemsPointers
Definition overworld.h:70
std::vector< std::vector< uint16_t > > OWBlockset
Represents tile32 data for the overworld.
Definition common.h:19
Definition common.cc:22
Overworld map tile32 data.
Definition common.h:24