backend-infra-engineer: Pre-0.2.2 snapshot (2023)

This commit is contained in:
scawful
2023-12-29 22:43:40 -05:00
parent e7470bdfac
commit d94b7a3e81
174 changed files with 31731 additions and 4836 deletions

View File

@@ -1,6 +1,7 @@
#include "bitmap.h"
#include <SDL.h>
#include <png.h>
#include <cstdint>
#include <memory>
@@ -23,35 +24,180 @@ void GrayscalePalette(SDL_Palette *palette) {
palette->colors[i].b = i * 31;
}
}
} // namespace
Bitmap::Bitmap(int width, int height, int depth, uchar *data) {
Create(width, height, depth, data);
void PngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) {
std::vector<uint8_t> *p = (std::vector<uint8_t> *)png_get_io_ptr(png_ptr);
p->insert(p->end(), data, data + length);
}
bool ConvertSurfaceToPNG(SDL_Surface *surface, std::vector<uint8_t> &buffer) {
png_structp png_ptr = png_create_write_struct("1.6.40", NULL, NULL, NULL);
if (!png_ptr) {
SDL_Log("Failed to create PNG write struct");
return false;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
SDL_Log("Failed to create PNG info struct");
return false;
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
SDL_Log("Error during PNG write");
return false;
}
png_set_write_fn(png_ptr, &buffer, PngWriteCallback, NULL);
png_colorp pal_ptr;
/* Prepare chunks */
int colortype = PNG_COLOR_MASK_COLOR;
int i = 0;
SDL_Palette *pal;
if (surface->format->BytesPerPixel > 0 &&
surface->format->BytesPerPixel <= 8 && (pal = surface->format->palette)) {
SDL_Log("Writing PNG image with palette");
colortype |= PNG_COLOR_MASK_PALETTE;
pal_ptr = (png_colorp)malloc(pal->ncolors * sizeof(png_color));
for (i = 0; i < pal->ncolors; i++) {
pal_ptr[i].red = pal->colors[i].r;
pal_ptr[i].green = pal->colors[i].g;
pal_ptr[i].blue = pal->colors[i].b;
}
png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors);
free(pal_ptr);
}
auto depth = surface->format->BitsPerPixel;
// Set image attributes.
png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, depth, colortype,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_set_bgr(png_ptr);
// Write the image data.
std::vector<png_bytep> row_pointers(surface->h);
for (int y = 0; y < surface->h; ++y) {
row_pointers[y] = (png_bytep)(surface->pixels) + y * surface->pitch;
}
png_set_rows(png_ptr, info_ptr, row_pointers.data());
SDL_Log("Writing PNG image...");
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
SDL_Log("PNG image write complete");
png_destroy_write_struct(&png_ptr, &info_ptr);
return true;
}
void PngReadCallback(png_structp png_ptr, png_bytep outBytes,
png_size_t byteCountToRead) {
png_voidp io_ptr = png_get_io_ptr(png_ptr);
if (!io_ptr) return;
std::vector<uint8_t> *png_data =
reinterpret_cast<std::vector<uint8_t> *>(io_ptr);
size_t pos = png_data->size() - byteCountToRead;
memcpy(outBytes, png_data->data() + pos, byteCountToRead);
png_data->resize(pos); // Reduce the buffer size
}
void ConvertPngToSurface(const std::vector<uint8_t> &png_data,
SDL_Surface **outSurface) {
std::vector<uint8_t> data(png_data);
png_structp png_ptr = png_create_read_struct("1.6.40", NULL, NULL, NULL);
if (!png_ptr) {
throw std::runtime_error("Failed to create PNG read struct");
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, NULL, NULL);
throw std::runtime_error("Failed to create PNG info struct");
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
throw std::runtime_error("Error during PNG read");
}
// Set our custom read function
png_set_read_fn(png_ptr, &data, PngReadCallback);
// Read the PNG info
png_read_info(png_ptr, info_ptr);
uint32_t width = png_get_image_width(png_ptr, info_ptr);
uint32_t height = png_get_image_height(png_ptr, info_ptr);
png_byte color_type = png_get_color_type(png_ptr, info_ptr);
png_byte bit_depth = png_get_bit_depth(png_ptr, info_ptr);
// Set up transformations, e.g., strip 16-bit PNGs down to 8-bit, expand
// palettes, etc.
if (bit_depth == 16) {
png_set_strip_16(png_ptr);
}
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(png_ptr);
}
// PNG files pack pixels, expand them
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
png_set_expand_gray_1_2_4_to_8(png_ptr);
}
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_tRNS_to_alpha(png_ptr);
}
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
}
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(png_ptr);
}
// Update info structure with transformations
png_read_update_info(png_ptr, info_ptr);
// Read the file
std::vector<png_bytep> row_pointers(height);
std::vector<uint8_t> raw_data(width * height *
4); // Assuming 4 bytes per pixel (RGBA)
for (size_t y = 0; y < height; y++) {
row_pointers[y] = &raw_data[y * width * 4];
}
png_read_image(png_ptr, row_pointers.data());
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
// Create SDL_Surface from raw pixel data
*outSurface = SDL_CreateRGBSurfaceWithFormatFrom(
raw_data.data(), width, height, 32, width * 4, SDL_PIXELFORMAT_RGBA32);
if (*outSurface == nullptr) {
SDL_Log("SDL_CreateRGBSurfaceWithFormatFrom failed: %s\n", SDL_GetError());
} else {
SDL_Log("Successfully created SDL_Surface from PNG data");
}
}
} // namespace
Bitmap::Bitmap(int width, int height, int depth, int data_size) {
Create(width, height, depth, data_size);
}
Bitmap::Bitmap(int width, int height, int depth, uchar *data, int data_size) {
Create(width, height, depth, data, data_size);
}
// Pass raw pixel data directly to the surface
void Bitmap::Create(int width, int height, int depth, uchar *data) {
active_ = true;
width_ = width;
height_ = height;
depth_ = depth;
pixel_data_ = data;
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
SDL_PIXELFORMAT_INDEX8),
SDL_Surface_Deleter());
surface_->pixels = pixel_data_;
GrayscalePalette(surface_->format->palette);
}
// Reserves data to later draw to surface via pointer
void Bitmap::Create(int width, int height, int depth, int size) {
active_ = true;
@@ -69,23 +215,7 @@ void Bitmap::Create(int width, int height, int depth, int size) {
GrayscalePalette(surface_->format->palette);
}
// Pass raw pixel data directly to the surface
void Bitmap::Create(int width, int height, int depth, uchar *data, int size) {
active_ = true;
width_ = width;
height_ = height;
depth_ = depth;
pixel_data_ = data;
data_size_ = size;
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
SDL_PIXELFORMAT_INDEX8),
SDL_Surface_Deleter());
surface_->pixels = pixel_data_;
GrayscalePalette(surface_->format->palette);
}
void Bitmap::Create(int width, int height, int depth, Bytes data) {
void Bitmap::Create(int width, int height, int depth, const Bytes &data) {
active_ = true;
width_ = width;
height_ = height;
@@ -100,9 +230,62 @@ void Bitmap::Create(int width, int height, int depth, Bytes data) {
GrayscalePalette(surface_->format->palette);
}
void Bitmap::Apply(Bytes data) {
pixel_data_ = data.data();
data_ = data;
// Creates the texture that will be displayed to the screen.
void Bitmap::CreateTexture(SDL_Renderer *renderer) {
// Ensure width and height are non-zero
if (width_ <= 0 || height_ <= 0) {
SDL_Log("Invalid texture dimensions: width=%d, height=%d\n", width_,
height_);
return;
}
texture_ = std::shared_ptr<SDL_Texture>{
SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888,
SDL_TEXTUREACCESS_STREAMING, width_, height_),
SDL_Texture_Deleter{}};
if (texture_ == nullptr) {
SDL_Log("SDL_CreateTextureFromSurface failed: %s\n", SDL_GetError());
}
SDL_Surface *converted_surface =
SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0);
if (converted_surface) {
// Create texture from the converted surface
converted_surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
converted_surface, SDL_Surface_Deleter());
} else {
// Handle the error
SDL_Log("SDL_ConvertSurfaceFormat failed: %s\n", SDL_GetError());
}
SDL_LockTexture(texture_.get(), nullptr, (void **)&texture_pixels,
&converted_surface_->pitch);
memcpy(texture_pixels, converted_surface_->pixels,
converted_surface_->h * converted_surface_->pitch);
SDL_UnlockTexture(texture_.get());
}
void Bitmap::UpdateTexture(SDL_Renderer *renderer) {
SDL_Surface *converted_surface =
SDL_ConvertSurfaceFormat(surface_.get(), SDL_PIXELFORMAT_ARGB8888, 0);
if (converted_surface) {
// Create texture from the converted surface
converted_surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
converted_surface, SDL_Surface_Deleter());
} else {
// Handle the error
SDL_Log("SDL_ConvertSurfaceFormat failed: %s\n", SDL_GetError());
}
SDL_LockTexture(texture_.get(), nullptr, (void **)&texture_pixels,
&converted_surface_->pitch);
memcpy(texture_pixels, converted_surface_->pixels,
converted_surface_->h * converted_surface_->pitch);
SDL_UnlockTexture(texture_.get());
}
// Creates the texture that will be displayed to the screen.
@@ -112,22 +295,107 @@ void Bitmap::CreateTexture(std::shared_ptr<SDL_Renderer> renderer) {
SDL_Texture_Deleter{}};
}
void Bitmap::UpdateTexture(std::shared_ptr<SDL_Renderer> renderer) {
// SDL_DestroyTexture(texture_.get());
// texture_ = nullptr;
texture_ = std::shared_ptr<SDL_Texture>{
SDL_CreateTextureFromSurface(renderer.get(), surface_.get()),
SDL_Texture_Deleter{}};
}
void Bitmap::SaveSurfaceToFile(std::string_view filename) {
SDL_SaveBMP(surface_.get(), filename.data());
}
void Bitmap::SetSurface(SDL_Surface *surface) {
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
surface, SDL_Surface_Deleter());
}
std::vector<uint8_t> Bitmap::GetPngData() {
ConvertSurfaceToPNG(surface_.get(), png_data_);
return png_data_;
}
void Bitmap::LoadFromPngData(const std::vector<uint8_t> &png_data, int width,
int height) {
width_ = width;
height_ = height;
SDL_Surface *surface = surface_.get();
ConvertPngToSurface(png_data, &surface);
surface_.reset(surface);
}
// Convert SNESPalette to SDL_Palette for surface.
void Bitmap::ApplyPalette(const SNESPalette &palette) {
palette_ = palette;
for (int i = 0; i < palette.size_; ++i) {
if (palette.GetColor(i).transparent) {
SDL_UnlockSurface(surface_.get());
for (int i = 0; i < palette.size(); ++i) {
if (palette.GetColor(i).IsTransparent()) {
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 = palette.GetColor(i).GetRGB().x;
surface_->format->palette->colors[i].g = palette.GetColor(i).GetRGB().y;
surface_->format->palette->colors[i].b = palette.GetColor(i).GetRGB().z;
surface_->format->palette->colors[i].a = palette.GetColor(i).GetRGB().w;
}
}
SDL_LockSurface(surface_.get());
}
void Bitmap::ApplyPaletteWithTransparent(const SNESPalette &palette,
int index) {
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).GetRGB());
}
SDL_UnlockSurface(surface_.get());
int i = 0;
for (auto &each : colors) {
surface_->format->palette->colors[i].r = each.x;
surface_->format->palette->colors[i].g = each.y;
surface_->format->palette->colors[i].b = each.z;
surface_->format->palette->colors[i].a = each.w;
i++;
}
SDL_LockSurface(surface_.get());
}
void Bitmap::ApplyPalette(const std::vector<SDL_Color> &palette) {
SDL_UnlockSurface(surface_.get());
for (int i = 0; i < palette.size(); ++i) {
surface_->format->palette->colors[i].r = palette[i].r;
surface_->format->palette->colors[i].g = palette[i].g;
surface_->format->palette->colors[i].b = palette[i].b;
surface_->format->palette->colors[i].a = palette[i].a;
}
SDL_LockSurface(surface_.get());
}
void Bitmap::InitializeFromData(uint32_t width, uint32_t height, uint32_t depth,
const Bytes &data) {
active_ = true;
width_ = width;
height_ = height;
depth_ = depth;
data_ = data;
data_size_ = data.size();
pixel_data_ = data_.data();
surface_ = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(
SDL_CreateRGBSurfaceWithFormat(0, width_, height_, depth_,
SDL_PIXELFORMAT_INDEX8),
SDL_Surface_Deleter());
surface_->pixels = pixel_data_;
GrayscalePalette(surface_->format->palette);
}
} // namespace gfx

