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.
This commit is contained in:
kichikuou
2024-02-18 10:28:13 +09:00
parent 2895e338c0
commit cdb16f73a3
3 changed files with 34 additions and 36 deletions
+1 -1
View File
@@ -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 |
+32 -34
View File
@@ -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];
+1 -1
View File
@@ -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;