diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d430f65 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "subprojects/libsys4"] + path = subprojects/libsys4 + url = https://github.com/nunuhara/libsys4 diff --git a/README.md b/README.md index 39777d8..c752093 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/meson.build b/meson.build index 415a0cc..8ed32ec 100644 --- a/meson.build +++ b/meson.build @@ -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') diff --git a/src/acx.c b/src/acx.c deleted file mode 100644 index 0ea28fe..0000000 --- a/src/acx.c +++ /dev/null @@ -1,157 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#include - -#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; -} - - diff --git a/src/afa.c b/src/afa.c deleted file mode 100644 index f224663..0000000 --- a/src/afa.c +++ /dev/null @@ -1,309 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#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; -} diff --git a/src/ain.c b/src/ain.c deleted file mode 100644 index 5dbce09..0000000 --- a/src/ain.c +++ /dev/null @@ -1,1468 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * Credit to SLC for reverse engineering AIN formats. - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "little_endian.h" -#include "system4.h" -#include "system4/ain.h" -#include "system4/hashtable.h" -#include "system4/instructions.h" -#include "system4/string.h" -#include "system4/utfsjis.h" - -struct func_list { - int nr_slots; - int slots[]; -}; -#define func_list_size(nr_slots) (sizeof(struct func_list) + sizeof(int)*(nr_slots)) - -static void func_ht_add(struct ain *ain, int i) -{ - struct ht_slot *kv = ht_put(ain->_func_ht, ain->functions[i].name, NULL); - if (kv->value) { - // entry with this name already exists; add to list - struct func_list *list = kv->value; - list = xrealloc(list, func_list_size(list->nr_slots+1)); - list->slots[list->nr_slots] = i; - list->nr_slots++; - kv->value = list; - } else { - // empty bucket: create list - struct func_list *list = xmalloc(func_list_size(1)); - list->nr_slots = 1; - list->slots[0] = i; - kv->value = list; - } -} - -static void init_func_ht(struct ain *ain) -{ - ain->_func_ht = ht_create(1024); - for (int i = 0; i < ain->nr_functions; i++) { - func_ht_add(ain, i); - } -} - -static void struct_ht_add(struct ain *ain, struct ain_struct *s) -{ - struct ht_slot *kv = ht_put(ain->_struct_ht, s->name, NULL); - if (kv->value) { - ERROR("Duplicate structure names: '%s'", s->name); - } - kv->value = s; -} - -static void init_struct_ht(struct ain *ain) -{ - ain->_struct_ht = ht_create(1024); - for (int i = 0; i < ain->nr_structures; i++) { - struct_ht_add(ain, &ain->structures[i]); - } -} - -static intptr_t string_ht_add(struct ain *ain, const char *str, intptr_t i) -{ - struct ht_slot *kv = ht_put(ain->_string_ht, str, (void*)-1); - if ((intptr_t)kv->value >= 0) { - return (intptr_t)kv->value; - } - kv->value = (void*)i; - return i; -} - -static void init_string_ht(struct ain *ain) -{ - ain->_string_ht = ht_create(1024); - for (int i = 0; i < ain->nr_strings; i++) { - if (string_ht_add(ain, ain->strings[i]->text, i) != i) { - WARNING("Duplicate string in string table"); - } - } -} - -static bool function_is_member_of(char *func_name, char *struct_name) -{ - while (*func_name && *struct_name && *func_name != '@' && *func_name == *struct_name) { - func_name++; - struct_name++; - } - return !*struct_name && *func_name == '@'; -} - -/* - * Infer struct member functions from function names. - */ -void ain_init_member_functions(struct ain *ain, char *(*to_ascii)(const char*)) -{ - // XXX: we convert all struct names up front to avoid repeated conversions below - char **struct_names = xcalloc(ain->nr_structures, sizeof(char*)); - for (int i = 0; i < ain->nr_structures; i++) { - struct_names[i] = to_ascii(ain->structures[i].name); - } - char **enum_names = xcalloc(ain->nr_enums, sizeof(char*)); - for (int i = 0; i < ain->nr_enums; i++) { - enum_names[i] = to_ascii(ain->enums[i].name); - } - - for (int f = 0; f < ain->nr_functions; f++) { - ain->functions[f].struct_type = -1; - ain->functions[f].enum_type = -1; - char *name = to_ascii(ain->functions[f].name); - if (!strchr(name, '@')) { - free(name); - continue; - } - for (int s = 0; s < ain->nr_structures; s++) { - if (function_is_member_of(name, struct_names[s])) { - ain->functions[f].struct_type = s; - break; - } - } - if (ain->functions[f].struct_type != -1) { - free(name); - continue; - } - // check enums - for (int e = 0; e < ain->nr_enums; e++) { - if (function_is_member_of(name, enum_names[e])) { - ain->functions[f].enum_type = e; - break; - } - } - if (ain->functions[f].enum_type == -1) - WARNING("Failed to find struct type for function \"%s\"", name); - free(name); - } - for (int i = 0; i < ain->nr_structures; i++) { - free(struct_names[i]); - } - free(struct_names); - for (int i = 0; i < ain->nr_enums; i++) { - free(enum_names[i]); - } - free(enum_names); -} - -static struct func_list *get_function(struct ain *ain, const char *name) -{ - return ht_get(ain->_func_ht, name, NULL); -} - -int ain_get_function(struct ain *ain, char *name) -{ - size_t len; - long n = 0; - - // handle name#index syntax - for (len = 0; name[len]; len++) { - if (name[len] == '#') { - char *endptr; - n = strtol(name+len+1, &endptr, 10); - if (!name[len+1] || *endptr || n < 0) - ERROR("Invalid function name: '%s'", name); - name[len] = '\0'; - break; - } - } - - struct func_list *funs = get_function(ain, name); - if (!funs || n >= funs->nr_slots) - return -1; - return funs->slots[n]; -} - -int ain_get_function_index(struct ain *ain, struct ain_function *f) -{ - struct func_list *funs = get_function(ain, f->name); - if (!funs) - goto err; - - for (int i = 0; i < funs->nr_slots; i++) { - if (&ain->functions[funs->slots[i]] == f) - return i; - } -err: - ERROR("Invalid function: '%s'", f->name); -} - -int ain_get_struct(struct ain *ain, char *name) -{ - struct ain_struct *s = ht_get(ain->_struct_ht, name, NULL); - return s ? s - ain->structures : -1; -} - -int ain_add_struct(struct ain *ain, struct ain_struct *struc) -{ - ain->structures = xrealloc_array(ain->structures, ain->nr_structures, ain->nr_structures+1, sizeof(struct ain_struct)); - ain->structures[ain->nr_structures] = *struc; - struct_ht_add(ain, &ain->structures[ain->nr_structures]); - ain->nr_structures++; - return ain->nr_structures - 1; -} - -int ain_add_global(struct ain *ain, struct ain_variable *var) -{ - int no = ain->nr_globals; - ain->globals = xrealloc_array(ain->globals, ain->nr_globals, ain->nr_globals+1, sizeof(struct ain_variable)); - ain->globals[no] = *var; - if (AIN_VERSION_GTE(ain, 12, 0)) - ain->globals[no].name2 = strdup(""); - ain->globals[no].var_type = AIN_VAR_GLOBAL; - ain->nr_globals++; - return no; -} - -int ain_get_global(struct ain *ain, const char *name) -{ - for (int i = 0; i < ain->nr_globals; i++) { - if (!strcmp(ain->globals[i].name, name)) - return i; - } - return -1; -} - -int ain_add_initval(struct ain *ain, struct ain_initval *init) -{ - ain->global_initvals = xrealloc_array(ain->global_initvals, ain->nr_initvals, ain->nr_initvals+1, - sizeof(struct ain_initval)); - ain->global_initvals[ain->nr_initvals++] = *init; - return ain->nr_initvals - 1; -} - -int ain_add_function(struct ain *ain, struct ain_function *fun) -{ - int no = ain->nr_functions; - ain->functions = xrealloc_array(ain->functions, no, no+1, sizeof (struct ain_function)); - ain->functions[no] = *fun; - func_ht_add(ain, no); - ain->nr_functions++; - return no; -} - -int ain_add_functype(struct ain *ain, struct ain_function_type *fun) -{ - int no = ain->nr_function_types; - ain->function_types = xrealloc_array(ain->function_types, no, no+1, sizeof(struct ain_function_type)); - ain->function_types[no] = *fun; - ain->nr_function_types++; - return no; -} - -int ain_get_functype(struct ain *ain, const char *name) -{ - for (int i = 0; i < ain->nr_function_types; i++) { - if (!strcmp(ain->function_types[i].name, name)) - return i; - } - return -1; -} - -int ain_add_string(struct ain *ain, const char *str) -{ - if (!ain->_string_ht) - init_string_ht(ain); - int i = string_ht_add(ain, str, ain->nr_strings); - if (i == ain->nr_strings) { - ain->strings = xrealloc_array(ain->strings, ain->nr_strings, ain->nr_strings+1, sizeof(struct string*)); - ain->strings[ain->nr_strings++] = make_string(str, strlen(str)); - } - return i; -} - -int ain_add_message(struct ain *ain, const char *str) -{ - ain->messages = xrealloc_array(ain->messages, ain->nr_messages, ain->nr_messages+1, sizeof(struct string*)); - ain->messages[ain->nr_messages++] = make_string(str, strlen(str)); - return ain->nr_messages - 1; -} - -int ain_add_file(struct ain *ain, const char *filename) -{ - ain->filenames = xrealloc_array(ain->filenames, ain->nr_filenames, ain->nr_filenames+1, sizeof(char*)); - ain->filenames[ain->nr_filenames++] = strdup(filename); - return ain->nr_filenames - 1; -} - -int ain_add_library(struct ain *ain, struct ain_library *lib) -{ - int no = ain->nr_libraries; - ain->libraries = xrealloc_array(ain->libraries, no, no+1, sizeof(struct ain_library)); - ain->libraries[no] = *lib; - ain->nr_libraries++; - return no; -} - -static const char *errtab[AIN_MAX_ERROR] = { - [AIN_SUCCESS] = "Success", - [AIN_FILE_ERROR] = "Error opening AIN file", - [AIN_UNRECOGNIZED_FORMAT] = "Unrecognized or invalid AIN format", - [AIN_INVALID] = "Invalid AIN file" -}; - -const char *ain_strerror(int error) -{ - if (error < AIN_MAX_ERROR) - return errtab[error]; - return "Invalid error code"; -} - -const char *ain_strtype(struct ain *ain, enum ain_data_type type, int struct_type) -{ - static char buf[1024] = { [1023] = '\0' }; - struct ain_type t = { .data = type, .struc = struct_type, .rank = 0 }; - char *str = ain_strtype_d(ain, &t); - strncpy(buf, str, 1023); - free(str); - return buf; -} - -static char *type_sprintf(const char *fmt, ...) -{ - va_list ap; - char *buf = xmalloc(1024); - va_start(ap, fmt); - - vsnprintf(buf, 1023, fmt, ap); - buf[1023] = '\0'; - - va_end(ap); - - return buf; -} - -static char *array_type_string(const char *str, int rank) -{ - if (rank <= 1) - return strdup(str); - return type_sprintf("%s@%d", str, rank); -} - -static char *container_type_string(struct ain *ain, struct ain_type *t) -{ - char *buf = xmalloc(1024); - char *type = ain_strtype_d(ain, t->array_type); - const char *container_type - = t->data == AIN_ARRAY ? "array" - : t->data == AIN_REF_ARRAY ? "ref array" - : t->data == AIN_ITERATOR ? "iter" - : "unknown_container"; - - snprintf(buf, 1023, "%s<%s>", container_type, type); - - free(type); - buf[1023] = '\0'; - return buf; -} - -char *ain_strtype_d(struct ain *ain, struct ain_type *v) -{ - char buf[1024]; - if (!v) - return strdup("?"); - switch (v->data) { - case AIN_VOID: return strdup("void"); - case AIN_INT: return strdup("int"); - case AIN_FLOAT: return strdup("float"); - case AIN_STRING: return strdup("string"); - case AIN_STRUCT: - if (v->struc == -1 || !ain) - return strdup("struct"); - return strdup(ain->structures[v->struc].name); - case AIN_ARRAY_INT: return array_type_string("array@int", v->rank); - case AIN_ARRAY_FLOAT: return array_type_string("array@float", v->rank); - case AIN_ARRAY_STRING: return array_type_string("array@string", v->rank); - case AIN_ARRAY_STRUCT: - if (v->struc == -1 || !ain) - return array_type_string("array@struct", v->rank); - snprintf(buf, 1024, "array@%s", ain->structures[v->struc].name); - return array_type_string(buf, v->rank); - case AIN_REF_INT: return strdup("ref int"); - case AIN_REF_FLOAT: return strdup("ref float"); - case AIN_REF_STRING: return strdup("ref string"); - case AIN_REF_STRUCT: - if (v->struc == -1 || !ain) - return strdup("ref struct"); - return type_sprintf("ref %s", ain->structures[v->struc].name); - case AIN_REF_ARRAY_INT: return array_type_string("ref array@int", v->rank); - case AIN_REF_ARRAY_FLOAT: return array_type_string("ref array@float", v->rank); - case AIN_REF_ARRAY_STRING: return array_type_string("ref array@string", v->rank); - case AIN_REF_ARRAY_STRUCT: - if (v->struc == -1 || !ain) - return strdup("ref array@struct"); - snprintf(buf, 1024, "ref array@%s", ain->structures[v->struc].name); - return array_type_string(buf, v->rank); - case AIN_IMAIN_SYSTEM: return strdup("imain_system"); - case AIN_FUNC_TYPE: return strdup("functype"); - case AIN_ARRAY_FUNC_TYPE: return array_type_string("array@functype", v->rank); - case AIN_REF_FUNC_TYPE: return strdup("ref functype"); - case AIN_REF_ARRAY_FUNC_TYPE: return array_type_string("ref array@functype", v->rank); - case AIN_BOOL: return strdup("bool"); - case AIN_ARRAY_BOOL: return array_type_string("array@bool", v->rank); - case AIN_REF_BOOL: return strdup("ref bool"); - case AIN_REF_ARRAY_BOOL: return array_type_string("ref array@bool", v->rank); - case AIN_LONG_INT: return strdup("lint"); - case AIN_ARRAY_LONG_INT: return array_type_string("array@lint", v->rank); - case AIN_REF_LONG_INT: return strdup("ref lint"); - case AIN_REF_ARRAY_LONG_INT: return array_type_string("ref array@lint", v->rank); - case AIN_DELEGATE: return strdup("delegate"); - case AIN_ARRAY_DELEGATE: return array_type_string("array@delegate", v->rank); - case AIN_REF_ARRAY_DELEGATE: return array_type_string("ref array@delegate", v->rank); - case AIN_UNKNOWN_TYPE_67: return strdup("type_67"); - case AIN_UNKNOWN_TYPE_74: return strdup("type_74"); - case AIN_UNKNOWN_TYPE_75: return strdup("type_75"); - case AIN_ARRAY: - case AIN_REF_ARRAY: - case AIN_ITERATOR: - return container_type_string(ain, v); - case AIN_ENUM1: - if (!v->array_type || v->array_type->struc == -1 || !ain || v->array_type->struc >= ain->nr_enums) - return strdup("enum"); - return type_sprintf("%s#86", ain->enums[v->array_type->struc].name); - case AIN_UNKNOWN_TYPE_87: return strdup("type_87"); - case AIN_IFACE: - if (v->struc == -1 || !ain) - return strdup("interface"); - return strdup(ain->structures[v->struc].name); - case AIN_ENUM2: - case AIN_ENUM3: - if (v->struc == -1 || !ain || v->struc >= ain->nr_enums) - return strdup("enum"); - return type_sprintf("%s#%d", ain->enums[v->struc].name, v->data); - case AIN_REF_ENUM: - if (v->struc == -1 || !ain || v->struc >= ain->nr_enums) - return strdup("enum"); - return type_sprintf("ref %s", ain->enums[v->struc].name); - case AIN_UNKNOWN_TYPE_95: return strdup("type_95"); - case AIN_UNKNOWN_TYPE_100: return strdup("type_100"); - default: - WARNING("Unknown type: %d", v->data); - return type_sprintf("unknown_type_%d", v->data); - } -} - -const char *ain_variable_to_string(struct ain *ain, struct ain_variable *v) -{ - static char buf[2048] = { [2047] = '\0' }; - char *type = ain_strtype_d(ain, &v->type); - int i = snprintf(buf, 2047, "%s %s", type, v->name); - free(type); - - if (v->has_initval) { - switch (v->type.data) { - case AIN_STRING: { - // FIXME: string needs escaping - snprintf(buf+i, 2047-i, " = \"%s\"", v->initval.s); - break; - } - case AIN_DELEGATE: - case AIN_REF_TYPE: - break; - case AIN_FLOAT: - snprintf(buf+i, 2047-i, " = %f", v->initval.f); - break; - default: - snprintf(buf+i, 2047-i, " = %d", v->initval.i); - break; - } - } - return buf; -} - -struct ain_reader { - uint8_t *buf; - size_t size; - size_t index; - struct ain_section *section; - struct ain *ain; -}; - -static int32_t read_int32(struct ain_reader *r) -{ - int32_t v = LittleEndian_getDW(r->buf, r->index); - r->index += 4; - return v; -} - -static uint8_t *read_bytes(struct ain_reader *r, size_t len) -{ - uint8_t *bytes = xmalloc(len); - memcpy(bytes, r->buf + r->index, len); - r->index += len; - return bytes; -} - -static char *read_string(struct ain_reader *r) -{ - char *str = strdup((char*)r->buf + r->index); - r->index += strlen(str) + 1; - return str; -} - -// TODO: should just memcpy the whole section and create pointers into it -// e.g. char *_messages; // raw AIN section -// char **messages; // array of pointers into _messages -static char **read_strings(struct ain_reader *r, int count) -{ - char **strings = calloc(count, sizeof(char*)); - for (int i = 0; i < count; i++) { - strings[i] = read_string(r); - } - return strings; -} - -static struct string *read_vm_string(struct ain_reader *r) -{ - size_t len = strlen((char*)r->buf + r->index); - struct string *s = make_string((char*)r->buf + r->index, len); - s->cow = true; - r->index += len + 1; - return s; -} - -static struct string **read_vm_strings(struct ain_reader *r, int count) -{ - struct string **strings = calloc(count, sizeof(struct string*)); - for (int i = 0; i < count; i++) { - strings[i] = read_vm_string(r); - } - return strings; -} - -static struct string *read_msg1_string(struct ain_reader *r) -{ - int32_t len = read_int32(r); - struct string *s = make_string((char*)r->buf + r->index, len); - s->cow = true; - r->index += len; - - // why... - for (int i = 0; i < len; i++) { - s->text[i] -= (uint8_t)i; - s->text[i] -= 0x60; - } - - return s; -} - -static struct string **read_msg1_strings(struct ain_reader *r, int count) -{ - struct string **strings = calloc(count, sizeof(struct string*)); - for (int i = 0; i < count; i++) { - strings[i] = read_msg1_string(r); - } - return strings; -} - -static void read_variable_type(struct ain_reader *r, struct ain_type *t); - -static struct ain_type *read_array_type(struct ain_reader *r, int rank) -{ - if (rank <= 0) - ERROR("Invalid array rank: %d", rank); - - struct ain_type *type = xcalloc(rank, sizeof(struct ain_type)); - for (int i = 0; i < rank; i++) { - read_variable_type(r, type+i); - } - - return type; -} - -static void read_variable_initval(struct ain_reader *r, struct ain_variable *v) -{ - if ((v->has_initval = read_int32(r))) { - if (v->has_initval != 1) - ERROR("variable->has_initval is not boolean? %d (at %p)", v->has_initval, r->index - 4); - switch (v->type.data) { - case AIN_STRING: - v->initval.s = read_string(r); - break; - case AIN_STRUCT: - case AIN_DELEGATE: - case AIN_REF_TYPE: - case AIN_ARRAY: - break; - default: - v->initval.i = read_int32(r); - } - } -} - -static void read_variable_type(struct ain_reader *r, struct ain_type *t) -{ - t->data = read_int32(r); - t->struc = read_int32(r); - t->rank = read_int32(r); - - if (ain_is_array_data_type(t->data)) - t->array_type = read_array_type(r, t->rank); -} - -static struct ain_variable *read_variables(struct ain_reader *r, int count, struct ain *ain, enum ain_variable_type var_type) -{ - struct ain_variable *variables = calloc(count, sizeof(struct ain_variable)); - for (int i = 0; i < count; i++) { - struct ain_variable *v = &variables[i]; - v->var_type = var_type; - v->name = read_string(r); - if (AIN_VERSION_GTE(ain, 12, 0)) - v->name2 = read_string(r); // ??? - read_variable_type(r, &v->type); - if (AIN_VERSION_GTE(ain, 8, 0)) - read_variable_initval(r, v); - } - return variables; -} - -static void read_return_type(struct ain_reader *r, struct ain_type *t, struct ain *ain) -{ - if (AIN_VERSION_GTE(ain, 11, 0)) { - read_variable_type(r, t); - return; - } - - t->data = read_int32(r); - t->struc = read_int32(r); -} - -static struct ain_function *read_functions(struct ain_reader *r, int count, struct ain *ain) -{ - struct ain_function *funs = calloc(count, sizeof(struct ain_function)); - for (int i = 0; i < count; i++) { - funs[i].address = read_int32(r); - funs[i].name = read_string(r); - if (!strcmp(funs[i].name, "0")) - ain->alloc = i; - - if (ain->version > 0 && ain->version < 7) - funs[i].is_label = read_int32(r); - - read_return_type(r, &funs[i].return_type, ain); - - funs[i].nr_args = read_int32(r); - funs[i].nr_vars = read_int32(r); - - if (AIN_VERSION_GTE(ain, 11, 0)) { - funs[i].is_lambda = read_int32(r); // known values: 0, 1 - if (funs[i].is_lambda && funs[i].is_lambda != 1) { - ERROR("function->is_lambda is not a boolean? %d (at %p)", funs[i].is_lambda, r->index - 4); - } - } - - if (ain->version > 0) { - funs[i].crc = read_int32(r); - } - if (funs[i].nr_vars > 0) { - funs[i].vars = read_variables(r, funs[i].nr_vars, ain, AIN_VAR_LOCAL); - } - } - return funs; -} - -static struct ain_variable *read_globals(struct ain_reader *r, int count, struct ain *ain) -{ - struct ain_variable *globals = calloc(count, sizeof(struct ain_variable)); - for (int i = 0; i < count; i++) { - globals[i].name = read_string(r); - if (AIN_VERSION_GTE(ain, 12, 0)) - globals[i].name2 = read_string(r); - read_variable_type(r, &globals[i].type); - if (AIN_VERSION_GTE(ain, 5, 0)) - globals[i].group_index = read_int32(r); - globals[i].var_type = AIN_VAR_GLOBAL; - } - return globals; -} - -static struct ain_initval *read_initvals(struct ain_reader *r, int count) -{ - struct ain_initval *values = calloc(count, sizeof(struct ain_initval)); - for (int i = 0; i < count; i++) { - values[i].global_index = read_int32(r); - values[i].data_type = read_int32(r); - if (values[i].data_type == AIN_STRING) { - values[i].string_value = read_string(r); - } else { - values[i].int_value = read_int32(r); - } - } - return values; -} - -static struct ain_struct *read_structures(struct ain_reader *r, int count, struct ain *ain) -{ - struct ain_struct *structures = calloc(count, sizeof(struct ain_struct)); - for (int i = 0; i < count; i++) { - structures[i].name = read_string(r); - if (AIN_VERSION_GTE(ain, 11, 0)) { - structures[i].nr_interfaces = read_int32(r); - structures[i].interfaces = xcalloc(structures[i].nr_interfaces, sizeof(struct ain_interface)); - for (int j = 0; j < structures[i].nr_interfaces; j++) { - structures[i].interfaces[j].struct_type = read_int32(r); - structures[i].interfaces[j].uk = read_int32(r); - } - } - structures[i].constructor = read_int32(r); - structures[i].destructor = read_int32(r); - structures[i].nr_members = read_int32(r); - structures[i].members = read_variables(r, structures[i].nr_members, ain, AIN_VAR_MEMBER); - - // Staring with Hentai Labyrinth, there is a list of functions at the end. - // I believe this is a listing of the functions in the vtable. - if (AIN_VERSION_GTE(ain, 14, 1)) { - structures[i].nr_vmethods = read_int32(r); - structures[i].vmethods = xcalloc(structures[i].nr_vmethods, sizeof(int32_t)); - for (int j = 0; j < structures[i].nr_vmethods; j++) { - structures[i].vmethods[j] = read_int32(r); - } - } - } - return structures; -} - -static struct ain_hll_argument *read_hll_arguments(struct ain_reader *r, int count) -{ - struct ain_hll_argument *arguments = calloc(count, sizeof(struct ain_hll_argument)); - for (int i = 0; i < count; i++) { - arguments[i].name = read_string(r); - if (AIN_VERSION_GTE(r->ain, 14, 0)) { - read_variable_type(r, &arguments[i].type); - } else { - arguments[i].type.data = read_int32(r); - arguments[i].type.struc = -1; - arguments[i].type.rank = 0; - } - } - return arguments; -} - -static struct ain_hll_function *read_hll_functions(struct ain_reader *r, int count) -{ - struct ain_hll_function *functions = calloc(count, sizeof(struct ain_hll_function)); - for (int i = 0; i < count; i++) { - functions[i].name = read_string(r); - if (AIN_VERSION_GTE(r->ain, 14, 0)) { - read_variable_type(r, &functions[i].return_type); - } else { - functions[i].return_type.data = read_int32(r); - functions[i].return_type.struc = -1; - functions[i].return_type.rank = 0; - } - functions[i].nr_arguments = read_int32(r); - if (functions[i].nr_arguments > 100 || functions[i].nr_arguments < 0) - ERROR("TOO MANY ARGUMENTS (AT 0x%x)", r->index); - functions[i].arguments = read_hll_arguments(r, functions[i].nr_arguments); - } - return functions; -} - -static struct ain_library *read_libraries(struct ain_reader *r, int count) -{ - struct ain_library *libraries = calloc(count, sizeof(struct ain_library)); - for (int i = 0; i < count; i++) { - libraries[i].name = read_string(r); - libraries[i].nr_functions = read_int32(r); - libraries[i].functions = read_hll_functions(r, libraries[i].nr_functions); - } - return libraries; -} - -static struct ain_switch_case *read_switch_cases(struct ain_reader *r, int count, struct ain_switch *parent) -{ - struct ain_switch_case *cases = calloc(count, sizeof(struct ain_switch)); - for (int i = 0; i < count; i++) { - cases[i].value = read_int32(r); - cases[i].address = read_int32(r); - cases[i].parent = parent; - } - return cases; -} - -static struct ain_switch *read_switches(struct ain_reader *r, int count) -{ - struct ain_switch *switches = calloc(count, sizeof(struct ain_switch)); - for (int i = 0; i < count; i++) { - switches[i].case_type = read_int32(r); - switches[i].default_address = read_int32(r); - switches[i].nr_cases = read_int32(r); - switches[i].cases = read_switch_cases(r, switches[i].nr_cases, &switches[i]); - } - return switches; -} - -static struct ain_function_type *read_function_types(struct ain_reader *r, int count, struct ain *ain) -{ - struct ain_function_type *types = calloc(count, sizeof(struct ain_function_type)); - for (int i = 0; i < count; i++) { - types[i].name = read_string(r); - read_return_type(r, &types[i].return_type, ain); - types[i].nr_arguments = read_int32(r); - types[i].nr_variables = read_int32(r); - types[i].variables = read_variables(r, types[i].nr_variables, ain, AIN_VAR_LOCAL); - } - return types; -} - -#define for_each_instruction(ain, addr, instr, start, user_code) \ - do { \ - const struct instruction *instr; \ - for (size_t addr = start; addr < ain->code_size; addr += instruction_width(instr->opcode)) { \ - uint16_t _fei_opcode = LittleEndian_getW(ain->code, addr); \ - if (_fei_opcode >= NR_OPCODES) \ - ERROR("Unknown/invalid opcode: %u", _fei_opcode); \ - instr = &instructions[_fei_opcode]; \ - if (addr + instr->nr_args * 4 >= ain->code_size) \ - ERROR("CODE section truncated?"); \ - user_code; \ - } \ - } while (0) - -struct ain_enum *read_enums(struct ain_reader *r, int count, struct ain *ain) -{ - char **names = read_strings(r, count); - struct ain_enum *enums = xcalloc(count, sizeof(struct ain_enum)); - - for (int i = 0; i < count; i++) { - enums[i].name = names[i]; - - char buf[1024] = { [1023] = '\0' }; - snprintf(buf, 1023, ain->version < 14 ? "%s@String" : "%s::ToString", names[i]); - - struct func_list *funs = get_function(ain, buf); - if (!funs || funs->nr_slots != 1) { - WARNING("Failed to parse enum: %s", names[i]); - continue; - } - - int j = 0; - for_each_instruction(ain, addr, instr, ain->functions[funs->slots[0]].address, { - if (instr->opcode == ENDFUNC) - break; - if (instr->opcode != S_PUSH) - continue; - - int32_t strno = LittleEndian_getDW(ain->code, addr + 2); - if (strno < 0 || strno >= ain->nr_strings) { - WARNING("Encountered invalid string number when parsing enums"); - continue; - } - if (!ain->strings[strno]->size) - continue; - - enums[i].symbols = xrealloc(enums[i].symbols, sizeof(char*) * (j+1)); - enums[i].symbols[j] = ain->strings[strno]->text; - enums[i].nr_symbols = j + 1; - j++; - }); - } - - free(names); - return enums; -} - -static void start_section(struct ain_reader *r, struct ain_section *section) -{ - if (r->section) - r->section->size = r->index - r->section->addr; - r->section = section; - if (section) { - r->section->addr = r->index; - r->section->present = true; - r->index += 4; - } -} - -static void detect_quirks(struct ain *ain) -{ - char *tmp = strdup(ain->ain_path); - char *name = basename(tmp); - - if (ain->version == 14) { - if (!strncasecmp(name, "HentaiLabyrinth.ain", 19)) - ain->minor_version = 1; - } - - free(tmp); -} - -static bool read_tag(struct ain_reader *r, struct ain *ain) -{ - if (r->index + 4 >= r->size) { - start_section(r, NULL); - return false; - } - - uint8_t *tag_loc = r->buf + r->index; - -#define TAG_EQ(tag) !strncmp((char*)tag_loc, tag, 4) - // FIXME: need to check len or could segfault on corrupt AIN file - if (TAG_EQ("VERS")) { - start_section(r, &ain->VERS); - ain->version = read_int32(r); - if (AIN_VERSION_GTE(ain, 11, 0)) { - instructions[CALLHLL].nr_args = 3; - instructions[NEW].nr_args = 2; - instructions[S_MOD].nr_args = 1; - instructions[OBJSWAP].nr_args = 1; - instructions[DG_STR_TO_METHOD].nr_args = 1; - instructions[CALLMETHOD].args[0] = T_INT; - } - detect_quirks(ain); - } else if (TAG_EQ("KEYC")) { - start_section(r, &ain->KEYC); - ain->keycode = read_int32(r); - } else if (TAG_EQ("CODE")) { - start_section(r, &ain->CODE); - ain->code_size = read_int32(r); - ain->code = read_bytes(r, ain->code_size); - } else if (TAG_EQ("FUNC")) { - start_section(r, &ain->FUNC); - ain->nr_functions = read_int32(r); - ain->functions = read_functions(r, ain->nr_functions, ain); - init_func_ht(ain); - } else if (TAG_EQ("GLOB")) { - start_section(r, &ain->GLOB); - ain->nr_globals = read_int32(r); - ain->globals = read_globals(r, ain->nr_globals, ain); - } else if (TAG_EQ("GSET")) { - start_section(r, &ain->GSET); - ain->nr_initvals = read_int32(r); - ain->global_initvals = read_initvals(r, ain->nr_initvals); - } else if (TAG_EQ("STRT")) { - start_section(r, &ain->STRT); - ain->nr_structures = read_int32(r); - ain->structures = read_structures(r, ain->nr_structures, ain); - init_struct_ht(ain); - } else if (TAG_EQ("MSG0")) { - start_section(r, &ain->MSG0); - ain->nr_messages = read_int32(r); - ain->messages = read_vm_strings(r, ain->nr_messages); - } else if (TAG_EQ("MSG1")) { - start_section(r, &ain->MSG1); - ain->nr_messages = read_int32(r); - ain->msg1_uk = read_int32(r); // ??? - ain->messages = read_msg1_strings(r, ain->nr_messages); - } else if (TAG_EQ("MAIN")) { - start_section(r, &ain->MAIN); - ain->main = read_int32(r); - } else if (TAG_EQ("MSGF")) { - start_section(r, &ain->MSGF); - ain->msgf = read_int32(r); - } else if (TAG_EQ("HLL0")) { - start_section(r, &ain->HLL0); - ain->nr_libraries = read_int32(r); - ain->libraries = read_libraries(r, ain->nr_libraries); - } else if (TAG_EQ("SWI0")) { - start_section(r, &ain->SWI0); - ain->nr_switches = read_int32(r); - ain->switches = read_switches(r, ain->nr_switches); - } else if (TAG_EQ("GVER")) { - start_section(r, &ain->GVER); - ain->game_version = read_int32(r); - } else if (TAG_EQ("STR0")) { - start_section(r, &ain->STR0); - ain->nr_strings = read_int32(r); - ain->strings = read_vm_strings(r, ain->nr_strings); - } else if (TAG_EQ("FNAM")) { - start_section(r, &ain->FNAM); - ain->nr_filenames = read_int32(r); - ain->filenames = read_strings(r, ain->nr_filenames); - } else if (TAG_EQ("OJMP")) { - start_section(r, &ain->OJMP); - ain->ojmp = read_int32(r); - } else if (TAG_EQ("FNCT")) { - start_section(r, &ain->FNCT); - ain->fnct_size = read_int32(r); - ain->nr_function_types = read_int32(r); - ain->function_types = read_function_types(r, ain->nr_function_types, ain); - } else if (TAG_EQ("DELG")) { - start_section(r, &ain->DELG); - ain->delg_size = read_int32(r); - ain->nr_delegates = read_int32(r); - ain->delegates = read_function_types(r, ain->nr_delegates, ain); - } else if (TAG_EQ("OBJG")) { - start_section(r, &ain->OBJG); - ain->nr_global_groups = read_int32(r); - ain->global_group_names = read_strings(r, ain->nr_global_groups); - } else if (TAG_EQ("ENUM")) { - start_section(r, &ain->ENUM); - ain->ENUM.present = true; - ain->nr_enums = read_int32(r); - ain->enums = read_enums(r, ain->nr_enums, ain); - } else { - start_section(r, NULL); - WARNING("Junk at end of AIN file?"); - return false; - } -#undef TAG_EQ - - return true; -} - -static void distribute_initvals(struct ain *ain) -{ - for (int i = 0; i < ain->nr_initvals; i++) { - struct ain_variable *g = &ain->globals[ain->global_initvals[i].global_index]; - g->has_initval = true; - if (ain->global_initvals[i].data_type == AIN_STRING) - g->initval.s = ain->global_initvals[i].string_value; - else - g->initval.i = ain->global_initvals[i].int_value; - } -} - -static uint8_t *decompress_ain(uint8_t *in, long *len) -{ - uint8_t *out; - long out_len = LittleEndian_getDW(in, 8); - long in_len = LittleEndian_getDW(in, 12); - - if (out_len < 0 || in_len < 0) - return NULL; - - out = xmalloc(out_len); - int r = uncompress(out, (unsigned long*)&out_len, in+16, in_len); - if (r != Z_OK) { - if (r == Z_BUF_ERROR) - WARNING("uncompress failed: Z_BUF_ERROR"); - else if (r == Z_MEM_ERROR) - WARNING("uncompress failed: Z_MEM_ERROR"); - else if (r == Z_DATA_ERROR) - WARNING("uncompress failed: Z_DATA_ERROR"); - free(out); - return NULL; - } - - *len = out_len; - return out; -} - -static uint32_t temper(uint32_t in) -{ - in ^= (in >> 11); - in ^= (in << 7) & 0x9D2C5680; - in ^= (in << 15) & 0xEFC60000; - in ^= (in >> 18); - return in; -} - -static void update(uint32_t *state) -{ - int ahead = 0x18D; - int write = 0; - int read = 2; - uint32_t current = state[1]; - uint32_t last = state[0]; - uint32_t tmp; - - for (int i = 0; i < 0x270; i++) { - tmp = (((current ^ last) & 0x7FFFFFFE) ^ last) >> 1; - tmp = (current & 1) ? (tmp ^ 0x9908B0DF) : tmp; - state[write] = tmp ^ state[ahead]; - last = current; - current = state[read]; - write++; - read++; - ahead++; - while (read >= 0x270) - read -= 0x270; - while (ahead >= 0x270) - ahead -= 0x270; - } -} - -void ain_decrypt(uint8_t *buf, size_t len) -{ - uint32_t state[0x270]; - uint32_t key = 0x5D3E3; - int off = 0; - - for (int i = 0; i < 0x270; i++) { - state[i] = key; - key *= 0x10DCD; - } - - update(state); - for (size_t i = 0; i < len; i++) { - if (off >= 0x270) { - update(state); - off = 0; - } - buf[i] ^= (uint8_t)(temper(state[off]) & 0xFF); - off++; - } -} - -static bool ain_is_encrypted(uint8_t *buf) -{ - uint8_t magic[8]; - - memcpy(magic, buf, 8); - ain_decrypt(magic, 8); - - return !strncmp((char*)magic, "VERS", 4) && !magic[5] && !magic[6] && !magic[7]; -} - -uint8_t *ain_read(const char *path, long *len, int *error) -{ - FILE *fp; - uint8_t *buf = NULL; - - if (!(fp = fopen(path, "rb"))) { - *error = AIN_FILE_ERROR; - goto err; - } - - // get size of AIN file - fseek(fp, 0, SEEK_END); - *len = ftell(fp); - fseek(fp, 0, SEEK_SET); - - // read AIN file into memory - buf = xmalloc(*len + 4); // why +4? - fread(buf, 1, *len, fp); - fclose(fp); - - // check magic - if (!strncmp((char*)buf, "AI2\0\0\0\0", 8)) { - uint8_t *uc = decompress_ain(buf, len); - if (!uc) { - *error = AIN_INVALID; - goto err; - } - free(buf); - buf = uc; - } else if (ain_is_encrypted(buf)) { - ain_decrypt(buf, *len); - } else { - printf("%.4s\n", buf); - *error = AIN_UNRECOGNIZED_FORMAT; - goto err; - } - - return buf; -err: - free(buf); - return NULL; -} - -struct ain *ain_open(const char *path, int *error) -{ - long len; - struct ain *ain = NULL; - uint8_t *buf = ain_read(path, &len, error); - if (!buf) - goto err; - - // read data into ain struct - ain = xcalloc(1, sizeof(struct ain)); - ain->ain_path = xstrdup(path); - struct ain_reader r = { - .buf = buf, - .index = 0, - .size = len, - .ain = ain - }; - while (read_tag(&r, ain)); - if (!ain->version) { - *error = AIN_INVALID; - goto err; - } - distribute_initvals(ain); - - free(buf); - *error = AIN_SUCCESS; - return ain; -err: - free(buf); - free(ain); - return NULL; -} - -struct ain *ain_new(int version) -{ - struct ain *ain = xcalloc(1, sizeof(struct ain)); - ain->VERS.present = true; - ain->KEYC.present = version < 12; - ain->CODE.present = true; - ain->FUNC.present = true; - ain->GLOB.present = true; - ain->GSET.present = version < 12; - ain->STRT.present = true; - // XXX: Starting with Rance IX, MSG1 section is used instead of MSG0. - // We can't tell from the ain version alone whether MSG0 or MSG1 - // should be used since the ain version did not increase with - // this change, but v7+ at least should always use MSG1. - ain->MSG0.present = ain->version < 7; - ain->MSG1.present = !ain->MSG0.present; - ain->MAIN.present = true; - ain->MSGF.present = true; - ain->HLL0.present = true; - ain->SWI0.present = true; - ain->GVER.present = true; - ain->STR0.present = true; - ain->FNAM.present = ain->version < 12; - ain->OJMP.present = ain->version < 7; - // XXX: Another change starting from Rance IX: FNCT section disappears. - // Maybe they just stopped using function types, but still support - // them in the VM? Will need to change this flag when function - // types are added to the ain file. - ain->FNCT.present = ain->version < 7; - // XXX: Starting with Oyako Rankan... earlier v6 games don't have DELG. - // Will need to change this flag when delegates are added to the - // ain file. - ain->DELG.present = ain->version >= 7; - ain->OBJG.present = ain->version >= 5; - ain->ENUM.present = ain->version >= 12; - - ain->version = version; - ain->main = -1; - ain->msgf = -1; - ain->ojmp = -1; - ain->game_version = 100; - - ain->nr_functions = 1; - ain->functions = xcalloc(1, sizeof(struct ain_function)); - ain->functions[0].name = strdup("NULL"); - ain->functions[0].return_type = (struct ain_type) { - .data = AIN_VOID, - .struc = -1, - .rank = 0 - }; - - ain->nr_messages = 1; - ain->messages = xcalloc(1, sizeof(struct string*)); - ain->messages[0] = make_string("", 0); - - ain->nr_strings = 1; - ain->strings = xcalloc(1, sizeof(struct string *)); - ain->strings[0] = make_string("", 0); - - init_struct_ht(ain); - init_func_ht(ain); - - return ain; -} - -static void ain_free_type(struct ain_type *type) -{ - if (!type->array_type) - return; - - for (int i = 0; i < type->rank; i++) { - ain_free_type(&type->array_type[i]); - } - free(type->array_type); -} - -static void ain_free_variables(struct ain_variable *vars, int nr_vars) -{ - for (int i = 0; i < nr_vars; i++) { - free(vars[i].name); - free(vars[i].name2); - ain_free_type(&vars[i].type); - if (vars[i].has_initval && vars[i].type.data == AIN_STRING) - free(vars[i].initval.s); - } - free(vars); -} - -static void _ain_free_function_types(struct ain_function_type *funs, int n) -{ - for (int i = 0; i < n; i++) { - free(funs[i].name); - ain_free_type(&funs[i].return_type); - ain_free_variables(funs[i].variables, funs[i].nr_variables); - } - free(funs); -} - -static void ain_free_vmstrings(struct string **strings, int n) -{ - for (int i = 0; i < n; i++) { - free_string(strings[i]); - } - free(strings); -} - -static void ain_free_cstrings(char **strings, int n) -{ - for (int i = 0; i < n; i++) { - free(strings[i]); - } - free(strings); -} - -void ain_free_functions(struct ain *ain) -{ - for (int f = 0; f < ain->nr_functions; f++) { - free(ain->functions[f].name); - ain_free_type(&ain->functions[f].return_type); - ain_free_variables(ain->functions[f].vars, ain->functions[f].nr_vars); - } - free(ain->functions); - ain->functions = NULL; - ain->nr_functions = 0; -} - -void ain_free_globals(struct ain *ain) -{ - ain_free_variables(ain->globals, ain->nr_globals); - ain->globals = NULL; - ain->nr_globals = 0; -} - -void ain_free_initvals(struct ain *ain) -{ - free(ain->global_initvals); - ain->global_initvals = NULL; - ain->nr_initvals = 0; -} - -void ain_free_structures(struct ain *ain) -{ - for (int s = 0; s < ain->nr_structures; s++) { - free(ain->structures[s].name); - free(ain->structures[s].interfaces); - free(ain->structures[s].vmethods); - ain_free_variables(ain->structures[s].members, ain->structures[s].nr_members); - } - free(ain->structures); - ain->structures = NULL; - ain->nr_structures = 0; -} - -void ain_free_messages(struct ain *ain) -{ - ain_free_vmstrings(ain->messages, ain->nr_messages); - ain->messages = NULL; - ain->nr_messages = 0; -} - -void ain_free_libraries(struct ain *ain) -{ - for (int lib = 0; lib < ain->nr_libraries; lib++) { - free(ain->libraries[lib].name); - for (int f = 0; f < ain->libraries[lib].nr_functions; f++) { - free(ain->libraries[lib].functions[f].name); - ain_free_type(&ain->libraries[lib].functions[f].return_type); - for (int a = 0; a < ain->libraries[lib].functions[f].nr_arguments; a++) { - free(ain->libraries[lib].functions[f].arguments[a].name); - ain_free_type(&ain->libraries[lib].functions[f].arguments[a].type); - } - free(ain->libraries[lib].functions[f].arguments); - } - free(ain->libraries[lib].functions); - } - free(ain->libraries); - ain->libraries = NULL; - ain->nr_libraries = 0; -} - -void ain_free_switches(struct ain *ain) -{ - for (int i = 0; i < ain->nr_switches; i++) { - free(ain->switches[i].cases); - } - free(ain->switches); - ain->switches = NULL; - ain->nr_switches = 0; -} - -void ain_free_strings(struct ain *ain) -{ - ain_free_vmstrings(ain->strings, ain->nr_strings); - ain->strings = NULL; - ain->nr_strings = 0; -} - -void ain_free_filenames(struct ain *ain) -{ - ain_free_cstrings(ain->filenames, ain->nr_filenames); - ain->filenames = NULL; - ain->nr_filenames = 0; -} - -void ain_free_function_types(struct ain *ain) -{ - _ain_free_function_types(ain->function_types, ain->nr_function_types); - ain->function_types = NULL; - ain->nr_function_types = 0; -} - -void ain_free_delegates(struct ain *ain) -{ - _ain_free_function_types(ain->delegates, ain->nr_delegates); - ain->delegates = NULL; - ain->nr_delegates = 0; -} - -void ain_free_global_groups(struct ain *ain) -{ - ain_free_cstrings(ain->global_group_names, ain->nr_global_groups); - ain->global_group_names = NULL; - ain->nr_global_groups = 0; -} - -void ain_free_enums(struct ain *ain) -{ - for (int i = 0; i < ain->nr_enums; i++) { - free(ain->enums[i].name); - free(ain->enums[i].symbols); - } - free(ain->enums); - ain->enums = NULL; - ain->nr_enums = 0; -} - -void ain_free(struct ain *ain) -{ - free(ain->ain_path); - free(ain->code); - - ht_foreach_value(ain->_func_ht, free); - ht_free(ain->_func_ht); - ht_free(ain->_struct_ht); - if (ain->_string_ht) - ht_free(ain->_string_ht); - - ain_free_functions(ain); - ain_free_globals(ain); - ain_free_initvals(ain); - ain_free_structures(ain); - ain_free_messages(ain); - ain_free_libraries(ain); - ain_free_switches(ain); - ain_free_strings(ain); - ain_free_filenames(ain); - ain_free_function_types(ain); - ain_free_delegates(ain); - ain_free_global_groups(ain); - ain_free_enums(ain); - - free(ain); -} diff --git a/src/ajp.c b/src/ajp.c deleted file mode 100644 index 5a9cfd0..0000000 --- a/src/ajp.c +++ /dev/null @@ -1,156 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#include -#include -#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); -} diff --git a/src/ald.c b/src/ald.c deleted file mode 100644 index b9ab719..0000000 --- a/src/ald.c +++ /dev/null @@ -1,402 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * Based on code from xsystem35 - * Copyright (C) 1997-1998 Masaki Chikama (Wren) - * 1998- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#include -#include -#include -#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; -} - diff --git a/src/archive.c b/src/archive.c deleted file mode 100644 index 2c62401..0000000 --- a/src/archive.c +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#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"; -} diff --git a/src/buffer.c b/src/buffer.c deleted file mode 100644 index 3ebf442..0000000 --- a/src/buffer.c +++ /dev/null @@ -1,153 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#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); -} diff --git a/src/cg.c b/src/cg.c deleted file mode 100644 index 7395f22..0000000 --- a/src/cg.c +++ /dev/null @@ -1,143 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#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; -} diff --git a/src/dcf.c b/src/dcf.c deleted file mode 100644 index f8dc44b..0000000 --- a/src/dcf.c +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#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); -} diff --git a/src/ex.c b/src/ex.c deleted file mode 100644 index 46af4ce..0000000 --- a/src/ex.c +++ /dev/null @@ -1,517 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#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); -} - diff --git a/src/file.c b/src/file.c deleted file mode 100644 index 25bb956..0000000 --- a/src/file.c +++ /dev/null @@ -1,135 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "system4.h" - -#if (defined(_WIN32) || defined(__WIN32__)) -#include -#include -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; -} diff --git a/src/flat.c b/src/flat.c deleted file mode 100644 index c1aecd6..0000000 --- a/src/flat.c +++ /dev/null @@ -1,330 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#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; -} diff --git a/src/fnl.c b/src/fnl.c deleted file mode 100644 index 818ac6d..0000000 --- a/src/fnl.c +++ /dev/null @@ -1,193 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#include -#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); -} diff --git a/src/hashtable.c b/src/hashtable.c deleted file mode 100644 index def464f..0000000 --- a/src/hashtable.c +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#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); -} diff --git a/src/ini.c b/src/ini.c deleted file mode 100644 index bcb6f13..0000000 --- a/src/ini.c +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#include -#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); -} diff --git a/src/ini_lexer.l b/src/ini_lexer.l deleted file mode 100644 index cac2850..0000000 --- a/src/ini_lexer.l +++ /dev/null @@ -1,76 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -%{ - -#pragma GCC diagnostic ignored "-Wunused-function" - -#include -#include -#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); - -{ - \" { - 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++; - } -} - -%% diff --git a/src/ini_parser.y b/src/ini_parser.y deleted file mode 100644 index 1ec0477..0000000 --- a/src/ini_parser.y +++ /dev/null @@ -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 -#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 TRUE FALSE FORMATION -%token INTEGER -%token STRING IDENTIFIER - -%type inifile -%type entries -%type entry -%type value -%type 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); } - ; - -%% diff --git a/src/instructions.c b/src/instructions.c deleted file mode 100644 index bb418c3..0000000 --- a/src/instructions.c +++ /dev/null @@ -1,403 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include - -#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. -} diff --git a/src/meson.build b/src/meson.build index 11e2a0b..dd03dae 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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') diff --git a/src/pms.c b/src/pms.c deleted file mode 100644 index 5493884..0000000 --- a/src/pms.c +++ /dev/null @@ -1,327 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * Based on code from xsystem35 - * Copyright (C) 1997-1998 Masaki Chikama (Wren) - * 1998- - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#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); -} diff --git a/src/qnt.c b/src/qnt.c deleted file mode 100644 index afdb04c..0000000 --- a/src/qnt.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * qnt.c extract QNT cg - * - * Copyright (C) 1997-1998 Masaki Chikama (Wren) - * 1998- - * 2019 Nunuhara Cabbage - * - * 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 -#include -#include -#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; -} diff --git a/src/string.c b/src/string.c deleted file mode 100644 index 1cbed63..0000000 --- a/src/string.c +++ /dev/null @@ -1,397 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include -#include -#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; -} diff --git a/src/system.c b/src/system.c deleted file mode 100644 index 1455903..0000000 --- a/src/system.c +++ /dev/null @@ -1,110 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include - -#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); -} diff --git a/src/tools/acx/meson.build b/src/tools/acx/meson.build index 2fafcf1..3d6cf72 100644 --- a/src/tools/acx/meson.build +++ b/src/tools/acx/meson.build @@ -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) diff --git a/src/tools/aincmp/meson.build b/src/tools/aincmp/meson.build index 5ba8751..0c98353 100644 --- a/src/tools/aincmp/meson.build +++ b/src/tools/aincmp/meson.build @@ -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) diff --git a/src/tools/aindump/meson.build b/src/tools/aindump/meson.build index 393dada..7182a40 100644 --- a/src/tools/aindump/meson.build +++ b/src/tools/aindump/meson.build @@ -10,5 +10,4 @@ aindump = ['../../cJSON.c', executable('aindump', aindump, dependencies : tool_deps, - include_directories : incdir, - link_with : libsys4) + include_directories : incdir) diff --git a/src/tools/ainedit/meson.build b/src/tools/ainedit/meson.build index e31c6d0..6f29a8d 100644 --- a/src/tools/ainedit/meson.build +++ b/src/tools/ainedit/meson.build @@ -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) diff --git a/src/tools/ar/meson.build b/src/tools/ar/meson.build index 06886b7..00eda2e 100644 --- a/src/tools/ar/meson.build +++ b/src/tools/ar/meson.build @@ -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) diff --git a/src/tools/ex/meson.build b/src/tools/ex/meson.build index cfd9c58..9c65451 100644 --- a/src/tools/ex/meson.build +++ b/src/tools/ex/meson.build @@ -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) diff --git a/src/tools/fnl/meson.build b/src/tools/fnl/meson.build index ff7080d..6daa7e4 100644 --- a/src/tools/fnl/meson.build +++ b/src/tools/fnl/meson.build @@ -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) diff --git a/src/tools/meson.build b/src/tools/meson.build index cc16344..27124be 100644 --- a/src/tools/meson.build +++ b/src/tools/meson.build @@ -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') diff --git a/src/utfsjis.c b/src/utfsjis.c deleted file mode 100644 index c182159..0000000 --- a/src/utfsjis.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * utfsjis.c -- utf-8/sjis related function - * - * Copyright (C) 1997 Yutaka OIWA - * - * written for Satoshi KURAMOCHI's "eplaymidi" - * - * - * 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 -#include -#include - -#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; -} diff --git a/src/webp.c b/src/webp.c deleted file mode 100644 index 93b2483..0000000 --- a/src/webp.c +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright (C) 2019 Nunuhara Cabbage - * - * 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 . - */ - -#include -#include -#include - -#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 -#include -#include -#include - -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); -} diff --git a/subprojects/libsys4 b/subprojects/libsys4 new file mode 160000 index 0000000..171f771 --- /dev/null +++ b/subprojects/libsys4 @@ -0,0 +1 @@ +Subproject commit 171f771302ac09a3092aa90cf29c355f49bed90f