/* * dri.c: dri loader * * Copyright (C) 1997-1998 Masaki Chikama (Wren) * 1998- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* $Id: dri.c,v 1.21 2004/10/31 04:18:06 chikama Exp $ */ #include #include #include #include #include "portab.h" #include "system.h" #include "LittleEndian.h" #include "dri.h" static bool read_index(int volume, drifiles *d, FILE *fp) { fseek(fp, 0L, SEEK_END); int filesize = ftell(fp); // 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 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 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; } for (int i = 0; i < d->nr_files; i++) { if (d->link[i * 3] != volume) continue; if (d->maxno < i) d->maxno = i; int offset_index = LittleEndian_getW(d->link, i * 3 + 1); d->offset[i] = LittleEndian_get3B(otbl, offset_index * 3) << 8; } free(otbl); return true; } drifiles *dri_init(const char **file, int cnt, bool use_mmap) { drifiles *d = calloc(1, sizeof(drifiles)); #if !defined(HAVE_MEMORY_MAPPED_FILE) || defined(__EMSCRIPTEN__) use_mmap = false; #endif for (int i = 0; i < cnt; i++) { if (!file[i]) continue; FILE *fp = fopen(file[i], "rb"); if (!fp) SYSERROR("%s: %s", file[i], strerror(errno)); 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) SYSERROR("%s: not an ALD file", file[i]); WARNING("%s: not an ALD file", file[i]); fclose(fp); continue; } d->fnames[i] = strdup(file[i]); fclose(fp); if (use_mmap) { mmap_t *m = map_file(file[i]); if (!m) { use_mmap = d->mmapped = false; i = 0; /* retry */ } else { d->mmap[i] = m; d->mmapped = true; } } } return d; } bool dri_is_linked(drifiles *d, int no) { return no >= 0 && no < d->nr_files && d->link[no * 3]; } bool dri_exists(drifiles *d, int no) { return dri_is_linked(d, no) && d->offset[no]; } static void warn_missing_ald(drifiles *d, int no) { static bool warned = false; if (warned) return; warned = true; int vol = d->link[no * 3]; char msgbuf[1024]; if (d->fnames[vol - 1]) { snprintf(msgbuf, sizeof(msgbuf), "Cannot read resource #%d from %s.", no, d->fnames[vol - 1]); } else if (d->fnames[0]) { char *fname = strdup(d->fnames[0]); char *p = strrchr(fname, '.'); if (p && p > fname) p[-1] = vol - 1 + 'A'; snprintf(msgbuf, sizeof(msgbuf), "%s is not found.\nPlease make sure you have all .ALD files copied from the CD-ROM.", fname); free(fname); } else { snprintf(msgbuf, sizeof(msgbuf), "Cannot read resource #%d.", no); } sys_show_message_box(MESSAGEBOX_WARNING, "xsystem35", msgbuf); } dridata *dri_getdata(drifiles *d, int no) { if (!dri_exists(d, no)) { if (dri_is_linked(d, no)) warn_missing_ald(d, no); return NULL; } int volume = d->link[no * 3]; uint8_t *data; int ptr, size; if (d->mmapped) { 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[volume - 1], "rb"); if (!fp) return NULL; uint8_t entry_header[8]; fseek(fp, d->offset[no], SEEK_SET); if (fread(entry_header, sizeof(entry_header), 1, fp) != 1) { fclose(fp); return NULL; } ptr = LittleEndian_getDW(entry_header, 0); size = LittleEndian_getDW(entry_header, 4); data = malloc(ptr + size); memcpy(data, entry_header, sizeof(entry_header)); if (fread(data + sizeof(entry_header), ptr + size - sizeof(entry_header), 1, fp) != 1) { free(data); fclose(fp); return NULL; } fclose(fp); } dridata *dfile = calloc(1, sizeof(dridata)); dfile->data_raw = data; /* dri data header */ dfile->data = data + ptr; /* real data */ dfile->size = size; dfile->name = data + 16; dfile->a = d; /* archive file */ return dfile; }