15 return absl::InvalidArgumentError(
"ROM is not loaded");
74 if (pal_group.overworld_main.size() > 5) {
75 const auto& src = pal_group.overworld_main[5];
77 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
86 LOG_INFO(
"TitleScreen",
"Palette 0: added %zu colors from overworld_main[5]", added);
90 if (pal_group.overworld_animated.size() > 0) {
91 const auto& src = pal_group.overworld_animated[0];
93 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
101 LOG_INFO(
"TitleScreen",
"Palette 1: added %zu colors from overworld_animated[0]", added);
105 if (pal_group.overworld_aux.size() > 3) {
106 auto src = pal_group.overworld_aux[3];
107 for (
int pal = 0; pal < 2; pal++) {
109 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
117 LOG_INFO(
"TitleScreen",
"Palette %d: added %zu colors from overworld_aux[3]", 2+pal, added);
122 if (pal_group.hud.size() > 0) {
123 auto src = pal_group.hud.palette(0);
125 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
133 LOG_INFO(
"TitleScreen",
"Palette 4: added %zu colors from hud[0]", added);
137 for (
int i = 0; i < 8; i++) {
140 LOG_INFO(
"TitleScreen",
"Palette 5: added 8 transparent/black colors");
143 if (pal_group.sprites_aux1.size() > 1) {
144 auto src = pal_group.sprites_aux1[1];
145 for (
int pal = 0; pal < 2; pal++) {
147 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
155 LOG_INFO(
"TitleScreen",
"Palette %d: added %zu colors from sprites_aux1[1]", 6+pal, added);
167 return absl::OkStatus();
173 uint8_t staticgfx[16] = {0};
176 constexpr int kTitleScreenTilesGFX = 0x064207;
177 constexpr int kTitleScreenSpritesGFX = 0x06420C;
182 LOG_INFO(
"TitleScreen",
"GFX group indices: tiles=%d, sprites=%d",
183 tiles_gfx_index, sprites_gfx_index);
189 uint32_t main_gfx_table =
SnesToPc(gfx_groups_snes);
191 LOG_INFO(
"TitleScreen",
"GFX groups table: SNES=0x%04X, PC=0x%06X",
192 gfx_groups_snes, main_gfx_table);
195 int main_gfx_offset = main_gfx_table + (tiles_gfx_index * 8);
196 for (
int i = 0; i < 8; i++) {
203 int sprite_gfx_table = main_gfx_table + (37 * 8) + (82 * 4);
204 int sprite_gfx_offset = sprite_gfx_table + (sprites_gfx_index * 4);
206 staticgfx[8] = 115 + 0;
208 staticgfx[9] = 115 + sprite3;
209 staticgfx[10] = 115 + 6;
210 staticgfx[11] = 115 + 7;
212 staticgfx[12] = 115 + sprite0;
222 LOG_INFO(
"TitleScreen",
"Graphics buffer size: %zu bytes", gfx_buffer.size());
223 LOG_INFO(
"TitleScreen",
"Tiles8 bitmap size: %zu bytes", tiles8_data.size());
226 LOG_INFO(
"TitleScreen",
"Loading 16 graphics sheets:");
227 for (
int i = 0; i < 16; i++) {
228 LOG_INFO(
"TitleScreen",
" staticgfx[%d] = %d", i, staticgfx[i]);
231 for (
int i = 0; i < 16; i++) {
232 int sheet_id = staticgfx[i];
235 if (sheet_id > 222) {
236 LOG_ERROR(
"TitleScreen",
"Sheet %d: Invalid sheet_id=%d (max 222), using sheet 0 instead",
241 int source_offset = sheet_id * 0x1000;
242 int dest_offset = i * 0x1000;
244 if (source_offset + 0x1000 <= gfx_buffer.size() &&
245 dest_offset + 0x1000 <= tiles8_data.size()) {
247 std::copy(gfx_buffer.begin() + source_offset,
248 gfx_buffer.begin() + source_offset + 0x1000,
249 tiles8_data.begin() + dest_offset);
252 LOG_INFO(
"TitleScreen",
"Sheet %d (ID %d): Sample pixels: %02X %02X %02X %02X",
254 tiles8_data[dest_offset], tiles8_data[dest_offset+1],
255 tiles8_data[dest_offset+2], tiles8_data[dest_offset+3]);
257 LOG_ERROR(
"TitleScreen",
"Sheet %d (ID %d): out of bounds! source=%d, dest=%d, buffer_size=%zu",
258 i, sheet_id, source_offset, dest_offset, gfx_buffer.size());
268 LOG_INFO(
"TitleScreen",
" Palette colors 0-7: %04X %04X %04X %04X %04X %04X %04X %04X",
281 return absl::OkStatus();
291 uint32_t snes_addr = (bank_byte << 16) | (high_byte << 8) | low_byte;
292 uint32_t pc_addr =
SnesToPc(snes_addr);
294 LOG_INFO(
"TitleScreen",
"Title screen pointer: SNES=0x%06X, PC=0x%06X", snes_addr, pc_addr);
297 for (
int i = 0; i < 1024; i++) {
303 if (pc_addr >= 0x108000 && pc_addr <= 0x10FFFF) {
304 LOG_INFO(
"TitleScreen",
"Detected ZScream expanded format");
314 LOG_INFO(
"TitleScreen",
"BG1 Header: dest=0x%04X, length=0x%04X", bg1_dest, bg1_length);
317 for (
int i = 0; i < 1024; i++) {
329 LOG_INFO(
"TitleScreen",
"BG2 Header: dest=0x%04X, length=0x%04X", bg2_dest, bg2_length);
332 for (
int i = 0; i < 1024; i++) {
338 LOG_INFO(
"TitleScreen",
"Loaded 2048 tilemap entries from ZScream expanded format");
344 LOG_INFO(
"TitleScreen",
"Using vanilla DMA format (EXPERIMENTAL)");
347 int total_entries = 0;
351 while (pos < rom->size() && blocks_read < 20) {
357 if (dest_addr == 0xFFFF || (dest_addr & 0xFF) == 0xFF) {
358 LOG_INFO(
"TitleScreen",
"Found DMA terminator at pos=0x%06X", pos - 2);
366 bool increment64 = (length_flags & 0x8000) == 0x8000;
367 bool fixsource = (length_flags & 0x4000) == 0x4000;
368 int length = (length_flags & 0x0FFF);
370 LOG_INFO(
"TitleScreen",
"Block %d: dest=0x%04X, len=%d, inc64=%d, fix=%d",
371 blocks_read, dest_addr, length, increment64, fixsource);
373 int tile_count = (length / 2) + 1;
374 int source_start = pos;
377 for (
int j = 0; j < tile_count; j++) {
381 if (dest_addr >= 0x1000 && dest_addr < 0x1400) {
383 int index = (dest_addr - 0x1000) / 2;
388 }
else if (dest_addr < 0x0800) {
390 int index = dest_addr / 2;
412 pos = source_start + 2;
418 LOG_INFO(
"TitleScreen",
"Loaded %d tilemap entries from %d DMA blocks (may be incorrect)",
419 total_entries, blocks_read);
453 return absl::OkStatus();
462 for (
int tile_y = 0; tile_y < 32; tile_y++) {
463 for (
int tile_x = 0; tile_x < 32; tile_x++) {
464 int tilemap_index = tile_y * 32 + tile_x;
468 int tile_id = tile_word & 0x3FF;
469 int palette = (tile_word >> 10) & 0x07;
470 bool h_flip = (tile_word & 0x4000) != 0;
471 bool v_flip = (tile_word & 0x8000) != 0;
475 LOG_WARN(
"TitleScreen",
"BG1: Suspicious tile_id=%d at (%d,%d), word=0x%04X",
476 tile_id, tile_x, tile_y, tile_word);
482 int sheet_index = tile_id / 256;
483 int tile_in_sheet = tile_id % 256;
484 int src_tile_x = (tile_in_sheet % 16) * 8;
485 int src_tile_y = (sheet_index * 32) + ((tile_in_sheet / 16) * 8);
488 for (
int py = 0; py < 8; py++) {
489 for (
int px = 0; px < 8; px++) {
491 int src_px = h_flip ? (7 - px) : px;
492 int src_py = v_flip ? (7 - py) : py;
495 int src_x = src_tile_x + src_px;
496 int src_y = src_tile_y + src_py;
497 int src_pos = src_y * 128 + src_x;
499 int dest_x = tile_x * 8 + px;
500 int dest_y = tile_y * 8 + py;
501 int dest_pos = dest_y * 256 + dest_x;
505 if (src_pos < tile8_bitmap_data.size() && dest_pos < bg1_data.size()) {
506 uint8_t pixel_value = tile8_bitmap_data[src_pos];
509 bg1_data[dest_pos] = pixel_value;
523 return absl::OkStatus();
532 for (
int tile_y = 0; tile_y < 32; tile_y++) {
533 for (
int tile_x = 0; tile_x < 32; tile_x++) {
534 int tilemap_index = tile_y * 32 + tile_x;
538 int tile_id = tile_word & 0x3FF;
539 int palette = (tile_word >> 10) & 0x07;
540 bool h_flip = (tile_word & 0x4000) != 0;
541 bool v_flip = (tile_word & 0x8000) != 0;
546 int sheet_index = tile_id / 256;
547 int tile_in_sheet = tile_id % 256;
548 int src_tile_x = (tile_in_sheet % 16) * 8;
549 int src_tile_y = (sheet_index * 32) + ((tile_in_sheet / 16) * 8);
552 for (
int py = 0; py < 8; py++) {
553 for (
int px = 0; px < 8; px++) {
555 int src_px = h_flip ? (7 - px) : px;
556 int src_py = v_flip ? (7 - py) : py;
559 int src_x = src_tile_x + src_px;
560 int src_y = src_tile_y + src_py;
561 int src_pos = src_y * 128 + src_x;
563 int dest_x = tile_x * 8 + px;
564 int dest_y = tile_y * 8 + py;
565 int dest_pos = dest_y * 256 + dest_x;
569 if (src_pos < tile8_bitmap_data.size() && dest_pos < bg2_data.size()) {
570 uint8_t pixel_value = tile8_bitmap_data[src_pos];
573 bg2_data[dest_pos] = pixel_value;
587 return absl::OkStatus();
592 return absl::InvalidArgumentError(
"ROM is not loaded");
597 std::vector<uint8_t> compressed_data;
600 auto WriteWord = [&compressed_data](uint16_t value) {
601 compressed_data.push_back(value & 0xFF);
602 compressed_data.push_back((value >> 8) & 0xFF);
606 uint16_t bg2_dest = 0x0000;
607 for (
int i = 0; i < 1024; i++) {
610 WriteWord(bg2_dest + i);
615 while (i + run_length < 1024 &&
tiles_bg2_buffer_[i + run_length] == tile_value) {
620 uint16_t length_flags = (run_length - 1) * 2;
621 if (run_length > 1) {
622 length_flags |= 0x4000;
624 WriteWord(length_flags);
627 WriteWord(tile_value);
634 uint16_t bg1_dest = 0x1000;
635 for (
int i = 0; i < 1024; i++) {
638 WriteWord(bg1_dest + i);
643 while (i + run_length < 1024 &&
tiles_bg1_buffer_[i + run_length] == tile_value) {
648 uint16_t length_flags = (run_length - 1) * 2;
649 if (run_length > 1) {
650 length_flags |= 0x4000;
652 WriteWord(length_flags);
655 WriteWord(tile_value);
662 compressed_data.push_back(0x80);
669 int pos = (byte2 << 16) + (byte1 << 8) + byte0;
673 for (
size_t i = 0; i < compressed_data.size(); i++) {
677 return absl::OkStatus();
686 std::fill(composite_data.begin(), composite_data.end(), 0);
690 for (
int i = 0; i < 256 * 256; i++) {
691 composite_data[i] = bg2_data[i];
697 for (
int i = 0; i < 256 * 256; i++) {
698 uint8_t pixel = bg1_data[i];
701 if ((pixel & 0x07) != 0) {
702 composite_data[i] = pixel;
714 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data.
auto palette_group() const
absl::Status WriteByte(int addr, uint8_t value)
absl::StatusOr< uint16_t > ReadWord(int offset)
absl::StatusOr< uint8_t > ReadByte(int offset)
auto graphics_buffer() 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.
std::vector< uint8_t > & mutable_data()
void AddColor(const SnesColor &color)
std::array< uint16_t, 0x1000 > tiles_bg2_buffer_
gfx::Bitmap tiles8_bitmap_
gfx::SnesPalette palette_
gfx::Bitmap title_composite_bitmap_
gfx::Bitmap tiles_bg2_bitmap_
std::array< uint16_t, 0x1000 > tiles_bg1_buffer_
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 Create(Rom *rom)
Initialize and load title screen data from ROM.
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 RETURN_IF_ERROR(expression)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
constexpr int kGfxGroupsPointer
Main namespace for the application.
uint32_t SnesToPc(uint32_t addr) noexcept