View File

@@ -6,6 +6,7 @@
#include <cstdint>
#include <memory>
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
@@ -19,33 +20,136 @@ namespace gfx {
class Bitmap {
public:
Bitmap() = default;
Bitmap(int width, int height, int depth, uchar *data);
Bitmap(int width, int height, int depth, int data_size);
Bitmap(int width, int height, int depth, uchar *data, int data_size);
void Create(int width, int height, int depth, uchar *data);
void Create(int width, int height, int depth, int data_size);
void Create(int width, int height, int depth, uchar *data, int data_size);
void Create(int width, int height, int depth, Bytes data);
void Apply(Bytes data);
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
void ApplyPalette(const SNESPalette &palette);
void WriteToPixel(int position, uchar value) {
this->pixel_data_[position] = value;
Bitmap(int width, int height, int depth, const Bytes &data)
: width_(width), height_(height), depth_(depth), data_(data) {
InitializeFromData(width, height, depth, data);
}
int GetWidth() const { return width_; }
int GetHeight() const { return height_; }
auto GetSize() const { return data_size_; }
auto GetData() const { return pixel_data_; }
auto GetByte(int i) const { return pixel_data_[i]; }
auto GetTexture() const { return texture_.get(); }
auto GetSurface() const { return surface_.get(); }
void Create(int width, int height, int depth, int data_size);
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);
void CreateTexture(std::shared_ptr<SDL_Renderer> renderer);
void UpdateTexture(std::shared_ptr<SDL_Renderer> renderer);
void CreateTexture(SDL_Renderer *renderer);
void UpdateTexture(SDL_Renderer *renderer);
void SaveSurfaceToFile(std::string_view filename);
void SetSurface(SDL_Surface *surface);
std::vector<uint8_t> GetPngData();
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);
void ApplyPalette(const std::vector<SDL_Color> &palette);
void WriteToPixel(int position, uchar value) {
if (pixel_data_ == nullptr) {
pixel_data_ = data_.data();
}
pixel_data_[position] = value;
modified_ = true;
}
void WriteWordToPixel(int position, uint16_t value) {
if (pixel_data_ == nullptr) {
pixel_data_ = data_.data();
}
pixel_data_[position] = value & 0xFF;
pixel_data_[position + 1] = (value >> 8) & 0xFF;
modified_ = true;
}
void Get8x8Tile(int tile_index, int x, int y, std::vector<uint8_t> &tile_data,
int &tile_data_offset) {
int tile_offset = tile_index * 64;
int tile_x = x * 8;
int tile_y = y * 8;
for (int i = 0; i < 8; i++) {
int row_offset = tile_offset + (i * 8);
for (int j = 0; j < 8; j++) {
int pixel_offset = row_offset + j;
int pixel_value = data_[pixel_offset];
tile_data[tile_data_offset] = pixel_value;
tile_data_offset++;
}
}
}
void WriteColor(int position, const ImVec4 &color) {
// Convert ImVec4 (RGBA) to SDL_Color (RGBA)
SDL_Color sdl_color;
sdl_color.r = static_cast<Uint8>(color.x * 255);
sdl_color.g = static_cast<Uint8>(color.y * 255);
sdl_color.b = static_cast<Uint8>(color.z * 255);
sdl_color.a = static_cast<Uint8>(color.w * 255);
// Map SDL_Color to the nearest color index in the surface's palette
Uint8 index =
SDL_MapRGB(surface_->format, sdl_color.r, sdl_color.g, sdl_color.b);
// Write the color index to the pixel data
pixel_data_[position] = index;
modified_ = true;
}
void Cleanup() {
// Reset texture_
if (texture_) {
texture_.reset();
}
// Reset surface_ and its pixel data
if (surface_) {
surface_->pixels = nullptr;
surface_.reset();
}
// Reset data_
data_.clear();
// Reset other members if necessary
active_ = false;
pixel_data_ = nullptr;
width_ = 0;
height_ = 0;
depth_ = 0;
data_size_ = 0;
palette_.Clear();
}
auto sdl_palette() {
if (surface_ == nullptr) {
throw std::runtime_error("Surface is null.");
}
return surface_->format->palette;
}
auto palette() const { return palette_; }
auto palette_size() const { return palette_.size(); }
int width() const { return width_; }
int height() const { return height_; }
auto depth() const { return depth_; }
auto size() const { return data_size_; }
auto data() const { return data_.data(); }
auto &mutable_data() { return data_; }
auto mutable_pixel_data() { return pixel_data_; }
auto surface() const { return surface_.get(); }
auto mutable_surface() { return surface_.get(); }
void set_data(const Bytes &data) { data_ = data; }
auto vector() const { return data_; }
auto at(int i) const { return data_[i]; }
auto texture() const { return texture_.get(); }
auto modified() const { return modified_; }
void set_modified(bool modified) { modified_ = modified; }
auto IsActive() const { return active_; }
auto SetActive(bool active) { active_ = active; }
private:
struct SDL_Texture_Deleter {
@@ -71,13 +175,75 @@ class Bitmap {
int height_ = 0;
int depth_ = 0;
int data_size_ = 0;
bool freed_ = false;
bool active_ = false;
bool modified_ = false;
void *texture_pixels = nullptr;
uchar *pixel_data_;
Bytes data_;
std::vector<uint8_t> png_data_;
gfx::SNESPalette palette_;
std::shared_ptr<SDL_Texture> texture_ = nullptr;
std::shared_ptr<SDL_Surface> surface_ = nullptr;
std::shared_ptr<SDL_Surface> converted_surface_ = nullptr;
};
using BitmapTable = std::unordered_map<int, gfx::Bitmap>;
class BitmapManager {
private:
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>> bitmap_cache_;
public:
void LoadBitmap(int id, const Bytes &data, int width, int height, int depth) {
bitmap_cache_[id] =
std::make_shared<gfx::Bitmap>(width, height, depth, data);
}
std::shared_ptr<gfx::Bitmap> const &CopyBitmap(const gfx::Bitmap &bitmap,
int id) {
auto new_bitmap = std::make_shared<gfx::Bitmap>(
bitmap.width(), bitmap.height(), bitmap.depth(), bitmap.vector());
bitmap_cache_[id] = new_bitmap;
return new_bitmap;
}
std::shared_ptr<gfx::Bitmap> const &operator[](int id) {
auto it = bitmap_cache_.find(id);
if (it != bitmap_cache_.end()) {
return it->second;
}
return nullptr;
}
auto mutable_bitmap(int id) { return bitmap_cache_[id]; }
using value_type = std::pair<const int, std::shared_ptr<gfx::Bitmap>>;
using iterator =
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>>::iterator;
using const_iterator =
std::unordered_map<int, std::shared_ptr<gfx::Bitmap>>::const_iterator;
iterator begin() noexcept { return bitmap_cache_.begin(); }
iterator end() noexcept { return bitmap_cache_.end(); }
const_iterator begin() const noexcept { return bitmap_cache_.begin(); }
const_iterator end() const noexcept { return bitmap_cache_.end(); }
const_iterator cbegin() const noexcept { return bitmap_cache_.cbegin(); }
const_iterator cend() const noexcept { return bitmap_cache_.cend(); }
std::shared_ptr<gfx::Bitmap> const &GetBitmap(int id) {
auto it = bitmap_cache_.find(id);
if (it != bitmap_cache_.end()) {
return it->second;
}
return nullptr; // or handle the error accordingly
}
void ClearCache() { bitmap_cache_.clear(); }
};
} // namespace gfx

1186
src/app/gfx/compression.cc Normal file

File diff suppressed because it is too large Load Diff

213
src/app/gfx/compression.h Normal file
View File

@@ -0,0 +1,213 @@
#ifndef YAZE_APP_GFX_COMPRESSION_H
#define YAZE_APP_GFX_COMPRESSION_H
#include <iostream>
#include <memory>
#include <string>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/constants.h"
#define BUILD_HEADER(command, length) (command << 5) + (length - 1)
namespace yaze {
namespace app {
namespace gfx {
namespace lc_lz2 {
constexpr int kCommandDirectCopy = 0;
constexpr int kCommandByteFill = 1;
constexpr int kCommandWordFill = 2;
constexpr int kCommandIncreasingFill = 3;
constexpr int kCommandRepeatingBytes = 4;
constexpr int kCommandLongLength = 7;
constexpr int kMaxLengthNormalHeader = 32;
constexpr int kMaxLengthCompression = 1024;
constexpr int kNintendoMode1 = 0;
constexpr int kNintendoMode2 = 1;
constexpr int kSnesByteMax = 0xFF;
constexpr int kCommandMod = 0x07;
constexpr int kExpandedMod = 0xE0;
constexpr int kExpandedLengthMod = 0x3FF;
constexpr int kNormalLengthMod = 0x1F;
constexpr int kCompressionStringMod = 7 << 5;
// Represents a command in the compression algorithm.
struct CompressionCommand {
// The command arguments for each possible command.
std::array<std::array<char, 2>, 5> arguments;
// The size of each possible command.
std::array<uint, 5> cmd_size;
// The size of the data processed by each possible command.
std::array<uint, 5> data_size;
};
using CommandArgumentArray = std::array<std::array<char, 2>, 5>;
using CommandSizeArray = std::array<uint, 5>;
using DataSizeArray = std::array<uint, 5>;
// Represents a piece of compressed data.
struct CompressionPiece {
char command;
int length;
int argument_length;
std::string argument;
std::shared_ptr<CompressionPiece> next = nullptr;
CompressionPiece() = default;
CompressionPiece(int cmd, int len, std::string args, int arg_len)
: command(cmd), length(len), argument_length(arg_len), argument(args) {}
};
using CompressionPiece = struct CompressionPiece;
using CompressionPiecePointer = std::shared_ptr<CompressionPiece>;
void PrintCompressionPiece(const CompressionPiecePointer& piece);
void PrintCompressionChain(const CompressionPiecePointer& chain_head);
// Compression V1
void CheckByteRepeat(const uchar* rom_data, DataSizeArray& data_size_taken,
CommandArgumentArray& cmd_args, uint& src_data_pos,
const uint last_pos);
void CheckWordRepeat(const uchar* rom_data, DataSizeArray& data_size_taken,
CommandArgumentArray& cmd_args, uint& src_data_pos,
const uint last_pos);
void CheckIncByte(const uchar* rom_data, DataSizeArray& data_size_taken,
CommandArgumentArray& cmd_args, uint& src_data_pos,
const uint last_pos);
void CheckIntraCopy(const uchar* rom_data, DataSizeArray& data_size_taken,
CommandArgumentArray& cmd_args, uint& src_data_pos,
const uint last_pos, uint start);
void ValidateForByteGain(const DataSizeArray& data_size_taken,
const CommandSizeArray& cmd_size, uint& max_win,
uint& cmd_with_max);
void CompressionCommandAlternative(const uchar* rom_data,
CompressionPiecePointer& compressed_chain,
const CommandSizeArray& cmd_size,
const CommandArgumentArray& cmd_args,
uint& src_data_pos, uint& comp_accumulator,
uint& cmd_with_max, uint& max_win);
// Compression V2
void CheckByteRepeatV2(const uchar* data, uint& src_pos, const uint last_pos,
CompressionCommand& cmd);
void CheckWordRepeatV2(const uchar* data, uint& src_pos, const uint last_pos,
CompressionCommand& cmd);
void CheckIncByteV2(const uchar* data, uint& src_pos, const uint last_pos,
CompressionCommand& cmd);
void CheckIntraCopyV2(const uchar* data, uint& src_pos, const uint last_pos,
uint start, CompressionCommand& cmd);
void ValidateForByteGainV2(const CompressionCommand& cmd, uint& max_win,
uint& cmd_with_max);
void CompressionCommandAlternativeV2(const uchar* data,
const CompressionCommand& cmd,
CompressionPiecePointer& compressed_chain,
uint& src_pos, uint& comp_accumulator,
uint& cmd_with_max, uint& max_win);
absl::StatusOr<Bytes> CompressV2(const uchar* data, const int start,
const int length, int mode = 1,
bool check = false);
absl::StatusOr<Bytes> CompressGraphics(const uchar* data, const int pos,
const int length);
absl::StatusOr<Bytes> CompressOverworld(const uchar* data, const int pos,
const int length);
absl::StatusOr<CompressionPiecePointer> SplitCompressionPiece(
CompressionPiecePointer& piece, int mode);
Bytes CreateCompressionString(CompressionPiecePointer& start, int mode);
absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head,
int mode, int start, int src_data_pos);
CompressionPiecePointer MergeCopy(CompressionPiecePointer& start);
// Compression V3
struct CompressionContext {
std::vector<uint8_t> data;
std::vector<uint8_t> compressed_data;
std::vector<CompressionPiece> compression_pieces;
std::vector<uint8_t> compression_string;
uint src_pos;
uint last_pos;
uint start;
uint comp_accumulator = 0;
uint cmd_with_max = kCommandDirectCopy;
uint max_win = 0;
CompressionCommand current_cmd = {};
int mode;
// Constructor to initialize the context
CompressionContext(const std::vector<uint8_t>& data_, const int start,
const int length)
: data(data_), src_pos(start), last_pos(start + length - 1), mode(0) {}
// Constructor to initialize the context
CompressionContext(const std::vector<uint8_t>& data_, const int start,
const int length, int mode_)
: data(data_),
src_pos(start),
last_pos(start + length - 1),
mode(mode_) {}
};
void CheckByteRepeatV3(CompressionContext& context);
void CheckWordRepeatV3(CompressionContext& context);
void CheckIncByteV3(CompressionContext& context);
void CheckIntraCopyV3(CompressionContext& context);
void InitializeCompression(CompressionContext& context);
void CheckAvailableCompressionCommands(CompressionContext& context);
void DetermineBestCompression(CompressionContext& context);
void HandleDirectCopy(CompressionContext& context);
void AddCompressionToChain(CompressionContext& context);
absl::Status ValidateCompressionResultV3(const CompressionContext& context);
absl::StatusOr<CompressionPiece> SplitCompressionPieceV3(
CompressionPiece& piece, int mode);
void FinalizeCompression(CompressionContext& context);
absl::StatusOr<Bytes> CompressV3(const std::vector<uint8_t> data,
const int start, const int length,
int mode = 1, bool check = false);
// Decompression
std::string SetBuffer(const std::vector<uint8_t>& data, int src_pos,
int comp_accumulator);
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);
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);
absl::StatusOr<Bytes> DecompressOverworld(const uchar* data, int pos, int size);
absl::StatusOr<Bytes> DecompressOverworld(const std::vector<uint8_t> data,
int pos, int size);
} // namespace lc_lz2
} // namespace gfx
} // namespace app
} // namespace yaze
#endif // YAZE_APP_GFX_COMPRESSION_H

