From c21140d177aa48318d68afbfaa6d449365d0faf2 Mon Sep 17 00:00:00 2001 From: VHANTOMI Date: Wed, 7 Oct 2026 06:42:32 +0200 Subject: [PATCH 1/2] Honor DWORD-aligned rows in 24-bit BMP textures ## Summary Uncompressed BMP scanlines are DWORD-aligned, but the 24-bpp loader advances through source data as if each row were exactly `width * 3` bytes. When a row has padding, the loader reads padding bytes as pixel data for the next row and the texture colors are corrupted. Calculate and validate the 24-bpp source-row stride, then index each source row using that stride. ## Testing - Build gsKit with the PS2 toolchain. - Load 24-bpp BMPs with widths 1, 2, and 3 and at least two rows; use distinct colors per row and verify row order and colors. - Load a 24-bpp BMP with width 4 to verify that rows without padding remain correct. - Verify a truncated 24-bpp pixel payload is rejected before row data is accessed. --- ee/toolkit/src/gsToolkit.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/ee/toolkit/src/gsToolkit.c b/ee/toolkit/src/gsToolkit.c index eadacda..160147e 100644 --- a/ee/toolkit/src/gsToolkit.c +++ b/ee/toolkit/src/gsToolkit.c @@ -488,8 +488,27 @@ int gsKit_texture_bmp(GSGLOBAL *gsGlobal, GSTEXTURE *Texture, char *Path) Texture->Mem = memalign(128,TextureSize); if(Bitmap.InfoHeader.BitCount == 24) - { - image = memalign(128, FTexSize); +{ + u64 RowStride64; + u32 RowStride; + u8 *SourceRow; + + RowStride64 = (((u64)Texture->Width * 24 + 31) & ~31ULL) >> 3; + if (Texture->Width == 0 || Texture->Height == 0 || + RowStride64 == 0 || RowStride64 > FTexSize || + Texture->Height > FTexSize / RowStride64) + { + printf("BMP: Invalid 24-bit image data size\n"); + if (Texture->Mem) { + free(Texture->Mem); + Texture->Mem = NULL; + } + fclose(File); + return -1; + } + RowStride = (u32)RowStride64; + + image = memalign(128, FTexSize); if (image == NULL) { printf("BMP: Failed to allocate memory\n"); if (Texture->Mem) { @@ -507,6 +526,14 @@ int gsKit_texture_bmp(GSGLOBAL *gsGlobal, GSTEXTURE *Texture, char *Path) fread(image, FTexSize, 1, File); p = (void *)((u32)Texture->Mem); for (y = Texture->Height - 1, cy = 0; y >= 0; y--, cy++) { + SourceRow = image + (u32)cy * RowStride; + + for (x = 0; x < Texture->Width; x++) { + p[(y * Texture->Width + x) * 3 + 2] = SourceRow[x * 3 + 0]; + p[(y * Texture->Width + x) * 3 + 1] = SourceRow[x * 3 + 1]; + p[(y * Texture->Width + x) * 3 + 0] = SourceRow[x * 3 + 2]; + } + } for (x = 0; x < Texture->Width; x++) { p[(y * Texture->Width + x) * 3 + 2] = image[(cy * Texture->Width + x) * 3 + 0]; p[(y * Texture->Width + x) * 3 + 1] = image[(cy * Texture->Width + x) * 3 + 1]; From 490a9d81dcee0aac08f0ec1a229f2a0c9c8389e3 Mon Sep 17 00:00:00 2001 From: VHANTOMI Date: Wed, 7 Oct 2026 16:01:46 +0200 Subject: [PATCH 2/2] Update gsToolkit.c --- ee/toolkit/src/gsToolkit.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ee/toolkit/src/gsToolkit.c b/ee/toolkit/src/gsToolkit.c index 160147e..7538d3c 100644 --- a/ee/toolkit/src/gsToolkit.c +++ b/ee/toolkit/src/gsToolkit.c @@ -533,12 +533,6 @@ int gsKit_texture_bmp(GSGLOBAL *gsGlobal, GSTEXTURE *Texture, char *Path) p[(y * Texture->Width + x) * 3 + 1] = SourceRow[x * 3 + 1]; p[(y * Texture->Width + x) * 3 + 0] = SourceRow[x * 3 + 2]; } - } - for (x = 0; x < Texture->Width; x++) { - p[(y * Texture->Width + x) * 3 + 2] = image[(cy * Texture->Width + x) * 3 + 0]; - p[(y * Texture->Width + x) * 3 + 1] = image[(cy * Texture->Width + x) * 3 + 1]; - p[(y * Texture->Width + x) * 3 + 0] = image[(cy * Texture->Width + x) * 3 + 2]; - } } free(image); image = NULL;