From cdb16f73a32d35794e7c2fedb63e44bffb7ec749 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 18 Feb 2024 10:16:53 +0900 Subject: [PATCH] Read link table only from first ALD volume This fixes https://github.com/kichikuou/xsystem35-sdl2/issues/45. The behavior when the contents of link table differ between ALD volumes is different between System 3.8 and System 3.9, and System 3.9 uses the link table of the first volume. The MangaGamer version of Rance 5D depends on this behavior. As far as I know, the only game that depends on the System 3.8 behavior is Child Assassin (in Alice CD 2.50). Considering the additional complexity, we do not implement switching to the old behavior. --- game_compatibility.md | 2 +- src/dri.c | 66 +++++++++++++++++++++---------------------- src/dri.h | 2 +- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/game_compatibility.md b/game_compatibility.md index b1665e3..a0a0135 100644 --- a/game_compatibility.md +++ b/game_compatibility.md @@ -76,7 +76,7 @@ Game Compatibility | マスカレイド | Supported | From アリスCD | | キャロル ~聖なる鐘が響く夜~ | Supported | From アリスCD, Online match is not supported | | できるかなー? | Supported | From アリスCD | -| Child Assassin | Supported | From アリスCD | +| Child Assassin | Unsupported | From アリスCD | | Klavier | Supported | From アリスCD | | 弱肉狂食 | Supported | From アリスCD | | 眼鏡大戦2 | Supported | From アリスCD | diff --git a/src/dri.c b/src/dri.c index c9ce490..e25fe29 100644 --- a/src/dri.c +++ b/src/dri.c @@ -30,50 +30,48 @@ #include "LittleEndian.h" #include "dri.h" -static bool read_index(int disk, drifiles *d, FILE *fp) { +static bool read_index(int volume, drifiles *d, FILE *fp) { fseek(fp, 0L, SEEK_END); int filesize = ftell(fp); - // Get ptrsize and mapsize + // Get the size of offsets table and link table. uint8_t hdr[6]; fseek(fp, 0L, SEEK_SET); if (fread(hdr, 6, 1, fp) != 1) return false; - int ptrsize = LittleEndian_get3B(hdr, 0) << 8; - int mapsize = (LittleEndian_get3B(hdr, 3) << 8) - ptrsize; - if (ptrsize <= 0 || mapsize <= 0 || ptrsize + mapsize > filesize) + int ofssize = LittleEndian_get3B(hdr, 0) << 8; + int linksize = (LittleEndian_get3B(hdr, 3) << 8) - ofssize; + if (ofssize <= 0 || linksize <= 0 || ofssize + linksize > filesize) return false; - // Read the pointer table and the link table - uint8_t *ptbl = malloc(ptrsize + mapsize); - memcpy(ptbl, hdr, 6); - if (fread(ptbl + 6, ptrsize + mapsize - 6, 1, fp) != 1) { - free(ptbl); + // Read the link table. In System 3.9, only the first volume's link table is + // valid. It's different in System 3.8 and earlier, and games that depend + // on the old behavior (e.g. Child Assassin in Alice CD 2.50) won't work. + if (!d->link) { + d->nr_files = linksize / 3; + d->link = calloc(1, linksize); + d->offset = calloc(sizeof(uint32_t), d->nr_files); + fseek(fp, ofssize, SEEK_SET); + if (fread(d->link, linksize, 1, fp) != 1) + return false; + } + + // Read the offsets table. + uint8_t *otbl = malloc(ofssize); + fseek(fp, 0L, SEEK_SET); + if (fread(otbl, ofssize, 1, fp) != 1) { + free(otbl); return false; } - uint8_t *ltbl = ptbl + ptrsize; - - // (Re)allocate the index buffers - int nr_files = mapsize / 3; - if (d->nr_files < nr_files) { - d->disk = realloc(d->disk, nr_files); - d->offset = realloc(d->offset, sizeof(uint32_t) * nr_files); - memset(d->disk + d->nr_files, 0, nr_files - d->nr_files); - memset(d->offset + d->nr_files, 0, sizeof(uint32_t) * (nr_files - d->nr_files)); - d->nr_files = nr_files; - } - - // Parse the index - for (int i = 0; i < nr_files; i++) { - if (disk != ltbl[i * 3] - 1) + for (int i = 0; i < d->nr_files; i++) { + if (d->link[i * 3] != volume) continue; if (d->maxno < i) d->maxno = i; - d->disk[i] = ltbl[i * 3]; - int ptr = LittleEndian_getW(ltbl, i * 3 + 1); - d->offset[i] = LittleEndian_get3B(ptbl, ptr * 3) << 8; + int offset_index = LittleEndian_getW(d->link, i * 3 + 1); + d->offset[i] = LittleEndian_get3B(otbl, offset_index * 3) << 8; } - free(ptbl); + free(otbl); return true; } @@ -90,7 +88,7 @@ drifiles *dri_init(const char **file, int cnt, boolean use_mmap) { FILE *fp = fopen(file[i], "rb"); if (!fp) SYSERROR("%s: %s", file[i], strerror(errno)); - if (!read_index(i, d, fp)) { + if (!read_index(i + 1, d, fp)) { // Only errors in *A.ALD are fatal, because some games have // dummy (invalid) *[B-Z].ALD files. if (i == 0) @@ -117,18 +115,18 @@ drifiles *dri_init(const char **file, int cnt, boolean use_mmap) { } dridata *dri_getdata(drifiles *d, int no) { - if (no < 0 || no >= d->nr_files || !d->disk[no] || !d->offset[no]) + if (no < 0 || no >= d->nr_files || !d->link[no * 3] || !d->offset[no]) return NULL; - int disk = d->disk[no] - 1; + int volume = d->link[no * 3]; uint8_t *data; int ptr, size; if (d->mmapped) { - data = d->mmap[disk]->addr + d->offset[no]; + data = d->mmap[volume - 1]->addr + d->offset[no]; ptr = LittleEndian_getDW(data, 0); size = LittleEndian_getDW(data, 4); } else { - FILE *fp = fopen(d->fnames[disk], "rb"); + FILE *fp = fopen(d->fnames[volume - 1], "rb"); if (!fp) return NULL; uint8_t entry_header[8]; diff --git a/src/dri.h b/src/dri.h index 2ac6d4c..7c4304f 100644 --- a/src/dri.h +++ b/src/dri.h @@ -35,7 +35,7 @@ struct _drifiles { char *fnames[DRIFILEMAX]; int nr_files; // upper limit on how many files could be referenced by this archive int maxno; - uint8_t *disk; // file numbers + uint8_t *link; // link table uint32_t *offset; // offsets in file }; typedef struct _drifiles drifiles;