backend-infra-engineer: Release v0.3.9-hotfix7 snapshot

This commit is contained in:
scawful
2025-11-23 13:37:10 -05:00
parent c8289bffda
commit 2934c82b75
202 changed files with 34914 additions and 845 deletions

View File

@@ -1235,6 +1235,8 @@ absl::Status Tile16Editor::LoadTile8() {
std::vector<uint8_t> tile_data(64); // 8x8 = 64 pixels
// Extract tile data from the main graphics bitmap
// Keep raw 4-bit pixel values (0-15); palette offset is applied in
// RefreshAllPalettes() via SetPaletteWithTransparent
for (int py = 0; py < 8; ++py) {
for (int px = 0; px < 8; ++px) {
int src_x = tile_x * 8 + px;
@@ -1246,10 +1248,9 @@ absl::Status Tile16Editor::LoadTile8() {
dst_index < 64) {
uint8_t pixel_value = current_gfx_bmp_.data()[src_index];
// Apply normalization based on settings
if (auto_normalize_pixels_) {
pixel_value &= palette_normalization_mask_;
}
// Normalize to 4-bit range for proper SNES 4bpp graphics
// The actual palette offset is applied during palette refresh
pixel_value &= 0x0F;
tile_data[dst_index] = pixel_value;
}
@@ -1324,7 +1325,8 @@ absl::Status Tile16Editor::SetCurrentTile(int tile_id) {
tile_data.resize(kTile16PixelCount);
// Manual extraction without the buggy offset increment
// Manual extraction - preserve pixel values for palette-based rendering
// The 4-bit mask is applied after extraction to normalize values
for (int ty = 0; ty < kTile16Size; ty++) {
for (int tx = 0; tx < kTile16Size; tx++) {
int pixel_x = tile_x + tx;
@@ -1335,36 +1337,59 @@ absl::Status Tile16Editor::SetCurrentTile(int tile_id) {
if (src_index < static_cast<int>(tile16_blockset_->atlas.size()) &&
dst_index < static_cast<int>(tile_data.size())) {
uint8_t pixel_value = tile16_blockset_->atlas.data()[src_index];
// Normalize pixel values to valid palette range
pixel_value &= 0x0F; // Keep only lower 4 bits for palette index
// Normalize pixel values to 4-bit range for sub-palette indexing
// The actual palette offset is applied via SetPaletteWithTransparent
pixel_value &= 0x0F;
tile_data[dst_index] = pixel_value;
}
}
}
} else {
// Normalize the extracted data based on settings
if (auto_normalize_pixels_) {
for (auto& pixel : tile_data) {
pixel &= palette_normalization_mask_;
}
// Normalize the extracted data to 4-bit range
for (auto& pixel : tile_data) {
pixel &= 0x0F;
}
}
// Create the bitmap with the extracted data
current_tile16_bmp_.Create(kTile16Size, kTile16Size, 8, tile_data);
// Use the same palette system as the overworld (complete 256-color palette)
// CRITICAL FIX: Use SetPaletteWithTransparent with proper palette offset
// based on current_palette_ selection and default sheet (sheet 0 for tile16)
gfx::SnesPalette display_palette;
if (overworld_palette_.size() >= 256) {
// Use complete 256-color palette (same as overworld system)
// The pixel data already contains correct color indices for the 256-color
// palette
current_tile16_bmp_.SetPalette(overworld_palette_);
display_palette = overworld_palette_;
} else if (palette_.size() >= 256) {
current_tile16_bmp_.SetPalette(palette_);
display_palette = palette_;
} else if (rom()->palette_group().overworld_main.size() > 0) {
current_tile16_bmp_.SetPalette(rom()->palette_group().overworld_main[0]);
display_palette = rom()->palette_group().overworld_main[0];
}
// Calculate palette offset: use sheet 0 (main blockset) as default for tile16
// palette_base * 16 gives the row offset, current_palette_ * 8 gives
// sub-palette
int palette_base = GetPaletteBaseForSheet(0); // Default to main blockset
size_t palette_offset = (palette_base * 16) + (current_palette_ * 8);
// Defensive checks: ensure palette is present and offset is valid
if (display_palette.empty()) {
util::logf("Tile16Editor: display palette empty; falling back to offset 0");
return absl::FailedPreconditionError("display palette unavailable");
}
if (palette_offset + 7 >= display_palette.size()) {
util::logf("Tile16Editor: palette offset %zu out of range (size=%zu); "
"using offset 0",
palette_offset, display_palette.size());
palette_offset = 0;
if (display_palette.size() < 8) {
return absl::FailedPreconditionError("display palette too small");
}
}
// Apply the correct sub-palette with transparency
current_tile16_bmp_.SetPaletteWithTransparent(display_palette, palette_offset,
7);
// Queue texture creation via Arena's deferred system
gfx::Arena::Get().QueueTextureCommand(gfx::Arena::TextureCommandType::CREATE,
&current_tile16_bmp_);
@@ -2062,6 +2087,36 @@ int Tile16Editor::GetActualPaletteSlotForCurrentTile16() const {
return GetActualPaletteSlot(current_palette_, 0);
}
int Tile16Editor::GetPaletteBaseForSheet(int sheet_index) const {
// Based on overworld palette structure and how ProcessGraphicsBuffer assigns
// colors: The 256-color palette is organized as 16 rows of 16 colors each.
// Different graphics sheets map to different palette regions:
//
// Row 0: Transparent/system colors
// Row 1: HUD colors (palette index 0x10-0x1F)
// Rows 2-4: MAIN/AUX1 palette region for main graphics
// Rows 5-7: AUX2 palette region for area-specific graphics
// Row 7: ANIMATED palette for animated tiles
//
// The palette_button (0-7) selects within the region.
switch (sheet_index) {
case 0: // Main blockset
case 3: // Area graphics set 1
case 4: // Area graphics set 2
return 2; // AUX1 palette region starts at row 2
case 5: // Area graphics set 3
case 6: // Area graphics set 4
return 5; // AUX2 palette region starts at row 5
case 1: // Main graphics
case 2: // Main graphics
return 2; // MAIN palette region starts at row 2
case 7: // Animated tiles
return 7; // ANIMATED palette region at row 7
default:
return 2; // Default to MAIN region
}
}
// Helper methods for palette management
absl::Status Tile16Editor::UpdateTile8Palette(int tile8_id) {
if (tile8_id < 0 ||
@@ -2187,13 +2242,29 @@ absl::Status Tile16Editor::RefreshAllPalettes() {
gfx::Arena::TextureCommandType::UPDATE, &current_tile16_bmp_);
}
// Update all individual tile8 graphics with complete 256-color palette
// CRITICAL FIX: Update individual tile8 graphics with proper palette offsets
// Each tile8 belongs to a specific graphics sheet, which maps to a specific
// region of the 256-color palette. The current_palette_ (0-7) button selects
// within that region.
for (size_t i = 0; i < current_gfx_individual_.size(); ++i) {
if (current_gfx_individual_[i].is_active()) {
// Use complete 256-color palette (same as overworld system)
// The pixel data already contains correct color indices for the 256-color
// palette
current_gfx_individual_[i].SetPalette(display_palette);
// Determine which sheet this tile belongs to and get the palette offset
int sheet_index = GetSheetIndexForTile8(static_cast<int>(i));
int palette_base = GetPaletteBaseForSheet(sheet_index);
// Calculate the palette offset in the 256-color palette:
// - palette_base * 16: row offset in the 16x16 palette grid
// - current_palette_: additional offset within the region (0-7 maps to
// different sub-palettes)
// For 4bpp SNES graphics, we use 8 colors per sub-palette with
// transparent index 0
size_t palette_offset = (palette_base * 16) + (current_palette_ * 8);
// Use SetPaletteWithTransparent to apply the correct 8-color sub-palette
// This extracts 7 colors starting at palette_offset and creates
// transparent index 0
current_gfx_individual_[i].SetPaletteWithTransparent(
display_palette, palette_offset, 7);
current_gfx_individual_[i].set_modified(true);
// Queue texture update via Arena's deferred system
gfx::Arena::Get().QueueTextureCommand(