From e1287e5b862be6278d8368a81b4211bcbde23ee9 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Fri, 6 Jun 2025 09:31:03 +0900 Subject: [PATCH] load_pms: Abort decoding when pixel data is insufficient This fixes https://github.com/kichikuou/system3-sdl2/issues/34. Some PMS in Rance 4.1/4.2 fan English translation have one less row of pixel data than image height. When such data is encountered, decoding stops. The image rows decoded up to that point are transferred to VRAM. --- src/sys/ags.h | 2 +- src/sys/ags_draw.cpp | 6 ++-- src/sys/ags_pms.cpp | 86 +++++++++++++++++++++++--------------------- 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/src/sys/ags.h b/src/sys/ags.h index 5baced4..16eb648 100644 --- a/src/sys/ags.h +++ b/src/sys/ags.h @@ -55,7 +55,7 @@ private: void load_gm3(const std::vector& data, int transparent); // Intruder -桜屋敷の探索- void load_vsp2l(const std::vector& data, int transparent); // Little Vampire void load_gl3(const std::vector& data, bool set_palette, int transparent); - void load_pms(const std::vector& data, bool set_palette, int transparent); + void load_pms(int page, const std::vector& data, bool set_palette, int transparent); void load_bmp(const char* file_name); // あゆみちゃん物語 フルカラー実写版 void load_vsp(const std::vector& data, bool set_palette, int transparent); diff --git a/src/sys/ags_draw.cpp b/src/sys/ags_draw.cpp index cf004b2..7c01230 100644 --- a/src/sys/ags_draw.cpp +++ b/src/sys/ags_draw.cpp @@ -56,12 +56,12 @@ void AGS::load_cg(int page, int transparent) load_vsp(data, set_palette, transparent); } else if (game_id.is_sdps()) { // Super D.P.S - load_pms(data, set_palette, transparent); + load_pms(page, data, set_palette, transparent); } else { if(data[0x8] == 0) { load_vsp(data, set_palette, transparent); } else { - load_pms(data, set_palette, transparent); + load_pms(page, data, set_palette, transparent); } } break; @@ -72,7 +72,7 @@ void AGS::load_cg(int page, int transparent) load_vsp(data, set_palette, transparent); } else { set_palette = set_palette || game_id.is(GameId::FUNNYBEE_CD); - load_pms(data, set_palette, transparent); + load_pms(page, data, set_palette, transparent); } break; } diff --git a/src/sys/ags_pms.cpp b/src/sys/ags_pms.cpp index 10bef81..97a9032 100644 --- a/src/sys/ags_pms.cpp +++ b/src/sys/ags_pms.cpp @@ -8,7 +8,7 @@ #include "game_id.h" #include -void AGS::load_pms(const std::vector& data, bool set_palette, int transparent) +void AGS::load_pms(int page, const std::vector& data, bool set_palette, int transparent) { // ヘッダ取得 int sx = data[0x0] | (data[0x1] << 8); @@ -87,66 +87,72 @@ void AGS::load_pms(const std::vector& data, bool set_palette, int trans } } - // PMS展開 - uint8 cgdata[3][640]; + if (!extract_cg) { + return; + } + // Extract pixel data + std::vector buf[3]; + buf[0].resize(width); + buf[1].resize(width); + buf[2].resize(width); int p = 0x320; - memset(cgdata, 0, sizeof(cgdata)); - for(int y = 0; y < height; y++) { + for (int y = 0; y < height; y++) { int x = 0; - while(x < width) { - uint8 d1 = data[p++]; - if(d1 == 0xff) { + while (x < width) { + // Some PMS images in Rance 4.1/4.2 English translation do not have + // enough pixels for the image size. + if (p >= data.size()) { + WARNING("CG #%d: PMS data is incomplete or corrupted.", page); + goto finish; + } + + uint8_t d1 = data[p++]; + if (d1 == 0xff) { int length = data[p++] + 3; - memcpy(cgdata[0] + x, cgdata[1] + x, length); + memcpy(buf[0].data() + x, buf[1].data() + x, length); x += length; - } else if(d1 == 0xfe) { + } else if (d1 == 0xfe) { int length = data[p++] + 3; - memcpy(cgdata[0] + x, cgdata[2] + x, length); + memcpy(buf[0].data() + x, buf[2].data() + x, length); x += length; - } else if(d1 == 0xfd) { + } else if (d1 == 0xfd) { int length = data[p++] + 4; - uint8 d2 = data[p++]; - memset(cgdata[0] + x, d2, length); + uint8_t d2 = data[p++]; + memset(buf[0].data() + x, d2, length); x += length; - } else if(d1 == 0xfc) { + } else if (d1 == 0xfc) { int length = data[p++] + 3; - uint8 d2 = data[p++]; - uint8 d3 = data[p++]; + uint8_t d2 = data[p++]; + uint8_t d3 = data[p++]; for(int i = 0; i < length; i++) { - cgdata[0][x++] = d2; - cgdata[0][x++] = d3; + buf[0][x++] = d2; + buf[0][x++] = d3; } - } else if(d1 == 0xfb || d1 == 0xfa || d1 == 0xf9 || d1 == 0xf8) { - cgdata[0][x++] = data[p++]; + } else if (d1 == 0xfb || d1 == 0xfa || d1 == 0xf9 || d1 == 0xf8) { + buf[0][x++] = data[p++]; } else { - cgdata[0][x++] = d1; + buf[0][x++] = d1; } } - // VRAMに転送 - if(extract_cg) { - uint8_t* dest = &vram[dest_screen][y + sy][sx]; - if(transparent == -1) { - for(int x = 0; x < width; x++) { - cgdata[2][x] = cgdata[1][x]; - cgdata[1][x] = cgdata[0][x]; - dest[x] = cgdata[0][x]; - } - } else { - for(int x = 0; x < width; x++) { - cgdata[2][x] = cgdata[1][x]; - cgdata[1][x] = cgdata[0][x]; - if(cgdata[0][x] != transparent) { - dest[x] = cgdata[0][x]; - } + // Transfer the row to VRAM + uint8_t* dest = &vram[dest_screen][y + sy][sx]; + if (transparent == -1) { + memcpy(dest, buf[0].data(), width); + } else { + for (int x = 0; x < width; x++) { + if (buf[0][x] != transparent) { + dest[x] = buf[0][x]; } } } + buf[2].swap(buf[1]); + buf[1].swap(buf[0]); } - // 画面更新 - if(dest_screen == 0 && extract_cg) { +finish: + if (dest_screen == 0) { draw_screen(sx, sy, width, height); } }