Refactor ROM class, add RunTransaction
This commit is contained in:
@@ -10,12 +10,42 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/flat_hash_map.h" // for flat_hash_map
|
||||
#include "absl/status/status.h" // for Status
|
||||
#include "app/core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
// Define a hash map to hold the addresses of different palette groups
|
||||
const absl::flat_hash_map<std::string, uint32_t> paletteGroupAddresses = {
|
||||
{"ow_main", core::overworldPaletteMain},
|
||||
{"ow_aux", core::overworldPaletteAuxialiary},
|
||||
{"ow_animated", core::overworldPaletteAnimated},
|
||||
{"hud", core::hudPalettes},
|
||||
{"global_sprites", core::globalSpritePalettesLW},
|
||||
{"armors", core::armorPalettes},
|
||||
{"swords", core::swordPalettes},
|
||||
{"shields", core::shieldPalettes},
|
||||
{"sprites_aux1", core::spritePalettesAux1},
|
||||
{"sprites_aux2", core::spritePalettesAux2},
|
||||
{"sprites_aux3", core::spritePalettesAux3},
|
||||
{"dungeon_main", core::dungeonMainPalettes},
|
||||
{"grass", core::hardcodedGrassLW},
|
||||
{"3d_object", core::triforcePalette},
|
||||
{"ow_mini_map", core::overworldMiniMapPalettes},
|
||||
};
|
||||
|
||||
// Define a hash map to hold the number of colors in each palette group
|
||||
const absl::flat_hash_map<std::string, uint32_t> paletteGroupColorCounts = {
|
||||
{"ow_main", 35}, {"ow_aux", 21}, {"ow_animated", 7},
|
||||
{"hud", 32}, {"global_sprites", 60}, {"armors", 15},
|
||||
{"swords", 3}, {"shields", 4}, {"sprites_aux1", 7},
|
||||
{"sprites_aux2", 7}, {"sprites_aux3", 7}, {"dungeon_main", 90},
|
||||
{"grass", 1}, {"3d_object", 8}, {"ow_mini_map", 128},
|
||||
};
|
||||
|
||||
constexpr uint16_t SNES_RED_MASK = 32;
|
||||
constexpr uint16_t SNES_GREEN_MASK = 32;
|
||||
constexpr uint16_t SNES_BLUE_MASK = 32;
|
||||
@@ -65,6 +95,16 @@ std::vector<char> Convert(const std::vector<snes_color>& palette) {
|
||||
return data;
|
||||
}
|
||||
|
||||
SNESColor ReadColorFromROM(int offset, const uchar* rom) {
|
||||
short color = (ushort)((rom[offset + 1]) << 8) | rom[offset];
|
||||
snes_color new_color;
|
||||
new_color.red = (color & 0x1F) * 8;
|
||||
new_color.green = ((color >> 5) & 0x1F) * 8;
|
||||
new_color.blue = ((color >> 10) & 0x1F) * 8;
|
||||
SNESColor snes_color(new_color);
|
||||
return snes_color;
|
||||
}
|
||||
|
||||
SNESColor GetCgxColor(uint16_t color) {
|
||||
ImVec4 rgb;
|
||||
rgb.x = (color & 0x1F) * 8;
|
||||
@@ -163,6 +203,40 @@ SDL_Palette* SNESPalette::GetSDL_Palette() {
|
||||
return sdl_palette.get();
|
||||
}
|
||||
|
||||
SNESPalette ReadPaletteFromROM(int offset, int num_colors, const uchar* rom) {
|
||||
int color_offset = 0;
|
||||
std::vector<gfx::SNESColor> colors(num_colors);
|
||||
|
||||
while (color_offset < num_colors) {
|
||||
short color = (ushort)((rom[offset + 1]) << 8) | rom[offset];
|
||||
gfx::snes_color new_color;
|
||||
new_color.red = (color & 0x1F) * 8;
|
||||
new_color.green = ((color >> 5) & 0x1F) * 8;
|
||||
new_color.blue = ((color >> 10) & 0x1F) * 8;
|
||||
colors[color_offset].SetSNES(ConvertRGBtoSNES(new_color));
|
||||
color_offset++;
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
gfx::SNESPalette palette(colors);
|
||||
return palette;
|
||||
}
|
||||
|
||||
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
|
||||
size_t color_index) {
|
||||
// Retrieve the base address for the palette group
|
||||
uint32_t base_address = paletteGroupAddresses.at(group_name);
|
||||
|
||||
// Retrieve the number of colors for each palette in the group
|
||||
uint32_t colors_per_palette = paletteGroupColorCounts.at(group_name);
|
||||
|
||||
// Calculate the address for thes specified color in the ROM
|
||||
uint32_t address = base_address + (palette_index * colors_per_palette * 2) +
|
||||
(color_index * 2);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
std::array<float, 4> ToFloatArray(const SNESColor& color) {
|
||||
std::array<float, 4> colorArray;
|
||||
colorArray[0] = color.GetRGB().x / 255.0f;
|
||||
|
||||
@@ -12,35 +12,83 @@
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/casts.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/constants.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief Struct representing an SNES color.
|
||||
*/
|
||||
struct snes_color {
|
||||
uint16_t red;
|
||||
uint16_t blue;
|
||||
uint16_t green;
|
||||
uint16_t red; /**< Red component of the color. */
|
||||
uint16_t blue; /**< Blue component of the color. */
|
||||
uint16_t green; /**< Green component of the color. */
|
||||
};
|
||||
using snes_color = struct snes_color;
|
||||
|
||||
/**
|
||||
* @brief Struct representing an SNES palette.
|
||||
*/
|
||||
struct snes_palette {
|
||||
uint id;
|
||||
uint size;
|
||||
snes_color* colors;
|
||||
uint id; /**< ID of the palette. */
|
||||
uint size; /**< Size of the palette. */
|
||||
snes_color* colors; /**< Pointer to the colors in the palette. */
|
||||
};
|
||||
using snes_palette = struct snes_palette;
|
||||
|
||||
/**
|
||||
* @brief Converts an RGB color to an SNES color.
|
||||
*
|
||||
* @param color The RGB color to convert.
|
||||
* @return The converted SNES color.
|
||||
*/
|
||||
uint16_t ConvertRGBtoSNES(const snes_color& color);
|
||||
|
||||
/**
|
||||
* @brief Converts an SNES color to an RGB color.
|
||||
*
|
||||
* @param snes_color The SNES color to convert.
|
||||
* @return The converted RGB color.
|
||||
*/
|
||||
snes_color ConvertSNEStoRGB(uint16_t snes_color);
|
||||
|
||||
/**
|
||||
* @brief Extracts a vector of SNES colors from a data buffer.
|
||||
*
|
||||
* @param data The data buffer to extract from.
|
||||
* @param offset The offset in the buffer to start extracting from.
|
||||
* @param palette_size The size of the palette to extract.
|
||||
* @return A vector of SNES colors extracted from the buffer.
|
||||
*/
|
||||
std::vector<snes_color> Extract(const char* data, unsigned int offset,
|
||||
unsigned int palette_size);
|
||||
|
||||
/**
|
||||
* @brief Converts a vector of SNES colors to a vector of characters.
|
||||
*
|
||||
* @param palette The vector of SNES colors to convert.
|
||||
* @return A vector of characters representing the converted SNES colors.
|
||||
*/
|
||||
std::vector<char> Convert(const std::vector<snes_color>& palette);
|
||||
|
||||
/**
|
||||
* @brief Struct representing an SNES color with additional functionality.
|
||||
*/
|
||||
struct SNESColor {
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*/
|
||||
SNESColor() : rgb(0.f, 0.f, 0.f, 0.f), snes(0) {}
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes an ImVec4 value and converts it to an SNES
|
||||
* color.
|
||||
*
|
||||
* @param val The ImVec4 value to convert.
|
||||
*/
|
||||
explicit SNESColor(const ImVec4 val) : rgb(val) {
|
||||
snes_color color;
|
||||
color.red = val.x / 255;
|
||||
@@ -49,12 +97,29 @@ struct SNESColor {
|
||||
snes = ConvertRGBtoSNES(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes an SNES color and initializes the object with
|
||||
* it.
|
||||
*
|
||||
* @param val The SNES color to initialize with.
|
||||
*/
|
||||
explicit SNESColor(const snes_color val)
|
||||
: rgb(val.red, val.green, val.blue, 255.f),
|
||||
snes(ConvertRGBtoSNES(val)),
|
||||
rom_color(val) {}
|
||||
|
||||
/**
|
||||
* @brief Gets the RGB value of the color.
|
||||
*
|
||||
* @return The RGB value of the color.
|
||||
*/
|
||||
ImVec4 GetRGB() const { return rgb; }
|
||||
|
||||
/**
|
||||
* @brief Sets the RGB value of the color.
|
||||
*
|
||||
* @param val The RGB value to set.
|
||||
*/
|
||||
void SetRGB(const ImVec4 val) {
|
||||
rgb.x = val.x / 255;
|
||||
rgb.y = val.y / 255;
|
||||
@@ -68,11 +133,25 @@ struct SNESColor {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Used for indexed Bitmaps from CGX files
|
||||
/**
|
||||
* @brief Gets the RGB value of the color as it appears in the ROM.
|
||||
*
|
||||
* @return The RGB value of the color as it appears in the ROM.
|
||||
*/
|
||||
snes_color GetRomRGB() const { return rom_color; }
|
||||
|
||||
/**
|
||||
* @brief Gets the SNES value of the color.
|
||||
*
|
||||
* @return The SNES value of the color.
|
||||
*/
|
||||
uint16_t GetSNES() const { return snes; }
|
||||
|
||||
/**
|
||||
* @brief Sets the SNES value of the color.
|
||||
*
|
||||
* @param val The SNES value to set.
|
||||
*/
|
||||
void SetSNES(uint16_t val) {
|
||||
snes = val;
|
||||
snes_color col = ConvertSNEStoRGB(val);
|
||||
@@ -80,25 +159,79 @@ struct SNESColor {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the color has been modified.
|
||||
*
|
||||
* @return True if the color has been modified, false otherwise.
|
||||
*/
|
||||
bool isModified() const { return modified; }
|
||||
|
||||
/**
|
||||
* @brief Checks if the color is transparent.
|
||||
*
|
||||
* @return True if the color is transparent, false otherwise.
|
||||
*/
|
||||
bool isTransparent() const { return transparent; }
|
||||
|
||||
/**
|
||||
* @brief Sets whether the color is transparent or not.
|
||||
*
|
||||
* @param t True to set the color as transparent, false otherwise.
|
||||
*/
|
||||
void setTransparent(bool t) { transparent = t; }
|
||||
|
||||
/**
|
||||
* @brief Sets whether the color has been modified or not.
|
||||
*
|
||||
* @param m True to set the color as modified, false otherwise.
|
||||
*/
|
||||
void setModified(bool m) { modified = m; }
|
||||
|
||||
private:
|
||||
ImVec4 rgb;
|
||||
uint16_t snes;
|
||||
snes_color rom_color;
|
||||
bool modified = false;
|
||||
bool transparent = false;
|
||||
ImVec4 rgb; /**< The RGB value of the color. */
|
||||
uint16_t snes; /**< The SNES value of the color. */
|
||||
snes_color
|
||||
rom_color; /**< The RGB value of the color as it appears in the ROM. */
|
||||
bool modified = false; /**< Whether the color has been modified or not. */
|
||||
bool transparent = false; /**< Whether the color is transparent or not. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Reads an SNES color from a ROM.
|
||||
*
|
||||
* @param offset The offset in the ROM to read from.
|
||||
* @param rom The ROM to read from.
|
||||
*/
|
||||
gfx::SNESColor ReadColorFromROM(int offset, const uchar* rom);
|
||||
|
||||
/**
|
||||
* @brief Gets an SNES color from a CGX color.
|
||||
*
|
||||
* @param color The CGX color to get the SNES color from.
|
||||
* @return The SNES color.
|
||||
*/
|
||||
SNESColor GetCgxColor(uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Gets a vector of SNES colors from a data buffer.
|
||||
*
|
||||
* @param data The data buffer to extract from.
|
||||
* @return A vector of SNES colors extracted from the buffer.
|
||||
*/
|
||||
std::vector<SNESColor> GetColFileData(uchar* data);
|
||||
|
||||
/**
|
||||
* @brief Class representing an SNES palette with additional functionality.
|
||||
*/
|
||||
class SNESPalette {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor that takes a vector of data and initializes the palette
|
||||
* with it.
|
||||
*
|
||||
* @tparam T The type of data in the vector.
|
||||
* @param data The vector of data to initialize the palette with.
|
||||
*/
|
||||
template <typename T>
|
||||
explicit SNESPalette(const std::vector<T>& data) {
|
||||
for (const auto& item : data) {
|
||||
@@ -106,16 +239,72 @@ class SNESPalette {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*/
|
||||
SNESPalette() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes a size and initializes the palette with it.
|
||||
*
|
||||
* @param mSize The size to initialize the palette with.
|
||||
*/
|
||||
explicit SNESPalette(uint8_t mSize);
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes a character array and initializes the palette
|
||||
* with it.
|
||||
*
|
||||
* @param snesPal The character array to initialize the palette with.
|
||||
*/
|
||||
explicit SNESPalette(char* snesPal);
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes an unsigned character array and initializes
|
||||
* the palette with it.
|
||||
*
|
||||
* @param snes_pal The unsigned character array to initialize the palette
|
||||
* with.
|
||||
*/
|
||||
explicit SNESPalette(const unsigned char* snes_pal);
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes a vector of ImVec4 values and initializes the
|
||||
* palette with it.
|
||||
*
|
||||
* @param The vector of ImVec4 values to initialize the palette with.
|
||||
*/
|
||||
explicit SNESPalette(const std::vector<ImVec4>&);
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes a vector of SNES colors and initializes the
|
||||
* palette with it.
|
||||
*
|
||||
* @param The vector of SNES colors to initialize the palette with.
|
||||
*/
|
||||
explicit SNESPalette(const std::vector<snes_color>&);
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes a vector of SNES colors with additional
|
||||
* functionality and initializes the palette with it.
|
||||
*
|
||||
* @param The vector of SNES colors with additional functionality to
|
||||
* initialize the palette with.
|
||||
*/
|
||||
explicit SNESPalette(const std::vector<SNESColor>&);
|
||||
|
||||
/**
|
||||
* @brief Gets the SDL palette associated with the SNES palette.
|
||||
*
|
||||
* @return The SDL palette.
|
||||
*/
|
||||
SDL_Palette* GetSDL_Palette();
|
||||
|
||||
/**
|
||||
* @brief Creates the palette with a vector of SNES colors.
|
||||
*
|
||||
* @param cols The vector of SNES colors to create the palette with.
|
||||
*/
|
||||
void Create(const std::vector<SNESColor>& cols) {
|
||||
for (const auto& each : cols) {
|
||||
colors.push_back(each);
|
||||
@@ -123,16 +312,32 @@ class SNESPalette {
|
||||
size_ = cols.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an SNES color to the palette.
|
||||
*
|
||||
* @param color The SNES color to add.
|
||||
*/
|
||||
void AddColor(SNESColor color) {
|
||||
colors.push_back(color);
|
||||
size_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an SNES color to the palette.
|
||||
*
|
||||
* @param color The SNES color to add.
|
||||
*/
|
||||
void AddColor(snes_color color) {
|
||||
colors.emplace_back(color);
|
||||
size_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the color at the specified index.
|
||||
*
|
||||
* @param i The index of the color to get.
|
||||
* @return The color at the specified index.
|
||||
*/
|
||||
auto GetColor(int i) const {
|
||||
if (i > size_) {
|
||||
throw std::out_of_range("SNESPalette: Index out of bounds");
|
||||
@@ -140,13 +345,27 @@ class SNESPalette {
|
||||
return colors[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the palette.
|
||||
*/
|
||||
void Clear() {
|
||||
colors.clear();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the size of the palette.
|
||||
*
|
||||
* @return The size of the palette.
|
||||
*/
|
||||
auto size() const { return colors.size(); }
|
||||
|
||||
/**
|
||||
* @brief Gets the color at the specified index.
|
||||
*
|
||||
* @param i The index of the color to get.
|
||||
* @return The color at the specified index.
|
||||
*/
|
||||
SNESColor operator[](int i) {
|
||||
if (i > size_) {
|
||||
throw std::out_of_range("SNESPalette: Index out of bounds");
|
||||
@@ -154,6 +373,12 @@ class SNESPalette {
|
||||
return colors[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the color at the specified index.
|
||||
*
|
||||
* @param i The index of the color to set.
|
||||
* @param color The color to set.
|
||||
*/
|
||||
void operator()(int i, const SNESColor& color) {
|
||||
if (i >= size_) {
|
||||
throw std::out_of_range("SNESPalette: Index out of bounds");
|
||||
@@ -161,6 +386,12 @@ class SNESPalette {
|
||||
colors[i] = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the color at the specified index.
|
||||
*
|
||||
* @param i The index of the color to set.
|
||||
* @param color The color to set.
|
||||
*/
|
||||
void operator()(int i, const ImVec4& color) {
|
||||
if (i >= size_) {
|
||||
throw std::out_of_range("SNESPalette: Index out of bounds");
|
||||
@@ -170,35 +401,96 @@ class SNESPalette {
|
||||
}
|
||||
|
||||
private:
|
||||
int size_ = 0;
|
||||
std::vector<SNESColor> colors;
|
||||
int size_ = 0; /**< The size of the palette. */
|
||||
std::vector<SNESColor> colors; /**< The colors in the palette. */
|
||||
};
|
||||
|
||||
SNESPalette ReadPaletteFromROM(int offset, int num_colors, const uchar* rom);
|
||||
|
||||
/**
|
||||
* @brief Gets the address of a palette in a group.
|
||||
*
|
||||
* @param group_name The name of the group.
|
||||
* @param palette_index The index of the palette.
|
||||
* @param color_index The index of the color.
|
||||
* @return The address of the palette.
|
||||
*/
|
||||
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
|
||||
size_t color_index);
|
||||
|
||||
/**
|
||||
* @brief Converts an SNES color to a float array.
|
||||
*
|
||||
* @param color The SNES color to convert.
|
||||
* @return The float array representing the SNES color.
|
||||
*/
|
||||
std::array<float, 4> ToFloatArray(const SNESColor& color);
|
||||
|
||||
/**
|
||||
* @brief Struct representing a group of SNES palettes.
|
||||
*/
|
||||
struct PaletteGroup {
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*/
|
||||
PaletteGroup() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructor that takes a size and initializes the group with it.
|
||||
*
|
||||
* @param mSize The size to initialize the group with.
|
||||
*/
|
||||
explicit PaletteGroup(uint8_t mSize);
|
||||
|
||||
void AddPalette(SNESPalette pal) {
|
||||
/**
|
||||
* @brief Adds a palette to the group.
|
||||
*
|
||||
* @param pal The palette to add.
|
||||
* @return An absl::Status indicating whether the operation was successful or
|
||||
* not.
|
||||
*/
|
||||
absl::Status AddPalette(SNESPalette pal) {
|
||||
palettes.emplace_back(pal);
|
||||
size_ = palettes.size();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void AddColor(SNESColor color) {
|
||||
/**
|
||||
* @brief Adds a color to the group.
|
||||
*
|
||||
* @param color The color to add.
|
||||
* @return An absl::Status indicating whether the operation was successful or
|
||||
* not.
|
||||
*/
|
||||
absl::Status AddColor(SNESColor color) {
|
||||
if (size_ == 0) {
|
||||
palettes.emplace_back();
|
||||
}
|
||||
palettes[0].AddColor(color);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the group.
|
||||
*/
|
||||
void Clear() {
|
||||
palettes.clear();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the size of the group.
|
||||
*
|
||||
* @return The size of the group.
|
||||
*/
|
||||
auto size() const { return palettes.size(); }
|
||||
|
||||
/**
|
||||
* @brief Gets the palette at the specified index.
|
||||
*
|
||||
* @param i The index of the palette to get.
|
||||
* @return The palette at the specified index.
|
||||
*/
|
||||
SNESPalette operator[](int i) {
|
||||
if (i > size_) {
|
||||
std::cout << "PaletteGroup: Index out of bounds" << std::endl;
|
||||
@@ -207,11 +499,63 @@ struct PaletteGroup {
|
||||
return palettes[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the palette at the specified index.
|
||||
*
|
||||
* @param i The index of the palette to get.
|
||||
* @return The palette at the specified index.
|
||||
*/
|
||||
const SNESPalette& operator[](int i) const {
|
||||
if (i > size_) {
|
||||
std::cout << "PaletteGroup: Index out of bounds" << std::endl;
|
||||
return palettes[0];
|
||||
}
|
||||
return palettes[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the color at the specified index.
|
||||
*
|
||||
* @param i The index of the color to set.
|
||||
* @param color The color to set.
|
||||
* @return An absl::Status indicating whether the operation was successful or
|
||||
* not.
|
||||
*/
|
||||
absl::Status operator()(int i, const SNESColor& color) {
|
||||
if (i >= size_) {
|
||||
return absl::InvalidArgumentError("PaletteGroup: Index out of bounds");
|
||||
}
|
||||
palettes[i](0, color);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the color at the specified index.
|
||||
*
|
||||
* @param i The index of the color to set.
|
||||
* @param color The color to set.
|
||||
* @return An absl::Status indicating whether the operation was successful or
|
||||
* not.
|
||||
*/
|
||||
absl::Status operator()(int i, const ImVec4& color) {
|
||||
if (i >= size_) {
|
||||
return absl::InvalidArgumentError("PaletteGroup: Index out of bounds");
|
||||
}
|
||||
palettes[i](0, color);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
private:
|
||||
int size_ = 0;
|
||||
std::vector<SNESPalette> palettes;
|
||||
int size_ = 0; /**< The size of the group. */
|
||||
std::vector<SNESPalette> palettes; /**< The palettes in the group. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a palette group from a vector of SNES colors.
|
||||
*
|
||||
* @param colors The vector of SNES colors to create the group from.
|
||||
* @return The created palette group.
|
||||
*/
|
||||
PaletteGroup CreatePaletteGroupFromColFile(std::vector<SNESColor>& colors);
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
@@ -52,6 +52,13 @@ class TileInfo {
|
||||
vertical_mirror_(v),
|
||||
horizontal_mirror_(h),
|
||||
palette_(palette) {}
|
||||
|
||||
bool operator==(const TileInfo& other) const {
|
||||
return id_ == other.id_ && over_ == other.over_ &&
|
||||
vertical_mirror_ == other.vertical_mirror_ &&
|
||||
horizontal_mirror_ == other.horizontal_mirror_ &&
|
||||
palette_ == other.palette_;
|
||||
}
|
||||
};
|
||||
|
||||
uint16_t TileInfoToWord(TileInfo tile_info);
|
||||
@@ -123,6 +130,13 @@ class Tile16 {
|
||||
tiles_info.push_back(tile2_);
|
||||
tiles_info.push_back(tile3_);
|
||||
}
|
||||
|
||||
bool operator==(const Tile16& other) const {
|
||||
return tile0_ == other.tile0_ && tile1_ == other.tile1_ &&
|
||||
tile2_ == other.tile2_ && tile3_ == other.tile3_;
|
||||
}
|
||||
|
||||
bool operator!=(const Tile16& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
class OAMTile {
|
||||
|
||||
423
src/app/rom.cc
423
src/app/rom.cc
@@ -28,6 +28,164 @@
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace {
|
||||
absl::Status LoadOverworldMainPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["ow_main"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::overworldPaletteMain + (i * (35 * 2)),
|
||||
/*num_colors*/ 35, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadOverworldAuxiliaryPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["ow_aux"].AddPalette(gfx::ReadPaletteFromROM(
|
||||
core::overworldPaletteAuxialiary + (i * (21 * 2)),
|
||||
/*num_colors*/ 21, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadOverworldAnimatedPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 14; i++) {
|
||||
RETURN_IF_ERROR(
|
||||
palette_groups["ow_animated"].AddPalette(gfx::ReadPaletteFromROM(
|
||||
core::overworldPaletteAnimated + (i * (7 * 2)), 7, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadHUDPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["hud"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::hudPalettes + (i * 64), 32, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadGlobalSpritePalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
RETURN_IF_ERROR(palette_groups["global_sprites"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::globalSpritePalettesLW, 60, data)))
|
||||
RETURN_IF_ERROR(palette_groups["global_sprites"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::globalSpritePalettesDW, 60, data)))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadArmorPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["armors"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::armorPalettes + (i * 30), 15, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadSwordPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["swords"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::swordPalettes + (i * 6), 3, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadShieldPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["shields"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::shieldPalettes + (i * 8), 4, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadSpriteAux1Palettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 12; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["sprites_aux1"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::spritePalettesAux1 + (i * 14), 7, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadSpriteAux2Palettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 11; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["sprites_aux2"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::spritePalettesAux2 + (i * 14), 7, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadSpriteAux3Palettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 24; i++) {
|
||||
RETURN_IF_ERROR(palette_groups["sprites_aux3"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::spritePalettesAux3 + (i * 14), 7, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadDungeonMainPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
RETURN_IF_ERROR(
|
||||
palette_groups["dungeon_main"].AddPalette(gfx::ReadPaletteFromROM(
|
||||
core::dungeonMainPalettes + (i * 180), 90, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadGrassColors(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
RETURN_IF_ERROR(palette_groups["grass"].AddColor(
|
||||
gfx::ReadColorFromROM(core::hardcodedGrassLW, rom_data.data())))
|
||||
RETURN_IF_ERROR(palette_groups["grass"].AddColor(
|
||||
gfx::ReadColorFromROM(core::hardcodedGrassDW, rom_data.data())))
|
||||
RETURN_IF_ERROR(palette_groups["grass"].AddColor(
|
||||
gfx::ReadColorFromROM(core::hardcodedGrassSpecial, rom_data.data())));
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status Load3DObjectPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
RETURN_IF_ERROR(palette_groups["3d_object"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::triforcePalette, 8, data)))
|
||||
RETURN_IF_ERROR(palette_groups["3d_object"].AddPalette(
|
||||
gfx::ReadPaletteFromROM(core::crystalPalette, 8, data)))
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status LoadOverworldMiniMapPalettes(const Bytes& rom_data,
|
||||
PaletteGroupMap& palette_groups) {
|
||||
auto data = rom_data.data();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
RETURN_IF_ERROR(
|
||||
palette_groups["ow_mini_map"].AddPalette(gfx::ReadPaletteFromROM(
|
||||
core::overworldMiniMapPalettes + (i * 256), 128, data)))
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
absl::StatusOr<Bytes> ROM::Load2BppGraphics() {
|
||||
Bytes sheet;
|
||||
const uint8_t sheets[] = {113, 114, 218, 219, 220, 221};
|
||||
@@ -44,8 +202,6 @@ absl::StatusOr<Bytes> ROM::Load2BppGraphics() {
|
||||
return sheet;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
absl::Status ROM::LoadAllGraphicsData() {
|
||||
Bytes sheet;
|
||||
bool bpp3 = false;
|
||||
@@ -85,7 +241,24 @@ absl::Status ROM::LoadAllGraphicsData() {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
absl::Status ROM::LoadAllPalettes() {
|
||||
RETURN_IF_ERROR(LoadOverworldMainPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadOverworldAuxiliaryPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadOverworldAnimatedPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadHUDPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadGlobalSpritePalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadArmorPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadSwordPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadShieldPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadSpriteAux1Palettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadSpriteAux2Palettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadSpriteAux3Palettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadDungeonMainPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadGrassColors(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(Load3DObjectPalettes(rom_data_, palette_groups_));
|
||||
RETURN_IF_ERROR(LoadOverworldMiniMapPalettes(rom_data_, palette_groups_));
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status ROM::LoadFromFile(const absl::string_view& filename,
|
||||
bool z3_load) {
|
||||
@@ -141,8 +314,6 @@ absl::Status ROM::LoadFromFile(const absl::string_view& filename,
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
absl::Status ROM::LoadFromPointer(uchar* data, size_t length) {
|
||||
if (!data)
|
||||
return absl::InvalidArgumentError(
|
||||
@@ -153,8 +324,6 @@ absl::Status ROM::LoadFromPointer(uchar* data, size_t length) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
absl::Status ROM::LoadFromBytes(const Bytes& data) {
|
||||
if (data.empty()) {
|
||||
return absl::InvalidArgumentError(
|
||||
@@ -166,138 +335,6 @@ absl::Status ROM::LoadFromBytes(const Bytes& data) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
void ROM::LoadAllPalettes() {
|
||||
// 35 colors each, 7x5 (0,2 on grid)
|
||||
for (int i = 0; i < 6; i++) {
|
||||
palette_groups_["ow_main"].AddPalette(
|
||||
ReadPalette(core::overworldPaletteMain + (i * (35 * 2)), 35));
|
||||
}
|
||||
// 21 colors each, 7x3 (8,2 and 8,5 on grid)
|
||||
for (int i = 0; i < 20; i++) {
|
||||
palette_groups_["ow_aux"].AddPalette(
|
||||
ReadPalette(core::overworldPaletteAuxialiary + (i * (21 * 2)), 21));
|
||||
}
|
||||
// 7 colors each 7x1 (0,7 on grid)
|
||||
for (int i = 0; i < 14; i++) {
|
||||
palette_groups_["ow_animated"].AddPalette(
|
||||
ReadPalette(core::overworldPaletteAnimated + (i * (7 * 2)), 7));
|
||||
}
|
||||
// 32 colors each 16x2 (0,0 on grid)
|
||||
for (int i = 0; i < 2; i++) {
|
||||
palette_groups_["hud"].AddPalette(
|
||||
ReadPalette(core::hudPalettes + (i * 64), 32));
|
||||
}
|
||||
|
||||
palette_groups_["global_sprites"].AddPalette(
|
||||
ReadPalette(core::globalSpritePalettesLW, 60));
|
||||
palette_groups_["global_sprites"].AddPalette(
|
||||
ReadPalette(core::globalSpritePalettesDW, 60));
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
palette_groups_["armors"].AddPalette(
|
||||
ReadPalette(core::armorPalettes + (i * 30), 15));
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
palette_groups_["swords"].AddPalette(
|
||||
ReadPalette(core::swordPalettes + (i * 6), 3));
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
palette_groups_["shields"].AddPalette(
|
||||
ReadPalette(core::shieldPalettes + (i * 8), 4));
|
||||
}
|
||||
for (int i = 0; i < 12; i++) {
|
||||
palette_groups_["sprites_aux1"].AddPalette(
|
||||
ReadPalette(core::spritePalettesAux1 + (i * 14), 7));
|
||||
}
|
||||
for (int i = 0; i < 11; i++) {
|
||||
palette_groups_["sprites_aux2"].AddPalette(
|
||||
ReadPalette(core::spritePalettesAux2 + (i * 14), 7));
|
||||
}
|
||||
for (int i = 0; i < 24; i++) {
|
||||
palette_groups_["sprites_aux3"].AddPalette(
|
||||
ReadPalette(core::spritePalettesAux3 + (i * 14), 7));
|
||||
}
|
||||
for (int i = 0; i < 20; i++) {
|
||||
palette_groups_["dungeon_main"].AddPalette(
|
||||
ReadPalette(core::dungeonMainPalettes + (i * 180), 90));
|
||||
}
|
||||
|
||||
// TODO: Make these grass colors editable color fields
|
||||
palette_groups_["grass"].AddColor(ReadColor(core::hardcodedGrassLW));
|
||||
palette_groups_["grass"].AddColor(ReadColor(core::hardcodedGrassDW));
|
||||
palette_groups_["grass"].AddColor(ReadColor(core::hardcodedGrassSpecial));
|
||||
|
||||
palette_groups_["3d_object"].AddPalette(
|
||||
ReadPalette(core::triforcePalette, 8));
|
||||
palette_groups_["3d_object"].AddPalette(ReadPalette(core::crystalPalette, 8));
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
palette_groups_["ow_mini_map"].AddPalette(
|
||||
ReadPalette(core::overworldMiniMapPalettes + (i * 256), 128));
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
absl::Status ROM::UpdatePaletteColor(const std::string& groupName,
|
||||
size_t paletteIndex, size_t colorIndex,
|
||||
const gfx::SNESColor& newColor) {
|
||||
// Check if the groupName exists in the palette_groups_ map
|
||||
if (palette_groups_.find(groupName) != palette_groups_.end()) {
|
||||
// Check if the paletteIndex is within the range of available palettes in
|
||||
// the group
|
||||
if (paletteIndex < palette_groups_[groupName].size()) {
|
||||
// Check if the colorIndex is within the range of available colors in the
|
||||
// palette
|
||||
if (colorIndex < palette_groups_[groupName][paletteIndex].size()) {
|
||||
// Update the color value in the palette
|
||||
palette_groups_[groupName][paletteIndex][colorIndex] = newColor;
|
||||
palette_groups_[groupName][paletteIndex][colorIndex].setModified(true);
|
||||
} else {
|
||||
return absl::AbortedError(
|
||||
"Error: Invalid color index in UpdatePaletteColor.");
|
||||
}
|
||||
} else {
|
||||
return absl::AbortedError(
|
||||
"Error: Invalid palette index in UpdatePaletteColor.");
|
||||
}
|
||||
} else {
|
||||
return absl::AbortedError(
|
||||
"Error: Invalid group name in UpdatePaletteColor");
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
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& [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];
|
||||
SavePalette(i, group_name, palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
absl::Status ROM::SaveToFile(bool backup, absl::string_view filename) {
|
||||
if (rom_data_.empty()) {
|
||||
return absl::InternalError("ROM data is empty.");
|
||||
@@ -338,19 +375,26 @@ absl::Status ROM::SaveToFile(bool backup, absl::string_view filename) {
|
||||
}
|
||||
|
||||
// Open the file that we know exists for writing
|
||||
std::fstream file(filename.data(), std::ios::binary | std::ios::out);
|
||||
if (!file.is_open()) {
|
||||
std::ofstream file(filename.data(), std::ios::binary);
|
||||
if (!file) {
|
||||
return absl::InternalError(
|
||||
absl::StrCat("Could not open ROM file: ", filename));
|
||||
}
|
||||
|
||||
using byte_cast = const char* (*)(const void*);
|
||||
|
||||
// Save the data to the file
|
||||
for (auto i = 0; i < size_; ++i) {
|
||||
file << rom_data_[i];
|
||||
try {
|
||||
file.write(
|
||||
static_cast<const char*>(static_cast<const void*>(rom_data_.data())),
|
||||
rom_data_.size());
|
||||
} catch (const std::ofstream::failure& e) {
|
||||
return absl::InternalError(absl::StrCat(
|
||||
"Error while writing to ROM file: ", filename, " - ", e.what()));
|
||||
}
|
||||
|
||||
// Check for write errors
|
||||
if (!file.good()) {
|
||||
if (!file) {
|
||||
return absl::InternalError(
|
||||
absl::StrCat("Error while writing to ROM file: ", filename));
|
||||
}
|
||||
@@ -358,54 +402,57 @@ absl::Status ROM::SaveToFile(bool backup, absl::string_view filename) {
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
gfx::SNESColor ROM::ReadColor(int offset) {
|
||||
short color = toint16(offset);
|
||||
gfx::snes_color new_color;
|
||||
new_color.red = (color & 0x1F) * 8;
|
||||
new_color.green = ((color >> 5) & 0x1F) * 8;
|
||||
new_color.blue = ((color >> 10) & 0x1F) * 8;
|
||||
gfx::SNESColor snes_color(new_color);
|
||||
return snes_color;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
gfx::SNESPalette ROM::ReadPalette(int offset, int num_colors) {
|
||||
int color_offset = 0;
|
||||
std::vector<gfx::SNESColor> colors(num_colors);
|
||||
|
||||
while (color_offset < num_colors) {
|
||||
short color = toint16(offset);
|
||||
gfx::snes_color new_color;
|
||||
new_color.red = (color & 0x1F) * 8;
|
||||
new_color.green = ((color >> 5) & 0x1F) * 8;
|
||||
new_color.blue = ((color >> 10) & 0x1F) * 8;
|
||||
colors[color_offset].SetSNES(gfx::ConvertRGBtoSNES(new_color));
|
||||
color_offset++;
|
||||
offset += 2;
|
||||
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(gfx::GetPaletteAddress(group_name, index, j), color);
|
||||
color.setModified(false); // Reset the modified flag after saving
|
||||
}
|
||||
}
|
||||
|
||||
gfx::SNESPalette palette(colors);
|
||||
return palette;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
void ROM::SaveAllPalettes() {
|
||||
// Iterate through all 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];
|
||||
SavePalette(i, group_name, palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ROM::GetPaletteAddress(const std::string& groupName,
|
||||
size_t paletteIndex, size_t color_index) const {
|
||||
// Retrieve the base address for the palette group
|
||||
uint32_t base_address = paletteGroupAddresses.at(groupName);
|
||||
|
||||
// Retrieve the number of colors for each palette in the group
|
||||
uint32_t colors_per_palette = paletteGroupColorCounts.at(groupName);
|
||||
|
||||
// Calculate the address for the specified color in the ROM
|
||||
uint32_t address = base_address + (paletteIndex * colors_per_palette * 2) +
|
||||
(color_index * 2);
|
||||
|
||||
return address;
|
||||
absl::Status ROM::UpdatePaletteColor(const std::string& groupName,
|
||||
size_t paletteIndex, size_t colorIndex,
|
||||
const gfx::SNESColor& newColor) {
|
||||
// Check if the groupName exists in the palette_groups_ map
|
||||
if (palette_groups_.find(groupName) != palette_groups_.end()) {
|
||||
// Check if the paletteIndex is within the range of available palettes in
|
||||
// the group
|
||||
if (paletteIndex < palette_groups_[groupName].size()) {
|
||||
// Check if the colorIndex is within the range of available colors in the
|
||||
// palette
|
||||
if (colorIndex < palette_groups_[groupName][paletteIndex].size()) {
|
||||
// Update the color value in the palette
|
||||
palette_groups_[groupName][paletteIndex][colorIndex] = newColor;
|
||||
palette_groups_[groupName][paletteIndex][colorIndex].setModified(true);
|
||||
} else {
|
||||
return absl::AbortedError(
|
||||
"Error: Invalid color index in UpdatePaletteColor.");
|
||||
}
|
||||
} else {
|
||||
return absl::AbortedError(
|
||||
"Error: Invalid palette index in UpdatePaletteColor.");
|
||||
}
|
||||
} else {
|
||||
return absl::AbortedError(
|
||||
"Error: Invalid group name in UpdatePaletteColor");
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::shared_ptr<ROM> SharedROM::shared_rom_ = nullptr;
|
||||
|
||||
218
src/app/rom.h
218
src/app/rom.h
@@ -19,7 +19,8 @@
|
||||
#include <stack> // for stack
|
||||
#include <string> // for hash, operator==
|
||||
#include <unordered_map> // for unordered_map
|
||||
#include <vector> // for vector
|
||||
#include <variant>
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "SDL_render.h" // for SDL_Renderer
|
||||
#include "absl/container/flat_hash_map.h" // for flat_hash_map
|
||||
@@ -38,6 +39,9 @@
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
using PaletteGroupMap = std::unordered_map<std::string, gfx::PaletteGroup>;
|
||||
|
||||
// Define an enum class for the different versions of the game
|
||||
enum class Z3_Version {
|
||||
US = 1,
|
||||
JP = 2,
|
||||
@@ -45,11 +49,11 @@ enum class Z3_Version {
|
||||
RANDO = 4,
|
||||
};
|
||||
|
||||
// Define a struct to hold the version-specific constants
|
||||
struct VersionConstants {
|
||||
uint32_t kGgxAnimatedPointer;
|
||||
uint32_t kOverworldGfxGroups1;
|
||||
uint32_t kOverworldGfxGroups2;
|
||||
// long ptrs all tiles of maps[high/low] (mapid* 3)
|
||||
uint32_t kCompressedAllMap32PointersHigh;
|
||||
uint32_t kCompressedAllMap32PointersLow;
|
||||
uint32_t overworldMapPaletteGroup;
|
||||
@@ -66,6 +70,7 @@ struct VersionConstants {
|
||||
uint32_t kSpriteBlocksetPointer;
|
||||
};
|
||||
|
||||
// Define a map to hold the version constants for each version
|
||||
static const std::map<Z3_Version, VersionConstants> kVersionConstantsMap = {
|
||||
{Z3_Version::US,
|
||||
{
|
||||
@@ -106,10 +111,9 @@ static const std::map<Z3_Version, VersionConstants> kVersionConstantsMap = {
|
||||
0x20000, // kMap32TileBL
|
||||
0x233C0, // kMap32TileBR
|
||||
0x5B97, // kSpriteBlocksetPointer
|
||||
}}
|
||||
|
||||
};
|
||||
}}};
|
||||
|
||||
// Define some constants used throughout the ROM class
|
||||
constexpr uint32_t kOverworldGraphicsPos1 = 0x4F80;
|
||||
constexpr uint32_t kOverworldGraphicsPos2 = 0x505F;
|
||||
constexpr uint32_t kOverworldGraphicsPos3 = 0x513E;
|
||||
@@ -122,34 +126,35 @@ constexpr uint32_t kNormalGfxSpaceEnd = 0xC4200;
|
||||
constexpr uint32_t kLinkSpriteLocation = 0x80000;
|
||||
constexpr uint32_t kFontSpriteLocation = 0x70000;
|
||||
|
||||
const absl::flat_hash_map<std::string, uint32_t> paletteGroupAddresses = {
|
||||
{"ow_main", core::overworldPaletteMain},
|
||||
{"ow_aux", core::overworldPaletteAuxialiary},
|
||||
{"ow_animated", core::overworldPaletteAnimated},
|
||||
{"hud", core::hudPalettes},
|
||||
{"global_sprites", core::globalSpritePalettesLW},
|
||||
{"armors", core::armorPalettes},
|
||||
{"swords", core::swordPalettes},
|
||||
{"shields", core::shieldPalettes},
|
||||
{"sprites_aux1", core::spritePalettesAux1},
|
||||
{"sprites_aux2", core::spritePalettesAux2},
|
||||
{"sprites_aux3", core::spritePalettesAux3},
|
||||
{"dungeon_main", core::dungeonMainPalettes},
|
||||
{"grass", core::hardcodedGrassLW},
|
||||
{"3d_object", core::triforcePalette},
|
||||
{"ow_mini_map", core::overworldMiniMapPalettes},
|
||||
};
|
||||
|
||||
const absl::flat_hash_map<std::string, uint32_t> paletteGroupColorCounts = {
|
||||
{"ow_main", 35}, {"ow_aux", 21}, {"ow_animated", 7},
|
||||
{"hud", 32}, {"global_sprites", 60}, {"armors", 15},
|
||||
{"swords", 3}, {"shields", 4}, {"sprites_aux1", 7},
|
||||
{"sprites_aux2", 7}, {"sprites_aux3", 7}, {"dungeon_main", 90},
|
||||
{"grass", 1}, {"3d_object", 8}, {"ow_mini_map", 128},
|
||||
struct WriteAction {
|
||||
int address;
|
||||
std::variant<uint8_t, uint16_t, std::vector<uint8_t>, gfx::SNESColor> value;
|
||||
};
|
||||
|
||||
class ROM {
|
||||
public:
|
||||
template <typename... Args>
|
||||
absl::Status RunTransaction(Args... args) {
|
||||
absl::Status status;
|
||||
// Fold expression to apply the Write function on each argument
|
||||
((status = WriteHelper(args)), ...);
|
||||
return status;
|
||||
}
|
||||
|
||||
absl::Status WriteHelper(const WriteAction& action) {
|
||||
if (std::holds_alternative<uint8_t>(action.value)) {
|
||||
return Write(action.address, std::get<uint8_t>(action.value));
|
||||
} else if (std::holds_alternative<uint16_t>(action.value)) {
|
||||
return WriteShort(action.address, std::get<uint16_t>(action.value));
|
||||
} else if (std::holds_alternative<std::vector<uint8_t>>(action.value)) {
|
||||
return WriteVector(action.address,
|
||||
std::get<std::vector<uint8_t>>(action.value));
|
||||
} else if (std::holds_alternative<gfx::SNESColor>(action.value)) {
|
||||
return WriteColor(action.address, std::get<gfx::SNESColor>(action.value));
|
||||
}
|
||||
return absl::InvalidArgumentError("Invalid write argument type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads 2bpp graphics from ROM data.
|
||||
*
|
||||
@@ -177,6 +182,17 @@ class ROM {
|
||||
*/
|
||||
absl::Status LoadAllGraphicsData();
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*
|
||||
*/
|
||||
absl::Status LoadAllPalettes();
|
||||
|
||||
/**
|
||||
* Load ROM data from a file.
|
||||
*
|
||||
@@ -190,37 +206,84 @@ class ROM {
|
||||
absl::Status LoadFromBytes(const Bytes& data);
|
||||
|
||||
/**
|
||||
* @brief Loads all the palettes for the game.
|
||||
* @brief Saves the ROM data to a file
|
||||
*
|
||||
* 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.
|
||||
* @param backup If true, creates a backup file with timestamp in its name
|
||||
* @param filename The name of the file to save the ROM data to
|
||||
*
|
||||
* @return absl::Status Returns an OK status if the save was successful,
|
||||
* otherwise returns an error status
|
||||
*/
|
||||
void LoadAllPalettes();
|
||||
absl::Status SaveToFile(bool backup, absl::string_view filename = "");
|
||||
|
||||
// Save functions
|
||||
/**
|
||||
* Saves the given palette to the ROM if any of its colors have been modified.
|
||||
*
|
||||
* @param index The index of the palette to save.
|
||||
* @param group_name The name of the group containing the palette.
|
||||
* @param palette The palette to save.
|
||||
*/
|
||||
void SavePalette(int index, const std::string& group_name,
|
||||
gfx::SNESPalette& palette);
|
||||
|
||||
/**
|
||||
* @brief Saves all palettes in the ROM.
|
||||
*
|
||||
* This function iterates through all palette groups and all palettes in each
|
||||
* group, and saves each palette using the SavePalette() function.
|
||||
*/
|
||||
void SaveAllPalettes();
|
||||
|
||||
/**
|
||||
* @brief Updates a color in a specified palette group.
|
||||
*
|
||||
* This function updates the color at the specified `colorIndex` in the
|
||||
* palette at `palette_index` within the palette group with the given
|
||||
* `group_name`. If the group, palette, or color indices are invalid, an error
|
||||
* is returned.
|
||||
*
|
||||
* @param group_name The name of the palette group to update.
|
||||
* @param palette_index The index of the palette within the group to update.
|
||||
* @param colorIndex The index of the color within the palette to update.
|
||||
* @param newColor The new color value to set.
|
||||
*
|
||||
* @return An `absl::Status` indicating whether the update was successful.
|
||||
* Returns `absl::OkStatus()` if successful, or an error status if the
|
||||
* group, palette, or color indices are invalid.
|
||||
*/
|
||||
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);
|
||||
uint8_t ReadByte(int offset) { return rom_data_[offset]; }
|
||||
uint16_t ReadWord(int offset) {
|
||||
return (uint16_t)(rom_data_[offset] | (rom_data_[offset + 1] << 8));
|
||||
absl::StatusOr<uint8_t> ReadByte(int offset) {
|
||||
if (offset >= rom_data_.size()) {
|
||||
return absl::InvalidArgumentError("Offset out of range");
|
||||
}
|
||||
return rom_data_[offset];
|
||||
}
|
||||
uint16_t ReadShort(int offset) {
|
||||
return (uint16_t)(rom_data_[offset] | (rom_data_[offset + 1] << 8));
|
||||
|
||||
absl::StatusOr<uint16_t> ReadWord(int offset) {
|
||||
if (offset + 1 >= rom_data_.size()) {
|
||||
return absl::InvalidArgumentError("Offset out of range");
|
||||
}
|
||||
auto result = (uint16_t)(rom_data_[offset] | (rom_data_[offset + 1] << 8));
|
||||
return result;
|
||||
}
|
||||
std::vector<uint8_t> ReadByteVector(uint32_t offset, uint32_t length) {
|
||||
|
||||
absl::StatusOr<uint16_t> ReadShort(int offset) {
|
||||
if (offset + 1 >= rom_data_.size()) {
|
||||
return absl::InvalidArgumentError("Offset out of range");
|
||||
}
|
||||
auto result = (uint16_t)(rom_data_[offset] | (rom_data_[offset + 1] << 8));
|
||||
return result;
|
||||
}
|
||||
|
||||
absl::StatusOr<std::vector<uint8_t>> ReadByteVector(uint32_t offset,
|
||||
uint32_t length) {
|
||||
if (offset + length > rom_data_.size()) {
|
||||
return absl::InvalidArgumentError("Offset and length out of range");
|
||||
}
|
||||
std::vector<uint8_t> result;
|
||||
for (int i = offset; i < length; i++) {
|
||||
result.push_back(rom_data_[i]);
|
||||
@@ -228,53 +291,72 @@ class ROM {
|
||||
return result;
|
||||
}
|
||||
|
||||
gfx::Tile16 ReadTile16(uint32_t tile16_id) {
|
||||
absl::StatusOr<gfx::Tile16> ReadTile16(uint32_t tile16_id) {
|
||||
// Skip 8 bytes per tile.
|
||||
auto tpos = 0x78000 + (tile16_id * 0x08);
|
||||
gfx::Tile16 tile16;
|
||||
tile16.tile0_ = gfx::WordToTileInfo(ReadShort(tpos));
|
||||
ASSIGN_OR_RETURN(auto new_tile0, ReadShort(tpos))
|
||||
tile16.tile0_ = gfx::WordToTileInfo(new_tile0);
|
||||
tpos += 2;
|
||||
tile16.tile1_ = gfx::WordToTileInfo(ReadShort(tpos));
|
||||
ASSIGN_OR_RETURN(auto new_tile1, ReadShort(tpos))
|
||||
tile16.tile1_ = gfx::WordToTileInfo(new_tile1);
|
||||
tpos += 2;
|
||||
tile16.tile2_ = gfx::WordToTileInfo(ReadShort(tpos));
|
||||
ASSIGN_OR_RETURN(auto new_tile2, ReadShort(tpos))
|
||||
tile16.tile2_ = gfx::WordToTileInfo(new_tile2);
|
||||
tpos += 2;
|
||||
tile16.tile3_ = gfx::WordToTileInfo(ReadShort(tpos));
|
||||
ASSIGN_OR_RETURN(auto new_tile3, ReadShort(tpos))
|
||||
tile16.tile3_ = gfx::WordToTileInfo(new_tile3);
|
||||
return tile16;
|
||||
}
|
||||
|
||||
void WriteTile16(int tile16_id, const gfx::Tile16& tile) {
|
||||
absl::Status WriteTile16(int tile16_id, const gfx::Tile16& tile) {
|
||||
// Skip 8 bytes per tile.
|
||||
auto tpos = 0x78000 + (tile16_id * 0x08);
|
||||
WriteShort(tpos, gfx::TileInfoToWord(tile.tile0_));
|
||||
RETURN_IF_ERROR(WriteShort(tpos, gfx::TileInfoToWord(tile.tile0_)));
|
||||
tpos += 2;
|
||||
WriteShort(tpos, gfx::TileInfoToWord(tile.tile1_));
|
||||
RETURN_IF_ERROR(WriteShort(tpos, gfx::TileInfoToWord(tile.tile1_)));
|
||||
tpos += 2;
|
||||
WriteShort(tpos, gfx::TileInfoToWord(tile.tile2_));
|
||||
RETURN_IF_ERROR(WriteShort(tpos, gfx::TileInfoToWord(tile.tile2_)));
|
||||
tpos += 2;
|
||||
WriteShort(tpos, gfx::TileInfoToWord(tile.tile3_));
|
||||
RETURN_IF_ERROR(WriteShort(tpos, gfx::TileInfoToWord(tile.tile3_)));
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// Write functions
|
||||
void Write(int addr, int value) { rom_data_[addr] = value; }
|
||||
|
||||
void WriteShort(uint32_t addr, uint16_t value) {
|
||||
rom_data_[addr] = (uint8_t)(value & 0xFF);
|
||||
rom_data_[addr + 1] = (uint8_t)((value >> 8) & 0xFF);
|
||||
absl::Status Write(int addr, int value) {
|
||||
if (addr >= rom_data_.size()) {
|
||||
return absl::InvalidArgumentError("Address out of range");
|
||||
}
|
||||
rom_data_[addr] = value;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void WriteVector(int addr, std::vector<uint8_t> data) {
|
||||
absl::Status WriteShort(uint32_t addr, uint16_t value) {
|
||||
if (addr + 1 >= rom_data_.size()) {
|
||||
return absl::InvalidArgumentError("Address out of range");
|
||||
}
|
||||
rom_data_[addr] = (uint8_t)(value & 0xFF);
|
||||
rom_data_[addr + 1] = (uint8_t)((value >> 8) & 0xFF);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status WriteVector(int addr, std::vector<uint8_t> data) {
|
||||
if (addr + data.size() > rom_data_.size()) {
|
||||
return absl::InvalidArgumentError("Address and data size out of range");
|
||||
}
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
rom_data_[addr + i] = data[i];
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void WriteColor(uint32_t address, const gfx::SNESColor& color) {
|
||||
absl::Status WriteColor(uint32_t address, const gfx::SNESColor& color) {
|
||||
uint16_t bgr = ((color.GetSNES() >> 10) & 0x1F) |
|
||||
((color.GetSNES() & 0x1F) << 10) |
|
||||
(color.GetSNES() & 0x7C00);
|
||||
|
||||
// Write the 16-bit color value to the ROM at the specified address
|
||||
WriteShort(address, bgr);
|
||||
return WriteShort(address, bgr);
|
||||
}
|
||||
|
||||
void Expand(int size) {
|
||||
@@ -298,8 +380,6 @@ class ROM {
|
||||
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) {
|
||||
return palette_groups_[group];
|
||||
}
|
||||
|
||||
@@ -212,23 +212,27 @@ absl::Status Overworld::SaveOverworldMaps() {
|
||||
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<uint8_t>(snes_pos & 0xFF));
|
||||
rom()->Write(kCompressedAllMap32PointersLow + 1 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 8) & 0xFF));
|
||||
rom()->Write(kCompressedAllMap32PointersLow + 2 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 16) & 0xFF));
|
||||
rom()->WriteVector(pos, a);
|
||||
|
||||
RETURN_IF_ERROR(rom()->RunTransaction(
|
||||
WriteAction{kCompressedAllMap32PointersLow + 0 + 3 * i,
|
||||
uint8_t(snes_pos & 0xFF)},
|
||||
WriteAction{kCompressedAllMap32PointersLow + 1 + 3 * i,
|
||||
uint8_t((snes_pos >> 8) & 0xFF)},
|
||||
WriteAction{kCompressedAllMap32PointersLow + 2 + 3 * i,
|
||||
uint8_t((snes_pos >> 16) & 0xFF)},
|
||||
WriteAction{pos, std::vector<uint8_t>(a)}))
|
||||
|
||||
pos += a.size();
|
||||
} else {
|
||||
// Save pointer for map1
|
||||
int snes_pos = map_pointers1[map_pointers1_id[i]];
|
||||
rom()->Write(kCompressedAllMap32PointersLow + 0 + 3 * i,
|
||||
static_cast<uint8_t>(snes_pos & 0xFF));
|
||||
rom()->Write(kCompressedAllMap32PointersLow + 1 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 8) & 0xFF));
|
||||
rom()->Write(kCompressedAllMap32PointersLow + 2 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 16) & 0xFF));
|
||||
RETURN_IF_ERROR(rom()->RunTransaction(
|
||||
WriteAction{kCompressedAllMap32PointersLow + 0 + 3 * i,
|
||||
uint8_t(snes_pos & 0xFF)},
|
||||
WriteAction{kCompressedAllMap32PointersLow + 1 + 3 * i,
|
||||
uint8_t((snes_pos >> 8) & 0xFF)},
|
||||
WriteAction{kCompressedAllMap32PointersLow + 2 + 3 * i,
|
||||
uint8_t((snes_pos >> 16) & 0xFF)}))
|
||||
}
|
||||
|
||||
if (map_pointers2_id[i] == -1) {
|
||||
@@ -236,23 +240,25 @@ absl::Status Overworld::SaveOverworldMaps() {
|
||||
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<uint8_t>(snes_pos & 0xFF));
|
||||
rom()->Write(kCompressedAllMap32PointersHigh + 1 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 8) & 0xFF));
|
||||
rom()->Write(kCompressedAllMap32PointersHigh + 2 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 16) & 0xFF));
|
||||
rom()->WriteVector(pos, b);
|
||||
RETURN_IF_ERROR(rom()->RunTransaction(
|
||||
WriteAction{kCompressedAllMap32PointersHigh + 0 + 3 * i,
|
||||
static_cast<uint8_t>(snes_pos & 0xFF)},
|
||||
WriteAction{kCompressedAllMap32PointersHigh + 1 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 8) & 0xFF)},
|
||||
WriteAction{kCompressedAllMap32PointersHigh + 2 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 16) & 0xFF)},
|
||||
WriteAction{pos, std::vector<uint8_t>(b)}))
|
||||
pos += b.size();
|
||||
} else {
|
||||
// Save pointer for map2
|
||||
int snes_pos = map_pointers2[map_pointers2_id[i]];
|
||||
rom()->Write(kCompressedAllMap32PointersHigh + 0 + 3 * i,
|
||||
static_cast<uint8_t>(snes_pos & 0xFF));
|
||||
rom()->Write(kCompressedAllMap32PointersHigh + 1 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 8) & 0xFF));
|
||||
rom()->Write(kCompressedAllMap32PointersHigh + 2 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 16) & 0xFF));
|
||||
RETURN_IF_ERROR(rom()->RunTransaction(
|
||||
WriteAction{kCompressedAllMap32PointersHigh + 0 + 3 * i,
|
||||
static_cast<uint8_t>(snes_pos & 0xFF)},
|
||||
WriteAction{kCompressedAllMap32PointersHigh + 1 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 8) & 0xFF)},
|
||||
WriteAction{kCompressedAllMap32PointersHigh + 2 + 3 * i,
|
||||
static_cast<uint8_t>((snes_pos >> 16) & 0xFF)}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,18 +549,24 @@ bool Overworld::CreateTile32Tilemap(bool only_show) {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void Overworld::SaveMap16Tiles() {
|
||||
absl::Status Overworld::SaveMap16Tiles() {
|
||||
int tpos = kMap16Tiles;
|
||||
// 3760
|
||||
for (int i = 0; i < NumberOfMap16; i += 1) {
|
||||
rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile0_));
|
||||
tpos += 2;
|
||||
rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile1_));
|
||||
tpos += 2;
|
||||
rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile2_));
|
||||
tpos += 2;
|
||||
rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile3_));
|
||||
tpos += 2;
|
||||
RETURN_IF_ERROR(rom()->RunTransaction(
|
||||
WriteAction{tpos, uint16_t(TileInfoToShort(tiles16[i].tile0_))},
|
||||
WriteAction{tpos += 2, uint16_t(TileInfoToShort(tiles16[i].tile1_))},
|
||||
WriteAction{tpos += 2, uint16_t(TileInfoToShort(tiles16[i].tile2_))},
|
||||
WriteAction{tpos += 2, uint16_t(TileInfoToShort(tiles16[i].tile3_))}));
|
||||
|
||||
// rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile0_));
|
||||
// tpos += 2;
|
||||
// rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile1_));
|
||||
// tpos += 2;
|
||||
// rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile2_));
|
||||
// tpos += 2;
|
||||
// rom()->WriteShort(tpos, TileInfoToShort(tiles16[i].tile3_));
|
||||
// tpos += 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,8 +634,8 @@ void Overworld::AssembleMap32Tiles() {
|
||||
for (int i = 0; i < 0x33F0; i += 6) {
|
||||
// Loop through each quadrant of the 32x32 pixel tile.
|
||||
for (int k = 0; k < 4; k++) {
|
||||
// Generate the 16-bit tile for the current quadrant of the current 32x32
|
||||
// pixel tile.
|
||||
// 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);
|
||||
@@ -634,8 +646,8 @@ void Overworld::AssembleMap32Tiles() {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the light_world, dark_world, and special_world vectors with the
|
||||
// appropriate number of tiles.
|
||||
// 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);
|
||||
|
||||
@@ -186,7 +186,7 @@ class Overworld : public SharedROM {
|
||||
absl::Status SaveLargeMaps();
|
||||
|
||||
bool CreateTile32Tilemap(bool onlyShow = false);
|
||||
void SaveMap16Tiles();
|
||||
absl::Status SaveMap16Tiles();
|
||||
absl::Status SaveMap32Tiles();
|
||||
|
||||
auto GetTiles16() const { return tiles16; }
|
||||
|
||||
@@ -21,20 +21,58 @@ absl::Status Tile16Transfer::handle(const std::vector<std::string>& arg_vec) {
|
||||
ROM dest_rom;
|
||||
RETURN_IF_ERROR(dest_rom.LoadFromFile(arg_vec[1]))
|
||||
|
||||
std::vector<uint32_t> tileIDs;
|
||||
|
||||
// Parse the CSV list of tile16 IDs.
|
||||
std::stringstream ss(arg_vec[2].data());
|
||||
for (std::string tile16_id; std::getline(ss, tile16_id, ',');) {
|
||||
std::cout << "Writing tile16 ID " << tile16_id << " to dest rom."
|
||||
<< std::endl;
|
||||
for (std::string tileID; std::getline(ss, tileID, ',');) {
|
||||
if (tileID == "*") {
|
||||
// for (uint32_t i = 0; i <= rom_.GetMaxTileID(); ++i) {
|
||||
// tileIDs.push_back(i);
|
||||
// }
|
||||
break; // No need to continue parsing if * is used
|
||||
} else if (tileID.find('-') != std::string::npos) {
|
||||
// Handle range: split by hyphen and add all tile IDs in the range.
|
||||
std::stringstream rangeSS(tileID);
|
||||
std::string start;
|
||||
std::string end;
|
||||
std::getline(rangeSS, start, '-');
|
||||
std::getline(rangeSS, end);
|
||||
uint32_t startID = std::stoi(start, nullptr, 16);
|
||||
uint32_t endID = std::stoi(end, nullptr, 16);
|
||||
for (uint32_t i = startID; i <= endID; ++i) {
|
||||
tileIDs.push_back(i);
|
||||
}
|
||||
} else {
|
||||
// Handle single tile ID
|
||||
uint32_t tileID_int = std::stoi(tileID, nullptr, 16);
|
||||
tileIDs.push_back(tileID_int);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the string to a base16 integer.
|
||||
uint32_t tile16_id_int = std::stoi(tile16_id, nullptr, /*base=*/16);
|
||||
for (const auto& tile16_id_int : tileIDs) {
|
||||
// Compare the tile16 data between source and destination ROMs.
|
||||
// auto source_tile16_data = rom_.ReadTile16(tile16_id_int);
|
||||
// auto dest_tile16_data = dest_rom.ReadTile16(tile16_id_int);
|
||||
ASSIGN_OR_RETURN(auto source_tile16_data, rom_.ReadTile16(tile16_id_int))
|
||||
ASSIGN_OR_RETURN(auto dest_tile16_data, dest_rom.ReadTile16(tile16_id_int))
|
||||
if (source_tile16_data != dest_tile16_data) {
|
||||
// Notify user of difference
|
||||
std::cout << "Difference detected in tile16 ID " << tile16_id_int
|
||||
<< ". Do you want to transfer it to dest rom? (y/n): ";
|
||||
char userChoice;
|
||||
std::cin >> userChoice;
|
||||
|
||||
// Read the tile16 definition from the source ROM.
|
||||
auto tile16_data = rom_.ReadTile16(tile16_id_int);
|
||||
|
||||
// Write the tile16 definition to the destination ROM.
|
||||
dest_rom.WriteTile16(tile16_id_int, tile16_data);
|
||||
// Transfer if user confirms
|
||||
if (userChoice == 'y' || userChoice == 'Y') {
|
||||
dest_rom.WriteTile16(tile16_id_int, source_tile16_data);
|
||||
std::cout << "Transferred tile16 ID " << tile16_id_int
|
||||
<< " to dest rom." << std::endl;
|
||||
} else {
|
||||
std::cout << "Skipped transferring tile16 ID " << tile16_id_int << "."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(dest_rom.SaveToFile(/*backup=*/true, arg_vec[1]))
|
||||
|
||||
@@ -230,14 +230,18 @@ class ReadFromRom : public CommandHandler {
|
||||
}
|
||||
|
||||
if (length > 1) {
|
||||
auto returned_bytes = rom_.ReadByteVector(offset, length);
|
||||
auto returned_bytes_status = rom_.ReadByteVector(offset, length);
|
||||
if (!returned_bytes_status.ok()) {
|
||||
return returned_bytes_status.status();
|
||||
}
|
||||
auto returned_bytes = returned_bytes_status.value();
|
||||
for (const auto& each : returned_bytes) {
|
||||
std::cout << each;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
} else {
|
||||
auto byte = rom_.ReadByte(offset);
|
||||
std::cout << std::hex << byte << std::endl;
|
||||
std::cout << std::hex << byte.value() << std::endl;
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
|
||||
Reference in New Issue
Block a user