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.
This commit is contained in:
kichikuou
2025-06-06 09:31:03 +09:00
parent 073da3464b
commit e1287e5b86
3 changed files with 50 additions and 44 deletions
+1 -1
View File
@@ -55,7 +55,7 @@ private:
void load_gm3(const std::vector<uint8_t>& data, int transparent); // Intruder -桜屋敷の探索-
void load_vsp2l(const std::vector<uint8_t>& data, int transparent); // Little Vampire
void load_gl3(const std::vector<uint8_t>& data, bool set_palette, int transparent);
void load_pms(const std::vector<uint8_t>& data, bool set_palette, int transparent);
void load_pms(int page, const std::vector<uint8_t>& data, bool set_palette, int transparent);
void load_bmp(const char* file_name); // あゆみちゃん物語 フルカラー実写版
void load_vsp(const std::vector<uint8_t>& data, bool set_palette, int transparent);
+3 -3
View File
@@ -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;
}
+46 -40
View File
@@ -8,7 +8,7 @@
#include "game_id.h"
#include <string.h>
void AGS::load_pms(const std::vector<uint8_t>& data, bool set_palette, int transparent)
void AGS::load_pms(int page, const std::vector<uint8_t>& data, bool set_palette, int transparent)
{
// ヘッダ取得
int sx = data[0x0] | (data[0x1] << 8);
@@ -87,66 +87,72 @@ void AGS::load_pms(const std::vector<uint8_t>& data, bool set_palette, int trans
}
}
// PMS展開
uint8 cgdata[3][640];
if (!extract_cg) {
return;
}
// Extract pixel data
std::vector<uint8_t> 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);
}
}