From 89c9c9128efb855a4e699f5673fb4dcd859b2695 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Fri, 19 Feb 2021 21:51:38 +0900 Subject: [PATCH] Implement DataFile HLL library It provides access to data tables in *DA.ald, and is used in Big Bang Age. mt19937int.c is the first standard version of Mersenne Twister, downloaded from http://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/VERSIONS/C-LANG/ver980409.html. --- include/mt19937int.h | 23 +++ src/ffi.c | 2 + src/hll/DataFile.c | 409 +++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 2 + src/mt19937int.c | 120 +++++++++++++ src/system4.c | 7 + 6 files changed, 563 insertions(+) create mode 100644 include/mt19937int.h create mode 100644 src/hll/DataFile.c create mode 100644 src/mt19937int.c diff --git a/include/mt19937int.h b/include/mt19937int.h new file mode 100644 index 0000000..9552dfa --- /dev/null +++ b/include/mt19937int.h @@ -0,0 +1,23 @@ +/* Copyright (C) 2021 kichikuou + * + * 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 . + */ + +#ifndef SYSTEM4_MT19937INT_H +#define SYSTEM4_MT19937INT_H + +void sgenrand(unsigned long seed); +unsigned long genrand(void); + +#endif /* SYSTEM4_MT19937INT_H */ diff --git a/src/ffi.c b/src/ffi.c index 385d507..9d51081 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -137,6 +137,7 @@ extern struct static_library lib_AliceLogo; extern struct static_library lib_AliceLogo2; extern struct static_library lib_AliceLogo3; extern struct static_library lib_Confirm2; +extern struct static_library lib_DataFile; extern struct static_library lib_DrawGraph; extern struct static_library lib_DrawPluginManager; extern struct static_library lib_File; @@ -158,6 +159,7 @@ static struct static_library *static_libraries[] = { &lib_AliceLogo2, &lib_AliceLogo3, &lib_Confirm2, + &lib_DataFile, &lib_DrawGraph, &lib_DrawPluginManager, &lib_File, diff --git a/src/hll/DataFile.c b/src/hll/DataFile.c new file mode 100644 index 0000000..3e571e0 --- /dev/null +++ b/src/hll/DataFile.c @@ -0,0 +1,409 @@ +/* Copyright (C) 2021 kichikuou + * + * 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/buffer.h" +#include "system4/string.h" + +#include "hll.h" +#include "mt19937int.h" +#include "xsystem4.h" + +enum value_type { + GDAT_INT = 0, + GDAT_STRING = 2, + GDAT_FLOAT = 3 +}; + +struct gdat_col { + enum value_type type; + uint32_t index; +}; + +struct gdat_row { + struct gdat_col *cols; + uint32_t nr_cols; +}; + +struct gdat_table { + struct gdat_row *rows; + uint32_t nr_rows; +}; + +struct gdat { + char **labels; + uint32_t nr_labels; + struct gdat_table *tables; + uint32_t nr_tables; + int32_t *ints; + uint32_t nr_ints; + float *floats; + uint32_t nr_floats; + struct string **strings; + uint32_t nr_strings; +}; + +static struct gdat *current_gdat; + +static void gdat_free(struct gdat *dat) +{ + if (!dat) + return; + if (dat->labels) { + for (uint32_t i = 0; i < dat->nr_labels; i++) + free(dat->labels[i]); + free(dat->labels); + } + if (dat->tables) { + for (uint32_t i = 0; i < dat->nr_tables; i++) { + for (uint32_t j = 0; j < dat->tables[i].nr_rows; j++) { + free(dat->tables[i].rows[j].cols); + } + free(dat->tables[i].rows); + } + free(dat->tables); + } + if (dat->ints) + free(dat->ints); + if (dat->floats) + free(dat->floats); + if (dat->strings) { + for (uint32_t i = 0; i < dat->nr_strings; i++) { + if (dat->strings[i]) + free_string(dat->strings[i]); + } + free(dat->strings); + } + free(dat); +} + +static struct gdat *gdat_read(struct buffer *r) +{ + if (strncmp(buffer_strdata(r), "GDAT", 4)) { + WARNING("Missing GDAT section marker"); + return NULL; + } + buffer_skip(r, 4); + + int32_t version = buffer_read_int32(r); + if (version != 1) { + WARNING("Unknown GDAT version %d", version); + return NULL; + } + + struct gdat *dat = xcalloc(1, sizeof(struct gdat)); + + if (strncmp(buffer_strdata(r), "LABL", 4)) { + WARNING("Missing LABL section marker"); + gdat_free(dat); + return NULL; + } + buffer_skip(r, 4); + + dat->nr_labels = buffer_read_int32(r); + dat->labels = xcalloc(dat->nr_labels, sizeof(const char *)); + for (uint32_t i = 0; i < dat->nr_labels; i++) { + dat->labels[i] = xstrdup(buffer_strdata(r)); + buffer_skip(r, strlen(dat->labels[i]) + 1); + } + + if (strncmp(buffer_strdata(r), "DATA", 4)) { + WARNING("Missing DATA section marker"); + gdat_free(dat); + return NULL; + } + buffer_skip(r, 4); + + dat->nr_tables = buffer_read_int32(r); + dat->tables = xcalloc(dat->nr_tables, sizeof(struct gdat_table)); + for (uint32_t i = 0; i < dat->nr_tables; i++) { + struct gdat_table *tbl = &dat->tables[i]; + tbl->nr_rows = buffer_read_int32(r); + tbl->rows = xcalloc(tbl->nr_rows, sizeof(struct gdat_row)); + for (uint32_t j = 0; j < tbl->nr_rows; j++) { + struct gdat_row *row = &tbl->rows[j]; + row->nr_cols = buffer_read_int32(r); + row->cols = xcalloc(row->nr_cols, sizeof(struct gdat_col)); + for (uint32_t k = 0; k < row->nr_cols; k++) { + row->cols[k].type = buffer_read_int32(r); + row->cols[k].index = buffer_read_int32(r); + } + } + } + + if (strncmp(buffer_strdata(r), "INT_", 4) == 0) { + buffer_skip(r, 4); + dat->nr_ints = buffer_read_int32(r); + dat->ints = xmalloc(dat->nr_ints * sizeof(int32_t)); + for (uint32_t i = 0; i < dat->nr_ints; i++) + dat->ints[i] = buffer_read_int32(r); + } + + if (strncmp(buffer_strdata(r), "FLOT", 4) == 0) { + buffer_skip(r, 4); + dat->nr_floats = buffer_read_int32(r); + dat->floats = xmalloc(dat->nr_floats * sizeof(float)); + for (uint32_t i = 0; i < dat->nr_floats; i++) + dat->floats[i] = buffer_read_float(r); + } + + if (strncmp(buffer_strdata(r), "STRG", 4) == 0) { + buffer_skip(r, 4); + dat->nr_strings = buffer_read_int32(r); + dat->strings = xcalloc(dat->nr_strings, sizeof(struct string*)); + for (uint32_t i = 0; i < dat->nr_strings; i++) { + dat->strings[i] = cstr_to_string(buffer_strdata(r)); + buffer_skip(r, dat->strings[i]->size + 1); + } + } + + if (buffer_remaining(r)) { + WARNING("Unknown GDAT section header"); + gdat_free(dat); + return NULL; + } + return dat; +} + +static struct gdat_table *gdat_get_table(struct gdat *dat, int table) +{ + if (!dat || table < 0 || table >= dat->nr_tables) { + WARNING("Table index out of bounds: %d", table); + return NULL; + } + return &dat->tables[table]; +} + +static struct gdat_row *gdat_get_row(struct gdat *dat, int table, int row) +{ + struct gdat_table *tbl = gdat_get_table(dat, table); + if (!tbl) + return NULL; + if (row < 0 || row >= tbl->nr_rows) { + WARNING("Row index out of bounds: %d", row); + return NULL; + } + return &tbl->rows[row]; +} + +static struct gdat_col *gdat_get_column(struct gdat *dat, int table, int row, int col) +{ + struct gdat_row *r = gdat_get_row(dat, table, row); + if (!r) + return NULL; + if (col < 0 || col >= r->nr_cols) { + WARNING("Column index out of bounds: %d", col); + return NULL; + } + return &r->cols[col]; +} + +// for debug +possibly_unused static void gdat_dump_as_json(struct gdat *dat, FILE *fp) +{ + fputs("{\n", fp); + for (uint32_t i = 0; i < dat->nr_tables; i++) { + fprintf(fp, " \"%s\": [\n", dat->labels[i]); + struct gdat_table *tbl = &dat->tables[i]; + for (uint32_t j = 0; j < tbl->nr_rows; j++) { + fputs(" [", fp); + struct gdat_row *row = &tbl->rows[j]; + for (uint32_t k = 0; k < row->nr_cols; k++) { + struct gdat_col *col = &row->cols[k]; + switch (col->type) { + case GDAT_INT: + fprintf(fp, "%d", dat->ints[col->index]); + break; + case GDAT_FLOAT: + fprintf(fp, "%f", dat->floats[col->index]); + break; + case GDAT_STRING: + fprintf(fp, "\"%s\"", dat->strings[col->index]->text); + break; + } + if (k < row->nr_cols - 1) + fputs(", ", fp); + } + fprintf(fp, "]%s\n", j < tbl->nr_rows - 1 ? "," : ""); + } + fprintf(fp, " ]%s\n", i < dat->nr_labels - 1 ? "," : ""); + } + fputs("}\n", fp); +} + +static int DataFile_Open(int link) +{ + struct archive_data *dfile = archive_get(ald[ALDFILE_DATA], link - 1); + if (!dfile) { + WARNING("Failed to load data file %d", link); + return 0; + } + + struct buffer r; + buffer_init(&r, dfile->data, dfile->size); + + if (strncmp(buffer_strdata(&r), "DD\x01\x01", 4)) { + WARNING("Invalid data file"); + archive_free_data(dfile); + return 0; + } + buffer_skip(&r, 4); + + unsigned long uncompressed_size = buffer_read_int32(&r); + uint32_t compressed_size = buffer_remaining(&r); + + // decode compressed data + uint8_t *compressed_data = xmalloc(compressed_size); + buffer_read_bytes(&r, compressed_data, compressed_size); + archive_free_data(dfile); + sgenrand(4588163); + for (size_t i = 0; i < compressed_size; i++) + compressed_data[i] ^= genrand(); + + uint8_t *raw = xmalloc(uncompressed_size); + int rv = uncompress(raw, &uncompressed_size, compressed_data, compressed_size); + free(compressed_data); + if (rv != Z_OK) { + WARNING("Uncompress failed: %d", rv); + free(raw); + return 0; + } + + buffer_init(&r, raw, uncompressed_size); + gdat_free(current_gdat); + current_gdat = gdat_read(&r); + + free(raw); + return current_gdat ? 1 : 0; +} + +static int DataFile_SarchLabel(struct string *label) +{ + if (!current_gdat) + return -1; + for (uint32_t i = 0; i < current_gdat->nr_labels; i++) { + if (!strcmp(current_gdat->labels[i], label->text)) + return i; + } + WARNING("Label '%s' not found", label->text); + return -1; +} + +static int DataFile_GetNumofVertical(int label) +{ + struct gdat_table *tbl = gdat_get_table(current_gdat, label); + return tbl ? tbl->nr_rows : -1; +} + +static int DataFile_GetNumofHorizontal(int label, int vartical) +{ + struct gdat_row *row = gdat_get_row(current_gdat, label, vartical); + return row ? row->nr_cols : -1; +} + +static int DataFile_SarchString(int label, int horizontal, struct string *data) +{ + struct gdat_table *tbl = gdat_get_table(current_gdat, label); + if (!tbl) + return -1; + for (int i = 0; i < tbl->nr_rows; i++) { + struct gdat_row *row = &tbl->rows[i]; + if (horizontal < row->nr_cols && + row->cols[horizontal].type == GDAT_STRING && + !strcmp(current_gdat->strings[row->cols[horizontal].index]->text, data->text)) + return i; + } + return -1; +} + +static int DataFile_SarchInt(int label, int horizontal, int data) +{ + struct gdat_table *tbl = gdat_get_table(current_gdat, label); + if (!tbl) + return -1; + for (int i = 0; i < tbl->nr_rows; i++) { + struct gdat_row *row = &tbl->rows[i]; + if (horizontal < row->nr_cols && + row->cols[horizontal].type == GDAT_INT && + current_gdat->ints[row->cols[horizontal].index] == data) + return i; + } + return -1; +} + +static int DataFile_GetInt(int label, int vartical, int horizontal) +{ + struct gdat_col *col = gdat_get_column(current_gdat, label, vartical, horizontal); + if (!col) + return -1; + + if (col->type != GDAT_INT) { + WARNING("Value is not an int"); + return -1; + } + if (col->index >= current_gdat->nr_ints) { + WARNING("Broken GDAT table; integer index %d out of bounds", col->index); + return -1; + } + return current_gdat->ints[col->index]; +} + +static float DataFile_GetFloat(int label, int vartical, int horizontal) +{ + struct gdat_col *col = gdat_get_column(current_gdat, label, vartical, horizontal); + if (!col) + return -1.0; + + if (col->type != GDAT_FLOAT) { + WARNING("Value is not a float"); + return -1.0; + } + if (col->index >= current_gdat->nr_floats) { + WARNING("Broken GDAT table; float index %d out of bounds", col->index); + return -1.0; + } + return current_gdat->floats[col->index]; +} + +static struct string *DataFile_GetString(int label, int vartical, int horizontal) +{ + struct gdat_col *col = gdat_get_column(current_gdat, label, vartical, horizontal); + if (!col) + return string_ref(&EMPTY_STRING); + + if (col->type != GDAT_STRING) { + WARNING("Value is not a string"); + return string_ref(&EMPTY_STRING); + } + if (col->index >= current_gdat->nr_strings) { + WARNING("Broken GDAT table; string index %d out of bounds", col->index); + return string_ref(&EMPTY_STRING); + } + return string_ref(current_gdat->strings[col->index]); +} + +HLL_LIBRARY(DataFile, + HLL_EXPORT(Open, DataFile_Open), + HLL_EXPORT(SarchLabel, DataFile_SarchLabel), + HLL_EXPORT(GetNumofVertical, DataFile_GetNumofVertical), + HLL_EXPORT(GetNumofHorizontal, DataFile_GetNumofHorizontal), + HLL_EXPORT(SarchString, DataFile_SarchString), + HLL_EXPORT(SarchInt, DataFile_SarchInt), + HLL_EXPORT(GetInt, DataFile_GetInt), + HLL_EXPORT(GetFloat, DataFile_GetFloat), + HLL_EXPORT(GetString, DataFile_GetString)); diff --git a/src/meson.build b/src/meson.build index c265194..3dc148c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -8,6 +8,7 @@ xsystem4 = ['audio.c', 'format.c', 'heap.c', 'input.c', + 'mt19937int.c', 'page.c', 'resume.c', 'sact_graphics.c', @@ -22,6 +23,7 @@ xsystem4 = ['audio.c', 'hll/AliceLogo2.c', 'hll/AliceLogo3.c', 'hll/Confirm2.c', + 'hll/DataFile.c', 'hll/DrawGraph.c', 'hll/DrawPluginManager.c', 'hll/File.c', diff --git a/src/mt19937int.c b/src/mt19937int.c new file mode 100644 index 0000000..b6d8667 --- /dev/null +++ b/src/mt19937int.c @@ -0,0 +1,120 @@ +/* A C-program for MT19937: Integer version (1998/4/6) */ +/* genrand() generates one pseudorandom unsigned integer (32bit) */ +/* which is uniformly distributed among 0 to 2^32-1 for each */ +/* call. sgenrand(seed) set initial values to the working area */ +/* of 624 words. Before genrand(), sgenrand(seed) must be */ +/* called once. (seed is any 32-bit integer except for 0). */ +/* Coded by Takuji Nishimura, considering the suggestions by */ +/* Topher Cooper and Marc Rieffel in July-Aug. 1997. */ + +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later */ +/* version. */ +/* This library 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 Library General Public License for more details. */ +/* You should have received a copy of the GNU Library General */ +/* Public License along with this library; if not, write to the */ +/* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */ +/* 02111-1307 USA */ + +/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */ +/* When you use this, send an email to: matumoto@math.keio.ac.jp */ +/* with an appropriate reference to your work. */ + +/* REFERENCE */ +/* M. Matsumoto and T. Nishimura, */ +/* "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform */ +/* Pseudo-Random Number Generator", */ +/* ACM Transactions on Modeling and Computer Simulation, */ +/* Vol. 8, No. 1, January 1998, pp 3--30. */ + +#include + +#include "mt19937int.h" + +/* Period parameters */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0df /* constant vector a */ +#define UPPER_MASK 0x80000000 /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffff /* least significant r bits */ + +/* Tempering parameters */ +#define TEMPERING_MASK_B 0x9d2c5680 +#define TEMPERING_MASK_C 0xefc60000 +#define TEMPERING_SHIFT_U(y) (y >> 11) +#define TEMPERING_SHIFT_S(y) (y << 7) +#define TEMPERING_SHIFT_T(y) (y << 15) +#define TEMPERING_SHIFT_L(y) (y >> 18) + +static unsigned long mt[N]; /* the array for the state vector */ +static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ + +/* initializing the array with a NONZERO seed */ +void +sgenrand(seed) + unsigned long seed; +{ + /* setting initial seeds to mt[N] using */ + /* the generator Line 25 of Table 1 in */ + /* [KNUTH 1981, The Art of Computer Programming */ + /* Vol. 2 (2nd Ed.), pp102] */ + mt[0]= seed & 0xffffffff; + for (mti=1; mti= N) { /* generate N words at one time */ + int kk; + + if (mti == N+1) /* if sgenrand() has not been called, */ + sgenrand(4357); /* a default initial seed is used */ + + for (kk=0;kk> 1) ^ mag01[y & 0x1]; + } + for (;kk> 1) ^ mag01[y & 0x1]; + } + y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; + + mti = 0; + } + + y = mt[mti++]; + y ^= TEMPERING_SHIFT_U(y); + y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; + y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; + y ^= TEMPERING_SHIFT_L(y); + + return y; +} + +#if 0 +/* this main() outputs first 1000 generated numbers */ +main() +{ + int j; + + sgenrand(4357); /* any nonzero integer can be used as a seed */ + for (j=0; j<1000; j++) { + printf("%10lu ", genrand()); + if (j%8==7) printf("\n"); + } + printf("\n"); +} +#endif diff --git a/src/system4.c b/src/system4.c index 2cb3760..47d324b 100644 --- a/src/system4.c +++ b/src/system4.c @@ -205,6 +205,11 @@ static void init_gamedata_dir(const char *path) ald_filenames[ALDFILE_WAVE][dno] = strdup(filepath); ald_count[ALDFILE_WAVE] = max(ald_count[ALDFILE_WAVE], dno+1); break; + case 'd': + case 'D': + ald_filenames[ALDFILE_DATA][dno] = strdup(filepath); + ald_count[ALDFILE_DATA] = max(ald_count[ALDFILE_DATA], dno+1); + break; default: WARNING("Unhandled ALD file: %s", d->d_name); break; @@ -231,6 +236,8 @@ static void init_gamedata_dir(const char *path) ald_init(ALDFILE_CG, ald_filenames[ALDFILE_CG], ald_count[ALDFILE_CG]); if (ald_count[ALDFILE_WAVE] > 0) ald_init(ALDFILE_WAVE, ald_filenames[ALDFILE_WAVE], ald_count[ALDFILE_WAVE]); + if (ald_count[ALDFILE_DATA] > 0) + ald_init(ALDFILE_DATA, ald_filenames[ALDFILE_DATA], ald_count[ALDFILE_DATA]); closedir(dir); }