mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-30 02:48:14 +03:00
Plus a bit of SACT2 implementation, and fix a bug where global arrays were not initialized properly.
316 lines
7.3 KiB
C
316 lines
7.3 KiB
C
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
|
*
|
|
* Based on code from xsystem35
|
|
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
|
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
|
*
|
|
* 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, see <http://gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include "little_endian.h"
|
|
#include "ald.h"
|
|
|
|
static const char *errtab[ALD_MAX_ERROR] = {
|
|
[ALD_SUCCESS] = "Success",
|
|
[ALD_FILE_ERROR] = "Error opening ALD file",
|
|
[ALD_BAD_ARCHIVE_ERROR] = "Invalid ALD file"
|
|
};
|
|
|
|
/* Get a message describing an error. */
|
|
const char *ald_strerror(int error)
|
|
{
|
|
if (error < ALD_MAX_ERROR)
|
|
return errtab[error];
|
|
return "Invalid error number";
|
|
}
|
|
|
|
/* Get the size of a file in bytes. */
|
|
static long get_file_size(FILE *fp)
|
|
{
|
|
fseek(fp, 0L, SEEK_END);
|
|
return ftell(fp);
|
|
}
|
|
|
|
/* Check validity of file. */
|
|
static bool file_check(FILE *fp)
|
|
{
|
|
uint8_t b[6];
|
|
int mapsize, ptrsize;
|
|
long filesize;
|
|
|
|
// get filesize / 256
|
|
filesize = (get_file_size(fp) + 255) >> 8;
|
|
|
|
long fsize = get_file_size(fp);
|
|
fseek(fp, fsize & ~0xFF, SEEK_SET);
|
|
fread(b, 1, 4, fp);
|
|
|
|
// read top 6 bytes
|
|
fseek(fp, 0L, SEEK_SET);
|
|
fread(b, 1, 6, fp);
|
|
|
|
// get ptrsize and mapsize
|
|
ptrsize = LittleEndian_get3B(b, 0);
|
|
mapsize = LittleEndian_get3B(b, 3) - ptrsize;
|
|
|
|
// check that sizes are valid
|
|
if (ptrsize < 0 || mapsize < 0)
|
|
return false;
|
|
if (ptrsize > filesize || mapsize > filesize)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/* Read the file map of an ALD file into a `struct ald_archive`. */
|
|
static void get_filemap(struct ald_archive *archive, FILE *fp)
|
|
{
|
|
uint8_t b[6], *_b;
|
|
int mapsize, ptrsize;
|
|
|
|
// read top 6 bytes
|
|
fseek(fp, 0L, SEEK_SET);
|
|
fread(b, 1, 6, fp);
|
|
|
|
// get ptrsize and mapsize
|
|
ptrsize = LittleEndian_get3B(b, 0);
|
|
mapsize = LittleEndian_get3B(b, 3) - ptrsize;
|
|
|
|
// allocate read buffer
|
|
_b = malloc(mapsize * 256);
|
|
|
|
// read filemap
|
|
fseek(fp, ptrsize * 256L, SEEK_SET);
|
|
fread(_b, 256, mapsize, fp);
|
|
|
|
// get max file number from mapdata
|
|
archive->maxfile = (mapsize * 256) / 3;
|
|
|
|
// map of disk
|
|
archive->map_disk = malloc(archive->maxfile);
|
|
archive->map_ptr = malloc(sizeof(short) * archive->maxfile);
|
|
|
|
for (int i = 0; i < archive->maxfile; i++) {
|
|
archive->map_disk[i] = _b[i * 3] - 1;
|
|
archive->map_ptr[i] = LittleEndian_getW(_b, i * 3 + 1) - 1;
|
|
}
|
|
|
|
free(_b);
|
|
return;
|
|
}
|
|
|
|
/* Read the pointer map of an ALD file into a `struct ald_archive`. */
|
|
static void get_ptrmap(struct ald_archive *archive, FILE *fp, int disk)
|
|
{
|
|
uint8_t b[8], *_b;
|
|
int ptrsize, filecnt;
|
|
|
|
// read top 6 bytes
|
|
fseek(fp, 0L, SEEK_SET);
|
|
fread(b, 1, 6, fp);
|
|
|
|
// get ptrmap size
|
|
ptrsize = LittleEndian_get3B(b, 0);
|
|
|
|
// estimate number of entries in ptrmap
|
|
filecnt = (ptrsize * 256) / 3;
|
|
|
|
// allocate read buffer
|
|
_b = malloc(ptrsize * 256);
|
|
|
|
// read pointers
|
|
fseek(fp, 0L, SEEK_SET);
|
|
fread(_b, 256, ptrsize, fp);
|
|
|
|
// allocate pointers buffer
|
|
archive->fileptr[disk] = calloc(filecnt, sizeof(int));
|
|
|
|
// store pointers
|
|
for (int i = 0; i < filecnt - 1; i++) {
|
|
*(archive->fileptr[disk] + i) = (LittleEndian_get3B(_b, i * 3 + 3) * 256);
|
|
}
|
|
|
|
free(_b);
|
|
return;
|
|
}
|
|
|
|
static int _ald_get(struct ald_archive *ar, int no, int *disk_out, int *dataptr_out)
|
|
{
|
|
int disk, ptr, dataptr, dataptr2;
|
|
|
|
// check that index is within range of file map
|
|
if (no > ar->maxfile)
|
|
return 0;
|
|
|
|
// look up data in file map
|
|
disk = ar->map_disk[no];
|
|
ptr = ar->map_ptr[no];
|
|
if (disk < 0 || ptr < 0)
|
|
return 0;
|
|
|
|
// no file registered
|
|
if (ar->fileptr[disk] == NULL)
|
|
return 0;
|
|
|
|
// get pointer in file and size
|
|
dataptr = *(ar->fileptr[disk] + ptr);
|
|
dataptr2 = *(ar->fileptr[disk] + ptr + 1);
|
|
if (!dataptr || !dataptr2)
|
|
return 0;
|
|
|
|
*dataptr_out = dataptr;
|
|
*disk_out = disk;
|
|
return dataptr2 - dataptr;
|
|
}
|
|
|
|
bool ald_data_exists(struct ald_archive *ar, int no)
|
|
{
|
|
int disk, dataptr;
|
|
return ar && !!_ald_get(ar, no, &disk, &dataptr);
|
|
}
|
|
|
|
/* Get a piece of data from an ALD archive. */
|
|
struct archive_data *ald_get(struct ald_archive *ar, int no)
|
|
{
|
|
if (!ar)
|
|
return NULL;
|
|
|
|
uint8_t *data;
|
|
struct archive_data *dfile;
|
|
int disk, dataptr, ptr, size;
|
|
int readsize = _ald_get(ar, no, &disk, &dataptr);
|
|
|
|
// get data top
|
|
if (ar->mmapped) {
|
|
data = ar->files[disk].data + dataptr;
|
|
} else {
|
|
//int readsize = dataptr2 - dataptr;
|
|
FILE *fp;
|
|
// FIXME: check return values
|
|
data = malloc(sizeof(char) * readsize);
|
|
fp = fopen(ar->files[disk].name, "r");
|
|
fseek(fp, dataptr, SEEK_SET);
|
|
fread(data, 1, readsize, fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
// get real data and size
|
|
ptr = LittleEndian_getDW(data, 0);
|
|
size = LittleEndian_getDW(data, 4);
|
|
|
|
dfile = calloc(1, sizeof(struct archive_data));
|
|
dfile->data_raw = data;
|
|
dfile->data = data + ptr;
|
|
dfile->size = size;
|
|
dfile->name = strdup((char*)data + 16);
|
|
dfile->archive = ar;
|
|
return dfile;
|
|
}
|
|
|
|
/* Free an ald_data strcture returned by `ald_get`. */
|
|
void ald_free_data(struct archive_data *data)
|
|
{
|
|
if (!data)
|
|
return;
|
|
if (!((struct ald_archive*)data->archive)->mmapped)
|
|
free(data->data);
|
|
free(data->name);
|
|
free(data);
|
|
}
|
|
|
|
/* Free an ald_archive structure returned by `ald_open`. */
|
|
void ald_free_archive(struct ald_archive *ar)
|
|
{
|
|
if (!ar)
|
|
return;
|
|
// unmap mmap files
|
|
if (ar->mmapped) {
|
|
for (int i = 0; i < ALD_FILEMAX; i++) {
|
|
if (ar->files[i].data)
|
|
munmap(ar->files[i].data, ar->files[i].size);
|
|
}
|
|
}
|
|
free(ar);
|
|
}
|
|
|
|
/* Open an ALD archive, optionally memory-mapping it. */
|
|
struct ald_archive *ald_open(char **files, int count, int flags, int *error)
|
|
{
|
|
FILE *fp;
|
|
long filesize;
|
|
struct ald_archive *ar = calloc(1, sizeof(struct ald_archive));
|
|
bool gotmap = false;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
// XXX: why allow this?
|
|
if (!files[i])
|
|
continue;
|
|
if (!(fp = fopen(files[i], "r"))) {
|
|
*error = ALD_FILE_ERROR;
|
|
goto exit_err;
|
|
}
|
|
// check if it's a valid archive
|
|
if (!file_check(fp)) {
|
|
*error = ALD_BAD_ARCHIVE_ERROR;
|
|
fclose(fp);
|
|
goto exit_err;
|
|
}
|
|
// get file map, if we haven't already
|
|
if (!gotmap) {
|
|
get_filemap(ar, fp);
|
|
gotmap = true;
|
|
}
|
|
// get pointer map
|
|
get_ptrmap(ar, fp, i);
|
|
// get file size for mmap
|
|
filesize = get_file_size(fp);
|
|
// copy filename
|
|
ar->files[i].name = strdup(files[i]);
|
|
// close
|
|
fclose(fp);
|
|
if (flags & ALD_MMAP) {
|
|
int fd;
|
|
if (0 > (fd = open(files[i], O_RDONLY))) {
|
|
*error = ALD_FILE_ERROR;
|
|
goto exit_err;
|
|
}
|
|
ar->files[i].data = mmap(0, filesize, PROT_READ, MAP_SHARED, fd, 0);
|
|
close(fd);
|
|
if (ar->files[i].data == MAP_FAILED) {
|
|
*error = ALD_FILE_ERROR;
|
|
goto exit_err;
|
|
}
|
|
ar->files[i].size = filesize;
|
|
}
|
|
}
|
|
int c = 0;
|
|
for (int i = 0; i < ar->maxfile; i++) {
|
|
if (ar->map_disk[i] < 0 || ar->map_ptr[i] < 0)
|
|
continue;
|
|
c++;
|
|
}
|
|
ar->mmapped = flags & ALD_MMAP;
|
|
ar->nr_files = count;
|
|
return ar;
|
|
exit_err:
|
|
free(ar);
|
|
return NULL;
|
|
}
|