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(int index, int quadrant,
102 int dimension) {
103 const uint32_t map32address[4] = {rom_.version_constants().kMap32TileTL,
107 ASSIGN_OR_RETURN(auto arg1,
108 rom_.ReadByte(map32address[dimension] + quadrant + (index)));
109 ASSIGN_OR_RETURN(auto arg2, rom_.ReadWord(map32address[dimension] + (index) +
110 (quadrant <= 1 ? 4 : 5)));
111 return (uint16_t)(arg1 +
112 (((arg2 >> (quadrant % 2 == 0 ? 4 : 0)) & 0x0F) * 256));
113}
114
115constexpr int kMap32TilesLength = 0x33F0;
116
118 // Loop through each 32x32 pixel tile in the rom
119 for (int i = 0; i < kMap32TilesLength; i += 6) {
120 // Loop through each quadrant of the 32x32 pixel tile.
121 for (int k = 0; k < 4; k++) {
122 // Generate the 16-bit tile for the current quadrant of the current
123 // 32x32 pixel tile.
124 ASSIGN_OR_RETURN(uint16_t tl,
126 ASSIGN_OR_RETURN(uint16_t tr,
128 ASSIGN_OR_RETURN(uint16_t bl,
130 ASSIGN_OR_RETURN(uint16_t br,
132
133 // Add the generated 16-bit tiles to the tiles32 vector.
134 tiles32_unique_.emplace_back(gfx::Tile32(tl, tr, bl, br));
135 }
136 }
137
138 map_tiles_.light_world.resize(0x200);
139 map_tiles_.dark_world.resize(0x200);
140 map_tiles_.special_world.resize(0x200);
141 for (int i = 0; i < 0x200; i++) {
142 map_tiles_.light_world[i].resize(0x200);
143 map_tiles_.dark_world[i].resize(0x200);
144 map_tiles_.special_world[i].resize(0x200);
145 }
146
147 return absl::OkStatus();
148}
149
151 int tpos = kMap16Tiles;
152 for (int i = 0; i < kNumTile16Individual; i += 1) {
153 gfx::TileInfo t0 = gfx::GetTilesInfo(rom()->toint16(tpos));
154 tpos += 2;
155 gfx::TileInfo t1 = gfx::GetTilesInfo(rom()->toint16(tpos));
156 tpos += 2;
157 gfx::TileInfo t2 = gfx::GetTilesInfo(rom()->toint16(tpos));
158 tpos += 2;
159 gfx::TileInfo t3 = gfx::GetTilesInfo(rom()->toint16(tpos));
160 tpos += 2;
161 tiles16_.emplace_back(t0, t1, t2, t3);
162 }
163}
164
165void Overworld::AssignWorldTiles(int x, int y, int sx, int sy, int tpos,
166 OWBlockset &world) {
167 int position_x1 = (x * 2) + (sx * 32);
168 int position_y1 = (y * 2) + (sy * 32);
169 int position_x2 = (x * 2) + 1 + (sx * 32);
170 int position_y2 = (y * 2) + 1 + (sy * 32);
171 world[position_x1][position_y1] = tiles32_unique_[tpos].tile0_;
172 world[position_x2][position_y1] = tiles32_unique_[tpos].tile1_;
173 world[position_x1][position_y2] = tiles32_unique_[tpos].tile2_;
174 world[position_x2][position_y2] = tiles32_unique_[tpos].tile3_;
175}
176
177void Overworld::OrganizeMapTiles(std::vector<uint8_t> &bytes,
178 std::vector<uint8_t> &bytes2, int i, int sx,
179 int sy, int &ttpos) {
180 for (int y = 0; y < 16; y++) {
181 for (int x = 0; x < 16; x++) {
182 auto tidD = (uint16_t)((bytes2[ttpos] << 8) + bytes[ttpos]);
183 if (int tpos = tidD; tpos < tiles32_unique_.size()) {
184 if (i < 64) {
185 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.light_world);
186 } else if (i < 128 && i >= 64) {
187 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.dark_world);
188 } else {
189 AssignWorldTiles(x, y, sx, sy, tpos, map_tiles_.special_world);
190 }
191 }
192 ttpos += 1;
193 }
194 }
195}
196
198 const auto get_ow_map_gfx_ptr = [this](int index, uint32_t map_ptr) {
199 int p = (rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
200 (rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
201 (rom()->data()[map_ptr + (3 * index)]);
202 return core::SnesToPc(p);
203 };
204
205 uint32_t lowest = 0x0FFFFF;
206 uint32_t highest = 0x0F8000;
207 int sx = 0;
208 int sy = 0;
209 int c = 0;
210 for (int i = 0; i < 160; i++) {
211 auto p1 = get_ow_map_gfx_ptr(
212 i, rom()->version_constants().kCompressedAllMap32PointersHigh);
213 auto p2 = get_ow_map_gfx_ptr(
214 i, rom()->version_constants().kCompressedAllMap32PointersLow);
215
216 int ttpos = 0;
217
218 if (p1 >= highest) highest = p1;
219 if (p2 >= highest) highest = p2;
220
221 if (p1 <= lowest && p1 > 0x0F8000) lowest = p1;
222 if (p2 <= lowest && p2 > 0x0F8000) lowest = p2;
223
224 std::vector<uint8_t> bytes, bytes2;
225 int size1, size2;
226 auto decomp = gfx::lc_lz2::Uncompress(rom()->data() + p2, &size1, 1);
227 bytes.resize(size1);
228 for (int j = 0; j < size1; j++) {
229 bytes[j] = decomp[j];
230 }
231 free(decomp);
232 decomp = gfx::lc_lz2::Uncompress(rom()->data() + p1, &size2, 1);
233 bytes2.resize(size2);
234 for (int j = 0; j < size2; j++) {
235 bytes2[j] = decomp[j];
236 }
237 free(decomp);
238
239 OrganizeMapTiles(bytes, bytes2, i, sx, sy, ttpos);
240
241 sx++;
242 if (sx >= 8) {
243 sy++;
244 sx = 0;
245 }
246
247 c++;
248 if (c >= 64) {
249 sx = 0;
250 sy = 0;
251 c = 0;
252 }
253 }
254 return absl::OkStatus();
255}
256
258 auto size = tiles16_.size();
259 std::vector<std::future<absl::Status>> futures;
260 for (int i = 0; i < kNumOverworldMaps; ++i) {
261 int world_type = 0;
262 if (i >= 64 && i < 0x80) {
263 world_type = 1;
264 } else if (i >= 0x80) {
265 world_type = 2;
266 }
267 auto task_function = [this, i, size, world_type]() {
268 return overworld_maps_[i].BuildMap(size, game_state_, world_type,
269 tiles16_, GetMapTiles(world_type));
270 };
271 futures.emplace_back(std::async(std::launch::async, task_function));
272 }
273
274 // Wait for all tasks to complete and check their results
275 for (auto &future : futures) {
276 absl::Status status = future.get();
277 if (!status.ok()) {
278 return status;
279 }
280 }
281 return absl::OkStatus();
282}
283
285 for (int i = 0; i < 0x200; i++) {
287 rom()->data()[rom()->version_constants().overworldTilesType + i];
288 }
289}
290
292 for (int i = 0; i < 129; i++) {
293 short map_id = rom()->toint16(OWEntranceMap + (i * 2));
294 uint16_t map_pos = rom()->toint16(OWEntrancePos + (i * 2));
295 uint8_t entrance_id = rom_[OWEntranceEntranceId + i];
296 int p = map_pos >> 1;
297 int x = (p % 64);
298 int y = (p >> 6);
299 bool deleted = false;
300 if (map_pos == 0xFFFF) {
301 deleted = true;
302 }
303 all_entrances_.emplace_back(
304 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
305 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id, map_pos,
306 deleted);
307 }
308
309 for (int i = 0; i < 0x13; i++) {
310 auto map_id = (short)((rom_[OWHoleArea + (i * 2) + 1] << 8) +
311 (rom_[OWHoleArea + (i * 2)]));
312 auto map_pos = (short)((rom_[OWHolePos + (i * 2) + 1] << 8) +
313 (rom_[OWHolePos + (i * 2)]));
314 uint8_t entrance_id = (rom_[OWHoleEntrance + i]);
315 int p = (map_pos + 0x400) >> 1;
316 int x = (p % 64);
317 int y = (p >> 6);
318 all_holes_.emplace_back(
319 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
320 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id,
321 (uint16_t)(map_pos + 0x400), true);
322 }
323}
324
325absl::Status Overworld::LoadExits() {
326 const int NumberOfOverworldExits = 0x4F;
327 std::vector<OverworldExit> exits;
328 for (int i = 0; i < NumberOfOverworldExits; i++) {
329 auto rom_data = rom()->data();
330
331 uint16_t exit_room_id;
332 uint16_t exit_map_id;
333 uint16_t exit_vram;
334 uint16_t exit_y_scroll;
335 uint16_t exit_x_scroll;
336 uint16_t exit_y_player;
337 uint16_t exit_x_player;
338 uint16_t exit_y_camera;
339 uint16_t exit_x_camera;
340 uint16_t exit_scroll_mod_y;
341 uint16_t exit_scroll_mod_x;
342 uint16_t exit_door_type_1;
343 uint16_t exit_door_type_2;
344 RETURN_IF_ERROR(rom()->ReadTransaction(
345 exit_room_id, (OWExitRoomId + (i * 2)), exit_map_id, OWExitMapId + i,
346 exit_vram, OWExitVram + (i * 2), exit_y_scroll, OWExitYScroll + (i * 2),
347 exit_x_scroll, OWExitXScroll + (i * 2), exit_y_player,
348 OWExitYPlayer + (i * 2), exit_x_player, OWExitXPlayer + (i * 2),
349 exit_y_camera, OWExitYCamera + (i * 2), exit_x_camera,
350 OWExitXCamera + (i * 2), exit_scroll_mod_y, OWExitUnk1 + i,
351 exit_scroll_mod_x, OWExitUnk2 + i, exit_door_type_1,
352 OWExitDoorType1 + (i * 2), exit_door_type_2,
353 OWExitDoorType2 + (i * 2)));
354
355 uint16_t py = (uint16_t)((rom_data[OWExitYPlayer + (i * 2) + 1] << 8) +
356 rom_data[OWExitYPlayer + (i * 2)]);
357 uint16_t px = (uint16_t)((rom_data[OWExitXPlayer + (i * 2) + 1] << 8) +
358 rom_data[OWExitXPlayer + (i * 2)]);
359
360 if (rom()->flags()->kLogToConsole) {
361 std::cout << "Exit: " << i << " RoomID: " << exit_room_id
362 << " MapID: " << exit_map_id << " VRAM: " << exit_vram
363 << " YScroll: " << exit_y_scroll
364 << " XScroll: " << exit_x_scroll << " YPlayer: " << py
365 << " XPlayer: " << px << " YCamera: " << exit_y_camera
366 << " XCamera: " << exit_x_camera
367 << " ScrollModY: " << exit_scroll_mod_y
368 << " ScrollModX: " << exit_scroll_mod_x
369 << " DoorType1: " << exit_door_type_1
370 << " DoorType2: " << exit_door_type_2 << std::endl;
371 }
372
373 exits.emplace_back(exit_room_id, exit_map_id, exit_vram, exit_y_scroll,
374 exit_x_scroll, py, px, exit_y_camera, exit_x_camera,
375 exit_scroll_mod_y, exit_scroll_mod_x, exit_door_type_1,
376 exit_door_type_2, (px & py) == 0xFFFF);
377 }
379 return absl::OkStatus();
380}
381
382absl::Status Overworld::LoadItems() {
383 ASSIGN_OR_RETURN(uint32_t pointer,
385 uint32_t pointer_pc = core::SnesToPc(pointer); // 1BC2F9 -> 0DC2F9
386 for (int i = 0; i < 128; i++) {
387 ASSIGN_OR_RETURN(uint16_t word_address,
388 rom()->ReadWord(pointer_pc + i * 2));
389 uint32_t addr = (pointer & 0xFF0000) | word_address; // 1B F9 3C
390 addr = core::SnesToPc(addr);
391
392 if (overworld_maps_[i].is_large_map()) {
393 if (overworld_maps_[i].parent() != (uint8_t)i) {
394 continue;
395 }
396 }
397
398 while (true) {
399 ASSIGN_OR_RETURN(uint8_t b1, rom()->ReadByte(addr));
400 ASSIGN_OR_RETURN(uint8_t b2, rom()->ReadByte(addr + 1));
401 ASSIGN_OR_RETURN(uint8_t b3, rom()->ReadByte(addr + 2));
402
403 if (b1 == 0xFF && b2 == 0xFF) {
404 break;
405 }
406
407 int p = (((b2 & 0x1F) << 8) + b1) >> 1;
408
409 int x = p % 64;
410 int y = p >> 6;
411
412 int fakeID = i;
413 if (fakeID >= 64) {
414 fakeID -= 64;
415 }
416
417 int sy = fakeID / 8;
418 int sx = fakeID - (sy * 8);
419
420 all_items_.emplace_back(b3, (uint16_t)i, (x * 16) + (sx * 512),
421 (y * 16) + (sy * 512), false);
422 auto size = all_items_.size();
423
424 all_items_[size - 1].game_x_ = (uint8_t)x;
425 all_items_[size - 1].game_y_ = (uint8_t)y;
426 addr += 3;
427 }
428 }
429 return absl::OkStatus();
430}
431
433 for (int i = 0; i < 3; i++) {
434 all_sprites_.emplace_back();
435 }
436
440 return absl::OkStatus();
441}
442
443absl::Status Overworld::LoadSpritesFromMap(int sprites_per_gamestate_ptr,
444 int num_maps_per_gamestate,
445 int game_state) {
446 for (int i = 0; i < num_maps_per_gamestate; i++) {
447 if (map_parent_[i] != i) continue;
448
449 int current_spr_ptr = sprites_per_gamestate_ptr + (i * 2);
450 ASSIGN_OR_RETURN(auto word_addr, rom()->ReadWord(current_spr_ptr));
451 int sprite_address = core::SnesToPc((0x09 << 0x10) | word_addr);
452 while (true) {
453 ASSIGN_OR_RETURN(uint8_t b1, rom()->ReadByte(sprite_address));
454 ASSIGN_OR_RETURN(uint8_t b2, rom()->ReadByte(sprite_address + 1));
455 ASSIGN_OR_RETURN(uint8_t b3, rom()->ReadByte(sprite_address + 2));
456 if (b1 == 0xFF) break;
457
458 int editor_map_index = i;
459 if (game_state != 0) {
460 if (editor_map_index >= 128)
461 editor_map_index -= 128;
462 else if (editor_map_index >= 64)
463 editor_map_index -= 64;
464 }
465 int mapY = (editor_map_index / 8);
466 int mapX = (editor_map_index % 8);
467
468 int realX = ((b2 & 0x3F) * 16) + mapX * 512;
469 int realY = ((b1 & 0x3F) * 16) + mapY * 512;
470 all_sprites_[game_state].emplace_back(
471 overworld_maps_[i].current_graphics(), (uint8_t)i, b3,
472 (uint8_t)(b2 & 0x3F), (uint8_t)(b1 & 0x3F), realX, realY);
473 // all_sprites_[game_state][i].Draw();
474
475 sprite_address += 3;
476 }
477 }
478
479 return absl::OkStatus();
480}
481
482// ---------------------------------------------------------------------------
483
484absl::Status Overworld::Save(Rom &rom) {
485 rom_ = rom;
486
492
493 return absl::OkStatus();
494}
495
497 core::Logger::log("Saving Overworld Maps");
498
499 // Initialize map pointers
500 std::fill(map_pointers1_id.begin(), map_pointers1_id.end(), -1);
501 std::fill(map_pointers2_id.begin(), map_pointers2_id.end(), -1);
502
503 // Compress and save each map
504 int pos = 0x058000;
505 for (int i = 0; i < 160; i++) {
506 std::vector<uint8_t> single_map_1(512);
507 std::vector<uint8_t> single_map_2(512);
508
509 // Copy tiles32 data to single_map_1 and single_map_2
510 int npos = 0;
511 for (int y = 0; y < 16; y++) {
512 for (int x = 0; x < 16; x++) {
513 auto packed = tiles32_list_[npos + (i * 256)];
514 single_map_1[npos] = packed & 0xFF; // Lower 8 bits
515 single_map_2[npos] = (packed >> 8) & 0xFF; // Next 8 bits
516 npos++;
517 }
518 }
519
520 std::vector<uint8_t> a, b;
521 int size_a, size_b;
522 // Compress single_map_1 and single_map_2
523 auto a_char = gfx::lc_lz2::Compress(single_map_1.data(), 256, &size_a, 1);
524 auto b_char = gfx::lc_lz2::Compress(single_map_2.data(), 256, &size_b, 1);
525 if (a_char == nullptr || b_char == nullptr) {
526 return absl::AbortedError("Error compressing map gfx.");
527 }
528 // Copy the compressed data to a and b
529 a.resize(size_a);
530 b.resize(size_b);
531 // Copy the arrays manually
532 for (int k = 0; k < size_a; k++) {
533 a[k] = a_char[k];
534 }
535 for (int k = 0; k < size_b; k++) {
536 b[k] = b_char[k];
537 }
538
539 // Save compressed data and pointers
540 map_data_p1[i] = std::vector<uint8_t>(size_a);
541 map_data_p2[i] = std::vector<uint8_t>(size_b);
542
543 if ((pos + size_a) >= 0x5FE70 && (pos + size_a) <= 0x60000) {
544 pos = 0x60000;
545 }
546
547 if ((pos + size_a) >= 0x6411F && (pos + size_a) <= 0x70000) {
548 core::Logger::log("Pos set to overflow region for map " +
549 std::to_string(i) + " at " +
551 pos = OverworldMapDataOverflow; // 0x0F8780;
552 }
553
554 auto compareArray = [](const std::vector<uint8_t> &array1,
555 const std::vector<uint8_t> &array2) -> bool {
556 if (array1.size() != array2.size()) {
557 return false;
558 }
559
560 for (size_t i = 0; i < array1.size(); i++) {
561 if (array1[i] != array2[i]) {
562 return false;
563 }
564 }
565
566 return true;
567 };
568
569 for (int j = 0; j < i; j++) {
570 if (compareArray(a, map_data_p1[j])) {
571 // Reuse pointer id j for P1 (a)
572 map_pointers1_id[i] = j;
573 }
574
575 if (compareArray(b, map_data_p2[j])) {
576 map_pointers2_id[i] = j;
577 // Reuse pointer id j for P2 (b)
578 }
579 }
580
581 if (map_pointers1_id[i] == -1) {
582 // Save compressed data and pointer for map1
583 std::copy(a.begin(), a.end(), map_data_p1[i].begin());
584 int snes_pos = core::PcToSnes(pos);
585 map_pointers1[i] = snes_pos;
586 core::Logger::log("Saving map pointers1 and compressed data for map " +
587 core::UppercaseHexByte(i) + " at " +
588 core::UppercaseHexLong(snes_pos));
589 RETURN_IF_ERROR(rom()->WriteLong(
590 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
591 snes_pos));
592 RETURN_IF_ERROR(rom()->WriteVector(pos, a));
593 pos += size_a;
594 } else {
595 // Save pointer for map1
596 int snes_pos = map_pointers1[map_pointers1_id[i]];
597 core::Logger::log("Saving map pointers1 for map " +
598 core::UppercaseHexByte(i) + " at " +
599 core::UppercaseHexLong(snes_pos));
600 RETURN_IF_ERROR(rom()->WriteLong(
601 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
602 snes_pos));
603 }
604
605 if ((pos + b.size()) >= 0x5FE70 && (pos + b.size()) <= 0x60000) {
606 pos = 0x60000;
607 }
608
609 if ((pos + b.size()) >= 0x6411F && (pos + b.size()) <= 0x70000) {
610 core::Logger::log("Pos set to overflow region for map " +
611 core::UppercaseHexByte(i) + " at " +
614 }
615
616 if (map_pointers2_id[i] == -1) {
617 // Save compressed data and pointer for map2
618 std::copy(b.begin(), b.end(), map_data_p2[i].begin());
619 int snes_pos = core::PcToSnes(pos);
620 map_pointers2[i] = snes_pos;
621 core::Logger::log("Saving map pointers2 and compressed data for map " +
622 core::UppercaseHexByte(i) + " at " +
623 core::UppercaseHexLong(snes_pos));
624 RETURN_IF_ERROR(rom()->WriteLong(
625 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
626 snes_pos));
627 RETURN_IF_ERROR(rom()->WriteVector(pos, b));
628 pos += size_b;
629 } else {
630 // Save pointer for map2
631 int snes_pos = map_pointers2[map_pointers2_id[i]];
632 core::Logger::log("Saving map pointers2 for map " +
633 core::UppercaseHexByte(i) + " at " +
634 core::UppercaseHexLong(snes_pos));
635 RETURN_IF_ERROR(rom()->WriteLong(
636 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
637 snes_pos));
638 }
639 }
640
641 // Check if too many maps data
642 if (pos > 0x137FFF) {
643 std::cerr << "Too many maps data " << std::hex << pos << std::endl;
644 return absl::AbortedError("Too many maps data " + std::to_string(pos));
645 }
646
647 // Save large maps
649
650 return absl::OkStatus();
651}
652
654 core::Logger::log("Saving Large Maps");
655 std::vector<uint8_t> checked_map;
656
657 for (int i = 0; i < 0x40; i++) {
658 int y_pos = i / 8;
659 int x_pos = i % 8;
660 int parent_y_pos = overworld_maps_[i].parent() / 8;
661 int parent_x_pos = overworld_maps_[i].parent() % 8;
662
663 // Always write the map parent since it should not matter
665 rom()->Write(overworldMapParentId + i, overworld_maps_[i].parent()))
666
667 if (std::find(checked_map.begin(), checked_map.end(), i) !=
668 checked_map.end()) {
669 continue;
670 }
671
672 // If it's large then save parent pos *
673 // 0x200 otherwise pos * 0x200
674 if (overworld_maps_[i].is_large_map()) {
675 const uint8_t large_map_offsets[] = {0, 1, 8, 9};
676 for (const auto &offset : large_map_offsets) {
677 // Check 1
678 RETURN_IF_ERROR(rom()->WriteByte(overworldMapSize + i + offset, 0x20));
679 // Check 2
681 rom()->WriteByte(overworldMapSizeHighByte + i + offset, 0x03));
682 // Check 3
684 rom()->WriteByte(overworldScreenSize + i + offset, 0x00));
686 rom()->WriteByte(overworldScreenSize + i + offset + 64, 0x00));
687 // Check 4
689 rom()->WriteByte(OverworldScreenSizeForLoading + i + offset, 0x04));
690 RETURN_IF_ERROR(rom()->WriteByte(
691 OverworldScreenSizeForLoading + i + offset + 64, 0x04));
692 RETURN_IF_ERROR(rom()->WriteByte(
693 OverworldScreenSizeForLoading + i + offset + 128, 0x04));
694 }
695
696 // Check 5 and 6
698 rom()->WriteShort(transition_target_north + (i * 2),
699 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
701 rom()->WriteShort(transition_target_west + (i * 2),
702 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
703
705 rom()->WriteShort(transition_target_north + (i * 2) + 2,
706 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
708 rom()->WriteShort(transition_target_west + (i * 2) + 2,
709 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
710
712 rom()->WriteShort(transition_target_north + (i * 2) + 16,
713 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
715 rom()->WriteShort(transition_target_west + (i * 2) + 16,
716 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
717
719 rom()->WriteShort(transition_target_north + (i * 2) + 18,
720 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
722 rom()->WriteShort(transition_target_west + (i * 2) + 18,
723 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
724
725 // Check 7 and 8
726 RETURN_IF_ERROR(rom()->WriteShort(overworldTransitionPositionX + (i * 2),
727 (parent_x_pos * 0x200)));
728 RETURN_IF_ERROR(rom()->WriteShort(overworldTransitionPositionY + (i * 2),
729 (parent_y_pos * 0x200)));
730
731 RETURN_IF_ERROR(rom()->WriteShort(
732 overworldTransitionPositionX + (i * 2) + 02, (parent_x_pos * 0x200)));
733 RETURN_IF_ERROR(rom()->WriteShort(
734 overworldTransitionPositionY + (i * 2) + 02, (parent_y_pos * 0x200)));
735
736 // problematic
737 RETURN_IF_ERROR(rom()->WriteShort(
738 overworldTransitionPositionX + (i * 2) + 16, (parent_x_pos * 0x200)));
739 RETURN_IF_ERROR(rom()->WriteShort(
740 overworldTransitionPositionY + (i * 2) + 16, (parent_y_pos * 0x200)));
741
742 RETURN_IF_ERROR(rom()->WriteShort(
743 overworldTransitionPositionX + (i * 2) + 18, (parent_x_pos * 0x200)));
744 RETURN_IF_ERROR(rom()->WriteShort(
745 overworldTransitionPositionY + (i * 2) + 18, (parent_y_pos * 0x200)));
746
747 // Check 9
748 RETURN_IF_ERROR(rom()->WriteShort(
749 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 00, 0x0060));
750 RETURN_IF_ERROR(rom()->WriteShort(
751 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 02, 0x0060));
752
753 // If parentX == 0 then lower submaps == 0x0060 too
754 if (parent_x_pos == 0) {
755 RETURN_IF_ERROR(rom()->WriteShort(
756 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 16, 0x0060));
757 RETURN_IF_ERROR(rom()->WriteShort(
758 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 18, 0x0060));
759 } else {
760 // Otherwise lower submaps == 0x1060
761 RETURN_IF_ERROR(rom()->WriteShort(
762 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 16, 0x1060));
763 RETURN_IF_ERROR(rom()->WriteShort(
764 OverworldScreenTileMapChangeByScreen1 + (i * 2) + 18, 0x1060));
765
766 // If the area to the left is a large map, we don't need to add an
767 // offset to it. otherwise leave it the same. Just to make sure where
768 // don't try to read outside of the array.
769 if ((i - 1) >= 0) {
770 // If the area to the left is a large area.
771 if (overworld_maps_[i - 1].is_large_map()) {
772 // If the area to the left is the bottom right of a large area.
773 if (overworld_maps_[i - 1].large_index() == 1) {
774 RETURN_IF_ERROR(rom()->WriteShort(
776 0x0060));
777 }
778 }
779 }
780 }
781
782 // Always 0x0080
783 RETURN_IF_ERROR(rom()->WriteShort(
784 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 00, 0x0080));
785 RETURN_IF_ERROR(rom()->WriteShort(
786 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 2, 0x0080));
787 // Lower always 0x1080
788 RETURN_IF_ERROR(rom()->WriteShort(
789 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 16, 0x1080));
790 RETURN_IF_ERROR(rom()->WriteShort(
791 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 18, 0x1080));
792
793 // If the area to the right is a large map, we don't need to add an offset
794 // to it. otherwise leave it the same. Just to make sure where don't try
795 // to read outside of the array.
796 if ((i + 2) < 64) {
797 // If the area to the right is a large area.
798 if (overworld_maps_[i + 2].is_large_map()) {
799 // If the area to the right is the top left of a large area.
800 if (overworld_maps_[i + 2].large_index() == 0) {
801 RETURN_IF_ERROR(rom()->WriteShort(
802 OverworldScreenTileMapChangeByScreen2 + (i * 2) + 18, 0x0080));
803 }
804 }
805 }
806
807 // Always 0x1800
808 RETURN_IF_ERROR(rom()->WriteShort(
809 OverworldScreenTileMapChangeByScreen3 + (i * 2), 0x1800));
810 RETURN_IF_ERROR(rom()->WriteShort(
811 OverworldScreenTileMapChangeByScreen3 + (i * 2) + 16, 0x1800));
812 // Right side is always 0x1840
813 RETURN_IF_ERROR(rom()->WriteShort(
814 OverworldScreenTileMapChangeByScreen3 + (i * 2) + 2, 0x1840));
815 RETURN_IF_ERROR(rom()->WriteShort(
816 OverworldScreenTileMapChangeByScreen3 + (i * 2) + 18, 0x1840));
817
818 // If the area above is a large map, we don't need to add an offset to it.
819 // otherwise leave it the same.
820 // Just to make sure where don't try to read outside of the array.
821 if (i - 8 >= 0) {
822 // If the area just above us is a large area.
823 if (overworld_maps_[i - 8].is_large_map()) {
824 // If the area just above us is the bottom left of a large area.
825 if (overworld_maps_[i - 8].large_index() == 2) {
826 RETURN_IF_ERROR(rom()->WriteShort(
827 OverworldScreenTileMapChangeByScreen3 + (i * 2) + 02, 0x1800));
828 }
829 }
830 }
831
832 // Always 0x2000
833 RETURN_IF_ERROR(rom()->WriteShort(
834 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 00, 0x2000));
835 RETURN_IF_ERROR(rom()->WriteShort(
836 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 16, 0x2000));
837 // Right side always 0x2040
838 RETURN_IF_ERROR(rom()->WriteShort(
839 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 2, 0x2040));
840 RETURN_IF_ERROR(rom()->WriteShort(
841 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 18, 0x2040));
842
843 // If the area below is a large map, we don't need to add an offset to it.
844 // otherwise leave it the same.
845 // Just to make sure where don't try to read outside of the array.
846 if (i + 16 < 64) {
847 // If the area just below us is a large area.
848 if (overworld_maps_[i + 16].is_large_map()) {
849 // If the area just below us is the top left of a large area.
850 if (overworld_maps_[i + 16].large_index() == 0) {
851 RETURN_IF_ERROR(rom()->WriteShort(
852 OverworldScreenTileMapChangeByScreen4 + (i * 2) + 18, 0x2000));
853 }
854 }
855 }
856
857 checked_map.emplace_back(i);
858 checked_map.emplace_back((i + 1));
859 checked_map.emplace_back((i + 8));
860 checked_map.emplace_back((i + 9));
861
862 } else {
863 RETURN_IF_ERROR(rom()->WriteByte(overworldMapSize + i, 0x00));
864 RETURN_IF_ERROR(rom()->WriteByte(overworldMapSizeHighByte + i, 0x01));
865
866 RETURN_IF_ERROR(rom()->WriteByte(overworldScreenSize + i, 0x01));
867 RETURN_IF_ERROR(rom()->WriteByte(overworldScreenSize + i + 64, 0x01));
868
870 rom()->WriteByte(OverworldScreenSizeForLoading + i, 0x02));
872 rom()->WriteByte(OverworldScreenSizeForLoading + i + 64, 0x02));
874 rom()->WriteByte(OverworldScreenSizeForLoading + i + 128, 0x02));
875
876 RETURN_IF_ERROR(rom()->WriteShort(
877 OverworldScreenTileMapChangeByScreen1 + (i * 2), 0x0060));
878
879 // If the area to the left is a large map, we don't need to add an offset
880 // to it. otherwise leave it the same.
881 // Just to make sure where don't try to read outside of the array.
882 if (i - 1 >= 0 && parent_x_pos != 0) {
883 if (overworld_maps_[i - 1].is_large_map()) {
884 if (overworld_maps_[i - 1].large_index() == 3) {
885 RETURN_IF_ERROR(rom()->WriteShort(
886 OverworldScreenTileMapChangeByScreen1 + (i * 2), 0xF060));
887 }
888 }
889 }
890
891 RETURN_IF_ERROR(rom()->WriteShort(
892 OverworldScreenTileMapChangeByScreen2 + (i * 2), 0x0040));
893
894 if (i + 1 < 64 && parent_x_pos != 7) {
895 if (overworld_maps_[i + 1].is_large_map()) {
896 if (overworld_maps_[i + 1].large_index() == 2) {
897 RETURN_IF_ERROR(rom()->WriteShort(
898 OverworldScreenTileMapChangeByScreen2 + (i * 2), 0xF040));
899 }
900 }
901 }
902
903 RETURN_IF_ERROR(rom()->WriteShort(
904 OverworldScreenTileMapChangeByScreen3 + (i * 2), 0x1800));
905
906 // If the area above is a large map, we don't need to add an offset to it.
907 // otherwise leave it the same.
908 // Just to make sure where don't try to read outside of the array.
909 if (i - 8 >= 0) {
910 // If the area just above us is a large area.
911 if (overworld_maps_[i - 8].is_large_map()) {
912 // If we are under the bottom right of the large area.
913 if (overworld_maps_[i - 8].large_index() == 3) {
914 RETURN_IF_ERROR(rom()->WriteShort(
915 OverworldScreenTileMapChangeByScreen3 + (i * 2), 0x17C0));
916 }
917 }
918 }
919
920 RETURN_IF_ERROR(rom()->WriteShort(
921 OverworldScreenTileMapChangeByScreen4 + (i * 2), 0x1000));
922
923 // If the area below is a large map, we don't need to add an offset to it.
924 // otherwise leave it the same.
925 // Just to make sure where don't try to read outside of the array.
926 if (i + 8 < 64) {
927 // If the area just below us is a large area.
928 if (overworld_maps_[i + 8].is_large_map()) {
929 // If we are on top of the top right of the large area.
930 if (overworld_maps_[i + 8].large_index() == 1) {
931 RETURN_IF_ERROR(rom()->WriteShort(
932 OverworldScreenTileMapChangeByScreen4 + (i * 2), 0x0FC0));
933 }
934 }
935 }
936
937 RETURN_IF_ERROR(rom()->WriteShort(transition_target_north + (i * 2),
938 (uint16_t)((y_pos * 0x200) - 0xE0)));
939 RETURN_IF_ERROR(rom()->WriteShort(transition_target_west + (i * 2),
940 (uint16_t)((x_pos * 0x200) - 0x100)));
941
942 RETURN_IF_ERROR(rom()->WriteShort(overworldTransitionPositionX + (i * 2),
943 (x_pos * 0x200)));
944 RETURN_IF_ERROR(rom()->WriteShort(overworldTransitionPositionY + (i * 2),
945 (y_pos * 0x200)));
946
947 checked_map.emplace_back(i);
948 }
949 }
950
951 constexpr int OverworldScreenTileMapChangeMask = 0x1262C;
952
954 rom()->WriteShort(OverworldScreenTileMapChangeMask + 0, 0x1F80));
956 rom()->WriteShort(OverworldScreenTileMapChangeMask + 2, 0x1F80));
958 rom()->WriteShort(OverworldScreenTileMapChangeMask + 4, 0x007F));
960 rom()->WriteShort(OverworldScreenTileMapChangeMask + 6, 0x007F));
961
962 return absl::OkStatus();
963}
964
965namespace {
966std::vector<uint64_t> GetAllTile16(OWMapTiles &map_tiles_) {
967 std::vector<uint64_t> all_tile_16; // Ensure it's 64 bits
968
969 int sx = 0;
970 int sy = 0;
971 int c = 0;
972 OWBlockset tiles_used;
973 for (int i = 0; i < kNumOverworldMaps; i++) {
974 if (i < 64) {
975 tiles_used = map_tiles_.light_world;
976 } else if (i < 128 && i >= 64) {
977 tiles_used = map_tiles_.dark_world;
978 } else {
979 tiles_used = map_tiles_.special_world;
980 }
981
982 for (int y = 0; y < 32; y += 2) {
983 for (int x = 0; x < 32; x += 2) {
984 gfx::Tile32 current_tile(
985 tiles_used[x + (sx * 32)][y + (sy * 32)],
986 tiles_used[x + 1 + (sx * 32)][y + (sy * 32)],
987 tiles_used[x + (sx * 32)][y + 1 + (sy * 32)],
988 tiles_used[x + 1 + (sx * 32)][y + 1 + (sy * 32)]);
989
990 all_tile_16.emplace_back(current_tile.GetPackedValue());
991 }
992 }
993
994 sx++;
995 if (sx >= 8) {
996 sy++;
997 sx = 0;
998 }
999
1000 c++;
1001 if (c >= 64) {
1002 sx = 0;
1003 sy = 0;
1004 c = 0;
1005 }
1006 }
1007
1008 return all_tile_16;
1009}
1010} // namespace
1011
1013 tiles32_unique_.clear();
1014 tiles32_list_.clear();
1015
1016 // Get all tiles16 and packs them into tiles32
1017 std::vector<uint64_t> all_tile_16 = GetAllTile16(map_tiles_);
1018
1019 // Convert to set then back to vector
1020 std::set<uint64_t> unique_tiles_set(all_tile_16.begin(), all_tile_16.end());
1021
1022 std::vector<uint64_t> unique_tiles(all_tile_16);
1023 unique_tiles.assign(unique_tiles_set.begin(), unique_tiles_set.end());
1024
1025 // Create the indexed tiles list
1026 std::unordered_map<uint64_t, uint16_t> all_tiles_indexed;
1027 for (size_t tile32_id = 0; tile32_id < unique_tiles.size(); tile32_id++) {
1028 all_tiles_indexed.insert(
1029 {unique_tiles[tile32_id], static_cast<uint16_t>(tile32_id)});
1030 }
1031
1032 // Add all tiles32 from all maps.
1033 // Convert all tiles32 non-unique IDs into unique array of IDs.
1034 for (int j = 0; j < NumberOfMap32; j++) {
1035 tiles32_list_.emplace_back(all_tiles_indexed[all_tile_16[j]]);
1036 }
1037
1038 // Create the unique tiles list
1039 for (size_t i = 0; i < unique_tiles.size(); ++i) {
1040 tiles32_unique_.emplace_back(gfx::Tile32(unique_tiles[i]));
1041 }
1042
1043 while (tiles32_unique_.size() % 4 != 0) {
1044 gfx::Tile32 padding_tile(0, 0, 0, 0);
1045 tiles32_unique_.emplace_back(padding_tile.GetPackedValue());
1046 }
1047
1048 if (tiles32_unique_.size() > LimitOfMap32) {
1049 return absl::InternalError(absl::StrFormat(
1050 "Number of unique Tiles32: %d Out of: %d\nUnique Tile32 count exceed "
1051 "the limit\nThe ROM Has not been saved\nYou can fill maps with grass "
1052 "tiles to free some space\nOr use the option Clear DW Tiles in the "
1053 "Overworld Menu",
1054 unique_tiles.size(), LimitOfMap32));
1055 }
1056
1057 if (flags()->kLogToConsole) {
1058 std::cout << "Number of unique Tiles32: " << tiles32_unique_.size()
1059 << " Saved:" << tiles32_unique_.size()
1060 << " Out of: " << LimitOfMap32 << std::endl;
1061 }
1062
1063 int v = tiles32_unique_.size();
1064 for (int i = v; i < LimitOfMap32; i++) {
1065 gfx::Tile32 padding_tile(420, 420, 420, 420);
1066 tiles32_unique_.emplace_back(padding_tile.GetPackedValue());
1067 }
1068
1069 return absl::OkStatus();
1070}
1071
1073 core::Logger::log("Saving Map32 Tiles");
1074 constexpr int kMaxUniqueTiles = 0x4540;
1075 constexpr int kTilesPer32x32Tile = 6;
1076
1077 int unique_tile_index = 0;
1078 int num_unique_tiles = tiles32_unique_.size();
1079
1080 for (int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
1081 if (unique_tile_index >= kMaxUniqueTiles) {
1082 return absl::AbortedError("Too many unique tile32 definitions.");
1083 }
1084
1085 // Top Left.
1086 auto top_left = rom()->version_constants().kMap32TileTL;
1087
1088 RETURN_IF_ERROR(rom()->WriteByte(
1089 top_left + i,
1090 (uint8_t)(tiles32_unique_[unique_tile_index].tile0_ & 0xFF)));
1091 RETURN_IF_ERROR(rom()->WriteByte(
1092 top_left + (i + 1),
1093 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile0_ & 0xFF)));
1094 RETURN_IF_ERROR(rom()->WriteByte(
1095 top_left + (i + 2),
1096 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile0_ & 0xFF)));
1097 RETURN_IF_ERROR(rom()->WriteByte(
1098 top_left + (i + 3),
1099 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile0_ & 0xFF)));
1100
1101 RETURN_IF_ERROR(rom()->WriteByte(
1102 top_left + (i + 4),
1103 (uint8_t)(((tiles32_unique_[unique_tile_index].tile0_ >> 4) & 0xF0) +
1104 ((tiles32_unique_[unique_tile_index + 1].tile0_ >> 8) &
1105 0x0F))));
1106 RETURN_IF_ERROR(rom()->WriteByte(
1107 top_left + (i + 5),
1108 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile0_ >> 4) &
1109 0xF0) +
1110 ((tiles32_unique_[unique_tile_index + 3].tile0_ >> 8) &
1111 0x0F))));
1112
1113 // Top Right.
1114 auto top_right = rom()->version_constants().kMap32TileTR;
1115 RETURN_IF_ERROR(rom()->WriteByte(
1116 top_right + i,
1117 (uint8_t)(tiles32_unique_[unique_tile_index].tile1_ & 0xFF)));
1118 RETURN_IF_ERROR(rom()->WriteByte(
1119 top_right + (i + 1),
1120 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile1_ & 0xFF)));
1121 RETURN_IF_ERROR(rom()->WriteByte(
1122 top_right + (i + 2),
1123 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile1_ & 0xFF)));
1124 RETURN_IF_ERROR(rom()->WriteByte(
1125 top_right + (i + 3),
1126 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile1_ & 0xFF)));
1127
1128 RETURN_IF_ERROR(rom()->WriteByte(
1129 top_right + (i + 4),
1130 (uint8_t)(((tiles32_unique_[unique_tile_index].tile1_ >> 4) & 0xF0) |
1131 ((tiles32_unique_[unique_tile_index + 1].tile1_ >> 8) &
1132 0x0F))));
1133 RETURN_IF_ERROR(rom()->WriteByte(
1134 top_right + (i + 5),
1135 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile1_ >> 4) &
1136 0xF0) |
1137 ((tiles32_unique_[unique_tile_index + 3].tile1_ >> 8) &
1138 0x0F))));
1139
1140 // Bottom Left.
1141 const auto map32TilesBL = rom()->version_constants().kMap32TileBL;
1142 RETURN_IF_ERROR(rom()->WriteByte(
1143 map32TilesBL + i,
1144 (uint8_t)(tiles32_unique_[unique_tile_index].tile2_ & 0xFF)));
1145 RETURN_IF_ERROR(rom()->WriteByte(
1146 map32TilesBL + (i + 1),
1147 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile2_ & 0xFF)));
1148 RETURN_IF_ERROR(rom()->WriteByte(
1149 map32TilesBL + (i + 2),
1150 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile2_ & 0xFF)));
1151 RETURN_IF_ERROR(rom()->WriteByte(
1152 map32TilesBL + (i + 3),
1153 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile2_ & 0xFF)));
1154
1155 RETURN_IF_ERROR(rom()->WriteByte(
1156 map32TilesBL + (i + 4),
1157 (uint8_t)(((tiles32_unique_[unique_tile_index].tile2_ >> 4) & 0xF0) |
1158 ((tiles32_unique_[unique_tile_index + 1].tile2_ >> 8) &
1159 0x0F))));
1160 RETURN_IF_ERROR(rom()->WriteByte(
1161 map32TilesBL + (i + 5),
1162 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile2_ >> 4) &
1163 0xF0) |
1164 ((tiles32_unique_[unique_tile_index + 3].tile2_ >> 8) &
1165 0x0F))));
1166
1167 // Bottom Right.
1168 const auto map32TilesBR = rom()->version_constants().kMap32TileBR;
1169 RETURN_IF_ERROR(rom()->WriteByte(
1170 map32TilesBR + i,
1171 (uint8_t)(tiles32_unique_[unique_tile_index].tile3_ & 0xFF)));
1172 RETURN_IF_ERROR(rom()->WriteByte(
1173 map32TilesBR + (i + 1),
1174 (uint8_t)(tiles32_unique_[unique_tile_index + 1].tile3_ & 0xFF)));
1175 RETURN_IF_ERROR(rom()->WriteByte(
1176 map32TilesBR + (i + 2),
1177 (uint8_t)(tiles32_unique_[unique_tile_index + 2].tile3_ & 0xFF)));
1178 RETURN_IF_ERROR(rom()->WriteByte(
1179 map32TilesBR + (i + 3),
1180 (uint8_t)(tiles32_unique_[unique_tile_index + 3].tile3_ & 0xFF)));
1181
1182 RETURN_IF_ERROR(rom()->WriteByte(
1183 map32TilesBR + (i + 4),
1184 (uint8_t)(((tiles32_unique_[unique_tile_index].tile3_ >> 4) & 0xF0) |
1185 ((tiles32_unique_[unique_tile_index + 1].tile3_ >> 8) &
1186 0x0F))));
1187 RETURN_IF_ERROR(rom()->WriteByte(
1188 map32TilesBR + (i + 5),
1189 (uint8_t)(((tiles32_unique_[unique_tile_index + 2].tile3_ >> 4) &
1190 0xF0) |
1191 ((tiles32_unique_[unique_tile_index + 3].tile3_ >> 8) &
1192 0x0F))));
1193
1194 unique_tile_index += 4;
1195 num_unique_tiles += 2;
1196 }
1197
1198 return absl::OkStatus();
1199}
1200
1202 core::Logger::log("Saving Map16 Tiles");
1203 int tpos = kMap16Tiles;
1204 // 3760
1205 for (int i = 0; i < NumberOfMap16; i += 1) {
1207 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile0_)))
1208 tpos += 2;
1210 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile1_)))
1211 tpos += 2;
1213 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile2_)))
1214 tpos += 2;
1216 rom()->WriteShort(tpos, TileInfoToShort(tiles16_[i].tile3_)))
1217 tpos += 2;
1218 }
1219 return absl::OkStatus();
1220}
1221
1223 core::Logger::log("Saving Entrances");
1224 for (int i = 0; i < 129; i++) {
1226 rom()->WriteShort(OWEntranceMap + (i * 2), all_entrances_[i].map_id_))
1228 rom()->WriteShort(OWEntrancePos + (i * 2), all_entrances_[i].map_pos_))
1229 RETURN_IF_ERROR(rom()->WriteByte(OWEntranceEntranceId + i,
1230 all_entrances_[i].entrance_id_))
1231 }
1232
1233 for (int i = 0; i < 0x13; i++) {
1235 rom()->WriteShort(OWHoleArea + (i * 2), all_holes_[i].map_id_))
1237 rom()->WriteShort(OWHolePos + (i * 2), all_holes_[i].map_pos_))
1239 rom()->WriteByte(OWHoleEntrance + i, all_holes_[i].entrance_id_))
1240 }
1241
1242 return absl::OkStatus();
1243}
1244
1245absl::Status Overworld::SaveExits() {
1246 core::Logger::log("Saving Exits");
1247 for (int i = 0; i < 0x4F; i++) {
1249 rom()->WriteShort(OWExitRoomId + (i * 2), all_exits_[i].room_id_));
1250 RETURN_IF_ERROR(rom()->Write(OWExitMapId + i, all_exits_[i].map_id_));
1252 rom()->WriteShort(OWExitVram + (i * 2), all_exits_[i].map_pos_));
1254 rom()->WriteShort(OWExitYScroll + (i * 2), all_exits_[i].y_scroll_));
1256 rom()->WriteShort(OWExitXScroll + (i * 2), all_exits_[i].x_scroll_));
1258 rom()->WriteByte(OWExitYPlayer + (i * 2), all_exits_[i].y_player_));
1260 rom()->WriteByte(OWExitXPlayer + (i * 2), all_exits_[i].x_player_));
1262 rom()->WriteByte(OWExitYCamera + (i * 2), all_exits_[i].y_camera_));
1264 rom()->WriteByte(OWExitXCamera + (i * 2), all_exits_[i].x_camera_));
1266 rom()->WriteByte(OWExitUnk1 + i, all_exits_[i].scroll_mod_y_));
1268 rom()->WriteByte(OWExitUnk2 + i, all_exits_[i].scroll_mod_x_));
1269 RETURN_IF_ERROR(rom()->WriteShort(OWExitDoorType1 + (i * 2),
1270 all_exits_[i].door_type_1_));
1271 RETURN_IF_ERROR(rom()->WriteShort(OWExitDoorType2 + (i * 2),
1272 all_exits_[i].door_type_2_));
1273 }
1274
1275 return absl::OkStatus();
1276}
1277
1278namespace {
1279
1280bool compareItemsArrays(std::vector<OverworldItem> itemArray1,
1281 std::vector<OverworldItem> itemArray2) {
1282 if (itemArray1.size() != itemArray2.size()) {
1283 return false;
1284 }
1285
1286 bool match;
1287 for (size_t i = 0; i < itemArray1.size(); i++) {
1288 match = false;
1289 for (size_t j = 0; j < itemArray2.size(); j++) {
1290 // Check all sprite in 2nd array if one match
1291 if (itemArray1[i].x_ == itemArray2[j].x_ &&
1292 itemArray1[i].y_ == itemArray2[j].y_ &&
1293 itemArray1[i].id_ == itemArray2[j].id_) {
1294 match = true;
1295 break;
1296 }
1297 }
1298
1299 if (!match) {
1300 return false;
1301 }
1302 }
1303
1304 return true;
1305}
1306
1307} // namespace
1308
1309absl::Status Overworld::SaveItems() {
1310 std::vector<std::vector<OverworldItem>> room_items(128);
1311
1312 for (int i = 0; i < 128; i++) {
1313 room_items[i] = std::vector<OverworldItem>();
1314 for (const OverworldItem &item : all_items_) {
1315 if (item.room_map_id_ == i) {
1316 room_items[i].emplace_back(item);
1317 if (item.id_ == 0x86) {
1318 RETURN_IF_ERROR(rom()->WriteWord(
1319 0x16DC5 + (i * 2), (item.game_x_ + (item.game_y_ * 64)) * 2));
1320 }
1321 }
1322 }
1323 }
1324
1325 int data_pos = overworldItemsPointers + 0x100;
1326
1327 int item_pointers[128];
1328 int item_pointers_reuse[128];
1329 int empty_pointer = 0;
1330
1331 for (int i = 0; i < 128; i++) {
1332 item_pointers_reuse[i] = -1;
1333 for (int ci = 0; ci < i; ci++) {
1334 if (room_items[i].empty()) {
1335 item_pointers_reuse[i] = -2;
1336 break;
1337 }
1338
1339 // Copy into separator vectors from i to ci, then ci to end
1340 if (compareItemsArrays(
1341 std::vector<OverworldItem>(room_items[i].begin(),
1342 room_items[i].end()),
1343 std::vector<OverworldItem>(room_items[ci].begin(),
1344 room_items[ci].end()))) {
1345 item_pointers_reuse[i] = ci;
1346 break;
1347 }
1348 }
1349 }
1350
1351 for (int i = 0; i < 128; i++) {
1352 if (item_pointers_reuse[i] == -1) {
1353 item_pointers[i] = data_pos;
1354 for (const OverworldItem &item : room_items[i]) {
1355 short map_pos =
1356 static_cast<short>(((item.game_y_ << 6) + item.game_x_) << 1);
1357
1358 uint32_t data = static_cast<uint8_t>(map_pos & 0xFF) |
1359 static_cast<uint8_t>(map_pos >> 8) |
1360 static_cast<uint8_t>(item.id_);
1361 RETURN_IF_ERROR(rom()->WriteLong(data_pos, data));
1362 data_pos += 3;
1363 }
1364
1365 empty_pointer = data_pos;
1366 RETURN_IF_ERROR(rom()->WriteWord(data_pos, 0xFFFF));
1367 data_pos += 2;
1368 } else if (item_pointers_reuse[i] == -2) {
1369 item_pointers[i] = empty_pointer;
1370 } else {
1371 item_pointers[i] = item_pointers[item_pointers_reuse[i]];
1372 }
1373
1374 int snesaddr = core::PcToSnes(item_pointers[i]);
1376 rom()->WriteWord(overworldItemsPointers + (i * 2), snesaddr));
1377 }
1378
1379 if (data_pos > overworldItemsEndData) {
1380 return absl::AbortedError("Too many items");
1381 }
1382
1383 if (flags()->kLogToConsole) {
1384 std::cout << "End of Items : " << data_pos << std::endl;
1385 }
1386
1387 return absl::OkStatus();
1388}
1389
1391 core::Logger::log("Saving Map Properties");
1392 for (int i = 0; i < 64; i++) {
1393 RETURN_IF_ERROR(rom()->WriteByte(kAreaGfxIdPtr + i,
1394 overworld_maps_[i].area_graphics()));
1396 overworld_maps_[i].area_palette()));
1397 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + i,
1398 overworld_maps_[i].sprite_graphics(0)));
1399 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + 64 + i,
1400 overworld_maps_[i].sprite_graphics(1)));
1401 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + 128 + i,
1402 overworld_maps_[i].sprite_graphics(2)));
1404 overworld_maps_[i].sprite_palette(0)));
1405 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 64 + i,
1406 overworld_maps_[i].sprite_palette(1)));
1407 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 128 + i,
1408 overworld_maps_[i].sprite_palette(2)));
1409 }
1410
1411 for (int i = 64; i < 128; i++) {
1412 RETURN_IF_ERROR(rom()->WriteByte(kAreaGfxIdPtr + i,
1413 overworld_maps_[i].area_graphics()));
1414 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + i,
1415 overworld_maps_[i].sprite_graphics(0)));
1416 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + 64 + i,
1417 overworld_maps_[i].sprite_graphics(1)));
1418 RETURN_IF_ERROR(rom()->WriteByte(overworldSpriteset + 128 + i,
1419 overworld_maps_[i].sprite_graphics(2)));
1421 overworld_maps_[i].area_palette()));
1422 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 64 + i,
1423 overworld_maps_[i].sprite_palette(0)));
1424 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 128 + i,
1425 overworld_maps_[i].sprite_palette(1)));
1426 RETURN_IF_ERROR(rom()->WriteByte(kOverworldSpritePaletteIds + 192 + i,
1427 overworld_maps_[i].sprite_palette(2)));
1428 }
1429
1430 return absl::OkStatus();
1431}
1432
1433} // namespace overworld
1434} // namespace zelda3
1435} // namespace app
1436} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:145
VersionConstants version_constants() const
Definition rom.h:491
absl::StatusOr< uint16_t > ReadWord(int offset)
Definition rom.h:264
absl::StatusOr< uint8_t > ReadByte(int offset)
Definition rom.h:256
static void log(std::string message)
Definition common.h:215
Tile composition of four 16x16 tiles.
Definition snes_tile.h:84
uint64_t GetPackedValue() const
Definition snes_tile.h:114
SNES 16-bit tile metadata container.
Definition snes_tile.h:52
std::vector< std::vector< Sprite > > all_sprites_
Definition overworld.h:585
OWBlockset & GetMapTiles(int world_type)
Definition overworld.h:495
std::vector< OverworldItem > all_items_
Definition overworld.h:584
std::vector< OverworldEntrance > all_entrances_
Definition overworld.h:581
std::vector< gfx::Tile32 > tiles32_unique_
Definition overworld.h:579
std::vector< OverworldExit > all_exits_
Definition overworld.h:583
void OrganizeMapTiles(std::vector< uint8_t > &bytes, std::vector< uint8_t > &bytes2, int i, int sx, int sy, int &ttpos)
Definition overworld.cc:177
absl::StatusOr< uint16_t > GetTile16ForTile32(int index, int quadrant, int dimension)
Definition overworld.cc:101
std::vector< gfx::Tile16 > tiles16_
Definition overworld.h:576
void AssignWorldTiles(int x, int y, int sx, int sy, int tpos, OWBlockset &world)
Definition overworld.cc:165
std::vector< OverworldEntrance > all_holes_
Definition overworld.h:582
std::vector< OverworldMap > overworld_maps_
Definition overworld.h:580
std::vector< std::vector< uint8_t > > map_data_p2
Definition overworld.h:591
std::vector< std::vector< uint8_t > > map_data_p1
Definition overworld.h:589
absl::Status LoadSpritesFromMap(int spriteStart, int spriteCount, int spriteIndex)
Definition overworld.cc:443
std::vector< uint16_t > tiles32_list_
Definition overworld.h:578
#define RETURN_IF_ERROR(expression)
Definition constants.h:69
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition constants.h:77
uint32_t PcToSnes(uint32_t addr)
Definition common.h:231
std::string UppercaseHexLong(uint32_t dword)
Definition labeling.cc:33
uint32_t SnesToPc(uint32_t addr) noexcept
Definition common.h:223
std::string UppercaseHexByte(uint8_t byte, bool leading)
Definition labeling.cc:21
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:370
bool compareItemsArrays(std::vector< OverworldItem > itemArray1, std::vector< OverworldItem > itemArray2)
std::vector< uint64_t > GetAllTile16(OWMapTiles &map_tiles_)
Definition overworld.cc:966
constexpr int overworldTransitionPositionY
Definition overworld.h:414
constexpr int kOverworldItemsAddress
Definition overworld.h:74
constexpr int overworldMapParentId
Definition overworld.h:413
constexpr int overworldSpriteset
Definition overworld.h:383
constexpr int overworldItemsEndData
Definition overworld.h:76
constexpr int OverworldScreenSizeForLoading
Definition overworld.h:417
constexpr int OverworldScreenTileMapChangeByScreen2
Definition overworld.h:421
constexpr int kOverworldMapPaletteIds
Definition overworld.h:379
constexpr int overworldSpritesZelda
Definition overworld.h:388
constexpr int OverworldMapDataOverflow
Definition overworld.h:425
constexpr int OWEntranceEntranceId
Definition overworld.h:319
constexpr int kNumTile16Individual
Definition overworld.h:434
constexpr int kMap32TilesLength
Definition overworld.cc:115
constexpr int kNumOverworldMaps
Definition overworld.h:433
constexpr int overworldMapSize
Definition overworld.h:402
constexpr int overworldScreenSize
Definition overworld.h:416
constexpr int OWExitDoorType2
Definition overworld.h:142
constexpr int kOverworldSpritePaletteIds
Definition overworld.h:380
constexpr int OverworldScreenTileMapChangeByScreen3
Definition overworld.h:422
constexpr int transition_target_north
Definition overworld.h:427
constexpr int overworldMapSizeHighByte
Definition overworld.h:405
constexpr int overworldSpritesBegining
Definition overworld.h:386
constexpr int transition_target_west
Definition overworld.h:428
constexpr int OverworldScreenTileMapChangeByScreen4
Definition overworld.h:423
constexpr int overworldTransitionPositionX
Definition overworld.h:415
constexpr int overworldSpritesAgahnim
Definition overworld.h:387
constexpr int OverworldScreenTileMapChangeByScreen1
Definition overworld.h:420
constexpr int OWExitDoorType1
Definition overworld.h:141
constexpr int overworldItemsPointers
Definition overworld.h:73
std::vector< std::vector< uint16_t > > OWBlockset
Represents tile32 data for the overworld.
Definition common.h:15
Definition common.cc:21
Overworld map tile32 data.
Definition common.h:20