281
src/app/gfx/scad_format.cc Normal file
View File

@@ -0,0 +1,281 @@
#include "scad_format.h"
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <vector>
#include "absl/status/status.h"
#include "app/core/constants.h"
#include "app/gfx/snes_tile.h"
namespace yaze {
namespace app {
namespace gfx {
void FindMetastamp() {
int matching_position = -1;
bool matched = false;
Bytes cgx_rom;
Bytes raw_data_;
for (int i = 0;
i < cgx_rom.size() - sizeof(kMatchedBytes) - kOffsetFromMatchedBytesEnd;
i++) {
raw_data_.push_back(cgx_rom[i]);
bool is_match = std::equal(std::begin(kMatchedBytes),
std::end(kMatchedBytes), &cgx_rom[i]);
if (is_match) {
matching_position = i;
matched = true;
break;
}
}
if (matched) {
int bpp_marker_position =
matching_position + sizeof(kMatchedBytes) + kOffsetFromMatchedBytesEnd;
int bpp_marker = cgx_rom[bpp_marker_position];
std::string bpp_type = (bpp_marker == 0x31) ? "8bpp" : "4bpp";
}
}
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) {
std::ifstream file(filename.data(), std::ios::binary);
if (!file.is_open()) {
return absl::NotFoundError("CGX file not found.");
}
std::vector<uint8_t> file_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
cgx_data =
std::vector<uint8_t>(file_content.begin(), file_content.end() - 0x500);
file.seekg(cgx_data.size() + 0x100);
cgx_header = std::vector<uint8_t>((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
if (bpp > 8) {
cgx_loaded = gfx::Bpp8SnesToIndexed(cgx_data, 40);
return absl::OkStatus();
}
cgx_loaded = gfx::Bpp8SnesToIndexed(cgx_data, bpp);
return absl::OkStatus();
}
absl::Status LoadScr(std::string_view filename, uint8_t input_value,
std::vector<uint8_t>& map_data) {
std::ifstream file(filename.data(), std::ios::binary);
if (!file.is_open()) {
return absl::NotFoundError("SCR/PNL/MAP file not found.");
}
// Check if file extension is PNL
bool pnl = false;
if (filename.find("PNL") != std::string::npos) {
std::vector<uint8_t> scr_data;
map_data.resize(0x8000);
scr_data.resize(0x8000);
// Read from file for 0x8000 bytes
std::vector<uint8_t> file_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
scr_data = std::vector<uint8_t>(file_content.begin(), file_content.end());
int md = 0x100;
for (int i = input_value * 0x400; i < 0x1000 + input_value * 0x400;
i += 2) {
auto b1_pos = (i - (input_value * 0x400));
map_data[b1_pos] = gfx::TileInfoToShort(
gfx::GetTilesInfo((ushort)scr_data[md + (i * 2)]));
auto b2_pos = (i - (input_value * 0x400) + 1);
map_data[b2_pos] = gfx::TileInfoToShort(
gfx::GetTilesInfo((ushort)scr_data[md + (i * 2) + 2]));
}
// 0x900
} else {
int offset = 0;
std::vector<uint8_t> scr_data;
map_data.resize(0x2000);
scr_data.resize(0x2000);
// read from file for 0x2000 bytes
std::vector<uint8_t> file_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
scr_data = std::vector<uint8_t>(file_content.begin(), file_content.end());
for (int i = 0; i < 0x1000 - offset; i++) {
map_data[i] = gfx::TileInfoToShort(
gfx::GetTilesInfo((ushort)scr_data[((i + offset) * 2)]));
}
}
return absl::OkStatus();
}
absl::Status DrawScrWithCgx(uint8_t bpp, std::vector<uint8_t>& map_data,
std::vector<uint8_t>& map_bitmap_data,
std::vector<uint8_t>& cgx_loaded) {
const std::vector<uint16_t> dimensions = {0x000, 0x400, 0x800, 0xC00};
uint8_t p = 0;
for (const auto each_dimension : dimensions) {
p = each_dimension;
// for each tile on the tile buffer
for (int i = 0; i < 0x400; i++) {
if (map_data[i + p] != 0xFFFF) {
auto t = gfx::GetTilesInfo(map_data[i + p]);
for (auto yl = 0; yl < 8; yl++) {
for (auto xl = 0; xl < 8; xl++) {
int mx = xl * (1 - t.horizontal_mirror_) +
(7 - xl) * (t.horizontal_mirror_);
int my =
yl * (1 - t.vertical_mirror_) + (7 - yl) * (t.vertical_mirror_);
int ty = (t.id_ / 16) * 1024;
int tx = (t.id_ % 16) * 8;
auto pixel = cgx_loaded[(tx + ty) + (yl * 128) + xl];
int index = (((i % 32) * 8) + ((i / 32) * 2048) + mx + (my * 256));
if (bpp != 8) {
map_bitmap_data[index] =
(uchar)((pixel & 0xFF) + t.palette_ * 16);
} else {
map_bitmap_data[index] = (uchar)(pixel & 0xFF);
}
}
}
}
}
}
return absl::OkStatus();
}
std::vector<SDL_Color> DecodeColFile(const std::string_view filename) {
std::vector<SDL_Color> decoded_col;
std::ifstream file(filename.data(), std::ios::binary | std::ios::ate);
if (!file.is_open()) {
return decoded_col; // Return an empty vector if the file couldn't be
// opened.
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (file.read(buffer.data(), size)) {
buffer.resize(size - 0x200);
int k = 0;
for (size_t i = 0; i < buffer.size() / 2; i++) {
uint16_t current_color = static_cast<unsigned char>(buffer[k]) |
(static_cast<unsigned char>(buffer[k + 1]) << 8);
SDL_Color color;
color.r = (current_color & 31) << 3;
color.g = ((current_color >> 5) & 31) << 3;
color.b = ((current_color >> 10) & 31) << 3;
color.a = (i & 0xF) == 0 ? 0 : 255;
decoded_col.push_back(color);
k += 2;
}
}
return decoded_col;
}
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) {
std::vector<uint8_t> header_obj;
int obj_range;
int expected_cut;
if (obj_loaded == 0) {
obj_range = 0x180;
expected_cut = 0x500;
} else {
obj_range = 0x300;
expected_cut = 0x900;
}
std::ifstream file(filename.data(), std::ios::binary);
if (!file.is_open()) {
return absl::NotFoundError("OBJ file not found.");
}
std::vector<uint8_t> file_content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
obj_data = file_content;
file.close();
int cut = obj_data.size() & 0x0FFF;
actual_obj_data =
std::vector<uint8_t>(obj_data.begin(), obj_data.end() - cut);
decoded_extra_obj =
std::vector<uint8_t>(obj_data.begin() + actual_obj_data.size(),
obj_data.begin() + actual_obj_data.size() + 0x100);
header_obj = std::vector<uint8_t>(
actual_obj_data.begin() + actual_obj_data.size(), actual_obj_data.end());
if (cut > expected_cut) {
std::vector<uint8_t> scad_data;
int j = 0;
int k = (obj_loaded == 0) ? 63 : 127;
for (size_t i = 0; i < (actual_obj_data.size() / 6); i++) {
std::vector<uint8_t> data = {
actual_obj_data[k * 6 + 0 + j], // display
actual_obj_data[k * 6 + 1 + j], // unknown
actual_obj_data[k * 6 + 2 + j], // y-disp
actual_obj_data[k * 6 + 3 + j], // x-disp
actual_obj_data[k * 6 + 5 + j], // props
actual_obj_data[k * 6 + 4 + j] // tile
};
scad_data.insert(scad_data.end(), data.begin(), data.end());
k = k - 1;
if (k == -1) {
k = (obj_loaded == 0) ? 63 : 127;
j = j + ((k + 1) * 6);
}
}
int extra_data_range = 0x400 * (obj_loaded + 1) + 0x100;
for (int i = 0; i < extra_data_range; i++) {
scad_data.push_back(header_obj[i]);
}
obj_data = scad_data;
actual_obj_data =
std::vector<uint8_t>(obj_data.begin(), obj_data.end() - cut);
}
decoded_obj.clear();
for (int k = 0; k < 128; k++) {
decoded_obj["frame " + std::to_string(k)] = std::vector<uint8_t>(obj_range);
for (int i = 0; i < obj_range; i++) {
try {
decoded_obj["frame " + std::to_string(k)][i] =
obj_data[i + (obj_range * k)];
} catch (...) {
decoded_obj["frame " + std::to_string(k)][i] = 0;
}
}
}
return absl::OkStatus();
}
} // namespace gfx
} // namespace app
} // namespace yaze

78
src/app/gfx/scad_format.h Normal file
View File

@@ -0,0 +1,78 @@
#ifndef YAZE_APP_GFX_scad_format_H
#define YAZE_APP_GFX_scad_format_H
#include <SDL.h>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/constants.h"
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
struct CgxHeader {
char file_type[4];
char bit_mode[5];
char version_number[9];
uint32_t header_size;
char hardware_name[4];
uint8_t bg_obj_flag;
uint8_t color_palette_number;
uint8_t reserved[0xE3];
uint8_t color_path[0x100];
};
constexpr uint16_t kMatchedBytes[] = {0x4E, 0x41, 0x4B, 0x31, 0x39, 0x38, 0x39};
constexpr uint16_t kOffsetFromMatchedBytesEnd = 0x1D;
void FindMetastamp();
absl::Status LoadScr(std::string_view filename, uint8_t input_value,
std::vector<uint8_t>& map_data);
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);
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);
std::vector<SDL_Color> DecodeColFile(const std::string_view filename);
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 gfx
} // namespace app
} // namespace yaze
#endif // YAZE_APP_GFX_scad_format_H

