16 return absl::InvalidArgumentError(
"ROM is not loaded");
72 return absl::FailedPreconditionError(
"GameData not set for TitleScreen");
80 if (pal_group.overworld_main.size() > 5) {
81 const auto& src = pal_group.overworld_main[5];
83 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
93 "Palette 0: added %zu colors from overworld_main[5]", added);
97 if (pal_group.overworld_animated.size() > 0) {
98 const auto& src = pal_group.overworld_animated[0];
100 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
109 "Palette 1: added %zu colors from overworld_animated[0]", added);
113 if (pal_group.overworld_aux.size() > 3) {
114 auto src = pal_group.overworld_aux[3];
115 for (
int pal = 0; pal < 2; pal++) {
117 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
126 "Palette %d: added %zu colors from overworld_aux[3]", 2 + pal,
132 if (pal_group.hud.size() > 0) {
133 auto src = pal_group.hud.palette(0);
135 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
143 LOG_INFO(
"TitleScreen",
"Palette 4: added %zu colors from hud[0]", added);
147 for (
int i = 0; i < 8; i++) {
150 LOG_INFO(
"TitleScreen",
"Palette 5: added 8 transparent/black colors");
153 if (pal_group.sprites_aux1.size() > 1) {
154 auto src = pal_group.sprites_aux1[1];
155 for (
int pal = 0; pal < 2; pal++) {
157 for (
size_t i = 0; i < 8 && i < src.size(); i++) {
166 "Palette %d: added %zu colors from sprites_aux1[1]", 6 + pal,
171 LOG_INFO(
"TitleScreen",
"Built composite palette: %zu colors (should be 64)",
180 return absl::OkStatus();
186 uint8_t staticgfx[16] = {0};
189 constexpr int kTitleScreenTilesGFX = 0x064207;
190 constexpr int kTitleScreenSpritesGFX = 0x06420C;
193 rom->
ReadByte(kTitleScreenTilesGFX));
195 rom->
ReadByte(kTitleScreenSpritesGFX));
197 LOG_INFO(
"TitleScreen",
"GFX group indices: tiles=%d, sprites=%d",
198 tiles_gfx_index, sprites_gfx_index);
204 uint32_t main_gfx_table =
SnesToPc(gfx_groups_snes);
206 LOG_INFO(
"TitleScreen",
"GFX groups table: SNES=0x%04X, PC=0x%06X",
207 gfx_groups_snes, main_gfx_table);
210 int main_gfx_offset = main_gfx_table + (tiles_gfx_index * 8);
211 for (
int i = 0; i < 8; i++) {
218 int sprite_gfx_table = main_gfx_table + (37 * 8) + (82 * 4);
219 int sprite_gfx_offset = sprite_gfx_table + (sprites_gfx_index * 4);
221 staticgfx[8] = 115 + 0;
223 staticgfx[9] = 115 + sprite3;
224 staticgfx[10] = 115 + 6;
225 staticgfx[11] = 115 + 7;
227 staticgfx[12] = 115 + sprite0;
235 return absl::FailedPreconditionError(
"GameData not set");
240 LOG_INFO(
"TitleScreen",
"Graphics buffer size: %zu bytes", gfx_buffer.size());
241 LOG_INFO(
"TitleScreen",
"Tiles8 bitmap size: %zu bytes", tiles8_data.size());
244 LOG_INFO(
"TitleScreen",
"Loading 16 graphics sheets:");
245 for (
int i = 0; i < 16; i++) {
246 LOG_INFO(
"TitleScreen",
" staticgfx[%d] = %d", i, staticgfx[i]);
249 for (
int i = 0; i < 16; i++) {
250 int sheet_id = staticgfx[i];
253 if (sheet_id > 222) {
256 "Sheet %d: Invalid sheet_id=%d (max 222), using sheet 0 instead", i,
261 int source_offset = sheet_id * 0x1000;
262 int dest_offset = i * 0x1000;
264 if (source_offset + 0x1000 <= gfx_buffer.size() &&
265 dest_offset + 0x1000 <= tiles8_data.size()) {
266 std::copy(gfx_buffer.begin() + source_offset,
267 gfx_buffer.begin() + source_offset + 0x1000,
268 tiles8_data.begin() + dest_offset);
272 "Sheet %d (ID %d): Sample pixels: %02X %02X %02X %02X", i,
273 sheet_id, tiles8_data[dest_offset], tiles8_data[dest_offset + 1],
274 tiles8_data[dest_offset + 2], tiles8_data[dest_offset + 3]);
277 "Sheet %d (ID %d): out of bounds! source=%d, dest=%d, "
279 i, sheet_id, source_offset, dest_offset, gfx_buffer.size());
286 LOG_INFO(
"TitleScreen",
"Applied palette to tiles8_bitmap: %zu colors",
291 " Palette colors 0-7: %04X %04X %04X %04X %04X %04X %04X %04X",
305 return absl::OkStatus();
315 uint32_t snes_addr = (bank_byte << 16) | (high_byte << 8) | low_byte;
316 uint32_t pc_addr =
SnesToPc(snes_addr);
318 LOG_INFO(
"TitleScreen",
"Title screen pointer: SNES=0x%06X, PC=0x%06X",
322 for (
int i = 0; i < 1024; i++) {
328 if (pc_addr >= 0x108000 && pc_addr <= 0x10FFFF) {
329 LOG_INFO(
"TitleScreen",
"Detected ZScream expanded format");
339 LOG_INFO(
"TitleScreen",
"BG1 Header: dest=0x%04X, length=0x%04X", bg1_dest,
343 for (
int i = 0; i < 1024; i++) {
355 LOG_INFO(
"TitleScreen",
"BG2 Header: dest=0x%04X, length=0x%04X", bg2_dest,
359 for (
int i = 0; i < 1024; i++) {
366 "Loaded 2048 tilemap entries from ZScream expanded format");
372 LOG_INFO(
"TitleScreen",
"Using vanilla DMA format (EXPERIMENTAL)");
375 int total_entries = 0;
379 while (pos < rom->size() && blocks_read < 20) {
385 if (dest_addr == 0xFFFF || (dest_addr & 0xFF) == 0xFF) {
386 LOG_INFO(
"TitleScreen",
"Found DMA terminator at pos=0x%06X", pos - 2);
394 bool increment64 = (length_flags & 0x8000) == 0x8000;
395 bool fixsource = (length_flags & 0x4000) == 0x4000;
396 int length = (length_flags & 0x0FFF);
398 LOG_INFO(
"TitleScreen",
"Block %d: dest=0x%04X, len=%d, inc64=%d, fix=%d",
399 blocks_read, dest_addr, length, increment64, fixsource);
401 int tile_count = (length / 2) + 1;
402 int source_start = pos;
405 for (
int j = 0; j < tile_count; j++) {
409 if (dest_addr >= 0x1000 && dest_addr < 0x1400) {
411 int index = (dest_addr - 0x1000) / 2;
416 }
else if (dest_addr < 0x0800) {
418 int index = dest_addr / 2;
440 pos = source_start + 2;
447 "Loaded %d tilemap entries from %d DMA blocks (may be incorrect)",
448 total_entries, blocks_read);
482 return absl::OkStatus();
491 for (
int tile_y = 0; tile_y < 32; tile_y++) {
492 for (
int tile_x = 0; tile_x < 32; tile_x++) {
493 int tilemap_index = tile_y * 32 + tile_x;
497 int tile_id = tile_word & 0x3FF;
498 int palette = (tile_word >> 10) & 0x07;
499 bool h_flip = (tile_word & 0x4000) != 0;
500 bool v_flip = (tile_word & 0x8000) != 0;
505 "BG1: Suspicious tile_id=%d at (%d,%d), word=0x%04X", tile_id,
506 tile_x, tile_y, tile_word);
513 int sheet_index = tile_id / 256;
514 int tile_in_sheet = tile_id % 256;
515 int src_tile_x = (tile_in_sheet % 16) * 8;
516 int src_tile_y = (sheet_index * 32) + ((tile_in_sheet / 16) * 8);
519 for (
int py = 0; py < 8; py++) {
520 for (
int px = 0; px < 8; px++) {
522 int src_px = h_flip ? (7 - px) : px;
523 int src_py = v_flip ? (7 - py) : py;
526 int src_x = src_tile_x + src_px;
527 int src_y = src_tile_y + src_py;
531 int dest_x = tile_x * 8 + px;
532 int dest_y = tile_y * 8 + py;
533 int dest_pos = dest_y * 256 + dest_x;
538 if (src_pos < tile8_bitmap_data.size() &&
539 dest_pos < bg1_data.size()) {
540 uint8_t pixel_value = tile8_bitmap_data[src_pos];
543 bg1_data[dest_pos] = pixel_value;
557 return absl::OkStatus();
566 for (
int tile_y = 0; tile_y < 32; tile_y++) {
567 for (
int tile_x = 0; tile_x < 32; tile_x++) {
568 int tilemap_index = tile_y * 32 + tile_x;
572 int tile_id = tile_word & 0x3FF;
573 int palette = (tile_word >> 10) & 0x07;
574 bool h_flip = (tile_word & 0x4000) != 0;
575 bool v_flip = (tile_word & 0x8000) != 0;
581 int sheet_index = tile_id / 256;
582 int tile_in_sheet = tile_id % 256;
583 int src_tile_x = (tile_in_sheet % 16) * 8;
584 int src_tile_y = (sheet_index * 32) + ((tile_in_sheet / 16) * 8);
587 for (
int py = 0; py < 8; py++) {
588 for (
int px = 0; px < 8; px++) {
590 int src_px = h_flip ? (7 - px) : px;
591 int src_py = v_flip ? (7 - py) : py;
594 int src_x = src_tile_x + src_px;
595 int src_y = src_tile_y + src_py;
599 int dest_x = tile_x * 8 + px;
600 int dest_y = tile_y * 8 + py;
601 int dest_pos = dest_y * 256 + dest_x;
606 if (src_pos < tile8_bitmap_data.size() &&
607 dest_pos < bg2_data.size()) {
608 uint8_t pixel_value = tile8_bitmap_data[src_pos];
611 bg2_data[dest_pos] = pixel_value;
625 return absl::OkStatus();
630 return absl::InvalidArgumentError(
"ROM is not loaded");
635 std::vector<uint8_t> compressed_data;
638 auto WriteWord = [&compressed_data](uint16_t value) {
639 compressed_data.push_back(value & 0xFF);
640 compressed_data.push_back((value >> 8) & 0xFF);
644 uint16_t bg2_dest = 0x0000;
645 for (
int i = 0; i < 1024; i++) {
648 WriteWord(bg2_dest + i);
653 while (i + run_length < 1024 &&
659 uint16_t length_flags = (run_length - 1) * 2;
660 if (run_length > 1) {
661 length_flags |= 0x4000;
663 WriteWord(length_flags);
666 WriteWord(tile_value);
673 uint16_t bg1_dest = 0x1000;
674 for (
int i = 0; i < 1024; i++) {
677 WriteWord(bg1_dest + i);
682 while (i + run_length < 1024 &&
688 uint16_t length_flags = (run_length - 1) * 2;
689 if (run_length > 1) {
690 length_flags |= 0x4000;
692 WriteWord(length_flags);
695 WriteWord(tile_value);
702 compressed_data.push_back(0x80);
709 int pos = (byte2 << 16) + (byte1 << 8) + byte0;
713 for (
size_t i = 0; i < compressed_data.size(); i++) {
717 return absl::OkStatus();
726 std::fill(composite_data.begin(), composite_data.end(), 0);
730 for (
int i = 0; i < 256 * 256; i++) {
731 composite_data[i] = bg2_data[i];
737 for (
int i = 0; i < 256 * 256; i++) {
738 uint8_t pixel = bg1_data[i];
741 if ((pixel & 0x07) != 0) {
742 composite_data[i] = pixel;
754 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< uint16_t > ReadWord(int offset)
absl::StatusOr< uint8_t > ReadByte(int offset)
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::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 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)
gfx::PaletteGroupMap palette_groups
std::vector< uint8_t > graphics_buffer