From 97c0921a0cef706fa3f3a398fc6ecf442616eb5b Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:21:02 +0100 Subject: [PATCH 01/19] Render sprites with OpenGL directly (but graphics still get generated on-the-fly) --- src/platform/win32/opengl.c | 36 +++++++++++++++++++++--------------- src/platform/win32/win32.c | 12 ++++++++---- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 5ff9897c4..8ecf9893f 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -71,28 +71,31 @@ static void TempConvert4bppToRGBA8_DynTextureBuffer(const u8 *bitmap4bpp, int wi } else { printf("realloc: w 0x%X, h 0x%X, full: 0x%X\n", width, height, (u32)(width * height * sizeof(u32))); } + + sDynTextureBuffer.width = width; + sDynTextureBuffer.height = height; } u8 *texturePalette = TempConvertPLTTEntryToRGBA8(paletteId); - sDynTextureBuffer.width = width; - sDynTextureBuffer.height = height; u16 widthInTiles = width >> 3; int numSourcePixels = width * height; for (int frameY = 0; frameY < height; frameY++) { for (int frameX = 0; frameX < width; frameX++) { - u8 colorIndex = ((frameY & 0x7) * 8 + (frameX & 0x7)); + int tileIndex = (frameY >> 3) * widthInTiles + (frameX >> 3); + int tileColorIndex = ((frameY & 0x7) * 8 + (frameX & 0x7)) + (tileIndex * (8*8)); + int targetColorIndex = ((frameY * width) + frameX); - bool8 doShift = (colorIndex & 1); - u8 textureColorId = bitmap4bpp[colorIndex >> 1] & (0xF << (doShift * 4)); + bool8 doShift = (targetColorIndex & 1); + int textureColorId = bitmap4bpp[tileColorIndex >> 1] & (0xF << (doShift * 4)); textureColorId >>= doShift * 4; - sDynTextureBuffer.data[colorIndex * 4 + 0] = tempRgbaPalette[textureColorId][0]; - sDynTextureBuffer.data[colorIndex * 4 + 1] = tempRgbaPalette[textureColorId][1]; - sDynTextureBuffer.data[colorIndex * 4 + 2] = tempRgbaPalette[textureColorId][2]; - sDynTextureBuffer.data[colorIndex * 4 + 3] = 0xFF; + sDynTextureBuffer.data[targetColorIndex * 4 + 0] = texturePalette[textureColorId * 4 + 0]; + sDynTextureBuffer.data[targetColorIndex * 4 + 1] = texturePalette[textureColorId * 4 + 1]; + sDynTextureBuffer.data[targetColorIndex * 4 + 2] = texturePalette[textureColorId * 4 + 2]; + sDynTextureBuffer.data[targetColorIndex * 4 + 3] = 0; } } } @@ -109,14 +112,14 @@ void OpenGL_OnInit() glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - glGenTextures(2, &sTempTextureHandles[0]); + glGenTextures(3, &sTempTextureHandles[0]); } void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) { const SpriteOffset *dims = sprite->dimensions; - - if (dims != (void *)-1) { + + if (dims != (void *)-1) { // Convert vertices screenspace -> unit space glMatrixMode(GL_PROJECTION); #if 0 @@ -124,7 +127,10 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) #else float a = 2.0f / DISPLAY_WIDTH; float b = 2.0f / DISPLAY_HEIGHT; - float projMtx[] = { a, 0, 0, 0, 0, b, 0, 0, 0, 0, 1, 0, -1, -1, 0, 1 }; + float projMtx[] = { a, 0, 0, 0, // + 0, b, 0, 0, // + 0, 0, 0, 0, // + -1, -1, 0, +1 }; // glLoadMatrixf(projMtx); #endif @@ -193,7 +199,7 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight glLoadMatrixf(projMtx); #endif - // TempConvertPLTTToRGBA8(); + //TempConvertPLTTToRGBA8(); // Convert the "software-rendered" image from ABGR1555 -> RGBA8 for (int i = 0; i < ARRAY_COUNT(tempRgbaFrame); i++) { @@ -225,9 +231,9 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + return; glClearColor(1.0, 1.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); - glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[1]); glBegin(GL_TRIANGLES); { diff --git a/src/platform/win32/win32.c b/src/platform/win32/win32.c index 261a18e60..42d928f85 100644 --- a/src/platform/win32/win32.c +++ b/src/platform/win32/win32.c @@ -328,8 +328,8 @@ void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) #if (RENDERER == RENDERER_OPENGL) // TEMP - Currently the display buffer gets drawn in software, but we should load the assets as a textures and let OpenGL render // everything - // OpenGL_DisplaySprite(sprite, oamPaletteNum); - // return; + OpenGL_DisplaySprite(sprite, oamPaletteNum); + return; #endif const SpriteOffset *dims = sprite->dimensions; @@ -376,7 +376,9 @@ void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) u16 widthInTiles = dims->width >> 3; for (int frameY = 0; frameY < dims->height; frameY++) { - s32 finalY = (tempY + frameY); + s32 finalY = (sprite->frameFlags & SPRITE_FLAG_MASK_Y_FLIP) + ? (tempY + dims->height - 1 - frameY) + : (tempY + frameY); if (finalY < 0) continue; @@ -386,7 +388,9 @@ void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) for (int frameX = 0; frameX < dims->width; frameX++) { - s32 finalX = (tempX + frameX); + s32 finalX = (sprite->frameFlags & SPRITE_FLAG_MASK_X_FLIP) + ? (tempX + dims->width - 1 - frameX) + : (tempX + frameX); if (finalX < 0) continue; From 9c2ab63f1d111cf8c237b3f2329c37fb3382ecbe Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Tue, 27 Jan 2026 13:04:54 +0100 Subject: [PATCH 02/19] Some Modelview mtx tests --- include/platform/platform.h | 2 + include/platform/shared/opengl.h | 1 + src/platform/win32/opengl.c | 90 +++++++++++++++++++++++++++----- src/platform/win32/win32.c | 11 ++++ src/sprite.c | 6 ++- 5 files changed, 95 insertions(+), 15 deletions(-) diff --git a/include/platform/platform.h b/include/platform/platform.h index 0a44b8f1f..faa0a1ce5 100644 --- a/include/platform/platform.h +++ b/include/platform/platform.h @@ -22,5 +22,7 @@ extern void Platform_LZDecompressUnsafe(unsigned char *src, unsigned char *dest) extern void Platform_RLDecompressUnsafe(unsigned char *src, unsigned char *dest); extern void Platform_QueueAudio(const void *data, u32 numBytes); +// TODO: Re-enable once #include-ing global.h/core.h/sprite.h does not result in compilation errors. +// void Platform_TransformSprite(Sprite *s, SpriteTransform *transform); #endif // GUARD_SA2_PLATFORM_H diff --git a/include/platform/shared/opengl.h b/include/platform/shared/opengl.h index dc643c216..5d16781f0 100644 --- a/include/platform/shared/opengl.h +++ b/include/platform/shared/opengl.h @@ -5,6 +5,7 @@ void OpenGL_OnInit(); void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum); +void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform); void OpenGL_Render(void *tempBufferPixels, int windowWidth, int windowHeight); #endif // GUARD_PLATFORM_SHARED_OPENGL_H diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 8ecf9893f..ef648c857 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -1,8 +1,10 @@ #include // realloc #include // printf #include +#include // sinf/cosf #include "global.h" // TEMP for PLTT #include "platform/shared/opengl.h" +#include "trig.h" // ONE_CYCLE // NOTE: This is NOT final at all. EXPERIMENTAL!!!!! @@ -13,7 +15,7 @@ static GLuint sTempTextureHandles[3] = { 0 }; #define NUM_RGB_CHANNELS 4 u8 tempRgbaPalette[16 * 32][NUM_RGB_CHANNELS] = {}; -u8 tempRgbaFrame[DISPLAY_WIDTH * DISPLAY_HEIGHT][NUM_RGB_CHANNELS] = {}; +//u8 tempRgbaFrame[DISPLAY_WIDTH * DISPLAY_HEIGHT][NUM_RGB_CHANNELS] = {}; typedef struct { u8 *data; @@ -21,6 +23,16 @@ typedef struct { } TextureBuffer; static TextureBuffer sDynTextureBuffer = { 0 }; +void Debug_PrintMatrix(float *mtx) +{ + printf("%f %f %f %f\n" + "%f %f %f %f\n" + "%f %f %f %f\n" + "%f %f %f %f\n\n", + mtx[0], mtx[1], mtx[2], mtx[3], mtx[4], mtx[5], mtx[6], mtx[7], mtx[8], mtx[9], mtx[10], mtx[11], mtx[12], mtx[13], mtx[14], + mtx[15]); +} + static void TempConvertPLTTToRGBA8(void) { // Convert PLTT from ABGR1555 -> RGBA8 @@ -115,11 +127,38 @@ void OpenGL_OnInit() glGenTextures(3, &sTempTextureHandles[0]); } +void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform) +{ + glMatrixMode(GL_MODELVIEW); + s16 rotation = (transform->rotation & ONE_CYCLE); + float theta = 2.0f * M_PI * (((float)rotation) / 1024.0f); + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + float modelViewMatrix[] = { + +cosTheta, +sinTheta, 0, 0, + -sinTheta, +cosTheta, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, + }; + glLoadMatrixf(modelViewMatrix); + + Debug_PrintMatrix(modelViewMatrix); + + // TODO: This doesn't happen in the original TransformSprite() procedure! + SPRITE_FLAG_SET(sprite, ROT_SCALE_ENABLE); +} + void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) { const SpriteOffset *dims = sprite->dimensions; - + int x = sprite->x; + int y = sprite->y; + if (dims != (void *)-1) { + if (!(sprite->oamFlags & SPRITE_FLAG_MASK_ROT_SCALE_ENABLE)) { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } // Convert vertices screenspace -> unit space glMatrixMode(GL_PROJECTION); #if 0 @@ -127,8 +166,8 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) #else float a = 2.0f / DISPLAY_WIDTH; float b = 2.0f / DISPLAY_HEIGHT; - float projMtx[] = { a, 0, 0, 0, // - 0, b, 0, 0, // + float projMtx[] = { +a, 0, 0, 0, // + 0, +b, 0, 0, // 0, 0, 0, 0, // -1, -1, 0, +1 }; // glLoadMatrixf(projMtx); @@ -145,18 +184,34 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - // glClearColor((float)tempRgbaPalette[1][0] / 255., - // (float)tempRgbaPalette[1][1] / 255., - // (float)tempRgbaPalette[1][2] / 255., - // 1.0); - // glClear(GL_COLOR_BUFFER_BIT); - - float minX = sprite->x - (dims->width >> 1); +#if 0 + x -= (sprite->frameFlags & SPRITE_FLAG_MASK_X_FLIP) ? dims->width - dims->offsetX : dims->offsetX; + y -= (sprite->frameFlags & SPRITE_FLAG_MASK_Y_FLIP) ? dims->height - dims->offsetY : dims->offsetY; +#else + x -= dims->offsetX; + y -= dims->offsetY; +#endif + y -= dims->offsetY; + x += (dims->width >> 1); + y += (dims->height >> 1); + float minX = x - (dims->width >> 1); float maxX = minX + dims->width; - float minY = sprite->y - (dims->height >> 1); + float minY = (DISPLAY_HEIGHT - y) - (dims->height >> 1); float maxY = minY + dims->height; + if (sprite->frameFlags & SPRITE_FLAG_MASK_X_FLIP) { + float temp = minX; + minX = maxX; + maxX = temp; + } + + if (sprite->frameFlags & SPRITE_FLAG_MASK_Y_FLIP) { + float temp = minY; + minY = maxY; + maxY = temp; + } + glBegin(GL_TRIANGLES); { glTexCoord2f(0.0, 0.0); @@ -175,6 +230,9 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) } glEnd(); } + + // TODO: This doesn't happen in the original DisplaySprite() procedure! + SPRITE_FLAG_CLEAR(sprite, ROT_SCALE_ENABLE); } void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight) @@ -201,6 +259,7 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight //TempConvertPLTTToRGBA8(); +#if 0 // Convert the "software-rendered" image from ABGR1555 -> RGBA8 for (int i = 0; i < ARRAY_COUNT(tempRgbaFrame); i++) { u16 color = ((u16 *)tempBufferPixels)[i]; @@ -213,7 +272,9 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight tempRgbaFrame[i][2] = r * 255.0; tempRgbaFrame[i][3] = 1 * 255.0; } +#endif + // Update palette glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, tempRgbaPalette); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // downscale filtering @@ -222,16 +283,16 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#if 0 + // Update software-rendered image glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, tempRgbaFrame); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // downscale filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // upscale filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - return; glClearColor(1.0, 1.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[1]); @@ -252,4 +313,5 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight glVertex2f(DISPLAY_WIDTH, 0); } glEnd(); +#endif } \ No newline at end of file diff --git a/src/platform/win32/win32.c b/src/platform/win32/win32.c index 42d928f85..95b5bd7f4 100644 --- a/src/platform/win32/win32.c +++ b/src/platform/win32/win32.c @@ -320,6 +320,11 @@ static void Win32_ProcessPendingMessages(HWND window) // Converts GBA -> Win32 RGB value #define RGB_SHIFT(value) (((value >> 10) & 0x1F) | (value & 0x3E0) | (((value & 0x1F) << 10))) +void Platform_TransformSprite(Sprite *s, SpriteTransform *transform) +{ + OpenGL_TransformSprite(s, transform); +} + void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) { if (sprite->graphics.src == NULL) @@ -344,6 +349,12 @@ void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) x = sprite->x; y = sprite->y; + // Effectively unused, but here for accuracy's sake + if (sprite->frameFlags & SPRITE_FLAG_GLOBAL_OFFSET) { + x -= gSpriteOffset.x; + y -= gSpriteOffset.y; + } + { // TEMP - from sprite.c sprWidth = dims->width; diff --git a/src/sprite.c b/src/sprite.c index 94f677da0..0534cdfc6 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -11,6 +11,7 @@ #if !PLATFORM_GBA && !PLATFORM_SDL extern void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum); +extern void Platform_TransformSprite(Sprite *sprite, SpriteTransform *transform); #endif #define ReadInstruction(script, cursor) ((void *)(script) + (cursor * sizeof(s32))) @@ -393,7 +394,10 @@ NONMATCH("asm/non_matching/engine/TransformSprite.inc", void TransformSprite(Spr // sp24 = s UnkSpriteStruct big; const SpriteOffset *dimensions = s->dimensions; - +#if PORTABLE + Platform_TransformSprite(s, transform); + return; +#endif if (dimensions != (SpriteOffset *)-1) { s16 res; s16 x16, y16; From b59a2e0017f1e565ea6ba80f754d5dd2cc75fdd3 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Sat, 31 Jan 2026 22:27:53 +0100 Subject: [PATCH 03/19] Some data documentation --- src/game/stage/camera.c | 46 +++++++++++++---------------- src/game/stage/player_super_sonic.c | 2 +- src/game/title_screen.c | 39 ++++++++++++------------ src/sprite.c | 4 +-- 4 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/game/stage/camera.c b/src/game/stage/camera.c index 541eee949..395f02c7f 100644 --- a/src/game/stage/camera.c +++ b/src/game/stage/camera.c @@ -783,8 +783,8 @@ void RenderMetatileLayers(s32 x, s32 y) void CreateStageBg_Zone1(void) { Background *background = &gStageBackgroundsRam.unk0; - gDispCnt |= 0x100; - gBgCntRegs[0] = 0x1B0F; + gDispCnt |= DISPCNT_BG0_ON; + gBgCntRegs[0] = BGCNT_SCREENBASE(27) | BGCNT_CHARBASE(3) | BGCNT_PRIORITY(3); #ifndef COLLECT_RINGS_ROM if (gGameMode != GAME_MODE_MULTI_PLAYER_COLLECT_RINGS) { @@ -795,8 +795,8 @@ void CreateStageBg_Zone1(void) background->graphics.dest = (void *)BG_SCREEN_ADDR(24); background->layoutVram = (void *)BG_SCREEN_ADDR(27); - background->targetTilesX = 0x20; - background->targetTilesY = 0x20; + background->targetTilesX = 256 / TILE_WIDTH; + background->targetTilesY = 256 / TILE_WIDTH; } else #endif { @@ -807,8 +807,8 @@ void CreateStageBg_Zone1(void) background->graphics.dest = (void *)BG_SCREEN_ADDR(24); background->layoutVram = (void *)BG_SCREEN_ADDR(27); - background->targetTilesX = 0x20; - background->targetTilesY = 0x1E; + background->targetTilesX = 256 / TILE_WIDTH; + background->targetTilesY = 240 / TILE_WIDTH; } DrawBackground(background); @@ -999,10 +999,8 @@ const u8 gUnknown_080D5B20[16][3] = { { 255, 5, 6 }, // }; -// TODO: This data is unused in this module -// But the place that references this is -// further down in code than the .rodata after this. -const u8 gUnknown_080D5B50[DISPLAY_HEIGHT] = { // 0-105 +// NOTE: Only values > 105 appear to be used. +const u8 gUnknown_080D5B50[DISPLAY_HEIGHT] = { // 0-94 10, 10, 10, // 10, 10, 10, // 10, 10, 10, // @@ -1034,13 +1032,11 @@ const u8 gUnknown_080D5B50[DISPLAY_HEIGHT] = { // 0-105 10, 10, 10, // 10, 10, 10, // 10, 10, 10, // - 10, 10, 8, // - 8, 8, 8, // - 7, 7, 7, // - 7, 6, 6, // - - // 106-159 | This data appears to be unused - 6, 6, 6, // + 10, 10, 8, 8, 8, 8, // 95-98 + 7, 7, 7, 7, // 99-102 + 6, 6, 6, // 103-104 + /* 105-159 */ + 6, 6, // 6, 6, 6, // 5, 5, 5, // 5, 5, 5, // @@ -1070,8 +1066,8 @@ static s16 sUnknown_03000408; void CreateStageBg_Zone3(void) { Background *background = &gStageBackgroundsRam.unk0; - gDispCnt |= 0x100; - gBgCntRegs[0] = 0x1B0F; + gDispCnt |= DISPCNT_BG0_ON; + gBgCntRegs[0] = BGCNT_SCREENBASE(27) | BGCNT_CHARBASE(3) | BGCNT_PRIORITY(3); *background = gStageCameraBgTemplates[3]; @@ -1189,7 +1185,7 @@ void CreateStageBg_Zone4(void) { Background *background = &gStageBackgroundsRam.unk0; const Background *templates; - gBgCntRegs[0] = 0x1B0F; + gBgCntRegs[0] = BGCNT_SCREENBASE(27) | BGCNT_CHARBASE(3) | BGCNT_PRIORITY(3); *background = gStageCameraBgTemplates[CAMBG_BACK_B_LAYER]; @@ -1465,7 +1461,7 @@ const u8 gUnknown_080D5C02[2][16][3] = { void CreateStageBg_Zone6_Acts(void) { gDispCnt |= DISPCNT_BG0_ON; - gBgCntRegs[0] = 0x1a0f; + gBgCntRegs[0] = BGCNT_SCREENBASE(26) | BGCNT_CHARBASE(3) | BGCNT_PRIORITY(3); INIT_BG_SPRITES_LAYER_32(0); DmaFill32(3, 0, BG_SCREEN_ADDR(24), sizeof(Background)); gBgScrollRegs[0][0] = 0; @@ -1486,7 +1482,7 @@ void CreateStageBg_Zone6_Boss(void) { Background *background = &gStageBackgroundsRam.unk0; gDispCnt |= DISPCNT_BG0_ON; - gBgCntRegs[0] = 0x1a0f; + gBgCntRegs[0] = BGCNT_SCREENBASE(26) | BGCNT_CHARBASE(3) | BGCNT_PRIORITY(3); INIT_BG_SPRITES_LAYER_32(0); DmaFill32(3, 0, BG_SCREEN_ADDR(24), sizeof(Background)); gBgScrollRegs[0][0] = 0; @@ -2316,9 +2312,9 @@ void StageBgUpdate_Zone5ActBoss(UNUSED s32 a, UNUSED s32 b) void StageBgUpdate_Zone6ActBoss(UNUSED s32 a, UNUSED s32 b) { - gBgCntRegs[0] |= 0x3; - gBgCntRegs[3] &= ~0x3; - gBgCntRegs[3] |= 0x2; + gBgCntRegs[0] |= BGCNT_PRIORITY(3); + gBgCntRegs[3] &= ~BGCNT_PRIORITY(3); + gBgCntRegs[3] |= BGCNT_PRIORITY(2); gBgScrollRegs[0][0] = (gBgScrollRegs[0][0] - 2) & 0xFF; gBgScrollRegs[0][1] = (gBgScrollRegs[0][1] + 1) & 0xFF; } diff --git a/src/game/stage/player_super_sonic.c b/src/game/stage/player_super_sonic.c index d2880fe2a..ee22d3693 100644 --- a/src/game/stage/player_super_sonic.c +++ b/src/game/stage/player_super_sonic.c @@ -849,4 +849,4 @@ static void sub_802C9B0(struct SuperSonic *sonic) } } -bool32 sub_802C9E0(void) { return FALSE; } \ No newline at end of file +bool32 sub_802C9E0(void) { return FALSE; } diff --git a/src/game/title_screen.c b/src/game/title_screen.c index 436356017..8da0b8a63 100644 --- a/src/game/title_screen.c +++ b/src/game/title_screen.c @@ -21,6 +21,7 @@ #include "game/stage/screen_fade.h" #include "game/time_attack/lobby.h" #include "game/time_attack/mode_select.h" +#include "game/sa1_sa2_shared/camera.h" // TILE_WIDTH #include "data/recordings.h" @@ -415,8 +416,8 @@ static void CreateTitleScreenWithoutIntro(TitleScreen *titleScreen) bg0->unk20 = 0; bg0->unk22 = 0; bg0->unk24 = 0; - bg0->targetTilesX = 26; - bg0->targetTilesY = 10; + bg0->targetTilesX = 208 / TILE_WIDTH; + bg0->targetTilesY = 80 / TILE_WIDTH; bg0->paletteOffset = 0; bg0->flags = BACKGROUND_FLAG_4 | BACKGROUND_FLAGS_BG_ID(2); @@ -441,8 +442,8 @@ static void CreateTitleScreenWithoutIntro(TitleScreen *titleScreen) config40->unk20 = 0; config40->unk22 = 0; config40->unk24 = 0; - config40->targetTilesX = 0x20; - config40->targetTilesY = 0x40; + config40->targetTilesX = 256 / TILE_WIDTH; + config40->targetTilesY = 512 / TILE_WIDTH; config40->paletteOffset = 0; config40->flags = BACKGROUND_FLAGS_BG_ID(1); @@ -461,9 +462,9 @@ static void InitTitleScreenBackgrounds(TitleScreen *titleScreen) gDispCnt = DISPCNT_MODE_1; gDispCnt |= DISPCNT_OBJ_1D_MAP | DISPCNT_BG2_ON | DISPCNT_OBJ_ON; - gBgCntRegs[0] = 0x1f04; - gBgCntRegs[1] = 0x9d0a; - gBgCntRegs[2] = 0x7a81; + gBgCntRegs[0] = BGCNT_16COLOR | BGCNT_TXT256x256 | BGCNT_SCREENBASE(31) | BGCNT_CHARBASE(1) | BGCNT_PRIORITY(0); + gBgCntRegs[1] = BGCNT_16COLOR | BGCNT_TXT256x512 | BGCNT_SCREENBASE(29) | BGCNT_CHARBASE(2) | BGCNT_PRIORITY(2); + gBgCntRegs[2] = BGCNT_256COLOR | BGCNT_AFF256x256 | BGCNT_WRAP | BGCNT_SCREENBASE(26) | BGCNT_CHARBASE(0) | BGCNT_PRIORITY(1); INIT_BG_SPRITES_LAYER_32(0); INIT_BG_SPRITES_LAYER_32(1); INIT_BG_SPRITES_LAYER_32(2); @@ -489,8 +490,8 @@ static void InitTitleScreenBackgrounds(TitleScreen *titleScreen) bg80->unk20 = 0; bg80->unk22 = 0; bg80->unk24 = 0; - bg80->targetTilesX = 30; - bg80->targetTilesY = 20; + bg80->targetTilesX = DISPLAY_WIDTH / TILE_WIDTH; + bg80->targetTilesY = DISPLAY_HEIGHT / TILE_WIDTH; bg80->paletteOffset = 0; bg80->flags = 0; @@ -508,8 +509,8 @@ static void InitTitleScreenBackgrounds(TitleScreen *titleScreen) bg0->unk20 = 0; bg0->unk22 = 0; bg0->unk24 = 0; - bg0->targetTilesX = 32; - bg0->targetTilesY = 32; + bg0->targetTilesX = 256 / TILE_WIDTH; + bg0->targetTilesY = 256 / TILE_WIDTH; bg0->paletteOffset = 0; bg0->flags = BACKGROUND_FLAG_4 | BACKGROUND_FLAGS_BG_ID(2); @@ -795,13 +796,13 @@ static void Task_IntroPanSkyAnim(void) } gBgScrollRegs[1][1] -= titleScreen->introPanUpVelocity; - if (gBgScrollRegs[1][1] < 0xAF) { - gBgScrollRegs[1][1] = 0xAF; + if (gBgScrollRegs[1][1] < 175) { + gBgScrollRegs[1][1] = 175; } - if (gBgScrollRegs[1][1] < 0x15F) { - gDispCnt &= 0xFBFF; - gDispCnt &= 0xBFFF; + if (gBgScrollRegs[1][1] < 351) { + gDispCnt &= ~DISPCNT_BG2_ON; + gDispCnt &= ~DISPCNT_WIN1_ON; } else { titleScreen->wavesTopOffset += titleScreen->introPanUpVelocity; WavesBackgroundAnim(titleScreen); @@ -944,11 +945,11 @@ static void Task_IntroSkyAnim(void) DrawBackground(bg0); - gBgCntRegs[2] &= 0xDFFF; + gBgCntRegs[2] &= ~DISPCNT_WIN0_ON; gCurTask->main = Task_IntroFadeInTitleScreenAnim; - gDispCnt |= 0x400; - gDispCnt &= 0xFEFF; + gDispCnt |= DISPCNT_BG2_ON; + gDispCnt &= ~DISPCNT_BG0_ON; gBldRegs.bldAlpha = 0x1000; gBldRegs.bldCnt = 0x244; diff --git a/src/sprite.c b/src/sprite.c index 0534cdfc6..3764cf198 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -394,9 +394,9 @@ NONMATCH("asm/non_matching/engine/TransformSprite.inc", void TransformSprite(Spr // sp24 = s UnkSpriteStruct big; const SpriteOffset *dimensions = s->dimensions; -#if PORTABLE +#if PORTABLE && (RENDERER != RENDERER_SOFTWARE) Platform_TransformSprite(s, transform); - return; + return; #endif if (dimensions != (SpriteOffset *)-1) { s16 res; From bea1f8e150f22f70f79292eee7dffce990db6a86 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:24:14 +0100 Subject: [PATCH 04/19] Very WIP changes --- include/platform/platform.h | 1 + include/platform/shared/opengl.h | 1 + src/background.c | 3 + src/platform/win32/opengl.c | 175 ++++++++++++++++++++----------- src/platform/win32/win32.c | 29 ++--- 5 files changed, 134 insertions(+), 75 deletions(-) diff --git a/include/platform/platform.h b/include/platform/platform.h index faa0a1ce5..323b195c0 100644 --- a/include/platform/platform.h +++ b/include/platform/platform.h @@ -22,6 +22,7 @@ extern void Platform_LZDecompressUnsafe(unsigned char *src, unsigned char *dest) extern void Platform_RLDecompressUnsafe(unsigned char *src, unsigned char *dest); extern void Platform_QueueAudio(const void *data, u32 numBytes); +extern void Platform_ProcessBackgroundsCopyQueue(void); // TODO: Re-enable once #include-ing global.h/core.h/sprite.h does not result in compilation errors. // void Platform_TransformSprite(Sprite *s, SpriteTransform *transform); diff --git a/include/platform/shared/opengl.h b/include/platform/shared/opengl.h index 5d16781f0..f3d6a5daf 100644 --- a/include/platform/shared/opengl.h +++ b/include/platform/shared/opengl.h @@ -4,6 +4,7 @@ #include "sprite.h" // for Sprite void OpenGL_OnInit(); +void OpenGL_ProcessBackgroundsCopyQueue(); void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum); void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform); void OpenGL_Render(void *tempBufferPixels, int windowWidth, int windowHeight); diff --git a/src/background.c b/src/background.c index 7fba66626..d4c36d31e 100644 --- a/src/background.c +++ b/src/background.c @@ -4,6 +4,7 @@ #include "sprite.h" #include "trig.h" #include "lib/m4a/m4a.h" +#include "platform/platform.h" #include "animation_commands.h" @@ -72,6 +73,7 @@ void DrawBackground(Background *background) // (85.37%) https://decomp.me/scratch/617Jb // (87.46%) https://decomp.me/scratch/1CFim +// TODO: ProcessBackgroundsCopyQueue might be a good name for this function? NONMATCH("asm/non_matching/engine/sub_8002B20.inc", bool32 sub_8002B20(void)) { u16 sp00; @@ -90,6 +92,7 @@ NONMATCH("asm/non_matching/engine/sub_8002B20.inc", bool32 sub_8002B20(void)) #if (RENDERER == RENDERER_OPENGL) // TEMP + Platform_ProcessBackgroundsCopyQueue(); return TRUE; #endif diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index ef648c857..34c1843a2 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -3,8 +3,10 @@ #include #include // sinf/cosf #include "global.h" // TEMP for PLTT +#include "core.h" // temp? #include "platform/shared/opengl.h" #include "trig.h" // ONE_CYCLE +#include "game/sa1_sa2_shared/globals.h" // gCurrentLevel - TEMP: this shouldn't be exposed to the OpenGL backend... // NOTE: This is NOT final at all. EXPERIMENTAL!!!!! @@ -13,9 +15,12 @@ // TEMP static GLuint sTempTextureHandles[3] = { 0 }; -#define NUM_RGB_CHANNELS 4 +#define TILE_SIZE_RGBA ((8 * 8) * sizeof(u32)) +#define CHUNK_SIZE_RGBA ((12 * 12) * TILE_SIZE_RGBA) +#define CHUNK_SIZE_TEXCOORD ((12 * 12) * sizeof(ChunkTileVertex)) +#define NUM_RGB_CHANNELS 4 u8 tempRgbaPalette[16 * 32][NUM_RGB_CHANNELS] = {}; -//u8 tempRgbaFrame[DISPLAY_WIDTH * DISPLAY_HEIGHT][NUM_RGB_CHANNELS] = {}; +// u8 tempRgbaFrame[DISPLAY_WIDTH * DISPLAY_HEIGHT][NUM_RGB_CHANNELS] = {}; typedef struct { u8 *data; @@ -90,24 +95,78 @@ static void TempConvert4bppToRGBA8_DynTextureBuffer(const u8 *bitmap4bpp, int wi u8 *texturePalette = TempConvertPLTTEntryToRGBA8(paletteId); - u16 widthInTiles = width >> 3; int numSourcePixels = width * height; for (int frameY = 0; frameY < height; frameY++) { for (int frameX = 0; frameX < width; frameX++) { int tileIndex = (frameY >> 3) * widthInTiles + (frameX >> 3); - int tileColorIndex = ((frameY & 0x7) * 8 + (frameX & 0x7)) + (tileIndex * (8*8)); + int tileColorIndex = ((frameY & 0x7) * 8 + (frameX & 0x7)) + (tileIndex * (8 * 8)); int targetColorIndex = ((frameY * width) + frameX); - bool8 doShift = (targetColorIndex & 1); + bool8 doShift = (targetColorIndex & 1); int textureColorId = bitmap4bpp[tileColorIndex >> 1] & (0xF << (doShift * 4)); textureColorId >>= doShift * 4; sDynTextureBuffer.data[targetColorIndex * 4 + 0] = texturePalette[textureColorId * 4 + 0]; sDynTextureBuffer.data[targetColorIndex * 4 + 1] = texturePalette[textureColorId * 4 + 1]; sDynTextureBuffer.data[targetColorIndex * 4 + 2] = texturePalette[textureColorId * 4 + 2]; - sDynTextureBuffer.data[targetColorIndex * 4 + 3] = 0; + sDynTextureBuffer.data[targetColorIndex * 4 + 3] = (textureColorId == 0) ? 0x00 : 0xFF; + } + } +} + +typedef struct ChunkTileVertex { + u8 x, y; +} ChunkTileVertex; + +typedef struct ChunkGfx { + ChunkTileVertex *tileVerts; + s16 levelId; +} ChunkGfx; + +static ChunkGfx chunkGfx = { NULL, -1 }; + +void UpdateChunkGfx(ChunkGfx *gfx, Background *bg) +{ + bool32 updateAll = TRUE; + + if (chunkGfx.tileVerts == NULL || chunkGfx.levelId != gCurrentLevel) { + // 288 KB + gfx->tileVerts = calloc(32 * 32, CHUNK_SIZE_TEXCOORD); + } + + //!(bg->flags & BACKGROUND_DISABLE_TILESET_UPDATE) + // If the palette didn't change, all tiles have to be updated + // if (bg->flags & BACKGROUND_DISABLE_PALETTE_UPDATE) { + // updateAll = FALSE; + // } + +#if 0 + // NOTE: Need to call glEnableClientState(GL_TEXTURE_COORD_ARRAY) first! + // + // Call using this: + void glTexCoordPointer(2, // coordinates per vertex + GL_BYTE, // data type of each vertex coordinate + 0, // stride (0 = tightly packed) + chunkIndex*CHUNK_SIZE_TEXCOORD); +#endif +} + +void OpenGL_ProcessBackgroundsCopyQueue(void) +{ + while (gBackgroundsCopyQueueCursor != gBackgroundsCopyQueueIndex) { + Background *bg = gBackgroundsCopyQueue[gBackgroundsCopyQueueCursor]; + INC_BACKGROUNDS_QUEUE_CURSOR(gBackgroundsCopyQueueCursor); + + if ((bg->flags & BACKGROUND_FLAG_20) && (bg->scrollX == bg->prevScrollX) && bg->scrollY == bg->prevScrollY) + continue; + + if (bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP) { + UpdateChunkGfx(&chunkGfx, bg); + + int k = 123; + } else { } } } @@ -117,6 +176,11 @@ void OpenGL_OnInit() // Enable texturing glEnable(GL_TEXTURE_2D); + // Enable blending via Alpha Test + // TODO: Maybe only do this for sprites? + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 1.0); + glMatrixMode(GL_TEXTURE); glLoadIdentity(); @@ -131,20 +195,40 @@ void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform) { glMatrixMode(GL_MODELVIEW); s16 rotation = (transform->rotation & ONE_CYCLE); - float theta = 2.0f * M_PI * (((float)rotation) / 1024.0f); + float theta = -(2.0f * M_PI * (((float)rotation) / 1024.0f)); float sinTheta = sinf(theta); float cosTheta = cosf(theta); float modelViewMatrix[] = { - +cosTheta, +sinTheta, 0, 0, - -sinTheta, +cosTheta, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, +#if 0 + 1, 0, 0, 0, // + 0, 1, 0, 0, // + 0, 0, 1, 0, // + 0, 0, 0, 1, // +#else + +cosTheta, + +sinTheta, + 0, + 0, + -sinTheta, + +cosTheta, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, +#endif }; glLoadMatrixf(modelViewMatrix); + // glTranslatef(transform->x, transform->y, 0); - Debug_PrintMatrix(modelViewMatrix); + // Debug_PrintMatrix(modelViewMatrix); - // TODO: This doesn't happen in the original TransformSprite() procedure! + // TODO: This doesn't happen in the original TransformSprite() procedure! SPRITE_FLAG_SET(sprite, ROT_SCALE_ENABLE); } @@ -154,11 +238,11 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) int x = sprite->x; int y = sprite->y; - if (dims != (void *)-1) { + if (dims != (void *)-1) { if (!(sprite->oamFlags & SPRITE_FLAG_MASK_ROT_SCALE_ENABLE)) { - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - } + // glMatrixMode(GL_MODELVIEW); + // glLoadIdentity(); + } // Convert vertices screenspace -> unit space glMatrixMode(GL_PROJECTION); #if 0 @@ -166,10 +250,10 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) #else float a = 2.0f / DISPLAY_WIDTH; float b = 2.0f / DISPLAY_HEIGHT; - float projMtx[] = { +a, 0, 0, 0, // - 0, +b, 0, 0, // - 0, 0, 0, 0, // - -1, -1, 0, +1 }; // + float projMtx[] = { +a, 0, 0, 0, // + 0, +b, 0, 0, // + 0, 0, 0, 0, // + -1, -1, 0, +1 }; // glLoadMatrixf(projMtx); #endif @@ -191,22 +275,21 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) x -= dims->offsetX; y -= dims->offsetY; #endif - y -= dims->offsetY; - x += (dims->width >> 1); - y += (dims->height >> 1); + x += (dims->width >> 1); + y += (dims->height >> 1); float minX = x - (dims->width >> 1); float maxX = minX + dims->width; float minY = (DISPLAY_HEIGHT - y) - (dims->height >> 1); float maxY = minY + dims->height; - if (sprite->frameFlags & SPRITE_FLAG_MASK_X_FLIP) { + if (sprite->frameFlags & SPRITE_FLAG_MASK_X_FLIP) { float temp = minX; minX = maxX; maxX = temp; } - - if (sprite->frameFlags & SPRITE_FLAG_MASK_Y_FLIP) { + + if (sprite->frameFlags & SPRITE_FLAG_MASK_Y_FLIP) { float temp = minY; minY = maxY; maxY = temp; @@ -239,6 +322,8 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight { glViewport(0, 0, viewportWidth, viewportHeight); + Background **bgQueue = &gBackgroundsCopyQueue[0]; +// __debugbreak(); #if 0 // Don't convert input-vertices glMatrixMode(GL_PROJECTION); @@ -257,7 +342,7 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight glLoadMatrixf(projMtx); #endif - //TempConvertPLTTToRGBA8(); + // TempConvertPLTTToRGBA8(); #if 0 // Convert the "software-rendered" image from ABGR1555 -> RGBA8 @@ -274,7 +359,7 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight } #endif - // Update palette + // Update palette glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, tempRgbaPalette); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // downscale filtering @@ -282,36 +367,4 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - -#if 0 - // Update software-rendered image - glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[1]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, tempRgbaFrame); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // downscale filtering - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // upscale filtering - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - glClearColor(1.0, 1.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[1]); - glBegin(GL_TRIANGLES); - { - glTexCoord2f(0.0, 0.0); - glVertex2f(0.0, +DISPLAY_HEIGHT); - glTexCoord2f(1.0, 1.0); - glVertex2f(+DISPLAY_WIDTH, 0); - glTexCoord2f(0.0, 1.0); - glVertex2f(0, 0); - - glTexCoord2f(0.0, 0.0); - glVertex2f(0, DISPLAY_HEIGHT); - glTexCoord2f(1.0, 0.0); - glVertex2f(DISPLAY_WIDTH, DISPLAY_HEIGHT); - glTexCoord2f(1.0, 1.0); - glVertex2f(DISPLAY_WIDTH, 0); - } - glEnd(); -#endif } \ No newline at end of file diff --git a/src/platform/win32/win32.c b/src/platform/win32/win32.c index 95b5bd7f4..3cb2c1263 100644 --- a/src/platform/win32/win32.c +++ b/src/platform/win32/win32.c @@ -320,21 +320,26 @@ static void Win32_ProcessPendingMessages(HWND window) // Converts GBA -> Win32 RGB value #define RGB_SHIFT(value) (((value >> 10) & 0x1F) | (value & 0x3E0) | (((value & 0x1F) << 10))) -void Platform_TransformSprite(Sprite *s, SpriteTransform *transform) +void Platform_ProcessBackgroundsCopyQueue(void) { - OpenGL_TransformSprite(s, transform); +#if (RENDERER == RENDERER_OPENGL) + OpenGL_ProcessBackgroundsCopyQueue(); +#endif } +void Platform_TransformSprite(Sprite *s, SpriteTransform *transform) { OpenGL_TransformSprite(s, transform); } + void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) { - if (sprite->graphics.src == NULL) + if (sprite->graphics.src == NULL) { return; + } #if (RENDERER == RENDERER_OPENGL) - // TEMP - Currently the display buffer gets drawn in software, but we should load the assets as a textures and let OpenGL render - // everything - OpenGL_DisplaySprite(sprite, oamPaletteNum); - return; + // TEMP - Currently the display buffer gets drawn in software, but we should load the assets as a textures and let OpenGL render + // everything + OpenGL_DisplaySprite(sprite, oamPaletteNum); + return; #endif const SpriteOffset *dims = sprite->dimensions; @@ -349,7 +354,7 @@ void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) x = sprite->x; y = sprite->y; - // Effectively unused, but here for accuracy's sake + // Effectively unused, but here for accuracy's sake if (sprite->frameFlags & SPRITE_FLAG_GLOBAL_OFFSET) { x -= gSpriteOffset.x; y -= gSpriteOffset.y; @@ -387,9 +392,7 @@ void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) u16 widthInTiles = dims->width >> 3; for (int frameY = 0; frameY < dims->height; frameY++) { - s32 finalY = (sprite->frameFlags & SPRITE_FLAG_MASK_Y_FLIP) - ? (tempY + dims->height - 1 - frameY) - : (tempY + frameY); + s32 finalY = (sprite->frameFlags & SPRITE_FLAG_MASK_Y_FLIP) ? (tempY + dims->height - 1 - frameY) : (tempY + frameY); if (finalY < 0) continue; @@ -399,9 +402,7 @@ void Platform_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) for (int frameX = 0; frameX < dims->width; frameX++) { - s32 finalX = (sprite->frameFlags & SPRITE_FLAG_MASK_X_FLIP) - ? (tempX + dims->width - 1 - frameX) - : (tempX + frameX); + s32 finalX = (sprite->frameFlags & SPRITE_FLAG_MASK_X_FLIP) ? (tempX + dims->width - 1 - frameX) : (tempX + frameX); if (finalX < 0) continue; From b3a4bd66ab17c6dbfc0f5f0efda85681fe0ad579 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:31:59 +0100 Subject: [PATCH 05/19] Some more prep for tilemap rendering --- src/platform/win32/opengl.c | 119 +++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 34c1843a2..433d5b9a1 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -6,6 +6,7 @@ #include "core.h" // temp? #include "platform/shared/opengl.h" #include "trig.h" // ONE_CYCLE +#include "game/stage/camera.h" // TEMP? #include "game/sa1_sa2_shared/globals.h" // gCurrentLevel - TEMP: this shouldn't be exposed to the OpenGL backend... // NOTE: This is NOT final at all. EXPERIMENTAL!!!!! @@ -122,25 +123,112 @@ typedef struct ChunkTileVertex { typedef struct ChunkGfx { ChunkTileVertex *tileVerts; - s16 levelId; + u8 *rgbaChunks; + u16 count; + u16 capacity; } ChunkGfx; -static ChunkGfx chunkGfx = { NULL, -1 }; +static ChunkGfx sChunkGfx = { 0 }; +#define RENDERED_CHUNKS_W ((DISPLAY_WIDTH / 96) + 1) +#define RENDERED_CHUNKS_H ((DISPLAY_HEIGHT / 96) + 1) +#define RENDERED_CHUNKS (RENDERED_CHUNKS_W * RENDERED_CHUNKS_H) -void UpdateChunkGfx(ChunkGfx *gfx, Background *bg) +// Set of chunks that have to be drawn this frame +typedef struct ChunkSet { + u16 *items; + u16 count, capacity; +} ChunkSet; +static ChunkSet sChunkSet = {0}; + +void CreateChunkSet(void) +{ + const int defaultCap = 16; + sChunkSet.items = calloc(defaultCap, sizeof(*sChunkSet.items)); + sChunkSet.count = 0; + sChunkSet.capacity = defaultCap; +} + +void FindUniqueChunks(ChunkSet *set, Background *bg, u16 mapChunkX, u16 mapChunkY, u8 screenChunkWidth, u8 screenChunkHeight) { - bool32 updateAll = TRUE; - if (chunkGfx.tileVerts == NULL || chunkGfx.levelId != gCurrentLevel) { - // 288 KB - gfx->tileVerts = calloc(32 * 32, CHUNK_SIZE_TEXCOORD); + for (int y = 0; y < screenChunkHeight; y++) { + for (int x = 0; x < screenChunkWidth; x++) { + u32 screenChunksStartIndex = (mapChunkY + y) * bg->mapWidth + (mapChunkX + x); + u16 chunkId = bg->metatileMap[screenChunksStartIndex]; + bool8 isInSet = FALSE; + + // Find chunk ID in set + for (int setI = 0; setI < set->count; setI++) { + if (set->items[setI] == chunkId) { + isInSet = TRUE; + break; + } + } + + // Add if not inside + if (!isInSet) { + while (set->count + 1 >= set->capacity) { + set->items = realloc(set->items, set->capacity * 2 * sizeof(u16)); + set->capacity *= 2; + } + + set->items[set->count] = chunkId; + set->count++; + } + } + } +} + + +void CacheUncachedUniqueChunks(ChunkSet *set, ChunkGfx *gfx) { + +} + +void UpdateChunkGfx(ChunkGfx *gfx, Background *bg) +{ + u32 mapWidthPixels = bg->mapWidth * 96; + u32 mapHeightPixels = bg->mapHeight * 96; + u16 mapChunkX = gCamera.x / 96; + u16 mapChunkY = gCamera.y / 96; + u8 screenChunkWidth = MIN(bg->mapWidth - mapChunkX, RENDERED_CHUNKS_W); + u8 screenChunkHeight = MIN(bg->mapHeight - mapChunkY, RENDERED_CHUNKS_H); + //bool8 updateAll = TRUE; + + if (sChunkGfx.tileVerts == NULL || sChunkGfx.rgbaChunks == NULL) { + const int DEFAULT_CAP = 16; + gfx->tileVerts = calloc(RENDERED_CHUNKS, CHUNK_SIZE_TEXCOORD * DEFAULT_CAP); + gfx->rgbaChunks = calloc(RENDERED_CHUNKS, 96 * 96 * NUM_RGB_CHANNELS * DEFAULT_CAP); + gfx->count = 0; + gfx->capacity = DEFAULT_CAP; } - //!(bg->flags & BACKGROUND_DISABLE_TILESET_UPDATE) - // If the palette didn't change, all tiles have to be updated - // if (bg->flags & BACKGROUND_DISABLE_PALETTE_UPDATE) { - // updateAll = FALSE; - // } + // TODO/PERFORMANCE: + // This should probably be two sets, like a "Double Buffer", + // with the previous set not getting reset and chunks from it getting copied over if needed. + // Both would somehow have to be reset on a stage transition + // (more accurately: when the tileset/tilemap changes), though! + sChunkSet.count = 0; + FindUniqueChunks(&sChunkSet, bg, mapChunkX, mapChunkY, screenChunkWidth, screenChunkHeight); +#if DEBUG + printf("Unique : %d\n", sChunkSet.count); +#endif + + CacheUncachedUniqueChunks(&sChunkSet, &sChunkGfx); + + // Draw metatile RGBA graphics + if (!(bg->flags & (BACKGROUND_DISABLE_TILESET_UPDATE | BACKGROUND_DISABLE_PALETTE_UPDATE))) { + for (int chunkSetItemIndex = 0; chunkSetItemIndex < sChunkSet.count; chunkSetItemIndex++) { + int chunkIndex = sChunkSet->items[chunkSetItemIndex]; + if (chunkIndex == 0) + continue; + + for (int tileIdY; tileIdY < 12; tileIdY++) { + for (int tileIdX; tileIdX < 12; tileIdX++) { + + } + } + } + } #if 0 // NOTE: Need to call glEnableClientState(GL_TEXTURE_COORD_ARRAY) first! @@ -163,7 +251,7 @@ void OpenGL_ProcessBackgroundsCopyQueue(void) continue; if (bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP) { - UpdateChunkGfx(&chunkGfx, bg); + UpdateChunkGfx(&sChunkGfx, bg); int k = 123; } else { @@ -189,6 +277,9 @@ void OpenGL_OnInit() glLoadIdentity(); glGenTextures(3, &sTempTextureHandles[0]); + + // TODO: Maybe this shuld be done in the platform layers? + CreateChunkSet(); } void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform) @@ -199,7 +290,7 @@ void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform) float sinTheta = sinf(theta); float cosTheta = cosf(theta); float modelViewMatrix[] = { -#if 0 +#if 01 1, 0, 0, 0, // 0, 1, 0, 0, // 0, 0, 1, 0, // From 3daca57327756be29c01926a1f22ea70acb03501 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Mon, 9 Feb 2026 23:59:14 +0100 Subject: [PATCH 06/19] Draw back chunks --- src/platform/win32/opengl.c | 316 +++++++++++++++++++++++------------- 1 file changed, 206 insertions(+), 110 deletions(-) diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 433d5b9a1..905aa79d1 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -1,9 +1,11 @@ -#include // realloc +//#include // realloc #include // printf +#include #include #include // sinf/cosf #include "global.h" // TEMP for PLTT #include "core.h" // temp? +#include "tilemap.h" // struct Tile #include "platform/shared/opengl.h" #include "trig.h" // ONE_CYCLE #include "game/stage/camera.h" // TEMP? @@ -16,19 +18,29 @@ // TEMP static GLuint sTempTextureHandles[3] = { 0 }; -#define TILE_SIZE_RGBA ((8 * 8) * sizeof(u32)) -#define CHUNK_SIZE_RGBA ((12 * 12) * TILE_SIZE_RGBA) -#define CHUNK_SIZE_TEXCOORD ((12 * 12) * sizeof(ChunkTileVertex)) -#define NUM_RGB_CHANNELS 4 -u8 tempRgbaPalette[16 * 32][NUM_RGB_CHANNELS] = {}; +#define TILES_PER_CHUNK_AXIS 12 +#define TILES_PER_CHUNK (SQUARE(TILES_PER_CHUNK_AXIS)) +#define TILE_SIZE_PIXELS (8 * 8) +#define TILE_SIZE_RGBA (TILE_SIZE_PIXELS * sizeof(u32)) +#define CHUNK_SIZE_RGBA (TILES_PER_CHUNK * TILE_SIZE_RGBA) +#define CHUNK_SIZE_TEXCOORD (TILES_PER_CHUNK * sizeof(ChunkTileVertex)) +#define NUM_RGB_CHANNELS 4 // u8 tempRgbaFrame[DISPLAY_WIDTH * DISPLAY_HEIGHT][NUM_RGB_CHANNELS] = {}; +typedef struct ColorRGBA { + u8 r; + u8 g; + u8 b; + u8 a; +} ColorRGBA; typedef struct { u8 *data; int width, height; } TextureBuffer; static TextureBuffer sDynTextureBuffer = { 0 }; +ColorRGBA tempRgbaPalette[16 * 32] = {}; + void Debug_PrintMatrix(float *mtx) { printf("%f %f %f %f\n" @@ -48,14 +60,14 @@ static void TempConvertPLTTToRGBA8(void) float g = (float)((color & 0x3E0) >> 5) / 31.0; float b = (float)((color & 0x7C00) >> 10) / 31.0; - tempRgbaPalette[i][0] = r * 255.0; - tempRgbaPalette[i][1] = g * 255.0; - tempRgbaPalette[i][2] = b * 255.0; - tempRgbaPalette[i][3] = 1 * 255.0; + tempRgbaPalette[i].r = r * 255.0; + tempRgbaPalette[i].g = g * 255.0; + tempRgbaPalette[i].b = b * 255.0; + tempRgbaPalette[i].a = 1 * 255.0; } } -static u8 *TempConvertPLTTEntryToRGBA8(u8 paletteId) +static ColorRGBA *TempConvertPLTTEntryToRGBA8(u8 paletteId) { // Convert PLTT from ABGR1555 -> RGBA8 u16 *pal4BPP = &PLTT[(paletteId + 16) * 16]; @@ -67,13 +79,13 @@ static u8 *TempConvertPLTTEntryToRGBA8(u8 paletteId) float g = (float)((color & 0x3E0) >> 5) / 31.0; float b = (float)((color & 0x7C00) >> 10) / 31.0; - tempRgbaPalette[paletteId * 16 + i][0] = r * 255.0; - tempRgbaPalette[paletteId * 16 + i][1] = g * 255.0; - tempRgbaPalette[paletteId * 16 + i][2] = b * 255.0; - tempRgbaPalette[paletteId * 16 + i][3] = 1 * 255.0; + tempRgbaPalette[paletteId * 16 + i].r = r * 255.0; + tempRgbaPalette[paletteId * 16 + i].g = g * 255.0; + tempRgbaPalette[paletteId * 16 + i].b = b * 255.0; + tempRgbaPalette[paletteId * 16 + i].a = 1 * 255.0; } - return tempRgbaPalette[paletteId * 16]; + return &tempRgbaPalette[paletteId * 16]; } // TODO: This should be done offline. @@ -94,7 +106,7 @@ static void TempConvert4bppToRGBA8_DynTextureBuffer(const u8 *bitmap4bpp, int wi sDynTextureBuffer.height = height; } - u8 *texturePalette = TempConvertPLTTEntryToRGBA8(paletteId); + ColorRGBA *texturePalette = TempConvertPLTTEntryToRGBA8(paletteId); u16 widthInTiles = width >> 3; @@ -109,9 +121,9 @@ static void TempConvert4bppToRGBA8_DynTextureBuffer(const u8 *bitmap4bpp, int wi int textureColorId = bitmap4bpp[tileColorIndex >> 1] & (0xF << (doShift * 4)); textureColorId >>= doShift * 4; - sDynTextureBuffer.data[targetColorIndex * 4 + 0] = texturePalette[textureColorId * 4 + 0]; - sDynTextureBuffer.data[targetColorIndex * 4 + 1] = texturePalette[textureColorId * 4 + 1]; - sDynTextureBuffer.data[targetColorIndex * 4 + 2] = texturePalette[textureColorId * 4 + 2]; + sDynTextureBuffer.data[targetColorIndex * 4 + 0] = texturePalette[textureColorId].r; + sDynTextureBuffer.data[targetColorIndex * 4 + 1] = texturePalette[textureColorId].g; + sDynTextureBuffer.data[targetColorIndex * 4 + 2] = texturePalette[textureColorId].b; sDynTextureBuffer.data[targetColorIndex * 4 + 3] = (textureColorId == 0) ? 0x00 : 0xFF; } } @@ -124,13 +136,13 @@ typedef struct ChunkTileVertex { typedef struct ChunkGfx { ChunkTileVertex *tileVerts; u8 *rgbaChunks; - u16 count; + u16 count; u16 capacity; } ChunkGfx; static ChunkGfx sChunkGfx = { 0 }; -#define RENDERED_CHUNKS_W ((DISPLAY_WIDTH / 96) + 1) -#define RENDERED_CHUNKS_H ((DISPLAY_HEIGHT / 96) + 1) +#define RENDERED_CHUNKS_W ((DISPLAY_WIDTH / 96) + 2) +#define RENDERED_CHUNKS_H ((DISPLAY_HEIGHT / 96) + 2) #define RENDERED_CHUNKS (RENDERED_CHUNKS_W * RENDERED_CHUNKS_H) // Set of chunks that have to be drawn this frame @@ -138,13 +150,47 @@ typedef struct ChunkSet { u16 *items; u16 count, capacity; } ChunkSet; -static ChunkSet sChunkSet = {0}; +static ChunkSet sChunkSet = { 0 }; + +void OpenGL_RenderRGBABuffer(u8 *buffer, u16 bufferWidth, u16 bufferHeight, float minX, float minY, float maxX, float maxY) +{ + glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[2]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferWidth, bufferHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // downscale filtering + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // upscale filtering + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glBegin(GL_TRIANGLES); + { + glTexCoord2f(0.0, 0.0); + glVertex2f(minX, maxY); + glTexCoord2f(1.0, 1.0); + glVertex2f(maxX, minY); + glTexCoord2f(0.0, 1.0); + glVertex2f(minX, minY); + + glTexCoord2f(0.0, 0.0); + glVertex2f(minX, maxY); + glTexCoord2f(1.0, 0.0); + glVertex2f(maxX, maxY); + glTexCoord2f(1.0, 1.0); + glVertex2f(maxX, minY); + } + glEnd(); +} void CreateChunkSet(void) { const int defaultCap = 16; - sChunkSet.items = calloc(defaultCap, sizeof(*sChunkSet.items)); - sChunkSet.count = 0; + // NOTE(Jace): For some reason, malloc and realloc (at least with current compile settings on Win32) use different heaps. + // That means, calling realloc with a pointer from malloc, which *should* work, crashes inside realloc. + // So for dynamic memory, we need to init with realloc for now... + void *mem = realloc(NULL, defaultCap * sizeof(*sChunkSet.items)); + sChunkSet.items = mem; + sChunkSet.items[0] = 0; + sChunkSet.count = 1; // initialize to 1, to skip zero-filled chunk (which always exists) sChunkSet.capacity = defaultCap; } @@ -152,83 +198,146 @@ void FindUniqueChunks(ChunkSet *set, Background *bg, u16 mapChunkX, u16 mapChunk { for (int y = 0; y < screenChunkHeight; y++) { - for (int x = 0; x < screenChunkWidth; x++) { + for (int x = 0; x < screenChunkWidth; x++) { u32 screenChunksStartIndex = (mapChunkY + y) * bg->mapWidth + (mapChunkX + x); u16 chunkId = bg->metatileMap[screenChunksStartIndex]; bool8 isInSet = FALSE; - - // Find chunk ID in set - for (int setI = 0; setI < set->count; setI++) { - if (set->items[setI] == chunkId) { - isInSet = TRUE; - break; - } - } - - // Add if not inside - if (!isInSet) { - while (set->count + 1 >= set->capacity) { - set->items = realloc(set->items, set->capacity * 2 * sizeof(u16)); - set->capacity *= 2; - } - - set->items[set->count] = chunkId; - set->count++; - } - } - } + + // Find chunk ID in set + for (int setI = 0; setI < set->count; setI++) { + if (set->items[setI] == chunkId) { + isInSet = TRUE; + break; + } + } + + // Add if not inside + if (!isInSet) { + while (set->count + 1 >= set->capacity) { + set->items = realloc(set->items, set->capacity * 2 * sizeof(*set->items)); + set->capacity *= 2; + } + + set->items[set->count] = chunkId; + set->count++; + } + } + } } +void CacheUncachedUniqueChunks(Background *bg, ChunkSet *set, ChunkGfx *gfx) +{ + // if (!(bg->flags & (BACKGROUND_DISABLE_TILESET_UPDATE | BACKGROUND_DISABLE_PALETTE_UPDATE))) + { + for (int chunkSetItemIndex = 0; chunkSetItemIndex < sChunkSet.count; chunkSetItemIndex++) { + int chunkIndex = sChunkSet.items[chunkSetItemIndex]; + if (chunkIndex == 0) + continue; + + ColorRGBA *chunkRGBA = (ColorRGBA *)(&gfx->rgbaChunks[chunkSetItemIndex * CHUNK_SIZE_RGBA]); + const u8 *tileset4BPP = &((const u8 *)bg->graphics.src)[0]; + + for (int tileIdY = 0; tileIdY < TILES_PER_CHUNK_AXIS; tileIdY++) { + for (int tileIdX = 0; tileIdX < TILES_PER_CHUNK_AXIS; tileIdX++) { + int tileId = tileIdY * TILES_PER_CHUNK_AXIS + tileIdX; + ColorRGBA *dstTileRGBA = &chunkRGBA[tileIdY * 8 * (TILES_PER_CHUNK_AXIS * 8) + tileIdX * 8]; + + int tileInChunkIndex = (chunkIndex * TILES_PER_CHUNK) + tileIdY * TILES_PER_CHUNK_AXIS + tileIdX; + Tile tile = *(const Tile *)&bg->layout[tileInChunkIndex]; + const u8 *srcTile4BPP = &tileset4BPP[(tile.index * TILE_SIZE_PIXELS) >> 1]; + + for (int tileY = 0; tileY < 8; tileY++) { + for (int tileX = 0; tileX < 8; tileX++) { + int targetColorIndex = ((tileY * 8) + tileX); + bool8 doShift = (targetColorIndex & 1); + int colorId = srcTile4BPP[targetColorIndex >> 1] & (0xF << (doShift * 4)); + colorId >>= doShift * 4; + + ColorRGBA *tilePalette = (ColorRGBA *)(&tempRgbaPalette[tile.pal * 16 + colorId]); + dstTileRGBA[tileY * (TILES_PER_CHUNK_AXIS * 8) + tileX].r = tilePalette->r; + dstTileRGBA[tileY * (TILES_PER_CHUNK_AXIS * 8) + tileX].g = tilePalette->g; + dstTileRGBA[tileY * (TILES_PER_CHUNK_AXIS * 8) + tileX].b = tilePalette->b; + dstTileRGBA[tileY * (TILES_PER_CHUNK_AXIS * 8) + tileX].a = tilePalette->a; + } + } + } + } + } + } +} + +u16 GetSetIndexFromChunkId(ChunkSet *set, u16 chunkId) +{ + for (int i = 0; i < set->count; i++) { + if (set->items[i] == chunkId) { + return i; + } + } + + return 0; +} -void CacheUncachedUniqueChunks(ChunkSet *set, ChunkGfx *gfx) { +void RenderScreenChunks(ChunkSet *set, ChunkGfx *gfx, Background *bg, s16 mapChunkX, s16 mapChunkY, s16 screenX, s16 screenY, + u8 screenChunkWidth, u8 screenChunkHeight) +{ + // for(int layerI = 0; layerI < 2; layerI++) + for (int layerI = 0; layerI < 1; layerI++) { + for (int chunkY = 0; chunkY < screenChunkHeight; chunkY++) { + int chunkPosIndexY = (mapChunkY + chunkY); + if (chunkPosIndexY >= bg->mapHeight) { + break; + } + for (int chunkX = 0; chunkX < screenChunkWidth; chunkX++) { + int chunkPosIndexX = (mapChunkX + chunkX); + if (chunkPosIndexX >= bg->mapWidth) { + break; + } + int chunkPosIndex = chunkPosIndexY * bg->mapWidth + chunkPosIndexX; + int chunkSetIndex = GetSetIndexFromChunkId(set, bg->metatileMap[chunkPosIndex]); + int chunkStartIndex = chunkSetIndex * CHUNK_SIZE_RGBA; + int chunkScreenX = (screenX + (chunkX * 96)); + int chunkScreenY = DISPLAY_HEIGHT - (screenY + (chunkY * 96)); + OpenGL_RenderRGBABuffer(&gfx->rgbaChunks[chunkStartIndex], 96, 96, chunkScreenX, chunkScreenY, chunkScreenX + 96, + chunkScreenY + 96); + } + } + } } void UpdateChunkGfx(ChunkGfx *gfx, Background *bg) { - u32 mapWidthPixels = bg->mapWidth * 96; - u32 mapHeightPixels = bg->mapHeight * 96; + u32 mapWidthPixels = bg->mapWidth * 96; + u32 mapHeightPixels = bg->mapHeight * 96; u16 mapChunkX = gCamera.x / 96; u16 mapChunkY = gCamera.y / 96; - u8 screenChunkWidth = MIN(bg->mapWidth - mapChunkX, RENDERED_CHUNKS_W); + s16 screenX = -(gCamera.x % 96u); + s16 screenY = 96 - (gCamera.y % 96u); + u8 screenChunkWidth = MIN(bg->mapWidth - mapChunkX, RENDERED_CHUNKS_W); u8 screenChunkHeight = MIN(bg->mapHeight - mapChunkY, RENDERED_CHUNKS_H); - //bool8 updateAll = TRUE; + // bool8 updateAll = TRUE; if (sChunkGfx.tileVerts == NULL || sChunkGfx.rgbaChunks == NULL) { const int DEFAULT_CAP = 16; gfx->tileVerts = calloc(RENDERED_CHUNKS, CHUNK_SIZE_TEXCOORD * DEFAULT_CAP); gfx->rgbaChunks = calloc(RENDERED_CHUNKS, 96 * 96 * NUM_RGB_CHANNELS * DEFAULT_CAP); - gfx->count = 0; + gfx->count = 1; // initialize to 1, to skip zero-filled chunk (which always exists) gfx->capacity = DEFAULT_CAP; } - // TODO/PERFORMANCE: - // This should probably be two sets, like a "Double Buffer", - // with the previous set not getting reset and chunks from it getting copied over if needed. - // Both would somehow have to be reset on a stage transition - // (more accurately: when the tileset/tilemap changes), though! - sChunkSet.count = 0; + // TODO/PERFORMANCE: + // This should probably be two sets, like a "Double Buffer", + // with the previous set not getting reset and chunks from it getting copied over if needed. + // Both would somehow have to be reset on a stage transition + // (more accurately: when the tileset/tilemap changes), though! + sChunkSet.count = 1; // initialize to 1, to skip zero-filled chunk (which always exists) FindUniqueChunks(&sChunkSet, bg, mapChunkX, mapChunkY, screenChunkWidth, screenChunkHeight); -#if DEBUG - printf("Unique : %d\n", sChunkSet.count); -#endif - CacheUncachedUniqueChunks(&sChunkSet, &sChunkGfx); + TempConvertPLTTToRGBA8(); + CacheUncachedUniqueChunks(bg, &sChunkSet, &sChunkGfx); - // Draw metatile RGBA graphics - if (!(bg->flags & (BACKGROUND_DISABLE_TILESET_UPDATE | BACKGROUND_DISABLE_PALETTE_UPDATE))) { - for (int chunkSetItemIndex = 0; chunkSetItemIndex < sChunkSet.count; chunkSetItemIndex++) { - int chunkIndex = sChunkSet->items[chunkSetItemIndex]; - if (chunkIndex == 0) - continue; - - for (int tileIdY; tileIdY < 12; tileIdY++) { - for (int tileIdX; tileIdX < 12; tileIdX++) { - - } - } - } - } + // Draw metatile RGBA graphics + RenderScreenChunks(&sChunkSet, gfx, bg, mapChunkX, mapChunkY, screenX, screenY, screenChunkWidth, screenChunkHeight); #if 0 // NOTE: Need to call glEnableClientState(GL_TEXTURE_COORD_ARRAY) first! @@ -278,8 +387,8 @@ void OpenGL_OnInit() glGenTextures(3, &sTempTextureHandles[0]); - // TODO: Maybe this shuld be done in the platform layers? - CreateChunkSet(); + // TODO: Maybe this shuld be done in the platform layers? + CreateChunkSet(); } void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform) @@ -291,10 +400,22 @@ void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform) float cosTheta = cosf(theta); float modelViewMatrix[] = { #if 01 - 1, 0, 0, 0, // - 0, 1, 0, 0, // - 0, 0, 1, 0, // - 0, 0, 0, 1, // + 1, + 0, + 0, + 0, // + 0, + 1, + 0, + 0, // + 0, + 0, + 1, + 0, // + 0, + 0, + 0, + 1, // #else +cosTheta, +sinTheta, @@ -350,15 +471,6 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) TempConvert4bppToRGBA8_DynTextureBuffer(sprite->graphics.src, dims->width, dims->height, sprite->palId + oamPaletteNum); - // glGenTextures(1, &sTempTextureHandles[2]); - glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[2]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dims->width, dims->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, sDynTextureBuffer.data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // downscale filtering - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // upscale filtering - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - #if 0 x -= (sprite->frameFlags & SPRITE_FLAG_MASK_X_FLIP) ? dims->width - dims->offsetX : dims->offsetX; y -= (sprite->frameFlags & SPRITE_FLAG_MASK_Y_FLIP) ? dims->height - dims->offsetY : dims->offsetY; @@ -386,23 +498,7 @@ void OpenGL_DisplaySprite(Sprite *sprite, u8 oamPaletteNum) maxY = temp; } - glBegin(GL_TRIANGLES); - { - glTexCoord2f(0.0, 0.0); - glVertex2f(minX, maxY); - glTexCoord2f(1.0, 1.0); - glVertex2f(maxX, minY); - glTexCoord2f(0.0, 1.0); - glVertex2f(minX, minY); - - glTexCoord2f(0.0, 0.0); - glVertex2f(minX, maxY); - glTexCoord2f(1.0, 0.0); - glVertex2f(maxX, maxY); - glTexCoord2f(1.0, 1.0); - glVertex2f(maxX, minY); - } - glEnd(); + OpenGL_RenderRGBABuffer(sDynTextureBuffer.data, dims->width, dims->height, minX, minY, maxX, maxY); } // TODO: This doesn't happen in the original DisplaySprite() procedure! From b0bd9ffd5ac4c952ddba29cdcfa68ca4abac03e9 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Tue, 10 Feb 2026 00:15:54 +0100 Subject: [PATCH 07/19] Apply X/Y flips where needed --- src/platform/win32/opengl.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 905aa79d1..4d68ec875 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -246,18 +246,21 @@ void CacheUncachedUniqueChunks(Background *bg, ChunkSet *set, ChunkGfx *gfx) Tile tile = *(const Tile *)&bg->layout[tileInChunkIndex]; const u8 *srcTile4BPP = &tileset4BPP[(tile.index * TILE_SIZE_PIXELS) >> 1]; - for (int tileY = 0; tileY < 8; tileY++) { - for (int tileX = 0; tileX < 8; tileX++) { + for (int tileLoopY = 0; tileLoopY < 8; tileLoopY++) { + int tileY = (tile.yFlip) ? (7 - tileLoopY) : tileLoopY; + for (int tileLoopX = 0; tileLoopX < 8; tileLoopX++) { + int tileX = (tile.xFlip) ? (7 - tileLoopX) : tileLoopX; + int targetColorIndex = ((tileY * 8) + tileX); bool8 doShift = (targetColorIndex & 1); int colorId = srcTile4BPP[targetColorIndex >> 1] & (0xF << (doShift * 4)); colorId >>= doShift * 4; ColorRGBA *tilePalette = (ColorRGBA *)(&tempRgbaPalette[tile.pal * 16 + colorId]); - dstTileRGBA[tileY * (TILES_PER_CHUNK_AXIS * 8) + tileX].r = tilePalette->r; - dstTileRGBA[tileY * (TILES_PER_CHUNK_AXIS * 8) + tileX].g = tilePalette->g; - dstTileRGBA[tileY * (TILES_PER_CHUNK_AXIS * 8) + tileX].b = tilePalette->b; - dstTileRGBA[tileY * (TILES_PER_CHUNK_AXIS * 8) + tileX].a = tilePalette->a; + dstTileRGBA[tileLoopY * (TILES_PER_CHUNK_AXIS * 8) + tileLoopX].r = tilePalette->r; + dstTileRGBA[tileLoopY * (TILES_PER_CHUNK_AXIS * 8) + tileLoopX].g = tilePalette->g; + dstTileRGBA[tileLoopY * (TILES_PER_CHUNK_AXIS * 8) + tileLoopX].b = tilePalette->b; + dstTileRGBA[tileLoopY * (TILES_PER_CHUNK_AXIS * 8) + tileLoopX].a = tilePalette->a; } } } From 177168643fedf78af930c2a7b537f49c31ab19ce Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Tue, 10 Feb 2026 15:28:24 +0100 Subject: [PATCH 08/19] Render 2nd chunk layer --- src/platform/win32/opengl.c | 137 ++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 60 deletions(-) diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 4d68ec875..ff86367b5 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -8,7 +8,6 @@ #include "tilemap.h" // struct Tile #include "platform/shared/opengl.h" #include "trig.h" // ONE_CYCLE -#include "game/stage/camera.h" // TEMP? #include "game/sa1_sa2_shared/globals.h" // gCurrentLevel - TEMP: this shouldn't be exposed to the OpenGL backend... // NOTE: This is NOT final at all. EXPERIMENTAL!!!!! @@ -152,6 +151,7 @@ typedef struct ChunkSet { } ChunkSet; static ChunkSet sChunkSet = { 0 }; +// TODO: minY should be from upper-left corner, not bottom-left! void OpenGL_RenderRGBABuffer(u8 *buffer, u16 bufferWidth, u16 bufferHeight, float minX, float minY, float maxX, float maxY) { glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[2]); @@ -196,11 +196,11 @@ void CreateChunkSet(void) void FindUniqueChunks(ChunkSet *set, Background *bg, u16 mapChunkX, u16 mapChunkY, u8 screenChunkWidth, u8 screenChunkHeight) { - for (int y = 0; y < screenChunkHeight; y++) { for (int x = 0; x < screenChunkWidth; x++) { - u32 screenChunksStartIndex = (mapChunkY + y) * bg->mapWidth + (mapChunkX + x); - u16 chunkId = bg->metatileMap[screenChunksStartIndex]; + u32 screenChunkIndex = (mapChunkY + y) * bg->mapWidth + (mapChunkX + x); + const u16 *chunk = &bg->metatileMap[screenChunkIndex]; + const u16 chunkId = *chunk; bool8 isInSet = FALSE; // Find chunk ID in set @@ -225,6 +225,47 @@ void FindUniqueChunks(ChunkSet *set, Background *bg, u16 mapChunkX, u16 mapChunk } } +void RenderTilemap(ColorRGBA *dstBuffer, Background *bg, int chunkIndex) +{ + const u8 *tileset = bg->graphics.src; + int chunkTileCount = bg->xTiles * bg->yTiles; + + if (!(bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP)) { + // NOTE(Jace): Non-stage maps do not have any "chunks", + // but chunks themselves get rendered like all other tilemaps + chunkIndex = 0; + } + + for (int tileIdY = 0; tileIdY < bg->yTiles; tileIdY++) { + for (int tileIdX = 0; tileIdX < bg->xTiles; tileIdX++) { + // int tileId = tileIdY * bg->xTiles + tileIdX; + ColorRGBA *dstTileRGBA = &dstBuffer[tileIdY * 8 * (bg->xTiles * 8) + tileIdX * 8]; + + int tileInChunkIndex = (chunkIndex * chunkTileCount) + tileIdY * bg->xTiles + tileIdX; + Tile tile = *(const Tile *)&bg->layout[tileInChunkIndex]; + const u8 *srcTile4BPP = &tileset[(tile.index * TILE_SIZE_PIXELS) >> 1]; + + for (int tileLoopY = 0; tileLoopY < 8; tileLoopY++) { + int tileY = (tile.yFlip) ? (7 - tileLoopY) : tileLoopY; + for (int tileLoopX = 0; tileLoopX < 8; tileLoopX++) { + int tileX = (tile.xFlip) ? (7 - tileLoopX) : tileLoopX; + + int targetColorIndex = ((tileY * 8) + tileX); + bool8 doShift = (targetColorIndex & 1); + int colorId = srcTile4BPP[targetColorIndex >> 1] & (0xF << (doShift * 4)); + colorId >>= doShift * 4; + + ColorRGBA *tilePalette = (ColorRGBA *)(&tempRgbaPalette[tile.pal * 16 + colorId]); + dstTileRGBA[tileLoopY * (bg->xTiles * 8) + tileLoopX].r = tilePalette->r; + dstTileRGBA[tileLoopY * (bg->xTiles * 8) + tileLoopX].g = tilePalette->g; + dstTileRGBA[tileLoopY * (bg->xTiles * 8) + tileLoopX].b = tilePalette->b; + dstTileRGBA[tileLoopY * (bg->xTiles * 8) + tileLoopX].a = (colorId == 0) ? 0x00 : 0xFF; + } + } + } + } +} + void CacheUncachedUniqueChunks(Background *bg, ChunkSet *set, ChunkGfx *gfx) { // if (!(bg->flags & (BACKGROUND_DISABLE_TILESET_UPDATE | BACKGROUND_DISABLE_PALETTE_UPDATE))) @@ -236,40 +277,12 @@ void CacheUncachedUniqueChunks(Background *bg, ChunkSet *set, ChunkGfx *gfx) ColorRGBA *chunkRGBA = (ColorRGBA *)(&gfx->rgbaChunks[chunkSetItemIndex * CHUNK_SIZE_RGBA]); const u8 *tileset4BPP = &((const u8 *)bg->graphics.src)[0]; - - for (int tileIdY = 0; tileIdY < TILES_PER_CHUNK_AXIS; tileIdY++) { - for (int tileIdX = 0; tileIdX < TILES_PER_CHUNK_AXIS; tileIdX++) { - int tileId = tileIdY * TILES_PER_CHUNK_AXIS + tileIdX; - ColorRGBA *dstTileRGBA = &chunkRGBA[tileIdY * 8 * (TILES_PER_CHUNK_AXIS * 8) + tileIdX * 8]; - - int tileInChunkIndex = (chunkIndex * TILES_PER_CHUNK) + tileIdY * TILES_PER_CHUNK_AXIS + tileIdX; - Tile tile = *(const Tile *)&bg->layout[tileInChunkIndex]; - const u8 *srcTile4BPP = &tileset4BPP[(tile.index * TILE_SIZE_PIXELS) >> 1]; - - for (int tileLoopY = 0; tileLoopY < 8; tileLoopY++) { - int tileY = (tile.yFlip) ? (7 - tileLoopY) : tileLoopY; - for (int tileLoopX = 0; tileLoopX < 8; tileLoopX++) { - int tileX = (tile.xFlip) ? (7 - tileLoopX) : tileLoopX; - - int targetColorIndex = ((tileY * 8) + tileX); - bool8 doShift = (targetColorIndex & 1); - int colorId = srcTile4BPP[targetColorIndex >> 1] & (0xF << (doShift * 4)); - colorId >>= doShift * 4; - - ColorRGBA *tilePalette = (ColorRGBA *)(&tempRgbaPalette[tile.pal * 16 + colorId]); - dstTileRGBA[tileLoopY * (TILES_PER_CHUNK_AXIS * 8) + tileLoopX].r = tilePalette->r; - dstTileRGBA[tileLoopY * (TILES_PER_CHUNK_AXIS * 8) + tileLoopX].g = tilePalette->g; - dstTileRGBA[tileLoopY * (TILES_PER_CHUNK_AXIS * 8) + tileLoopX].b = tilePalette->b; - dstTileRGBA[tileLoopY * (TILES_PER_CHUNK_AXIS * 8) + tileLoopX].a = tilePalette->a; - } - } - } - } + RenderTilemap(chunkRGBA, bg, chunkIndex); } } } -u16 GetSetIndexFromChunkId(ChunkSet *set, u16 chunkId) +u16 GetChunkSetIndexFromChunkId(ChunkSet *set, u16 chunkId) { for (int i = 0; i < set->count; i++) { if (set->items[i] == chunkId) { @@ -283,27 +296,24 @@ u16 GetSetIndexFromChunkId(ChunkSet *set, u16 chunkId) void RenderScreenChunks(ChunkSet *set, ChunkGfx *gfx, Background *bg, s16 mapChunkX, s16 mapChunkY, s16 screenX, s16 screenY, u8 screenChunkWidth, u8 screenChunkHeight) { - // for(int layerI = 0; layerI < 2; layerI++) - for (int layerI = 0; layerI < 1; layerI++) { - for (int chunkY = 0; chunkY < screenChunkHeight; chunkY++) { - int chunkPosIndexY = (mapChunkY + chunkY); - if (chunkPosIndexY >= bg->mapHeight) { - break; - } + for (int chunkY = 0; chunkY < screenChunkHeight; chunkY++) { + int chunkPosIndexY = (mapChunkY + chunkY); + if (chunkPosIndexY >= bg->mapHeight) { + break; + } - for (int chunkX = 0; chunkX < screenChunkWidth; chunkX++) { - int chunkPosIndexX = (mapChunkX + chunkX); - if (chunkPosIndexX >= bg->mapWidth) { - break; - } - int chunkPosIndex = chunkPosIndexY * bg->mapWidth + chunkPosIndexX; - int chunkSetIndex = GetSetIndexFromChunkId(set, bg->metatileMap[chunkPosIndex]); - int chunkStartIndex = chunkSetIndex * CHUNK_SIZE_RGBA; - int chunkScreenX = (screenX + (chunkX * 96)); - int chunkScreenY = DISPLAY_HEIGHT - (screenY + (chunkY * 96)); - OpenGL_RenderRGBABuffer(&gfx->rgbaChunks[chunkStartIndex], 96, 96, chunkScreenX, chunkScreenY, chunkScreenX + 96, - chunkScreenY + 96); + for (int chunkX = 0; chunkX < screenChunkWidth; chunkX++) { + int chunkPosIndexX = (mapChunkX + chunkX); + if (chunkPosIndexX >= bg->mapWidth) { + break; } + int chunkPosIndex = chunkPosIndexY * bg->mapWidth + chunkPosIndexX; + int chunkSetIndex = GetChunkSetIndexFromChunkId(set, bg->metatileMap[chunkPosIndex]); + int chunkStartIndex = chunkSetIndex * CHUNK_SIZE_RGBA; + int chunkScreenX = (screenX + (chunkX * 96)); + int chunkScreenY = DISPLAY_HEIGHT - (screenY + (chunkY * 96)); + OpenGL_RenderRGBABuffer(&gfx->rgbaChunks[chunkStartIndex], 96, 96, chunkScreenX, chunkScreenY, chunkScreenX + 96, + chunkScreenY + 96); } } } @@ -312,10 +322,10 @@ void UpdateChunkGfx(ChunkGfx *gfx, Background *bg) { u32 mapWidthPixels = bg->mapWidth * 96; u32 mapHeightPixels = bg->mapHeight * 96; - u16 mapChunkX = gCamera.x / 96; - u16 mapChunkY = gCamera.y / 96; - s16 screenX = -(gCamera.x % 96u); - s16 screenY = 96 - (gCamera.y % 96u); + u16 mapChunkX = bg->scrollX / 96; + u16 mapChunkY = bg->scrollY / 96; + s16 screenX = -(bg->scrollX % 96u); + s16 screenY = 96 - (bg->scrollY % 96u); u8 screenChunkWidth = MIN(bg->mapWidth - mapChunkX, RENDERED_CHUNKS_W); u8 screenChunkHeight = MIN(bg->mapHeight - mapChunkY, RENDERED_CHUNKS_H); // bool8 updateAll = TRUE; @@ -364,9 +374,16 @@ void OpenGL_ProcessBackgroundsCopyQueue(void) if (bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP) { UpdateChunkGfx(&sChunkGfx, bg); - - int k = 123; } else { +#if 0 + // TEMP!!! + // DON'T MALLOC WITHOUT FREEING!!! + // (Doing it like this only works for 1 frame, anyway) + void *tilemapRGBA = malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA); + TempConvertPLTTToRGBA8(); + RenderTilemap(tilemapRGBA, bg, 0); + OpenGL_RenderRGBABuffer(tilemapRGBA, bg->xTiles * 8, bg->yTiles * 8, 0, -DISPLAY_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); +#endif } } } @@ -547,7 +564,6 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight tempRgbaFrame[i][2] = r * 255.0; tempRgbaFrame[i][3] = 1 * 255.0; } -#endif // Update palette glBindTexture(GL_TEXTURE_2D, sTempTextureHandles[0]); @@ -557,4 +573,5 @@ void OpenGL_Render(void *tempBufferPixels, int viewportWidth, int viewportHeight glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#endif } \ No newline at end of file From 62832e0b68bea9fb47a87a90ed50975d10b658aa Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:23:24 +0100 Subject: [PATCH 09/19] Document palette IDs in all uses of gObjPalette --- src/game/boost_effect.c | 2 +- src/game/bosses/boss_2.c | 8 ++++---- src/game/bosses/boss_3.c | 8 ++++---- src/game/bosses/boss_4.c | 4 ++-- src/game/bosses/boss_5.c | 8 ++++---- src/game/bosses/boss_6.c | 4 ++-- src/game/bosses/boss_9.c | 8 ++++---- src/game/character_select.c | 2 +- src/game/cutscenes/extra_ending_fall.c | 4 ++-- src/game/multiboot/collect_rings/time_display.c | 2 +- src/game/sa1_sa2_shared/pause_menu.c | 6 +++--- src/game/special_stage/ui.c | 4 ++-- src/game/stage/intro.c | 2 +- src/game/stage/ui.c | 2 +- 14 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/game/boost_effect.c b/src/game/boost_effect.c index c05b69e91..d1227ddb9 100644 --- a/src/game/boost_effect.c +++ b/src/game/boost_effect.c @@ -173,7 +173,7 @@ static inline void sub_8015B64_inline(AnimId anim, u16 palId) #endif numColors = *pAnim % 256u; - DmaCopy32(3, &gRefSpriteTables->palettes[animPalId * 16], &gObjPalette[insertOffset], numColors * sizeof(u16)); + DmaCopy32(3, &gRefSpriteTables->palettes[animPalId * 16], &gObjPalette[0 * 16 + insertOffset], numColors * sizeof(u16)); gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; } diff --git a/src/game/bosses/boss_2.c b/src/game/bosses/boss_2.c index 807dca7cf..82c2e1d21 100644 --- a/src/game/bosses/boss_2.c +++ b/src/game/bosses/boss_2.c @@ -576,22 +576,22 @@ static void UpdateBomberTankPalette(EggBomberTank *boss) u8 i; if (boss->bossHitTimer != 0) { for (i = 0; i < 16; i++) { - gObjPalette[i + 0x80] = gUnknown_080D7B70[(gStageTime & 2) >> 1][i]; + gObjPalette[8 * 16 + i] = gUnknown_080D7B70[(gStageTime & 2) >> 1][i]; } } else { for (i = 0; i < 16; i++) { - gObjPalette[i + 0x80] = gUnknown_080D7B70[1][i]; + gObjPalette[8 * 16 + i] = gUnknown_080D7B70[1][i]; } } if (boss->cannonHitTimer != 0) { boss->cannonHitTimer--; for (i = 0; i < 16; i++) { - gObjPalette[i + 0xD0] = gUnknown_080D7B70[(gStageTime & 2) >> 1][i]; + gObjPalette[13 * 16 + i] = gUnknown_080D7B70[(gStageTime & 2) >> 1][i]; } } else { for (i = 0; i < 16; i++) { - gObjPalette[i + 0xD0] = gUnknown_080D7B70[1][i]; + gObjPalette[13 * 16 + i] = gUnknown_080D7B70[1][i]; } } diff --git a/src/game/bosses/boss_3.c b/src/game/bosses/boss_3.c index 333be2a0b..c5f77c98e 100644 --- a/src/game/bosses/boss_3.c +++ b/src/game/bosses/boss_3.c @@ -1505,11 +1505,11 @@ void sub_8040F14(EggTotem *totem) if (totem->unk35 != 0) { for (i = 0; i < 16; i++) { - gObjPalette[128 + i] = gUnknown_080D7F14[((gStageTime & 0x2) / 2u)][i]; + gObjPalette[8 * 16 + i] = gUnknown_080D7F14[((gStageTime & 0x2) / 2u)][i]; } } else { for (i = 0; i < 16; i++) { - gObjPalette[128 + i] = gUnknown_080D7F14[1][i]; + gObjPalette[8 * 16 + i] = gUnknown_080D7F14[1][i]; } } @@ -1517,11 +1517,11 @@ void sub_8040F14(EggTotem *totem) totem->unk36--; for (i = 0; i < 16; i++) { - gObjPalette[176 + i] = gUnknown_080D7F14[((gStageTime & 0x2) / 2u)][i]; + gObjPalette[11 * 16 + i] = gUnknown_080D7F14[((gStageTime & 0x2) / 2u)][i]; } } else { for (i = 0; i < 16; i++) { - gObjPalette[176 + i] = gUnknown_080D7F14[1][i]; + gObjPalette[11 * 16 + i] = gUnknown_080D7F14[1][i]; } } diff --git a/src/game/bosses/boss_4.c b/src/game/bosses/boss_4.c index 744d9d956..43316bd0b 100644 --- a/src/game/bosses/boss_4.c +++ b/src/game/bosses/boss_4.c @@ -762,11 +762,11 @@ static void sub_8042560(AeroEgg *boss) if (boss->main.unk16 != 0) { for (i = 0; i < ARRAY_COUNT(sPalAeroEggHit[PAL_BOSS_4_DEFAULT]); i++) { - gObjPalette[128 + i] = sPalAeroEggHit[((gStageTime & 0x2) >> 1)][i]; + gObjPalette[8 * 16 + i] = sPalAeroEggHit[((gStageTime & 0x2) >> 1)][i]; } } else { for (i = 0; i < ARRAY_COUNT(sPalAeroEggHit[PAL_BOSS_4_HIT]); i++) { - gObjPalette[128 + i] = sPalAeroEggHit[PAL_BOSS_4_HIT][i]; + gObjPalette[8 * 16 + i] = sPalAeroEggHit[PAL_BOSS_4_HIT][i]; } } diff --git a/src/game/bosses/boss_5.c b/src/game/bosses/boss_5.c index 241296e11..f5c0169dc 100644 --- a/src/game/bosses/boss_5.c +++ b/src/game/bosses/boss_5.c @@ -1757,22 +1757,22 @@ void sub_8045898(EggSaucer *boss) val = (gStageTime & 2) >> 1; if (boss->unk13 != 0) { for (i = 0; i < 0x10; i++) { - gObjPalette[i + 0x80] = gUnknown_080D7FF0[val][i]; + gObjPalette[8 * 16 + i] = gUnknown_080D7FF0[val][i]; } } else { for (i = 0; i < 0x10; i++) { - gObjPalette[i + 0x80] = gUnknown_080D7FF0[1][i]; + gObjPalette[8 * 16 + i] = gUnknown_080D7FF0[1][i]; } } if (boss->unk1F != 0) { boss->unk1F--; for (i = 0; i < 0x10; i++) { - gObjPalette[i + 0x90] = gUnknown_080D7FF0[val][i]; + gObjPalette[9 * 16 + i] = gUnknown_080D7FF0[val][i]; } } else { for (i = 0; i < 0x10; i++) { - gObjPalette[i + 0x90] = gUnknown_080D7FF0[1][i]; + gObjPalette[9 * 16 + i] = gUnknown_080D7FF0[1][i]; } } diff --git a/src/game/bosses/boss_6.c b/src/game/bosses/boss_6.c index 9e31221ae..0409e72db 100644 --- a/src/game/bosses/boss_6.c +++ b/src/game/bosses/boss_6.c @@ -1165,11 +1165,11 @@ static void SetPalette(EggGoRound *boss) u8 i; if (boss->invincibilityTimer > 0) { for (i = 0; i < 16; i++) { - gObjPalette[128 + i] = sPalettes[(gStageTime & 2) / 2][i]; + gObjPalette[8 * 16 + i] = sPalettes[(gStageTime & 2) / 2][i]; } } else { for (i = 0; i < 16; i++) { - gObjPalette[128 + i] = sPalettes[1][i]; + gObjPalette[8 * 16 + i] = sPalettes[1][i]; } } diff --git a/src/game/bosses/boss_9.c b/src/game/bosses/boss_9.c index b8b7d5687..297ec3adb 100644 --- a/src/game/bosses/boss_9.c +++ b/src/game/bosses/boss_9.c @@ -1385,7 +1385,7 @@ void sub_804E4CC(struct TA53_unk48 *unk48) b = sRGB_080D8E20[3][c][2]; b = ((b * r6) >> 12) & 0x1F; - gObjPalette[c + 8 * 16] = RGB16_REV(r, g, b); + gObjPalette[8 * 16 + c] = RGB16_REV(r, g, b); gBgPalette[c] = RGB16_REV(r, g, b); } @@ -2870,8 +2870,8 @@ void sub_8050958(TA53Boss *boss) if (boss->unkD > 0) { if (--boss->unkD == 0) { for (i = 0; i < 16; i++) { - gObjPalette[i + 8 * 16] = gUnknown_080D8EF0[1][i]; - gBgPalette[i + 0 * 16] = gObjPalette[i + 8 * 16]; + gObjPalette[8 * 16 + i] = gUnknown_080D8EF0[1][i]; + gBgPalette[0 * 16 + i] = gObjPalette[i + 8 * 16]; } } else { // _080509B0 @@ -2887,7 +2887,7 @@ void sub_8050958(TA53Boss *boss) u32 r0; u32 r2; u32 colId; - u16 *objPalTgt = &gObjPalette[0]; + u16 *objPalTgt = &gObjPalette[0 * 16 + 0]; u32 objPalId; colId = ((i + r6) % 16u); diff --git a/src/game/character_select.c b/src/game/character_select.c index 5257307d9..b1aabe56a 100644 --- a/src/game/character_select.c +++ b/src/game/character_select.c @@ -516,7 +516,7 @@ void CreateCharacterSelectionScreen(u8 initialSelection, bool8 allUnlocked) UpdateSpriteAnimation(s); for (i = 0; i < 16; i++) { - gObjPalette[i + 240] = 0; + gObjPalette[15 * 16 + i] = 0; } gFlags |= 0x2; diff --git a/src/game/cutscenes/extra_ending_fall.c b/src/game/cutscenes/extra_ending_fall.c index 7ce33dc55..e7e30f6e0 100644 --- a/src/game/cutscenes/extra_ending_fall.c +++ b/src/game/cutscenes/extra_ending_fall.c @@ -834,9 +834,9 @@ void sub_8090F6C(struct ExtraEndingCutScene *scene) if (scene->unk37F != 0) { scene->unk37F--; if (scene->unk37E <= 3) { - DmaCopy32(3, gUnknown_080E15C8[scene->unk37E], &gObjPalette[0x30], sizeof(gUnknown_080E15C8[0])); + DmaCopy32(3, gUnknown_080E15C8[scene->unk37E], &gObjPalette[3 * 16 + 0], sizeof(gUnknown_080E15C8[0])); } else { - DmaCopy32(3, gUnknown_080E15C8[6 - scene->unk37E], &gObjPalette[0x30], sizeof(gUnknown_080E15C8[0])); + DmaCopy32(3, gUnknown_080E15C8[6 - scene->unk37E], &gObjPalette[3 * 16 + 0], sizeof(gUnknown_080E15C8[0])); } } else { scene->unk37E++; diff --git a/src/game/multiboot/collect_rings/time_display.c b/src/game/multiboot/collect_rings/time_display.c index bb754d434..ce349f3af 100644 --- a/src/game/multiboot/collect_rings/time_display.c +++ b/src/game/multiboot/collect_rings/time_display.c @@ -86,7 +86,7 @@ void CreateCollectRingsTimeDisplay(void) } for (i = 0; i < 16; i++) { - gObjPalette[i + 0x70] = gUnknown_080E0270[i]; + gObjPalette[7 * 16 + i] = gUnknown_080E0270[i]; } gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; diff --git a/src/game/sa1_sa2_shared/pause_menu.c b/src/game/sa1_sa2_shared/pause_menu.c index 38ef95ccf..16881289a 100644 --- a/src/game/sa1_sa2_shared/pause_menu.c +++ b/src/game/sa1_sa2_shared/pause_menu.c @@ -182,8 +182,8 @@ void Task_PauseMenuInit(void) m4aSongNumStart(SE_PAUSE_SCREEN); gFlags |= FLAGS_PAUSE_GAME; - DmaCopy16(3, &gObjPalette[249], pm->pal64, sizeof(pm->pal64)); - DmaCopy16(3, &gObjPalette[252], pm->pal6A, sizeof(pm->pal6A)); + DmaCopy16(3, &gObjPalette[15 * 16 + 9], pm->pal64, sizeof(pm->pal64)); + DmaCopy16(3, &gObjPalette[15 * 16 + 12], pm->pal6A, sizeof(pm->pal6A)); gCurTask->main = Task_PauseMenuUpdate; } @@ -200,7 +200,7 @@ void sub_800AE58(void) { DmaCopy16(3, &Tileset_zone_1_act_1_fg[(4 * 16 * TILE_SIZE_4BPP) / sizeof(u16)], (void *)(OBJ_VRAM1 + 0x3EC0), 0x140); - gObjPalette[1] = RGB_WHITE; + gObjPalette[0 * 16 + 1] = RGB_WHITE; gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; gFlags &= ~FLAGS_PAUSE_GAME; } \ No newline at end of file diff --git a/src/game/special_stage/ui.c b/src/game/special_stage/ui.c index d11ee9036..829414298 100644 --- a/src/game/special_stage/ui.c +++ b/src/game/special_stage/ui.c @@ -335,8 +335,8 @@ static void HandlePaused(struct SpecialStageUI *ui) sub_806CA88(s, 1, pauseMenuVariants[lang][0], pauseMenuVariants[lang][1], 0x1000, (DISPLAY_WIDTH / 2), (DISPLAY_HEIGHT / 2), 0, pauseMenuVariants[lang][2], 0); - DmaCopy16(3, &gObjPalette[249], ui->pauseMenuPalette1, sizeof(ui->pauseMenuPalette1)); - DmaCopy16(3, &gObjPalette[252], ui->pauseMenuPalette2, sizeof(ui->pauseMenuPalette2)); + DmaCopy16(3, &gObjPalette[15 * 16 + 9], ui->pauseMenuPalette1, sizeof(ui->pauseMenuPalette1)); + DmaCopy16(3, &gObjPalette[15 * 16 + 12], ui->pauseMenuPalette2, sizeof(ui->pauseMenuPalette2)); ui->wasPaused = TRUE; m4aMPlayAllStop(); m4aSongNumStart(SE_PAUSE_SCREEN); diff --git a/src/game/stage/intro.c b/src/game/stage/intro.c index f18408765..da9f0e6e6 100644 --- a/src/game/stage/intro.c +++ b/src/game/stage/intro.c @@ -737,7 +737,7 @@ static void Task_802F9F8(void) g = ((gUnknown_080D6FF5[gSelectedCharacter][i][1] * frameCounter) / 16u); b = ((gUnknown_080D6FF5[gSelectedCharacter][i][2] * frameCounter) / 16u); - gObjPalette[i] = RGB16_REV(r, g, b); + gObjPalette[0 * 16 + i] = RGB16_REV(r, g, b); if (gCheese != NULL) { r = gUnknown_080D6FF5[5][i][0]; diff --git a/src/game/stage/ui.c b/src/game/stage/ui.c index eac9d749b..fb3589952 100644 --- a/src/game/stage/ui.c +++ b/src/game/stage/ui.c @@ -220,7 +220,7 @@ struct Task *CreateStageUI(void) ui->ringCurrentFrame = 0; for (i = 0; i < 16; i++) { - gObjPalette[0x70 + i] = sPalette1UpIcons[i]; + gObjPalette[7 * 16 + i] = sPalette1UpIcons[i]; } gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; From 2bd592edb67243393d107bb2f57b455988b30c55 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:53:36 +0100 Subject: [PATCH 10/19] Some VRAM Heap documentation --- include/core.h | 2 +- include/gba/defines.h | 3 ++ include/malloc_vram.h | 5 +-- src/core.c | 2 +- src/malloc_vram.c | 44 +++++++++++++++++++++--- src/platform/win32/opengl.c | 68 +++++++++++++++++++++++++++++++------ src/platform/win32/win32.c | 2 +- 7 files changed, 105 insertions(+), 21 deletions(-) diff --git a/include/core.h b/include/core.h index 81dd790b7..15a635d2e 100644 --- a/include/core.h +++ b/include/core.h @@ -251,7 +251,7 @@ extern u8 gIwramHeap[TASK_HEAP_SIZE]; extern void *gVramHeapStartAddr; extern u16 gVramHeapMaxTileSlots; -extern u16 gVramHeapState[256]; +extern u16 gVramHeapState[OBJ_VRAM_TOTAL_SIZE / VRAM_HEAP_SEGMENT_SIZE]; extern bool8 gExecSoundMain; diff --git a/include/gba/defines.h b/include/gba/defines.h index b904ee74d..d11003525 100644 --- a/include/gba/defines.h +++ b/include/gba/defines.h @@ -82,6 +82,7 @@ extern uint8_t OAM[OAM_SIZE]; #define OBJ_VRAM0 &VRAM[0x10000] #define OBJ_VRAM1 &VRAM[0x14000] +#define OBJ_VRAM_TOTAL_SIZE (VRAM_SIZE - BG_VRAM_SIZE) #else #define DISPLAY_WIDTH 240 #define DISPLAY_HEIGHT 160 @@ -118,8 +119,10 @@ extern uint8_t OAM[OAM_SIZE]; #define OAM 0x7000000 #define OAM_SIZE (OAM_ENTRY_COUNT*sizeof(OamData)) +#define OBJ_VRAM_TOTAL_SIZE (VRAM_SIZE - BG_VRAM_SIZE) #endif + #if WIDESCREEN_HACK #define WIN_REG_SIZE 4 #define WIN_RANGE(a, b) (((a) << 16) | (b)) diff --git a/include/malloc_vram.h b/include/malloc_vram.h index 5f69afce4..a50c328a2 100644 --- a/include/malloc_vram.h +++ b/include/malloc_vram.h @@ -3,8 +3,9 @@ #include "global.h" -#define VRAM_HEAP_SEGMENT_SIZE 0x80 -#define VRAM_TILE_SLOTS_PER_SEGMENT (VRAM_HEAP_SEGMENT_SIZE / TILE_SIZE_4BPP) +#define VRAM_HEAP_TILE_SIZE TILE_SIZE_4BPP +#define VRAM_HEAP_SEGMENT_SIZE (4 * VRAM_HEAP_TILE_SIZE) +#define VRAM_TILE_SLOTS_PER_SEGMENT (VRAM_HEAP_SEGMENT_SIZE / VRAM_HEAP_TILE_SIZE) // TODO: Find out where these numbers come from #if (ENGINE == ENGINE_1) diff --git a/src/core.c b/src/core.c index 4bd4c0782..a0543ef7f 100644 --- a/src/core.c +++ b/src/core.c @@ -159,7 +159,7 @@ u8 gUnknown_0300620C ALIGNED(4) = 0; u8 gOamMallocOrders_EndIndex[] ALIGNED(16) = {}; u8 gBgSprites_Unknown1[] = {}; OamData gOamBuffer[] ALIGNED(16) = {}; -u16 gVramHeapState[] = {}; +u16 gVramHeapState[OBJ_VRAM_TOTAL_SIZE / VRAM_HEAP_SEGMENT_SIZE] = {}; u8 gBgSpritesCount ALIGNED(4) = 0; u16 SA2_LABEL(gUnknown_03005394) ALIGNED(4) = 0; u16 SA2_LABEL(gUnknown_03005398) ALIGNED(4) = 0; diff --git a/src/malloc_vram.c b/src/malloc_vram.c index 149c5940e..c20fec1d8 100644 --- a/src/malloc_vram.c +++ b/src/malloc_vram.c @@ -8,15 +8,48 @@ #define HEAP_END ewram_end #endif +/* --- HOW THIS WORKS --- + * + * gVramHeapState[] represents (or *can* represent) the entire Object Tiles memory range in VRAM (0x06010000 -> 0x06018000). + * Whereby each u16 word in gVramHeapState contains a reserved number of segments. + * Each segment in SA2: 4 tiles * size-per-tile = (4 * 0x20 | 0x80) [segment tile-count might vary per game] + * + * I say *can*, because in practice, there's a much smaller available heap. + * In practice it starts at: + * 0x06013200 in SA1 + * 0x06013A00 in SA2 + * 0x06012C50 in SA3 + * ( OBJ_VRAM1 - (VRAM_HEAP_TILE_COUNT * TILE_SIZE_4BPP) ) + * + * Take this representation for example: + * + * Imagine every digit being a u16 word. + * 0000000000000000 + * ^ + * This is basically an entirely empty VRAM heap, so starting with the first byte, some number of segments could be reserved. + * Note that VramResetHeapState() just clears the entire gVramHeapState[], so anything allocated before would be allocateable by a new + * VramMalloc() call. + * + * Below, each * is a word reserved by the segment-count preceding it, so those cannot be allocated. + * 6*****4***000000 + * ^ ^ + * | | + * +-----+--- 6 allocated segments (24 tiles) + * | + * 4 allocated segments (16 tiles) + * + * Note that the count itself is part of the number of allocated segments + */ + void *VramMalloc(u32 numTiles) { u16 i, j; - u32 count = numTiles; - count = (count + (VRAM_TILE_SLOTS_PER_SEGMENT - 1)) / VRAM_TILE_SLOTS_PER_SEGMENT; // round up + u32 segCount = numTiles; + segCount = (segCount + (VRAM_TILE_SLOTS_PER_SEGMENT - 1)) / VRAM_TILE_SLOTS_PER_SEGMENT; // round up for (i = 0; i < gVramHeapMaxTileSlots / VRAM_TILE_SLOTS_PER_SEGMENT; i++) { if (gVramHeapState[i] == 0) { - for (j = 0; j < count; j++) { + for (j = 0; j < segCount; j++) { if (i + j >= (gVramHeapMaxTileSlots / VRAM_TILE_SLOTS_PER_SEGMENT)) { return HEAP_END; } @@ -25,8 +58,8 @@ void *VramMalloc(u32 numTiles) } } - if (j == count) { - gVramHeapState[i] = count; + if (j == segCount) { + gVramHeapState[i] = segCount; return (void *)(gVramHeapStartAddr + i * VRAM_HEAP_SEGMENT_SIZE); } } else { @@ -36,6 +69,7 @@ void *VramMalloc(u32 numTiles) return HEAP_END; } +// Clear the entire gVramHeapState[], leading to zero tiles being allocated (even if OBJ_VRAM is populated) void VramResetHeapState(void) { DmaFill16(3, 0, gVramHeapState, sizeof(gVramHeapState)); } void VramFree(void *addr) diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index ff86367b5..9daddc922 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -39,6 +39,7 @@ typedef struct { static TextureBuffer sDynTextureBuffer = { 0 }; ColorRGBA tempRgbaPalette[16 * 32] = {}; +static Background *sActiveBackgrounds[4] = { 0 }; void Debug_PrintMatrix(float *mtx) { @@ -363,8 +364,22 @@ void UpdateChunkGfx(ChunkGfx *gfx, Background *bg) #endif } +Background *SwitchActiveBackground(Background *bg) +{ + u8 bgId = bg->flags & BACKGROUND_FLAGS_MASK_BG_ID; + + Background *prev = sActiveBackgrounds[bgId]; + sActiveBackgrounds[bgId] = bg; + return prev; +} + void OpenGL_ProcessBackgroundsCopyQueue(void) { + // 'renderOrder' contains one background ID in each slot. + u8 renderOrder[4] = { 0 }; + u8 orderIndex = 0; + bool8 needsUpdate[4] = { 0 }; + while (gBackgroundsCopyQueueCursor != gBackgroundsCopyQueueIndex) { Background *bg = gBackgroundsCopyQueue[gBackgroundsCopyQueueCursor]; INC_BACKGROUNDS_QUEUE_CURSOR(gBackgroundsCopyQueueCursor); @@ -372,18 +387,49 @@ void OpenGL_ProcessBackgroundsCopyQueue(void) if ((bg->flags & BACKGROUND_FLAG_20) && (bg->scrollX == bg->prevScrollX) && bg->scrollY == bg->prevScrollY) continue; - if (bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP) { - UpdateChunkGfx(&sChunkGfx, bg); - } else { -#if 0 - // TEMP!!! - // DON'T MALLOC WITHOUT FREEING!!! - // (Doing it like this only works for 1 frame, anyway) - void *tilemapRGBA = malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA); - TempConvertPLTTToRGBA8(); - RenderTilemap(tilemapRGBA, bg, 0); - OpenGL_RenderRGBABuffer(tilemapRGBA, bg->xTiles * 8, bg->yTiles * 8, 0, -DISPLAY_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); + Background *prev = SwitchActiveBackground(bg); + u8 bgId = bg->flags & BACKGROUND_FLAGS_MASK_BG_ID; + needsUpdate[bgId] = (prev != bg); + } + + for (int prioIndex = 3; prioIndex >= 0; prioIndex--) { + for (int i = 3; i >= 0; i--) { + u8 bgPrio = gBgCntRegs[i] & BGCNT_PRIORITY(0x3); + if (bgPrio == prioIndex) { + renderOrder[orderIndex] = i; + + if (++orderIndex == 4) { + goto loopBreak; + } + } + } + } +loopBreak: + + for (orderIndex = 0; orderIndex < 4; orderIndex++) { + u8 bgId = renderOrder[orderIndex]; + Background *bg = sActiveBackgrounds[bgId]; + + // TODO: Find way to go without REG_DISPCNT! + if (REG_DISPCNT & (DISPCNT_BG0_ON << bgId)) { + if (bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP) { + UpdateChunkGfx(&sChunkGfx, bg); + } else { +#if 01 + // TEMP!!! + // DON'T MALLOC AND FREE TILEMAPS ALL THE TIME!!! + // (Also currently it's possible to get corrupted Background pointers, leading to crashes) + //if (needsUpdate[bgId]) { + //} + //bg->graphics.dest = (needsUpdate[bgId]) ? malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA) : bg->graphics.dest; + bg->graphics.dest = malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA); + TempConvertPLTTToRGBA8(); + RenderTilemap(bg->graphics.dest, bg, 0); + OpenGL_RenderRGBABuffer(bg->graphics.dest, bg->xTiles * 8, bg->yTiles * 8, 0, 0, bg->targetTilesX * 8, + bg->targetTilesY * 8); + free(bg->graphics.dest); #endif + } } } } diff --git a/src/platform/win32/win32.c b/src/platform/win32/win32.c index 3cb2c1263..9d3bb40da 100644 --- a/src/platform/win32/win32.c +++ b/src/platform/win32/win32.c @@ -123,7 +123,7 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR lpCmdLine, DWORD threadId; #if 01 - REG_KEYINPUT &= ~START_BUTTON; + // REG_KEYINPUT &= ~START_BUTTON; while (sRunning) { memset(sImageBuffer, 0, sizeof(sImageBuffer)); From 0794c207d825013f52ef01fdee94d7487143a947 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Sat, 14 Feb 2026 20:22:15 +0100 Subject: [PATCH 11/19] Use MetatileIndexType in Background::metatileMap --- include/sprite.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sprite.h b/include/sprite.h index 255f3ef19..21464bca5 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -118,7 +118,7 @@ typedef struct { /* 0x36 */ u16 prevScrollY; /* Only used by stage maps (they are encoded as Tilemaps) */ - /* 0x38 */ const u16 *metatileMap; + /* 0x38 */ const MetatileIndexType *metatileMap; /* 0x3C */ u16 mapWidth; /* 0x3E */ u16 mapHeight; } Background; /* size = 0x40 */ From 2b951413a0221fcb0c215889fba5e11923106092 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Sun, 15 Feb 2026 12:55:23 +0100 Subject: [PATCH 12/19] Stretch Sonic Team logo on entire screen. Keep gBgScrollRegs in mind for positioning BGs --- src/game/title_screen.c | 6 +++--- src/platform/win32/opengl.c | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/game/title_screen.c b/src/game/title_screen.c index 8da0b8a63..1ca73ecdc 100644 --- a/src/game/title_screen.c +++ b/src/game/title_screen.c @@ -684,8 +684,8 @@ static void Task_IntroStartSonicTeamLogoAnim(void) bg->unk20 = 0; bg->unk22 = 0; bg->unk24 = 0; - bg->targetTilesX = 30; - bg->targetTilesY = 20; + bg->targetTilesX = DISPLAY_WIDTH / 8; + bg->targetTilesY = DISPLAY_HEIGHT / 8; bg->paletteOffset = 0; bg->flags = 0x10; DrawBackground(bg); @@ -846,7 +846,7 @@ static void Task_IntroPanSkyAnim(void) bg0->flags = 0x10; DrawBackground(bg0); - gBgScrollRegs[0][1] = 0x4F; + gBgScrollRegs[0][1] = 79; gCurTask->main = Task_IntroSkyAnim; titleScreen->animFrame = 0; diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 9daddc922..225de9f3f 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -409,6 +409,8 @@ void OpenGL_ProcessBackgroundsCopyQueue(void) for (orderIndex = 0; orderIndex < 4; orderIndex++) { u8 bgId = renderOrder[orderIndex]; Background *bg = sActiveBackgrounds[bgId]; + s16 bgScrollX = gBgScrollRegs[bgId][0]; + s16 bgScrollY = gBgScrollRegs[bgId][1]; // TODO: Find way to go without REG_DISPCNT! if (REG_DISPCNT & (DISPCNT_BG0_ON << bgId)) { @@ -419,14 +421,15 @@ void OpenGL_ProcessBackgroundsCopyQueue(void) // TEMP!!! // DON'T MALLOC AND FREE TILEMAPS ALL THE TIME!!! // (Also currently it's possible to get corrupted Background pointers, leading to crashes) - //if (needsUpdate[bgId]) { + // if (needsUpdate[bgId]) { //} - //bg->graphics.dest = (needsUpdate[bgId]) ? malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA) : bg->graphics.dest; + // bg->graphics.dest = (needsUpdate[bgId]) ? malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA) : + // bg->graphics.dest; bg->graphics.dest = malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA); TempConvertPLTTToRGBA8(); RenderTilemap(bg->graphics.dest, bg, 0); - OpenGL_RenderRGBABuffer(bg->graphics.dest, bg->xTiles * 8, bg->yTiles * 8, 0, 0, bg->targetTilesX * 8, - bg->targetTilesY * 8); + OpenGL_RenderRGBABuffer(bg->graphics.dest, bg->xTiles * 8, bg->yTiles * 8, bgScrollX, bgScrollY, + bgScrollX + bg->targetTilesX * 8, bgScrollY + bg->targetTilesY * 8); free(bg->graphics.dest); #endif } @@ -461,7 +464,8 @@ void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform) { glMatrixMode(GL_MODELVIEW); s16 rotation = (transform->rotation & ONE_CYCLE); - float theta = -(2.0f * M_PI * (((float)rotation) / 1024.0f)); + float rotNormalized = (((float)rotation) / 1024.0f); + float theta = -(2.0f * M_PI * rotNormalized); float sinTheta = sinf(theta); float cosTheta = cosf(theta); float modelViewMatrix[] = { @@ -501,8 +505,9 @@ void OpenGL_TransformSprite(Sprite *sprite, SpriteTransform *transform) 1, #endif }; - glLoadMatrixf(modelViewMatrix); - // glTranslatef(transform->x, transform->y, 0); + + // glLoadMatrixf(modelViewMatrix); + // glTranslatef(transform->x, transform->y, 0); // Debug_PrintMatrix(modelViewMatrix); From 4e10974d4319765eaa459b2cf2f17e73f561ee79 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:14:43 +0100 Subject: [PATCH 13/19] Formatter. Fix realloc in win32. Fix crash in Zone 4 --- include/global.h | 2 ++ src/game/stage/camera.c | 5 +++++ src/platform/win32/opengl.c | 23 +++++++++++------------ src/platform/win32/win32.c | 10 +++++++++- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/include/global.h b/include/global.h index 946528d0d..8f186cacb 100644 --- a/include/global.h +++ b/include/global.h @@ -19,9 +19,11 @@ #if !PLATFORM_GBA #ifdef _WIN32 void *Platform_malloc(size_t numBytes); +void *Platform_realloc(void *ptr, size_t numBytes); void Platform_free(void *ptr); #define malloc(numBytes) Platform_malloc(numBytes) #define calloc(count, size) Platform_malloc(count *size) +#define realloc(ptr, size) Platform_realloc(ptr, size) #define free(numBytes) Platform_free(numBytes) #endif #endif diff --git a/src/game/stage/camera.c b/src/game/stage/camera.c index 395f02c7f..a7f71a97a 100644 --- a/src/game/stage/camera.c +++ b/src/game/stage/camera.c @@ -1234,8 +1234,13 @@ void StageBgUpdate_Zone4Acts12(s32 cameraX, s32 cameraY) gBldRegs.bldAlpha = 0xc0c; } +#ifdef BUG_FIX + UpdateBgAnimationTiles(&gStageBackgroundsRam.unk0); + DrawBackground(&gStageBackgroundsRam.unk0); +#else DrawBackground(&gStageBackgroundsRam.unk0); UpdateBgAnimationTiles(&gStageBackgroundsRam.unk0); +#endif if ((gStageTime % 16u) == 0) { gBgScrollRegs[0][0] = (gBgScrollRegs[0][0] - 1) & 0xff; diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 225de9f3f..540e139bc 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -185,10 +185,7 @@ void OpenGL_RenderRGBABuffer(u8 *buffer, u16 bufferWidth, u16 bufferHeight, floa void CreateChunkSet(void) { const int defaultCap = 16; - // NOTE(Jace): For some reason, malloc and realloc (at least with current compile settings on Win32) use different heaps. - // That means, calling realloc with a pointer from malloc, which *should* work, crashes inside realloc. - // So for dynamic memory, we need to init with realloc for now... - void *mem = realloc(NULL, defaultCap * sizeof(*sChunkSet.items)); + void *mem = malloc(defaultCap * sizeof(*sChunkSet.items)); sChunkSet.items = mem; sChunkSet.items[0] = 0; sChunkSet.count = 1; // initialize to 1, to skip zero-filled chunk (which always exists) @@ -373,6 +370,10 @@ Background *SwitchActiveBackground(Background *bg) return prev; } +// TODO/TEMP: The VRAM memory mimmicking the GBA, used by the software-renderer, shall not be part "hardware renderers". +// We need to initialize all sprites and tilemaps without hardcoded values (as happens in the base games). +#define IN_VRAM(ptr) (((ptr) >= (void *)&VRAM[0]) && ((ptr) < (void *)&VRAM[VRAM_SIZE])) + void OpenGL_ProcessBackgroundsCopyQueue(void) { // 'renderOrder' contains one background ID in each slot. @@ -417,21 +418,19 @@ void OpenGL_ProcessBackgroundsCopyQueue(void) if (bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP) { UpdateChunkGfx(&sChunkGfx, bg); } else { -#if 01 // TEMP!!! // DON'T MALLOC AND FREE TILEMAPS ALL THE TIME!!! // (Also currently it's possible to get corrupted Background pointers, leading to crashes) - // if (needsUpdate[bgId]) { - //} - // bg->graphics.dest = (needsUpdate[bgId]) ? malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA) : - // bg->graphics.dest; - bg->graphics.dest = malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA); + if (needsUpdate[bgId]) { + if (bg->graphics.dest && !IN_VRAM(bg->graphics.dest)) { + free(bg->graphics.dest); + } + bg->graphics.dest = malloc((bg->xTiles * 8) * (bg->yTiles * 8) * TILE_SIZE_RGBA); + } TempConvertPLTTToRGBA8(); RenderTilemap(bg->graphics.dest, bg, 0); OpenGL_RenderRGBABuffer(bg->graphics.dest, bg->xTiles * 8, bg->yTiles * 8, bgScrollX, bgScrollY, bgScrollX + bg->targetTilesX * 8, bgScrollY + bg->targetTilesY * 8); - free(bg->graphics.dest); -#endif } } } diff --git a/src/platform/win32/win32.c b/src/platform/win32/win32.c index 9d3bb40da..21d27dbf9 100644 --- a/src/platform/win32/win32.c +++ b/src/platform/win32/win32.c @@ -462,7 +462,15 @@ void VBlankIntrWait() } void *Platform_malloc(size_t numBytes) { return HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, numBytes); } - +void *Platform_realloc(void *ptr, size_t numBytes) +{ + if (ptr == NULL) { + // HeapReAlloc returns NULL when called with NULL, unlike C std realloc(). + return HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, numBytes); + } else { + return HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, ptr, numBytes); + } +} void Platform_free(void *ptr) { HeapFree(GetProcessHeap(), 0, ptr); } void Platform_QueueAudio(const u8 *data, u32 numBytes) { } From 010920aefffd2dd892a548831f1e7ecdb6c038c3 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Sun, 15 Feb 2026 17:56:15 +0100 Subject: [PATCH 14/19] Render 8BPP tilemaps --- src/platform/win32/opengl.c | 64 ++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 540e139bc..a5e223359 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -227,6 +227,8 @@ void RenderTilemap(ColorRGBA *dstBuffer, Background *bg, int chunkIndex) { const u8 *tileset = bg->graphics.src; int chunkTileCount = bg->xTiles * bg->yTiles; + u8 bgId = bg->flags & BACKGROUND_FLAGS_MASK_BG_ID; + bool8 mapIs4BPP = (gBgCntRegs[bgId] & BGCNT_256COLOR) ? FALSE : TRUE; if (!(bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP)) { // NOTE(Jace): Non-stage maps do not have any "chunks", @@ -239,25 +241,49 @@ void RenderTilemap(ColorRGBA *dstBuffer, Background *bg, int chunkIndex) // int tileId = tileIdY * bg->xTiles + tileIdX; ColorRGBA *dstTileRGBA = &dstBuffer[tileIdY * 8 * (bg->xTiles * 8) + tileIdX * 8]; - int tileInChunkIndex = (chunkIndex * chunkTileCount) + tileIdY * bg->xTiles + tileIdX; - Tile tile = *(const Tile *)&bg->layout[tileInChunkIndex]; - const u8 *srcTile4BPP = &tileset[(tile.index * TILE_SIZE_PIXELS) >> 1]; - - for (int tileLoopY = 0; tileLoopY < 8; tileLoopY++) { - int tileY = (tile.yFlip) ? (7 - tileLoopY) : tileLoopY; - for (int tileLoopX = 0; tileLoopX < 8; tileLoopX++) { - int tileX = (tile.xFlip) ? (7 - tileLoopX) : tileLoopX; - - int targetColorIndex = ((tileY * 8) + tileX); - bool8 doShift = (targetColorIndex & 1); - int colorId = srcTile4BPP[targetColorIndex >> 1] & (0xF << (doShift * 4)); - colorId >>= doShift * 4; - - ColorRGBA *tilePalette = (ColorRGBA *)(&tempRgbaPalette[tile.pal * 16 + colorId]); - dstTileRGBA[tileLoopY * (bg->xTiles * 8) + tileLoopX].r = tilePalette->r; - dstTileRGBA[tileLoopY * (bg->xTiles * 8) + tileLoopX].g = tilePalette->g; - dstTileRGBA[tileLoopY * (bg->xTiles * 8) + tileLoopX].b = tilePalette->b; - dstTileRGBA[tileLoopY * (bg->xTiles * 8) + tileLoopX].a = (colorId == 0) ? 0x00 : 0xFF; + if (mapIs4BPP) { + int tileInChunkIndex = (chunkIndex * chunkTileCount) + tileIdY * bg->xTiles + tileIdX; + Tile tile = *(const Tile *)&bg->layout[tileInChunkIndex]; + const u8 *srcTile4BPP = &tileset[(tile.index * TILE_SIZE_PIXELS) >> 1]; + + for (int tileLoopY = 0; tileLoopY < 8; tileLoopY++) { + int tileY = (tile.yFlip) ? (7 - tileLoopY) : tileLoopY; + for (int tileLoopX = 0; tileLoopX < 8; tileLoopX++) { + int tileX = (tile.xFlip) ? (7 - tileLoopX) : tileLoopX; + + int targetColorIndex = ((tileY * 8) + tileX); + bool8 doShift = (targetColorIndex & 1); + int colorId = srcTile4BPP[targetColorIndex >> 1] & (0xF << (doShift * 4)); + colorId >>= doShift * 4; + + ColorRGBA *tilePalette = (ColorRGBA *)(&tempRgbaPalette[tile.pal * 16 + colorId]); + int dstTileIndex = tileLoopY * (bg->xTiles * 8) + tileLoopX; + dstTileRGBA[dstTileIndex].r = tilePalette->r; + dstTileRGBA[dstTileIndex].g = tilePalette->g; + dstTileRGBA[dstTileIndex].b = tilePalette->b; + dstTileRGBA[dstTileIndex].a = (colorId == 0) ? 0x00 : 0xFF; + } + } + } else { + // 8BPP + u8 tile = ((const u8 *)bg->layout)[tileIdY * bg->xTiles + tileIdX]; + const u8 *srcTile8BPP = &tileset[tile * TILE_SIZE_PIXELS]; + + for (int tileLoopY = 0; tileLoopY < 8; tileLoopY++) { + int tileY = tileLoopY; + for (int tileLoopX = 0; tileLoopX < 8; tileLoopX++) { + int tileX = tileLoopX; + + int targetColorIndex = ((tileY * 8) + tileX); + int colorId = srcTile8BPP[targetColorIndex]; + + ColorRGBA *tilePalette = (ColorRGBA *)(&tempRgbaPalette[colorId]); + int dstTileIndex = tileLoopY * (bg->xTiles * 8) + tileLoopX; + dstTileRGBA[dstTileIndex].r = tilePalette->r; + dstTileRGBA[dstTileIndex].g = tilePalette->g; + dstTileRGBA[dstTileIndex].b = tilePalette->b; + dstTileRGBA[dstTileIndex].a = (colorId == 0) ? 0x00 : 0xFF; + } } } } From 40565cf18dd397bc9ea3e0b32b565cdd56db410e Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Sun, 15 Feb 2026 19:03:38 +0100 Subject: [PATCH 15/19] Some crash fixes --- src/game/sa1_sa2_shared/unused_level_select.c | 7 +++++++ src/game/special_stage/utils.c | 7 +++++++ src/platform/win32/opengl.c | 3 +-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/game/sa1_sa2_shared/unused_level_select.c b/src/game/sa1_sa2_shared/unused_level_select.c index aae79bbe8..68082beec 100644 --- a/src/game/sa1_sa2_shared/unused_level_select.c +++ b/src/game/sa1_sa2_shared/unused_level_select.c @@ -37,6 +37,13 @@ static void Task_LoadStage(void); void CreateUnusedLevelSelect(void) { +#ifdef BUG_FIX + // You can come into a situation where a backgorund gets put onto the queue, + // but the memory gets free'd. + // So we need to make sure the Background Copy Queue is clear. + PAUSE_BACKGROUNDS_QUEUE(); + PAUSE_GRAPHICS_QUEUE(); +#endif struct Task *t = TaskCreate(Task_UnusedLevelSelectInit, sizeof(LevelSelect), 0x2000, 0, NULL); gMultiplayerMissingHeartbeats[3] = 0; gMultiplayerMissingHeartbeats[2] = 0; diff --git a/src/game/special_stage/utils.c b/src/game/special_stage/utils.c index 6f9adc369..323e5f298 100644 --- a/src/game/special_stage/utils.c +++ b/src/game/special_stage/utils.c @@ -201,6 +201,13 @@ void InitSpecialStageScreenVram(void) { gSpecialStageVramPointer = (void *)OBJ_VRAM0; gSpecialStageSubMenuVramPointer = NULL; +#ifdef BUG_FIX + // You can come into a situation where a backgorund gets put onto the queue, + // but the memory gets free'd. + // So we need to make sure the Background Copy Queue is clear. + PAUSE_BACKGROUNDS_QUEUE(); + PAUSE_GRAPHICS_QUEUE(); +#endif } void SpecialStageDrawBackground(Background *background, u32 a, u32 b, u8 tilemapId, u16 d, u16 e, u16 palOffset, u8 bg_id, u16 scrollX, diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index a5e223359..8dfc59dcb 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -439,8 +439,7 @@ void OpenGL_ProcessBackgroundsCopyQueue(void) s16 bgScrollX = gBgScrollRegs[bgId][0]; s16 bgScrollY = gBgScrollRegs[bgId][1]; - // TODO: Find way to go without REG_DISPCNT! - if (REG_DISPCNT & (DISPCNT_BG0_ON << bgId)) { + if (gDispCnt & (DISPCNT_BG0_ON << bgId)) { if (bg->flags & BACKGROUND_FLAG_IS_LEVEL_MAP) { UpdateChunkGfx(&sChunkGfx, bg); } else { From be0d30094b719bbbc0bb8e7f657fe0dbc2f0d6e4 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:17:32 +0100 Subject: [PATCH 16/19] Minor priority documentation --- src/platform/win32/opengl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/win32/opengl.c b/src/platform/win32/opengl.c index 8dfc59dcb..b03815a14 100644 --- a/src/platform/win32/opengl.c +++ b/src/platform/win32/opengl.c @@ -420,10 +420,10 @@ void OpenGL_ProcessBackgroundsCopyQueue(void) } for (int prioIndex = 3; prioIndex >= 0; prioIndex--) { - for (int i = 3; i >= 0; i--) { - u8 bgPrio = gBgCntRegs[i] & BGCNT_PRIORITY(0x3); + for (int bgId = 3; bgId >= 0; bgId--) { + u8 bgPrio = gBgCntRegs[bgId] & BGCNT_PRIORITY(0x3); if (bgPrio == prioIndex) { - renderOrder[orderIndex] = i; + renderOrder[orderIndex] = bgId; if (++orderIndex == 4) { goto loopBreak; From 2813a51652aa4ab4b30b7f6be9a2734525fb761a Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Mon, 16 Feb 2026 23:32:28 +0100 Subject: [PATCH 17/19] Address explicit gVramHeapState size in initialization --- src/core.c | 2 +- src/lib/m4a/m4a0.s | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core.c b/src/core.c index e51d70254..ea52547b7 100644 --- a/src/core.c +++ b/src/core.c @@ -159,7 +159,7 @@ u8 gUnknown_0300620C ALIGNED(4) = 0; u8 gOamMallocOrders_EndIndex[] ALIGNED(16) = {}; u8 gBgSprites_Unknown1[] = {}; OamData gOamBuffer[] ALIGNED(16) = {}; -u16 gVramHeapState[OBJ_VRAM_TOTAL_SIZE / VRAM_HEAP_SEGMENT_SIZE] = {}; +u16 gVramHeapState[] = {}; u8 gBgSpritesCount ALIGNED(4) = 0; u16 SA2_LABEL(gUnknown_03005394) ALIGNED(4) = 0; u16 SA2_LABEL(gUnknown_03005398) ALIGNED(4) = 0; diff --git a/src/lib/m4a/m4a0.s b/src/lib/m4a/m4a0.s index 7f000b7f0..d5bccca9a 100644 --- a/src/lib/m4a/m4a0.s +++ b/src/lib/m4a/m4a0.s @@ -25,7 +25,7 @@ SoundMain: ldr r3, [r0, o_SoundInfo_ident] cmp r2, r3 beq SoundMain_1 - bx lr @ Exit the function if ident doesn't match ID_NUMBER. + bx lr @ Exit the function if ident does not match ID_NUMBER. SoundMain_1: adds r3, 1 str r3, [r0, o_SoundInfo_ident] From a8e70b539a82ed1d7b70232b8764cedcb9b1134a Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Tue, 17 Feb 2026 01:38:07 +0100 Subject: [PATCH 18/19] Streamline most uses of gObjPalette --- include/core.h | 11 +++++- src/core.c | 8 ++-- src/game/boost_effect.c | 2 +- src/game/bosses/boss_1.c | 6 +-- src/game/bosses/boss_2.c | 18 ++++----- src/game/bosses/boss_3.c | 18 ++++----- src/game/bosses/boss_4.c | 10 ++--- src/game/bosses/boss_5.c | 21 ++++++----- src/game/bosses/boss_6.c | 12 +++--- src/game/bosses/boss_8.c | 14 +++---- src/game/bosses/boss_9.c | 37 ++++++++----------- src/game/character_select.c | 6 +-- src/game/cutscenes/extra_ending_fall.c | 4 +- .../multiboot/collect_rings/time_display.c | 6 +-- src/game/sa1_sa2_shared/pause_menu.c | 6 +-- src/game/special_stage/ui.c | 4 +- src/game/stage/intro.c | 16 ++++---- src/game/stage/ui.c | 6 +-- src/sprite.c | 10 +++-- 19 files changed, 109 insertions(+), 106 deletions(-) diff --git a/include/core.h b/include/core.h index 9a9440a58..5a7c57ede 100644 --- a/include/core.h +++ b/include/core.h @@ -264,11 +264,18 @@ extern u16 gDispCnt; #define WINREG_WININ 4 #define WINREG_WINOUT 5 +#define PALETTE_LEN_4BPP 16u +#define GET_PALETTE_COLOR_OBJ(_paletteId, _colorId) gObjPalette[(_paletteId)*PALETTE_LEN_4BPP + (_colorId)] +#define GET_PALETTE_COLOR_BG(_paletteId, _colorId) gBgPalette[(_paletteId)*PALETTE_LEN_4BPP + (_colorId)] +#define SET_PALETTE_COLOR_OBJ(_paletteId, _colorId, _color) GET_PALETTE_COLOR_OBJ(_paletteId, _colorId) = (_color); +#define SET_PALETTE_COLOR_BG(_paletteId, _colorId, _color) GET_PALETTE_COLOR_BG(_paletteId, _colorId) = (_color); + extern winreg_t gWinRegs[6]; extern struct BlendRegs gBldRegs; extern BgAffineReg gBgAffineRegs[NUM_AFFINE_BACKGROUNDS]; -extern u16 gObjPalette[OBJ_PLTT_SIZE / sizeof(u16)]; -extern u16 gBgPalette[BG_PLTT_SIZE / sizeof(u16)]; +extern u16 gObjPalette[16 * PALETTE_LEN_4BPP]; +extern u16 gBgPalette[16 * PALETTE_LEN_4BPP]; + extern u16 gBgCntRegs[4]; // TODO: Turn this into a struct-array? diff --git a/src/core.c b/src/core.c index ea52547b7..fcbe84850 100644 --- a/src/core.c +++ b/src/core.c @@ -622,12 +622,12 @@ void UpdateScreenDma(void) DmaCopy32(3, gBgCntRegs, (void *)REG_ADDR_BG0CNT, sizeof(gBgCntRegs)); if (gFlags & FLAGS_UPDATE_BACKGROUND_PALETTES) { - DmaCopy32(3, gBgPalette, (void *)BG_PLTT, BG_PLTT_SIZE); + DmaCopy32(3, gBgPalette, (void *)BG_PLTT, sizeof(gBgPalette)); gFlags ^= FLAGS_UPDATE_BACKGROUND_PALETTES; } if (gFlags & FLAGS_UPDATE_SPRITE_PALETTES) { - DmaCopy32(3, gObjPalette, (void *)OBJ_PLTT, OBJ_PLTT_SIZE); + DmaCopy32(3, gObjPalette, (void *)OBJ_PLTT, sizeof(gObjPalette)); gFlags ^= FLAGS_UPDATE_SPRITE_PALETTES; } @@ -761,12 +761,12 @@ void UpdateScreenCpuSet(void) CpuCopy32(gBgCntRegs, (void *)REG_ADDR_BG0CNT, sizeof(gBgCntRegs)); if (gFlags & FLAGS_UPDATE_BACKGROUND_PALETTES) { - CpuFastCopy(gBgPalette, (void *)BG_PLTT, BG_PLTT_SIZE); + CpuFastCopy(gBgPalette, (void *)BG_PLTT, sizeof(gBgPalette)); gFlags ^= FLAGS_UPDATE_BACKGROUND_PALETTES; } if (gFlags & FLAGS_UPDATE_SPRITE_PALETTES) { - CpuFastCopy(gObjPalette, (void *)OBJ_PLTT, OBJ_PLTT_SIZE); + CpuFastCopy(gObjPalette, (void *)OBJ_PLTT, sizeof(gObjPalette)); gFlags ^= FLAGS_UPDATE_SPRITE_PALETTES; } diff --git a/src/game/boost_effect.c b/src/game/boost_effect.c index d1227ddb9..313759914 100644 --- a/src/game/boost_effect.c +++ b/src/game/boost_effect.c @@ -173,7 +173,7 @@ static inline void sub_8015B64_inline(AnimId anim, u16 palId) #endif numColors = *pAnim % 256u; - DmaCopy32(3, &gRefSpriteTables->palettes[animPalId * 16], &gObjPalette[0 * 16 + insertOffset], numColors * sizeof(u16)); + DmaCopy32(3, &gRefSpriteTables->palettes[animPalId * 16], &GET_PALETTE_COLOR_OBJ(0, insertOffset), numColors * sizeof(u16)); gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; } diff --git a/src/game/bosses/boss_1.c b/src/game/bosses/boss_1.c index 964691300..d4c97977c 100644 --- a/src/game/bosses/boss_1.c +++ b/src/game/bosses/boss_1.c @@ -184,7 +184,7 @@ static const HammertankFunc sBossStateHandlers[] = { [EGG_HAMMER_TANK_II_STATE_DRAG] = StateHandler_HammerDrag, [EGG_HAMMER_TANK_II_STATE_RETRACT] = StateHandler_HammerRetract, }; -static const u16 gUnknown_080D7AD0[][16] = { +static const u16 gUnknown_080D7AD0[][PALETTE_LEN_4BPP] = { INCBIN_U16("graphics/80D7AD0.gbapal"), INCBIN_U16("graphics/80D7AF0.gbapal"), }; @@ -1613,8 +1613,8 @@ static void HandleBossHitPalette(EggHammerTankII *boss) if (boss->timerInvulnerability > 0) { u8 i; - for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + i] = gUnknown_080D7AD0[(boss->timerInvulnerability & 4) >> 2][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, gUnknown_080D7AD0[(boss->timerInvulnerability & 4) >> 2][i]); } gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; diff --git a/src/game/bosses/boss_2.c b/src/game/bosses/boss_2.c index 82c2e1d21..628e5de5f 100644 --- a/src/game/bosses/boss_2.c +++ b/src/game/bosses/boss_2.c @@ -152,7 +152,7 @@ static const u16 gUnknown_080D7B4E[][2] = { static const BossFunction sBossModeTasks[] = { HandleCannonBombTrigger, HandleCannonlessBombTrigger }; -static const u16 gUnknown_080D7B70[][16] = { +static const u16 gUnknown_080D7B70[][PALETTE_LEN_4BPP] = { INCBIN_U16("graphics/80D7B70.gbapal"), INCBIN_U16("graphics/80D7B90.gbapal"), }; @@ -575,23 +575,23 @@ static void UpdateBomberTankPalette(EggBomberTank *boss) { u8 i; if (boss->bossHitTimer != 0) { - for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + i] = gUnknown_080D7B70[(gStageTime & 2) >> 1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, gUnknown_080D7B70[(gStageTime & 2) >> 1][i]); } } else { - for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + i] = gUnknown_080D7B70[1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, gUnknown_080D7B70[1][i]); } } if (boss->cannonHitTimer != 0) { boss->cannonHitTimer--; - for (i = 0; i < 16; i++) { - gObjPalette[13 * 16 + i] = gUnknown_080D7B70[(gStageTime & 2) >> 1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(13, i, gUnknown_080D7B70[(gStageTime & 2) >> 1][i]); } } else { - for (i = 0; i < 16; i++) { - gObjPalette[13 * 16 + i] = gUnknown_080D7B70[1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(13, i, gUnknown_080D7B70[1][i]); } } diff --git a/src/game/bosses/boss_3.c b/src/game/bosses/boss_3.c index c5f77c98e..fd82fe68d 100644 --- a/src/game/bosses/boss_3.c +++ b/src/game/bosses/boss_3.c @@ -185,7 +185,7 @@ const u8 *const gUnknown_080D7ED4[] = { const s8 gUnknown_080D7F10[EGGTOTEM_NUM_PLATFORMS] = { 14, 14, 8 }; -const s16 gUnknown_080D7F14[2][16] = { +const s16 gUnknown_080D7F14[2][PALETTE_LEN_4BPP] = { INCBIN_U16("graphics/boss_3_a.gbapal"), INCBIN_U16("graphics/boss_3_b.gbapal"), }; @@ -1504,24 +1504,24 @@ void sub_8040F14(EggTotem *totem) u8 i; if (totem->unk35 != 0) { - for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + i] = gUnknown_080D7F14[((gStageTime & 0x2) / 2u)][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, gUnknown_080D7F14[((gStageTime & 0x2) / 2u)][i]); } } else { - for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + i] = gUnknown_080D7F14[1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, gUnknown_080D7F14[1][i]); } } if (totem->unk36 > 0) { totem->unk36--; - for (i = 0; i < 16; i++) { - gObjPalette[11 * 16 + i] = gUnknown_080D7F14[((gStageTime & 0x2) / 2u)][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(11, i, gUnknown_080D7F14[((gStageTime & 0x2) / 2u)][i]); } } else { - for (i = 0; i < 16; i++) { - gObjPalette[11 * 16 + i] = gUnknown_080D7F14[1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(11, i, gUnknown_080D7F14[1][i]); } } diff --git a/src/game/bosses/boss_4.c b/src/game/bosses/boss_4.c index 43316bd0b..68b81366c 100644 --- a/src/game/bosses/boss_4.c +++ b/src/game/bosses/boss_4.c @@ -42,7 +42,7 @@ #define RESERVED_EXPLOSION_TILES_VRAM (void *)(OBJ_VRAM0 + 0x2980) -static const u16 sPalAeroEggHit[2][16] = { +static const u16 sPalAeroEggHit[2][PALETTE_LEN_4BPP] = { [PAL_BOSS_4_DEFAULT] = INCBIN_U16("graphics/boss_4_a.gbapal"), [PAL_BOSS_4_HIT] = INCBIN_U16("graphics/boss_4_b.gbapal"), }; @@ -761,12 +761,12 @@ static void sub_8042560(AeroEgg *boss) u8 i; if (boss->main.unk16 != 0) { - for (i = 0; i < ARRAY_COUNT(sPalAeroEggHit[PAL_BOSS_4_DEFAULT]); i++) { - gObjPalette[8 * 16 + i] = sPalAeroEggHit[((gStageTime & 0x2) >> 1)][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, sPalAeroEggHit[((gStageTime & 0x2) >> 1)][i]); } } else { - for (i = 0; i < ARRAY_COUNT(sPalAeroEggHit[PAL_BOSS_4_HIT]); i++) { - gObjPalette[8 * 16 + i] = sPalAeroEggHit[PAL_BOSS_4_HIT][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, sPalAeroEggHit[PAL_BOSS_4_HIT][i]); } } diff --git a/src/game/bosses/boss_5.c b/src/game/bosses/boss_5.c index f5c0169dc..0179f5cb8 100644 --- a/src/game/bosses/boss_5.c +++ b/src/game/bosses/boss_5.c @@ -149,7 +149,7 @@ static const TileInfo gUnknown_080D7FB0[] = { { 2, SA2_ANIM_EGG_SAUCER_SMACK_PARTICLE_UP, 0 }, { 4, SA2_ANIM_EGG_SAUCER_SMACK_PARTICLE_UP_RIGHT, 0 }, }; -static const u16 gUnknown_080D7FF0[][16] = { +static const u16 gUnknown_080D7FF0[][PALETTE_LEN_4BPP] = { INCBIN_U16("graphics/80D7FF0.gbapal"), INCBIN_U16("graphics/80D8010.gbapal"), }; @@ -1293,6 +1293,7 @@ static void sub_8044CBC(EggSaucer *boss) s->prevVariant = -1; } } + boss->unk36[0][boss->unkB6] = boss->unkB8; boss->unk36[1][boss->unkB6] = boss->unkBA; @@ -1756,27 +1757,27 @@ void sub_8045898(EggSaucer *boss) if (boss->unk15 == 0) { val = (gStageTime & 2) >> 1; if (boss->unk13 != 0) { - for (i = 0; i < 0x10; i++) { - gObjPalette[8 * 16 + i] = gUnknown_080D7FF0[val][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, gUnknown_080D7FF0[val][i]); } } else { - for (i = 0; i < 0x10; i++) { - gObjPalette[8 * 16 + i] = gUnknown_080D7FF0[1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, gUnknown_080D7FF0[1][i]); } } if (boss->unk1F != 0) { boss->unk1F--; - for (i = 0; i < 0x10; i++) { - gObjPalette[9 * 16 + i] = gUnknown_080D7FF0[val][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(9, i, gUnknown_080D7FF0[val][i]); } } else { - for (i = 0; i < 0x10; i++) { - gObjPalette[9 * 16 + i] = gUnknown_080D7FF0[1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(9, i, gUnknown_080D7FF0[1][i]); } } - gFlags |= 2; + gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; } } diff --git a/src/game/bosses/boss_6.c b/src/game/bosses/boss_6.c index 0409e72db..e34dafafc 100644 --- a/src/game/bosses/boss_6.c +++ b/src/game/bosses/boss_6.c @@ -168,7 +168,7 @@ static const u16 gUnknown_080D809E[][10] = { { 257, 257, 257, 257, 257, 256, 256, 256, 222, 222 }, }; -static const u16 sPalettes[][16] = { +static const u16 sPalettes[][PALETTE_LEN_4BPP] = { INCBIN_U16("graphics/80D80C6.gbapal"), // hit palette INCBIN_U16("graphics/80D80E6.gbapal"), // normal palette }; @@ -1164,16 +1164,16 @@ static void SetPalette(EggGoRound *boss) { u8 i; if (boss->invincibilityTimer > 0) { - for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + i] = sPalettes[(gStageTime & 2) / 2][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, sPalettes[(gStageTime & 2) / 2][i]); } } else { - for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + i] = sPalettes[1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, i, sPalettes[1][i]); } } - gFlags |= 2; + gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; } static void HandleCollisions(EggGoRound *boss) diff --git a/src/game/bosses/boss_8.c b/src/game/bosses/boss_8.c index f43700e4a..d714ec63e 100644 --- a/src/game/bosses/boss_8.c +++ b/src/game/bosses/boss_8.c @@ -173,7 +173,7 @@ const u16 gUnknown_080D8888[2][2] = { { Q(188), Q(110) }, { Q(162), Q(110) } }; static const EggRoboFn sArmFuncs[8] = { sub_804B43C, sub_804B594, sub_804B734, sub_804B984, sub_804BC44, sub_804BE6C, sub_804BAC0, sub_804C240 }; -const u16 sArmPalettes[2][16] = { +const u16 sArmPalettes[2][PALETTE_LEN_4BPP] = { INCBIN_U16("graphics/boss_8_a.gbapal"), INCBIN_U16("graphics/boss_8_b.gbapal"), }; @@ -2009,31 +2009,31 @@ static void sub_804CA70(SuperEggRoboZ *boss) if (boss->unkB != 0) { for (i = 0; i < ARRAY_COUNT(sArmPalettes[0]); i++) { - gObjPalette[8 * 16 + i] = sArmPalettes[pal][i]; + SET_PALETTE_COLOR_OBJ(8, i, sArmPalettes[pal][i]); } } else { for (i = 0; i < ARRAY_COUNT(sArmPalettes[0]); i++) { - gObjPalette[8 * 16 + i] = sArmPalettes[0][i]; + SET_PALETTE_COLOR_OBJ(8, i, sArmPalettes[0][i]); } } if (boss->unk3E[0] > 0) { for (i = 0; i < ARRAY_COUNT(sArmPalettes[0]); i++) { - gObjPalette[9 * 16 + i] = sArmPalettes[pal][i]; + SET_PALETTE_COLOR_OBJ(9, i, sArmPalettes[pal][i]); } } else { for (i = 0; i < ARRAY_COUNT(sArmPalettes[0]); i++) { - gObjPalette[9 * 16 + i] = sArmPalettes[0][i]; + SET_PALETTE_COLOR_OBJ(9, i, sArmPalettes[0][i]); } } if (boss->unk3E[1] > 0) { for (i = 0; i < ARRAY_COUNT(sArmPalettes[0]); i++) { - gObjPalette[12 * 16 + i] = sArmPalettes[pal][i]; + SET_PALETTE_COLOR_OBJ(12, i, sArmPalettes[pal][i]); } } else { for (i = 0; i < ARRAY_COUNT(sArmPalettes[0]); i++) { - gObjPalette[12 * 16 + i] = sArmPalettes[0][i]; + SET_PALETTE_COLOR_OBJ(12, i, sArmPalettes[0][i]); } } diff --git a/src/game/bosses/boss_9.c b/src/game/bosses/boss_9.c index 297ec3adb..5fbbf0044 100644 --- a/src/game/bosses/boss_9.c +++ b/src/game/bosses/boss_9.c @@ -321,7 +321,7 @@ const TA53_Data1 gUnknown_080D8DCC[6] = { }; const TA53_Rocket_Callback gUnknown_080D8E14[3] = { sub_804E974, sub_804EB6C, sub_804EC6C }; -const u8 sRGB_080D8E20[4][16][3] = { +const u8 sRGB_080D8E20[4][PALETTE_LEN_4BPP][3] = { { { 0, 0, 0 }, { 2, 2, 28 }, @@ -1385,8 +1385,8 @@ void sub_804E4CC(struct TA53_unk48 *unk48) b = sRGB_080D8E20[3][c][2]; b = ((b * r6) >> 12) & 0x1F; - gObjPalette[8 * 16 + c] = RGB16_REV(r, g, b); - gBgPalette[c] = RGB16_REV(r, g, b); + SET_PALETTE_COLOR_OBJ(8, c, RGB16_REV(r, g, b)); + SET_PALETTE_COLOR_BG(0, c, RGB16_REV(r, g, b)); } gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; @@ -1407,7 +1407,7 @@ void sub_804E4CC(struct TA53_unk48 *unk48) g = ((g * r6) >> 9) & 0x1F; b = sRGB_080D8E20[i][c][2]; b = ((b * r6) >> 9) & 0x1F; - gBgPalette[0x70 + c + (i * 16)] = RGB16_REV(r, g, b); + gBgPalette[7 * PALETTE_LEN_4BPP + c + (i * PALETTE_LEN_4BPP)] = RGB16_REV(r, g, b); } } @@ -2870,31 +2870,24 @@ void sub_8050958(TA53Boss *boss) if (boss->unkD > 0) { if (--boss->unkD == 0) { for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + i] = gUnknown_080D8EF0[1][i]; - gBgPalette[0 * 16 + i] = gObjPalette[i + 8 * 16]; + SET_PALETTE_COLOR_OBJ(8, i, gUnknown_080D8EF0[1][i]); + SET_PALETTE_COLOR_BG(0, i, gUnknown_080D8EF0[1][i]); } } else { // _080509B0 - u16 r6 = (gStageTime >> 1) % 16u; + u16 r6 = (gStageTime >> 1) % PALETTE_LEN_4BPP; if (boss->lives < 4) { - for (i = 0; i < 16; i++) { - gObjPalette[8 * 16 + ((i + r6) % 16u)] = gUnknown_080D8EF0[0][i] >> 5; - gBgPalette[0 * 16 + ((i + r6) % 16u)] = gObjPalette[8 * 16 + ((i + r6) % 16u)]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, ((i + r6) % PALETTE_LEN_4BPP), gUnknown_080D8EF0[0][i] >> 5); + SET_PALETTE_COLOR_BG(0, ((i + r6) % PALETTE_LEN_4BPP), gUnknown_080D8EF0[0][i] >> 5); } } else { - for (i = 0; i < 16; i++) { - u32 r0; - u32 r2; - u32 colId; - u16 *objPalTgt = &gObjPalette[0 * 16 + 0]; - u32 objPalId; - - colId = ((i + r6) % 16u); - objPalId = 8 * 16 + colId; - objPalTgt[objPalId] = ((gUnknown_080D8EF0[0][i] << 5) | (gUnknown_080D8EF0[0][i] >> 5)) | gUnknown_080D8EF0[0][i]; - gBgPalette[0 * 16 + colId] - = ((gUnknown_080D8EF0[0][i] << 5) | (gUnknown_080D8EF0[0][i] >> 5)) | gUnknown_080D8EF0[0][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(8, ((i + r6) % PALETTE_LEN_4BPP), + ((gUnknown_080D8EF0[0][i] << 5) | (gUnknown_080D8EF0[0][i] >> 5)) | gUnknown_080D8EF0[0][i]); + SET_PALETTE_COLOR_BG(0, ((i + r6) % PALETTE_LEN_4BPP), + ((gUnknown_080D8EF0[0][i] << 5) | (gUnknown_080D8EF0[0][i] >> 5)) | gUnknown_080D8EF0[0][i]); } } } diff --git a/src/game/character_select.c b/src/game/character_select.c index b1aabe56a..ad43c8762 100644 --- a/src/game/character_select.c +++ b/src/game/character_select.c @@ -515,11 +515,11 @@ void CreateCharacterSelectionScreen(u8 initialSelection, bool8 allUnlocked) s->frameFlags = 0; UpdateSpriteAnimation(s); - for (i = 0; i < 16; i++) { - gObjPalette[15 * 16 + i] = 0; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(15, i, 0); } - gFlags |= 0x2; + gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; } static void Task_FadeInAndStartRollInAnim(void) diff --git a/src/game/cutscenes/extra_ending_fall.c b/src/game/cutscenes/extra_ending_fall.c index e7e30f6e0..2d3b53459 100644 --- a/src/game/cutscenes/extra_ending_fall.c +++ b/src/game/cutscenes/extra_ending_fall.c @@ -834,9 +834,9 @@ void sub_8090F6C(struct ExtraEndingCutScene *scene) if (scene->unk37F != 0) { scene->unk37F--; if (scene->unk37E <= 3) { - DmaCopy32(3, gUnknown_080E15C8[scene->unk37E], &gObjPalette[3 * 16 + 0], sizeof(gUnknown_080E15C8[0])); + DmaCopy32(3, gUnknown_080E15C8[scene->unk37E], &GET_PALETTE_COLOR_OBJ(3, 0), sizeof(gUnknown_080E15C8[0])); } else { - DmaCopy32(3, gUnknown_080E15C8[6 - scene->unk37E], &gObjPalette[3 * 16 + 0], sizeof(gUnknown_080E15C8[0])); + DmaCopy32(3, gUnknown_080E15C8[6 - scene->unk37E], &GET_PALETTE_COLOR_OBJ(3, 0), sizeof(gUnknown_080E15C8[0])); } } else { scene->unk37E++; diff --git a/src/game/multiboot/collect_rings/time_display.c b/src/game/multiboot/collect_rings/time_display.c index dbd8582cd..518073d96 100644 --- a/src/game/multiboot/collect_rings/time_display.c +++ b/src/game/multiboot/collect_rings/time_display.c @@ -23,7 +23,7 @@ const u8 gUnknown_080E0234[] = { 103, 104, 112, 114, 115, 117, 119, 120, 128, 130, 131, 133, 135, 136, 144, 146, 147, 149, 151, 152, }; -const u16 gUnknown_080E0270[] = INCBIN_U16("graphics/80E0270.gbapal"); +const u16 gUnknown_080E0270[PALETTE_LEN_4BPP] = INCBIN_U16("graphics/80E0270.gbapal"); #ifndef COLLECT_RINGS_ROM #define NUM_TILES 9 @@ -85,8 +85,8 @@ void CreateCollectRingsTimeDisplay(void) UpdateSpriteAnimation(s); } - for (i = 0; i < 16; i++) { - gObjPalette[7 * 16 + i] = gUnknown_080E0270[i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(7, i, gUnknown_080E0270[i]); } gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; diff --git a/src/game/sa1_sa2_shared/pause_menu.c b/src/game/sa1_sa2_shared/pause_menu.c index 16881289a..388e08e6a 100644 --- a/src/game/sa1_sa2_shared/pause_menu.c +++ b/src/game/sa1_sa2_shared/pause_menu.c @@ -182,8 +182,8 @@ void Task_PauseMenuInit(void) m4aSongNumStart(SE_PAUSE_SCREEN); gFlags |= FLAGS_PAUSE_GAME; - DmaCopy16(3, &gObjPalette[15 * 16 + 9], pm->pal64, sizeof(pm->pal64)); - DmaCopy16(3, &gObjPalette[15 * 16 + 12], pm->pal6A, sizeof(pm->pal6A)); + DmaCopy16(3, &GET_PALETTE_COLOR_OBJ(15, +9), pm->pal64, sizeof(pm->pal64)); + DmaCopy16(3, &GET_PALETTE_COLOR_OBJ(15, +12), pm->pal6A, sizeof(pm->pal6A)); gCurTask->main = Task_PauseMenuUpdate; } @@ -200,7 +200,7 @@ void sub_800AE58(void) { DmaCopy16(3, &Tileset_zone_1_act_1_fg[(4 * 16 * TILE_SIZE_4BPP) / sizeof(u16)], (void *)(OBJ_VRAM1 + 0x3EC0), 0x140); - gObjPalette[0 * 16 + 1] = RGB_WHITE; + GET_PALETTE_COLOR_OBJ(0, 1) = RGB_WHITE; gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; gFlags &= ~FLAGS_PAUSE_GAME; } \ No newline at end of file diff --git a/src/game/special_stage/ui.c b/src/game/special_stage/ui.c index 1ea756260..508e539d2 100644 --- a/src/game/special_stage/ui.c +++ b/src/game/special_stage/ui.c @@ -335,8 +335,8 @@ static void HandlePaused(struct SpecialStageUI *ui) sub_806CA88(s, 1, pauseMenuVariants[lang][0], pauseMenuVariants[lang][1], 0x1000, (DISPLAY_WIDTH / 2), (DISPLAY_HEIGHT / 2), 0, pauseMenuVariants[lang][2], 0); - DmaCopy16(3, &gObjPalette[15 * 16 + 9], ui->pauseMenuPalette1, sizeof(ui->pauseMenuPalette1)); - DmaCopy16(3, &gObjPalette[15 * 16 + 12], ui->pauseMenuPalette2, sizeof(ui->pauseMenuPalette2)); + DmaCopy16(3, &GET_PALETTE_COLOR_OBJ(15, 9), ui->pauseMenuPalette1, sizeof(ui->pauseMenuPalette1)); + DmaCopy16(3, &GET_PALETTE_COLOR_OBJ(15, 12), ui->pauseMenuPalette2, sizeof(ui->pauseMenuPalette2)); ui->wasPaused = TRUE; m4aMPlayAllStop(); m4aSongNumStart(SE_PAUSE_SCREEN); diff --git a/src/game/stage/intro.c b/src/game/stage/intro.c index da9f0e6e6..c8828179c 100644 --- a/src/game/stage/intro.c +++ b/src/game/stage/intro.c @@ -165,7 +165,7 @@ static const u8 sGettingReadyAnimationDuration[NUM_CHARACTERS] = { [CHARACTER_SONIC] = 40, [CHARACTER_CREAM] = 55, [CHARACTER_TAILS] = 52, [CHARACTER_KNUCKLES] = 40, [CHARACTER_AMY] = 40 }; // Each byte represents one RGB channel (0-31) -static const u8 gUnknown_080D6FF5[NUM_CHARACTERS + 1][16][3] = { +static const u8 gUnknown_080D6FF5[NUM_CHARACTERS + 1][PALETTE_LEN_4BPP][3] = { { { 0x00, 0x17, 0x06 }, { 0x16, 0x16, 0x16 }, @@ -731,13 +731,13 @@ static void Task_802F9F8(void) if (IS_SINGLE_PLAYER) { // _0802FA4C+8 - for (i = 0; i < 16; i++) { + for (i = 0; i < PALETTE_LEN_4BPP; i++) { r = gUnknown_080D6FF5[gSelectedCharacter][i][0]; r = (r * frameCounter) / 16u; g = ((gUnknown_080D6FF5[gSelectedCharacter][i][1] * frameCounter) / 16u); b = ((gUnknown_080D6FF5[gSelectedCharacter][i][2] * frameCounter) / 16u); - gObjPalette[0 * 16 + i] = RGB16_REV(r, g, b); + SET_PALETTE_COLOR_OBJ(0, i, RGB16_REV(r, g, b)); if (gCheese != NULL) { r = gUnknown_080D6FF5[5][i][0]; @@ -745,7 +745,7 @@ static void Task_802F9F8(void) g = ((gUnknown_080D6FF5[5][i][1] * frameCounter) / 16u); b = ((gUnknown_080D6FF5[5][i][2] * frameCounter) / 16u); - gObjPalette[14 * 16 + i] = RGB16_REV(r, g, b); + SET_PALETTE_COLOR_OBJ(14, i, RGB16_REV(r, g, b)); } } } else { @@ -754,25 +754,25 @@ static void Task_802F9F8(void) for (sid = 0; sid < MULTI_SIO_PLAYERS_MAX; sid++) { if (GetBit(gMultiplayerConnections, sid)) { - for (i = 0; i < 16; i++) { + for (i = 0; i < PALETTE_LEN_4BPP; i++) { r = gUnknown_080D6FF5[(gMultiplayerCharacters)[sid]][i][0]; r = (r * frameCounter) / 16u; g = ((gUnknown_080D6FF5[(gMultiplayerCharacters)[sid]][i][1] * frameCounter) / 16u); b = ((gUnknown_080D6FF5[(gMultiplayerCharacters)[sid]][i][2] * frameCounter) / 16u); - gObjPalette[sid * 16 + i] = RGB16_REV(r, g, b); + SET_PALETTE_COLOR_OBJ(sid, i, RGB16_REV(r, g, b)); } } } if (gCheese != NULL) { - for (i = 0; i < 16; i++) { + for (i = 0; i < PALETTE_LEN_4BPP; i++) { r = gUnknown_080D6FF5[5][i][0]; r = (r * frameCounter) / 16u; g = ((gUnknown_080D6FF5[5][i][1] * frameCounter) / 16u); b = ((gUnknown_080D6FF5[5][i][2] * frameCounter) / 16u); - gObjPalette[14 * 16 + i] = RGB16_REV(r, g, b); + SET_PALETTE_COLOR_OBJ(14, i, RGB16_REV(r, g, b)); } } } diff --git a/src/game/stage/ui.c b/src/game/stage/ui.c index fb3589952..a2b3f8b7c 100644 --- a/src/game/stage/ui.c +++ b/src/game/stage/ui.c @@ -43,7 +43,7 @@ const u16 sAnims1UpIcons[][3] [CHARACTER_KNUCKLES] = { ONE_UP_ICON_TILE_COUNT, SA2_ANIM_LIFE_COUNTER, SA2_ANIM_VARIANT_LIFE_COUNTER_KNUCKLES }, [CHARACTER_AMY] = { ONE_UP_ICON_TILE_COUNT, SA2_ANIM_LIFE_COUNTER, SA2_ANIM_VARIANT_LIFE_COUNTER_AMY } }; -const u16 sPalette1UpIcons[] = INCBIN_U16("graphics/ui_icon_1_up.gbapal"); +const u16 sPalette1UpIcons[PALETTE_LEN_4BPP] = INCBIN_U16("graphics/ui_icon_1_up.gbapal"); const u32 sOrdersOfMagnitude[6] = { 100000, 10000, 1000, 100, 10, 1, @@ -219,8 +219,8 @@ struct Task *CreateStageUI(void) s->frameFlags = 0; ui->ringCurrentFrame = 0; - for (i = 0; i < 16; i++) { - gObjPalette[7 * 16 + i] = sPalette1UpIcons[i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_OBJ(7, i, sPalette1UpIcons[i]); } gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; diff --git a/src/sprite.c b/src/sprite.c index 3764cf198..f4a7bbdea 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -326,10 +326,11 @@ static AnimCmdResult animCmd_GetPalette(void *cursor, Sprite *s) s32 paletteIndex = cmd->palId; if (gFlags & FLAGS_20000) { - CopyPalette(&gRefSpriteTables->palettes[paletteIndex * 16], s->palId * 16 + cmd->insertOffset, cmd->numColors); + CopyPalette(&gRefSpriteTables->palettes[paletteIndex * PALETTE_LEN_4BPP], s->palId * PALETTE_LEN_4BPP + cmd->insertOffset, + cmd->numColors); } else { - DmaCopy16(3, &gRefSpriteTables->palettes[paletteIndex * 16], &gObjPalette[s->palId * 16 + cmd->insertOffset], - cmd->numColors * 2); + DmaCopy16(3, &gRefSpriteTables->palettes[paletteIndex * PALETTE_LEN_4BPP], &GET_PALETTE_COLOR_OBJ(s->palId, cmd->insertOffset), + cmd->numColors * sizeof(u16)); gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; } @@ -1063,7 +1064,8 @@ static AnimCmdResult animCmd_GetPalette(void *cursor, Sprite *s) if (!(s->frameFlags & SPRITE_FLAG_MASK_18)) { s32 paletteIndex = cmd->palId; - DmaCopy32(3, &gRefSpriteTables->palettes[paletteIndex * 16], &gObjPalette[s->palId * 16 + cmd->insertOffset], cmd->numColors * 2); + DmaCopy32(3, &gRefSpriteTables->palettes[paletteIndex * PALETTE_LEN_4BPP], &GET_PALETTE_COLOR_OBJ(s->palId, cmd->insertOffset), + cmd->numColors * 2); gFlags |= FLAGS_UPDATE_SPRITE_PALETTES; } From 489b8181fa9ef41bc515fe95119c9d76209e0c74 Mon Sep 17 00:00:00 2001 From: JaceCear <11230293+JaceCear@users.noreply.github.com> Date: Tue, 17 Feb 2026 02:23:07 +0100 Subject: [PATCH 19/19] Streamline most uses of gBgPalette --- src/background.c | 9 +++++-- src/game/bosses/boss_7.c | 4 ++-- src/game/bosses/boss_9.c | 5 ++++ src/game/cutscenes/final_ending_land.c | 2 +- src/game/multiboot/connection.c | 16 ++++++------- src/game/sa1_sa2_shared/unused_flash_task.c | 2 +- src/game/sa1_sa2_shared/unused_level_select.c | 2 +- src/game/stage/camera.c | 24 +++++++++---------- src/game/stage/intro.c | 2 +- src/game/water_effects.c | 12 +++++----- 10 files changed, 43 insertions(+), 35 deletions(-) diff --git a/src/background.c b/src/background.c index d4c36d31e..aad7732c5 100644 --- a/src/background.c +++ b/src/background.c @@ -55,7 +55,7 @@ void DrawBackground(Background *background) background->paletteOffset = mapHeader->tileset.palOffset; if (!(background->flags & BACKGROUND_DISABLE_PALETTE_UPDATE)) { - DmaCopy16(3, pal, gBgPalette + background->paletteOffset, palSize * sizeof(*pal)); + DmaCopy16(3, pal, &GET_PALETTE_COLOR_BG(0, background->paletteOffset), palSize * sizeof(*pal)); gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; background->flags ^= BACKGROUND_DISABLE_PALETTE_UPDATE; } @@ -1292,7 +1292,12 @@ static AnimCmdResult animCmd_GetPalette_BG(void *cursor, Sprite *s) if (!(s->frameFlags & SPRITE_FLAG_MASK_18)) { s32 paletteIndex = cmd->palId; - DmaCopy32(3, &gRefSpriteTables->palettes[paletteIndex * 16], &gBgPalette[s->palId * 16 + cmd->insertOffset], cmd->numColors * 2); + // NOTE: + // For some reason, this only matches with a size of: + // (cmd->numColors * 2), not (cmd->numColors * sizeof(u16)) + // Same goes for sprite.c version called animCmd_GetPalette()... + DmaCopy32(3, &gRefSpriteTables->palettes[paletteIndex * 16], &GET_PALETTE_COLOR_BG(s->palId, cmd->insertOffset), + cmd->numColors * 2); gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; } diff --git a/src/game/bosses/boss_7.c b/src/game/bosses/boss_7.c index 420e92fe8..3450261f4 100644 --- a/src/game/bosses/boss_7.c +++ b/src/game/bosses/boss_7.c @@ -1160,8 +1160,8 @@ static void sub_804931C(EggFrog *boss) if (boss->unk16 != 0) { u8 i; - for (i = 0; i < 16; i++) { - gBgPalette[i + 0xB0] = gUnknown_080D8796[(boss->unk16 & 2) >> 1][i]; + for (i = 0; i < PALETTE_LEN_4BPP; i++) { + SET_PALETTE_COLOR_BG(11, i, gUnknown_080D8796[(boss->unk16 & 2) >> 1][i]); } } diff --git a/src/game/bosses/boss_9.c b/src/game/bosses/boss_9.c index 5fbbf0044..c8baeff91 100644 --- a/src/game/bosses/boss_9.c +++ b/src/game/bosses/boss_9.c @@ -1407,7 +1407,12 @@ void sub_804E4CC(struct TA53_unk48 *unk48) g = ((g * r6) >> 9) & 0x1F; b = sRGB_080D8E20[i][c][2]; b = ((b * r6) >> 9) & 0x1F; +#ifndef NON_MATCHING + // TODO: This should work... gBgPalette[7 * PALETTE_LEN_4BPP + c + (i * PALETTE_LEN_4BPP)] = RGB16_REV(r, g, b); +#else + SET_PALETTE_COLOR_BG(7, c + (i * PALETTE_LEN_4BPP), RGB16_REV(r, g, b)); +#endif } } diff --git a/src/game/cutscenes/final_ending_land.c b/src/game/cutscenes/final_ending_land.c index e82b4633c..8ee9b6ac2 100644 --- a/src/game/cutscenes/final_ending_land.c +++ b/src/game/cutscenes/final_ending_land.c @@ -504,7 +504,7 @@ void CreateFinalEndingLandingCutScene(void) if (!(gLoadedSaveGame->chaosEmeralds[gSelectedCharacter] & CHAOS_EMERALDS_COMPLETED)) { memcpy(unk1AF4, gUnknown_080E1AF4, sizeof(unk1AF4)); - DmaCopy32(3, unk1AF4, &gBgPalette[32], sizeof(unk1AF4)); + DmaCopy32(3, unk1AF4, &GET_PALETTE_COLOR_BG(2, 0), sizeof(unk1AF4)); } } diff --git a/src/game/multiboot/connection.c b/src/game/multiboot/connection.c index cc29085dd..ad64bcb5d 100644 --- a/src/game/multiboot/connection.c +++ b/src/game/multiboot/connection.c @@ -602,22 +602,22 @@ void sub_8081AD4(struct SinglePakConnectScreen *connectScreen) background->flags = BACKGROUND_FLAGS_BG_ID(0); DrawBackground(background); - CpuFill16(0, &gBgPalette[17], 30); + CpuFill16(0, &GET_PALETTE_COLOR_BG(1, 1), RGB16_REV(30, 0, 0)); - gDispCnt |= 0x2200; - temp = 0x1f01; + gDispCnt |= (DISPCNT_BG1_ON | DISPCNT_WIN0_ON); + temp = BGCNT_SCREENBASE(31) | BGCNT_PRIORITY(1); gBgScrollRegs[1][0] = 0; gBgScrollRegs[1][1] = 0; - gWinRegs[0] = 0x2828; - gWinRegs[2] = 0x8890; - gWinRegs[4] = 2; - gWinRegs[5] = 1; + gWinRegs[WINREG_WIN0H] = WIN_RANGE(40, 40); + gWinRegs[WINREG_WIN0V] = WIN_RANGE(136, 144); + gWinRegs[WINREG_WININ] = 2; + gWinRegs[WINREG_WINOUT] = 1; gBgCntRegs[1] = temp; CpuFill16(0xF3FF, (void *)BG_SCREEN_ADDR(31), 2049); CpuFill16(0xFFFF, (void *)VRAM + 1023 * TILE_SIZE_4BPP, TILE_SIZE_4BPP); - gBgPalette[255] = RGB_RED; + SET_PALETTE_COLOR_BG(15, 15, RGB_RED); gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; } diff --git a/src/game/sa1_sa2_shared/unused_flash_task.c b/src/game/sa1_sa2_shared/unused_flash_task.c index 5b90b77d2..2ae3ad48f 100644 --- a/src/game/sa1_sa2_shared/unused_flash_task.c +++ b/src/game/sa1_sa2_shared/unused_flash_task.c @@ -22,7 +22,7 @@ void CreateUnusedFlashTask(void) INIT_BG_SPRITES_LAYER_64(2); TaskCreate(Task_UnusedFlashTask, 0, 0x1000, 0, NULL); - gBgPalette[1] = RGB_WHITE; + SET_PALETTE_COLOR_BG(0, 1, RGB_WHITE); gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; } diff --git a/src/game/sa1_sa2_shared/unused_level_select.c b/src/game/sa1_sa2_shared/unused_level_select.c index 68082beec..f52ee0855 100644 --- a/src/game/sa1_sa2_shared/unused_level_select.c +++ b/src/game/sa1_sa2_shared/unused_level_select.c @@ -147,7 +147,7 @@ static void Task_Poll(void) static void Task_UnusedLevelSelectInit(void) { LevelSelect *levelSelect = TASK_DATA(gCurTask); - gBgPalette[1] = RGB_WHITE; + SET_PALETTE_COLOR_BG(0, 1, RGB_WHITE); gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; levelSelect->vram += RenderText(levelSelect->vram, Tileset_DebugAscii, 6, 14, 0, "STAGE", 0); diff --git a/src/game/stage/camera.c b/src/game/stage/camera.c index a7f71a97a..f79e15226 100644 --- a/src/game/stage/camera.c +++ b/src/game/stage/camera.c @@ -1612,7 +1612,7 @@ NONMATCH("asm/non_matching/game/stage/background/sub_801D24C.inc", void sub_801D } // _0801D4A2 - for (i = 0; i < 16; i++) { + for (i = 0; i < PALETTE_LEN_4BPP; i++) { s32 b, g, r; r = (p0 * gUnknown_080D5C02[1][i][0]) >> 4; r &= 0x1F; @@ -1623,7 +1623,7 @@ NONMATCH("asm/non_matching/game/stage/background/sub_801D24C.inc", void sub_801D b = (p0 * gUnknown_080D5C02[1][i][2]) >> 4; b &= 0x1F; - gBgPalette[15 * 16 + i] = ((b << 10) + (g << 5) + (r << 0)); + SET_PALETTE_COLOR_BG(15, i, RGB16_REV(r, g, b)); } gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; @@ -1664,7 +1664,7 @@ NONMATCH("asm/non_matching/game/stage/background/StageBgUpdate_Zone6Acts12.inc", r5 = 5; } - gBgPalette[0] = RGB_BLACK; + SET_PALETTE_COLOR_BG(0, 0, RGB_BLACK); gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; switch (r5) { @@ -1723,7 +1723,7 @@ NONMATCH("asm/non_matching/game/stage/background/StageBgUpdate_Zone6Acts12.inc", gBgCntRegs[3] |= BGCNT_PRIORITY(2); for (i = 0; i < 16; i++) { - gBgPalette[(15 * 16) + i] = RGB_BLACK; + SET_PALETTE_COLOR_BG(15, i, RGB_BLACK); } // jumps to _0801D8EE for this @@ -1756,7 +1756,7 @@ NONMATCH("asm/non_matching/game/stage/background/StageBgUpdate_Zone6Acts12.inc", u32 green = ((gUnknown_080D5C02[0][i][1] * r6) >> 5) & 0x1F; u32 blue = ((gUnknown_080D5C02[0][i][2] * r6) >> 5) & 0x1F; - gBgPalette[(15 * 16) + i] = ((blue << 10) | (green << 5) | red); + SET_PALETTE_COLOR_BG(15, i, RGB16_REV(red, blue, green)); } // jumps to _0801D83C for this @@ -1774,7 +1774,7 @@ NONMATCH("asm/non_matching/game/stage/background/StageBgUpdate_Zone6Acts12.inc", u32 green = gUnknown_080D5C02[0][i][1]; u32 blue = gUnknown_080D5C02[0][i][2]; - gBgPalette[(15 * 16) + i] = ((blue << 10) | (green << 5) | red); + SET_PALETTE_COLOR_BG(15, i, RGB16_REV(red, blue, green)); } // _0801D83C @@ -1800,7 +1800,7 @@ NONMATCH("asm/non_matching/game/stage/background/StageBgUpdate_Zone6Acts12.inc", u32 green = ((gUnknown_080D5C02[0][i][1] * r6) >> 4) & 0x1F; u32 blue = ((gUnknown_080D5C02[0][i][2] * r6) >> 4) & 0x1F; - gBgPalette[(15 * 16) + i] = ((blue << 10) | (green << 5) | red); + SET_PALETTE_COLOR_BG(15, i, RGB16_REV(red, blue, green)); } gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; @@ -1809,7 +1809,7 @@ NONMATCH("asm/non_matching/game/stage/background/StageBgUpdate_Zone6Acts12.inc", case 7: { s8 i; for (i = 0; i < 16; i++) { - gBgPalette[(15 * 16) + i] = RGB_BLACK; + SET_PALETTE_COLOR_BG(15, i, RGB_BLACK); } gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; gDispCnt &= ~(DISPCNT_BG0_ON); @@ -2035,9 +2035,7 @@ NONMATCH("asm/non_matching/game/stage/background/Zone7BgUpdate_Inside.inc", void #else { // Draw the "ceiling" movement for (lineY = 0; lineY < 8; lineY++) { - dst = gBgPalette; - dst += 209; - dst[lineY] = sPalette_Zone7BgCeiling[((x >> 4) & 0x7) + 1]; + SET_PALETTE_COLOR_BG(13, lineY + 1, sPalette_Zone7BgCeiling[((x >> 4) & 0x7) + 1]); } } #endif @@ -2137,7 +2135,7 @@ const u16 sZone7BgTransitionRegions[2][NUM_ZONE7_BG_TRANSITION_POSITIONS] = { { 1344, 2616, 9432, 15192, 18552, 19892, 23158, 25848 }, // ACT 2 }; -const u16 gUnknown_080D5CC2[16] = INCBIN_U16("graphics/080D5CC2.gbapal"); +const u16 gUnknown_080D5CC2[PALETTE_LEN_4BPP] = INCBIN_U16("graphics/080D5CC2.gbapal"); void CreateStageBg_ZoneFinal_0(void) { @@ -2188,7 +2186,7 @@ void CreateStageBg_ZoneFinal_0(void) gBgScrollRegs[3][1] = 0; for (i = 0; i < ARRAY_COUNT(gUnknown_080D5CC2); i++) { - gBgPalette[i] = gUnknown_080D5CC2[i]; + SET_PALETTE_COLOR_BG(0, i, gUnknown_080D5CC2[i]); } gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; diff --git a/src/game/stage/intro.c b/src/game/stage/intro.c index c8828179c..fc671b99d 100644 --- a/src/game/stage/intro.c +++ b/src/game/stage/intro.c @@ -1234,7 +1234,7 @@ static void Task_UpdateStageLoadingScreen(void) IntroBackgrounds *introBackgrounds = TASK_DATA(gCurTask); u32 counter = introBackgrounds->controller->counter; - gBgPalette[0] = sZoneLoadingCharacterColors[gSelectedCharacter]; + SET_PALETTE_COLOR_BG(0, 0, sZoneLoadingCharacterColors[gSelectedCharacter]); gFlags |= FLAGS_UPDATE_BACKGROUND_PALETTES; diff --git a/src/game/water_effects.c b/src/game/water_effects.c index f81fbc5f2..2ce366aa6 100644 --- a/src/game/water_effects.c +++ b/src/game/water_effects.c @@ -109,10 +109,10 @@ void InitWaterPalettes(void) #ifndef NON_MATCHING { const u16 *src = gSpritePalettes[pal]; - CopyPalette((u32 *)wd->pal[j], (u32 *)src, 16); + CopyPalette((u32 *)wd->pal[j], (u32 *)src, PALETTE_LEN_4BPP); }; #else - CopyPalette((u32 *)wd->pal[j], (u32 *)gSpritePalettes[pal], 16); + CopyPalette((u32 *)wd->pal[j], (u32 *)gSpritePalettes[pal], PALETTE_LEN_4BPP); #endif } } else { @@ -121,21 +121,21 @@ void InitWaterPalettes(void) animId = gUnknown_080D550C[character]; animation = gAnimations[animId]; pal = animation[0]->pal.palId; - CopyPalette((u32 *)wd->pal[0], (u32 *)gSpritePalettes[pal], 16); + CopyPalette((u32 *)wd->pal[0], (u32 *)gSpritePalettes[pal], PALETTE_LEN_4BPP); character = gPlayer.character; animId = sCharacterPalettesBoostEffect[character]; animation = gAnimations[animId]; pal = animation[0]->pal.palId; - CopyPalette((u32 *)wd->pal[1], (u32 *)gSpritePalettes[pal], 16); + CopyPalette((u32 *)wd->pal[1], (u32 *)gSpritePalettes[pal], PALETTE_LEN_4BPP); } animId = SA2_ANIM_PALETTE_554; animation = gAnimations[animId]; pal = (animation[0]->pal.palId + 4); - CopyPalette((u32 *)wd->pal[4], (u32 *)gSpritePalettes[pal], 12 * 16); + CopyPalette((u32 *)wd->pal[4], (u32 *)gSpritePalettes[pal], 12 * PALETTE_LEN_4BPP); - MaskPaletteWithUnderwaterColor_inline((u32 *)wd->pal[16], (u32 *)gBgPalette, water->mask, 16 * 16); + MaskPaletteWithUnderwaterColor_inline((u32 *)wd->pal[16], (u32 *)&GET_PALETTE_COLOR_BG(0, 0), water->mask, 16 * PALETTE_LEN_4BPP); } void CreateStageWaterTask(s32 waterLevel, u32 p1, u32 mask)