Move libsys4 into separate repository

To allow pulling xsystem4 and alice-tools apart.
This commit is contained in:
Nunuhara Cabbage
2020-08-28 21:32:18 -07:00
parent 300f85db72
commit 80b8eacab2
37 changed files with 28 additions and 6446 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "subprojects/libsys4"]
path = subprojects/libsys4
url = https://github.com/nunuhara/libsys4
+7
View File
@@ -25,6 +25,13 @@ First install the dependencies (corresponding Debian package in parentheses):
* SDL2_ttf (libsdl2-ttf-dev)
* zlib (zlib1g-dev)
Then fetch the git submodules,
git submodule init
git submodule update
(Alternatively, pass `--recurse-submodules` when cloning this repository)
Then build the xsystem4 executable with meson,
mkdir build
+3
View File
@@ -20,4 +20,7 @@ bison = find_program('bison')
incdir = include_directories('include')
libsys4_proj = subproject('libsys4')
libsys4_dep = libsys4_proj.get_variable('libsys4_dep')
subdir('src')
-157
View File
@@ -1,157 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdio.h>
#include <string.h>
#include <assert.h>
#include <zlib.h>
#include "system4.h"
#include "system4/acx.h"
#include "system4/string.h"
#include "little_endian.h"
static struct acx *acx_read(uint8_t *data_raw)
{
struct acx *acx = xcalloc(1, sizeof(struct acx));
acx->nr_columns = LittleEndian_getDW(data_raw, 0);
acx->nr_lines = LittleEndian_getDW(data_raw, 4 + acx->nr_columns * 4);
uint8_t *data = data_raw + (8 + acx->nr_columns * 4);
acx->column_types = xcalloc(acx->nr_columns, sizeof(int32_t));
for (int i = 0; i < acx->nr_columns; i++) {
acx->column_types[i] = LittleEndian_getDW(data_raw, 4 + 4*i);
if (acx->column_types[i] != ACX_INT && acx->column_types[i] != ACX_STRING)
WARNING("Column %d has unknown type (%d)", i, acx->column_types[i]);
}
int ptr = 0;
acx->lines = xcalloc(acx->nr_lines * acx->nr_columns, sizeof(union acx_value));
for (int line = 0; line < acx->nr_lines; line++) {
for (int col = 0; col < acx->nr_columns; col++) {
if (acx->column_types[col] == 2) {
int old_ptr = ptr;
while (data[ptr])
ptr++;
acx->lines[line*acx->nr_columns + col].s = make_string((char*)data+old_ptr, ptr-old_ptr);
ptr++;
} else {
acx->lines[line*acx->nr_columns + col].i = LittleEndian_getDW(data, ptr);
ptr += 4;
}
}
}
return acx;
}
struct acx *acx_load(const char *path)
{
FILE *fp;
long len;
uint8_t *buf = NULL;
if (!(fp = fopen(path, "rb"))) {
WARNING("ACXLoader.Load: Failed to open %s", path);
return NULL;
}
// get size of ACX file
fseek(fp, 0, SEEK_END);
len = ftell(fp);
fseek(fp, 0, SEEK_SET);
// read ACX file into memory
buf = xmalloc(len + 4);
fread(buf, 1, len, fp);
fclose(fp);
// check magic
if (strncmp((char*)buf, "ACX\0\0\0\0", 8)) {
free(buf);
return false;
}
int compressed_size = LittleEndian_getDW(buf, 8);
unsigned long size = LittleEndian_getDW(buf, 12);
uint8_t *data_raw = xmalloc(size);
if (Z_OK != uncompress(data_raw, &size, buf+16, compressed_size)) {
WARNING("ACXLoader.Load: uncompress failed");
free(buf);
free(data_raw);
return NULL;
}
struct acx *acx = acx_read(data_raw);
free(buf);
free(data_raw);
return acx;
}
void acx_free(struct acx *acx)
{
if (!acx)
return;
for (int col = 0; col < acx->nr_columns; col++) {
if (acx->column_types[col] != 2)
continue;
for (int line = 0; line < acx->nr_lines; line++) {
free_string(acx->lines[line*acx->nr_columns+col].s);
}
}
free(acx->lines);
free(acx->column_types);
free(acx);
}
int acx_nr_lines(struct acx *acx)
{
return acx->nr_lines;
}
int acx_nr_columns(struct acx *acx)
{
return acx->nr_columns;
}
enum acx_column_type acx_column_type(struct acx *acx, int col)
{
assert(col >= 0);
assert(col < acx->nr_columns);
return acx->column_types[col];
}
int acx_get_int(struct acx *acx, int line, int col)
{
assert(line >= 0);
assert(line < acx->nr_lines);
assert(col >= 0);
assert(col < acx->nr_columns);
return acx->lines[line*acx->nr_columns + col].i;
}
struct string *acx_get_string(struct acx *acx, int line, int col)
{
assert(line >= 0);
assert(line < acx->nr_lines);
assert(col >= 0);
assert(col < acx->nr_columns);
return acx->lines[line*acx->nr_columns + col].s;
}
-309
View File
@@ -1,309 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <zlib.h>
#include "little_endian.h"
#include "system4.h"
#include "system4/afa.h"
#include "system4/archive.h"
#include "system4/buffer.h"
#include "system4/string.h"
static bool afa_exists(struct archive *ar, int no);
static struct archive_data *afa_get(struct archive *ar, int no);
static struct archive_data *afa_get_by_name(struct archive *ar, const char *name);
static bool afa_load_file(struct archive_data *data);
static void afa_for_each(struct archive *ar, void (*iter)(struct archive_data *data, void *user), void *user);
static void afa_free_data(struct archive_data *data);
static void afa_free(struct archive *ar);
struct archive_ops afa_archive_ops = {
.exists = afa_exists,
.get = afa_get,
.get_by_name = afa_get_by_name,
.load_file = afa_load_file,
.for_each = afa_for_each,
.free_data = afa_free_data,
.free = afa_free,
};
static bool afa_exists(struct archive *_ar, int no)
{
struct afa_archive *ar = (struct afa_archive*)_ar;
return (uint32_t)no < ar->nr_files;
}
static bool afa_load_file(struct archive_data *data)
{
if (data->data)
return true;
struct afa_archive *ar = (struct afa_archive*)data->archive;
struct afa_entry *e = &ar->files[data->no];
if (ar->ar.mmapped) {
data->data = (uint8_t*)ar->mmap_ptr + ar->data_start + e->off;
return true;
}
fseek(ar->f, ar->data_start + e->off, SEEK_SET);
data->data = xmalloc(e->size);
if (fread(data->data, e->size, 1, ar->f) != 1) {
WARNING("Failed to read '%s': %s", ar->filename, strerror(errno));
free(data->data);
free(data);
return false;
}
return true;
}
static struct archive_data *afa_get_descriptor(struct archive *_ar, int no)
{
struct afa_archive *ar = (struct afa_archive*)_ar;
if ((uint32_t)no >= ar->nr_files)
return NULL;
struct afa_entry *e = &ar->files[no];
struct archive_data *data = xcalloc(1, sizeof(struct archive_data));
data->size = e->size;
data->name = e->name->text;
data->no = no;
data->archive = _ar;
return data;
}
static struct archive_data *afa_get(struct archive *_ar, int no)
{
struct archive_data *data = afa_get_descriptor(_ar, no);
if (!data)
return NULL;
if (!afa_load_file(data)) {
afa_free_data(data);
return NULL;
}
return data;
}
static struct archive_data *afa_get_by_name(struct archive *_ar, const char *name)
{
// TODO: index filenames
struct afa_archive *ar = (struct afa_archive*)_ar;
for (uint32_t i = 0; i < ar->nr_files; i++) {
if (!strcmp(name, ar->files[i].name->text)) {
return afa_get(_ar, i);
}
}
return NULL;
}
static void afa_for_each(struct archive *_ar, void (*iter)(struct archive_data *data, void *user), void *user)
{
struct afa_archive *ar = (struct afa_archive*)_ar;
for (uint32_t i = 0; i < ar->nr_files; i++) {
struct archive_data *data = afa_get_descriptor(_ar, i);
if (!data)
continue;
iter(data, user);
afa_free_data(data);
}
}
static void afa_free_data(struct archive_data *data)
{
if (data->data && !data->archive->mmapped)
free(data->data);
free(data);
}
static void afa_free(struct archive *_ar)
{
struct afa_archive *ar = (struct afa_archive*)_ar;
for (uint32_t i = 0; i < ar->nr_files; i++) {
free_string(ar->files[i].name);
}
if (ar->f)
fclose(ar->f);
free(ar->filename);
free(ar->files);
free(ar);
}
static bool afa_read_header(FILE *f, struct afa_archive *ar, int *error)
{
char buf[44];
if (fread(buf, 44, 1, f) != 1) {
*error = ARCHIVE_FILE_ERROR;
return false;
}
fseek(f, 0, SEEK_END);
ar->file_size = ftell(f);
fseek(f, 0, SEEK_SET);
if (strncmp(buf, "AFAH", 4) ||
strncmp(buf+8, "AlicArch", 8) ||
strncmp(buf+28, "INFO", 4) ||
LittleEndian_getDW((uint8_t*)buf, 4) != 0x1c) {
*error = ARCHIVE_BAD_ARCHIVE_ERROR;
return false;
}
ar->version = LittleEndian_getDW((uint8_t*)buf, 16);
ar->unknown = LittleEndian_getDW((uint8_t*)buf, 20);
ar->data_start = LittleEndian_getDW((uint8_t*)buf, 24);
ar->compressed_size = LittleEndian_getDW((uint8_t*)buf, 32) - 16;
ar->uncompressed_size = LittleEndian_getDW((uint8_t*)buf, 36);
ar->nr_files = LittleEndian_getDW((uint8_t*)buf, 40);
if (ar->data_start+8 >= ar->file_size) {
*error = ARCHIVE_FILE_ERROR;
return false;
}
fseek(f, ar->data_start, SEEK_SET);
if (fread(buf, 8, 1, f) != 1) {
*error = ARCHIVE_FILE_ERROR;
return false;
}
fseek(f, 0, SEEK_SET);
if (strncmp(buf, "DATA", 4)) {
*error = ARCHIVE_BAD_ARCHIVE_ERROR;
return false;
}
ar->data_size = LittleEndian_getDW((uint8_t*)buf, 4);
if (ar->data_start + ar->data_size > ar->file_size) {
*error = ARCHIVE_BAD_ARCHIVE_ERROR;
return false;
}
return true;
}
static bool afa_read_entry(struct buffer *in, struct afa_archive *ar, struct afa_entry *entry, int *error)
{
uint32_t name_len = buffer_read_int32(in);
entry->name = buffer_read_pascal_string(in); // NOTE: length is padded
entry->name->size = name_len; // fix length
entry->unknown0 = buffer_read_int32(in);
entry->unknown1 = buffer_read_int32(in);
if (ar->version == 1)
entry->unknown2 = buffer_read_int32(in);
entry->off = buffer_read_int32(in);
entry->size = buffer_read_int32(in);
return true;
}
static bool afa_read_file_table(FILE *f, struct afa_archive *ar, int *error)
{
uint8_t *buf = xmalloc(ar->compressed_size);
uint8_t *table = xmalloc(ar->uncompressed_size);
fseek(f, 44, SEEK_SET);
if (fread(buf, ar->compressed_size, 1, f) != 1) {
*error = ARCHIVE_FILE_ERROR;
goto exit_err;
}
unsigned long uncompressed_size = ar->uncompressed_size;
if (uncompress(table, &uncompressed_size, buf, ar->compressed_size) != Z_OK) {
*error = ARCHIVE_BAD_ARCHIVE_ERROR;
goto exit_err;
}
struct buffer r;
buffer_init(&r, table, ar->uncompressed_size);
ar->files = xcalloc(ar->nr_files, sizeof(struct afa_entry));
for (uint32_t i = 0; i < ar->nr_files; i++) {
if (!afa_read_entry(&r, ar, &ar->files[i], error)) {
free(ar->files);
goto exit_err;
}
}
free(buf);
free(table);
return true;
exit_err:
free(buf);
free(table);
return false;
}
struct afa_archive *afa_open(const char *file, int flags, int *error)
{
#ifdef _WIN32
flags &= ~ARCHIVE_MMAP;
#endif
FILE *fp;
struct afa_archive *ar = xcalloc(1, sizeof(struct afa_archive));
if (!(fp = fopen(file, "rb"))) {
WARNING("fopen failed: %s", strerror(errno));
*error = ARCHIVE_FILE_ERROR;
goto exit_err;
}
if (!afa_read_header(fp, ar, error)) {
WARNING("afa_read_header failed");
fclose(fp);
goto exit_err;
}
if (!afa_read_file_table(fp, ar, error)) {
WARNING("afa_read_file_table failed");
fclose(fp);
goto exit_err;
}
if (flags & ARCHIVE_MMAP) {
if (fclose(fp)) {
WARNING("fclose failed: %s", strerror(errno));
*error = ARCHIVE_FILE_ERROR;
goto exit_err;
}
int fd = open(file, O_RDONLY);
if (fd < 0) {
WARNING("open failed: %s", strerror(errno));
*error = ARCHIVE_FILE_ERROR;
goto exit_err;
}
ar->mmap_ptr = mmap(0, ar->file_size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
if (ar->mmap_ptr == MAP_FAILED) {
WARNING("mmap failed: %s", strerror(errno));
*error = ARCHIVE_FILE_ERROR;
goto exit_err;
}
ar->ar.mmapped = true;
} else {
ar->f = fp;
}
ar->filename = strdup(file);
ar->ar.ops = &afa_archive_ops;
return ar;
exit_err:
free(ar);
return NULL;
}
-1468
View File
File diff suppressed because it is too large Load Diff
-156
View File
@@ -1,156 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <string.h>
#include <turbojpeg.h>
#include <zlib.h>
#include "little_endian.h"
#include "system4.h"
#include "system4/cg.h"
#include "system4/pms.h"
bool ajp_checkfmt(const uint8_t *data)
{
if (data[0] != 'A' || data[1] != 'J' || data[2] != 'P' || data[3] != '\0')
return false;
return true;
}
static const uint8_t ajp_key[] = {
0x5d, 0x91, 0xae, 0x87,
0x4a, 0x56, 0x41, 0xcd,
0x83, 0xec, 0x4c, 0x92,
0xb5, 0xcb, 0x16, 0x34
};
static void ajp_decrypt(uint8_t *data, size_t size)
{
for (size_t i = 0; i < 16 && i < size; i++) {
data[i] ^= ajp_key[i];
}
}
struct ajp_header {
uint32_t width;
uint32_t height;
uint32_t jpeg_off;
uint32_t jpeg_size;
uint32_t mask_off;
uint32_t mask_size;
};
static void ajp_extract_header(const uint8_t *data, struct ajp_header *ajp)
{
ajp->width = LittleEndian_getDW(data, 12);
ajp->height = LittleEndian_getDW(data, 16);
ajp->jpeg_off = LittleEndian_getDW(data, 20);
ajp->jpeg_size = LittleEndian_getDW(data, 24);
ajp->mask_off = LittleEndian_getDW(data, 28);
ajp->mask_size = LittleEndian_getDW(data, 32);
}
static void ajp_init_metrics(struct ajp_header *ajp, struct cg_metrics *dst)
{
dst->w = ajp->width;
dst->h = ajp->height;
dst->bpp = 24;
dst->has_pixel = ajp->jpeg_size > 0;
//dst->has_alpha = ajp->mask_size > 0;
dst->has_alpha = false;
dst->pixel_pitch = ajp->width * 3;
dst->alpha_pitch = 1;
}
void ajp_extract(const uint8_t *data, size_t size, struct cg *cg)
{
uint8_t *buf = NULL, *jpeg_data = NULL, *mask_data = NULL;
int width, height, subsamp;
struct ajp_header ajp;
ajp_extract_header(data, &ajp);
ajp_init_metrics(&ajp, &cg->metrics);
if (ajp.jpeg_off > size)
ERROR("AJP JPEG offset invalid");
if (ajp.jpeg_off + ajp.jpeg_size > size)
ERROR("AJP JPEG size invalid");
if (ajp.mask_off > size)
ERROR("AJP mask offset invalid");
if (ajp.mask_off + ajp.mask_size > size)
ERROR("AJP mask size invalid");
jpeg_data = xmalloc(ajp.jpeg_size);
mask_data = xmalloc(ajp.mask_size);
memcpy(jpeg_data, data + ajp.jpeg_off, ajp.jpeg_size);
memcpy(mask_data, data + ajp.mask_off, ajp.mask_size);
ajp_decrypt(jpeg_data, ajp.jpeg_size);
ajp_decrypt(mask_data, ajp.mask_size);
tjhandle decompressor = tjInitDecompress();
tjDecompressHeader2(decompressor, jpeg_data, ajp.jpeg_size, &width, &height, &subsamp);
if ((uint32_t)width != ajp.width)
WARNING("AJP width doesn't match JPEG width (%d vs. %u)", width, ajp.width);
if ((uint32_t)height != ajp.height)
WARNING("AJP height doesn't match JPEG height (%d vs. %u)", height, ajp.height);
buf = xmalloc(width * height * 3);
if (tjDecompress2(decompressor, jpeg_data, ajp.jpeg_size, buf, width, 0, height, TJPF_RGB, TJFLAG_FASTDCT) < 0) {
WARNING("JPEG decompression failed: %s", tjGetErrorStr());
free(buf);
goto cleanup;
}
uint8_t *mask;
if (ajp.mask_size && pms8_checkfmt(mask_data)) {
mask = pms_extract_mask(mask_data, ajp.mask_size);
} else if (mask_data[0] == 0x78) {
// compressed
unsigned long uncompressed_size = width*height;
mask = xmalloc(uncompressed_size);
if (uncompress(mask, &uncompressed_size, mask_data, ajp.mask_size) != Z_OK) {
WARNING("uncompress failed");
memset(mask, 0xFF, width * height);
} else if (uncompressed_size != (unsigned)width * (unsigned)height) {
WARNING("Unexpected AJP mask size");
}
} else {
if (ajp.mask_size)
WARNING("Unsupported AJP mask format: %02x %02x %02x %02x",
mask_data[0], mask_data[1], mask_data[2], mask_data[3]);
mask = xmalloc(width * height);
memset(mask, 0xFF, width * height);
}
uint8_t *tmp = xmalloc(width * height * 4);
for (int i = 0; i < width * height; i++) {
tmp[i*4+0] = buf[i*3+0];
tmp[i*4+1] = buf[i*3+1];
tmp[i*4+2] = buf[i*3+2];
tmp[i*4+3] = mask[i];
}
free(buf);
free(mask);
buf = tmp;
cg->type = ALCG_AJP;
cg->pixels = buf;
cleanup:
free(jpeg_data);
free(mask_data);
tjDestroy(decompressor);
}
-402
View File
@@ -1,402 +0,0 @@
/* 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 <fcntl.h>
#include "little_endian.h"
#include "system4.h"
#include "system4/ald.h"
static bool ald_exists(struct archive *ar, int no);
static struct archive_data *ald_get(struct archive *ar, int no);
static struct archive_data *ald_get_by_name(struct archive *_ar, const char *name);
static bool ald_load_file(struct archive_data *data);
static void ald_for_each(struct archive *_ar, void (*iter)(struct archive_data *data, void *user), void *user);
static void ald_free_data(struct archive_data *data);
static void ald_free(struct archive *ar);
struct archive_ops ald_archive_ops = {
.exists = ald_exists,
.get = ald_get,
.get_by_name = ald_get_by_name,
.load_file = ald_load_file,
.for_each = ald_for_each,
.free_data = ald_free_data,
.free = ald_free,
};
/* 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 < 0 || 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;
}
static bool ald_exists(struct archive *ar, int no)
{
int disk, dataptr;
return ar && !!_ald_get((struct ald_archive*)ar, no, &disk, &dataptr);
}
/* Get a descriptor for a file in an ALD archive. */
struct archive_data *ald_get_descriptor(struct archive *_ar, int no)
{
if (!_ar)
return NULL;
struct ald_archive *ar = (struct ald_archive*)_ar;
struct ald_archive_data *dfile = calloc(1, sizeof(struct ald_archive_data));
if (!_ald_get(ar, no, &dfile->disk, &dfile->dataptr)) {
free(dfile);
return NULL;
}
// read header
if (ar->ar.mmapped) {
uint8_t *hdr = ar->files[dfile->disk].data + dfile->dataptr;
dfile->hdr_size = LittleEndian_getDW(hdr, 0);
dfile->data.size = LittleEndian_getDW(hdr, 4);
dfile->data.name = strdup((char*)hdr + 16);
} else {
uint8_t *hdr = xmalloc(8);
FILE *fp = ar->files[dfile->disk].fp;
// read header size, file size
fseek(fp, dfile->dataptr, SEEK_SET);
fread(hdr, 8, 1, fp);
dfile->hdr_size = LittleEndian_getDW(hdr, 0);
dfile->data.size = LittleEndian_getDW(hdr, 4);
// read name
dfile->data.name = xcalloc(dfile->hdr_size-8, 1);
fread(dfile->data.name, dfile->hdr_size-8, 1, fp);
free(hdr);
}
dfile->data.no = no;
dfile->data.archive = &ar->ar;
return &dfile->data;
}
/* Get a file from an ALD archive. */
struct archive_data *ald_get(struct archive *ar, int no)
{
if (!ar)
return NULL;
struct archive_data *data = ald_get_descriptor(ar, no);
if (!data)
return NULL;
if (!ald_load_file(data)) {
ald_free_data(data);
return NULL;
}
return data;
}
static struct archive_data *ald_get_by_name(struct archive *_ar, const char *name)
{
struct ald_archive *ar = (struct ald_archive*)_ar;
for (int i = 0; i < ar->maxfile; i++) {
struct archive_data *data = ald_get_descriptor(_ar, i);
if (!data)
continue;
if (!strcmp(data->name, name)) {
ald_load_file(data);
return data;
}
ald_free_data(data);
}
return NULL;
}
static bool ald_load_file(struct archive_data *data)
{
struct ald_archive *ar = (struct ald_archive*)data->archive;
struct ald_archive_data *dfile = (struct ald_archive_data*)data;
if (data->archive->mmapped) {
data->data = ar->files[dfile->disk].data + dfile->dataptr + dfile->hdr_size;
} else {
FILE *fp = ar->files[dfile->disk].fp;
data->data = xmalloc(data->size);
fseek(fp, dfile->dataptr + dfile->hdr_size, SEEK_SET);
fread(data->data, data->size, 1, fp);
}
return data;
}
static void ald_for_each(struct archive *_ar, void (*iter)(struct archive_data *data, void *user), void *user)
{
struct ald_archive *ar = (struct ald_archive*)_ar;
for (int i = 0; i < ar->maxfile; i++) {
struct archive_data *data = ald_get_descriptor(_ar, i);
if (!data)
continue;
iter(data, user);
ald_free_data(data);
}
}
/* Free an ald_data strcture returned by `ald_get`. */
static void ald_free_data(struct archive_data *data)
{
if (!data)
return;
if (!data->archive->mmapped && data->data)
free(data->data);
free(data->name);
free(data);
}
/* Free an ald_archive structure returned by `ald_open`. */
static void ald_free(struct archive *_ar)
{
if (!_ar)
return;
struct ald_archive *ar = (struct ald_archive*)_ar;
// unmap mmap files/close file descriptors
for (int i = 0; i < ALD_FILEMAX; i++) {
if (ar->ar.mmapped && ar->files[i].data) {
munmap(ar->files[i].data, ar->files[i].size);
} else if (ar->files[i].fp) {
fclose(ar->files[i].fp);
}
free(ar->files[i].name);
}
free(ar);
}
/* Open an ALD archive, optionally memory-mapping it. */
struct 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;
#ifdef _WIN32
flags &= ~ARCHIVE_MMAP;
#endif
for (int i = 0; i < count; i++) {
if (!files[i])
continue;
if (!(fp = fopen(files[i], "r"))) {
*error = ARCHIVE_FILE_ERROR;
goto exit_err;
}
// check if it's a valid archive
if (!file_check(fp)) {
*error = ARCHIVE_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 & ARCHIVE_MMAP) {
int fd;
if (0 > (fd = open(files[i], O_RDONLY))) {
*error = ARCHIVE_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 = ARCHIVE_FILE_ERROR;
goto exit_err;
}
ar->files[i].size = filesize;
} else {
// get a file descriptor for each file
if (!(ar->files[i].fp = fopen(files[i], "rb"))) {
*error = ARCHIVE_FILE_ERROR;
goto exit_err;
}
}
}
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->ar.mmapped = flags & ARCHIVE_MMAP;
ar->nr_files = count;
ar->ar.ops = &ald_archive_ops;
return &ar->ar;
exit_err:
free(ar);
return NULL;
}
-32
View File
@@ -1,32 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 "system4.h"
#include "system4/ald.h"
static const char *errtab[ARCHIVE_MAX_ERROR] = {
[ARCHIVE_SUCCESS] = "Success",
[ARCHIVE_FILE_ERROR] = "Error opening ALD file",
[ARCHIVE_BAD_ARCHIVE_ERROR] = "Invalid ALD file"
};
/* Get a message describing an error. */
const char *archive_strerror(int error)
{
if (error < ARCHIVE_MAX_ERROR)
return errtab[error];
return "Invalid error number";
}
-153
View File
@@ -1,153 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdint.h>
#include <string.h>
#include "little_endian.h"
#include "system4.h"
#include "system4/buffer.h"
#include "system4/string.h"
void buffer_init(struct buffer *r, uint8_t *buf, size_t size)
{
r->buf = buf;
r->size = size;
r->index = 0;
}
int32_t buffer_read_int32(struct buffer *r)
{
if (r->index + 4 > r->size)
ERROR("Out of bounds buffer read");
int32_t v = LittleEndian_getDW(r->buf, r->index);
r->index += 4;
return v;
}
uint8_t buffer_read_u8(struct buffer *r)
{
if (r->index + 1 > r->size)
ERROR("Out of bounds buffer read");
return r->buf[r->index++];
}
uint16_t buffer_read_u16(struct buffer *r)
{
if (r->index + 2 > r->size)
ERROR("Out of bounds buffer read");
uint16_t v = LittleEndian_getW(r->buf, r->index);
r->index += 2;
return v;
}
float buffer_read_float(struct buffer *r)
{
union { int32_t i; float f; } v;
v.i = buffer_read_int32(r);
return v.f;
}
struct string *buffer_read_pascal_string(struct buffer *r)
{
int32_t len = buffer_read_int32(r);
if (len < 0)
ERROR("Invalid string length: %d", len);
struct string *s = make_string((char*)r->buf+r->index, len);
r->index += len;
return s;
}
void buffer_skip(struct buffer *r, size_t off)
{
r->index = min(r->index + off, r->size);
}
size_t buffer_remaining(struct buffer *r)
{
return r->size - r->index;
}
char *buffer_strdata(struct buffer *r)
{
return (char*)r->buf + r->index;
}
static void alloc_buffer(struct buffer *b, size_t size)
{
if (b->index + size < b->size)
return;
size_t new_size = b->index + size;
if (!b->size)
b->size = 64;
while (b->size <= new_size)
b->size *= 2;
b->buf = xrealloc(b->buf, b->size);
}
void buffer_write_int32(struct buffer *b, uint32_t v)
{
alloc_buffer(b, 4);
b->buf[b->index++] = (v & 0x000000FF);
b->buf[b->index++] = (v & 0x0000FF00) >> 8;
b->buf[b->index++] = (v & 0x00FF0000) >> 16;
b->buf[b->index++] = (v & 0xFF000000) >> 24;
}
void buffer_write_int32_at(struct buffer *buf, size_t index, uint32_t v)
{
size_t tmp = buf->index;
buf->index = index;
buffer_write_int32(buf, v);
buf->index = tmp;
}
void buffer_write_int16(struct buffer *b, uint16_t v)
{
alloc_buffer(b, 2);
b->buf[b->index++] = (v & 0x00FF);
b->buf[b->index++] = (v & 0xFF00) >> 8;
}
void buffer_write_float(struct buffer *b, float f)
{
union { float f; uint32_t u; } v;
v.f = f;
buffer_write_int32(b, v.u);
}
void buffer_write_bytes(struct buffer *b, const uint8_t *bytes, size_t len)
{
alloc_buffer(b, len);
memcpy(b->buf + b->index, bytes, len);
b->index += len;
}
void buffer_write_string(struct buffer *b, struct string *s)
{
buffer_write_bytes(b, (uint8_t*)s->text, s->size+1);
}
void buffer_write_cstring(struct buffer *b, const char *s)
{
buffer_write_bytes(b, (uint8_t*)s, strlen(s)+1);
}
void buffer_write_pascal_string(struct buffer *b, struct string *s)
{
buffer_write_int32(b, s->size);
buffer_write_bytes(b, (uint8_t*)s->text, s->size);
}
-143
View File
@@ -1,143 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdint.h>
#include <stdlib.h>
#include "system4.h"
#include "system4/archive.h"
#include "system4/cg.h"
#include "system4/ajp.h"
#include "system4/dcf.h"
#include "system4/qnt.h"
#include "system4/webp.h"
/*
* Identify cg format
* data: pointer to compressed data
* return: cg type
*/
enum cg_type cg_check_format(uint8_t *data)
{
if (qnt_checkfmt(data)) {
return ALCG_QNT;
} else if (ajp_checkfmt(data)) {
return ALCG_AJP;
} else if (webp_checkfmt(data)) {
return ALCG_WEBP;
} else if (dcf_checkfmt(data)) {
return ALCG_DCF;
}
return ALCG_UNKNOWN;
}
bool cg_get_metrics(struct archive *ar, int no, struct cg_metrics *dst)
{
struct archive_data *dfile;
if (!(dfile = archive_get(ar, no)))
return false;
switch (cg_check_format(dfile->data)) {
case ALCG_QNT:
qnt_get_metrics(dfile->data, dst);
break;
case ALCG_AJP:
WARNING("AJP GetMetrics not implemented");
archive_free_data(dfile);
return false;
case ALCG_WEBP:
webp_get_metrics(dfile->data, dfile->size, dst);
break;
case ALCG_DCF:
dcf_get_metrics(dfile->data, dfile->size, dst);
break;
default:
WARNING("Unknown CG type (CG %d)", no);
archive_free_data(dfile);
return false;
}
archive_free_data(dfile);
return true;
}
/*
* Free CG data
* cg: object to free
*/
void cg_free(struct cg *cg)
{
if (!cg)
return;
free(cg->pixels);
free(cg);
}
struct cg *cg_load_data(struct archive_data *dfile)
{
int type;
struct cg *cg = xcalloc(1, sizeof(struct cg));
// check loaded cg format
type = cg_check_format(dfile->data);
// extract cg
switch(type) {
case ALCG_QNT:
qnt_extract(dfile->data, cg);
break;
case ALCG_AJP:
ajp_extract(dfile->data, dfile->size, cg);
break;
case ALCG_PNG:
WARNING("Unimplemented CG type: PNG");
break;
case ALCG_WEBP:
webp_extract(dfile->data, dfile->size, cg, dfile->archive);
break;
case ALCG_DCF:
dcf_extract(dfile->data, dfile->size, cg);
break;
default:
WARNING("Unknown CG type");
break;
}
if (cg->pixels)
return cg;
free(cg);
return NULL;
}
/*
* Load cg data from file or cache
* no: file no ( >= 0)
* return: cg object(extracted)
*/
struct cg *cg_load(struct archive *ar, int no)
{
struct cg *cg;
struct archive_data *dfile;
if (!(dfile = archive_get(ar, no))) {
WARNING("Failed to load CG %d", no);
return NULL;
}
cg = cg_load_data(dfile);
archive_free_data(dfile);
return cg;
}
-61
View File
@@ -1,61 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdlib.h>
#include <string.h>
#include "little_endian.h"
#include "system4.h"
#include "system4/cg.h"
#include "system4/dcf.h"
#include "system4/qnt.h"
static const uint8_t *dcf_get_qnt(const uint8_t *data)
{
if (data[0] != 'd' && data[1] != 'c' && data[2] != 'f' && data[3] != 0x20)
return NULL;
int h2 = 8 + LittleEndian_getDW(data, 4);
if (data[h2] != 'd' || data[h2+1] != 'f' || data[h2+2] != 'd' || data[h2+3] != 'l')
return NULL;
int h3 = h2 + 8 + LittleEndian_getDW(data, h2+4);
if (data[h3] != 'd' || data[h3+1] != 'c' || data[h3+2] != 'g' || data[h3+3] != 'd')
return NULL;
if (data[h3+8] != 'Q' || data[h3+9] != 'N' || data[h3+10] != 'T' || data[h3+11] != '\0')
return NULL;
return data + h3 + 8;
}
bool dcf_checkfmt(const uint8_t *data)
{
return !strncmp((const char*)data, "dcf\x20", 4);
}
void dcf_get_metrics(const uint8_t *data, possibly_unused size_t size, struct cg_metrics *m)
{
if (!(data = dcf_get_qnt(data)))
return;
qnt_get_metrics(data, m);
}
void dcf_extract(const uint8_t *data, possibly_unused size_t size, struct cg *cg)
{
if (!(data = dcf_get_qnt(data)))
return;
qnt_extract(data, cg);
}
-517
View File
@@ -1,517 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <zlib.h>
#include "little_endian.h"
#include "system4.h"
#include "system4/buffer.h"
#include "system4/ex.h"
#include "system4/string.h"
#define EX_ERROR(buf, fmt, ...) ERROR("At 0x%08x: " fmt, (uint32_t)(buf)->index, ##__VA_ARGS__)
#define EX_WARNING(buf, fmt, ...) WARNING("At 0x%08x: " fmt, (uint32_t)(buf)->index, ##__VA_ARGS__)
const char *ex_strtype(enum ex_value_type type)
{
switch (type) {
case EX_INT: return "int";
case EX_FLOAT: return "float";
case EX_STRING: return "string";
case EX_TABLE: return "table";
case EX_LIST: return "list";
case EX_TREE: return "tree";
default: return "unknown_type";
}
}
uint8_t ex_decode_table[256];
uint8_t ex_decode_table_inv[256];
static struct string *ex_read_string(struct buffer *r)
{
struct string *s = buffer_read_pascal_string(r);
// TODO: at most 3 bytes padding, no need for full strlen
s->size = strlen(s->text);
// TODO: validate?
return s;
}
static bool ex_initialized = false;
static void ex_init(void)
{
// initialize table
for (int i = 0; i < 256; i++) {
int tmpfor = i;
int tmp = tmpfor;
tmp = (tmp & 0x55) + ((tmp >> 1) & 0x55);
tmp = (tmp & 0x33) + ((tmp >> 2) & 0x33);
tmp = (tmp & 0x0F) + ((tmp >> 4) & 0x0F);
if ((tmp & 0x01) == 0) {
tmpfor = ((tmpfor << (8 - tmp)) | (tmpfor >> tmp)) & 0xFF;
} else {
tmpfor = ((tmpfor >> (8 - tmp)) | (tmpfor << tmp)) & 0xFF;
}
ex_decode_table[i] = tmpfor;
}
// initialize inverse table
for (int i = 0; i < 256; i++) {
ex_decode_table_inv[ex_decode_table[i]] = i;
}
ex_initialized = true;
}
void ex_encode(uint8_t *buf, size_t size)
{
if (!ex_initialized)
ex_init();
for (size_t i = 0; i < size; i++) {
buf[i] = ex_decode_table_inv[buf[i]];
}
}
static uint8_t *ex_decode(uint8_t *data, size_t *len, uint32_t *nr_blocks)
{
struct buffer r;
uint32_t compressed_size;
unsigned long uncompressed_size;
if (!ex_initialized)
ex_init();
buffer_init(&r, data, *len);
if (strncmp(buffer_strdata(&r), "HEAD", 4))
EX_ERROR(&r, "Missing HEAD section marker");
buffer_skip(&r, 8);
if (strncmp(buffer_strdata(&r), "EXTF", 4)) {
EX_ERROR(&r, "Missing EXTF section marker");
}
buffer_skip(&r, 8);
if (nr_blocks)
*nr_blocks = buffer_read_int32(&r);
else
buffer_read_int32(&r);
if (strncmp(buffer_strdata(&r), "DATA", 4)) {
EX_ERROR(&r, "Missing DATA section marker");
}
buffer_skip(&r, 4);
compressed_size = buffer_read_int32(&r);
uncompressed_size = buffer_read_int32(&r);
// decode compressed data
for (size_t i = 0; i < compressed_size; i++) {
data[r.index+i] = ex_decode_table[data[r.index+i]];
}
uint8_t *out = xmalloc(uncompressed_size);
int rv = uncompress(out, &uncompressed_size, (uint8_t*)buffer_strdata(&r), compressed_size);
switch (rv) {
case Z_BUF_ERROR: ERROR("Uncompress failed: Z_BUF_ERROR");
case Z_MEM_ERROR: ERROR("Uncompress failed: Z_MEM_ERROR");
case Z_DATA_ERROR: ERROR("Uncompress failed: Z_DATA_ERROR");
case Z_OK: break;
default: ERROR("Uncompress failed: %d", rv);
}
// decompress
*len = uncompressed_size;
return out;
}
static void ex_read_fields(struct buffer *r, struct ex_table *table);
static void ex_read_table(struct buffer *r, struct ex_table *table, struct ex_field *fields, uint32_t nr_fields);
static void ex_read_list(struct buffer *r, struct ex_list *list);
static void _ex_read_value(struct buffer *r, struct ex_value *value, struct ex_field *fields, uint32_t nr_fields)
{
switch (value->type) {
case EX_INT:
value->i = buffer_read_int32(r);
break;
case EX_FLOAT:
value->f = buffer_read_float(r);
break;
case EX_STRING:
value->s = buffer_read_pascal_string(r);
break;
case EX_TABLE:
value->t = xcalloc(1, sizeof(struct ex_table));
// XXX: if nr_fields is zero, we are NOT a sub-table and therefore need to read fields
if (!nr_fields) {
ex_read_fields(r, value->t);
ex_read_table(r, value->t, value->t->fields, value->t->nr_fields);
} else {
ex_read_table(r, value->t, fields, nr_fields);
}
break;
case EX_LIST:
value->list = xcalloc(1, sizeof(struct ex_list));
ex_read_list(r, value->list);
break;
default:
EX_ERROR(r, "Unhandled value type: %d", value->type);
}
}
static void ex_read_value(struct buffer *r, struct ex_value *value, struct ex_field *fields, uint32_t nr_fields)
{
value->type = buffer_read_int32(r);
_ex_read_value(r, value, fields, nr_fields);
}
static void ex_read_field(struct buffer *r, struct ex_field *field)
{
field->type = buffer_read_int32(r);
if (field->type < EX_INT || field->type > EX_TABLE)
EX_ERROR(r, "Unknown/invalid field type: %d", field->type);
field->name = ex_read_string(r);
field->has_value = buffer_read_int32(r);
field->is_index = buffer_read_int32(r);
if (field->has_value) {
field->value.type = field->type;
_ex_read_value(r, &field->value, NULL, 0);
}
if (field->has_value && field->has_value != 1)
EX_WARNING(r, "Non-boolean for field->has_value: %d", field->has_value);
if (field->is_index && field->is_index != 1)
EX_WARNING(r, "Non-boolean for field->is_index: %d", field->is_index);
if (field->type == EX_TABLE) {
field->nr_subfields = buffer_read_int32(r);
if (field->nr_subfields > 255)
EX_ERROR(r, "Too many subfields: %u", field->nr_subfields);
field->subfields = xcalloc(field->nr_subfields, sizeof(struct ex_field));
for (uint32_t i = 0; i < field->nr_subfields; i++) {
ex_read_field(r, &field->subfields[i]);
}
}
}
static void ex_read_fields(struct buffer *r, struct ex_table *table)
{
table->nr_fields = buffer_read_int32(r);
table->fields = xcalloc(table->nr_fields, sizeof(struct ex_field));
for (uint32_t i = 0; i < table->nr_fields; i++) {
ex_read_field(r, &table->fields[i]);
}
}
enum table_layout {
TABLE_DEFAULT,
TABLE_COLUMNS_FIRST,
TABLE_ROWS_FIRST
};
static enum table_layout table_layout = TABLE_DEFAULT;
static void ex_read_table(struct buffer *r, struct ex_table *table, struct ex_field *fields, uint32_t nr_fields)
{
// NOTE: Starting in Evenicle, the rows/columns quantities are reversed
if (table_layout == TABLE_ROWS_FIRST) {
table->nr_rows = buffer_read_int32(r);
table->nr_columns = buffer_read_int32(r);
} else {
table->nr_columns = buffer_read_int32(r);
table->nr_rows = buffer_read_int32(r);
}
// try switching to column-major order if fields don't make sense
if (table_layout == TABLE_DEFAULT && table->nr_columns != nr_fields) {
if (table->nr_rows == nr_fields) {
uint32_t tmp = table->nr_columns;
table->nr_columns = table->nr_rows;
table->nr_rows = tmp;
table_layout = TABLE_ROWS_FIRST;
} else {
EX_ERROR(r, "Number of fields doesn't match number of columns: %u, %u", table->nr_columns, nr_fields);
}
} else if (table->nr_columns != nr_fields) {
EX_ERROR(r, "Number of fields doesn't match number of columns: %u, %u", table->nr_columns, nr_fields);
}
table->rows = xcalloc(table->nr_rows, sizeof(struct ex_value*));
for (uint32_t i = 0; i < table->nr_rows; i++) {
table->rows[i] = xcalloc(table->nr_columns, sizeof(struct ex_value));
for (uint32_t j = 0; j < table->nr_columns; j++) {
ex_read_value(r, &table->rows[i][j], fields[j].subfields, fields[j].nr_subfields);
if (table->rows[i][j].type != fields[j].type) {
// broken table in Rance 03?
EX_WARNING(r, "Column type doesn't match field type: expected %s; got %s",
ex_strtype(fields[j].type), ex_strtype(table->rows[i][j].type));
}
}
}
}
static void ex_read_list(struct buffer *r, struct ex_list *list)
{
list->nr_items = buffer_read_int32(r);
list->items = xcalloc(list->nr_items, sizeof(struct ex_list_item));
for (uint32_t i = 0; i < list->nr_items; i++) {
list->items[i].value.type = buffer_read_int32(r);
list->items[i].size = buffer_read_int32(r);
size_t data_loc = r->index;
_ex_read_value(r, &list->items[i].value, NULL, 0);
if (r->index - data_loc != list->items[i].size) {
EX_ERROR(r, "Incorrect size for list item: %" SIZE_T_FMT "u / %" SIZE_T_FMT "u",
list->items[i].size, r->index - data_loc);
}
}
}
static void ex_read_tree(struct buffer *r, struct ex_tree *tree)
{
tree->name = ex_read_string(r);
uint32_t is_leaf = buffer_read_int32(r);
if (is_leaf > 1)
EX_ERROR(r, "tree->is_leaf is not a boolean: %u", is_leaf);
tree->is_leaf = is_leaf;
if (!tree->is_leaf) {
tree->nr_children = buffer_read_int32(r);
tree->children = xcalloc(tree->nr_children, sizeof(struct ex_tree));
for (uint32_t i = 0; i < tree->nr_children; i++) {
ex_read_tree(r, &tree->children[i]);
}
} else {
tree->leaf.value.type = buffer_read_int32(r);
tree->leaf.size = buffer_read_int32(r);
size_t data_loc = r->index;
tree->leaf.name = ex_read_string(r);
_ex_read_value(r, &tree->leaf.value, NULL, 0);
if (r->index - data_loc != tree->leaf.size) {
EX_ERROR(r, "Incorrect size for leaf node: %" SIZE_T_FMT "u / %" SIZE_T_FMT "u",
tree->leaf.size, r->index - data_loc);
}
int32_t zero = buffer_read_int32(r);
if (zero) {
EX_ERROR(r, "Expected 0 after leaf node: 0x%x at 0x%zx", zero, r->index);
}
}
}
static void ex_read_block(struct buffer *r, struct ex_block *block)
{
block->val.type = buffer_read_int32(r);
if (block->val.type < EX_INT || block->val.type > EX_TREE)
EX_ERROR(r, "Unknown/invalid block type: %d", block->val.type);
block->size = buffer_read_int32(r);
if (block->size > buffer_remaining(r))
EX_ERROR(r, "Block size extends past end of file: %" SIZE_T_FMT "u", block->size);
size_t data_loc = r->index;
block->name = ex_read_string(r);
switch (block->val.type) {
case EX_INT:
block->val.i = buffer_read_int32(r);
break;
case EX_FLOAT:
block->val.f = buffer_read_float(r);
break;
case EX_STRING:
block->val.s = buffer_read_pascal_string(r);
break;
case EX_TABLE:
block->val.t = xcalloc(1, sizeof(struct ex_table));
ex_read_fields(r, block->val.t);
ex_read_table(r, block->val.t, block->val.t->fields, block->val.t->nr_fields);
break;
case EX_LIST:
block->val.list = xcalloc(1, sizeof(struct ex_list));
ex_read_list(r, block->val.list);
break;
case EX_TREE:
block->val.tree = xcalloc(1, sizeof(struct ex_tree));
ex_read_tree(r, block->val.tree);
break;
}
if (r->index - data_loc != block->size) {
EX_ERROR(r, "Incorrect block size: %" SIZE_T_FMT "u / %" SIZE_T_FMT "u",
r->index - data_loc, block->size);
}
}
uint8_t *ex_decrypt(const char *path, size_t *size, uint32_t *nr_blocks)
{
FILE *fp;
long len;
uint8_t *buf = NULL;
if (!(fp = fopen(path, "rb")))
ERROR("fopen failed: %s", strerror(errno));
fseek(fp, 0, SEEK_END);
len = ftell(fp);
fseek(fp, 0, SEEK_SET);
buf = xmalloc(len+1);
if (fread(buf, len, 1, fp) != 1)
ERROR("fread failed: %s", strerror(errno));
if (fclose(fp))
ERROR("fclose failed: %s", strerror(errno));
buf[len] = '\0';
*size = len;
uint8_t *decoded = ex_decode(buf, size, nr_blocks);
free(buf);
return decoded;
}
struct ex *ex_read(const char *path)
{
size_t decoded_len;
uint32_t nr_blocks;
uint8_t *decoded = ex_decrypt(path, &decoded_len, &nr_blocks);
struct buffer r;
buffer_init(&r, decoded, decoded_len);
struct ex *ex = xmalloc(sizeof(struct ex));
ex->nr_blocks = nr_blocks;
ex->blocks = xcalloc(nr_blocks, sizeof(struct ex_block));
for (size_t i = 0; i < nr_blocks; i++) {
ex_read_block(&r, &ex->blocks[i]);
}
free(decoded);
return ex;
}
static void ex_free_table(struct ex_table *table);
static void ex_free_list(struct ex_list *list);
static void ex_free_tree(struct ex_tree *tree);
static void ex_free_value(struct ex_value *value)
{
switch (value->type) {
case EX_STRING:
free_string(value->s);
break;
case EX_TABLE:
ex_free_table(value->t);
break;
case EX_LIST:
ex_free_list(value->list);
break;
case EX_TREE:
ex_free_tree(value->tree);
free(value->tree);
break;
default:
break;
}
}
static void ex_free_values(struct ex_value *values, uint32_t n)
{
for (uint32_t i = 0; i < n; i++) {
ex_free_value(&values[i]);
}
free(values);
}
static void ex_free_fields(struct ex_field *fields, uint32_t n)
{
for (uint32_t i = 0; i < n; i++) {
free_string(fields[i].name);
if (fields[i].type == EX_STRING && fields[i].has_value)
free_string(fields[i].value.s);
ex_free_fields(fields[i].subfields, fields[i].nr_subfields);
}
free(fields);
}
static void ex_free_table(struct ex_table *table)
{
ex_free_fields(table->fields, table->nr_fields);
for (uint32_t i = 0; i < table->nr_rows; i++) {
ex_free_values(table->rows[i], table->nr_columns);
}
free(table->rows);
free(table);
}
static void ex_free_list(struct ex_list *list)
{
for (uint32_t i = 0; i < list->nr_items; i++) {
ex_free_value(&list->items[i].value);
}
free(list->items);
free(list);
}
static void ex_free_tree(struct ex_tree *tree)
{
free_string(tree->name);
if (tree->is_leaf) {
free_string(tree->leaf.name);
ex_free_value(&tree->leaf.value);
return;
}
for (uint32_t i = 0; i < tree->nr_children; i++) {
ex_free_tree(&tree->children[i]);
}
free(tree->children);
}
void ex_free(struct ex *ex)
{
for (uint32_t i = 0; i < ex->nr_blocks; i++) {
struct ex_block *block = &ex->blocks[i];
free_string(block->name);
switch (block->val.type) {
case EX_STRING:
free_string(block->val.s);
break;
case EX_TABLE:
ex_free_table(block->val.t);
break;
case EX_LIST:
ex_free_list(block->val.list);
break;
case EX_TREE:
ex_free_tree(block->val.tree);
free(block->val.tree);
break;
default:
break;
}
}
free(ex->blocks);
free(ex);
}
-135
View File
@@ -1,135 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>
#include "system4.h"
#if (defined(_WIN32) || defined(__WIN32__))
#include <Windows.h>
#include <direct.h>
static int make_dir(const char *path, possibly_unused int mode)
{
int nr_wchars = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
wchar_t *wpath = xmalloc(nr_wchars * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, nr_wchars);
int r = _wmkdir(wpath);
free(wpath);
return r;
}
#else
#define make_dir(path, mode) mkdir(path, mode)
#endif
FILE *file_open_utf8(const char *path, const char *mode)
{
#ifdef _WIN32
int nr_wchars = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
wchar_t *wpath = xmalloc(nr_wchars * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, nr_wchars);
wchar_t wmode[64];
mbstowcs(wmode, mode, 64);
FILE *f = _wfopen(wpath, wmode);
free(wpath);
return f;
#else
return fopen(path, mode);
#endif
}
void *file_read(const char *path, size_t *len_out)
{
FILE *fp;
long len;
uint8_t *buf;
if (!(fp = fopen(path, "rb")))
return NULL;
fseek(fp, 0, SEEK_END);
len = ftell(fp);
fseek(fp, 0, SEEK_SET);
buf = xmalloc(len + 1);
if (fread(buf, len, 1, fp) != 1) {
free(buf);
return NULL;
}
if (fclose(fp)) {
free(buf);
return NULL;
}
if (len_out)
*len_out = len;
buf[len] = 0;
return buf;
}
bool file_exists(const char *path)
{
return access(path, F_OK) != -1;
}
// Adapted from http://stackoverflow.com/a/2336245/119527
int mkdir_p(const char *path)
{
const size_t len = strlen(path);
char _path[PATH_MAX];
char *p;
errno = 0;
// Copy string so its mutable
if (len > sizeof(_path)-1) {
errno = ENAMETOOLONG;
return -1;
}
strcpy(_path, path);
// Iterate the string
for (p = _path + 1; *p; p++) {
if (*p == '/') {
// Temporarily truncate
*p = '\0';
if (make_dir(_path, S_IRWXU) != 0) {
if (errno != EEXIST)
return -1;
}
*p = '/';
}
}
if (make_dir(_path, S_IRWXU) != 0) {
if (errno != EEXIST)
return -1;
}
return 0;
}
-330
View File
@@ -1,330 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdlib.h>
#include <string.h>
#include <zlib.h>
#include "little_endian.h"
#include "file.h"
#include "system4.h"
#include "system4/archive.h"
#include "system4/buffer.h"
#include "system4/cg.h"
#include "system4/ajp.h"
enum flat_data_type {
FLAG_CG = 2,
FLAT_ZLIB = 5,
};
struct flat_entry {
size_t off;
size_t size;
int type;
};
struct flat_data {
struct archive_data super;
struct flat_entry *entry;
bool allocated;
};
struct flat_archive {
struct archive ar;
bool allocated;
size_t libl_off;
size_t talt_off;
size_t filesize;
unsigned nr_libl_files;
struct flat_entry *libl_files;
unsigned nr_talt_files;
struct flat_entry *talt_files;
uint8_t *data;
};
static const char *get_file_extension(int type, const char *data)
{
switch (type) {
case FLAG_CG:
if (!strncmp(data, "AJP", 4))
return ".ajp";
if (!strncmp(data, "QNT", 4))
return ".qnt";
return ".img";
case FLAT_ZLIB:
return ".z.dat";
default:
return ".dat";
}
}
static void flat_free_data(struct archive_data *data)
{
struct flat_data *flat = (struct flat_data*)data;
if (flat->allocated)
free(data->data);
free(data->name);
free(data);
}
static void flat_free(struct archive *_ar)
{
struct flat_archive *ar = (struct flat_archive*)_ar;
if (ar->allocated)
free(ar->data);
free(ar->libl_files);
free(ar->talt_files);
free(ar);
}
static struct flat_entry *flat_get_entry(struct flat_archive *ar, unsigned no)
{
if (no < ar->nr_libl_files)
return &ar->libl_files[no];
no -= ar->nr_libl_files;
if (no < ar->nr_talt_files)
return &ar->talt_files[no];
return NULL;
}
static struct archive_data *flat_get(struct archive *_ar, int no)
{
struct flat_archive *ar = (struct flat_archive*)_ar;
struct flat_entry *e = flat_get_entry(ar, no);
struct flat_data *data = xcalloc(1, sizeof(struct flat_data));
data->super.data = ar->data + e->off;
data->super.size = e->size;
data->super.name = xmalloc(256);
snprintf(data->super.name, 256, "%d%s", no, get_file_extension(e->type, (const char*)ar->data+e->off));
data->super.no = no;
data->super.archive = _ar;
data->entry = e;
return &data->super;
}
static bool flat_load_file(possibly_unused struct archive_data *data)
{
struct flat_archive *ar = (struct flat_archive*)data->archive;
struct flat_data *flatdata = (struct flat_data*)data;
struct flat_entry *e = flatdata->entry;
// inflate zlib compressed data
if (e->type == FLAT_ZLIB && ar->data[e->off+4] == 0x78) {
unsigned long size = LittleEndian_getDW(ar->data, e->off);
uint8_t *out = xmalloc(size);
if (uncompress(out, &size, ar->data + e->off + 4, e->size - 4) != Z_OK) {
WARNING("uncompress failed");
free(out);
return false;
}
data->data = out;
data->size = size;
flatdata->allocated = true;
}
return true;
}
static void flat_for_each(struct archive *_ar, void (*iter)(struct archive_data *data, void *user), void *user)
{
struct flat_archive *ar = (struct flat_archive*)_ar;
for (unsigned i = 0; i < ar->nr_libl_files + ar->nr_talt_files; i++) {
struct archive_data *data = flat_get(_ar, i);
if (!data)
continue;
iter(data, user);
flat_free_data(data);
}
}
static struct archive_ops flat_archive_ops = {
.get = flat_get,
.load_file = flat_load_file,
.for_each = flat_for_each,
.free_data = flat_free_data,
.free = flat_free,
};
static bool is_image(const char *data)
{
if (!strncmp(data, "AJP", 4))
return true;
if (!strncmp(data, "QNT", 4))
return true;
return false;
}
static void read_libl(uint8_t *data, size_t size, struct flat_archive *ar)
{
struct buffer r;
buffer_init(&r, data, size);
buffer_skip(&r, 8);
ar->nr_libl_files = buffer_read_int32(&r);
ar->libl_files = xcalloc(ar->nr_libl_files, sizeof(struct flat_entry));
for (unsigned i = 0; i < ar->nr_libl_files; i++) {
int len = buffer_read_int32(&r);
buffer_skip(&r, len);
buffer_align(&r, 4);
ar->libl_files[i].type = buffer_read_int32(&r);
ar->libl_files[i].size = buffer_read_int32(&r);
ar->libl_files[i].off = ar->libl_off + r.index;
if (ar->libl_files[i].type == FLAG_CG && !is_image(buffer_strdata(&r))) {
// XXX: special case: CG usually (not always!) has extra int32
if (is_image(buffer_strdata(&r)+4)) {
ar->libl_files[i].off += 4;
ar->libl_files[i].size -= 4;
buffer_skip(&r, 4);
} else {
WARNING("Couldn't read CG data in LIBL section");
}
}
buffer_skip(&r, ar->libl_files[i].size);
buffer_align(&r, 4);
}
if (r.index != size)
WARNING("Junk at end of LIBL section");
}
static void read_talt(uint8_t *data, size_t size, struct flat_archive *ar)
{
if (size < 16)
return;
struct buffer r;
buffer_init(&r, data, size);
buffer_skip(&r, 8);
ar->nr_talt_files = buffer_read_int32(&r);
ar->talt_files = xcalloc(ar->nr_talt_files, sizeof(struct flat_entry));
for (unsigned i = 0; i < ar->nr_talt_files; i++) {
ar->talt_files[i].size = buffer_read_int32(&r);
ar->talt_files[i].off = ar->talt_off + r.index;
if (strncmp(buffer_strdata(&r), "AJP", 4))
ERROR("NOT AN AJP FILE");
buffer_skip(&r, ar->talt_files[i].size);
buffer_align(&r, 4);
unsigned nr_meta = buffer_read_int32(&r);
for (unsigned j = 0; j < nr_meta; j++) {
unsigned size = buffer_read_int32(&r);
buffer_skip(&r, size);
buffer_align(&r, 4);
buffer_read_int32(&r);
buffer_read_int32(&r);
buffer_read_int32(&r);
buffer_read_int32(&r);
}
}
if (r.index != size)
WARNING("Junk at end of TALT section");
}
struct archive *flat_open(uint8_t *data, size_t size, int *error)
{
struct flat_archive *ar = NULL;
size_t off, libl_off, talt_off;
struct buffer r;
buffer_init(&r, data, size);
if (!strncmp(buffer_strdata(&r), "ELNA", 4)) {
buffer_seek(&r, 8);
}
// FLAT section
if (strncmp(buffer_strdata(&r), "FLAT", 4))
goto exit_err;
buffer_skip(&r, 4);
off = buffer_read_int32(&r);
buffer_skip(&r, off);
if (!strncmp(buffer_strdata(&r), "TMNL", 4)) {
buffer_skip(&r, 4);
off = buffer_read_int32(&r);
buffer_skip(&r, off);
}
// MTLC section
if (strncmp(buffer_strdata(&r), "MTLC", 4))
goto exit_err;
buffer_skip(&r, 4);
off = buffer_read_int32(&r);
buffer_skip(&r, off);
// LIBL section
if (strncmp(buffer_strdata(&r), "LIBL", 4))
goto exit_err;
libl_off = r.index;
buffer_skip(&r, 4);
off = buffer_read_int32(&r);
buffer_skip(&r, off);
// TALT section
talt_off = r.index;
if (r.index < size) {
if (strncmp(buffer_strdata(&r), "TALT", 4))
goto exit_err;
buffer_skip(&r, 4);
off = buffer_read_int32(&r);
}
if (r.index + off < size)
WARNING("Junk at end of FLAT file? %uB/%uB", (unsigned)r.index+off, (unsigned)size);
else if (r.index + off > size)
WARNING("FLAT file truncated? %uB/%uB", (unsigned)size, (unsigned)r.index+off);
ar = xcalloc(1, sizeof(struct flat_archive));
ar->libl_off = libl_off;
ar->talt_off = talt_off;
ar->filesize = size;
ar->data = data;
read_libl(data+libl_off, talt_off - libl_off, ar);
read_talt(data+talt_off, size - talt_off, ar);
ar->ar.ops = &flat_archive_ops;
return &ar->ar;
exit_err:
*error = ARCHIVE_BAD_ARCHIVE_ERROR;
free(ar);
return NULL;
}
struct archive *flat_open_file(const char *path, possibly_unused int flags, int *error)
{
size_t size;
uint8_t *data = file_read(path, &size);
if (!data)
return NULL;
struct flat_archive *ar = (struct flat_archive*)flat_open(data, size, error);
if (!ar) {
free(data);
return NULL;
}
ar->allocated = true;
return (struct archive*)ar;
}
-193
View File
@@ -1,193 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <zlib.h>
#include "system4.h"
#include "system4/buffer.h"
#include "system4/fnl.h"
#include "system4/utfsjis.h"
#include "file.h"
#include "little_endian.h"
static unsigned int sjis_code_to_index(uint16_t code)
{
// one byte
if (code < 0x20)
return 0;
if (code < 0x7f)
return code - 0x20;
if (code < 0xa1)
return 0;
if (code < 0xe0)
return code - 0x42;
// two byte
uint8_t fst = code >> 8;
uint8_t snd = code & 0xFF;
if (fst < 0x81)
return 0;
if (snd < 0x40 || snd == 0x7f || snd > 0xfc)
return 0;
return code - (0x80a2 + (68 * (fst - 0x81)) + (snd > 0x7f ? 1 : 0));
}
struct fnl_glyph *fnl_get_glyph(struct fnl_font_face *font, uint16_t code)
{
unsigned int index = sjis_code_to_index(code);
if (index >= font->nr_glyphs)
index = 0;
if (!font->glyphs[index].data_pos)
index = 0;
return &font->glyphs[index];
}
/*
* Get the closest font size.
*/
struct fnl_font_size *fnl_get_font_size(struct fnl_font *font, float size)
{
float min_diff = 9999;
struct fnl_font_size *closest = &font->sizes[0];
for (unsigned i = 0; i < font->nr_sizes; i++) {
float diff = fabsf(font->sizes[i].size - size);
if (diff < min_diff) {
min_diff = diff;
closest = &font->sizes[i];
}
}
return closest;
}
static void fnl_read_glyph(struct buffer *r, uint32_t height, struct fnl_glyph *dst)
{
dst->height = height;
dst->real_width = buffer_read_u16(r);
dst->data_pos = buffer_read_int32(r);
dst->data_compsize = buffer_read_int32(r);
}
uint8_t *fnl_glyph_data(struct fnl *fnl, struct fnl_glyph *g, unsigned long *size)
{
if (!g->data_pos) {
*size = 0;
return NULL;
}
*size = g->height * g->height * 4; // FIXME: determine real bound
uint8_t *data = xmalloc(*size);
int rv = uncompress(data, size, fnl->data + g->data_pos, g->data_compsize);
if (rv != Z_OK) {
if (rv == Z_BUF_ERROR)
ERROR("uncompress failed: Z_BUF_ERROR");
else if (rv == Z_MEM_ERROR)
ERROR("uncompress failed: Z_MEM_ERROR");
else if (rv == Z_DATA_ERROR)
ERROR("uncompress failed: Z_DATA_ERROR");
}
return data;
}
static void fnl_read_font_face(struct buffer *r, struct fnl_font_face *dst)
{
dst->height = buffer_read_int32(r);
dst->uk = buffer_read_int32(r);
dst->nr_glyphs = buffer_read_int32(r);
dst->glyphs = xcalloc(dst->nr_glyphs, sizeof(struct fnl_glyph));
for (size_t i = 0; i < dst->nr_glyphs; i++) {
fnl_read_glyph(r, dst->height, &dst->glyphs[i]);
}
for (unsigned i = 0; i < 11; i++) {
dst->_sizes[i] = dst->height / (float)(i+2);
}
}
static void fnl_read_font(struct buffer *r, struct fnl_font *dst)
{
dst->nr_faces = buffer_read_int32(r);
dst->faces = xcalloc(dst->nr_faces, sizeof(struct fnl_font_face));
for (size_t i = 0; i < dst->nr_faces; i++) {
dst->faces[i].font = dst;
fnl_read_font_face(r, &dst->faces[i]);
}
dst->nr_sizes = dst->nr_faces * 11;
dst->sizes = xcalloc(dst->nr_sizes, sizeof(struct fnl_font_size));
for (unsigned face = 0, s = 0; face < dst->nr_faces; face++) {
for (unsigned i = 0; i < 11; i++) {
dst->sizes[s++] = (struct fnl_font_size) {
.face = face,
.size = dst->faces[face]._sizes[i],
.denominator = i+2
};
}
}
}
struct fnl *fnl_open(const char *path)
{
size_t filesize;
struct fnl *fnl = xcalloc(1, sizeof(struct fnl));
fnl->data = file_read(path, &filesize);
if (fnl->data[0] != 'F' || fnl->data[1] != 'N' || fnl->data[2] != 'A' || fnl->data[3] != '\0')
goto err;
struct buffer r;
buffer_init(&r, fnl->data, filesize);
buffer_skip(&r, 4);
fnl->uk = buffer_read_int32(&r);
if (fnl->uk != 0)
WARNING("Unexpected value for fnl->uk: %d", fnl->uk);
fnl->filesize = buffer_read_int32(&r);
fnl->data_offset = buffer_read_int32(&r);
fnl->nr_fonts = buffer_read_int32(&r);
fnl->fonts = xcalloc(fnl->nr_fonts, sizeof(struct fnl_font));
for (size_t i = 0; i < fnl->nr_fonts; i++) {
fnl->fonts[i].fnl = fnl;
fnl_read_font(&r, &fnl->fonts[i]);
}
return fnl;
err:
free(fnl->data);
free(fnl);
return NULL;
}
void fnl_free(struct fnl *fnl)
{
for (size_t font = 0; font < fnl->nr_fonts; font++) {
for (size_t face = 0; face < fnl->fonts[font].nr_faces; face++) {
free(fnl->fonts[font].faces[face].glyphs);
}
free(fnl->fonts[font].faces);
}
free(fnl->fonts);
free(fnl->data);
free(fnl);
}
-116
View File
@@ -1,116 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdlib.h>
#include <string.h>
#include "system4.h"
#include "system4/hashtable.h"
struct ht_bucket {
size_t nr_slots;
struct ht_slot slots[];
};
struct hash_table {
size_t nr_buckets;
struct ht_bucket *buckets[];
};
static unsigned long string_hash(const char *_str)
{
const unsigned char *str = (const unsigned char*)_str;
unsigned long hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
struct hash_table *ht_create(size_t nr_buckets)
{
struct hash_table *ht = xmalloc(sizeof(struct hash_table) + sizeof(struct ht_bucket*)*nr_buckets);
ht->nr_buckets = nr_buckets;
for (size_t i = 0; i < nr_buckets; i++) {
ht->buckets[i] = NULL;
}
return ht;
}
void *ht_get(struct hash_table *ht, const char *key, void *dflt)
{
unsigned long k = string_hash(key) & (ht->nr_buckets - 1);
if (!ht->buckets[k])
return dflt;
for (size_t i = 0; i < ht->buckets[k]->nr_slots; i++) {
if (!strcmp(ht->buckets[k]->slots[i].key, key))
return ht->buckets[k]->slots[i].value;
}
return dflt;
}
struct ht_slot *ht_put(struct hash_table *ht, const char *key, void *dflt)
{
unsigned long k = string_hash(key) & (ht->nr_buckets - 1);
// init new bucket
if (!ht->buckets[k]) {
ht->buckets[k] = xmalloc(sizeof(struct ht_bucket) + sizeof(struct ht_slot));
ht->buckets[k]->nr_slots = 1;
ht->buckets[k]->slots[0].key = strdup(key);
ht->buckets[k]->slots[0].value = dflt;
return &ht->buckets[k]->slots[0];
}
// search for key in bucket
for (size_t i = 0; i < ht->buckets[k]->nr_slots; i++) {
if (!strcmp(ht->buckets[k]->slots[i].key, key))
return &ht->buckets[k]->slots[i];
}
// alloc new slot
size_t i = ht->buckets[k]->nr_slots;
ht->buckets[k] = xrealloc(ht->buckets[k], sizeof(struct ht_bucket) + sizeof(struct ht_slot)*(i+1));
ht->buckets[k]->nr_slots = i+1;
ht->buckets[k]->slots[i].key = strdup(key);
ht->buckets[k]->slots[i].value = dflt;
return &ht->buckets[k]->slots[i];
}
void ht_foreach_value(struct hash_table *ht, void(*fun)(void*))
{
for (size_t i = 0; i < ht->nr_buckets; i++) {
if (!ht->buckets[i])
continue;
for (size_t j = 0; j < ht->buckets[i]->nr_slots; j++) {
fun(ht->buckets[i]->slots[j].value);
}
}
}
void ht_free(struct hash_table *ht)
{
for (size_t i = 0; i < ht->nr_buckets; i++) {
if (!ht->buckets[i])
continue;
for (size_t j = 0; j < ht->buckets[i]->nr_slots; j++) {
free(ht->buckets[i]->slots[j].key);
}
free(ht->buckets[i]);
}
free(ht);
}
-145
View File
@@ -1,145 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "system4.h"
#include "system4/ini.h"
#include "system4/string.h"
#include "ini_parser.tab.h"
extern FILE *yini_in;
extern unsigned long yini_line;
static void list_assign(struct ini_value *list, size_t i, struct ini_value *item)
{
// grow list if needed
if (i >= list->list_size) {
list->list = xrealloc(list->list, (i+1) * sizeof(struct ini_value));
for (size_t j = list->list_size; j < i; j++) {
list->list[j].type = INI_NULL;
}
list->list_size = i+1;
}
list->list[i] = *item;
}
struct ini_entry *ini_parse(const char *path, int *nr_entries)
{
struct ini_entry _e;
if (!(yini_in = fopen(path, "rb"))) {
*nr_entries = INI_FILE_ERROR;
return NULL;
}
yini_line = 1;
yini_parse();
kvec_t(struct ini_entry) entries;
kv_init(entries);
for (size_t i = 0; i < kv_size(*yini_entries); i++) {
struct ini_entry *e = kv_A(*yini_entries, i);
if (e->value.type != _INI_LIST_ENTRY) {
kv_push(struct ini_entry, entries, *e);
free(e);
continue;
}
// try to find existing list to put entry into
struct ini_entry *list = NULL;
for (size_t i = 0; i < kv_size(entries); i++) {
struct ini_entry *other = &kv_A(entries, i);
if (strcmp(e->name->text, other->name->text))
continue;
if (other->value.type != INI_LIST) {
WARNING("ignoring list assignment to non-list: %s[%" SIZE_T_FMT "]", e->name, e->value._list_pos);
goto end_list_assign;
}
free_string(e->name);
list = other;
break;
}
// create new list if not found
if (!list) {
_e = (struct ini_entry) {
.name = e->name,
.value = {
.type = INI_LIST,
.list_size = 0,
.list = NULL
}
};
list = kv_pushp(struct ini_entry, entries);
*list = _e;
}
list_assign(&list->value, e->value._list_pos, e->value._list_value);
end_list_assign:
free(e->value._list_value);
free(e);
}
kv_destroy(*yini_entries);
free(yini_entries);
*nr_entries = kv_size(entries);
return kv_data(entries);
}
struct ini_entry *ini_make_entry(struct string *name, struct ini_value value)
{
struct ini_entry *entry = xmalloc(sizeof(struct ini_entry));
entry->name = name;
entry->value = value;
return entry;
}
static void ini_free_value(struct ini_value *value)
{
switch (value->type) {
case INI_STRING:
free_string(value->s);
break;
case INI_LIST:
for (size_t i = 0; i < value->list_size; i++) {
ini_free_value(&value->list[i]);
}
free(value->list);
break;
case _INI_LIST_ENTRY:
ini_free_value(value->_list_value);
free(value->_list_value);
break;
case INI_FORMATION:
for (size_t i = 0; i < value->nr_entries; i++) {
ini_free_entry(&value->entries[i]);
}
free(value->entries);
break;
default: break;
}
}
void ini_free_entry(struct ini_entry *entry)
{
free_string(entry->name);
ini_free_value(&entry->value);
}
-76
View File
@@ -1,76 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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/>.
*/
%{
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdio.h>
#include <stdlib.h>
#include "ini_parser.tab.h"
#include "system4.h"
#include "system4/string.h"
char string_buf[65536];
char *string_buf_ptr;
unsigned long yini_line = 1;
%}
%option noyywrap
%option prefix="yini_"
%x str
%%
\/\/[^\n]*\n yini_line++;
[ \t\r] ;
\n yini_line++;
;[^\n]*\n yini_line++;
\{ return '{';
\} return '}';
\[ return '[';
\] return ']';
= return '=';
, return ',';
true return TRUE;
false return FALSE;
Formation return FORMATION;
[0-9]+ yini_lval.i = atoi(yini_text); return INTEGER;
[a-zA-Z]+[a-zA-Z0-9_-]* yini_lval.s = make_string(yini_text, yini_leng); return IDENTIFIER;
\" string_buf_ptr = string_buf; BEGIN(str);
<str>{
\" {
BEGIN(INITIAL);
*string_buf_ptr = '\0';
yini_lval.s = make_string(string_buf, string_buf_ptr - string_buf);
return STRING;
}
\n ERROR("Unterminated string literal at line %lu", yini_line);
[^\n\"]+ {
char *yptr = yini_text;
while (*yptr)
*string_buf_ptr++ = *yptr++;
}
}
%%
-136
View File
@@ -1,136 +0,0 @@
%define api.prefix {yini_}
%union {
int token;
int i;
struct string *s;
struct ini_entry *entry;
struct ini_value value;
entry_list *entries;
value_list *list;
}
%code requires {
#include "kvec.h"
#include "system4/ini.h"
kv_decl(entry_list, struct ini_entry*);
kv_decl(value_list, struct ini_value);
entry_list *yini_entries;
}
%{
#include <stdio.h>
#include "system4.h"
#include "system4/string.h"
extern unsigned long yini_line;
extern int yini_lex();
void yini_error(const char *s) { ERROR("at line %lu: %s", yini_line, s); }
static entry_list *make_ini(void)
{
entry_list *ini = xmalloc(sizeof(entry_list));
kv_init(*ini);
return ini;
}
static void push_entry(entry_list *ini, struct ini_entry *entry)
{
kv_push(struct ini_entry*, *ini, entry);
}
static struct ini_value make_list(value_list *values)
{
if (!values)
return ini_make_list(NULL, 0);
struct ini_value *vals = kv_data(*values);
size_t nr_vals = kv_size(*values);
free(values);
return ini_make_list(vals, nr_vals);
}
static value_list *make_value_list(void)
{
value_list *values = xmalloc(sizeof(value_list));
kv_init(*values);
return values;
}
static void push_value(value_list *values, struct ini_value value)
{
kv_push(struct ini_value, *values, value);
}
static struct ini_entry *make_list_assign(struct string *name, int i, struct ini_value value)
{
struct ini_value *val = xmalloc(sizeof(struct ini_value));
*val = value;
struct ini_value wrapper = {
.type = _INI_LIST_ENTRY,
._list_pos = i,
._list_value = val
};
return ini_make_entry(name, wrapper);
}
static struct ini_entry *make_formation(struct string *name, entry_list *entries)
{
struct ini_value wrapper = {
.type = INI_FORMATION,
.nr_entries = kv_size(*entries),
.entries = xcalloc(kv_size(*entries), sizeof(struct ini_entry)),
};
for (size_t i = 0; i < wrapper.nr_entries; i++) {
wrapper.entries[i] = *kv_A(*entries, i);
free(kv_A(*entries, i));
}
kv_destroy(*entries);
free(entries);
return ini_make_entry(name, wrapper);
}
%}
%token <token> TRUE FALSE FORMATION
%token <i> INTEGER
%token <s> STRING IDENTIFIER
%type <entries> inifile
%type <entries> entries
%type <entry> entry
%type <value> value
%type <list> list
%start inifile
%%
inifile : entries { yini_entries = $1; }
;
entries : entry { $$ = make_ini(); push_entry($$, $1); }
| entries entry { push_entry($1, $2); }
;
entry : IDENTIFIER '=' value { $$ = ini_make_entry($1, $3); }
| IDENTIFIER '[' INTEGER ']' '=' value { $$ = make_list_assign($1, $3, $6); }
| FORMATION STRING '{' entries '}' { $$ = make_formation($2, $4); }
;
value : INTEGER { $$ = ini_make_integer($1); }
| TRUE { $$ = ini_make_boolean(true); }
| FALSE { $$ = ini_make_boolean(false); }
| STRING { $$ = ini_make_string($1); }
| '{' '}' { $$ = make_list(NULL); }
| '{' list '}' { $$ = make_list($2); }
| '{' list ',' '}' { $$ = make_list($2); }
;
list : value { $$ = make_value_list(); push_value($$, $1); }
| list ',' value { push_value($1, $3); }
;
%%
-403
View File
@@ -1,403 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "system4.h"
#include "system4/instructions.h"
#define SYS(syscode, sysname, rt, nargs, ...) \
[syscode] = { \
.code = syscode, \
.name = "system." #sysname , \
.implemented = true, \
.return_type = rt, \
.nr_args = nargs, \
.argtypes = { __VA_ARGS__ } \
}
#define TODO(syscode, sysname, rt, nargs, ...) \
[syscode] = { \
.code = syscode, \
.name = "system." #sysname , \
.implemented = false, \
.return_type = rt, \
.nr_args = nargs, \
.argtypes = { __VA_ARGS__ } \
}
const struct syscall syscalls[NR_SYSCALLS] = {
SYS ( SYS_EXIT, Exit, AIN_VOID_TYPE, 1, AIN_INT ),
SYS ( SYS_GLOBAL_SAVE, GlobalSave, AIN_INT_TYPE, 2, AIN_STRING, AIN_STRING ),
SYS ( SYS_GLOBAL_LOAD, GlobalLoad, AIN_INT_TYPE, 2, AIN_STRING, AIN_STRING ),
SYS ( SYS_LOCK_PEEK, LockPeek, AIN_INT_TYPE, 0 ),
SYS ( SYS_UNLOCK_PEEK, UnlockPeek, AIN_INT_TYPE, 0 ),
SYS ( SYS_RESET, Reset, AIN_VOID_TYPE, 0 ),
SYS ( SYS_OUTPUT, Output, AIN_STRING_TYPE, 1, AIN_STRING ),
SYS ( SYS_MSGBOX, MsgBox, AIN_STRING_TYPE, 1, AIN_STRING ),
SYS ( SYS_RESUME_SAVE, ResumeSave, AIN_INT_TYPE, 3, AIN_STRING, AIN_STRING, AIN_REF_INT ),
SYS ( SYS_RESUME_LOAD, ResumeLoad, AIN_VOID_TYPE, 2, AIN_STRING, AIN_STRING ),
SYS ( SYS_EXISTS_FILE, ExistsFile, AIN_INT_TYPE, 1, AIN_STRING ),
SYS ( SYS_OPEN_WEB, OpenWeb, AIN_VOID_TYPE, 1, AIN_STRING ),
SYS ( SYS_GET_SAVE_FOLDER_NAME, GetSaveFolderName, AIN_STRING_TYPE, 0 ),
SYS ( SYS_GET_TIME, GetTime, AIN_INT_TYPE, 0 ),
TODO ( SYS_GET_GAME_NAME, GetGameName, AIN_STRING_TYPE, 0 ),
SYS ( SYS_ERROR, Error, AIN_STRING_TYPE, 1, AIN_STRING ),
SYS ( SYS_EXISTS_SAVE_FILE, ExistsSaveFile, AIN_INT_TYPE, 1, AIN_STRING ),
SYS ( SYS_IS_DEBUG_MODE, IsDebugMode, AIN_INT_TYPE, 0 ),
SYS ( SYS_MSGBOX_OK_CANCEL, MsgBoxOkCancel, AIN_INT_TYPE, 1, AIN_STRING ),
SYS ( SYS_GET_FUNC_STACK_NAME, GetFuncStackName, AIN_STRING_TYPE, 1, AIN_INT ),
SYS ( SYS_PEEK, Peek, AIN_VOID_TYPE, 0 ),
SYS ( SYS_SLEEP, Sleep, AIN_VOID_TYPE, 1, AIN_INT ),
SYS ( SYS_GROUP_SAVE, GroupSave, AIN_INT_TYPE, 4, AIN_STRING, AIN_STRING, AIN_STRING, AIN_REF_INT ),
SYS ( SYS_GROUP_LOAD, GroupLoad, AIN_INT_TYPE, 4, AIN_STRING, AIN_STRING, AIN_STRING, AIN_REF_INT ),
TODO ( SYS_RESUME_WRITE_COMMENT, ResumeWriteComment, AIN_BOOL_TYPE, 3, AIN_STRING, AIN_STRING, AIN_REF_ARRAY_STRING ),
TODO ( SYS_RESUME_READ_COMMENT, ResumeReadComment, AIN_BOOL_TYPE, 3, AIN_STRING, AIN_STRING, AIN_REF_ARRAY_STRING ),
TODO ( SYS_DELETE_SAVE_FILE, DeleteSaveFile, AIN_INT_TYPE, 1, AIN_STRING ),
TODO ( SYS_EXIST_FUNC, ExistFunc, AIN_BOOL_TYPE, 1, AIN_STRING ),
TODO ( SYS_COPY_SAVE_FILE, CopySaveFile, AIN_INT_TYPE, 2, AIN_STRING, AIN_STRING ),
};
// JMP = instruction that modifies the instruction pointer
// OP = everything else
#define _OP(code, opname, nargs, ...) \
[code] = { \
.opcode = code, \
.name = opname, \
.nr_args = nargs, \
.ip_inc = 2 + nargs * 4, \
.implemented = true, \
.args = { __VA_ARGS__ } \
}
#define OP(code, nargs, ...) \
[code] = { \
.opcode = code, \
.name = #code , \
.nr_args = nargs, \
.ip_inc = 2 + nargs * 4, \
.implemented = true, \
.args = { __VA_ARGS__ } \
}
#define JMP(code, nargs, ...) \
[code] = { \
.opcode = code, \
.name = #code , \
.nr_args = nargs, \
.ip_inc = 0, \
.implemented = true, \
.args = { __VA_ARGS__ } \
}
#undef TODO
#define TODO(code, nargs, ...) \
[code] = { \
.opcode = code, \
.name = #code, \
.nr_args = nargs, \
.ip_inc = 2 + nargs * 4, \
.implemented = false, \
.args = { __VA_ARGS__ } \
}
struct instruction instructions[NR_OPCODES] = {
// TYPE OPCODE NR_ARGS, ARG_TYPES...
OP ( PUSH, 1, T_INT ),
OP ( POP, 0 ),
OP ( REF, 0 ),
OP ( REFREF, 0 ),
OP ( PUSHGLOBALPAGE, 0 ),
OP ( PUSHLOCALPAGE, 0 ),
OP ( INV, 0 ),
OP ( NOT, 0 ),
OP ( COMPL, 0 ),
OP ( ADD, 0 ),
OP ( SUB, 0 ),
OP ( MUL, 0 ),
OP ( DIV, 0 ),
OP ( MOD, 0 ),
OP ( AND, 0 ),
OP ( OR, 0 ),
OP ( XOR, 0 ),
OP ( LSHIFT, 0 ),
OP ( RSHIFT, 0 ),
OP ( LT, 0 ),
OP ( GT, 0 ),
OP ( LTE, 0 ),
OP ( GTE, 0 ),
OP ( NOTE, 0 ),
OP ( EQUALE, 0 ),
OP ( ASSIGN, 0 ),
OP ( PLUSA, 0 ),
OP ( MINUSA, 0 ),
OP ( MULA, 0 ),
OP ( DIVA, 0 ),
OP ( MODA, 0 ),
OP ( ANDA, 0 ),
OP ( ORA, 0 ),
OP ( XORA, 0 ),
OP ( LSHIFTA, 0 ),
OP ( RSHIFTA, 0 ),
OP ( F_ASSIGN, 0 ),
OP ( F_PLUSA, 0 ),
OP ( F_MINUSA, 0 ),
OP ( F_MULA, 0 ),
OP ( F_DIVA, 0 ),
OP ( DUP2, 0 ),
OP ( DUP_X2, 0 ),
TODO ( CMP, 0 ),
JMP ( JUMP, 1, T_ADDR ),
JMP ( IFZ, 1, T_ADDR ),
JMP ( IFNZ, 1, T_ADDR ),
JMP ( RETURN, 0 ),
JMP ( CALLFUNC, 1, T_FUNC ),
OP ( INC, 0 ),
OP ( DEC, 0 ),
OP ( FTOI, 0 ),
OP ( ITOF, 0 ),
OP ( F_INV, 0 ),
OP ( F_ADD, 0 ),
OP ( F_SUB, 0 ),
OP ( F_MUL, 0 ),
OP ( F_DIV, 0 ),
OP ( F_LT, 0 ),
OP ( F_GT, 0 ),
OP ( F_LTE, 0 ),
OP ( F_GTE, 0 ),
OP ( F_NOTE, 0 ),
OP ( F_EQUALE, 0 ),
OP ( F_PUSH, 1, T_FLOAT ),
OP ( S_PUSH, 1, T_STRING ),
OP ( S_POP, 0 ),
OP ( S_ADD, 0 ),
OP ( S_ASSIGN, 0 ),
TODO ( S_PLUSA, 0 ),
OP ( S_REF, 0 ),
TODO ( S_REFREF, 0 ),
OP ( S_NOTE, 0 ),
OP ( S_EQUALE, 0 ),
TODO ( SF_CREATE, 0 ),
TODO ( SF_CREATEPIXEL, 0 ),
TODO ( SF_CREATEALPHA, 0 ),
OP ( SR_POP, 0 ),
OP ( SR_ASSIGN, 0 ),
OP ( SR_REF, 1, T_STRUCT ),
TODO ( SR_REFREF, 0 ),
OP ( A_ALLOC, 0 ),
OP ( A_REALLOC, 0 ),
OP ( A_FREE, 0 ),
OP ( A_NUMOF, 0 ),
OP ( A_COPY, 0 ),
OP ( A_FILL, 0 ),
OP ( C_REF, 0 ),
OP ( C_ASSIGN, 0 ),
JMP ( MSG, 1, T_MSG ),
OP ( CALLHLL, 2, T_HLL, T_HLLFUNC, T_INT ), // XXX: changed in ain version > 8
OP ( PUSHSTRUCTPAGE, 0 ),
JMP ( CALLMETHOD, 1, T_FUNC ),
OP ( SH_GLOBALREF, 1, T_GLOBAL ),
OP ( SH_LOCALREF, 1, T_LOCAL ),
JMP ( SWITCH, 1, T_SWITCH ),
JMP ( STRSWITCH, 1, T_SWITCH ),
OP ( FUNC, 1, T_FUNC ),
_OP ( _EOF, "EOF", 1, T_FILE ),
OP ( CALLSYS, 1, T_SYSCALL ),
JMP ( SJUMP, 0 ),
OP ( CALLONJUMP, 0 ),
OP ( SWAP, 0 ),
OP ( SH_STRUCTREF, 1, T_MEMB ),
OP ( S_LENGTH, 0 ),
OP ( S_LENGTHBYTE, 0 ),
OP ( I_STRING, 0 ),
JMP ( CALLFUNC2, 0 ),
OP ( DUP2_X1, 0 ),
OP ( R_ASSIGN, 0 ),
OP ( FT_ASSIGNS, 0 ),
OP ( ASSERT, 0 ),
OP ( S_LT, 0 ),
OP ( S_GT, 0 ),
OP ( S_LTE, 0 ),
OP ( S_GTE, 0 ),
OP ( S_LENGTH2, 0 ),
TODO ( S_LENGTHBYTE2, 0 ),
OP ( NEW, 0, T_STRUCT, T_INT ), // FIXME: 2nd arg is T_FUNC *OR* -1
OP ( DELETE, 0 ),
TODO ( CHECKUDO, 0 ),
OP ( A_REF, 0 ),
OP ( DUP, 0 ),
OP ( DUP_U2, 0 ),
OP ( SP_INC, 0 ),
TODO ( SP_DEC, 0 ),
OP ( ENDFUNC, 1, T_FUNC ),
TODO ( R_EQUALE, 0 ),
TODO ( R_NOTE, 0 ),
OP ( SH_LOCALCREATE, 2, T_LOCAL, T_STRUCT ),
OP ( SH_LOCALDELETE, 1, T_LOCAL ),
OP ( STOI, 0 ),
OP ( A_PUSHBACK, 0 ),
OP ( A_POPBACK, 0 ),
OP ( S_EMPTY, 0 ),
OP ( A_EMPTY, 0 ),
OP ( A_ERASE, 0 ),
OP ( A_INSERT, 0 ),
OP ( SH_LOCALINC, 1, T_LOCAL ),
OP ( SH_LOCALDEC, 1, T_LOCAL ),
OP ( SH_LOCALASSIGN, 2, T_LOCAL, T_INT ),
OP ( ITOB, 0 ),
OP ( S_FIND, 0 ),
OP ( S_GETPART, 0 ),
OP ( A_SORT, 0 ),
TODO ( S_PUSHBACK, 0 ),
TODO ( S_POPBACK, 0 ),
OP ( FTOS, 0 ),
OP ( S_MOD, 0, T_INT ), // XXX: changed in ain version > 8
OP ( S_PLUSA2, 0 ),
OP ( OBJSWAP, 0, T_INT ), // XXX: changed in ain version > 8
TODO ( S_ERASE, 0 ),
TODO ( SR_REF2, 1, T_INT ),
OP ( S_ERASE2, 0 ),
OP ( S_PUSHBACK2, 0 ),
OP ( S_POPBACK2, 0 ),
OP ( ITOLI, 0 ),
OP ( LI_ADD, 0 ),
OP ( LI_SUB, 0 ),
OP ( LI_MUL, 0 ),
OP ( LI_DIV, 0 ),
OP ( LI_MOD, 0 ),
OP ( LI_ASSIGN, 0 ),
OP ( LI_PLUSA, 0 ),
OP ( LI_MINUSA, 0 ),
OP ( LI_MULA, 0 ),
OP ( LI_DIVA, 0 ),
OP ( LI_MODA, 0 ),
OP ( LI_ANDA, 0 ),
OP ( LI_ORA, 0 ),
OP ( LI_XORA, 0 ),
OP ( LI_LSHIFTA, 0 ),
OP ( LI_RSHIFTA, 0 ),
OP ( LI_INC, 0 ),
OP ( LI_DEC, 0 ),
OP ( A_FIND, 0 ),
OP ( A_REVERSE, 0 ),
TODO ( SH_SR_ASSIGN, 0 ),
TODO ( SH_MEM_ASSIGN_LOCAL, 2, T_MEMB, T_LOCAL ),
TODO ( A_NUMOF_GLOB_1, 1, T_GLOBAL ),
TODO ( A_NUMOF_STRUCT_1, 1, T_MEMB ),
TODO ( SH_MEM_ASSIGN_IMM, 2, T_MEMB, T_INT ),
TODO ( SH_LOCALREFREF, 1, T_LOCAL ),
TODO ( SH_LOCALASSIGN_SUB_IMM, 2, T_LOCAL, T_INT ),
TODO ( SH_IF_LOC_LT_IMM, 3, T_LOCAL, T_INT, T_ADDR ),
TODO ( SH_IF_LOC_GE_IMM, 3, T_LOCAL, T_INT, T_ADDR ),
TODO ( SH_LOCREF_ASSIGN_MEM, 2, T_LOCAL, T_MEMB ),
TODO ( PAGE_REF, 1, T_INT ),
TODO ( SH_GLOBAL_ASSIGN_LOCAL, 2, T_GLOBAL, T_LOCAL ),
TODO ( SH_STRUCTREF_GT_IMM, 2, T_MEMB, T_INT ),
TODO ( SH_STRUCT_ASSIGN_LOCALREF_ITOB, 2, T_MEMB, T_LOCAL ),
TODO ( SH_LOCAL_ASSIGN_STRUCTREF, 2, T_LOCAL, T_MEMB ),
TODO ( SH_IF_STRUCTREF_NE_LOCALREF, 3, T_MEMB, T_LOCAL, T_ADDR ),
TODO ( SH_IF_STRUCTREF_GT_IMM, 3, T_MEMB, T_INT, T_ADDR ),
TODO ( SH_STRUCTREF_CALLMETHOD_NO_PARAM, 2, T_MEMB, T_FUNC ),
TODO ( SH_STRUCTREF2, 2, T_MEMB, T_MEMB2 ),
TODO ( SH_REF_STRUCTREF2, 2, T_MEMB, T_MEMB2 ),
TODO ( SH_STRUCTREF3, 3, T_MEMB, T_MEMB2, T_MEMB3 ),
TODO ( SH_STRUCTREF2_CALLMETHOD_NO_PARAM, 3, T_MEMB, T_MEMB2, T_FUNC ),
TODO ( SH_IF_STRUCTREF_Z, 2, T_MEMB, T_ADDR ),
TODO ( SH_IF_STRUCT_A_NOT_EMPTY, 2, T_MEMB, T_ADDR ),
TODO ( SH_IF_LOC_GT_IMM, 3, T_LOCAL, T_INT, T_ADDR ),
TODO ( SH_IF_STRUCTREF_NE_IMM, 3, T_MEMB, T_INT, T_ADDR ),
TODO ( THISCALLMETHOD_NOPARAM, 1, T_FUNC ),
TODO ( SH_IF_LOC_NE_IMM, 3, T_LOCAL, T_INT, T_ADDR ),
TODO ( SH_IF_STRUCTREF_EQ_IMM, 3, T_MEMB, T_INT, T_ADDR ),
TODO ( SH_GLOBAL_ASSIGN_IMM, 2, T_GLOBAL, T_INT ),
TODO ( SH_LOCALSTRUCT_ASSIGN_IMM, 3, T_LOCAL, T_LOCMEMB, T_INT ),
TODO ( SH_STRUCT_A_PUSHBACK_LOCAL_STRUCT, 2, T_MEMB, T_LOCAL ),
TODO ( SH_GLOBAL_A_PUSHBACK_LOCAL_STRUCT, 2, T_GLOBAL, T_LOCAL ),
TODO ( SH_LOCAL_A_PUSHBACK_LOCAL_STRUCT, 2, T_LOCAL, T_LOCAL ),
TODO ( SH_IF_SREF_NE_STR0, 2, T_STRING, T_ADDR ),
TODO ( SH_S_ASSIGN_REF, 0 ),
TODO ( SH_A_FIND_SREF, 0 ),
TODO ( SH_SREF_EMPTY, 0 ),
TODO ( SH_STRUCTSREF_EQ_LOCALSREF, 2 , T_MEMB, T_LOCAL ),
TODO ( SH_LOCALSREF_EQ_STR0, 2, T_LOCAL, T_STRING ),
TODO ( SH_STRUCTSREF_NE_LOCALSREF, 2, T_MEMB, T_LOCAL ),
TODO ( SH_LOCALSREF_NE_STR0, 2, T_LOCAL, T_STRING ),
TODO ( SH_STRUCT_SR_REF, 2, T_MEMB, T_STRUCT ),
TODO ( SH_STRUCT_S_REF, 1, T_MEMB ),
TODO ( S_REF2, 1, T_MEMB ),
TODO ( SH_REF_LOCAL_ASSIGN_STRUCTREF2, 3, T_INT, T_INT, T_INT ), // FIXME: arguments types unknown
TODO ( SH_GLOBAL_S_REF, 1, T_GLOBAL ),
TODO ( SH_LOCAL_S_REF, 1, T_LOCAL ),
TODO ( SH_LOCALREF_SASSIGN_LOCALSREF, 2, T_LOCAL, T_LOCAL ),
TODO ( SH_LOCAL_APUSHBACK_LOCALSREF, 2, T_LOCAL, T_LOCAL ),
TODO ( SH_S_ASSIGN_CALLSYS19, 0 ),
TODO ( SH_S_ASSIGN_STR0, 1, T_STRING ),
TODO ( SH_SASSIGN_LOCALSREF, 1, T_LOCAL ),
TODO ( SH_STRUCTREF_SASSIGN_LOCALSREF, 2, T_MEMB, T_LOCAL ),
TODO ( SH_LOCALSREF_EMPTY, 1, T_LOCAL ),
TODO ( SH_GLOBAL_APUSHBACK_LOCALSREF, 2, T_GLOBAL, T_LOCAL ),
TODO ( SH_STRUCT_APUSHBACK_LOCALSREF, 2, T_MEMB, T_LOCAL ),
TODO ( SH_STRUCTSREF_EMPTY, 1, T_MEMB ),
TODO ( SH_GLOBALSREF_EMPTY, 1, T_GLOBAL ),
TODO ( SH_SASSIGN_STRUCTSREF, 1, T_MEMB ),
TODO ( SH_SASSIGN_GLOBALSREF, 1, T_GLOBAL ),
TODO ( SH_STRUCTSREF_NE_STR0, 2, T_MEMB, T_STRING ),
TODO ( SH_GLOBALSREF_NE_STR0, 2, T_GLOBAL, T_STRING ),
TODO ( SH_LOC_LT_IMM_OR_LOC_GE_IMM, 3, T_LOCAL, T_INT, T_INT ),
TODO ( A_SORT_MEM, 0 ),
TODO ( DG_ADD, 0 ),
TODO ( DG_SET, 0 ),
TODO ( DG_CALL, 2, T_DLG, T_ADDR ),
TODO ( DG_NUMOF, 0 ),
TODO ( DG_EXIST, 0 ),
TODO ( DG_ERASE, 0 ),
TODO ( DG_CLEAR, 0 ),
TODO ( DG_COPY, 0 ),
TODO ( DG_ASSIGN, 0 ),
TODO ( DG_PLUSA, 0 ),
TODO ( DG_POP, 0 ),
TODO ( DG_NEW_FROM_METHOD, 0 ),
TODO ( DG_MINUSA, 0 ),
TODO ( DG_CALLBEGIN, 1, T_DLG ),
TODO ( DG_NEW, 0 ),
TODO ( DG_STR_TO_METHOD, 0, T_DLG ), // XXX: changed in ain version > 8
TODO ( OP_0X102, 0 ),
TODO ( OP_0X103, 0 ),
TODO ( OP_0X104, 0 ),
TODO ( OP_0X105, 1, T_STRUCT ),
TODO ( OP_0X106, 1, T_INT ),
TODO ( OP_0X107, 0 ),
TODO ( OP_0X108, 0 ),
TODO ( OP_0X109, 0 ),
TODO ( OP_0X10A, 1, T_INT ),
TODO ( OP_0X10B, 2, T_INT, T_INT ),
TODO ( OP_0X10C, 1, T_INT ),
TODO ( OP_0X10D, 1, T_INT ),
TODO ( OP_0X10E, 1, T_INT ),
TODO ( OP_0X10F, 0 ),
TODO ( OP_0X110, 1, T_INT ),
};
void initialize_instructions(void)
{
// this is a dumb hack to force linking/initialization of the above
// arrays on Windows.
}
+2 -35
View File
@@ -1,28 +1,3 @@
# sources for libsys4.a
system4 = ['acx.c',
'afa.c',
'ain.c',
'ajp.c',
'ald.c',
'archive.c',
'buffer.c',
'cg.c',
'dcf.c',
'ex.c',
'file.c',
'flat.c',
'fnl.c',
'hashtable.c',
'ini.c',
'instructions.c',
'pms.c',
'qnt.c',
'string.c',
'system.c',
'utfsjis.c',
'webp.c',
]
# sources for xsystem4
xsystem4 = ['audio.c',
'cJSON.c',
@@ -62,7 +37,7 @@ xsystem4 = ['audio.c',
'hll/SystemServiceEx.c',
]
xsystem4_deps = [libm, zlib, sdl2, sdl2ttf, sdl2mixer, ffi, gl, glew]
xsystem4_deps = [libm, zlib, sdl2, sdl2ttf, sdl2mixer, ffi, gl, glew, libsys4_dep]
if chibi.found()
xsystem4_deps += chibi
xsystem4 += 'debugger.c'
@@ -77,17 +52,9 @@ bisongen = generator(bison,
output: ['@BASENAME@.tab.c', '@BASENAME@.tab.h'],
arguments : ['--verbose', '@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'])
system4 += flexgen.process('ini_lexer.l')
system4 += bisongen.process('ini_parser.y')
libsys4 = static_library('sys4', system4,
dependencies : [libm, zlib, tj, webp, png],
include_directories : incdir)
executable('xsystem4', xsystem4,
dependencies : xsystem4_deps,
c_args : ['-Wno-unused-parameter'],
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
subdir('tools')
-327
View File
@@ -1,327 +0,0 @@
/* 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 <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "little_endian.h"
#include "system4.h"
#include "system4/cg.h"
#include "system4/pms.h"
struct pms_header {
int version; // PMS data version
int header_size; // size of header
int bpp; // PMS data depth, 8 or 16
int bpps; // shadow data depth, if exists
int sf; // sprite flag (not used?)
int bf; // palette bank
int x; // display location x
int y; // display location y
int width; // image width
int height; // image height
int dp; // data pointer
int pp; // pointer to palette or alpha
int cp; // pointer to comment
};
bool pms_checkfmt(const uint8_t *data)
{
int x, y, w, h;
if (data[0] != 'P' || data[1] != 'M')
return false;
x = LittleEndian_getDW(data, 16);
y = LittleEndian_getDW(data, 20);
w = LittleEndian_getDW(data, 24);
h = LittleEndian_getDW(data, 28);
if (x < 0 || y < 0 || w < 0 || h < 0)
return false;
return true;
}
bool pms8_checkfmt(const uint8_t *data)
{
return pms_checkfmt(data) && data[6] == 8;
}
bool pms16_checkfmt(const uint8_t *data)
{
return pms_checkfmt(data) && data[7] == 16;
}
static struct cg_palette *pms_get_palette(const uint8_t *b)
{
struct cg_palette *pal = xmalloc(sizeof(struct cg_palette));
for (int i = 0; i < 256; i++) {
pal->red[i] = *b++;
pal->green[i] = *b++;
pal->blue[i] = *b++;
}
return pal;
}
/* Convert PMS8 image data to bitmap. */
static uint8_t *pms8_extract(struct pms_header *pms, const uint8_t *b)
{
int n, c0, c1;
const int scanline = pms->width;
uint8_t *pic = xmalloc((pms->width+10)*(pms->height+10));
// for each line...
for (int y = 0; y < pms->height; y ++) {
// for each pixel...
for (int x = 0; x < pms->width; ) {
int loc = y * scanline + x;
c0 = *b++;
// non-command byte: read 1 pixel into buffer
if (c0 <= 0xf7) {
pic[loc] = c0;
x++;
}
// copy n+3 pixels from previous line
else if (c0 == 0xff) {
n = (*b++) + 3;
x += n;
memcpy(pic + loc, pic + loc - scanline, n);
}
// copy n+3 pixels from 2 lines previous
else if (c0 == 0xfe) {
n = (*b++) + 3;
x += n;
memcpy(pic + loc, pic + loc - scanline * 2, n);
}
// repeat 1 pixel n+4 times (1-byte RLE)
else if (c0 == 0xfd) {
n = (*b++) + 4;
x += n;
c0 = *b++;
memset(pic + loc, c0, n);
}
// repeast a sequence of 2 pixels n+3 times (2-byte RLE)
else if (c0 == 0xfc) {
n = ((*b++) + 3) * 2;
x += n;
c0 = *b++;
c1 = *b++;
for (int i = 0; i < n; i+=2) {
pic[loc+i] = c0;
pic[loc+i+1] = c1;
}
}
// not sure why this exists, probably padding
else {
pic[loc] = *b++;
x++;
}
}
}
return pic;
}
/* Convert PMS16 image data to bitmap. */
static uint16_t *pms16_extract(struct pms_header *pms, const uint8_t *b)
{
int n, c0, c1, pc0, pc1;
const int scanline = pms->width;
uint16_t *pic = xmalloc(sizeof(uint16_t) * (pms->width+10) * (pms->height+10));
for (int y = 0; y < pms->height; y++) {
for (int x = 0; x < pms->width;) {
int loc = y * scanline + x;
c0 = *b++;
// non-command byte: read 1 pixel into buffer
if (c0 <= 0xf7) {
c1 = *b++;
x++;
pic[loc] = c0 | (c1 << 8);
}
// copy n+2 pixels from previous line into buffer
else if (c0 == 0xff) {
n = (*b++) + 2;
x += n;
for (int i = 0; i < n; i++) {
pic[loc + i] = pic[loc + i - scanline];
}
}
// copy n+2 pixels from 2 lines previous
else if (c0 == 0xfe) {
n = (*b++) + 2;
x += n;
for (int i = 0; i < n; i++) {
pic[loc + i] = pic[loc + i - scanline * 2];
}
}
// repeat a pixel n+3 times (1-byte RLE)
else if (c0 == 0xfd) {
n = (*b++) + 3;
x += n;
c0 = *b++;
c1 = *b++;
pc0 = c0 | (c1 << 8);
for (int i = 0; i < n; i++) {
pic[loc + i] = pc0;
}
}
// repeat a sequence of 2 pixels n+2 times (2-byte RLE)
else if (c0 == 0xfc) {
n = ((*b++) + 2) * 2;
x += n;
c0 = *b++;
c1 = *b++;
pc0 = c0 | (c1 << 8);
c0 = *b++;
c1 = *b++;
pc1 = c0 | (c1 << 8);
for (int i = 0; i < n; i+=2) {
pic[loc + i ] = pc0;
pic[loc + i + 1] = pc1;
}
}
// copy 1 pixel from previous line (left diagonal)
else if (c0 == 0xfb) {
x++;
pic[loc] = pic[loc - scanline - 1];
}
// copy 1 pixel from previous line (right diagonal)
else if (c0 == 0xfa) {
x++;
pic[loc] = pic[loc - scanline + 1];
}
// this one's a bit tricky, but basically what this does is it
// combines one byte with the next n bytes
else if (c0 == 0xf9) {
n = (*b++) + 1;
x += n;
c0 = *b++;
c1 = *b++;
pc0 = ((c0 & 0xe0)<<8) + ((c0 & 0x18)<<6) + ((c0 & 0x07)<<2);
pc1 = ((c1 & 0xc0)<<5) + ((c1 & 0x3c)<<3) + (c1 & 0x03);
pic[loc] = pc0 + pc1;
for (int i = 1; i < n; i++) {
c1 = *b++;
pc1 = ((c1 & 0xc0)<<5) + ((c1 & 0x3c)<<3) + (c1 & 0x03);
pic[loc + i] = pc0 | pc1;
}
}
// not sure why this exists, probably padding
else {
c0 = *b++;
c1 = *b++;
x++;
pic[loc] = c0 | (c1 << 8);
}
}
}
return pic;
}
static void pms_read_header(struct pms_header *hdr, const uint8_t *data)
{
hdr->version = LittleEndian_getW(data, 2);
hdr->header_size = LittleEndian_getW(data, 4);
hdr->bpp = data[6];
hdr->bpps = data[7];
hdr->sf = data[8];
hdr->bf = LittleEndian_getW(data, 10);
hdr->x = LittleEndian_getDW(data, 16);
hdr->y = LittleEndian_getDW(data, 20);
hdr->width = LittleEndian_getDW(data, 24);
hdr->height = LittleEndian_getDW(data, 28);
hdr->dp = LittleEndian_getDW(data, 32);
hdr->pp = LittleEndian_getDW(data, 36);
hdr->cp = LittleEndian_getDW(data, 40);
}
static void pms_init_metrics(struct pms_header *pms, struct cg_metrics *dst)
{
dst->x = pms->x;
dst->y = pms->y;
dst->w = pms->width;
dst->h = pms->height;
dst->bpp = pms->bpp;
dst->has_pixel = pms->dp;
dst->has_alpha = pms->pp;
dst->pixel_pitch = pms->width * (pms->bpp / 8);
dst->alpha_pitch = 1;
}
/* Load a PMS8 CG. */
static void pms8_load(const uint8_t *data, struct pms_header *pms, struct cg *cg)
{
cg->pal = pms_get_palette(data + pms->pp);
cg->type = ALCG_PMS8;
cg->pixels = pms8_extract(pms, data + pms->dp);
// TODO: convert to RGBA
}
static void pms16_load(const uint8_t *data, struct pms_header *pms, struct cg *cg)
{
cg->type = ALCG_PMS16;
cg->pixels = pms16_extract(pms, data + pms->dp);
if (pms->pp) {
// TODO: alpha channel
//uint8_t *alpha = malloc((pms->width + 10) * (pms->height + 10));
//pms8_extract(pms, alpha, data + pms->pp);
}
// TODO: convert to RGBA
}
void pms_extract(const uint8_t *data, size_t size, struct cg *cg)
{
struct pms_header pms;
pms_read_header(&pms, data);
pms_init_metrics(&pms, &cg->metrics);
if ((size_t)pms.dp > size)
ERROR("PMS pixel offset out of bounds");
if ((size_t)pms.pp > size)
ERROR("PMS palette/alpha offset out of bounds");
if (pms.bpp == 8)
pms8_load(data, &pms, cg);
else if (pms.bpp == 16)
pms16_load(data, &pms, cg);
else
WARNING("Unsupported PMS bpp: %d", pms.bpp);
}
uint8_t *pms_extract_mask(const uint8_t *data, size_t size)
{
struct pms_header pms;
pms_read_header(&pms, data);
if ((size_t)pms.dp > size)
ERROR("PMS pixel offset out of bounds");
if ((size_t)pms.pp > size)
ERROR("PMS palette/alpha offset out of bounds");
if (pms.bpp != 8)
ERROR("PMS mask is not 8bpp");
return pms8_extract(&pms, data + pms.dp);
}
-287
View File
@@ -1,287 +0,0 @@
/*
* qnt.c extract QNT cg
*
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
* 1998- <masaki-c@is.aist-nara.ac.jp>
* 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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
*
* @version 1.0 01/07/30 initial version
* @version 1.1 01/08/10 fix alpha map extraction
* @version 1.2 01/11/26 zlibの展開バッファが画像サイズよりも大きなものを
* 要求するときの workaround
* @version 1.2 01/11/28 format version 1 に対応
* *(top + 4) is 0 -> version 0
* *(top + 4) is 1 -> version 1
* @version 1.3 03/02/22 縦横奇数サイズ時にpixel/alphaともおかしかったのをfix
* zlib展開バッファを縦横それぞれ1byteづつ多くとる
* ようにした
*/
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include "little_endian.h"
#include "system4.h"
#include "system4/cg.h"
#include "system4/qnt.h"
/*
zlib の展開バッファで、幅×高さ×3に、さらにどれくらい余裕をとるか
(リクルスで 1164バイトというのがあった)
*/
#define ZLIBBUF_MARGIN 5*1024
/*
* Get information from header
*
* b: raw data
*
* return: acquired qnt information object
*/
void qnt_extract_header(const uint8_t *b, struct qnt_header *qnt)
{
int rsv0;
rsv0 = LittleEndian_getDW(b, 4);
if (rsv0 == 0) {
qnt->hdr_size = 48;
qnt->x0 = LittleEndian_getDW(b, 8);
qnt->y0 = LittleEndian_getDW(b, 12);
qnt->width = LittleEndian_getDW(b, 16);
qnt->height = LittleEndian_getDW(b, 20);
qnt->bpp = LittleEndian_getDW(b, 24);
qnt->rsv = LittleEndian_getDW(b, 28);
qnt->pixel_size = LittleEndian_getDW(b, 32);
qnt->alpha_size = LittleEndian_getDW(b, 36);
} else {
qnt->hdr_size = LittleEndian_getDW(b, 8);
qnt->x0 = LittleEndian_getDW(b, 12);
qnt->y0 = LittleEndian_getDW(b, 16);
qnt->width = LittleEndian_getDW(b, 20);
qnt->height = LittleEndian_getDW(b, 24);
qnt->bpp = LittleEndian_getDW(b, 28);
qnt->rsv = LittleEndian_getDW(b, 32);
qnt->pixel_size = LittleEndian_getDW(b, 36);
qnt->alpha_size = LittleEndian_getDW(b, 40);
}
if (qnt->bpp != 24)
ERROR("Unsupported bits-per-pixel: %d", qnt->bpp);
}
/*
* Do extract qnt pixel image
*
* qnt: qnt header information
* pic: pixel to be stored
* b : raw data (pointer to pixel)
*/
static void extract_pixel(struct qnt_header *qnt, uint8_t *pic, const uint8_t *b)
{
int i, j, x, y, w, h;
unsigned long ucbuf = (qnt->width+1) * (qnt->height+1) * 3 + ZLIBBUF_MARGIN;
uint8_t *raw = malloc(sizeof(uint8_t) * ucbuf);
if (Z_OK != uncompress(raw, &ucbuf, b, qnt->pixel_size)) {
WARNING("uncompress failed\n");
free(raw);
return;
}
w = qnt->width;
h = qnt->height;
j = 0;
for (i = 2; i >= 0; i--) {
for (y = 0; y < (h -1); y+=2) {
for (x = 0; x < (w -1); x+=2) {
pic[( y *w+x) *3 +i] = raw[j];
pic[((y+1)*w+x) *3 +i] = raw[j+1];
pic[( y *w+x+1)*3 +i] = raw[j+2];
pic[((y+1)*w+x+1)*3 +i] = raw[j+3];
j+=4;
}
if (x != w) {
pic[( y *w+x)*3 +i] = raw[j];
pic[((y+1)*w+x)*3 +i] = raw[j+1];
j+=4;
}
}
if (y != h) {
for (x = 0; x < (w -1); x+=2) {
pic[(y*w+x )*3+i] = raw[j];
pic[(y*w+x+1)*3+i] = raw[j+2];
j+=4;
}
if (x != w) {
pic[( y *w+x)*3 +i] = raw[j];
j+=4;
}
}
}
if (w > 1) {
for (x = 1; x < w; x++) {
pic[x*3 ] = pic[(x-1)*3 ] - pic[x*3 ];
pic[x*3+1] = pic[(x-1)*3+1] - pic[x*3+1];
pic[x*3+2] = pic[(x-1)*3+2] - pic[x*3+2];
}
}
if (h > 1) {
for (y = 1; y < h; y++) {
pic[(y*w)*3 ] = pic[((y-1)*w)*3 ] - pic[(y*w)*3 ];
pic[(y*w)*3+1] = pic[((y-1)*w)*3+1] - pic[(y*w)*3+1];
pic[(y*w)*3+2] = pic[((y-1)*w)*3+2] - pic[(y*w)*3+2];
for (x = 1; x < w; x++) {
int px, py;
py = pic[((y-1)*w+x )*3];
px = pic[( y *w+x-1)*3];
pic[(y*w+x)*3] = ((py+px)>>1) - pic[(y*w+x)*3];
py = pic[((y-1)*w+x )*3+1];
px = pic[( y *w+x-1)*3+1];
pic[(y*w+x)*3+1] = ((py+px)>>1) - pic[(y*w+x)*3+1];
py = pic[((y-1)*w+x )*3+2];
px = pic[( y *w+x-1)*3+2];
pic[(y*w+x)*3+2] = ((py+px)>>1) - pic[(y*w+x)*3+2];
}
}
}
free(raw);
}
/*
* Do extract qnt alpha image
*
* qnt: qnt header information
* pic: pixel to be stored
* b : raw data (pointer to alpha pixel)
*/
static void extract_alpha(struct qnt_header *qnt, uint8_t *pic, const uint8_t *b)
{
int i, x, y, w, h;
unsigned long ucbuf = (qnt->width+1) * (qnt->height+1) + ZLIBBUF_MARGIN;
uint8_t *raw = malloc(sizeof(uint8_t) * ucbuf);
if (Z_OK != uncompress(raw, &ucbuf, b, qnt->alpha_size)) {
WARNING("uncompress failed\n");
free(raw);
return;
}
w = qnt->width;
h = qnt->height;
i = 1;
if (w > 1) {
pic[0] = raw[0];
for (x = 1; x < w; x++) {
pic[x] = pic[x-1] - raw[i];
i++;
}
if (w%2) i++;
}
if (h > 1) {
for (y = 1; y < h; y++) {
pic[y*w] = pic[(y-1) *w] - raw[i]; i++;
for (x = 1; x < w; x++) {
int pax, pay;
pax = pic[ y * w + x -1];
pay = pic[(y-1)* w + x ];
pic[y*w+x] = ((pax+pay) >> 1) - raw[i];
i++;
}
if (w%2) i++;
}
}
free(raw);
}
/*
* Check data is qnt format cg or not
*
* data: raw data (pointer to data top)
*
* return: true if data is qnt
*/
bool qnt_checkfmt(const uint8_t *data)
{
if (data[0] != 'Q' || data[1] != 'N' || data[2] != 'T') return false;
return true;
}
static void qnt_init_metrics(struct qnt_header *qnt, struct cg_metrics *dst)
{
dst->w = qnt->width;
dst->h = qnt->height;
dst->bpp = qnt->bpp;
dst->has_pixel = qnt->pixel_size > 0;
dst->has_alpha = qnt->alpha_size > 0;
dst->pixel_pitch = qnt->width * (qnt->bpp / 8);
dst->alpha_pitch = 1;
}
bool qnt_get_metrics(const uint8_t *data, struct cg_metrics *dst)
{
struct qnt_header qnt;
qnt_extract_header(data, &qnt);
qnt_init_metrics(&qnt, dst);
return true;
}
/*
* Extract qnt header and pixel
*
* data: raw data (pointer to data top)
*
* return: extracted image data and information
*/
void qnt_extract(const uint8_t *data, struct cg *cg)
{
struct qnt_header qnt;
qnt_extract_header(data, &qnt);
qnt_init_metrics(&qnt, &cg->metrics);
uint8_t *pixels = xcalloc(3, (qnt.width+10) * (qnt.height+10));
extract_pixel(&qnt, pixels, data + qnt.hdr_size);
cg->type = ALCG_QNT;
// combine color/alpha data
uint8_t *alpha = xmalloc((qnt.width+10) * (qnt.height+10));
uint8_t *tmp = xmalloc((qnt.width+10) * (qnt.height+10) * 4);
if (qnt.alpha_size) {
extract_alpha(&qnt, alpha, data + qnt.hdr_size + qnt.pixel_size);
} else {
// FIXME: Some CGs don't display correctly unless we add an alpha channel here.
// Not sure why. It seems to affect some but not all alpha-less CGs.
// E.g. CG#90 (and similar) from the Rance 2 digest version.
memset(alpha, 0xFF, (qnt.width+10)*(qnt.height+10));
}
for (int src_i = 0, dst_i = 0, p = 0; p < qnt.width * qnt.height; p++) {
tmp[dst_i++] = pixels[src_i++];
tmp[dst_i++] = pixels[src_i++];
tmp[dst_i++] = pixels[src_i++];
tmp[dst_i++] = alpha[p];
}
free(alpha);
free(pixels);
cg->pixels = tmp;
}
-397
View File
@@ -1,397 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "system4.h"
#include "system4/string.h"
#include "system4/utfsjis.h"
struct string EMPTY_STRING = {
.cow = true,
.ref = 1,
.size = 0,
.text = ""
};
static struct string *alloc_string(int size)
{
return xmalloc(sizeof(struct string) + size + 1);
}
void free_string(struct string *str)
{
if (!str->ref)
ERROR("Double free of string object");
if (!--str->ref) {
free(str);
}
}
struct string *make_string(const char *str, unsigned int len)
{
struct string *s = alloc_string(len);
s->size = len;
s->ref = 1;
s->cow = 0;
memcpy(s->text, str, len);
s->text[len] = '\0';
return s;
}
struct string *cstr_to_string(const char *str)
{
return make_string(str, strlen(str));
}
struct string *string_ref(struct string *s)
{
s->cow = 1;
s->ref++;
return s;
}
struct string *string_dup(const struct string *in)
{
struct string *out = alloc_string(in->size);
out->size = in->size;
out->ref = 1;
out->cow = 0;
memcpy(out->text, in->text, in->size + 1);
return out;
}
struct string *integer_to_string(int n)
{
char buf[512];
int len = snprintf(buf, 512, "%d", n);
return make_string(buf, len);
}
static void number_zen2han(char *buf)
{
for (int src = 0, dst = 0; buf[src];) {
uint8_t b1 = buf[src];
if (SJIS_2BYTE(b1)) {
uint8_t b2 = buf[src+1];
if (b1 == 0x82 && b2 >= 0x4f && b2 <= 0x58) {
buf[dst++] = '0' + (b2 - 0x4f);
src += 2;
} else if (b1 == 0x81 && b2 == 0x7c) {
buf[dst++] = '-';
src += 2;
} else if (b1 == 0x81 && b2 == 0x44) {
buf[dst++] = '.';
src += 2;
} else if (b1 == 0x81 && b2 == 0x40) {
buf[dst++] = ' ';
src += 2;
} else {
buf[dst++] = buf[src++];
buf[dst++] = buf[src++];
}
} else {
buf[dst++] = buf[src++];
}
}
}
int string_to_integer(struct string *s)
{
char *buf = xstrdup(s->text);
number_zen2han(buf);
int n = atoi(buf);
free(buf);
return n;
}
struct string *float_to_string(float f, int precision)
{
char buf[512];
int len;
// System40.exe pushes -1, defaults to 6
if (precision < 0) {
precision = 6;
}
len = snprintf(buf, 512, "%.*f", precision, f);
return make_string(buf, len);
}
struct string *string_concatenate(const struct string *a, const struct string *b)
{
struct string *s = alloc_string(a->size + b->size);
s->size = a->size + b->size;
s->ref = 1;
s->cow = 0;
memcpy(s->text, a->text, a->size);
memcpy(s->text + a->size, b->text, b->size + 1);
return s;
}
struct string *string_copy(const struct string *s, int index, int len)
{
if (index < 0)
index = 0;
if (len <= 0)
return make_string("", 0);
if ((index = sjis_index(s->text, index)) < 0)
return make_string("", 0);
if ((len = sjis_index(s->text + index, len)) < 0)
len = s->size - index;
return make_string(s->text + index, len);
}
static struct string *cow_check(struct string *s)
{
if (s->cow && s->ref > 1) {
struct string *out = string_dup(s);
free_string(s);
return out;
}
if (s->cow)
s->cow = 0;
return s;
}
void string_append_cstr(struct string **_a, const char *b, size_t b_size)
{
if (!b_size)
return;
struct string *a = *_a = cow_check(*_a);
a = xrealloc(a, sizeof(struct string) + a->size + b_size + 1);
memcpy(a->text + a->size, b, b_size);
a->size += b_size;
a->text[a->size] = '\0';
*_a = a;
}
void string_append(struct string **_a, const struct string *b)
{
struct string *a = *_a = cow_check(*_a);
size_t a_size = a->size;
a = xrealloc(a, sizeof(struct string) + a->size + b->size + 1);
a->size = a_size + b->size;
memcpy(a->text + a_size, b->text, b->size + 1);
*_a = a;
}
void string_push_back(struct string **s, int c)
{
int bytes = SJIS_2BYTE(c) ? 2 : 1;
*s = cow_check(*s);
*s = xrealloc(*s, sizeof(struct string) + (*s)->size + bytes + 1);
(*s)->text[(*s)->size++] = c & 0xFF;
if (bytes == 2) {
(*s)->text[(*s)->size++] = c >> 8;
}
(*s)->text[(*s)->size] = '\0';
}
void string_pop_back(struct string **s)
{
*s = cow_check(*s);
// get index of last character
int c = 0;
for (int i = 0; i < (*s)->size; i++) {
c = i;
if (SJIS_2BYTE((*s)->text[i])) {
i++;
}
}
(*s)->text[c] = '\0';
(*s)->size = c;
}
void string_erase(struct string **s, int index)
{
int bytes;
if (index < 0)
index = 0;
if (index >= (*s)->size)
return;
if ((index = sjis_index((*s)->text, index)) < 0)
return;
bytes = SJIS_2BYTE((*s)->text[index]) ? 2 : 1;
*s = cow_check(*s);
size_t size = (*s)->size - index - bytes;
for (size_t i = 0; i < size; i++) {
(*s)->text[index+i] = (*s)->text[index+bytes+i];
}
(*s)->size -= bytes;
(*s)->text[(*s)->size] = '\0';
}
void string_clear(struct string *s)
{
s->size = 0;
s->text[0] = '\0';
}
int string_find(const struct string *haystack, const struct string *needle)
{
int c = 0;
for (int i = 0; i < haystack->size; i++, c++) {
if (!strncmp(haystack->text+i, needle->text, needle->size))
return c;
if (SJIS_2BYTE(haystack->text[i])) {
i++;
}
}
return -1;
}
int string_get_char(const struct string *str, int i)
{
if ((i = sjis_index(str->text, i)) < 0)
ERROR("String index out of bounds");
if (SJIS_2BYTE(str->text[i]))
return (uint8_t)str->text[i] | ((uint8_t)str->text[i+1] << 8);
return str->text[i];
}
void string_set_char(struct string **_str, int i, unsigned int c)
{
struct string *str = *_str = cow_check(*_str);
int bytes_src, bytes_dst;
if ((i = sjis_index(str->text, i)) < 0)
ERROR("String index out of bounds");
bytes_src = SJIS_2BYTE(c) ? 2 : 1;
bytes_dst = SJIS_2BYTE(str->text[i]) ? 2 : 1;
if (bytes_src == 1 && bytes_dst == 1) {
str->text[i] = c;
} else if (bytes_src == 2 && bytes_dst == 2) {
str->text[i] = c & 0xFF;
str->text[i+1] = (c >> 8) & 0xFF;
} else if (bytes_src == 1 && bytes_dst == 2) {
// shrink 1 byte
str->text[i] = c;
for (int j = i+1; j < str->size; j++) {
str->text[j] = str->text[j+1];
}
str->size--;
} else if (bytes_src == 2 && bytes_dst == 1) {
// grow 1 byte
str = xrealloc(str, sizeof(struct string) + str->size + 2);
str->size++;
*_str = str;
for (int j = str->size; j > i; j--) {
str->text[j] = str->text[j-1];
}
str->text[i] = c & 0xFF;
str->text[i+1] = (c >> 8) & 0xFF;
}
}
#define DIGIT_MAX 512
static int number_han2zen(char *buf, size_t size)
{
char *tmp = xmalloc(size*2);
int i = 0;
for (char *p = buf; *p && i < DIGIT_MAX-2; p++) {
switch (*p) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
tmp[i++] = 0x82;
tmp[i++] = 0x4f + (*p - '0');
break;
case '-':
tmp[i++] = 0x81;
tmp[i++] = 0x7c;
break;
case '.':
tmp[i++] = 0x81;
tmp[i++] = 0x44;
break;
case ' ':
tmp[i++] = 0x81;
tmp[i++] = 0x40;
break;
default:
tmp[i++] = *p;
}
}
tmp[i] = '\0';
memcpy(buf, tmp, i+1);
free(tmp);
return i;
}
int int_to_cstr(char *buf, size_t size, int v, int figures, bool zero_pad, bool zenkaku)
{
char fmt[64];
int i = 0;
// prepare format string for snprintf
fmt[i++] = '%';
if (figures > 0) {
if (zero_pad)
fmt[i++] = '0';
i += snprintf(fmt+i, 64-i, "%d", figures);
}
fmt[i++] = 'd';
fmt[i] = '\0';
i = snprintf(buf, size-1, fmt, v);
buf[i] = '\0';
if (zenkaku)
i = number_han2zen(buf, size);
return i;
}
int float_to_cstr(char *buf, size_t size, float v, int figures, bool zero_pad, int precision, bool zenkaku)
{
char fmt[64];
int i = 0;
fmt[i++] = '%';
if (figures > 0) {
if (zero_pad)
fmt[i++] = '0';
i += snprintf(fmt+i, 64-i, "%d", figures);
}
fmt[i++] = '.';
i += snprintf(fmt+i, 64-i, "%d", precision);
fmt[i++] = 'f';
fmt[i] = '\0';
i = snprintf(buf, size-1, fmt, v);
buf[i] = '\0';
if (zenkaku) {
i = number_han2zen(buf, DIGIT_MAX);
// XXX: bug in System40.exe
buf[i++] = 'F';
buf[i] = '\0';
}
return i;
}
-110
View File
@@ -1,110 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "system4.h"
#include "system4/utfsjis.h"
mem_alloc void *_xmalloc(size_t size, const char *func)
{
void *ptr = malloc(size);
if (!ptr) {
sys_error("*ERROR*(%s): Out of memory\n", func);
}
return ptr;
}
mem_alloc void *_xcalloc(size_t nmemb, size_t size, const char *func)
{
void *ptr = calloc(nmemb, size);
if (!ptr) {
sys_error("*ERROR*(%s): Out of memory\n", func);
}
return ptr;
}
mem_alloc void *_xrealloc(void *ptr, size_t size, const char *func)
{
ptr = realloc(ptr, size);
if (!ptr) {
sys_error("*ERROR*(%s): Out of memory\n", func);
}
return ptr;
}
mem_alloc char *_xstrdup(const char *in, const char *func)
{
char *out = strdup(in);
if (!out) {
sys_error("*ERROR*(%s): Out of memory\n", func);
}
return out;
}
mem_alloc void *xrealloc_array(void *dst, size_t old_nmemb, size_t new_nmemb, size_t size)
{
dst = xrealloc(dst, new_nmemb * size);
if (new_nmemb > old_nmemb)
memset((char*)dst + old_nmemb*size, 0, (new_nmemb - old_nmemb) * size);
return dst;
}
noreturn void sys_verror(const char *fmt, va_list ap)
{
vfprintf(stderr, fmt, ap);
sys_exit(1);
}
noreturn void sys_error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
sys_verror(fmt, ap);
}
void sys_vwarning(const char *fmt, va_list ap)
{
vfprintf(stderr, fmt, ap);
}
void sys_warning(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
sys_vwarning(fmt, ap);
va_end(ap);
}
void sys_message(const char *fmt, ...)
{
if (sys_silent)
return;
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
fflush(stdout);
}
noreturn void sys_exit(int code)
{
// TODO: cleanup
exit(code);
}
+2 -4
View File
@@ -12,11 +12,9 @@ acxbuild += bisongen.process('csv_parser.y')
executable('acxdump', acxdump,
dependencies : tool_deps,
c_args : ['-Wno-unused-parameter'],
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
executable('acxbuild', acxbuild,
dependencies : tool_deps,
c_args : ['-Wno-unused-parameter'],
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
+2 -4
View File
@@ -4,12 +4,10 @@ aincmp = ['../dasm.c',
executable('aincmp', aincmp,
dependencies : tool_deps,
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
excmp = ['excmp.c']
executable('excmp', excmp,
dependencies : tool_deps,
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
+1 -2
View File
@@ -10,5 +10,4 @@ aindump = ['../../cJSON.c',
executable('aindump', aindump,
dependencies : tool_deps,
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
+1 -2
View File
@@ -21,5 +21,4 @@ ainedit += bisongen.process('asm_parser.y', 'text_parser.y', 'jaf_parser.y')
executable('ainedit', ainedit,
dependencies : tool_deps,
c_args : ['-Wno-unused-parameter'],
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
+1 -2
View File
@@ -5,5 +5,4 @@ alicear = ['alice-ar.c',
executable('alice-ar', alicear,
dependencies: tool_deps,
c_args : ['-Wno-unused-parameter'],
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
+2 -4
View File
@@ -15,11 +15,9 @@ exbuild += bisongen.process('ex_parser.y')
executable('exdump', exdump,
dependencies : tool_deps,
c_args : ['-Wno-unused-parameter'],
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
executable('exbuild', exbuild,
dependencies : tool_deps,
c_args : ['-Wno-unused-parameter'],
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
+1 -2
View File
@@ -4,5 +4,4 @@ fnldump = ['fnldump.c',
executable('fnldump', fnldump,
dependencies: tool_deps,
c_args : ['-Wno-unused-parameter'],
include_directories : incdir,
link_with : libsys4)
include_directories : incdir)
+2 -2
View File
@@ -1,8 +1,8 @@
if build_machine.system() == 'windows'
iconv = dependency('iconv')
tool_deps = [libm, zlib, iconv]
tool_deps = [libm, zlib, iconv, libsys4_dep]
else
tool_deps = [libm, zlib]
tool_deps = [libm, zlib, libsys4_dep]
endif
subdir('acx')
-213
View File
@@ -1,213 +0,0 @@
/*
* utfsjis.c -- utf-8/sjis related function
*
* Copyright (C) 1997 Yutaka OIWA <oiwa@is.s.u-tokyo.ac.jp>
*
* written for Satoshi KURAMOCHI's "eplaymidi"
* <satoshi@ueda.info.waseda.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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "system4/utfsjis.h"
#include "system4/s2utbl.h"
// Convert a character-index to a byte-index for a given SJIS string.
int sjis_index(const char *_src, int index)
{
const uint8_t *src = (uint8_t*)_src;
int i, c;
for (i = 0, c = 0; c < index && src[i]; i++, c++) {
if (SJIS_2BYTE(src[i])) {
i++;
// check for invalid sjis data
if (!src[i])
return -1;
}
}
return src[i] ? i : -1;
}
#include "system4.h"
char *sjis2utf(const char *_src, size_t len) {
if (!len)
len = strlen(_src);
const uint8_t *src = (uint8_t*)_src;
uint8_t* dst = malloc(len * 3 + 1);
uint8_t* dstp = dst;
while (*src) {
if (*src <= 0x7f) {
*dstp++ = *src++;
continue;
}
int c;
if (*src >= 0xa0 && *src <= 0xdf) {
c = 0xff60 + *src - 0xa0;
src++;
} else {
// guard against invalid byte sequence
c = *(src+1);
if (c < 0x40 || c == 0x7f || c > 0xfc) {
*dstp++ = '?';
src++;
continue;
}
c = s2u[*src - 0x80][*(src+1) - 0x40];
src += 2;
}
if (c <= 0x7f) {
*dstp++ = c;
} else if (c <= 0x7ff) {
*dstp++ = 0xc0 | c >> 6;
*dstp++ = 0x80 | (c & 0x3f);
} else {
*dstp++ = 0xe0 | c >> 12;
*dstp++ = 0x80 | (c >> 6 & 0x3f);
*dstp++ = 0x80 | (c & 0x3f);
}
}
*dstp = '\0';
return (char*)dst;
}
static int unicode_to_sjis(int u) {
for (int b1 = 0x81; b1 <= 0xff; b1++) {
if (b1 >= 0xa0 && b1 <= 0xdf)
continue;
for (int b2 = 0x40; b2 <= 0xff; b2++) {
if (u == s2u[b1 - 0x80][b2 - 0x40])
return b1 << 8 | b2;
}
}
return 0;
}
char *utf2sjis(const char *_src, size_t len) {
if (!len)
len = strlen(_src);
const uint8_t *src = (uint8_t*)_src;
uint8_t* dst = malloc(len + 1);
uint8_t* dstp = dst;
while (*src) {
if (*src <= 0x7f) {
*dstp++ = *src++;
continue;
}
int u;
if (*src <= 0xdf) {
u = (src[0] & 0x1f) << 6 | (src[1] & 0x3f);
src += 2;
} else if (*src <= 0xef) {
u = (src[0] & 0xf) << 12 | (src[1] & 0x3f) << 6 | (src[2] & 0x3f);
src += 3;
} else {
*dstp++ = '?';
do src++; while ((*src & 0xc0) == 0x80);
continue;
}
if (u > 0xff60 && u <= 0xff9f) {
*dstp++ = u - 0xff60 + 0xa0;
} else {
int c = unicode_to_sjis(u);
if (c) {
*dstp++ = c >> 8;
*dstp++ = c & 0xff;
} else {
*dstp++ = '?';
}
}
}
*dstp = '\0';
return (char*)dst;
}
/* src 内に半角カナもしくはASCII文字があるかどうか */
bool sjis_has_hankaku(const char *_src) {
const uint8_t *src = (uint8_t*)_src;
while(*src) {
if (SJIS_2BYTE(*src)) {
src++;
} else {
return true;
}
src++;
}
return false;
}
/* src 内に 全角文字があるかどうか */
bool sjis_has_zenkaku(const char *_src) {
const uint8_t *src = (uint8_t*)_src;
while(*src) {
if (SJIS_2BYTE(*src)) {
return true;
}
src++;
}
return false;
}
/* src 中の文字数を数える 全角文字も1文字 */
int sjis_count_char(const char *_src) {
const uint8_t *src = (uint8_t*)_src;
int c = 0;
while(*src) {
if (SJIS_2BYTE(*src)) {
src++;
}
c++; src++;
}
return c;
}
/* SJIS(EUC) を含む文字列の ASCII を大文字化する */
void sjis_toupper(char *_src) {
uint8_t *src = (uint8_t*)_src;
while(*src) {
if (SJIS_2BYTE(*src)) {
src++;
} else {
if (*src >= 0x60 && *src <= 0x7a) {
*src &= 0xdf;
}
}
src++;
}
}
/* SJIS を含む文字列の ASCII を大文字化する2 */
char *sjis_toupper2(const char *_src, size_t len) {
const uint8_t *src = (uint8_t*)_src;
uint8_t *dst;
dst = malloc(len +1);
if (dst == NULL) return NULL;
strcpy((char*)dst, (char*)src);
sjis_toupper((char*)dst);
return (char*)dst;
}
-123
View File
@@ -1,123 +0,0 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 <stdlib.h>
#include <string.h>
#include <webp/decode.h>
#include "system4.h"
#include "system4/ald.h"
#include "system4/cg.h"
#include "system4/webp.h"
#include "little_endian.h"
#include "xsystem4.h"
bool webp_checkfmt(const uint8_t *data)
{
if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F')
return false;
if (data[8] != 'W' || data[9] != 'E' || data[10] != 'B' || data[11] != 'P')
return false;
return true;
}
static void webp_init_metrics(struct cg_metrics *m)
{
m->x = 0;
m->y = 0;
m->bpp = 24;
m->has_pixel = true;
m->has_alpha = true; // FIXME
m->pixel_pitch = m->w * 3;
m->alpha_pitch = 1;
}
void webp_get_metrics(uint8_t *data, size_t size, struct cg_metrics *m)
{
WebPGetInfo(data, size, &m->w, &m->h);
webp_init_metrics(m);
}
static int get_base_cg(uint8_t *data, size_t size)
{
if (size < 12)
return -1;
if (data[size-12] != 'O' || data[size-11] != 'V' || data[size-10] != 'E' || data[size-9] != 'R')
return -1;
int uk = LittleEndian_getDW(data, size-8); // size?
if (uk != 4)
WARNING("WEBP: expected 0x4 preceding base CG number, got %d", uk);
return LittleEndian_getDW(data, size-4);
}
void webp_extract(uint8_t *data, size_t size, struct cg *cg, struct archive *ar)
{
cg->pixels = WebPDecodeRGBA(data, size, &cg->metrics.w, &cg->metrics.h);
webp_init_metrics(&cg->metrics);
cg->type = ALCG_WEBP;
int base = get_base_cg(data, size);
if (base < 0)
return;
// FIXME: possible infinite recursion on broken/malicious ALD
struct cg *base_cg = cg_load(ar, base-1);
if (base_cg->metrics.w != cg->metrics.w || base_cg->metrics.h != cg->metrics.h) {
ERROR("webp base CG dimensions don't match: (%d,%d) / (%d,%d)",
base_cg->metrics.w, base_cg->metrics.h, cg->metrics.w, cg->metrics.h);
}
// mask alpha color
uint8_t *pixels = cg->pixels;
uint8_t *base_pixels = base_cg->pixels;
for (int row = 0; row < cg->metrics.h; row++) {
for (int x = 0; x < cg->metrics.w; x++) {
int p = cg->metrics.w*row*4 + x*4;
if (pixels[p] == 255 && pixels[p+1] == 0 && pixels[p+2] == 255) {
pixels[p+0] = base_pixels[p+0];
pixels[p+1] = base_pixels[p+1];
pixels[p+2] = base_pixels[p+2];
pixels[p+3] = base_pixels[p+3];
}
}
}
cg_free(base_cg);
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <webp/encode.h>
void webp_save(const char *path, uint8_t *pixels, int w, int h, bool alpha)
{
size_t len;
uint8_t *output;
FILE *f = fopen(path, "wb");
if (!f)
ERROR("fopen failed: %s", strerror(errno));
if (alpha) {
len = WebPEncodeLosslessRGBA(pixels, w, h, w*4, &output);
} else {
len = WebPEncodeLosslessRGB(pixels, w, h, w*3, &output);
}
if (!fwrite(output, len, 1, f))
ERROR("fwrite failed: %s", strerror(errno));
fclose(f);
WebPFree(output);
}
Submodule subprojects/libsys4 added at 171f771302