From fb398351d84ebf7913790ea204726d9114c03b3e Mon Sep 17 00:00:00 2001 From: scawful Date: Fri, 20 Oct 2023 02:17:35 -0400 Subject: [PATCH] cleanup rom, gfx, overworld and add comments --- src/app/core/constants.h | 1 - src/app/gfx/scad_format.cc | 4 +- src/app/gfx/snes_palette.cc | 9 - src/app/gfx/snes_palette.h | 3 +- src/app/gfx/snes_tile.cc | 6 +- src/app/gfx/snes_tile.h | 2 +- src/app/rom.cc | 60 ++++--- src/app/rom.h | 141 +++++++++------- src/app/zelda3/overworld.cc | 327 +++++++++++++++++------------------- src/app/zelda3/overworld.h | 20 +-- 10 files changed, 282 insertions(+), 291 deletions(-) diff --git a/src/app/core/constants.h b/src/app/core/constants.h index 8a3129d6..5c433306 100644 --- a/src/app/core/constants.h +++ b/src/app/core/constants.h @@ -141,7 +141,6 @@ constexpr ushort TileNameMask = 0x03FF; constexpr int Uncompressed3BPPSize = 0x0600; constexpr int UncompressedSheetSize = 0x0800; -constexpr int NumberOfSheets = 223; constexpr int NumberOfRooms = 296; constexpr int NumberOfColors = 3143; diff --git a/src/app/gfx/scad_format.cc b/src/app/gfx/scad_format.cc index a62ca21e..6626ae14 100644 --- a/src/app/gfx/scad_format.cc +++ b/src/app/gfx/scad_format.cc @@ -59,10 +59,10 @@ absl::Status LoadCgx(uint8_t bpp, std::string_view filename, file.close(); if (bpp > 8) { - cgx_loaded = gfx::BPP8SNESToIndexed(cgx_data, 40); + cgx_loaded = gfx::Bpp8SnesToIndexed(cgx_data, 40); return absl::OkStatus(); } - cgx_loaded = gfx::BPP8SNESToIndexed(cgx_data, bpp); + cgx_loaded = gfx::Bpp8SnesToIndexed(cgx_data, bpp); return absl::OkStatus(); } diff --git a/src/app/gfx/snes_palette.cc b/src/app/gfx/snes_palette.cc index 5e60e36f..9f04138f 100644 --- a/src/app/gfx/snes_palette.cc +++ b/src/app/gfx/snes_palette.cc @@ -146,15 +146,6 @@ SNESPalette::SNESPalette(const std::vector& cols) { size_ = cols.size(); } -char* SNESPalette::encode() { - auto data = new char[size_ * 2]; - for (unsigned int i = 0; i < size_; i++) { - data[i * 2] = (char)(colors[i].GetSNES() & 0xFF); - data[i * 2 + 1] = (char)(colors[i].GetSNES() >> 8); - } - return data; -} - SDL_Palette* SNESPalette::GetSDL_Palette() { auto sdl_palette = std::make_shared(); sdl_palette->ncolors = size_; diff --git a/src/app/gfx/snes_palette.h b/src/app/gfx/snes_palette.h index 63566968..10e70b12 100644 --- a/src/app/gfx/snes_palette.h +++ b/src/app/gfx/snes_palette.h @@ -114,11 +114,10 @@ class SNESPalette { explicit SNESPalette(const std::vector&); explicit SNESPalette(const std::vector&); - char* encode(); SDL_Palette* GetSDL_Palette(); void Create(const std::vector& cols) { - for (const auto each : cols) { + for (const auto& each : cols) { colors.push_back(each); } size_ = cols.size(); diff --git a/src/app/gfx/snes_tile.cc b/src/app/gfx/snes_tile.cc index 8b5d5706..6be31b4b 100644 --- a/src/app/gfx/snes_tile.cc +++ b/src/app/gfx/snes_tile.cc @@ -75,7 +75,9 @@ Bytes PackBppTile(const tile8& tile, const uint32_t bpp) { for (unsigned int col = 0; col < 8; col++) { for (unsigned int row = 0; row < 8; row++) { uchar color = tile.data[col * 8 + row]; - if (color > maxcolor) throw std::runtime_error("Invalid color value."); + if (color > maxcolor) { + throw std::invalid_argument("Invalid color value."); + } // 1bpp format if (bpp == 1) output[col] += (uchar)((color & 1) << (7 - row)); @@ -178,7 +180,7 @@ Bytes SnesTo8bppSheet(Bytes sheet, int bpp) { return sheet_buffer_out; } -Bytes BPP8SNESToIndexed(Bytes data, uint64_t bpp) { +Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp) { // 3BPP // [r0,bp1],[r0,bp2],[r1,bp1],[r1,bp2],[r2,bp1],[r2,bp2],[r3,bp1],[r3,bp2] // [r4,bp1],[r4,bp2],[r5,bp1],[r5,bp2],[r6,bp1],[r6,bp2],[r7,bp1],[r7,bp2] diff --git a/src/app/gfx/snes_tile.h b/src/app/gfx/snes_tile.h index 86104cb1..ce8f3fa7 100644 --- a/src/app/gfx/snes_tile.h +++ b/src/app/gfx/snes_tile.h @@ -15,7 +15,7 @@ constexpr uchar kGraphicsBitmap[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; Bytes SnesTo8bppSheet(Bytes sheet, int bpp); -Bytes BPP8SNESToIndexed(Bytes data, uint64_t bpp = 0); +Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp = 0); struct tile8 { uint32_t id; diff --git a/src/app/rom.cc b/src/app/rom.cc index 5cc7ae99..4360e51b 100644 --- a/src/app/rom.cc +++ b/src/app/rom.cc @@ -28,7 +28,7 @@ namespace yaze { namespace app { -absl::StatusOr ROM::Load2bppGraphics() { +absl::StatusOr ROM::Load2BppGraphics() { Bytes sheet; const uint8_t sheets[] = {113, 114, 218, 219, 220, 221}; @@ -46,16 +46,11 @@ absl::StatusOr ROM::Load2bppGraphics() { // ============================================================================ -// 0-112 -> compressed 3bpp bgr -> (decompressed each) 0x600 chars -// 113-114 -> compressed 2bpp -> (decompressed each) 0x800 chars -// 115-126 -> uncompressed 3bpp sprites -> (each) 0x600 chars -// 127-217 -> compressed 3bpp sprites -> (decompressed each) 0x600 chars -// 218-222 -> compressed 2bpp -> (decompressed each) 0x800 chars absl::Status ROM::LoadAllGraphicsData() { Bytes sheet; bool bpp3 = false; - for (int i = 0; i < core::NumberOfSheets; i++) { + for (int i = 0; i < kNumGfxSheets; i++) { if (i >= 115 && i <= 126) { // uncompressed sheets sheet.resize(core::Uncompressed3BPPSize); auto offset = GetGraphicsAddress(data(), i); @@ -94,38 +89,44 @@ absl::Status ROM::LoadAllGraphicsData() { absl::Status ROM::LoadFromFile(const absl::string_view& filename, bool z3_load) { + // Set filename filename_ = filename; + + // Open file std::ifstream file(filename.data(), std::ios::binary); if (!file.is_open()) { return absl::InternalError( absl::StrCat("Could not open ROM file: ", filename)); } + // Get file size and resize rom_data_ size_ = std::filesystem::file_size(filename); rom_data_.resize(size_); - for (auto i = 0; i < size_; ++i) { - char byte_to_read = ' '; - file.read(&byte_to_read, sizeof(char)); - rom_data_[i] = byte_to_read; - } + + // Read file into rom_data_ + file.read(reinterpret_cast(rom_data_.data()), size_); // Check if the sROM has a header constexpr size_t baseROMSize = 1048576; // 1MB constexpr size_t headerSize = 0x200; // 512 bytes - if (size_ % baseROMSize == headerSize) { has_header_ = true; } + // Remove header if present if (has_header_) { - // remove header + auto header = + std::vector(rom_data_.begin(), rom_data_.begin() + 0x200); rom_data_.erase(rom_data_.begin(), rom_data_.begin() + 0x200); size_ -= 0x200; } + // Close file file.close(); + + // Load Zelda 3 specific data if requested if (z3_load) { - // copy ROM title + // Copy ROM title memcpy(title_, rom_data_.data() + kTitleStringOffset, kTitleStringLength); if (rom_data_[kTitleStringOffset + 0x19] == 0) { version_ = Z3_Version::JP; @@ -134,6 +135,8 @@ absl::Status ROM::LoadFromFile(const absl::string_view& filename, } LoadAllPalettes(); } + + // Set is_loaded_ flag and return success is_loaded_ = true; return absl::OkStatus(); } @@ -269,22 +272,26 @@ absl::Status ROM::UpdatePaletteColor(const std::string& groupName, // ============================================================================ +void ROM::SavePalette(int index, const std::string& group_name, + gfx::SNESPalette& palette) { + // Iterate through all colors in the palette + for (size_t j = 0; j < palette.size(); ++j) { + gfx::SNESColor color = palette[j]; + // If the color is modified, save the color to the ROM + if (color.isModified()) { + WriteColor(GetPaletteAddress(group_name, index, j), color); + color.setModified(false); // Reset the modified flag after saving + } + } +} + void ROM::SaveAllPalettes() { // Iterate through all palette_groups_ - for (auto& [groupName, palettes] : palette_groups_) { + for (auto& [group_name, palettes] : palette_groups_) { // Iterate through all palettes in the group for (size_t i = 0; i < palettes.size(); ++i) { auto palette = palettes[i]; - - // Iterate through all colors in the palette - for (size_t j = 0; j < palette.size(); ++j) { - gfx::SNESColor color = palette[j]; - // If the color is modified, save the color to the ROM - if (color.isModified()) { - WriteColor(GetPaletteAddress(groupName, i, j), color); - color.setModified(false); // Reset the modified flag after saving - } - } + SavePalette(i, group_name, palette); } } } @@ -297,7 +304,6 @@ absl::Status ROM::SaveToFile(bool backup, absl::string_view filename) { } // Check if filename is empty - // If it is, use the filename_ member variable if (filename == "") { filename = filename_; } diff --git a/src/app/rom.h b/src/app/rom.h index 9e4f3269..47d98871 100644 --- a/src/app/rom.h +++ b/src/app/rom.h @@ -38,13 +38,20 @@ namespace yaze { namespace app { +enum class Z3_Version { + US = 1, + JP = 2, + SD = 3, + RANDO = 4, +}; + struct VersionConstants { uint32_t kGgxAnimatedPointer; uint32_t kOverworldGfxGroups1; uint32_t kOverworldGfxGroups2; // long ptrs all tiles of maps[high/low] (mapid* 3) - uint32_t compressedAllMap32PointersHigh; - uint32_t compressedAllMap32PointersLow; + uint32_t kCompressedAllMap32PointersHigh; + uint32_t kCompressedAllMap32PointersLow; uint32_t overworldMapPaletteGroup; uint32_t overlayPointers; uint32_t overlayPointersBank; @@ -59,26 +66,14 @@ struct VersionConstants { uint32_t kSpriteBlocksetPointer; }; -enum class Z3_Version { - US = 1, - JP = 2, - SD = 3, - RANDO = 4, -}; - -static constexpr uint32_t overworldMapPaletteGroup = 0x67E74; -static constexpr uint32_t overlayPointers = 0x3FAF4; -static constexpr uint32_t overlayPointersBank = 0x07; -static constexpr uint32_t overworldTilesType = 0x7FD94; - static const std::map kVersionConstantsMap = { {Z3_Version::US, { 0x10275, // kGgxAnimatedPointer 0x5D97, // kOverworldGfxGroups1 0x6073, // kOverworldGfxGroups2 - 0x1794D, // compressedAllMap32PointersHigh - 0x17B2D, // compressedAllMap32PointersLow + 0x1794D, // kCompressedAllMap32PointersHigh + 0x17B2D, // kCompressedAllMap32PointersLow 0x75504, // overworldMapPaletteGroup 0x77664, // overlayPointers 0x0E, // overlayPointersBank @@ -97,8 +92,8 @@ static const std::map kVersionConstantsMap = { 0x10624, // kGgxAnimatedPointer 0x5DD7, // kOverworldGfxGroups1 0x60B3, // kOverworldGfxGroups2 - 0x176B1, // compressedAllMap32PointersHigh - 0x17891, // compressedAllMap32PointersLow + 0x176B1, // kCompressedAllMap32PointersHigh + 0x17891, // kCompressedAllMap32PointersLow 0x67E74, // overworldMapPaletteGroup 0x3FAF4, // overlayPointers 0x07, // overlayPointersBank @@ -115,18 +110,15 @@ static const std::map kVersionConstantsMap = { }; -constexpr int kOverworldGraphicsPos1 = 0x4F80; -constexpr int kOverworldGraphicsPos2 = 0x505F; -constexpr int kOverworldGraphicsPos3 = 0x513E; -constexpr int kTile32Num = 4432; -constexpr int kTitleStringOffset = 0x7FC0; -constexpr int kTitleStringLength = 20; -constexpr int kSNESToPCOffset = 0x138000; - +constexpr uint32_t kOverworldGraphicsPos1 = 0x4F80; +constexpr uint32_t kOverworldGraphicsPos2 = 0x505F; +constexpr uint32_t kOverworldGraphicsPos3 = 0x513E; +constexpr uint32_t kTile32Num = 4432; +constexpr uint32_t kTitleStringOffset = 0x7FC0; +constexpr uint32_t kTitleStringLength = 20; constexpr uint32_t kNumGfxSheets = 223; constexpr uint32_t kNormalGfxSpaceStart = 0x87000; constexpr uint32_t kNormalGfxSpaceEnd = 0xC4200; -constexpr uint32_t kPtrTableStart = 0x4F80; constexpr uint32_t kLinkSpriteLocation = 0x80000; constexpr uint32_t kFontSpriteLocation = 0x70000; @@ -158,22 +150,66 @@ const absl::flat_hash_map paletteGroupColorCounts = { class ROM { public: - // Load functions - absl::StatusOr Load2bppGraphics(); + /** + * Loads 2bpp graphics from ROM data. + * + * This function loads 2bpp graphics from ROM data by iterating over a list of + * sheet IDs, decompressing the sheet data, converting it to 8bpp format, and + * appending the converted sheet data to a byte vector. + * + */ + absl::StatusOr Load2BppGraphics(); + + /** + * This function iterates over all graphics sheets in the ROM and loads them + * into memory. Depending on the sheet's index, it may be uncompressed or + * compressed using the LC-LZ2 algorithm. The uncompressed sheets are 3 bits + * per pixel (BPP), while the compressed sheets are 4 BPP. The loaded graphics + * data is converted to 8 BPP and stored in a bitmap. + * + * The graphics sheets are divided into the following ranges: + * 0-112 -> compressed 3bpp bgr -> (decompressed each) 0x600 chars + * 113-114 -> compressed 2bpp -> (decompressed each) 0x800 chars + * 115-126 -> uncompressed 3bpp sprites -> (each) 0x600 chars + * 127-217 -> compressed 3bpp sprites -> (decompressed each) 0x600 chars + * 218-222 -> compressed 2bpp -> (decompressed each) 0x800 chars + * + */ absl::Status LoadAllGraphicsData(); + + /** + * Load ROM data from a file. + * + * @param filename The name of the file to load. + * @param z3_load Whether to load data specific to Zelda 3. + * + */ absl::Status LoadFromFile(const absl::string_view& filename, bool z3_load = true); absl::Status LoadFromPointer(uchar* data, size_t length); absl::Status LoadFromBytes(const Bytes& data); + + /** + * @brief Loads all the palettes for the game. + * + * This function loads all the palettes for the game, including overworld, + * HUD, armor, swords, shields, sprites, dungeon, grass, and 3D object + * palettes. It also adds the loaded palettes to their respective palette + * groups. + * + */ void LoadAllPalettes(); // Save functions - absl::Status SaveToFile(bool backup, absl::string_view filename = ""); - absl::Status UpdatePaletteColor(const std::string& groupName, - size_t paletteIndex, size_t colorIndex, + absl::Status UpdatePaletteColor(const std::string& group_name, + size_t palette_index, size_t colorIndex, const gfx::SNESColor& newColor); + void SavePalette(int index, const std::string& group_name, + gfx::SNESPalette& palette); void SaveAllPalettes(); + absl::Status SaveToFile(bool backup, absl::string_view filename = ""); + // Read functions gfx::SNESColor ReadColor(int offset); gfx::SNESPalette ReadPalette(int offset, int num_colors); @@ -241,47 +277,19 @@ class ROM { WriteShort(address, bgr); } - void WriteTile32(int id, const gfx::Tile32& tile) { - const uint32_t map32address[4] = { - GetVersionConstants().kMap32TileTL, GetVersionConstants().kMap32TileTR, - GetVersionConstants().kMap32TileBL, GetVersionConstants().kMap32TileBR}; - - if (id < 0 || id >= 0x4540) { - std::cout << "Invalid tile ID: " << id << std::endl; - return; - } - - // Helper lambda to avoid code repetition - auto writeTilesToRom = [&](int base_addr, auto get_tile) { - for (int j = 0; j < 4; ++j) { - Write(base_addr + id + j, get_tile(tile) & 0xFF); - } - Write(base_addr + id + 4, - ((get_tile(tile) >> 4) & 0xF0) | ((get_tile(tile) >> 8) & 0x0F)); - Write(base_addr + id + 5, - ((get_tile(tile) >> 4) & 0xF0) | ((get_tile(tile) >> 8) & 0x0F)); - }; - - writeTilesToRom(map32address[0], - [](const gfx::Tile32& t) { return t.tile0_; }); - writeTilesToRom(map32address[1], - [](const gfx::Tile32& t) { return t.tile1_; }); - writeTilesToRom(map32address[2], - [](const gfx::Tile32& t) { return t.tile2_; }); - writeTilesToRom(map32address[3], - [](const gfx::Tile32& t) { return t.tile3_; }); - } - void Expand(int size) { rom_data_.resize(size); size_ = size; } - void QueueChanges(std::function function) { changes_.push(function); } + void QueueChanges(std::function const& function) { + changes_.push(function); + } VersionConstants GetVersionConstants() const { return kVersionConstantsMap.at(version_); } + int GetGraphicsAddress(const uchar* data, uint8_t addr) const { auto part_one = data[GetVersionConstants().kOverworldGfxPtr1 + addr] << 16; auto part_two = data[GetVersionConstants().kOverworldGfxPtr2 + addr] << 8; @@ -289,6 +297,7 @@ class ROM { auto snes_addr = (part_one | part_two | part_three); return core::SnesToPc(snes_addr); } + uint32_t GetPaletteAddress(const std::string& groupName, size_t paletteIndex, size_t colorIndex) const; gfx::PaletteGroup GetPaletteGroup(const std::string& group) { @@ -305,7 +314,9 @@ class ROM { auto vector() const { return rom_data_; } auto filename() const { return filename_; } auto isLoaded() const { return is_loaded_; } - auto char_data() { return reinterpret_cast(rom_data_.data()); } + auto char_data() { + return static_cast(static_cast(rom_data_.data())); + } auto push_back(uchar byte) { rom_data_.push_back(byte); } diff --git a/src/app/zelda3/overworld.cc b/src/app/zelda3/overworld.cc index ebcb7362..4f3ce5a3 100644 --- a/src/app/zelda3/overworld.cc +++ b/src/app/zelda3/overworld.cc @@ -38,6 +38,42 @@ uint GetOwMapGfxLowPtr(const uchar *rom, int index, uint32_t map_low_ptr) { return core::SnesToPc(p2); } +std::vector GetAllTile16(OWBlockset &tiles_used) { + std::vector all_tile_16; // Ensure it's 64 bits + + int sx = 0; + int sy = 0; + int c = 0; + for (int i = 0; i < kNumOverworldMaps; i++) { + for (int y = 0; y < 32; y += 2) { + for (int x = 0; x < 32; x += 2) { + gfx::Tile32 current_tile( + tiles_used[x + (sx * 32)][y + (sy * 32)], + tiles_used[x + 1 + (sx * 32)][y + (sy * 32)], + tiles_used[x + (sx * 32)][y + 1 + (sy * 32)], + tiles_used[x + 1 + (sx * 32)][y + 1 + (sy * 32)]); + + all_tile_16.push_back(current_tile.GetPackedValue()); + } + } + + sx++; + if (sx >= 8) { + sy++; + sx = 0; + } + + c++; + if (c >= 64) { + sx = 0; + sy = 0; + c = 0; + } + } + + return all_tile_16; +} + absl::flat_hash_map parseFile(const std::string &filename) { absl::flat_hash_map resultMap; @@ -136,137 +172,98 @@ absl::Status Overworld::Load(ROM &rom) { } absl::Status Overworld::SaveOverworldMaps() { - for (int i = 0; i < 160; i++) { - mapPointers1id[i] = -1; - mapPointers2id[i] = -1; - } + // Initialize map pointers + std::fill(map_pointers1_id.begin(), map_pointers1_id.end(), -1); + std::fill(map_pointers1_id.begin(), map_pointers1_id.end(), -1); + // Compress and save each map int pos = 0x058000; for (int i = 0; i < 160; i++) { - int npos = 0; - std::vector singleMap1(512); - std::vector singleMap2(512); + std::vector single_map_1(512); + std::vector single_map_2(512); + // Copy tiles32 data to single_map_1 and single_map_2 + int npos = 0; for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { auto packed1 = tiles32[npos + (i * 256)].GetPackedValue(); auto packed2 = tiles32[npos + (i * 256) + 16].GetPackedValue(); - singleMap1[npos] = static_cast(packed1 & 0xFF); - singleMap2[npos] = static_cast(packed2 & 0xFF); + single_map_1[npos] = static_cast(packed1 & 0xFF); + single_map_2[npos] = static_cast(packed2 & 0xFF); npos++; } } - ASSIGN_OR_RETURN(auto a, - gfx::lc_lz2::CompressOverworld(singleMap1.data(), 0, 256)); - ASSIGN_OR_RETURN(auto b, - gfx::lc_lz2::CompressOverworld(singleMap2.data(), 0, 256)); + // Compress single_map_1 and single_map_2 + ASSIGN_OR_RETURN( + auto a, gfx::lc_lz2::CompressOverworld(single_map_1.data(), 0, 256)) + ASSIGN_OR_RETURN( + auto b, gfx::lc_lz2::CompressOverworld(single_map_2.data(), 0, 256)) if (a.empty() || b.empty()) { return absl::AbortedError("Error compressing map gfx."); } - mapDatap1[i] = a; - mapDatap2[i] = b; - - int snesPos = core::PcToSnes(pos); - mapPointers1[i] = snesPos; - - // Handle the special case for debugging - if (i == 0x54) { - // Here, if you need to print the values, use the appropriate logging or - // output method. std::cout << std::hex << (pos + a.size()) << std::endl; - // std::cout << std::hex << (pos + b.size()) << std::endl; - } - - // Handle specific memory regions - if ((pos + a.size()) >= 0x5FE70 && (pos + a.size()) <= 0x60000) { - pos = 0x60000; - } - - if ((pos + a.size()) >= 0x6411F && (pos + a.size()) <= 0x70000) { - pos = OverworldMapDataOverflow; // Assuming you've defined - // Constants in C++ as well. - } - - for (int j = 0; j < i; j++) { - if (a == mapDatap1[j]) { - mapPointers1id[i] = j; - } - - if (b == mapDatap2[j]) { - mapPointers2id[i] = j; - } - } - - if (mapPointers1id[i] == -1) { - std::copy(a.begin(), a.end(), mapDatap1[i].begin()); - - int snesPos = core::PcToSnes(pos); - mapPointers1[i] = snesPos; - - // Assuming ROM is a class/namespace and Write is a function in it - rom()->Write(compressedAllMap32PointersLow + 0 + 3 * i, - static_cast(snesPos & 0xFF)); - rom()->Write(compressedAllMap32PointersLow + 1 + 3 * i, - static_cast((snesPos >> 8) & 0xFF)); - rom()->Write(compressedAllMap32PointersLow + 2 + 3 * i, - static_cast((snesPos >> 16) & 0xFF)); + // Save compressed data and pointers + map_data_p1[i] = a; + map_data_p2[i] = b; + if (map_pointers1_id[i] == -1) { + // Save compressed data and pointer for map1 + std::copy(a.begin(), a.end(), map_data_p1[i].begin()); + int snes_pos = core::PcToSnes(pos); + map_pointers1[i] = snes_pos; + rom()->Write(kCompressedAllMap32PointersLow + 0 + 3 * i, + static_cast(snes_pos & 0xFF)); + rom()->Write(kCompressedAllMap32PointersLow + 1 + 3 * i, + static_cast((snes_pos >> 8) & 0xFF)); + rom()->Write(kCompressedAllMap32PointersLow + 2 + 3 * i, + static_cast((snes_pos >> 16) & 0xFF)); rom()->WriteVector(pos, a); - pos += a.size(); } else { - int snesPos = mapPointers1[mapPointers1id[i]]; - rom()->Write(compressedAllMap32PointersLow + 0 + 3 * i, - static_cast(snesPos & 0xFF)); - rom()->Write(compressedAllMap32PointersLow + 1 + 3 * i, - static_cast((snesPos >> 8) & 0xFF)); - rom()->Write(compressedAllMap32PointersLow + 2 + 3 * i, - static_cast((snesPos >> 16) & 0xFF)); + // Save pointer for map1 + int snes_pos = map_pointers1[map_pointers1_id[i]]; + rom()->Write(kCompressedAllMap32PointersLow + 0 + 3 * i, + static_cast(snes_pos & 0xFF)); + rom()->Write(kCompressedAllMap32PointersLow + 1 + 3 * i, + static_cast((snes_pos >> 8) & 0xFF)); + rom()->Write(kCompressedAllMap32PointersLow + 2 + 3 * i, + static_cast((snes_pos >> 16) & 0xFF)); } - if ((pos + b.size()) >= 0x5FE70 && (pos + b.size()) <= 0x60000) { - pos = 0x60000; - } - if ((pos + b.size()) >= 0x6411F && (pos + b.size()) <= 0x70000) { - pos = OverworldMapDataOverflow; - } - - // Map2 - if (mapPointers2id[i] == -1) { - std::copy(b.begin(), b.end(), mapDatap2[i].begin()); - - int snesPos = core::PcToSnes(pos); - mapPointers2[i] = snesPos; - - rom()->Write(compressedAllMap32PointersHigh + 0 + 3 * i, - static_cast(snesPos & 0xFF)); - rom()->Write(compressedAllMap32PointersHigh + 1 + 3 * i, - static_cast((snesPos >> 8) & 0xFF)); - rom()->Write(compressedAllMap32PointersHigh + 2 + 3 * i, - static_cast((snesPos >> 16) & 0xFF)); - + if (map_pointers2_id[i] == -1) { + // Save compressed data and pointer for map2 + std::copy(b.begin(), b.end(), map_data_p2[i].begin()); + int snes_pos = core::PcToSnes(pos); + map_pointers2[i] = snes_pos; + rom()->Write(kCompressedAllMap32PointersHigh + 0 + 3 * i, + static_cast(snes_pos & 0xFF)); + rom()->Write(kCompressedAllMap32PointersHigh + 1 + 3 * i, + static_cast((snes_pos >> 8) & 0xFF)); + rom()->Write(kCompressedAllMap32PointersHigh + 2 + 3 * i, + static_cast((snes_pos >> 16) & 0xFF)); rom()->WriteVector(pos, b); - pos += b.size(); } else { - int snesPos = mapPointers2[mapPointers2id[i]]; - rom()->Write(compressedAllMap32PointersHigh + 0 + 3 * i, - static_cast(snesPos & 0xFF)); - rom()->Write(compressedAllMap32PointersHigh + 1 + 3 * i, - static_cast((snesPos >> 8) & 0xFF)); - rom()->Write(compressedAllMap32PointersHigh + 2 + 3 * i, - static_cast((snesPos >> 16) & 0xFF)); + // Save pointer for map2 + int snes_pos = map_pointers2[map_pointers2_id[i]]; + rom()->Write(kCompressedAllMap32PointersHigh + 0 + 3 * i, + static_cast(snes_pos & 0xFF)); + rom()->Write(kCompressedAllMap32PointersHigh + 1 + 3 * i, + static_cast((snes_pos >> 8) & 0xFF)); + rom()->Write(kCompressedAllMap32PointersHigh + 2 + 3 * i, + static_cast((snes_pos >> 16) & 0xFF)); } } + // Check if too many maps data if (pos > 0x137FFF) { std::cerr << "Too many maps data " << std::hex << pos << std::endl; return absl::AbortedError("Too many maps data"); } - RETURN_IF_ERROR(SaveLargeMaps()); // Assuming this function exists and - // returns an absl::Status + // Save large maps + RETURN_IF_ERROR(SaveLargeMaps()) return absl::OkStatus(); } @@ -476,79 +473,52 @@ absl::Status Overworld::SaveLargeMaps() { // ---------------------------------------------------------------------------- -bool Overworld::CreateTile32Tilemap(bool onlyShow) { +bool Overworld::CreateTile32Tilemap(bool only_show) { tiles32_unique_.clear(); tiles32.clear(); - std::vector allTile16; // Ensure it's 64 bits - - int sx = 0; - int sy = 0; - int c = 0; + OWBlockset *tiles_used; for (int i = 0; i < kNumOverworldMaps; i++) { - OWBlockset *tilesused; if (i < 64) { - tilesused = &map_tiles_.light_world; + tiles_used = &map_tiles_.light_world; } else if (i < 128 && i >= 64) { - tilesused = &map_tiles_.dark_world; + tiles_used = &map_tiles_.dark_world; } else { - tilesused = &map_tiles_.special_world; + tiles_used = &map_tiles_.special_world; } - for (int y = 0; y < 32; y += 2) { - for (int x = 0; x < 32; x += 2) { - gfx::Tile32 currentTile( - (*tilesused)[x + (sx * 32)][y + (sy * 32)], - (*tilesused)[x + 1 + (sx * 32)][y + (sy * 32)], - (*tilesused)[x + (sx * 32)][y + 1 + (sy * 32)], - (*tilesused)[x + 1 + (sx * 32)][y + 1 + (sy * 32)]); + std::vector all_tile_16 = GetAllTile16(*tiles_used); - allTile16.push_back(currentTile.GetPackedValue()); - } + std::vector unique_tiles(all_tile_16); // Ensure it's 64 bits + std::sort(unique_tiles.begin(), unique_tiles.end()); + unique_tiles.erase(std::unique(unique_tiles.begin(), unique_tiles.end()), + unique_tiles.end()); + + // Ensure it's 64 bits + std::unordered_map all_tiles_indexed; + for (size_t j = 0; j < unique_tiles.size(); j++) { + all_tiles_indexed.insert({unique_tiles[j], static_cast(j)}); } - sx++; - if (sx >= 8) { - sy++; - sx = 0; + for (int j = 0; j < NumberOfMap32; j++) { + tiles32.push_back(all_tiles_indexed[all_tile_16[j]]); } - c++; - if (c >= 64) { - sx = 0; - sy = 0; - c = 0; + for (const auto &tile : unique_tiles) { + tiles32_unique_.push_back(static_cast(tile)); + } + + while (tiles32_unique_.size() % 4 != 0) { + gfx::Tile32 padding_tile(420, 420, 420, 420); + tiles32_unique_.push_back(padding_tile.GetPackedValue()); } } - std::vector uniqueTiles(allTile16); // Ensure it's 64 bits - std::sort(uniqueTiles.begin(), uniqueTiles.end()); - uniqueTiles.erase(std::unique(uniqueTiles.begin(), uniqueTiles.end()), - uniqueTiles.end()); - - std::unordered_map alltilesIndexed; // Ensure it's 64 bits - for (size_t i = 0; i < uniqueTiles.size(); i++) { - alltilesIndexed.insert({uniqueTiles[i], static_cast(i)}); - } - - for (int i = 0; i < NumberOfMap32; i++) { - this->tiles32.push_back(alltilesIndexed[allTile16[i]]); - } - - for (const auto &tile : uniqueTiles) { - this->tiles32_unique_.push_back(static_cast(tile)); - } - - while (this->tiles32_unique_.size() % 4 != 0) { - gfx::Tile32 paddingTile(420, 420, 420, 420); - this->tiles32_unique_.push_back(paddingTile.GetPackedValue()); - } - - if (onlyShow) { - std::cout << "Number of unique Tiles32: " << uniqueTiles.size() + if (only_show) { + std::cout << "Number of unique Tiles32: " << tiles32_unique_.size() << " Out of: " << LimitOfMap32 << std::endl; - } else if (this->tiles32_unique_.size() > LimitOfMap32) { - std::cerr << "Number of unique Tiles32: " << uniqueTiles.size() + } else if (tiles32_unique_.size() > LimitOfMap32) { + std::cerr << "Number of unique Tiles32: " << tiles32_unique_.size() << " Out of: " << LimitOfMap32 << "\nUnique Tile32 count exceed the limit" << "\nThe ROM Has not been saved" @@ -558,14 +528,14 @@ bool Overworld::CreateTile32Tilemap(bool onlyShow) { return true; } - std::cout << "Number of unique Tiles32: " << uniqueTiles.size() - << " Saved:" << this->tiles32_unique_.size() + std::cout << "Number of unique Tiles32: " << tiles32_unique_.size() + << " Saved:" << tiles32_unique_.size() << " Out of: " << LimitOfMap32 << std::endl; - int v = this->tiles32_unique_.size(); + int v = tiles32_unique_.size(); for (int i = v; i < LimitOfMap32; i++) { - gfx::Tile32 paddingTile(420, 420, 420, 420); - this->tiles32_unique_.push_back(paddingTile.GetPackedValue()); + gfx::Tile32 padding_tile(420, 420, 420, 420); + tiles32_unique_.push_back(padding_tile.GetPackedValue()); } return false; @@ -588,14 +558,15 @@ void Overworld::SaveMap16Tiles() { } } -void Overworld::SaveMap32Tiles() { +// ---------------------------------------------------------------------------- + +absl::Status Overworld::SaveMap32Tiles() { int index = 0; int c = tiles32_unique_.size(); for (int i = 0; i < c; i += 6) { if (index >= 0x4540) { - std::cout << "Too many unique tiles!" << std::endl; - break; + return absl::AbortedError("Too many unique tile32 definitions."); } // Helper lambda to avoid code repetition @@ -624,19 +595,22 @@ void Overworld::SaveMap32Tiles() { index += 4; c += 2; } + return absl::OkStatus(); } // ---------------------------------------------------------------------------- -ushort Overworld::GenerateTile32(int i, int k, int dimension) { +uint16_t Overworld::GenerateTile32(int index, int quadrant, int dimension) { + // The addresses of the four 32x32 pixel tiles in the ROM. const uint32_t map32address[4] = {rom()->GetVersionConstants().kMap32TileTL, rom()->GetVersionConstants().kMap32TileTR, rom()->GetVersionConstants().kMap32TileBL, rom()->GetVersionConstants().kMap32TileBR}; - return (ushort)(rom_[map32address[dimension] + k + (i)] + - (((rom_[map32address[dimension] + (i) + (k <= 1 ? 4 : 5)] >> - (k % 2 == 0 ? 4 : 0)) & + return (ushort)(rom_[map32address[dimension] + quadrant + (index)] + + (((rom_[map32address[dimension] + (index) + + (quadrant <= 1 ? 4 : 5)] >> + (quadrant % 2 == 0 ? 4 : 0)) & 0x0F) * 256)); } @@ -644,15 +618,24 @@ ushort Overworld::GenerateTile32(int i, int k, int dimension) { // ---------------------------------------------------------------------------- void Overworld::AssembleMap32Tiles() { + // Loop through each 32x32 pixel tile in the ROM. for (int i = 0; i < 0x33F0; i += 6) { + // Loop through each quadrant of the 32x32 pixel tile. for (int k = 0; k < 4; k++) { - tiles32.push_back(gfx::Tile32( - /*top-left=*/GenerateTile32(i, k, (int)Dimension::map32TilesTL), - /*top-right=*/GenerateTile32(i, k, (int)Dimension::map32TilesTR), - /*bottom-left=*/GenerateTile32(i, k, (int)Dimension::map32TilesBL), - /*bottom-right=*/GenerateTile32(i, k, (int)Dimension::map32TilesBR))); + // Generate the 16-bit tile for the current quadrant of the current 32x32 + // pixel tile. + uint16_t tl = GenerateTile32(i, k, (int)Dimension::map32TilesTL); + uint16_t tr = GenerateTile32(i, k, (int)Dimension::map32TilesTR); + uint16_t bl = GenerateTile32(i, k, (int)Dimension::map32TilesBL); + uint16_t br = GenerateTile32(i, k, (int)Dimension::map32TilesBR); + + // Add the generated 16-bit tiles to the tiles32 vector. + tiles32.push_back(gfx::Tile32(tl, tr, bl, br)); } } + + // Initialize the light_world, dark_world, and special_world vectors with the + // appropriate number of tiles. map_tiles_.light_world.resize(kTile32Num); map_tiles_.dark_world.resize(kTile32Num); map_tiles_.special_world.resize(kTile32Num); @@ -726,10 +709,10 @@ absl::Status Overworld::DecompressAllMapTiles() { for (int i = 0; i < 160; i++) { auto p1 = GetOwMapGfxHighPtr( rom()->data(), i, - rom()->GetVersionConstants().compressedAllMap32PointersHigh); + rom()->GetVersionConstants().kCompressedAllMap32PointersHigh); auto p2 = GetOwMapGfxLowPtr( rom()->data(), i, - rom()->GetVersionConstants().compressedAllMap32PointersLow); + rom()->GetVersionConstants().kCompressedAllMap32PointersLow); int ttpos = 0; if (p1 >= highest) { diff --git a/src/app/zelda3/overworld.h b/src/app/zelda3/overworld.h index a7c5c6b5..9af3603c 100644 --- a/src/app/zelda3/overworld.h +++ b/src/app/zelda3/overworld.h @@ -108,8 +108,8 @@ class OverworldEntrance { } }; -constexpr int compressedAllMap32PointersHigh = 0x1794D; -constexpr int compressedAllMap32PointersLow = 0x17B2D; +constexpr int kCompressedAllMap32PointersHigh = 0x1794D; +constexpr int kCompressedAllMap32PointersLow = 0x17B2D; constexpr int overworldgfxGroups = 0x05D97; constexpr int overworldPalGroup1 = 0xDE6C8; constexpr int overworldPalGroup2 = 0xDE86C; @@ -187,7 +187,7 @@ class Overworld : public SharedROM { bool CreateTile32Tilemap(bool onlyShow = false); void SaveMap16Tiles(); - void SaveMap32Tiles(); + absl::Status SaveMap32Tiles(); auto GetTiles16() const { return tiles16; } auto GetOverworldMap(uint index) { return overworld_maps_[index]; } @@ -221,7 +221,7 @@ class Overworld : public SharedROM { map32TilesBR = 3 }; - ushort GenerateTile32(int i, int k, int dimension); + uint16_t GenerateTile32(int index, int quadrant, int dimension); void AssembleMap32Tiles(); void AssembleMap16Tiles(); void AssignWorldTiles(int x, int y, int sx, int sy, int tpos, @@ -253,16 +253,16 @@ class Overworld : public SharedROM { absl::flat_hash_map proto_map_data_; - std::vector> mapDatap1 = + std::vector> map_data_p1 = std::vector>(kNumOverworldMaps); - std::vector> mapDatap2 = + std::vector> map_data_p2 = std::vector>(kNumOverworldMaps); - std::vector mapPointers1id = std::vector(kNumOverworldMaps); - std::vector mapPointers2id = std::vector(kNumOverworldMaps); + std::vector map_pointers1_id = std::vector(kNumOverworldMaps); + std::vector map_pointers2_id = std::vector(kNumOverworldMaps); - std::vector mapPointers1 = std::vector(kNumOverworldMaps); - std::vector mapPointers2 = std::vector(kNumOverworldMaps); + std::vector map_pointers1 = std::vector(kNumOverworldMaps); + std::vector map_pointers2 = std::vector(kNumOverworldMaps); }; } // namespace zelda3