5#include <unordered_map>
8#include "absl/status/status.h"
21 if (
rom->size() == 0) {
22 return absl::InvalidArgumentError(
"ROM file not loaded");
42 return absl::OkStatus();
46 for (
int i = 128; i < 145; i++) {
56 std::array<bool, kNumMapsPerWorld> map_checked;
57 std::fill(map_checked.begin(), map_checked.end(),
false);
62 if (
int i = xx + (yy * 8); map_checked[i] ==
false) {
64 map_checked[i] =
true;
68 map_checked[i + 1] =
true;
72 map_checked[i + 8] =
true;
76 map_checked[i + 9] =
true;
83 map_checked[i] =
true;
99 int index,
int quadrant,
int dimension,
const uint32_t *map32address) {
101 auto arg1,
rom()->ReadByte(map32address[dimension] + quadrant + (index)));
103 (quadrant <= 1 ? 4 : 5)));
104 return (uint16_t)(arg1 +
105 (((arg2 >> (quadrant % 2 == 0 ? 4 : 0)) & 0x0F) * 256));
109 constexpr int kMap32TilesLength = 0x33F0;
110 int num_tile32 = kMap32TilesLength;
111 uint32_t map32address[4] = {
rom()->version_constants().kMap32TileTL,
112 rom()->version_constants().kMap32TileTR,
113 rom()->version_constants().kMap32TileBL,
114 rom()->version_constants().kMap32TileBR};
117 map32address[0] =
rom()->version_constants().kMap32TileTL;
126 for (
int i = 0; i < num_tile32; i += 6) {
128 for (
int k = 0; k < 4; k++) {
152 for (
int i = 0; i < 0x200; i++) {
158 return absl::OkStatus();
171 for (
int i = 0; i < num_tile16; i += 1) {
184 tiles16_.emplace_back(t0, t1, t2, t3);
186 return absl::OkStatus();
191 int position_x1 = (x * 2) + (sx * 32);
192 int position_y1 = (y * 2) + (sy * 32);
193 int position_x2 = (x * 2) + 1 + (sx * 32);
194 int position_y2 = (y * 2) + 1 + (sy * 32);
202 std::vector<uint8_t> &bytes2,
int i,
int sx,
203 int sy,
int &ttpos) {
204 for (
int y = 0; y < 16; y++) {
205 for (
int x = 0; x < 16; x++) {
206 auto tidD = (uint16_t)((bytes2[ttpos] << 8) + bytes[ttpos]);
222 const auto get_ow_map_gfx_ptr = [
this](
int index, uint32_t map_ptr) {
223 int p = (
rom()->data()[map_ptr + 2 + (3 * index)] << 16) +
224 (
rom()->data()[map_ptr + 1 + (3 * index)] << 8) +
225 (
rom()->data()[map_ptr + (3 * index)]);
229 constexpr uint32_t kBaseLowest = 0x0FFFFF;
230 constexpr uint32_t kBaseHighest = 0x0F8000;
232 uint32_t lowest = kBaseLowest;
233 uint32_t highest = kBaseHighest;
238 auto p1 = get_ow_map_gfx_ptr(
239 i,
rom()->version_constants().kCompressedAllMap32PointersHigh);
240 auto p2 = get_ow_map_gfx_ptr(
241 i,
rom()->version_constants().kCompressedAllMap32PointersLow);
245 if (p1 >= highest) highest = p1;
246 if (p2 >= highest) highest = p2;
248 if (p1 <= lowest && p1 > kBaseHighest) lowest = p1;
249 if (p2 <= lowest && p2 > kBaseHighest) lowest = p2;
273 std::vector<std::future<absl::Status>> futures;
281 auto task_function = [
this, i, size, world_type]() {
285 futures.emplace_back(std::async(std::launch::async, task_function));
289 for (
auto &future : futures) {
293 return absl::OkStatus();
299 rom()->data()[
rom()->version_constants().kOverworldTilesType + i];
307 int num_entrances = 129;
316 for (
int i = 0; i < num_entrances; i++) {
318 rom()->ReadWord(ow_entrance_map_ptr + (i * 2)));
320 rom()->ReadWord(ow_entrance_pos_ptr + (i * 2)));
322 int p = map_pos >> 1;
325 bool deleted =
false;
326 if (map_pos == 0xFFFF) {
330 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
331 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id, map_pos,
335 return absl::OkStatus();
339 constexpr int kNumHoles = 0x13;
340 for (
int i = 0; i < kNumHoles; i++) {
345 int p = (map_pos + 0x400) >> 1;
349 (x * 16) + (((map_id % 64) - (((map_id % 64) / 8) * 8)) * 512),
350 (y * 16) + (((map_id % 64) / 8) * 512), entrance_id, map_id,
351 (uint16_t)(map_pos + 0x400),
true);
353 return absl::OkStatus();
357 const int NumberOfOverworldExits = 0x4F;
358 std::vector<OverworldExit>
exits;
359 for (
int i = 0; i < NumberOfOverworldExits; i++) {
360 auto rom_data =
rom()->data();
362 uint16_t exit_room_id;
363 uint16_t exit_map_id;
365 uint16_t exit_y_scroll;
366 uint16_t exit_x_scroll;
367 uint16_t exit_y_player;
368 uint16_t exit_x_player;
369 uint16_t exit_y_camera;
370 uint16_t exit_x_camera;
371 uint16_t exit_scroll_mod_y;
372 uint16_t exit_scroll_mod_x;
373 uint16_t exit_door_type_1;
374 uint16_t exit_door_type_2;
382 exit_scroll_mod_x,
OWExitUnk2 + i, exit_door_type_1,
386 uint16_t py = (uint16_t)((rom_data[
OWExitYPlayer + (i * 2) + 1] << 8) +
388 uint16_t px = (uint16_t)((rom_data[
OWExitXPlayer + (i * 2) + 1] << 8) +
392 "Exit: %d RoomID: %d MapID: %d VRAM: %d YScroll: %d XScroll: "
393 "%d YPlayer: %d XPlayer: %d YCamera: %d XCamera: %d "
394 "ScrollModY: %d ScrollModX: %d DoorType1: %d DoorType2: %d",
395 i, exit_room_id, exit_map_id, exit_vram, exit_y_scroll, exit_x_scroll,
396 py, px, exit_y_camera, exit_x_camera, exit_scroll_mod_y,
397 exit_scroll_mod_x, exit_door_type_1, exit_door_type_2);
399 exits.emplace_back(exit_room_id, exit_map_id, exit_vram, exit_y_scroll,
400 exit_x_scroll, py, px, exit_y_camera, exit_x_camera,
401 exit_scroll_mod_y, exit_scroll_mod_x, exit_door_type_1,
402 exit_door_type_2, (px & py) == 0xFFFF);
405 return absl::OkStatus();
411 uint32_t pointer_pc =
SnesToPc(pointer);
412 for (
int i = 0; i < 128; i++) {
414 uint32_t addr = (pointer & 0xFF0000) | word_address;
428 if (b1 == 0xFF && b2 == 0xFF) {
432 int p = (((b2 & 0x1F) << 8) + b1) >> 1;
443 int sx = fakeID - (sy * 8);
445 all_items_.emplace_back(b3, (uint16_t)i, (x * 16) + (sx * 512),
446 (y * 16) + (sy * 512),
false);
454 return absl::OkStatus();
458 std::vector<std::future<absl::Status>> futures;
459 futures.emplace_back(std::async(std::launch::async, [
this]() {
462 futures.emplace_back(std::async(std::launch::async, [
this]() {
465 futures.emplace_back(std::async(std::launch::async, [
this]() {
469 for (
auto &future : futures) {
473 return absl::OkStatus();
477 int num_maps_per_gamestate,
479 for (
int i = 0; i < num_maps_per_gamestate; i++) {
482 int current_spr_ptr = sprites_per_gamestate_ptr + (i * 2);
484 int sprite_address =
SnesToPc((0x09 << 0x10) | word_addr);
489 if (b1 == 0xFF)
break;
491 int editor_map_index = i;
492 if (game_state != 0) {
493 if (editor_map_index >= 128)
494 editor_map_index -= 128;
495 else if (editor_map_index >= 64)
496 editor_map_index -= 64;
498 int mapY = (editor_map_index / 8);
499 int mapX = (editor_map_index % 8);
501 int realX = ((b2 & 0x3F) * 16) + mapX * 512;
502 int realY = ((b1 & 0x3F) * 16) + mapY * 512;
505 (uint8_t)(b2 & 0x3F), (uint8_t)(b1 & 0x3F), realX, realY);
512 return absl::OkStatus();
524 return absl::OkStatus();
528 util::logf(
"Saving Overworld Maps");
537 std::vector<uint8_t> single_map_1(512);
538 std::vector<uint8_t> single_map_2(512);
542 for (
int y = 0; y < 16; y++) {
543 for (
int x = 0; x < 16; x++) {
545 single_map_1[npos] = packed & 0xFF;
546 single_map_2[npos] = (packed >> 8) & 0xFF;
555 if (a.empty() || b.empty()) {
556 return absl::AbortedError(
"Error compressing map gfx.");
563 if ((pos + size_a) >= 0x5FE70 && (pos + size_a) <= 0x60000) {
567 if ((pos + size_a) >= 0x6411F && (pos + size_a) <= 0x70000) {
568 util::logf(
"Pos set to overflow region for map %s at %s",
573 const auto compare_array = [](
const std::vector<uint8_t> &array1,
574 const std::vector<uint8_t> &array2) ->
bool {
575 if (array1.size() != array2.size()) {
579 for (
size_t i = 0; i < array1.size(); i++) {
580 if (array1[i] != array2[i]) {
588 for (
int j = 0; j < i; j++) {
602 std::copy(a.begin(), a.end(),
map_data_p1[i].begin());
605 util::logf(
"Saving map pointers1 and compressed data for map %s at %s",
608 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
615 util::logf(
"Saving map pointers1 for map %s at %s",
util::HexByte(i),
618 rom()->version_constants().kCompressedAllMap32PointersLow + (3 * i),
622 if ((pos + b.size()) >= 0x5FE70 && (pos + b.size()) <= 0x60000) {
626 if ((pos + b.size()) >= 0x6411F && (pos + b.size()) <= 0x70000) {
627 util::logf(
"Pos set to overflow region for map %s at %s",
634 std::copy(b.begin(), b.end(),
map_data_p2[i].begin());
637 util::logf(
"Saving map pointers2 and compressed data for map %s at %s",
640 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
647 util::logf(
"Saving map pointers2 for map %s at %s",
util::HexByte(i),
650 rom()->version_constants().kCompressedAllMap32PointersHigh + (3 * i),
658 return absl::AbortedError(
"Too many maps data " + std::to_string(pos));
662 return absl::OkStatus();
666 util::logf(
"Saving Large Maps");
667 std::vector<uint8_t> checked_map;
679 if (std::find(checked_map.begin(), checked_map.end(), i) !=
687 const uint8_t large_map_offsets[] = {0, 1, 8, 9};
688 for (
const auto &offset : large_map_offsets) {
713 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
716 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
720 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
723 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
727 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
730 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
734 (uint16_t)((parent_y_pos * 0x200) - 0xE0)));
737 (uint16_t)((parent_x_pos * 0x200) - 0x100)));
741 (parent_x_pos * 0x200)));
743 (parent_y_pos * 0x200)));
747 (parent_x_pos * 0x200)));
750 (parent_y_pos * 0x200)));
755 (parent_x_pos * 0x200)));
758 (parent_y_pos * 0x200)));
762 (parent_x_pos * 0x200)));
765 (parent_y_pos * 0x200)));
774 if (parent_x_pos == 0) {
877 checked_map.emplace_back(i);
878 checked_map.emplace_back((i + 1));
879 checked_map.emplace_back((i + 8));
880 checked_map.emplace_back((i + 9));
902 if (i - 1 >= 0 && parent_x_pos != 0) {
914 if (i + 1 < 64 && parent_x_pos != 7) {
958 (uint16_t)((y_pos * 0x200) - 0xE0)));
960 (uint16_t)((x_pos * 0x200) - 0x100)));
967 checked_map.emplace_back(i);
971 constexpr int OverworldScreenTileMapChangeMask = 0x1262C;
974 rom()->WriteShort(OverworldScreenTileMapChangeMask + 0, 0x1F80));
976 rom()->WriteShort(OverworldScreenTileMapChangeMask + 2, 0x1F80));
978 rom()->WriteShort(OverworldScreenTileMapChangeMask + 4, 0x007F));
980 rom()->WriteShort(OverworldScreenTileMapChangeMask + 6, 0x007F));
982 return absl::OkStatus();
987 std::vector<uint64_t> all_tile_16;
1002 for (
int y = 0; y < 32; y += 2) {
1003 for (
int x = 0; x < 32; x += 2) {
1005 tiles_used[x + (sx * 32)][y + (sy * 32)],
1006 tiles_used[x + 1 + (sx * 32)][y + (sy * 32)],
1007 tiles_used[x + (sx * 32)][y + 1 + (sy * 32)],
1008 tiles_used[x + 1 + (sx * 32)][y + 1 + (sy * 32)]);
1037 std::vector<uint64_t> all_tile_16 = GetAllTile16(
map_tiles_);
1040 std::set<uint64_t> unique_tiles_set(all_tile_16.begin(), all_tile_16.end());
1042 std::vector<uint64_t> unique_tiles(all_tile_16);
1043 unique_tiles.assign(unique_tiles_set.begin(), unique_tiles_set.end());
1046 std::unordered_map<uint64_t, uint16_t> all_tiles_indexed;
1047 for (
size_t tile32_id = 0; tile32_id < unique_tiles.size(); tile32_id++) {
1048 all_tiles_indexed.insert(
1049 {unique_tiles[tile32_id],
static_cast<uint16_t
>(tile32_id)});
1055 tiles32_list_.emplace_back(all_tiles_indexed[all_tile_16[j]]);
1059 for (
size_t i = 0; i < unique_tiles.size(); ++i) {
1069 return absl::InternalError(absl::StrFormat(
1070 "Number of unique Tiles32: %d Out of: %d\nUnique Tile32 count exceed "
1071 "the limit\nThe ROM Has not been saved\nYou can fill maps with grass "
1072 "tiles to free some space\nOr use the option Clear DW Tiles in the "
1089 return absl::OkStatus();
1137 return absl::OkStatus();
1141 util::logf(
"Saving Map32 Tiles");
1142 constexpr int kMaxUniqueTiles = 0x4540;
1143 constexpr int kTilesPer32x32Tile = 6;
1145 int unique_tile_index = 0;
1148 for (
int i = 0; i < num_unique_tiles; i += kTilesPer32x32Tile) {
1149 if (unique_tile_index >= kMaxUniqueTiles) {
1150 return absl::AbortedError(
"Too many unique tile32 definitions.");
1154 auto top_left =
rom()->version_constants().kMap32TileTL;
1182 auto top_right =
rom()->version_constants().kMap32TileTR;
1187 top_right + (i + 1),
1190 top_right + (i + 2),
1193 top_right + (i + 3),
1197 top_right + (i + 4),
1202 top_right + (i + 5),
1262 unique_tile_index += 4;
1263 num_unique_tiles += 2;
1266 return absl::OkStatus();
1344 return absl::OkStatus();
1348 util::logf(
"Saving Map16 Tiles");
1361 return absl::OkStatus();
1365 util::logf(
"Saving Entrances");
1395 return absl::OkStatus();
1399 util::logf(
"Saving Exits");
1428 return absl::OkStatus();
1433 std::vector<OverworldItem> item_array2) {
1434 if (item_array1.size() != item_array2.size()) {
1439 for (
size_t i = 0; i < item_array1.size(); i++) {
1441 for (
size_t j = 0; j < item_array2.size(); j++) {
1443 if (item_array1[i].x_ == item_array2[j].x_ &&
1444 item_array1[i].y_ == item_array2[j].y_ &&
1445 item_array1[i].id_ == item_array2[j].id_) {
1461 std::vector<std::vector<OverworldItem>> room_items(
1465 room_items[i] = std::vector<OverworldItem>();
1467 if (item.room_map_id_ == i) {
1468 room_items[i].emplace_back(item);
1469 if (item.id_ == 0x86) {
1471 0x16DC5 + (i * 2), (item.game_x_ + (item.game_y_ * 64)) * 2));
1480 int empty_pointer = 0;
1482 item_pointers_reuse[i] = -1;
1483 for (
int ci = 0; ci < i; ci++) {
1484 if (room_items[i].empty()) {
1485 item_pointers_reuse[i] = -2;
1490 if (CompareItemsArrays(
1491 std::vector<OverworldItem>(room_items[i].begin(),
1492 room_items[i].end()),
1493 std::vector<OverworldItem>(room_items[ci].begin(),
1494 room_items[ci].end()))) {
1495 item_pointers_reuse[i] = ci;
1502 if (item_pointers_reuse[i] == -1) {
1503 item_pointers[i] = data_pos;
1506 static_cast<short>(((item.game_y_ << 6) + item.game_x_) << 1);
1508 uint32_t data =
static_cast<uint8_t
>(map_pos & 0xFF) |
1509 static_cast<uint8_t
>(map_pos >> 8) |
1510 static_cast<uint8_t
>(item.id_);
1515 empty_pointer = data_pos;
1518 }
else if (item_pointers_reuse[i] == -2) {
1519 item_pointers[i] = empty_pointer;
1521 item_pointers[i] = item_pointers[item_pointers_reuse[i]];
1524 int snesaddr =
PcToSnes(item_pointers[i]);
1530 return absl::AbortedError(
"Too many items");
1533 util::logf(
"End of Items : %d", data_pos);
1535 return absl::OkStatus();
1539 util::logf(
"Saving Map Properties");
1586 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data.
Tile composition of four 16x16 tiles.
uint64_t GetPackedValue() const
SNES 16-bit tile metadata container.
absl::Status SaveMap32Expanded()
std::vector< uint16_t > tiles32_list_
absl::Status Load(Rom *rom)
std::vector< OverworldItem > all_items_
void OrganizeMapTiles(std::vector< uint8_t > &bytes, std::vector< uint8_t > &bytes2, int i, int sx, int sy, int &ttpos)
std::array< int, kNumOverworldMaps > map_pointers1
std::vector< gfx::Tile32 > tiles32_unique_
void DecompressAllMapTiles()
absl::Status SaveMapProperties()
absl::Status SaveMap32Tiles()
std::vector< OverworldEntrance > all_entrances_
OverworldMapTiles map_tiles_
absl::Status SaveMap16Tiles()
absl::Status SaveLargeMaps()
std::array< uint8_t, kNumOverworldMaps > map_parent_
void AssignWorldTiles(int x, int y, int sx, int sy, int tpos, OverworldBlockset &world)
std::array< uint8_t, kNumTileTypes > all_tiles_types_
absl::Status CreateTile32Tilemap()
std::array< int, kNumOverworldMaps > map_pointers1_id
absl::Status SaveEntrances()
absl::Status LoadSprites()
std::vector< OverworldMap > overworld_maps_
absl::Status LoadEntrances()
absl::Status Save(Rom *rom)
absl::Status SaveOverworldMaps()
std::array< std::vector< Sprite >, 3 > all_sprites_
std::array< int, kNumOverworldMaps > map_pointers2_id
absl::Status LoadOverworldMaps()
std::vector< gfx::Tile16 > tiles16_
absl::Status AssembleMap16Tiles()
absl::Status LoadSpritesFromMap(int sprite_start, int sprite_count, int sprite_index)
std::array< std::vector< uint8_t >, kNumOverworldMaps > map_data_p1
absl::Status SaveMap16Expanded()
std::vector< OverworldExit > all_exits_
std::array< int, kNumOverworldMaps > map_pointers2
absl::StatusOr< uint16_t > GetTile16ForTile32(int index, int quadrant, int dimension, const uint32_t *map32address)
std::array< std::vector< uint8_t >, kNumOverworldMaps > map_data_p2
absl::Status AssembleMap32Tiles()
OverworldBlockset & GetMapTiles(int world_type)
std::vector< OverworldEntrance > all_holes_
#define RETURN_IF_ERROR(expression)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
std::vector< uint8_t > HyruleMagicDecompress(uint8_t const *src, int *const size, int const p_big_endian)
TileInfo GetTilesInfo(uint16_t tile)
std::vector< uint8_t > HyruleMagicCompress(uint8_t const *const src, int const oldsize, int *const size, int const flag)
std::string HexByte(uint8_t byte, HexStringParams params)
std::string HexLong(uint32_t dword, HexStringParams params)
bool CompareItemsArrays(std::vector< OverworldItem > item_array1, std::vector< OverworldItem > item_array2)
std::vector< uint64_t > GetAllTile16(OverworldMapTiles &map_tiles_)
Zelda 3 specific classes and functions.
constexpr int OWExitYScroll
constexpr int kAreaGfxIdPtr
constexpr int kOverworldHoleArea
constexpr int kOverworldTransitionPositionY
constexpr int kOverworldEntrancePos
constexpr int kOverworldHoleEntrance
constexpr int kNumMapsPerWorld
constexpr int kOverworldSpriteset
constexpr int kMap16ExpandedFlagPos
constexpr int LimitOfMap32
constexpr int NumberOfMap16Ex
constexpr int kOverworldScreenTileMapChangeByScreen1
constexpr int kOverworldMapDataOverflow
constexpr int kOverworldMapSizeHighByte
constexpr int kOverworldEntranceEntranceIdExpanded
constexpr int kNumTileTypes
constexpr int NumberOfMap32
constexpr int kNumOverworldMapItemPointers
constexpr int kOverworldScreenSize
constexpr int kOverworldScreenTileMapChangeByScreen4
constexpr int kNumTile16Individual
constexpr int kNumOverworldHoles
constexpr int kSpecialWorldMapIdStart
constexpr int OWExitDoorType2
constexpr int kOverworldEntranceEntranceId
constexpr int kOverworldItemsAddress
constexpr int kMap16Tiles
constexpr int kOverworldHolePos
constexpr int kNumOverworldMaps
constexpr int kOverworldEntranceMap
constexpr int kOverworldItemsEndData
constexpr int OWExitYCamera
std::vector< std::vector< uint16_t > > OverworldBlockset
Represents tile32 data for the overworld.
constexpr int kMap32TileBLExpanded
constexpr int kOverworldTransitionPositionX
constexpr int OWExitXScroll
constexpr int OWExitRoomId
constexpr int OWExitXCamera
constexpr int OWExitYPlayer
constexpr int kOverworldScreenSizeForLoading
constexpr int kOverworldSpritePaletteIds
constexpr int kMap32TileBRExpanded
constexpr int kMap32TileCountExpanded
constexpr int OWExitMapId
constexpr int kTransitionTargetWest
constexpr int kOverworldSpritesZelda
constexpr int kOverworldMapParentId
constexpr int kMap32ExpandedFlagPos
constexpr int kOverworldEntrancePosExpanded
constexpr int OWExitDoorType1
constexpr int kOverworldItemsPointers
constexpr int kNumOverworldExits
constexpr int NumberOfMap16
constexpr int kOverworldMapSize
constexpr int kOverworldScreenTileMapChangeByScreen2
constexpr int kOverworldEntranceMapExpanded
constexpr int kDarkWorldMapIdStart
constexpr int kOverworldCompressedMapPos
constexpr int kOverworldSpritesBeginning
constexpr int kOverworldScreenTileMapChangeByScreen3
constexpr int kMap16TilesExpanded
constexpr int kOverworldEntranceExpandedFlagPos
constexpr int kNumOverworldEntrances
constexpr int kOverworldSpritesAgahnim
constexpr int kTransitionTargetNorth
constexpr int kOverworldCompressedOverflowPos
constexpr int kMap32TileTRExpanded
constexpr int OWExitXPlayer
constexpr int kOverworldMapPaletteIds
Main namespace for the application.
uint32_t PcToSnes(uint32_t addr)
uint32_t SnesToPc(uint32_t addr) noexcept
Overworld map tile32 data.
OverworldBlockset dark_world
OverworldBlockset special_world
OverworldBlockset light_world