View File

@@ -10,91 +10,129 @@
#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 {
ushort ConvertRGBtoSNES(const snes_color color) {
uchar red = color.red / 8;
uchar green = color.green / 8;
uchar blue = color.blue / 8;
return blue * 1024 + green * 32 + red;
// 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;
constexpr uint16_t SNES_GREEN_SHIFT = 32;
constexpr uint16_t SNES_BLUE_SHIFT = 1024;
uint16_t ConvertRGBtoSNES(const snes_color& color) {
uint16_t red = color.red / 8;
uint16_t green = color.green / 8;
uint16_t blue = color.blue / 8;
return (blue * SNES_BLUE_SHIFT) + (green * SNES_GREEN_SHIFT) + red;
}
snes_color ConvertSNEStoRGB(const ushort color) {
snes_color toret;
toret.red = ((color) % 32) * 8;
toret.green = ((color / 32) % 32) * 8;
toret.blue = ((color / 1024) % 32) * 8;
toret.red = toret.red + toret.red / 32;
toret.green = toret.green + toret.green / 32;
toret.blue = toret.blue + toret.blue / 32;
return toret;
uint16_t ConvertRGBtoSNES(const ImVec4& color) {
snes_color new_color;
new_color.red = color.x * 255;
new_color.green = color.y * 255;
new_color.blue = color.z * 255;
return ConvertRGBtoSNES(new_color);
}
snes_palette* Extract(const char* data, const unsigned int offset,
const unsigned int palette_size) {
snes_palette* toret = nullptr; // palette_create(palette_size, 0)
unsigned colnum = 0;
for (int i = 0; i < palette_size * 2; i += 2) {
unsigned short snes_color;
snes_color = ((uchar)data[offset + i + 1]) << 8;
snes_color = snes_color | ((uchar)data[offset + i]);
toret->colors[colnum] = ConvertSNEStoRGB(snes_color);
colnum++;
snes_color ConvertSNEStoRGB(uint16_t color_snes) {
snes_color result;
result.red = (color_snes % SNES_RED_MASK) * 8;
result.green = ((color_snes / SNES_GREEN_MASK) % SNES_GREEN_MASK) * 8;
result.blue = ((color_snes / SNES_BLUE_SHIFT) % SNES_BLUE_MASK) * 8;
result.red += result.red / SNES_RED_MASK;
result.green += result.green / SNES_GREEN_MASK;
result.blue += result.blue / SNES_BLUE_MASK;
return result;
}
std::vector<snes_color> Extract(const char* data, unsigned int offset,
unsigned int palette_size) {
std::vector<snes_color> palette(palette_size);
for (unsigned int i = 0; i < palette_size * 2; i += 2) {
uint16_t snes_color = (static_cast<uint8_t>(data[offset + i + 1]) << 8) |
static_cast<uint8_t>(data[offset + i]);
palette[i / 2] = ConvertSNEStoRGB(snes_color);
}
return toret;
return palette;
}
char* Convert(const snes_palette pal) {
char* toret = (char*)malloc(pal.size * 2);
for (unsigned int i = 0; i < pal.size; i++) {
unsigned short snes_data = ConvertRGBtoSNES(pal.colors[i]);
toret[i * 2] = snes_data & 0xFF;
toret[i * 2 + 1] = snes_data >> 8;
std::vector<char> Convert(const std::vector<snes_color>& palette) {
std::vector<char> data(palette.size() * 2);
for (unsigned int i = 0; i < palette.size(); i++) {
uint16_t snes_data = ConvertRGBtoSNES(palette[i]);
data[i * 2] = snes_data & 0xFF;
data[i * 2 + 1] = snes_data >> 8;
}
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;
rgb.y = ((color & 0x3E0) >> 5) * 8;
rgb.z = ((color & 0x7C00) >> 10) * 8;
SNESColor toret;
toret.SetRGB(rgb);
return toret;
}
// ============================================================================
std::vector<SNESColor> GetColFileData(uchar* data) {
std::vector<SNESColor> colors;
colors.reserve(256);
colors.resize(256);
SNESColor::SNESColor() : rgb(ImVec4(0.f, 0.f, 0.f, 0.f)) {}
for (int i = 0; i < 512; i += 2) {
colors[i / 2] = GetCgxColor((uint16_t)((data[i + 1] << 8) + data[i]));
}
SNESColor::SNESColor(snes_color val) {
rgb.x = val.red;
rgb.y = val.green;
rgb.z = val.blue;
}
SNESColor::SNESColor(ImVec4 val) : rgb(val) {
snes_color col;
col.red = (uchar)val.x;
col.blue = (uchar)val.y;
col.green = (uchar)val.z;
snes = ConvertRGBtoSNES(col);
}
void SNESColor::setRgb(ImVec4 val) {
rgb = val;
snes_color col;
col.red = val.x;
col.blue = val.y;
col.green = val.z;
snes = ConvertRGBtoSNES(col);
}
void SNESColor::setSNES(snes_color val) {
rgb = ImVec4(val.red, val.green, val.blue, 255.f);
}
void SNESColor::setSNES(uint16_t val) {
snes = val;
snes_color col = ConvertSNEStoRGB(val);
rgb = ImVec4(col.red, col.green, col.blue, 0.f);
return colors;
}
// ============================================================================
@@ -110,10 +148,10 @@ SNESPalette::SNESPalette(char* data) : size_(sizeof(data) / 2) {
assert((sizeof(data) % 4 == 0) && (sizeof(data) <= 32));
for (unsigned i = 0; i < sizeof(data); i += 2) {
SNESColor col;
col.snes = static_cast<uchar>(data[i + 1]) << 8;
col.snes = col.snes | static_cast<uchar>(data[i]);
snes_color mColor = ConvertSNEStoRGB(col.snes);
col.rgb = ImVec4(mColor.red, mColor.green, mColor.blue, 1.f);
col.SetSNES(static_cast<uchar>(data[i + 1]) << 8);
col.SetSNES(col.GetSNES() | static_cast<uchar>(data[i]));
snes_color mColor = ConvertSNEStoRGB(col.GetSNES());
col.SetRGB(ImVec4(mColor.red, mColor.green, mColor.blue, 1.f));
colors.push_back(col);
}
}
@@ -123,10 +161,10 @@ SNESPalette::SNESPalette(const unsigned char* snes_pal)
assert((sizeof(snes_pal) % 4 == 0) && (sizeof(snes_pal) <= 32));
for (unsigned i = 0; i < sizeof(snes_pal); i += 2) {
SNESColor col;
col.snes = snes_pal[i + 1] << (uint16_t)8;
col.snes = col.snes | snes_pal[i];
snes_color mColor = ConvertSNEStoRGB(col.snes);
col.rgb = ImVec4(mColor.red, mColor.green, mColor.blue, 1.f);
col.SetSNES(snes_pal[i + 1] << (uint16_t)8);
col.SetSNES(col.GetSNES() | snes_pal[i]);
snes_color mColor = ConvertSNEStoRGB(col.GetSNES());
col.SetRGB(ImVec4(mColor.red, mColor.green, mColor.blue, 1.f));
colors.push_back(col);
}
}
@@ -134,7 +172,7 @@ SNESPalette::SNESPalette(const unsigned char* snes_pal)
SNESPalette::SNESPalette(const std::vector<ImVec4>& cols) {
for (const auto& each : cols) {
SNESColor scol;
scol.setRgb(each);
scol.SetRGB(each);
colors.push_back(scol);
}
size_ = cols.size();
@@ -143,7 +181,7 @@ SNESPalette::SNESPalette(const std::vector<ImVec4>& cols) {
SNESPalette::SNESPalette(const std::vector<snes_color>& cols) {
for (const auto& each : cols) {
SNESColor scol;
scol.setSNES(each);
scol.SetSNES(ConvertRGBtoSNES(each));
colors.push_back(scol);
}
size_ = cols.size();
@@ -156,32 +194,15 @@ SNESPalette::SNESPalette(const std::vector<SNESColor>& cols) {
size_ = cols.size();
}
void SNESPalette::Create(const std::vector<SNESColor>& cols) {
for (const auto each : cols) {
colors.push_back(each);
}
size_ = cols.size();
}
char* SNESPalette::encode() {
auto data = new char[size_ * 2];
for (unsigned int i = 0; i < size_; i++) {
std::cout << colors[i].snes << std::endl;
data[i * 2] = (char)(colors[i].snes & 0xFF);
data[i * 2 + 1] = (char)(colors[i].snes >> 8);
}
return data;
}
SDL_Palette* SNESPalette::GetSDL_Palette() {
auto sdl_palette = std::make_shared<SDL_Palette>();
sdl_palette->ncolors = size_;
auto color = std::vector<SDL_Color>(size_);
for (int i = 0; i < size_; i++) {
color[i].r = (uint8_t)colors[i].rgb.x * 100;
color[i].g = (uint8_t)colors[i].rgb.y * 100;
color[i].b = (uint8_t)colors[i].rgb.z * 100;
color[i].r = (uint8_t)colors[i].GetRGB().x * 100;
color[i].g = (uint8_t)colors[i].GetRGB().y * 100;
color[i].b = (uint8_t)colors[i].GetRGB().z * 100;
color[i].a = 0;
std::cout << "Color " << i << " added (R:" << color[i].r
<< " G:" << color[i].g << " B:" << color[i].b << ")" << std::endl;
@@ -190,7 +211,87 @@ SDL_Palette* SNESPalette::GetSDL_Palette() {
return sdl_palette.get();
}
PaletteGroup::PaletteGroup(uint8_t mSize) : size(mSize) {}
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));
if (color_offset == 0) {
colors[color_offset].SetTransparent(true);
}
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;
colorArray[1] = color.GetRGB().y / 255.0f;
colorArray[2] = color.GetRGB().z / 255.0f;
colorArray[3] = color.GetRGB().w;
return colorArray;
}
PaletteGroup::PaletteGroup(uint8_t mSize) : size_(mSize) {}
PaletteGroup CreatePaletteGroupFromColFile(
std::vector<SNESColor>& palette_rows) {
PaletteGroup toret;
for (int i = 0; i < palette_rows.size(); i += 8) {
SNESPalette palette;
for (int j = 0; j < 8; j++) {
palette.AddColor(palette_rows[i + j].GetRomRGB());
}
toret.AddPalette(palette);
}
return toret;
}
// Take a SNESPalette with N many colors and divide it into palettes of 8 colors
// each
PaletteGroup CreatePaletteGroupFromLargePalette(SNESPalette& palette) {
PaletteGroup toret;
std::cout << "Palette size is " << palette.size() << std::endl;
for (int i = 0; i < palette.size(); i += 8) {
SNESPalette new_palette;
if (i + 8 < palette.size()) {
for (int j = 0; j < 8; j++) {
new_palette.AddColor(palette[i + j]);
}
}
toret.AddPalette(new_palette);
}
return toret;
}
} // namespace gfx
} // namespace app

View File

@@ -11,6 +11,8 @@
#include <memory>
#include <vector>
#include "absl/base/casts.h"
#include "absl/status/status.h"
#include "app/core/constants.h"
namespace yaze {
@@ -18,47 +20,111 @@ namespace app {
namespace gfx {
struct snes_color {
uchar red;
uchar blue;
uchar 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;
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;
ushort ConvertRGBtoSNES(const snes_color color);
snes_color ConvertSNEStoRGB(const ushort snes_color);
snes_palette* Extract(const char* data, const unsigned int offset,
const unsigned int palette_size);
char* Convert(const snes_palette pal);
uint16_t ConvertRGBtoSNES(const snes_color& color);
uint16_t ConvertRGBtoSNES(const ImVec4& 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);
struct SNESColor {
SNESColor();
explicit SNESColor(ImVec4);
explicit SNESColor(snes_color);
SNESColor() : rgb(0.f, 0.f, 0.f, 0.f), snes(0) {}
void setRgb(ImVec4);
void setSNES(snes_color);
void setSNES(uint16_t);
void setTransparent(bool t) { transparent = t; }
auto RGB() {
return ImVec4(rgb.x / 255, rgb.y / 255, rgb.z / 255, rgb.w);
explicit SNESColor(const ImVec4 val) : rgb(val) {
snes_color color;
color.red = val.x / 255;
color.green = val.y / 255;
color.blue = val.z / 255;
snes = ConvertRGBtoSNES(color);
}
bool transparent = false;
uint16_t snes = 0;
explicit SNESColor(const snes_color val)
: rgb(val.red, val.green, val.blue, 255.f),
snes(ConvertRGBtoSNES(val)),
rom_color(val) {}
ImVec4 GetRGB() const { return rgb; }
void SetRGB(const ImVec4 val) {
rgb.x = val.x / 255;
rgb.y = val.y / 255;
rgb.z = val.z / 255;
snes_color color;
color.red = val.x;
color.green = val.y;
color.blue = val.z;
rom_color = color;
snes = ConvertRGBtoSNES(color);
modified = true;
}
snes_color GetRomRGB() const { return rom_color; }
uint16_t GetSNES() const { return snes; }
void SetSNES(uint16_t val) {
snes = val;
snes_color col = ConvertSNEStoRGB(val);
rgb = ImVec4(col.red, col.green, col.blue, 0.f);
modified = true;
}
bool IsModified() const { return modified; }
bool IsTransparent() const { return transparent; }
void SetTransparent(bool t) { transparent = t; }
void SetModified(bool m) { modified = m; }
private:
ImVec4 rgb;
uint16_t snes;
snes_color rom_color;
bool modified = false;
bool transparent = false;
};
gfx::SNESColor ReadColorFromROM(int offset, const uchar* rom);
SNESColor GetCgxColor(uint16_t color);
std::vector<SNESColor> GetColFileData(uchar* data);
class SNESPalette {
public:
template <typename T>
explicit SNESPalette(const std::vector<T>& data) {
for (const auto& item : data) {
colors.push_back(SNESColor(item));
}
}
SNESPalette() = default;
explicit SNESPalette(uint8_t mSize);
explicit SNESPalette(char* snesPal);
explicit SNESPalette(const unsigned char* snes_pal);
@@ -66,50 +132,146 @@ class SNESPalette {
explicit SNESPalette(const std::vector<snes_color>&);
explicit SNESPalette(const std::vector<SNESColor>&);
void Create(const std::vector<SNESColor>&);
void AddColor(SNESColor color) { colors.push_back(color); }
auto GetColor(int i) const { return colors[i]; }
SDL_Palette* GetSDL_Palette();
SNESColor operator[](int i) {
void Create(const std::vector<SNESColor>& cols) {
for (const auto& each : cols) {
colors.push_back(each);
}
size_ = cols.size();
}
void AddColor(SNESColor color) {
colors.push_back(color);
size_++;
}
void AddColor(snes_color color) {
colors.emplace_back(color);
size_++;
}
auto GetColor(int i) const {
if (i > size_) {
std::cout << "SNESPalette: Index out of bounds" << std::endl;
return colors[0];
throw std::out_of_range("SNESPalette: Index out of bounds");
}
return colors[i];
}
char* encode();
SDL_Palette* GetSDL_Palette();
void Clear() {
colors.clear();
size_ = 0;
}
int size_ = 0;
std::vector<SNESColor> colors;
auto size() const { return colors.size(); }
SNESColor& operator[](int i) {
if (i > size_) {
throw std::out_of_range("SNESPalette: Index out of bounds");
}
return colors[i];
}
void operator()(int i, const SNESColor& color) {
if (i >= size_) {
throw std::out_of_range("SNESPalette: Index out of bounds");
}
colors[i] = color;
}
void operator()(int i, const ImVec4& color) {
if (i >= size_) {
throw std::out_of_range("SNESPalette: Index out of bounds");
}
colors[i].SetRGB(color);
colors[i].SetModified(true);
}
SNESPalette sub_palette(int start, int end) const {
SNESPalette pal;
for (int i = start; i < end; i++) {
pal.AddColor(colors[i]);
}
return pal;
}
private:
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);
uint32_t GetPaletteAddress(const std::string& group_name, size_t palette_index,
size_t color_index);
std::array<float, 4> ToFloatArray(const SNESColor& color);
struct PaletteGroup {
PaletteGroup() = default;
explicit PaletteGroup(uint8_t mSize);
void AddPalette(SNESPalette pal) {
absl::Status AddPalette(SNESPalette pal) {
palettes.emplace_back(pal);
size = palettes.size();
size_ = palettes.size();
return absl::OkStatus();
}
void AddColor(SNESColor color) {
if (size == 0) {
SNESPalette empty_pal;
palettes.emplace_back(empty_pal);
absl::Status AddColor(SNESColor color) {
if (size_ == 0) {
palettes.emplace_back();
}
palettes[0].AddColor(color);
return absl::OkStatus();
}
void Clear() {
palettes.clear();
size_ = 0;
}
auto size() const { return palettes.size(); }
SNESPalette operator[](int i) {
if (i > size) {
if (i > size_) {
std::cout << "PaletteGroup: Index out of bounds" << std::endl;
return palettes[0];
}
return palettes[i];
}
int size = 0;
const SNESPalette& operator[](int i) const {
if (i > size_) {
std::cout << "PaletteGroup: Index out of bounds" << std::endl;
return palettes[0];
}
return palettes[i];
}
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();
}
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;
};
PaletteGroup CreatePaletteGroupFromColFile(std::vector<SNESColor>& colors);
PaletteGroup CreatePaletteGroupFromLargePalette(SNESPalette& palette);
} // namespace gfx
} // namespace app
} // namespace yaze

View File

@@ -9,6 +9,327 @@ namespace yaze {
namespace app {
namespace gfx {
tile8 UnpackBppTile(const Bytes& data, const uint32_t offset,
const uint32_t bpp) {
tile8 tile;
assert(bpp >= 1 && bpp <= 8);
unsigned int bpp_pos[8]; // More for conveniance and readibility
for (int col = 0; col < 8; col++) {
for (int row = 0; row < 8; row++) {
if (bpp == 1) {
tile.data[col * 8 + row] = (data[offset + col] >> (7 - row)) & 0x01;
continue;
}
/* SNES bpp format interlace each byte of the first 2 bitplanes.
* | byte 1 of first bitplane | byte 1 of second bitplane |
* | byte 2 of first bitplane | byte 2 of second bitplane | ..
*/
bpp_pos[0] = offset + col * 2;
bpp_pos[1] = offset + col * 2 + 1;
char mask = 1 << (7 - row);
tile.data[col * 8 + row] = (data[bpp_pos[0]] & mask) == mask;
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[1]] & mask) == mask)
<< 1;
if (bpp == 3) {
// When we have 3 bitplanes, the bytes for the third bitplane are after
// the 16 bytes of the 2 bitplanes.
bpp_pos[2] = offset + 16 + col;
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[2]] & mask) == mask)
<< 2;
}
if (bpp >= 4) {
// For 4 bitplanes, the 2 added bitplanes are interlaced like the first
// two.
bpp_pos[2] = offset + 16 + col * 2;
bpp_pos[3] = offset + 16 + col * 2 + 1;
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[2]] & mask) == mask)
<< 2;
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[3]] & mask) == mask)
<< 3;
}
if (bpp == 8) {
bpp_pos[4] = offset + 32 + col * 2;
bpp_pos[5] = offset + 32 + col * 2 + 1;
bpp_pos[6] = offset + 48 + col * 2;
bpp_pos[7] = offset + 48 + col * 2 + 1;
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[4]] & mask) == mask)
<< 4;
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[5]] & mask) == mask)
<< 5;
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[6]] & mask) == mask)
<< 6;
tile.data[col * 8 + row] |= (uchar)((data[bpp_pos[7]] & mask) == mask)
<< 7;
}
}
}
return tile;
}
Bytes PackBppTile(const tile8& tile, const uint32_t bpp) {
// Allocate memory for output data
std::vector<uchar> output(bpp * 8, 0); // initialized with 0
unsigned maxcolor = 2 << bpp;
// Iterate over all columns and rows of the tile
for (unsigned int col = 0; col < 8; col++) {
for (unsigned int row = 0; row < 8; row++) {
uchar color = tile.data[col * 8 + row];
if (color > maxcolor) {
throw std::invalid_argument("Invalid color value.");
}
// 1bpp format
if (bpp == 1) output[col] += (uchar)((color & 1) << (7 - row));
// 2bpp format
if (bpp >= 2) {
output[col * 2] += (uchar)((color & 1) << (7 - row));
output[col * 2 + 1] += (uchar)((uchar)((color & 2) == 2) << (7 - row));
}
// 3bpp format
if (bpp == 3)
output[16 + col] += (uchar)(((color & 4) == 4) << (7 - row));
// 4bpp format
if (bpp >= 4) {
output[16 + col * 2] += (uchar)(((color & 4) == 4) << (7 - row));
output[16 + col * 2 + 1] += (uchar)(((color & 8) == 8) << (7 - row));
}
// 8bpp format
if (bpp == 8) {
output[32 + col * 2] += (uchar)(((color & 16) == 16) << (7 - row));
output[32 + col * 2 + 1] += (uchar)(((color & 32) == 32) << (7 - row));
output[48 + col * 2] += (uchar)(((color & 64) == 64) << (7 - row));
output[48 + col * 2 + 1] +=
(uchar)(((color & 128) == 128) << (7 - row));
}
}
}
return output;
}
std::vector<uchar> ConvertBpp(const std::vector<uchar>& tiles,
uint32_t from_bpp, uint32_t to_bpp) {
unsigned int nb_tile = tiles.size() / (from_bpp * 8);
std::vector<uchar> converted(nb_tile * to_bpp * 8);
for (unsigned int i = 0; i < nb_tile; i++) {
tile8 tile = UnpackBppTile(tiles, i * from_bpp * 8, from_bpp);
std::vector<uchar> packed_tile = PackBppTile(tile, to_bpp);
std::memcpy(converted.data() + i * to_bpp * 8, packed_tile.data(),
to_bpp * 8);
}
return converted;
}
std::vector<uchar> Convert3bppTo4bpp(const std::vector<uchar>& tiles) {
return ConvertBpp(tiles, 3, 4);
}
std::vector<uchar> Convert4bppTo3bpp(const std::vector<uchar>& tiles) {
return ConvertBpp(tiles, 4, 3);
}
Bytes SnesTo8bppSheet(Bytes sheet, int bpp) {
int xx = 0; // positions where we are at on the sheet
int yy = 0;
int pos = 0;
int ypos = 0;
int num_tiles = 64;
int buffer_size = 0x1000;
if (bpp == 2) {
bpp = 16;
num_tiles = 128;
buffer_size = 0x2000;
} else if (bpp == 3) {
bpp = 24;
}
Bytes sheet_buffer_out(buffer_size);
for (int i = 0; i < num_tiles; i++) { // for each tiles, 16 per line
for (int y = 0; y < 8; y++) { // for each line
for (int x = 0; x < 8; x++) { //[0] + [1] + [16]
auto b1 = (sheet[(y * 2) + (bpp * pos)] & (kGraphicsBitmap[x]));
auto b2 = (sheet[((y * 2) + (bpp * pos)) + 1] & (kGraphicsBitmap[x]));
auto b3 = (sheet[(16 + y) + (bpp * pos)] & (kGraphicsBitmap[x]));
unsigned char b = 0;
if (b1 != 0) {
b |= 1;
}
if (b2 != 0) {
b |= 2;
}
if (b3 != 0 && bpp != 16) {
b |= 4;
}
sheet_buffer_out[x + xx + (y * 128) + (yy * 1024)] = b;
}
}
pos++;
ypos++;
xx += 8;
if (ypos >= 16) {
yy++;
xx = 0;
ypos = 0;
}
}
return sheet_buffer_out;
}
Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp) {
// 3BPP
// [r0,bp1],[r0,bp2],[r1,bp1],[r1,bp2],[r2,bp1],[r2,bp2],[r3,bp1],[r3,bp2]
// [r4,bp1],[r4,bp2],[r5,bp1],[r5,bp2],[r6,bp1],[r6,bp2],[r7,bp1],[r7,bp2]
// [r0,bp3],[r0,bp4],[r1,bp3],[r1,bp4],[r2,bp3],[r2,bp4],[r3,bp3],[r3,bp4]
// [r4,bp3],[r4,bp4],[r5,bp3],[r5,bp4],[r6,bp3],[r6,bp4],[r7,bp3],[r7,bp4]
// [r0,bp5],[r0,bp6],[r1,bp5],[r1,bp6],[r2,bp5],[r2,bp6],[r3,bp5],[r3,bp6]
// [r4,bp5],[r4,bp6],[r5,bp5],[r5,bp6],[r6,bp5],[r6,bp6],[r7,bp5],[r7,bp6]
// [r0,bp7],[r0,bp8],[r1,bp7],[r1,bp8],[r2,bp7],[r2,bp8],[r3,bp7],[r3,bp8]
// [r4,bp7],[r4,bp8],[r5,bp7],[r5,bp8],[r6,bp7],[r6,bp8],[r7,bp7],[r7,bp8]
// 16 tiles = 1024 bytes
auto buffer = Bytes(data.size());
std::vector<std::vector<uint8_t>> bitmap_data;
bitmap_data.resize(0x80);
for (auto& each : bitmap_data) {
each.reserve(0x800);
}
int yy = 0;
int xx = 0;
int pos = 0;
const uint16_t sheet_width = 128;
// 64 = 4096 bytes
// 16 = 1024?
int ypos = 0;
// for each tiles //16 per lines
for (int i = 0; i < 4096; i++) {
// for each lines
for (int y = 0; y < 8; y++) {
//[0] + [1] + [16]
for (int x = 0; x < 8; x++) {
const uint16_t bitmask[] = {0x80, 0x40, 0x20, 0x10,
0x08, 0x04, 0x02, 0x01};
auto b1 = (data[(y * 2) + ((bpp * 8) * pos)] & (bitmask[x]));
auto b2 = (data[((y * 2) + ((bpp * 8) * pos)) + 1] & (bitmask[x]));
auto b3 = (data[(y * 2) + ((bpp * 8) * pos) + 16] & (bitmask[x]));
auto b4 = (data[(y * 2) + ((bpp * 8) * pos) + 17] & (bitmask[x]));
auto b5 = (data[(y * 2) + ((bpp * 8) * pos) + 32] & (bitmask[x]));
auto b6 = (data[(y * 2) + ((bpp * 8) * pos) + 33] & (bitmask[x]));
auto b7 = (data[(y * 2) + ((bpp * 8) * pos) + 48] & (bitmask[x]));
auto b8 = (data[(y * 2) + ((bpp * 8) * pos) + 49] & (bitmask[x]));
auto b = 0;
if (b1 != 0) {
b |= 1;
}
if (b2 != 0) {
b |= 2;
}
if (bpp >= 4) {
if (b3 != 0) {
b |= 4;
}
if (b4 != 0) {
b |= 8;
}
}
if (bpp >= 8) {
if (b5 != 0) {
b |= 0x10;
}
if (b6 != 0) {
b |= 0x20;
}
if (b7 != 0) {
b |= 0x40;
}
if (b8 != 0) {
b |= 0x80;
}
}
// bitmap_data[((x + xx) * sheet_width) + y + (yy * 8)] = b;
bitmap_data[x + xx][y + (yy * 8)] = b;
}
}
pos++;
ypos++;
xx += 8;
if (ypos >= 16) {
yy++;
xx = 0;
ypos = 0;
}
}
int n = 0;
for (int y = 0; y < (data.size() / 64); y++) {
for (int x = 0; x < sheet_width; x++) { // 128 assumption
if (n < data.size()) {
// buffer[n] = bitmap_data[(x * sheet_width) + y];
buffer[n] = bitmap_data[x][y];
n++;
}
}
}
return buffer;
}
uint16_t TileInfoToWord(TileInfo tile_info) {
uint16_t result = 0;
// Copy the id_ value
result |= tile_info.id_ & 0x3FF; // ids are 10 bits
// Set the vertical_mirror_, horizontal_mirror_, and over_ flags
result |= (tile_info.vertical_mirror_ ? 1 : 0) << 15;
result |= (tile_info.horizontal_mirror_ ? 1 : 0) << 14;
result |= (tile_info.over_ ? 1 : 0) << 13;
// Set the palette_
result |= (tile_info.palette_ & 0x07) << 10; // palettes are 3 bits
return result;
}
TileInfo WordToTileInfo(uint16_t word) {
// Extract the id_ value
uint16_t id = word & 0x3FF; // ids are 10 bits
// Extract the vertical_mirror_, horizontal_mirror_, and over_ flags
bool vertical_mirror = (word >> 15) & 0x01;
bool horizontal_mirror = (word >> 14) & 0x01;
bool over = (word >> 13) & 0x01;
// Extract the palette_
uint8_t palette = (word >> 10) & 0x07; // palettes are 3 bits
return TileInfo(id, palette, vertical_mirror, horizontal_mirror, over);
}
ushort TileInfoToShort(TileInfo tile_info) {
ushort result = 0;
// Copy the id_ value
result |= tile_info.id_ & 0x3FF; // ids are 10 bits
// Set the vertical_mirror_, horizontal_mirror_, and over_ flags
result |= (tile_info.vertical_mirror_ ? 1 : 0) << 10;
result |= (tile_info.horizontal_mirror_ ? 1 : 0) << 11;
result |= (tile_info.over_ ? 1 : 0) << 12;
// Set the palette_
result |= (tile_info.palette_ & 0x07) << 13; // palettes are 3 bits
return result;
}
TileInfo GetTilesInfo(ushort tile) {
// vhopppcc cccccccc
bool o = false;

View File

@@ -2,6 +2,7 @@
#define YAZE_APP_GFX_SNES_TILE_H
#include <cstdint>
#include <cstring>
#include <vector>
#include "app/core/constants.h"
@@ -10,13 +11,30 @@ namespace yaze {
namespace app {
namespace gfx {
constexpr uchar kGraphicsBitmap[8] = {0x80, 0x40, 0x20, 0x10,
0x08, 0x04, 0x02, 0x01};
Bytes SnesTo8bppSheet(Bytes sheet, int bpp);
Bytes Bpp8SnesToIndexed(Bytes data, uint64_t bpp = 0);
struct tile8 {
unsigned int id;
uint32_t id;
char data[64];
unsigned int palette_id;
uint32_t palette_id;
};
using tile8 = struct tile8;
tile8 UnpackBppTile(const Bytes& data, const uint32_t offset,
const uint32_t bpp);
Bytes PackBppTile(const tile8& tile, const uint32_t bpp);
std::vector<uchar> ConvertBpp(const std::vector<uchar>& tiles,
uint32_t from_bpp, uint32_t to_bpp);
std::vector<uchar> Convert3bppTo4bpp(const std::vector<uchar>& tiles);
std::vector<uchar> Convert4bppTo3bpp(const std::vector<uchar>& tiles);
// vhopppcc cccccccc
// [0, 1]
// [2, 3]
@@ -34,19 +52,66 @@ 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);
TileInfo WordToTileInfo(uint16_t word);
ushort TileInfoToShort(TileInfo tile_info);
TileInfo GetTilesInfo(ushort tile);
class Tile32 {
public:
ushort tile0_;
ushort tile1_;
ushort tile2_;
ushort tile3_;
uint16_t tile0_;
uint16_t tile1_;
uint16_t tile2_;
uint16_t tile3_;
Tile32(ushort t0, ushort t1, ushort t2, ushort t3)
// Default constructor
Tile32() : tile0_(0), tile1_(0), tile2_(0), tile3_(0) {}
// Parameterized constructor
Tile32(uint16_t t0, uint16_t t1, uint16_t t2, uint16_t t3)
: tile0_(t0), tile1_(t1), tile2_(t2), tile3_(t3) {}
// Copy constructor
Tile32(const Tile32& other)
: tile0_(other.tile0_),
tile1_(other.tile1_),
tile2_(other.tile2_),
tile3_(other.tile3_) {}
// Constructor from packed value
Tile32(uint64_t packedVal) {
tile0_ = (packedVal >> 48) & 0xFFFF;
tile1_ = (packedVal >> 32) & 0xFFFF;
tile2_ = (packedVal >> 16) & 0xFFFF;
tile3_ = packedVal & 0xFFFF;
}
// Equality operator
bool operator==(const Tile32& other) const {
return tile0_ == other.tile0_ && tile1_ == other.tile1_ &&
tile2_ == other.tile2_ && tile3_ == other.tile3_;
}
// Inequality operator
bool operator!=(const Tile32& other) const { return !(*this == other); }
// Get packed uint64_t representation
uint64_t GetPackedValue() const {
return (static_cast<uint64_t>(tile0_) << 48) |
(static_cast<uint64_t>(tile1_) << 32) |
(static_cast<uint64_t>(tile2_) << 16) |
static_cast<uint64_t>(tile3_);
}
};
class Tile16 {
@@ -65,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 {