16 return absl::InvalidArgumentError(
"ROM is not loaded");
73 return absl::FailedPreconditionError(
"GameData not set for TitleScreen");
81 if (pal_group.overworld_main.size() > 5) {
82 const auto& src = pal_group.overworld_main[5];
84 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
94 "Palette 0: added %zu colors from overworld_main[5]", added);
98 if (pal_group.overworld_animated.size() > 0) {
99 const auto& src = pal_group.overworld_animated[0];
101 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
110 "Palette 1: added %zu colors from overworld_animated[0]", added);
114 if (pal_group.overworld_aux.size() > 3) {
115 auto src = pal_group.overworld_aux[3];
116 for (
int pal = 0; pal < 2; pal++) {
118 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
127 "Palette %d: added %zu colors from overworld_aux[3]", 2 + pal,
133 if (pal_group.hud.size() > 0) {
134 auto src = pal_group.hud.palette(0);
136 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
144 LOG_INFO(
"TitleScreen",
"Palette 4: added %zu colors from hud[0]", added);
148 for (
int i = 0; i < 8; i++) {
151 LOG_INFO(
"TitleScreen",
"Palette 5: added 8 transparent/black colors");
154 if (pal_group.sprites_aux1.size() > 1) {
155 auto src = pal_group.sprites_aux1[1];
156 for (
int pal = 0; pal < 2; pal++) {
158 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
167 "Palette %d: added %zu colors from sprites_aux1[1]", 6 + pal,
172 LOG_INFO(
"TitleScreen",
"Built composite palette: %zu colors (should be 64)",
181 return absl::OkStatus();
201 uint8_t staticgfx[16] = {0};
204 constexpr int kTitleScreenTilesGFX = 0x064207;
205 constexpr int kTitleScreenSpritesGFX = 0x06420C;
208 rom->
ReadByte(kTitleScreenTilesGFX));
210 rom->
ReadByte(kTitleScreenSpritesGFX));
212 LOG_INFO(
"TitleScreen",
"GFX group indices: tiles=%d, sprites=%d",
213 tiles_gfx_index, sprites_gfx_index);
219 uint32_t main_gfx_table =
SnesToPc(gfx_groups_snes);
221 LOG_INFO(
"TitleScreen",
"GFX groups table: SNES=0x%04X, PC=0x%06X",
222 gfx_groups_snes, main_gfx_table);
225 int main_gfx_offset = main_gfx_table + (tiles_gfx_index * 8);
226 for (
int i = 0; i < 8; i++) {
233 int sprite_gfx_table = main_gfx_table + (37 * 8) + (82 * 4);
234 int sprite_gfx_offset = sprite_gfx_table + (sprites_gfx_index * 4);
236 staticgfx[8] = 115 + 0;
238 staticgfx[9] = 115 + sprite3;
239 staticgfx[10] = 115 + 6;
240 staticgfx[11] = 115 + 7;
242 staticgfx[12] = 115 + sprite0;
250 return absl::FailedPreconditionError(
"GameData not set");
255 LOG_INFO(
"TitleScreen",
"Graphics buffer size: %zu bytes", gfx_buffer.size());
256 LOG_INFO(
"TitleScreen",
"Tiles8 bitmap size: %zu bytes", tiles8_data.size());
259 LOG_INFO(
"TitleScreen",
"Loading 16 graphics sheets:");
260 for (
int i = 0; i < 16; i++) {
261 LOG_INFO(
"TitleScreen",
" staticgfx[%d] = %d", i, staticgfx[i]);
264 for (
int i = 0; i < 16; i++) {
265 int sheet_id = staticgfx[i];
268 if (sheet_id > 222) {
271 "Sheet %d: Invalid sheet_id=%d (max 222), using sheet 0 instead", i,
276 int source_offset = sheet_id * 0x1000;
277 int dest_offset = i * 0x1000;
279 if (source_offset + 0x1000 <= gfx_buffer.size() &&
280 dest_offset + 0x1000 <= tiles8_data.size()) {
281 std::copy(gfx_buffer.begin() + source_offset,
282 gfx_buffer.begin() + source_offset + 0x1000,
283 tiles8_data.begin() + dest_offset);
287 "Sheet %d (ID %d): Sample pixels: %02X %02X %02X %02X", i,
288 sheet_id, tiles8_data[dest_offset], tiles8_data[dest_offset + 1],
289 tiles8_data[dest_offset + 2], tiles8_data[dest_offset + 3]);
292 "Sheet %d (ID %d): out of bounds! source=%d, dest=%d, "
294 i, sheet_id, source_offset, dest_offset, gfx_buffer.size());
301 LOG_INFO(
"TitleScreen",
"Applied palette to tiles8_bitmap: %zu colors",
306 " Palette colors 0-7: %04X %04X %04X %04X %04X %04X %04X %04X",
320 return absl::OkStatus();
330 uint32_t snes_addr = (bank_byte << 16) | (high_byte << 8) | low_byte;
331 uint32_t pc_addr =
SnesToPc(snes_addr);
333 LOG_INFO(
"TitleScreen",
"Title screen pointer: SNES=0x%06X, PC=0x%06X",
337 for (
int i = 0; i < 1024; i++) {
343 if (pc_addr >= 0x108000 && pc_addr <= 0x10FFFF) {
344 LOG_INFO(
"TitleScreen",
"Detected ZScream expanded format");
354 LOG_INFO(
"TitleScreen",
"BG1 Header: dest=0x%04X, length=0x%04X", bg1_dest,
358 for (
int i = 0; i < 1024; i++) {
370 LOG_INFO(
"TitleScreen",
"BG2 Header: dest=0x%04X, length=0x%04X", bg2_dest,
374 for (
int i = 0; i < 1024; i++) {
381 "Loaded 2048 tilemap entries from ZScream expanded format");
387 LOG_INFO(
"TitleScreen",
"Using vanilla DMA format (EXPERIMENTAL)");
390 int total_entries = 0;
394 while (pos < rom->size() && blocks_read < 20) {
400 if (dest_addr == 0xFFFF || (dest_addr & 0xFF) == 0xFF) {
401 LOG_INFO(
"TitleScreen",
"Found DMA terminator at pos=0x%06X", pos - 2);
409 bool increment64 = (length_flags & 0x8000) == 0x8000;
410 bool fixsource = (length_flags & 0x4000) == 0x4000;
411 int length = (length_flags & 0x0FFF);
413 LOG_INFO(
"TitleScreen",
"Block %d: dest=0x%04X, len=%d, inc64=%d, fix=%d",
414 blocks_read, dest_addr, length, increment64, fixsource);
416 int tile_count = (length / 2) + 1;
417 int source_start = pos;
420 for (
int j = 0; j < tile_count; j++) {
424 if (dest_addr >= 0x1000 && dest_addr < 0x1400) {
426 int index = (dest_addr - 0x1000) / 2;
431 }
else if (dest_addr < 0x0800) {
433 int index = dest_addr / 2;
455 pos = source_start + 2;
462 "Loaded %d tilemap entries from %d DMA blocks (may be incorrect)",
463 total_entries, blocks_read);
497 return absl::OkStatus();
506 for (
int tile_y = 0; tile_y < 32; tile_y++) {
507 for (
int tile_x = 0; tile_x < 32; tile_x++) {
508 int tilemap_index = tile_y * 32 + tile_x;
512 int tile_id = tile_word & 0x3FF;
513 int palette = (tile_word >> 10) & 0x07;
514 bool h_flip = (tile_word & 0x4000) != 0;
515 bool v_flip = (tile_word & 0x8000) != 0;
520 "BG1: Suspicious tile_id=%d at (%d,%d), word=0x%04X", tile_id,
521 tile_x, tile_y, tile_word);
528 int sheet_index = tile_id / 256;
529 int tile_in_sheet = tile_id % 256;
530 int src_tile_x = (tile_in_sheet % 16) * 8;
531 int src_tile_y = (sheet_index * 32) + ((tile_in_sheet / 16) * 8);
534 for (
int py = 0; py < 8; py++) {
535 for (
int px = 0; px < 8; px++) {
537 int src_px = h_flip ? (7 - px) : px;
538 int src_py = v_flip ? (7 - py) : py;
541 int src_x = src_tile_x + src_px;
542 int src_y = src_tile_y + src_py;
546 int dest_x = tile_x * 8 + px;
547 int dest_y = tile_y * 8 + py;
548 int dest_pos = dest_y * 256 + dest_x;
553 if (src_pos < tile8_bitmap_data.size() &&
554 dest_pos < bg1_data.size()) {
555 uint8_t pixel_value = tile8_bitmap_data[src_pos];
558 bg1_data[dest_pos] = pixel_value;
572 return absl::OkStatus();
581 for (
int tile_y = 0; tile_y < 32; tile_y++) {
582 for (
int tile_x = 0; tile_x < 32; tile_x++) {
583 int tilemap_index = tile_y * 32 + tile_x;
587 int tile_id = tile_word & 0x3FF;
588 int palette = (tile_word >> 10) & 0x07;
589 bool h_flip = (tile_word & 0x4000) != 0;
590 bool v_flip = (tile_word & 0x8000) != 0;
596 int sheet_index = tile_id / 256;
597 int tile_in_sheet = tile_id % 256;
598 int src_tile_x = (tile_in_sheet % 16) * 8;
599 int src_tile_y = (sheet_index * 32) + ((tile_in_sheet / 16) * 8);
602 for (
int py = 0; py < 8; py++) {
603 for (
int px = 0; px < 8; px++) {
605 int src_px = h_flip ? (7 - px) : px;
606 int src_py = v_flip ? (7 - py) : py;
609 int src_x = src_tile_x + src_px;
610 int src_y = src_tile_y + src_py;
614 int dest_x = tile_x * 8 + px;
615 int dest_y = tile_y * 8 + py;
616 int dest_pos = dest_y * 256 + dest_x;
621 if (src_pos < tile8_bitmap_data.size() &&
622 dest_pos < bg2_data.size()) {
623 uint8_t pixel_value = tile8_bitmap_data[src_pos];
626 bg2_data[dest_pos] = pixel_value;
640 return absl::OkStatus();
645 return absl::InvalidArgumentError(
"ROM is not loaded");
650 std::vector<uint8_t> compressed_data;
653 auto WriteWord = [&compressed_data](uint16_t value) {
654 compressed_data.push_back(value & 0xFF);
655 compressed_data.push_back((value >> 8) & 0xFF);
659 uint16_t bg2_dest = 0x0000;
660 for (
int i = 0; i < 1024; i++) {
663 WriteWord(bg2_dest + i);
668 while (i + run_length < 1024 &&
674 uint16_t length_flags = (run_length - 1) * 2;
675 if (run_length > 1) {
676 length_flags |= 0x4000;
678 WriteWord(length_flags);
681 WriteWord(tile_value);
688 uint16_t bg1_dest = 0x1000;
689 for (
int i = 0; i < 1024; i++) {
692 WriteWord(bg1_dest + i);
697 while (i + run_length < 1024 &&
703 uint16_t length_flags = (run_length - 1) * 2;
704 if (run_length > 1) {
705 length_flags |= 0x4000;
707 WriteWord(length_flags);
710 WriteWord(tile_value);
717 compressed_data.push_back(0x80);
724 int pos = (byte2 << 16) + (byte1 << 8) + byte0;
728 for (
size_t i = 0; i < compressed_data.size(); i++) {
732 return absl::OkStatus();
741 std::fill(composite_data.begin(), composite_data.end(), 0);
745 for (
int i = 0; i < 256 * 256; i++) {
746 composite_data[i] = bg2_data[i];
752 for (
int i = 0; i < 256 * 256; i++) {
753 uint8_t pixel = bg1_data[i];
756 if ((pixel & 0x07) != 0) {
757 composite_data[i] = pixel;
769 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status WriteByte(int addr, uint8_t value)
absl::StatusOr< uint8_t > ReadByte(int offset) const
absl::StatusOr< uint16_t > ReadWord(int offset) const
void QueueTextureCommand(TextureCommandType type, Bitmap *bitmap)
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
const std::vector< uint8_t > & vector() const
void UpdateSurfacePixels()
Update SDL surface with current pixel data from data_ vector Call this after modifying pixel data via...
BitmapMetadata & metadata()
void set_active(bool active)
void SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
std::vector< uint8_t > & mutable_data()
void AddColor(const SnesColor &color)
std::array< uint16_t, 0x1000 > tiles_bg2_buffer_
gfx::OamTile oam_data_[10]
gfx::Bitmap tiles8_bitmap_
gfx::SnesPalette palette_
gfx::Bitmap title_composite_bitmap_
gfx::Tilemap tile16_blockset_
gfx::Bitmap tiles_bg2_bitmap_
std::array< uint16_t, 0x1000 > tiles_bg1_buffer_
absl::Status Create(Rom *rom, GameData *game_data=nullptr)
Initialize and load title screen data from ROM.
absl::Status RenderCompositeLayer(bool show_bg1, bool show_bg2)
Render composite layer with BG1 on top of BG2 with transparency.
absl::Status BuildTileset(Rom *rom)
Build the tile16 blockset from ROM graphics.
absl::Status Save(Rom *rom)
absl::Status RenderBG2Layer()
Render BG2 tilemap into bitmap pixels Converts tile IDs from tiles_bg2_buffer_ into pixel data.
gfx::Bitmap oam_bg_bitmap_
gfx::Bitmap tiles_bg1_bitmap_
absl::Status RenderBG1Layer()
Render BG1 tilemap into bitmap pixels Converts tile IDs from tiles_bg1_buffer_ into pixel data.
absl::Status LoadTitleScreen(Rom *rom)
Load title screen tilemap data from ROM.
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
constexpr int kGfxGroupsPointer
uint32_t SnesToPc(uint32_t addr) noexcept
#define RETURN_IF_ERROR(expr)
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Pair map_size
Size of tilemap in tiles.
std::vector< std::array< gfx::TileInfo, 4 > > tile_info
Tile metadata (4 tiles per 16x16)
gfx::PaletteGroupMap palette_groups
std::vector< uint8_t > graphics_buffer