backend-infra-engineer: Pre-0.2.2 2024 Q2 snapshot

This commit is contained in:
scawful
2024-04-14 15:49:57 -05:00
parent 546093360f
commit 92cc574e15
113 changed files with 5631 additions and 1872 deletions

View File

@@ -325,54 +325,76 @@ void Bitmap::LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
}
// Convert SNESPalette to SDL_Palette for surface.
void Bitmap::ApplyPalette(const SnesPalette &palette) {
absl::Status Bitmap::ApplyPalette(const SnesPalette &palette) {
if (surface_ == nullptr) {
return absl::FailedPreconditionError("Surface is null");
}
if (surface_->format == nullptr || surface_->format->palette == nullptr) {
return absl::FailedPreconditionError("Surface format or palette is null");
}
palette_ = palette;
SDL_Palette *sdlPalette = surface_->format->palette;
if (sdlPalette == nullptr) {
return absl::InternalError("Failed to get SDL palette");
}
SDL_UnlockSurface(surface_.get());
for (int i = 0; i < palette.size(); ++i) {
if (palette.GetColor(i).is_transparent()) {
surface_->format->palette->colors[i].r = 0;
surface_->format->palette->colors[i].g = 0;
surface_->format->palette->colors[i].b = 0;
surface_->format->palette->colors[i].a = 0;
ASSIGN_OR_RETURN(gfx::SnesColor pal_color, palette.GetColor(i));
if (pal_color.is_transparent()) {
sdlPalette->colors[i].r = 0;
sdlPalette->colors[i].g = 0;
sdlPalette->colors[i].b = 0;
sdlPalette->colors[i].a = 0;
} else {
surface_->format->palette->colors[i].r = palette.GetColor(i).rgb().x;
surface_->format->palette->colors[i].g = palette.GetColor(i).rgb().y;
surface_->format->palette->colors[i].b = palette.GetColor(i).rgb().z;
surface_->format->palette->colors[i].a = palette.GetColor(i).rgb().w;
sdlPalette->colors[i].r = pal_color.rgb().x;
sdlPalette->colors[i].g = pal_color.rgb().y;
sdlPalette->colors[i].b = pal_color.rgb().z;
sdlPalette->colors[i].a = pal_color.rgb().w;
}
}
SDL_LockSurface(surface_.get());
return absl::OkStatus();
}
void Bitmap::ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
int palette_id) {
absl::Status Bitmap::ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
int palette_id) {
auto start_index = palette_id * 8;
palette_ = palette.sub_palette(start_index, start_index + 8);
SDL_UnlockSurface(surface_.get());
for (int i = 0; i < palette_.size(); ++i) {
if (palette_.GetColor(i).is_transparent()) {
ASSIGN_OR_RETURN(auto pal_color, palette_.GetColor(i));
if (pal_color.is_transparent()) {
surface_->format->palette->colors[i].r = 0;
surface_->format->palette->colors[i].g = 0;
surface_->format->palette->colors[i].b = 0;
surface_->format->palette->colors[i].a = 0;
} else {
surface_->format->palette->colors[i].r = palette_.GetColor(i).rgb().x;
surface_->format->palette->colors[i].g = palette_.GetColor(i).rgb().y;
surface_->format->palette->colors[i].b = palette_.GetColor(i).rgb().z;
surface_->format->palette->colors[i].a = palette_.GetColor(i).rgb().w;
surface_->format->palette->colors[i].r = pal_color.rgb().x;
surface_->format->palette->colors[i].g = pal_color.rgb().y;
surface_->format->palette->colors[i].b = pal_color.rgb().z;
surface_->format->palette->colors[i].a = pal_color.rgb().w;
}
}
SDL_LockSurface(surface_.get());
return absl::OkStatus();
}
void Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette, int index,
int length) {
absl::Status Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette,
int index, int length) {
auto start_index = index * 7;
palette_ = palette.sub_palette(start_index, start_index + 7);
std::vector<ImVec4> colors;
colors.push_back(ImVec4(0, 0, 0, 0));
for (int i = start_index; i < start_index + 7; ++i) {
colors.push_back(palette.GetColor(i).rgb());
ASSIGN_OR_RETURN(auto pal_color, palette.GetColor(i));
colors.push_back(pal_color.rgb());
}
SDL_UnlockSurface(surface_.get());
@@ -385,6 +407,7 @@ void Bitmap::ApplyPaletteWithTransparent(const SnesPalette &palette, int index,
i++;
}
SDL_LockSurface(surface_.get());
return absl::OkStatus();
}
void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {

View File

@@ -15,11 +15,32 @@
namespace yaze {
namespace app {
/**
* @namespace yaze::app::gfx
* @brief Contains classes for handling graphical data.
*/
namespace gfx {
/**
* @brief Convert SDL_Surface to PNG image data.
*/
bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer);
/**
* @brief Convert PNG image data to SDL_Surface.
*/
void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
SDL_Surface **outSurface);
/**
* @brief Represents a bitmap image.
*
* The `Bitmap` class provides functionality to create, manipulate, and display
* bitmap images. It supports various operations such as creating a bitmap
* object, creating and updating textures, applying palettes, and accessing
* pixel data.
*/
class Bitmap {
public:
Bitmap() = default;
@@ -29,14 +50,41 @@ class Bitmap {
: width_(width), height_(height), depth_(depth), data_(data) {
InitializeFromData(width, height, depth, data);
}
Bitmap(int width, int height, int depth, const Bytes &data,
const SnesPalette &palette)
: width_(width),
height_(height),
depth_(depth),
data_(data),
palette_(palette) {
InitializeFromData(width, height, depth, data);
ApplyPalette(palette);
}
/**
* @brief Creates a bitmap object and reserves space for graphical data.
*/
void Create(int width, int height, int depth, int data_size);
/**
* @brief Creates a bitmap object with the provided graphical data.
*/
void Create(int width, int height, int depth, const Bytes &data);
void InitializeFromData(uint32_t width, uint32_t height, uint32_t depth,
const Bytes &data);
/**
* @brief Creates the underlying SDL_Texture to be displayed.
*
* Converts the surface from a RGB to ARGB format.
* Uses SDL_TEXTUREACCESS_STREAMING to allow for live updates.
*/
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
/**
* @brief Updates the underlying SDL_Texture when it already exists.
*/
void UpdateTexture(std::shared_ptr<SDL_Renderer> renderer);
void CreateTexture(SDL_Renderer *renderer);
void UpdateTexture(SDL_Renderer *renderer, bool use_sdl_update = false);
@@ -47,11 +95,15 @@ class Bitmap {
void LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
int height);
void ApplyPalette(const SnesPalette &palette);
void ApplyPaletteWithTransparent(const SnesPalette &palette, int index,
int length = 7);
/**
* @brief Copy color data from the SnesPalette into the SDL_Palette
*/
absl::Status ApplyPalette(const SnesPalette &palette);
absl::Status ApplyPaletteWithTransparent(const SnesPalette &palette,
int index, int length = 7);
void ApplyPalette(const std::vector<SDL_Color> &palette);
void ApplyPaletteFromPaletteGroup(const SnesPalette &palette, int palette_id);
absl::Status ApplyPaletteFromPaletteGroup(const SnesPalette &palette,
int palette_id);
void WriteToPixel(int position, uchar value) {
if (pixel_data_ == nullptr) {
@@ -236,6 +288,9 @@ class Bitmap {
using BitmapTable = std::unordered_map<int, gfx::Bitmap>;
/**
* @brief Hash map container of shared pointers to Bitmaps.
*/
class BitmapManager {
private:
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>> bitmap_cache_;

View File

@@ -474,7 +474,7 @@ Bytes CreateCompressionString(CompressionPiecePointer& start, int mode) {
absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head,
int mode, int start, int src_data_pos) {
if (chain_head->next != nullptr) {
ROM temp_rom;
Rom temp_rom;
RETURN_IF_ERROR(
temp_rom.LoadFromBytes(CreateCompressionString(chain_head->next, mode)))
ASSIGN_OR_RETURN(auto decomp_data,
@@ -1173,7 +1173,7 @@ void AddCompressionToChain(CompressionContext& context) {
absl::Status ValidateCompressionResultV3(const CompressionContext& context) {
if (!context.compressed_data.empty()) {
ROM temp_rom;
Rom temp_rom;
RETURN_IF_ERROR(temp_rom.LoadFromBytes(context.compressed_data));
ASSIGN_OR_RETURN(auto decomp_data,
DecompressV2(temp_rom.data(), 0, temp_rom.size()))

View File

@@ -15,6 +15,12 @@ namespace yaze {
namespace app {
namespace gfx {
/**
* @namespace yaze::app::gfx::lc_lz2
* @brief Contains the LC_LZ2 compression algorithm.
*/
namespace lc_lz2 {
const int D_NINTENDO_C_MODE1 = 0;
const int D_NINTENDO_C_MODE2 = 1;
@@ -29,15 +35,6 @@ const int D_MAX_LENGTH = 1024;
const int INITIAL_ALLOC_SIZE = 1024;
namespace lc_lz2 {
absl::StatusOr<Bytes> ZS_Compress(const std::vector<uint8_t>& data,
const int start, const int length,
int mode = 1, bool check = false);
absl::StatusOr<Bytes> ZS_CompressOverworld(const std::vector<uint8_t> data,
const int pos, const int length);
constexpr int kCommandDirectCopy = 0;
constexpr int kCommandByteFill = 1;
constexpr int kCommandWordFill = 2;
@@ -141,6 +138,10 @@ void CompressionCommandAlternativeV2(const uchar* data,
uint& src_pos, uint& comp_accumulator,
uint& cmd_with_max, uint& max_win);
/**
* @brief Compresses a buffer of data using the LC_LZ2 algorithm.
* \deprecated Use Compress and Uncompress instead.
*/
absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
const int length, int mode = 1,
bool check = false);
@@ -208,6 +209,10 @@ absl::StatusOr<CompressionPiece> SplitCompressionPieceV3(
CompressionPiece& piece, int mode);
void FinalizeCompression(CompressionContext& context);
/**
* @brief Compresses a buffer of data using the LC_LZ2 algorithm.
* \deprecated Use Compress and Uncompress instead.
*/
absl::StatusOr<Bytes> CompressV3(const std::vector<uint8_t>& data,
const int start, const int length,
int mode = 1, bool check = false);
@@ -227,6 +232,10 @@ std::string SetBuffer(const uchar* data, int src_pos, int comp_accumulator);
void memfill(const uchar* data, Bytes& buffer, int buffer_pos, int offset,
int length);
/**
* @brief Decompresses a buffer of data using the LC_LZ2 algorithm.
* \deprecated Use Compress and Uncompress instead.
*/
absl::StatusOr<Bytes> DecompressV2(const uchar* data, int offset,
int size = 0x800, int mode = 1);
absl::StatusOr<Bytes> DecompressGraphics(const uchar* data, int pos, int size);

View File

@@ -15,6 +15,7 @@
namespace yaze {
namespace app {
namespace gfx {
namespace scad_format {
void FindMetastamp() {
int matching_position = -1;
@@ -276,6 +277,7 @@ absl::Status DecodeObjFile(
return absl::OkStatus();
}
} // namespace scad_format
} // namespace gfx
} // namespace app
} // namespace yaze
} // namespace yaze

View File

@@ -22,18 +22,27 @@ namespace yaze {
namespace app {
namespace gfx {
// キャラクタ(.SCH)ファイル
// ヘッダー情報
// アドレス 説明
// 00000 - 00003 ファイルタイプ "SCH"
// 00004 - 00008 ビットモード "?BIT"
// 00009 - 00013 バージョンナンバー "Ver-????\n"
// 00014 - 00017 ヘッダーサイズ
// 00018 - 0001B ハード名 "SFC" or "CGB" or "GB"
// 0001C - 0001C BG/OBJフラグ(AGBの時)
// 0001D - 0001D Color Pallette Number
// 0001D - 000FF 予約
// 00100 - 001FF Color Path
/**
* @namespace yaze::app::gfx::scad_format
* @brief Loading from prototype SCAD format
*/
namespace scad_format {
/**
* @brief Cgx file header
* キャラクタ(.SCH)ファイル
* ヘッダー情報
* アドレス 説明
* 00000 - 00003 ファイルタイプ "SCH"
* 00004 - 00008 ビットモード "?BIT"
* 00009 - 00013 バージョンナンバー "Ver-????\n"
* 00014 - 00017 ヘッダーサイズ
* 00018 - 0001B ハード名 "SFC" or "CGB" or "GB"
* 0001C - 0001C BG/OBJフラグ(AGBの時)
* 0001D - 0001D Color Pallette Number
* 0001D - 000FF 予約
* 00100 - 001FF Color Path
*/
struct CgxHeader {
char file_type[4];
char bit_mode[5];
@@ -49,28 +58,47 @@ struct CgxHeader {
constexpr uint16_t kMatchedBytes[] = {0x4E, 0x41, 0x4B, 0x31, 0x39, 0x38, 0x39};
constexpr uint16_t kOffsetFromMatchedBytesEnd = 0x1D;
/**
* @brief Find metastamp in CGX file
*/
void FindMetastamp();
/**
* @brief Load Scr file (screen data)
*/
absl::Status LoadScr(std::string_view filename, uint8_t input_value,
std::vector<uint8_t>& map_data);
/**
* @brief Load Cgx file (graphical content)
*/
absl::Status LoadCgx(uint8_t bpp, std::string_view filename,
std::vector<uint8_t>& cgx_data,
std::vector<uint8_t>& cgx_loaded,
std::vector<uint8_t>& cgx_header);
/**
* @brief Draw screen tilemap with graphical data
*/
absl::Status DrawScrWithCgx(uint8_t bpp, std::vector<uint8_t>& map_bitmap_data,
std::vector<uint8_t>& map_data,
std::vector<uint8_t>& cgx_loaded);
/**
* @brief Decode color file
*/
std::vector<SDL_Color> DecodeColFile(const std::string_view filename);
/**
* @brief Decode obj file
*/
absl::Status DecodeObjFile(
std::string_view filename, std::vector<uint8_t>& obj_data,
std::vector<uint8_t> actual_obj_data,
std::unordered_map<std::string, std::vector<uint8_t>> decoded_obj,
std::vector<uint8_t>& decoded_extra_obj, int& obj_loaded);
} // namespace scad_format
} // namespace gfx
} // namespace app
} // namespace yaze

View File

@@ -10,6 +10,9 @@ namespace yaze {
namespace app {
namespace gfx {
/**
* @brief Primitive of 16-bit RGB SNES color.
*/
struct snes_color {
uint16_t red; /**< Red component of the color. */
uint16_t blue; /**< Blue component of the color. */
@@ -26,6 +29,16 @@ std::vector<snes_color> Extract(const char* data, unsigned int offset,
std::vector<char> Convert(const std::vector<snes_color>& palette);
/**
* @brief SNES Color container
*
* Used for displaying the color to the screen and writing
* the color to the Rom file in the correct format.
*
* SNES colors may be represented in one of three formats:
* - Color data from the rom in a snes_color struct
* - Color data for displaying to the UI via ImVec4
*/
class SnesColor {
public:
SnesColor() : rgb_(0.f, 0.f, 0.f, 0.f), snes_(0) {}
@@ -53,6 +66,7 @@ class SnesColor {
}
ImVec4 rgb() const { return rgb_; }
void set_rgb(const ImVec4 val) {
rgb_.x = val.x / 255;
rgb_.y = val.y / 255;
@@ -65,6 +79,7 @@ class SnesColor {
snes_ = ConvertRGBtoSNES(color);
modified = true;
}
void set_snes(uint16_t val) {
snes_ = val;
snes_color col = ConvertSNEStoRGB(val);

View File

@@ -20,6 +20,169 @@ namespace yaze {
namespace app {
namespace gfx {
/**
* @namespace yaze::app::gfx::palette_group_internal
* @brief Internal functions for loading palettes by group.
*/
namespace palette_group_internal {
absl::Status LoadOverworldMainPalettes(const Bytes& rom_data,
gfx::PaletteGroupMap& palette_groups) {
auto data = rom_data.data();
for (int i = 0; i < 6; i++) {
RETURN_IF_ERROR(palette_groups.overworld_main.AddPalette(
gfx::ReadPaletteFromRom(core::overworldPaletteMain + (i * (35 * 2)),
/*num_colors*/ 35, data)))
}
return absl::OkStatus();
}
absl::Status LoadOverworldAuxiliaryPalettes(
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
auto data = rom_data.data();
for (int i = 0; i < 20; i++) {
RETURN_IF_ERROR(
palette_groups.overworld_aux.AddPalette(gfx::ReadPaletteFromRom(
core::overworldPaletteAuxialiary + (i * (21 * 2)),
/*num_colors*/ 21, data)))
}
return absl::OkStatus();
}
absl::Status LoadOverworldAnimatedPalettes(
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
auto data = rom_data.data();
for (int i = 0; i < 14; i++) {
RETURN_IF_ERROR(
palette_groups.overworld_animated.AddPalette(gfx::ReadPaletteFromRom(
core::overworldPaletteAnimated + (i * (7 * 2)), 7, data)))
}
return absl::OkStatus();
}
absl::Status LoadHUDPalettes(const Bytes& rom_data,
gfx::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,
gfx::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,
gfx::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,
gfx::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,
gfx::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,
gfx::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,
gfx::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,
gfx::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,
gfx::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,
gfx::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,
gfx::PaletteGroupMap& palette_groups) {
auto data = rom_data.data();
RETURN_IF_ERROR(palette_groups.object_3d.AddPalette(
gfx::ReadPaletteFromRom(core::triforcePalette, 8, data)))
RETURN_IF_ERROR(palette_groups.object_3d.AddPalette(
gfx::ReadPaletteFromRom(core::crystalPalette, 8, data)))
return absl::OkStatus();
}
absl::Status LoadOverworldMiniMapPalettes(
const Bytes& rom_data, gfx::PaletteGroupMap& palette_groups) {
auto data = rom_data.data();
for (int i = 0; i < 2; i++) {
RETURN_IF_ERROR(
palette_groups.overworld_mini_map.AddPalette(gfx::ReadPaletteFromRom(
core::overworldMiniMapPalettes + (i * 256), 128, data)))
}
return absl::OkStatus();
}
} // namespace palette_group_internal
const absl::flat_hash_map<std::string, uint32_t> kPaletteGroupAddressMap = {
{"ow_main", core::overworldPaletteMain},
{"ow_aux", core::overworldPaletteAuxialiary},
@@ -194,6 +357,7 @@ absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
for (int i = 0; i < palette.size(); i += 8) {
SnesPalette new_palette;
if (i + 8 < palette.size()) {
// new_palette.AddColor(SnesColor(ImVec4(0,0,0,0)));
for (int j = 0; j < 8; j++) {
new_palette.AddColor(palette[i + j]);
}
@@ -204,6 +368,27 @@ absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
return toret;
}
using namespace palette_group_internal;
absl::Status LoadAllPalettes(const Bytes& rom_data, PaletteGroupMap& groups) {
RETURN_IF_ERROR(LoadOverworldMainPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadOverworldAuxiliaryPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadOverworldAnimatedPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadHUDPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadGlobalSpritePalettes(rom_data, groups))
RETURN_IF_ERROR(LoadArmorPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadSwordPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadShieldPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadSpriteAux1Palettes(rom_data, groups))
RETURN_IF_ERROR(LoadSpriteAux2Palettes(rom_data, groups))
RETURN_IF_ERROR(LoadSpriteAux3Palettes(rom_data, groups))
RETURN_IF_ERROR(LoadDungeonMainPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadGrassColors(rom_data, groups))
RETURN_IF_ERROR(Load3DObjectPalettes(rom_data, groups))
RETURN_IF_ERROR(LoadOverworldMiniMapPalettes(rom_data, groups))
return absl::OkStatus();
}
} // namespace gfx
} // namespace app
} // namespace yaze

View File

@@ -21,6 +21,9 @@ namespace yaze {
namespace app {
namespace gfx {
/**
* @brief Primitive of a SNES color palette.
*/
struct snes_palette {
uint id; /**< ID of the palette. */
uint size; /**< Size of the palette. */
@@ -31,6 +34,18 @@ using snes_palette = struct snes_palette;
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
size_t color_index);
/**
* @brief Represents a palette of colors for the Super Nintendo Entertainment
* System (SNES).
*
* The `SnesPalette` class provides functionality to create, modify, and access
* colors in an SNES palette. It supports various constructors to initialize the
* palette with different types of data. The palette can be modified by adding
* or changing colors, and it can be cleared to remove all colors. Colors in the
* palette can be accessed using index-based access or through the `GetColor`
* method. The class also provides a method to create a sub-palette by selecting
* a range of colors from the original palette.
*/
class SnesPalette {
public:
template <typename T>
@@ -69,10 +84,9 @@ class SnesPalette {
size_++;
}
auto GetColor(int i) const {
absl::StatusOr<SnesColor> GetColor(int i) const {
if (i > size_) {
std::cout << "SNESPalette: Index out of bounds" << std::endl;
return colors[0];
return absl::InvalidArgumentError("SnesPalette: Index out of bounds");
}
return colors[i];
}
@@ -126,13 +140,17 @@ SnesPalette ReadPaletteFromRom(int offset, int num_colors, const uint8_t* rom);
std::array<float, 4> ToFloatArray(const SnesColor& color);
/**
* @brief Represents a group of palettes.
*
* Supports adding palettes and colors, clearing the group, and accessing
* palettes and colors by index.
*/
struct PaletteGroup {
PaletteGroup() = default;
explicit PaletteGroup(uint8_t mSize);
auto mutable_palette(int i) { return &palettes[i]; }
absl::Status AddPalette(SnesPalette pal) {
palettes.emplace_back(pal);
size_ = palettes.size();
@@ -152,7 +170,10 @@ struct PaletteGroup {
size_ = 0;
}
auto name() const { return name_; }
auto size() const { return palettes.size(); }
auto mutable_palette(int i) { return &palettes[i]; }
auto palette(int i) const { return palettes[i]; }
SnesPalette operator[](int i) {
if (i > size_) {
@@ -188,17 +209,128 @@ struct PaletteGroup {
private:
int size_ = 0;
std::string name_;
std::vector<SnesPalette> palettes;
};
/**
* @brief Represents a mapping of palette groups.
*
* Originally, this was an actual std::unordered_map but since the palette
* groups supported never change, it was changed to a struct with a method to
* get the group by name.
*/
struct PaletteGroupMap {
PaletteGroup overworld_main;
PaletteGroup overworld_aux;
PaletteGroup overworld_animated;
PaletteGroup hud;
PaletteGroup global_sprites;
PaletteGroup armors;
PaletteGroup swords;
PaletteGroup shields;
PaletteGroup sprites_aux1;
PaletteGroup sprites_aux2;
PaletteGroup sprites_aux3;
PaletteGroup dungeon_main;
PaletteGroup grass;
PaletteGroup object_3d;
PaletteGroup overworld_mini_map;
auto get_group(const std::string& group_name) {
if (group_name == "ow_main") {
return &overworld_main;
} else if (group_name == "ow_aux") {
return &overworld_aux;
} else if (group_name == "ow_animated") {
return &overworld_animated;
} else if (group_name == "hud") {
return &hud;
} else if (group_name == "global_sprites") {
return &global_sprites;
} else if (group_name == "armors") {
return &armors;
} else if (group_name == "swords") {
return &swords;
} else if (group_name == "shields") {
return &shields;
} else if (group_name == "sprites_aux1") {
return &sprites_aux1;
} else if (group_name == "sprites_aux2") {
return &sprites_aux2;
} else if (group_name == "sprites_aux3") {
return &sprites_aux3;
} else if (group_name == "dungeon_main") {
return &dungeon_main;
} else if (group_name == "grass") {
return &grass;
} else if (group_name == "3d_object") {
return &object_3d;
} else if (group_name == "ow_mini_map") {
return &overworld_mini_map;
} else {
throw std::out_of_range("PaletteGroupMap: Group not found");
}
}
template <typename Func>
void for_each(Func&& func) {
func(overworld_main);
func(overworld_aux);
func(overworld_animated);
func(hud);
func(global_sprites);
func(armors);
func(swords);
func(shields);
func(sprites_aux1);
func(sprites_aux2);
func(sprites_aux3);
func(dungeon_main);
func(grass);
func(object_3d);
func(overworld_mini_map);
}
};
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromColFile(
std::vector<SnesColor>& colors);
absl::StatusOr<PaletteGroup> CreatePaletteGroupFromLargePalette(
SnesPalette& palette);
/**
* @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(const Bytes& rom_data, PaletteGroupMap& groups);
/**
* @brief Represents a set of palettes used in a SNES graphics system.
*/
struct Paletteset {
/**
* @brief Default constructor for Paletteset.
*/
Paletteset() = default;
/**
* @brief Constructor for Paletteset.
* @param main The main palette.
* @param animated The animated palette.
* @param aux1 The first auxiliary palette.
* @param aux2 The second auxiliary palette.
* @param background The background color.
* @param hud The HUD palette.
* @param spr The sprite palette.
* @param spr2 The second sprite palette.
* @param comp The composite palette.
*/
Paletteset(gfx::SnesPalette main, gfx::SnesPalette animated,
gfx::SnesPalette aux1, gfx::SnesPalette aux2,
gfx::SnesColor background, gfx::SnesPalette hud,
@@ -212,15 +344,16 @@ struct Paletteset {
spr(spr),
spr2(spr2),
composite(comp) {}
gfx::SnesPalette main;
gfx::SnesPalette animated;
gfx::SnesPalette aux1;
gfx::SnesPalette aux2;
gfx::SnesColor background;
gfx::SnesPalette hud;
gfx::SnesPalette spr;
gfx::SnesPalette spr2;
gfx::SnesPalette composite;
gfx::SnesPalette main; /**< The main palette. */
gfx::SnesPalette animated; /**< The animated palette. */
gfx::SnesPalette aux1; /**< The first auxiliary palette. */
gfx::SnesPalette aux2; /**< The second auxiliary palette. */
gfx::SnesColor background; /**< The background color. */
gfx::SnesPalette hud; /**< The HUD palette. */
gfx::SnesPalette spr; /**< The sprite palette. */
gfx::SnesPalette spr2; /**< The second sprite palette. */
gfx::SnesPalette composite; /**< The composite palette. */
};
} // namespace gfx

View File

@@ -135,7 +135,7 @@ std::vector<uint8_t> Convert4bppTo3bpp(const std::vector<uint8_t>& tiles) {
return ConvertBpp(tiles, 4, 3);
}
Bytes SnesTo8bppSheet(Bytes sheet, int bpp) {
Bytes SnesTo8bppSheet(const Bytes& sheet, int bpp) {
int xx = 0; // positions where we are at on the sheet
int yy = 0;
int pos = 0;
@@ -148,6 +148,11 @@ Bytes SnesTo8bppSheet(Bytes sheet, int bpp) {
buffer_size = 0x2000;
} else if (bpp == 3) {
bpp = 24;
} else if (bpp == 4) {
bpp = 32;
buffer_size = 0x4000;
} else if (bpp == 8) {
bpp = 64;
}
Bytes sheet_buffer_out(buffer_size);

View File

@@ -14,7 +14,7 @@ namespace gfx {
constexpr uint8_t kGraphicsBitmap[8] = {0x80, 0x40, 0x20, 0x10,
0x08, 0x04, 0x02, 0x01};
Bytes SnesTo8bppSheet(Bytes sheet, int bpp);
Bytes SnesTo8bppSheet(const Bytes& sheet, int bpp);
Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp = 0);
struct tile8 {
@@ -35,9 +35,14 @@ std::vector<uint8_t> ConvertBpp(const std::vector<uint8_t>& tiles,
std::vector<uint8_t> Convert3bppTo4bpp(const std::vector<uint8_t>& tiles);
std::vector<uint8_t> Convert4bppTo3bpp(const std::vector<uint8_t>& tiles);
// vhopppcc cccccccc
// [0, 1]
// [2, 3]
/**
* @brief SNES 16-bit tile metadata container
*
* Format:
* vhopppcc cccccccc
* [0, 1]
* [2, 3]
*/
class TileInfo {
public:
uint16_t id_;
@@ -67,6 +72,9 @@ uint16_t TileInfoToShort(TileInfo tile_info);
TileInfo GetTilesInfo(uint16_t tile);
/**
* @brief Tile composition of four 16x16 tiles.
*/
class Tile32 {
public:
uint16_t tile0_;
@@ -113,6 +121,9 @@ class Tile32 {
bool operator!=(const Tile32& other) const { return !(*this == other); }
};
/**
* @brief Tile composition of four 8x8 tiles.
*/
class Tile16 {
public:
TileInfo tile0_;
@@ -138,7 +149,10 @@ class Tile16 {
bool operator!=(const Tile16& other) const { return !(*this == other); }
};
class OAMTile {
/**
* @brief Object Attribute Memory tile abstraction container
*/
class OamTile {
public:
int x_;
int y_;
@@ -146,8 +160,8 @@ class OAMTile {
int my_;
int pal_;
uint16_t tile_;
OAMTile() = default;
OAMTile(int x, int y, uint16_t tile, int pal, bool upper = false, int mx = 0,
OamTile() = default;
OamTile(int x, int y, uint16_t tile, int pal, bool upper = false, int mx = 0,
int my = 0)
: x_(x), y_(y), mx_(mx), my_(my), pal_(pal) {
if (upper) {

View File

@@ -0,0 +1,114 @@
#include "app/gfx/tilesheet.h"
#include <memory>
#include <vector>
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_color.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
namespace yaze {
namespace app {
namespace gfx {
absl::StatusOr<Tilesheet> CreateTilesheetFromGraphicsBuffer(
const uint8_t* graphics_buffer, int width, int height, TileType tile_type,
int sheet_id) {
Tilesheet tilesheet;
// Calculate the offset in the graphics buffer based on the sheet ID
int sheet_offset = sheet_id * width * height;
// Initialize the tilesheet with the specified width, height, and tile type
tilesheet.Init(width, height, tile_type);
// Iterate over the tiles in the sheet and copy them into the tilesheet
for (int row = 0; row < height; ++row) {
for (int col = 0; col < width; ++col) {
// Calculate the index of the current tile in the graphics buffer
int tile_index = sheet_offset + (row * width + col) * 64;
// Copy the tile data into the tilesheet
for (int y = 0; y < 8; ++y) {
for (int x = 0; x < 8; ++x) {
int srcIndex = tile_index + (y * 8 + x);
int destX = col * 8 + x;
int destY = row * 8 + y;
int destIndex = (destY * width * 8) + destX;
tilesheet.mutable_bitmap()->mutable_data()[destIndex] =
graphics_buffer[srcIndex];
}
}
}
}
return tilesheet;
}
void Tilesheet::Init(int width, int height, TileType tile_type) {
bitmap_ = std::make_shared<Bitmap>(width, height, 8, 0x20000);
internal_data_.resize(0x20000);
tile_type_ = tile_type;
if (tile_type_ == TileType::Tile8) {
tile_width_ = 8;
tile_height_ = 8;
} else {
tile_width_ = 16;
tile_height_ = 16;
}
}
void Tilesheet::ComposeTile16(const std::vector<uint8_t>& graphics_buffer,
const TileInfo& top_left,
const TileInfo& top_right,
const TileInfo& bottom_left,
const TileInfo& bottom_right) {
// Calculate the base position for this Tile16 in the full-size bitmap
int tiles_per_row = bitmap_->width() / tile_width_;
int tile16_row = num_tiles_ / tiles_per_row;
int tile16_column = num_tiles_ % tiles_per_row;
int baseX = tile16_column * tile_width_;
int baseY = tile16_row * tile_height_;
// Compose and place each part of the Tile16
ComposeAndPlaceTilePart(graphics_buffer, top_left, baseX, baseY);
ComposeAndPlaceTilePart(graphics_buffer, top_right, baseX + 8, baseY);
ComposeAndPlaceTilePart(graphics_buffer, bottom_left, baseX, baseY + 8);
ComposeAndPlaceTilePart(graphics_buffer, bottom_right, baseX + 8, baseY + 8);
tile_info_.push_back({top_left, top_right, bottom_left, bottom_right});
num_tiles_++;
}
void Tilesheet::ComposeAndPlaceTilePart(
const std::vector<uint8_t>& graphics_buffer, const TileInfo& tile_info,
int baseX, int baseY) {
std::vector<uint8_t> tile_data =
FetchTileDataFromGraphicsBuffer(graphics_buffer, tile_info.id_);
if (tile_info.vertical_mirror_) {
MirrorTileDataVertically(tile_data);
}
if (tile_info.horizontal_mirror_) {
MirrorTileDataHorizontally(tile_data);
}
// Place the tile data into the full-size bitmap at the calculated position
for (int y = 0; y < 8; ++y) {
for (int x = 0; x < 8; ++x) {
int srcIndex = y * 8 + x;
int destX = baseX + x;
int destY = baseY + y;
int destIndex = (destY * bitmap_->width()) + destX;
internal_data_[destIndex] = tile_data[srcIndex];
}
}
bitmap_->set_data(internal_data_);
}
} // namespace gfx
} // namespace app
} // namespace yaze

View File

@@ -1,10 +1,13 @@
#ifndef YAZE_APP_GFX_TILESHEET_H
#define YAZE_APP_GFX_TILESHEET_H
#include <memory>
#include <vector>
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_color.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
namespace yaze {
namespace app {
@@ -12,6 +15,14 @@ namespace gfx {
enum class TileType { Tile8, Tile16 };
/**
* @class Tilesheet
* @brief Represents a tilesheet, which is a collection of tiles stored in a
* bitmap.
*
* The Tilesheet class provides methods to manipulate and extract tiles from the
* tilesheet. It also supports copying and mirroring tiles within the tilesheet.
*/
class Tilesheet {
public:
Tilesheet() = default;
@@ -22,68 +33,14 @@ class Tilesheet {
tile_height_(tileHeight),
tile_type_(tile_type) {}
void Init(int width, int height, TileType tile_type) {
bitmap_ = std::make_shared<Bitmap>(width, height, 8, 0x20000);
internal_data_.resize(0x20000);
tile_type_ = tile_type;
if (tile_type_ == TileType::Tile8) {
tile_width_ = 8;
tile_height_ = 8;
} else {
tile_width_ = 16;
tile_height_ = 16;
}
}
void Init(int width, int height, TileType tile_type);
void ComposeTile16(const std::vector<uint8_t>& graphics_buffer,
const TileInfo& top_left, const TileInfo& top_right,
const TileInfo& bottom_left,
const TileInfo& bottom_right) {
// Calculate the base position for this Tile16 in the full-size bitmap
int tiles_per_row = bitmap_->width() / tile_width_;
int tile16_row = num_tiles_ / tiles_per_row;
int tile16_column = num_tiles_ % tiles_per_row;
int baseX = tile16_column * tile_width_;
int baseY = tile16_row * tile_height_;
// Compose and place each part of the Tile16
ComposeAndPlaceTilePart(graphics_buffer, top_left, baseX, baseY);
ComposeAndPlaceTilePart(graphics_buffer, top_right, baseX + 8, baseY);
ComposeAndPlaceTilePart(graphics_buffer, bottom_left, baseX, baseY + 8);
ComposeAndPlaceTilePart(graphics_buffer, bottom_right, baseX + 8,
baseY + 8);
tile_info_.push_back({top_left, top_right, bottom_left, bottom_right});
num_tiles_++;
}
const TileInfo& bottom_left, const TileInfo& bottom_right);
void ComposeAndPlaceTilePart(const std::vector<uint8_t>& graphics_buffer,
const TileInfo& tile_info, int baseX,
int baseY) {
std::vector<uint8_t> tile_data =
FetchTileDataFromGraphicsBuffer(graphics_buffer, tile_info.id_);
if (tile_info.vertical_mirror_) {
MirrorTileDataVertically(tile_data);
}
if (tile_info.horizontal_mirror_) {
MirrorTileDataHorizontally(tile_data);
}
// Place the tile data into the full-size bitmap at the calculated position
for (int y = 0; y < 8; ++y) {
for (int x = 0; x < 8; ++x) {
int srcIndex = y * 8 + x;
int destX = baseX + x;
int destY = baseY + y;
int destIndex = (destY * bitmap_->width()) + destX;
internal_data_[destIndex] = tile_data[srcIndex];
}
}
bitmap_->set_data(internal_data_);
}
const TileInfo& tile_info, int baseX, int baseY);
// Extracts a tile from the tilesheet
Bitmap GetTile(int tileX, int tileY, int bmp_width, int bmp_height) {
@@ -230,6 +187,10 @@ class Tilesheet {
TileType tile_type_;
};
absl::StatusOr<Tilesheet> CreateTilesheetFromGraphicsBuffer(
const uint8_t* graphics_buffer, int width, int height, TileType tile_type,
int sheet_id);
} // namespace gfx
} // namespace app
} // namespace yaze