mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-10-08 14:58:14 +03:00
Add ainedit utility
The ainedit utility is a tool capable of modifying AIN files up to
version 12 (Rance X). As far as I know, this is the first publicly
released tool that can handle AIN files above version 7.
The basic mode of operation is as follows:
* dump the CODE section with aindump
* edit the disassembled bytecode
* assemble & insert the edited bytebode back into the original AIN file
using ainedit
E.g.
aindump -c -o out.jam /path/to/Rance10.ain
$EDITOR out.jam
ainedit -c out.jam -o out.ain /path/to/Rance10.ain
mv /path/to/Rance10.ain /path/to/Rance10.ain.backup
mv out.ain /path/to/Rance10.ain
ainedit is also capable of editing declarations (structures, functions,
etc.) using the JSON (-j) output from aindump.
There is currently one major deficiency in this tool: it does not
rebuild the AIN file's switch table (SWI0 section). This means that you
can't insert or delete instructions, as it would render the addresses
stored in the switch table invalid. I hope to fix this soon.
A number of changes have also been made to the aindump utility, since
these tools need to work together.
This commit is contained in:
@@ -277,6 +277,8 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
/* Macro for iterating over an array or object */
|
||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||
|
||||
#define cJSON_ArrayForEachIndex(i, element, array) for(element = (array != NULL) ? (array)->child : NULL, i = 0; element != NULL; element = element->next, i++)
|
||||
|
||||
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||
|
||||
+57
-3
@@ -115,7 +115,8 @@ enum ain_data_type {
|
||||
case AIN_REF_ARRAY_BOOL: \
|
||||
case AIN_REF_LONG_INT: \
|
||||
case AIN_REF_ARRAY_LONG_INT: \
|
||||
case AIN_REF_ARRAY_DELEGATE
|
||||
case AIN_REF_ARRAY_DELEGATE: \
|
||||
case AIN_REF_ARRAY
|
||||
|
||||
enum ain_variable_type {
|
||||
AIN_VAR_LOCAL,
|
||||
@@ -224,6 +225,13 @@ struct ain_enum {
|
||||
};
|
||||
|
||||
struct kh_func_ht_s;
|
||||
struct kh_struct_ht_s;
|
||||
|
||||
struct ain_section {
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
bool present;
|
||||
};
|
||||
|
||||
struct ain {
|
||||
char *ain_path;
|
||||
@@ -240,6 +248,7 @@ struct ain {
|
||||
int32_t nr_structures;
|
||||
struct ain_struct *structures;
|
||||
int32_t nr_messages;
|
||||
int32_t msg1_uk;
|
||||
struct string **messages;
|
||||
int32_t main;
|
||||
int32_t alloc;
|
||||
@@ -254,16 +263,42 @@ struct ain {
|
||||
int32_t nr_filenames;
|
||||
char **filenames;
|
||||
int32_t ojmp;
|
||||
int nr_function_types;
|
||||
int32_t nr_function_types;
|
||||
int32_t fnct_size;
|
||||
struct ain_function_type *function_types;
|
||||
int nr_delegates;
|
||||
int32_t nr_delegates;
|
||||
int32_t delg_size;
|
||||
struct ain_function_type *delegates;
|
||||
int32_t nr_global_groups;
|
||||
char **global_group_names;
|
||||
int32_t nr_enums;
|
||||
struct ain_enum *enums;
|
||||
|
||||
// file map
|
||||
struct ain_section VERS;
|
||||
struct ain_section KEYC;
|
||||
struct ain_section CODE;
|
||||
struct ain_section FUNC;
|
||||
struct ain_section GLOB;
|
||||
struct ain_section GSET;
|
||||
struct ain_section STRT;
|
||||
struct ain_section MSG0;
|
||||
struct ain_section MSG1;
|
||||
struct ain_section MAIN;
|
||||
struct ain_section MSGF;
|
||||
struct ain_section HLL0;
|
||||
struct ain_section SWI0;
|
||||
struct ain_section GVER;
|
||||
struct ain_section STR0;
|
||||
struct ain_section FNAM;
|
||||
struct ain_section OJMP;
|
||||
struct ain_section FNCT;
|
||||
struct ain_section DELG;
|
||||
struct ain_section OBJG;
|
||||
struct ain_section ENUM;
|
||||
|
||||
struct kh_func_ht_s *_func_ht;
|
||||
struct kh_struct_ht_s *_struct_ht;
|
||||
};
|
||||
|
||||
const char *ain_strerror(int error);
|
||||
@@ -272,6 +307,25 @@ char *ain_strtype_d(struct ain *ain, struct ain_type *v);
|
||||
const char *ain_variable_to_string(struct ain *ain, struct ain_variable *v);
|
||||
uint8_t *ain_read(const char *path, long *len, int *error);
|
||||
struct ain *ain_open(const char *path, int *error);
|
||||
void ain_decrypt(uint8_t *buf, size_t len);
|
||||
|
||||
struct ain_function *ain_get_function(struct ain *ain, char *name);
|
||||
int ain_get_function_index(struct ain *ain, struct ain_function *f);
|
||||
struct ain_struct *ain_get_struct(struct ain *ain, char *name);
|
||||
|
||||
void ain_free(struct ain *ain);
|
||||
void ain_free_functions(struct ain *ain);
|
||||
void ain_free_globals(struct ain *ain);
|
||||
void ain_free_initvals(struct ain *ain);
|
||||
void ain_free_structures(struct ain *ain);
|
||||
void ain_free_messages(struct ain *ain);
|
||||
void ain_free_libraries(struct ain *ain);
|
||||
void ain_free_switches(struct ain *ain);
|
||||
void ain_free_strings(struct ain *ain);
|
||||
void ain_free_filenames(struct ain *ain);
|
||||
void ain_free_function_types(struct ain *ain);
|
||||
void ain_free_delegates(struct ain *ain);
|
||||
void ain_free_global_groups(struct ain *ain);
|
||||
void ain_free_enums(struct ain *ain);
|
||||
|
||||
#endif /* SYSTEM4_AIN_H */
|
||||
|
||||
@@ -328,10 +328,10 @@ enum opcode
|
||||
DG_NEW,
|
||||
DG_STR_TO_METHOD,
|
||||
|
||||
OP_0x102 = 0x102,
|
||||
OP_0x103 = 0x103,
|
||||
OP_0x104 = 0x104,
|
||||
OP_0x105 = 0x105,
|
||||
OP_0X102 = 0x102,
|
||||
OP_0X103 = 0x103,
|
||||
OP_0X104 = 0x104,
|
||||
OP_0X105 = 0x105,
|
||||
|
||||
NR_OPCODES
|
||||
};
|
||||
|
||||
@@ -40,10 +40,10 @@ struct func_list {
|
||||
#define func_list_size(nr_slots) (sizeof(struct func_list) + sizeof(struct ain_function*)*(nr_slots))
|
||||
|
||||
KHASH_MAP_INIT_STR(func_ht, struct func_list*);
|
||||
KHASH_MAP_INIT_STR(struct_ht, struct ain_struct*);
|
||||
|
||||
static void init_func_ht(struct ain *ain)
|
||||
{
|
||||
ain->_func_ht = kh_init(func_ht);
|
||||
for (int i = 0; i < ain->nr_functions; i++) {
|
||||
int ret;
|
||||
khiter_t k = kh_put(func_ht, ain->_func_ht, ain->functions[i].name, &ret);
|
||||
@@ -66,6 +66,21 @@ static void init_func_ht(struct ain *ain)
|
||||
}
|
||||
}
|
||||
|
||||
static void init_struct_ht(struct ain *ain)
|
||||
{
|
||||
for (int i = 0; i < ain->nr_structures; i++) {
|
||||
int ret;
|
||||
khiter_t k = kh_put(struct_ht, ain->_struct_ht, ain->structures[i].name, &ret);
|
||||
if (!ret) {
|
||||
ERROR("Duplicate structure names: '%s'", ain->structures[i].name);
|
||||
} else if (ret == 1) {
|
||||
kh_value(ain->_struct_ht, k) = &ain->structures[i];
|
||||
} else {
|
||||
ERROR("Failed to insert struct into hash table (%d)", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct func_list *get_function(struct ain *ain, const char *name)
|
||||
{
|
||||
int ret;
|
||||
@@ -75,6 +90,51 @@ static struct func_list *get_function(struct ain *ain, const char *name)
|
||||
return kh_value(ain->_func_ht, k);
|
||||
}
|
||||
|
||||
struct ain_function *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 NULL;
|
||||
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 (funs->slots[i] == f)
|
||||
return i;
|
||||
}
|
||||
err:
|
||||
ERROR("Invalid function: '%s'", f->name);
|
||||
}
|
||||
|
||||
struct ain_struct *ain_get_struct(struct ain *ain, char *name)
|
||||
{
|
||||
khiter_t k = kh_get(struct_ht, ain->_struct_ht, name);
|
||||
if (k == kh_end(ain->_struct_ht))
|
||||
return NULL;
|
||||
return kh_value(ain->_struct_ht, k);
|
||||
}
|
||||
|
||||
static const char *errtab[AIN_MAX_ERROR] = {
|
||||
[AIN_SUCCESS] = "Success",
|
||||
[AIN_FILE_ERROR] = "Error opening AIN file",
|
||||
@@ -150,8 +210,6 @@ char *ain_strtype_d(struct ain *ain, struct ain_type *v)
|
||||
case AIN_STRUCT:
|
||||
if (v->struc == -1 || !ain)
|
||||
return strdup("struct");
|
||||
if (v->struc < 0 || v->struc >= ain->nr_structures)
|
||||
ERROR("WTF: %d, %d", v->struc, ain->nr_structures);
|
||||
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);
|
||||
@@ -257,6 +315,7 @@ struct ain_reader {
|
||||
uint8_t *buf;
|
||||
size_t size;
|
||||
size_t index;
|
||||
struct ain_section *section;
|
||||
};
|
||||
|
||||
static int32_t read_int32(struct ain_reader *r)
|
||||
@@ -623,17 +682,31 @@ struct ain_enum *read_enums(struct ain_reader *r, int count, struct ain *ain)
|
||||
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 bool read_tag(struct ain_reader *r, struct ain *ain)
|
||||
{
|
||||
if (r->index + 4 >= r->size)
|
||||
if (r->index + 4 >= r->size) {
|
||||
start_section(r, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *tag_loc = r->buf + r->index;
|
||||
r->index += 4;
|
||||
|
||||
#define TAG_EQ(tag) !strncmp((char*)tag_loc, tag, 4)
|
||||
// FIXME: need to check len or could segfault on currupt AIN file
|
||||
if (TAG_EQ("VERS")) {
|
||||
start_section(r, &ain->VERS);
|
||||
ain->version = read_int32(r);
|
||||
if (ain->version >= 11) {
|
||||
instructions[CALLHLL].nr_args = 3;
|
||||
@@ -643,65 +716,89 @@ static bool read_tag(struct ain_reader *r, struct ain *ain)
|
||||
instructions[CALLMETHOD].args[0] = T_INT;
|
||||
}
|
||||
} 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);
|
||||
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")) {
|
||||
read_int32(r); // ???
|
||||
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")) {
|
||||
read_int32(r); // ???
|
||||
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
|
||||
@@ -713,6 +810,7 @@ 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
|
||||
@@ -730,7 +828,14 @@ static uint8_t *decompress_ain(uint8_t *in, long *len)
|
||||
return NULL;
|
||||
|
||||
out = xmalloc(out_len);
|
||||
if (Z_OK != uncompress(out, (unsigned long*)&out_len, in+16, in_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;
|
||||
}
|
||||
@@ -773,7 +878,7 @@ static void update(uint32_t *state)
|
||||
}
|
||||
}
|
||||
|
||||
static void decrypt_ain(uint8_t *buf, size_t len)
|
||||
void ain_decrypt(uint8_t *buf, size_t len)
|
||||
{
|
||||
uint32_t state[0x270];
|
||||
uint32_t key = 0x5D3E3;
|
||||
@@ -800,7 +905,7 @@ static bool ain_is_encrypted(uint8_t *buf)
|
||||
uint8_t magic[8];
|
||||
|
||||
memcpy(magic, buf, 8);
|
||||
decrypt_ain(magic, 8);
|
||||
ain_decrypt(magic, 8);
|
||||
|
||||
return !strncmp((char*)magic, "VERS", 4) && !magic[5] && !magic[6] && !magic[7];
|
||||
}
|
||||
@@ -835,7 +940,7 @@ uint8_t *ain_read(const char *path, long *len, int *error)
|
||||
free(buf);
|
||||
buf = uc;
|
||||
} else if (ain_is_encrypted(buf)) {
|
||||
decrypt_ain(buf, *len);
|
||||
ain_decrypt(buf, *len);
|
||||
} else {
|
||||
printf("%.4s\n", buf);
|
||||
*error = AIN_UNRECOGNIZED_FORMAT;
|
||||
@@ -853,6 +958,8 @@ 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
|
||||
struct ain_reader r = {
|
||||
@@ -861,6 +968,8 @@ struct ain *ain_open(const char *path, int *error)
|
||||
.size = len
|
||||
};
|
||||
ain = calloc(1, sizeof(struct ain));
|
||||
ain->_func_ht = kh_init(func_ht);
|
||||
ain->_struct_ht = kh_init(struct_ht);
|
||||
while (read_tag(&r, ain));
|
||||
if (!ain->version) {
|
||||
*error = AIN_INVALID;
|
||||
@@ -889,7 +998,7 @@ static void ain_free_variables(struct ain_variable *vars, int nr_vars)
|
||||
free(vars);
|
||||
}
|
||||
|
||||
static void ain_free_function_types(struct ain_function_type *funs, int n)
|
||||
static void _ain_free_function_types(struct ain_function_type *funs, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
free(funs[i].name);
|
||||
@@ -899,7 +1008,7 @@ static void ain_free_function_types(struct ain_function_type *funs, int n)
|
||||
free(funs);
|
||||
}
|
||||
|
||||
static void ain_free_strings(struct string **strings, int n)
|
||||
static void ain_free_vmstrings(struct string **strings, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
free_string(strings[i]);
|
||||
@@ -915,33 +1024,53 @@ static void ain_free_cstrings(char **strings, int n)
|
||||
free(strings);
|
||||
}
|
||||
|
||||
void ain_free(struct ain *ain)
|
||||
void ain_free_functions(struct ain *ain)
|
||||
{
|
||||
free(ain->code);
|
||||
|
||||
for (int f = 0; f < ain->nr_functions; f++) {
|
||||
free(ain->functions[f].name);
|
||||
free(ain->functions[f].return_type.array_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;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ain->nr_initvals; i++) {
|
||||
if (ain->global_initvals[i].data_type != AIN_STRING)
|
||||
continue;
|
||||
free(ain->global_initvals[i].string_value);
|
||||
}
|
||||
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);
|
||||
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++) {
|
||||
@@ -954,30 +1083,88 @@ void ain_free(struct ain *ain)
|
||||
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;
|
||||
}
|
||||
|
||||
ain_free_function_types(ain->function_types, ain->nr_function_types);
|
||||
ain_free_function_types(ain->delegates, ain->nr_delegates);
|
||||
void ain_free(struct ain *ain)
|
||||
{
|
||||
free(ain->code);
|
||||
|
||||
ain_free_strings(ain->strings, ain->nr_strings);
|
||||
ain_free_strings(ain->messages, ain->nr_messages);
|
||||
|
||||
ain_free_cstrings(ain->filenames, ain->nr_filenames);
|
||||
ain_free_cstrings(ain->global_group_names, ain->nr_global_groups);
|
||||
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);
|
||||
|
||||
struct func_list *list;
|
||||
kh_foreach_value(ain->_func_ht, list, free(list));
|
||||
kh_destroy(func_ht, ain->_func_ht);
|
||||
kh_destroy(struct_ht, ain->_struct_ht);
|
||||
|
||||
free(ain);
|
||||
}
|
||||
|
||||
+44
-2
@@ -50,6 +50,7 @@ static void usage(void)
|
||||
puts(" -A, --audit Audit AIN file for xsystem4 compatibility");
|
||||
puts(" -d, --decrypt Dump decrypted AIN file");
|
||||
puts(" -j, --json Dump to JSON format");
|
||||
puts(" --map Dump AIN file map");
|
||||
}
|
||||
|
||||
static void print_sjis(FILE *f, const char *s)
|
||||
@@ -289,7 +290,41 @@ static void ain_audit(FILE *f, struct ain *ain)
|
||||
fflush(f);
|
||||
}
|
||||
|
||||
static void ain_decrypt(FILE *f, const char *path)
|
||||
static void print_section(FILE *f, const char *name, struct ain_section *section)
|
||||
{
|
||||
if (section->present)
|
||||
fprintf(f, "%s: %08x -> %08x\n", name, section->addr, section->addr + section->size);
|
||||
}
|
||||
|
||||
static void ain_dump_map(FILE *f, struct ain *ain)
|
||||
{
|
||||
print_section(f, "VERS", &ain->VERS);
|
||||
print_section(f, "KEYC", &ain->KEYC);
|
||||
print_section(f, "CODE", &ain->CODE);
|
||||
print_section(f, "FUNC", &ain->FUNC);
|
||||
print_section(f, "GLOB", &ain->GLOB);
|
||||
print_section(f, "GSET", &ain->GSET);
|
||||
print_section(f, "STRT", &ain->STRT);
|
||||
print_section(f, "MSG0", &ain->MSG0);
|
||||
print_section(f, "MSG1", &ain->MSG1);
|
||||
print_section(f, "MAIN", &ain->MAIN);
|
||||
print_section(f, "MSGF", &ain->MSGF);
|
||||
print_section(f, "HLL0", &ain->HLL0);
|
||||
print_section(f, "SWI0", &ain->SWI0);
|
||||
print_section(f, "GVER", &ain->GVER);
|
||||
print_section(f, "STR0", &ain->STR0);
|
||||
print_section(f, "FNAM", &ain->FNAM);
|
||||
print_section(f, "OJMP", &ain->OJMP);
|
||||
print_section(f, "FNCT", &ain->FNCT);
|
||||
print_section(f, "DELG", &ain->DELG);
|
||||
print_section(f, "OBJG", &ain->OBJG);
|
||||
print_section(f, "ENUM", &ain->ENUM);
|
||||
|
||||
fprintf(f, "FNCT_SIZE = %d\n", ain->delg_size);
|
||||
fprintf(f, "FNCT.SIZE = %d\n", ain->DELG.size);
|
||||
}
|
||||
|
||||
static void dump_decrypted(FILE *f, const char *path)
|
||||
{
|
||||
int err;
|
||||
long len;
|
||||
@@ -321,6 +356,7 @@ int main(int argc, char *argv[])
|
||||
bool dump_global_group_names = false;
|
||||
bool dump_enums = false;
|
||||
bool dump_json = false;
|
||||
bool dump_map = false;
|
||||
bool audit = false;
|
||||
bool decrypt = false;
|
||||
char *output_file = NULL;
|
||||
@@ -348,6 +384,7 @@ int main(int argc, char *argv[])
|
||||
{ "audit", no_argument, 0, 'A' },
|
||||
{ "decrypt", no_argument, 0, 'd' },
|
||||
{ "json", no_argument, 0, 'j' },
|
||||
{ "map", no_argument, 0, 'M' },
|
||||
{ "output", required_argument, 0, 'o' },
|
||||
};
|
||||
int option_index = 0;
|
||||
@@ -412,6 +449,9 @@ int main(int argc, char *argv[])
|
||||
case 'j':
|
||||
dump_json = true;
|
||||
break;
|
||||
case 'M':
|
||||
dump_map = true;
|
||||
break;
|
||||
case 'o':
|
||||
output_file = xstrdup(optarg);
|
||||
break;
|
||||
@@ -436,7 +476,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (decrypt) {
|
||||
ain_decrypt(output, argv[0]);
|
||||
dump_decrypted(output, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -475,6 +515,8 @@ int main(int argc, char *argv[])
|
||||
disassemble_ain(output, ain, true);
|
||||
if (dump_json)
|
||||
ain_dump_json(output, ain);
|
||||
if (dump_map)
|
||||
ain_dump_map(output, ain);
|
||||
if (audit)
|
||||
ain_audit(output, ain);
|
||||
|
||||
|
||||
+107
-9
@@ -30,6 +30,7 @@
|
||||
|
||||
struct dasm_state {
|
||||
struct ain *ain;
|
||||
uint32_t flags;
|
||||
FILE *out;
|
||||
size_t addr;
|
||||
int func;
|
||||
@@ -90,9 +91,11 @@ static char *prepare_string(const char *str, const char *escape_chars, const cha
|
||||
char *u = sjis2utf(str, strlen(str));
|
||||
|
||||
// count number of required escapes
|
||||
for (int i = 0; str[i]; i++) {
|
||||
for (int i = 0; u[i]; i++) {
|
||||
if (u[i] & 0x80)
|
||||
continue;
|
||||
for (int j = 0; escape_chars[j]; j++) {
|
||||
if (str[i] == escape_chars[j]) {
|
||||
if (u[i] == escape_chars[j]) {
|
||||
escapes++;
|
||||
break;
|
||||
}
|
||||
@@ -151,7 +154,64 @@ static void print_identifier(struct dasm_state *dasm, const char *str)
|
||||
print_sjis(dasm, str);
|
||||
}
|
||||
|
||||
static void print_argument(struct dasm_state *dasm, int32_t arg, enum instruction_argtype type)
|
||||
static void print_local_variable(struct dasm_state *dasm, struct ain_function *func, int varno)
|
||||
{
|
||||
int dup_no = 0; // nr of duplicate-named variables preceding varno
|
||||
for (int i = 0; i < func->nr_vars; i++) {
|
||||
if (i == varno)
|
||||
break;
|
||||
if (!strcmp(func->vars[i].name, func->vars[varno].name))
|
||||
dup_no++;
|
||||
}
|
||||
|
||||
// if variable name is ambiguous, add #n suffix
|
||||
char *name;
|
||||
char buf[512];
|
||||
if (dup_no) {
|
||||
snprintf(buf, 512, "%s#%d", func->vars[varno].name, dup_no);
|
||||
name = buf;
|
||||
} else {
|
||||
name = func->vars[varno].name;
|
||||
}
|
||||
|
||||
print_identifier(dasm, name);
|
||||
}
|
||||
|
||||
static void print_function_name(struct dasm_state *dasm, struct ain_function *func)
|
||||
{
|
||||
int i = ain_get_function_index(dasm->ain, func);
|
||||
|
||||
char *name = func->name;
|
||||
char buf[512];
|
||||
if (i > 0) {
|
||||
snprintf(buf, 512, "%s#%d", func->name, i);
|
||||
name = buf;
|
||||
}
|
||||
|
||||
print_identifier(dasm, name);
|
||||
}
|
||||
|
||||
static void print_hll_function_name(struct dasm_state *dasm, struct ain_library *lib, int fno)
|
||||
{
|
||||
int dup_no = 0;
|
||||
for (int i = 0; i < lib->nr_functions; i++) {
|
||||
if (i == fno)
|
||||
break;
|
||||
if (!strcmp(lib->functions[i].name, lib->functions[fno].name))
|
||||
dup_no++;
|
||||
}
|
||||
|
||||
char *name = lib->functions[fno].name;
|
||||
char buf[512];
|
||||
if (dup_no) {
|
||||
snprintf(buf, 512, "%s#%d", lib->functions[fno].name, dup_no);
|
||||
name = buf;
|
||||
}
|
||||
|
||||
print_identifier(dasm, name);
|
||||
}
|
||||
|
||||
static void print_argument(struct dasm_state *dasm, int32_t arg, enum instruction_argtype type, possibly_unused const char **comment)
|
||||
{
|
||||
if (dasm->raw) {
|
||||
fprintf(dasm->out, "0x%x", arg);
|
||||
@@ -178,7 +238,7 @@ static void print_argument(struct dasm_state *dasm, int32_t arg, enum instructio
|
||||
case T_FUNC:
|
||||
if (arg < 0 || arg >= ain->nr_functions)
|
||||
DASM_ERROR(dasm, "Invalid function number: %d", arg);
|
||||
print_identifier(dasm, ain->functions[arg].name);
|
||||
print_function_name(dasm, &ain->functions[arg]);
|
||||
break;
|
||||
case T_DLG:
|
||||
if (arg < 0 || arg >= ain->nr_delegates)
|
||||
@@ -188,11 +248,15 @@ static void print_argument(struct dasm_state *dasm, int32_t arg, enum instructio
|
||||
case T_STRING:
|
||||
if (arg < 0 || arg >= ain->nr_strings)
|
||||
DASM_ERROR(dasm, "Invalid string number: %d", arg);
|
||||
//fprintf(dasm->out, "0x%x ", arg);
|
||||
//*comment = ain->strings[arg]->text;
|
||||
print_string(dasm, ain->strings[arg]->text);
|
||||
break;
|
||||
case T_MSG:
|
||||
if (arg < 0 || arg >= ain->nr_messages)
|
||||
DASM_ERROR(dasm, "Invalid message number: %d", arg);
|
||||
//fprintf(dasm->out, "0x%x ", arg);
|
||||
//*comment = ain->messages[arg]->text;
|
||||
print_string(dasm, ain->messages[arg]->text);
|
||||
break;
|
||||
case T_LOCAL:
|
||||
@@ -204,7 +268,8 @@ static void print_argument(struct dasm_state *dasm, int32_t arg, enum instructio
|
||||
}
|
||||
if (arg < 0 || arg >= ain->functions[dasm->func].nr_vars)
|
||||
DASM_ERROR(dasm, "Invalid variable number: %d", arg);
|
||||
print_identifier(dasm, ain->functions[dasm->func].vars[arg].name);
|
||||
print_local_variable(dasm, &ain->functions[dasm->func], arg);
|
||||
//print_identifier(dasm, ain->functions[dasm->func].vars[arg].name);
|
||||
break;
|
||||
case T_GLOBAL:
|
||||
if (arg < 0 || arg >= ain->nr_globals)
|
||||
@@ -249,18 +314,28 @@ static void print_arguments(struct dasm_state *dasm, const struct instruction *i
|
||||
if (instr->opcode == CALLHLL) {
|
||||
int32_t lib = LittleEndian_getDW(dasm->ain->code, dasm->addr + 2);
|
||||
int32_t fun = LittleEndian_getDW(dasm->ain->code, dasm->addr + 6);
|
||||
fprintf(dasm->out, " %s.%s", dasm->ain->libraries[lib].name, dasm->ain->libraries[lib].functions[fun].name);
|
||||
fprintf(dasm->out, " %s ", dasm->ain->libraries[lib].name);
|
||||
print_hll_function_name(dasm, &dasm->ain->libraries[lib], fun);
|
||||
if (dasm->ain->version >= 11) {
|
||||
fprintf(dasm->out, " %d", LittleEndian_getDW(dasm->ain->code, dasm->addr + 10));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (instr->opcode == FUNC) {
|
||||
fputc(' ', dasm->out);
|
||||
ain_dump_function(dasm->out, dasm->ain, &dasm->ain->functions[dasm->func]);
|
||||
fprintf(dasm->out, "0x%x", dasm->func);
|
||||
//ain_dump_function(dasm->out, dasm->ain, &dasm->ain->functions[dasm->func]);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *comment = NULL;
|
||||
for (int i = 0; i < instr->nr_args; i++) {
|
||||
fputc(' ', dasm->out);
|
||||
print_argument(dasm, LittleEndian_getDW(dasm->ain->code, dasm->addr + 2 + i*4), instr->args[i]);
|
||||
print_argument(dasm, LittleEndian_getDW(dasm->ain->code, dasm->addr + 2 + i*4), instr->args[i], &comment);
|
||||
}
|
||||
if (comment) {
|
||||
fprintf(dasm->out, "; ");
|
||||
print_string(dasm, comment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +350,10 @@ static void dasm_enter_function(struct dasm_state *dasm, int fno)
|
||||
dasm->func_stack[0] = dasm->func;
|
||||
dasm->func = fno;
|
||||
|
||||
fprintf(dasm->out, "; FUNC 0x%x\n", fno);
|
||||
fprintf(dasm->out, "; ");
|
||||
ain_dump_function(dasm->out, dasm->ain, &dasm->ain->functions[fno]);
|
||||
fputc('\n', dasm->out);
|
||||
//fprintf(dasm->out, "; FUNC 0x%x\n", fno);
|
||||
}
|
||||
|
||||
static void dasm_leave_function(struct dasm_state *dasm)
|
||||
@@ -328,6 +406,20 @@ static char *genlabel(size_t addr)
|
||||
return strdup(name);
|
||||
}
|
||||
|
||||
static char *switch_label(int switch_id, possibly_unused int type, struct ain_switch_case *c)
|
||||
{
|
||||
// TODO: replace with CASE pseudo-op, e.g.
|
||||
// ...
|
||||
// STRSWITCH
|
||||
// CASE "case one"
|
||||
// ...
|
||||
// CASE "case two"
|
||||
// ...
|
||||
char name[512];
|
||||
snprintf(name, 512, "switch%d_case_%d", switch_id, c->value);
|
||||
return strdup(name);
|
||||
}
|
||||
|
||||
static void generate_labels(struct dasm_state *dasm)
|
||||
{
|
||||
label_table_init();
|
||||
@@ -341,6 +433,12 @@ static void generate_labels(struct dasm_state *dasm)
|
||||
}
|
||||
dasm->addr += instruction_width(instr->opcode);
|
||||
}
|
||||
for (int i = 0; i < dasm->ain->nr_switches; i++) {
|
||||
for (int j = 0; j < dasm->ain->switches[i].nr_cases; j++) {
|
||||
struct ain_switch_case *c = &dasm->ain->switches[i].cases[j];
|
||||
add_label(switch_label(i, dasm->ain->switches[i].case_type, c), c->address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dasm_init(struct dasm_state *dasm, FILE *out, struct ain *ain, bool raw)
|
||||
|
||||
+2
-4
@@ -141,7 +141,7 @@ static cJSON *ain_library_to_json(struct ain_library *lib)
|
||||
for (int i = 0; i < lib->nr_functions; i++) {
|
||||
cJSON *f = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(f, "name", lib->functions[i].name);
|
||||
cJSON_AddNumberToObject(f, "type", lib->functions[i].data_type);
|
||||
cJSON_AddNumberToObject(f, "return-type", lib->functions[i].data_type);
|
||||
|
||||
cJSON *args = cJSON_CreateArray();
|
||||
for (int j = 0; j < lib->functions[i].nr_arguments; j++) {
|
||||
@@ -237,8 +237,6 @@ static cJSON *ain_to_json(struct ain *ain)
|
||||
}
|
||||
cJSON_AddItemToObject(j, "globals", a);
|
||||
|
||||
// TODO: use initval in ain_variable structure for global initvals
|
||||
|
||||
// STRT: structures
|
||||
a = cJSON_CreateArray();
|
||||
for (int i = 0; i < ain->nr_structures; i++) {
|
||||
@@ -279,7 +277,7 @@ static cJSON *ain_to_json(struct ain *ain)
|
||||
// OJMP: ???
|
||||
cJSON_AddNumberToObject(j, "ojmp", ain->ojmp);
|
||||
|
||||
// FUNCT: function types
|
||||
// FNCT: function types
|
||||
if (ain->nr_function_types > 0) {
|
||||
a = cJSON_CreateArray();
|
||||
for (int i = 0; i < ain->nr_function_types; i++) {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
aindump = ['../cJSON.c',
|
||||
'aindump.c',
|
||||
'dasm.c',
|
||||
'json.c',
|
||||
]
|
||||
|
||||
executable('aindump', aindump,
|
||||
dependencies : [libm, zlib],
|
||||
include_directories : incdir,
|
||||
link_with : libsys4)
|
||||
@@ -0,0 +1,116 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include "ainedit.h"
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
puts("Usage: ainedit [options...] input-file");
|
||||
puts(" Edit AIN files.");
|
||||
puts("");
|
||||
puts(" -h, --help Display this message and exit");
|
||||
puts(" -c, --code <jam-file> Update the CODE section (assemble .jam file)");
|
||||
puts(" -j, --json <json-file> Update AIN file from JSON data");
|
||||
puts(" -o, --output <path> Set output file path");
|
||||
puts(" --raw Read code in raw mode");
|
||||
puts(" --no-strings Read code in no-strings mode");
|
||||
//puts(" -p,--project <pje-file> Build AIN from project file");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct ain *ain;
|
||||
int err = AIN_SUCCESS;
|
||||
const char *code_file = NULL;
|
||||
const char *decl_file = NULL;
|
||||
const char *output_file = NULL;
|
||||
uint32_t flags = 0;
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "code", required_argument, 0, 'c' },
|
||||
{ "json", required_argument, 0, 'j' },
|
||||
{ "output", required_argument, 0, 'o' },
|
||||
{ "raw", no_argument, 0, 'R' },
|
||||
{ "no-strings", no_argument, 0, 'N' },
|
||||
};
|
||||
int option_index = 0;
|
||||
int c;
|
||||
|
||||
c = getopt_long(argc, argv, "hc:j:o:", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage();
|
||||
return 0;
|
||||
case 'c':
|
||||
code_file = optarg;
|
||||
break;
|
||||
case 'j':
|
||||
decl_file = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
output_file = optarg;
|
||||
break;
|
||||
case 'R':
|
||||
flags |= ASM_RAW;
|
||||
break;
|
||||
case 'N':
|
||||
flags |= ASM_NO_STRINGS;
|
||||
break;
|
||||
case '?':
|
||||
ERROR("Unknown command line argument");
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc != 1) {
|
||||
usage();
|
||||
ERROR("Wrong number of arguments.");
|
||||
}
|
||||
|
||||
if (!output_file) {
|
||||
usage();
|
||||
ERROR("No output file given");
|
||||
}
|
||||
|
||||
if (!(ain = ain_open(argv[0], &err))) {
|
||||
ERROR("Failed to open ain file: %s", ain_strerror(err));
|
||||
}
|
||||
|
||||
if (code_file) {
|
||||
asm_assemble_jam(code_file, ain, flags);
|
||||
}
|
||||
|
||||
if (decl_file) {
|
||||
read_declarations(decl_file, ain);
|
||||
}
|
||||
|
||||
NOTICE("Writing AIN file...");
|
||||
ain_write(output_file, ain);
|
||||
|
||||
ain_free(ain);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AINEDIT_AINEDIT_H
|
||||
#define AINEDIT_AINEDIT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "system4/ain.h"
|
||||
#include "system4/instructions.h"
|
||||
|
||||
enum {
|
||||
ASM_RAW = 1,
|
||||
ASM_NO_STRINGS = 2,
|
||||
};
|
||||
|
||||
// asm.c
|
||||
void asm_assemble_jam(const char *filename, struct ain *ain, uint32_t flags);
|
||||
struct instruction *asm_get_instruction(const char *name);
|
||||
|
||||
// json.c
|
||||
void read_declarations(const char *filename, struct ain *ain);
|
||||
|
||||
// repack.c
|
||||
void ain_write(const char *filename, struct ain *ain);
|
||||
|
||||
#endif /* AINEDIT_AINEDIT_H */
|
||||
@@ -0,0 +1,411 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "ainedit.h"
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/instructions.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
#include "asm_parser.tab.h"
|
||||
|
||||
extern FILE *yyin;
|
||||
|
||||
KHASH_MAP_INIT_STR(string_ht, size_t);
|
||||
|
||||
// TODO: better error messages
|
||||
#define ASM_ERROR(state, ...) ERROR(__VA_ARGS__)
|
||||
|
||||
struct string_table {
|
||||
struct string **strings;
|
||||
size_t size;
|
||||
size_t allocated;
|
||||
};
|
||||
|
||||
struct asm_state {
|
||||
struct ain *ain;
|
||||
uint32_t flags;
|
||||
uint8_t *buf;
|
||||
size_t buf_ptr;
|
||||
size_t buf_len;
|
||||
int func;
|
||||
int lib;
|
||||
struct string_table strings;
|
||||
struct string_table messages;
|
||||
khash_t(string_ht) *strings_index;
|
||||
khash_t(string_ht) *messages_index;
|
||||
};
|
||||
|
||||
static int string_table_add(struct string_table *t, const char *s)
|
||||
{
|
||||
if (!t->allocated) {
|
||||
t->allocated = 4096;
|
||||
t->strings = xmalloc(t->allocated * sizeof(struct string*));
|
||||
} else if (t->allocated <= t->size) {
|
||||
t->allocated *= 2;
|
||||
t->strings = xrealloc(t->strings, t->allocated * sizeof(struct string*));
|
||||
}
|
||||
|
||||
t->strings[t->size++] = make_string(s, strlen(s));
|
||||
return t->size - 1;
|
||||
}
|
||||
|
||||
static int asm_add_message(struct asm_state *state, const char *s)
|
||||
{
|
||||
char *u = utf2sjis(s, strlen(s));
|
||||
int i = string_table_add(&state->messages, u);
|
||||
free(u);
|
||||
return i;
|
||||
}
|
||||
|
||||
static int asm_add_string(struct asm_state *state, const char *s)
|
||||
{
|
||||
int ret;
|
||||
char *u = utf2sjis(s, strlen(s));
|
||||
khiter_t k = kh_put(string_ht, state->strings_index, u, &ret);
|
||||
if (!ret) {
|
||||
// nothing
|
||||
} else if (ret == 1) {
|
||||
kh_value(state->strings_index, k) = string_table_add(&state->strings, u);
|
||||
} else {
|
||||
ERROR("Hash table lookup failed (%d)", ret);
|
||||
}
|
||||
return kh_value(state->strings_index, k);
|
||||
}
|
||||
|
||||
static void init_asm_state(struct asm_state *state, struct ain *ain, uint32_t flags)
|
||||
{
|
||||
memset(state, 0, sizeof(*state));
|
||||
state->ain = ain;
|
||||
state->flags = flags;
|
||||
state->func = -1;
|
||||
state->lib = -1;
|
||||
state->strings_index = kh_init(string_ht);
|
||||
state->messages_index = kh_init(string_ht);
|
||||
}
|
||||
|
||||
static void fini_asm_state(struct asm_state *state)
|
||||
{
|
||||
kh_destroy(string_ht, state->strings_index);
|
||||
kh_destroy(string_ht, state->messages_index);
|
||||
}
|
||||
|
||||
static void asm_write_opcode(struct asm_state *state, uint16_t opcode)
|
||||
{
|
||||
// if (state->buf_len - state->buf_ptr <= (size_t)instruction_width(opcode)) {
|
||||
if (state->buf_len - state->buf_ptr <= 18) {
|
||||
if (!state->buf) {
|
||||
state->buf_len = 4096;
|
||||
state->buf = xmalloc(state->buf_len);
|
||||
} else {
|
||||
state->buf_len = state->buf_len * 2;
|
||||
state->buf = xrealloc(state->buf, state->buf_len);
|
||||
}
|
||||
}
|
||||
|
||||
state->buf[state->buf_ptr++] = opcode & 0xFF;
|
||||
state->buf[state->buf_ptr++] = (opcode & 0xFF00) >> 8;
|
||||
}
|
||||
|
||||
static void asm_write_argument(struct asm_state *state, uint32_t arg)
|
||||
{
|
||||
state->buf[state->buf_ptr++] = (arg & 0x000000FF);
|
||||
state->buf[state->buf_ptr++] = (arg & 0x0000FF00) >> 8;
|
||||
state->buf[state->buf_ptr++] = (arg & 0x00FF0000) >> 16;
|
||||
state->buf[state->buf_ptr++] = (arg & 0xFF000000) >> 24;
|
||||
}
|
||||
|
||||
struct instruction *asm_get_instruction(const char *name)
|
||||
{
|
||||
for (int i = 0; i < NR_OPCODES; i++) {
|
||||
if (!strcmp(name, instructions[i].name))
|
||||
return &instructions[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *parse_identifier(possibly_unused struct asm_state *state, char *s, int *n)
|
||||
{
|
||||
char *delim = strchr(s, '#');
|
||||
if (!delim) {
|
||||
*n = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
*delim = '\0';
|
||||
delim++;
|
||||
|
||||
char *endptr;
|
||||
long nn = strtol(delim, &endptr, 10);
|
||||
if (*delim && !*endptr) {
|
||||
*n = nn;
|
||||
return s;
|
||||
}
|
||||
|
||||
*delim = '#';
|
||||
ASM_ERROR(state, "Invalid identifier: '%s' (bad suffix)", s);
|
||||
}
|
||||
|
||||
static int32_t parse_integer_constant(possibly_unused struct asm_state *state, const char *arg)
|
||||
{
|
||||
char *endptr;
|
||||
errno = 0;
|
||||
long i = strtol(arg, &endptr, 0);
|
||||
if (errno || *endptr != '\0')
|
||||
ASM_ERROR(state, "Invalid integer constant: '%s'", arg);
|
||||
//if (i > INT32_MAX || i < INT32_MIN)
|
||||
// ASM_ERROR(state, "Integer would be truncated: %ld -> %d", i, (int32_t)i);
|
||||
return i;
|
||||
}
|
||||
|
||||
static uint32_t asm_resolve_arg(struct asm_state *state, enum instruction_argtype type, const char *arg)
|
||||
{
|
||||
if (state->flags & ASM_RAW)
|
||||
type = T_INT;
|
||||
|
||||
switch (type) {
|
||||
case T_INT:
|
||||
return parse_integer_constant(state, arg);
|
||||
case T_FLOAT: {
|
||||
char *endptr;
|
||||
union { int32_t i; float f; } v;
|
||||
errno = 0;
|
||||
v.f = strtof(arg, &endptr);
|
||||
if (errno || *endptr != '\0')
|
||||
ASM_ERROR(state, "Invalid float: %s", arg);
|
||||
return v.i;
|
||||
}
|
||||
case T_ADDR: {
|
||||
khiter_t k;
|
||||
k = kh_get(label_table, label_table, arg);
|
||||
if (k == kh_end(label_table))
|
||||
ASM_ERROR(state, "Unable to resolve label: '%s'", arg);
|
||||
return kh_value(label_table, k);
|
||||
}
|
||||
case T_FUNC: {
|
||||
char *u = utf2sjis(arg, strlen(arg));
|
||||
struct ain_function *f = ain_get_function(state->ain, u);
|
||||
free(u);
|
||||
if (!f)
|
||||
ASM_ERROR(state, "Unable to resolve function: '%s'", arg);
|
||||
return f - state->ain->functions;
|
||||
}
|
||||
case T_STRING: {
|
||||
if (state->flags & ASM_NO_STRINGS) {
|
||||
int32_t i = parse_integer_constant(state, arg);
|
||||
if (i < 0 || i >= state->ain->nr_strings)
|
||||
ASM_ERROR(state, "String index out of bounds: '%s'", arg);
|
||||
return i;
|
||||
}
|
||||
return asm_add_string(state, arg);
|
||||
}
|
||||
case T_MSG: {
|
||||
if (state->flags & ASM_NO_STRINGS) {
|
||||
int32_t i = parse_integer_constant(state, arg);
|
||||
if (i < 0 || i >= state->ain->nr_messages)
|
||||
ASM_ERROR(state, "Message index out of bounds: '%s'", arg);
|
||||
return i;
|
||||
}
|
||||
return asm_add_message(state, arg);
|
||||
}
|
||||
case T_LOCAL: {
|
||||
int n, count = 0;
|
||||
char *u = utf2sjis(arg, strlen(arg));
|
||||
u = parse_identifier(state, u, &n);
|
||||
struct ain_function *f = &state->ain->functions[state->func];
|
||||
for (int i = 0; i < f->nr_vars; i++) {
|
||||
if (!strcmp(u, f->vars[i].name)) {
|
||||
if (count < n) {
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
free(u);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ASM_ERROR(state, "Unable to resolve local variable: '%s'", arg);
|
||||
}
|
||||
case T_GLOBAL: {
|
||||
char *u = utf2sjis(arg, strlen(arg));
|
||||
for (int i = 0; i < state->ain->nr_globals; i++) {
|
||||
if (!strcmp(u, state->ain->globals[i].name)) {
|
||||
free(u);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ASM_ERROR(state, "Unable to resolve global variable: '%s'", arg);
|
||||
}
|
||||
case T_STRUCT: {
|
||||
char *u = utf2sjis(arg, strlen(arg));
|
||||
struct ain_struct *s = ain_get_struct(state->ain, u);
|
||||
free(u);
|
||||
if (!s)
|
||||
ASM_ERROR(state, "Unable to resolve struct: '%s'", arg);
|
||||
return s - state->ain->structures;
|
||||
}
|
||||
case T_SYSCALL: {
|
||||
for (int i = 0; i < NR_SYSCALLS; i++) {
|
||||
if (!strcmp(arg, syscalls[i].name))
|
||||
return i;
|
||||
}
|
||||
ASM_ERROR(state, "Unable to resolve system call: '%s'", arg);
|
||||
}
|
||||
case T_HLL: {
|
||||
for (int i = 0; i < state->ain->nr_libraries; i++) {
|
||||
if (!strcmp(arg, state->ain->libraries[i].name)) {
|
||||
state->lib = i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ASM_ERROR(state, "Unable to resolve library: '%s'", arg);
|
||||
}
|
||||
case T_HLLFUNC: {
|
||||
if (state->lib < 0)
|
||||
ERROR("Tried to resolve library function without active library?");
|
||||
int n, count = 0;
|
||||
char *u = utf2sjis(arg, strlen(arg));
|
||||
u = parse_identifier(state, u, &n);
|
||||
for (int i = 0; i < state->ain->libraries[state->lib].nr_functions; i++) {
|
||||
if (strcmp(u, state->ain->libraries[state->lib].functions[i].name))
|
||||
continue;
|
||||
if (count < n) {
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
state->lib = -1;
|
||||
free(u);
|
||||
return i;
|
||||
}
|
||||
ASM_ERROR(state, "Unable to resolve library function: '%s.%s'",
|
||||
state->ain->libraries[state->lib].name, arg);
|
||||
}
|
||||
case T_FILE: {
|
||||
if (!state->ain->nr_filenames)
|
||||
return atoi(arg);
|
||||
char *u = utf2sjis(arg, strlen(arg));
|
||||
for (int i = 0; i < state->ain->nr_filenames; i++) {
|
||||
if (!strcmp(u, state->ain->filenames[i])) {
|
||||
free(u);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ASM_ERROR(state, "Unable to resolve filename: '%s'", arg);
|
||||
}
|
||||
case T_DLG: {
|
||||
char *u = utf2sjis(arg, strlen(arg));
|
||||
for (int i = 0; i < state->ain->nr_delegates; i++) {
|
||||
if (!strcmp(u, state->ain->delegates[i].name)) {
|
||||
free(u);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ASM_ERROR(state, "Unable to resolve delegate: '%s'", arg);
|
||||
}
|
||||
default:
|
||||
ASM_ERROR(state, "Unhandled argument type: %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_assemble_jam(const char *filename, struct ain *ain, uint32_t flags)
|
||||
{
|
||||
struct asm_state state;
|
||||
init_asm_state(&state, ain, flags);
|
||||
|
||||
if (filename) {
|
||||
if (!strcmp(filename, "-"))
|
||||
yyin = stdin;
|
||||
else
|
||||
yyin = fopen(filename, "r");
|
||||
if (!yyin)
|
||||
ERROR("Opening input file '%s': %s", filename, strerror(errno));
|
||||
}
|
||||
label_table = kh_init(label_table);
|
||||
NOTICE("Parsing...");
|
||||
yyparse();
|
||||
|
||||
NOTICE("Encoding...");
|
||||
for (size_t i = 0; i < kv_size(*parsed_code); i++) {
|
||||
struct parse_instruction *instr = kv_A(*parsed_code, i);
|
||||
struct instruction *idef = &instructions[instr->opcode];
|
||||
|
||||
// NOTE: special case: we need to record the new function address in the ain structure
|
||||
if (idef->opcode == FUNC) {
|
||||
state.func = asm_resolve_arg(&state, T_INT, kv_A(*instr->args, 0)->text);
|
||||
state.ain->functions[state.func].address = state.buf_ptr + 6;
|
||||
asm_write_opcode(&state, FUNC);
|
||||
asm_write_argument(&state, state.func);
|
||||
continue;
|
||||
}
|
||||
|
||||
asm_write_opcode(&state, instr->opcode);
|
||||
for (int a = 0; a < idef->nr_args; a++) {
|
||||
asm_write_argument(&state, asm_resolve_arg(&state, idef->args[a], kv_A(*instr->args, a)->text));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < kv_size(*parsed_code); i++) {
|
||||
struct parse_instruction *instr = kv_A(*parsed_code, i);
|
||||
if (!instr->args)
|
||||
continue;
|
||||
for (size_t a = 0; a < kv_size(*instr->args); a++) {
|
||||
free_string(kv_A(*instr->args, a));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ain->nr_switches; i++) {
|
||||
if (ain->switches[i].case_type != 4)
|
||||
continue;
|
||||
for (int j = 0; j < ain->switches[i].nr_cases; j++) {
|
||||
if (ain->switches[i].cases[j].value >= ain->nr_strings)
|
||||
ERROR("Invalid string switch case");
|
||||
int c = asm_add_string(&state, ain->strings[ain->switches[i].cases[j].value]->text);
|
||||
ain->switches[i].cases[j].value = c;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (state.buf_ptr != ain->code_size)
|
||||
WARNING("CODE SIZE CHANGED");
|
||||
if (state.strings.size != (size_t)ain->nr_strings)
|
||||
WARNING("NR STRINGS CHANGED (%d -> %lu)", ain->nr_strings, state.strings.size);
|
||||
if (state.messages.size != (size_t)ain->nr_messages)
|
||||
WARNING("NR MESSAGES CHANGED (%d -> %lu)", ain->nr_messages, state.messages.size);
|
||||
*/
|
||||
|
||||
// TODO: rebuild switch table (use CASE pseudo-op)
|
||||
|
||||
free(ain->code);
|
||||
ain->code = state.buf;
|
||||
ain->code_size = state.buf_ptr;
|
||||
|
||||
ain_free_strings(ain);
|
||||
ain->strings = state.strings.strings;
|
||||
ain->nr_strings = state.strings.size;
|
||||
|
||||
ain_free_messages(ain);
|
||||
ain->messages = state.messages.strings;
|
||||
ain->nr_messages = state.messages.size;
|
||||
|
||||
// TODO: verify integrity of ain file (e.g. does MAIN still point to a valid function? etc.)
|
||||
|
||||
fini_asm_state(&state);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "asm_parser.tab.h"
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#define SAVE_TOKEN yylval.string = makes_string(yytext, yyleng)
|
||||
|
||||
char string_buf[65536];
|
||||
char *string_buf_ptr;
|
||||
|
||||
%}
|
||||
|
||||
%option noyywrap
|
||||
|
||||
%x str
|
||||
|
||||
id_char [^ \t\r\n:;\"]
|
||||
|
||||
%%
|
||||
|
||||
[ \t] ;
|
||||
;[^\n]*\n return NEWLINE;
|
||||
\n return NEWLINE;
|
||||
[a-zA-Z0-9_-]+: yylval.string = make_string(yytext, yyleng-1); return LABEL;
|
||||
({id_char}|:)*{id_char}+ yylval.string = make_string(yytext, yyleng); return IDENTIFIER;
|
||||
|
||||
|
||||
\" string_buf_ptr = string_buf; BEGIN(str);
|
||||
|
||||
<str>{
|
||||
\" {
|
||||
BEGIN(INITIAL);
|
||||
*string_buf_ptr = '\0';
|
||||
yylval.string = make_string(string_buf, strlen(string_buf));
|
||||
return IDENTIFIER;
|
||||
}
|
||||
|
||||
\n ERROR("Unterminated string literal");
|
||||
|
||||
\\n *string_buf_ptr++ = '\n';
|
||||
\\t *string_buf_ptr++ = '\t';
|
||||
\\r *string_buf_ptr++ = '\r';
|
||||
\\b *string_buf_ptr++ = '\b';
|
||||
\\f *string_buf_ptr++ = '\f';
|
||||
|
||||
\\(.|\n) *string_buf_ptr++ = yytext[1];
|
||||
|
||||
[^\\\n\"]+ {
|
||||
char *yptr = yytext;
|
||||
while (*yptr)
|
||||
*string_buf_ptr++ = *yptr++;
|
||||
}
|
||||
}
|
||||
|
||||
%%
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
/* A Bison parser, made by GNU Bison 3.3.2. */
|
||||
|
||||
/* Bison interface for Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2019 Free Software Foundation,
|
||||
Inc.
|
||||
|
||||
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 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
/* Undocumented macros, especially those whose name start with YY_,
|
||||
are private implementation details. Do not rely on them. */
|
||||
|
||||
#ifndef YY_YY_SRC_AINEDIT_48087CF_AINEDIT_EXE_ASM_PARSER_TAB_H_INCLUDED
|
||||
# define YY_YY_SRC_AINEDIT_48087CF_AINEDIT_EXE_ASM_PARSER_TAB_H_INCLUDED
|
||||
/* Debug traces. */
|
||||
#ifndef YYDEBUG
|
||||
# define YYDEBUG 0
|
||||
#endif
|
||||
#if YYDEBUG
|
||||
extern int yydebug;
|
||||
#endif
|
||||
/* "%code requires" blocks. */
|
||||
#line 9 "../src/ainedit/asm_parser.y" /* yacc.c:1921 */
|
||||
|
||||
#include <stdint.h>
|
||||
#include "khash.h"
|
||||
#include "kvec.h"
|
||||
|
||||
kv_decl(parse_instruction_list, struct parse_instruction*);
|
||||
kv_decl(parse_argument_list, struct string*);
|
||||
kv_decl(pointer_list, uint32_t*);
|
||||
|
||||
struct parse_instruction {
|
||||
uint16_t opcode;
|
||||
parse_argument_list *args;
|
||||
};
|
||||
|
||||
parse_instruction_list *parsed_code;
|
||||
|
||||
KHASH_MAP_INIT_STR(label_table, uint32_t);
|
||||
khash_t(label_table) *label_table;
|
||||
|
||||
#line 68 "src/ainedit/48087cf@@ainedit@exe/asm_parser.tab.h" /* yacc.c:1921 */
|
||||
|
||||
/* Token type. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
enum yytokentype
|
||||
{
|
||||
IDENTIFIER = 258,
|
||||
LABEL = 259,
|
||||
NEWLINE = 260
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Value type. */
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
|
||||
union YYSTYPE
|
||||
{
|
||||
#line 1 "../src/ainedit/asm_parser.y" /* yacc.c:1921 */
|
||||
|
||||
int token;
|
||||
struct string *string;
|
||||
parse_argument_list *args;
|
||||
struct parse_instruction *instr;
|
||||
parse_instruction_list *program;
|
||||
|
||||
#line 94 "src/ainedit/48087cf@@ainedit@exe/asm_parser.tab.h" /* yacc.c:1921 */
|
||||
};
|
||||
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
|
||||
extern YYSTYPE yylval;
|
||||
|
||||
int yyparse (void);
|
||||
|
||||
#endif /* !YY_YY_SRC_AINEDIT_48087CF_AINEDIT_EXE_ASM_PARSER_TAB_H_INCLUDED */
|
||||
@@ -0,0 +1,141 @@
|
||||
%union {
|
||||
int token;
|
||||
struct string *string;
|
||||
parse_argument_list *args;
|
||||
struct parse_instruction *instr;
|
||||
parse_instruction_list *program;
|
||||
}
|
||||
|
||||
%code requires {
|
||||
#include <stdint.h>
|
||||
#include "khash.h"
|
||||
#include "kvec.h"
|
||||
|
||||
kv_decl(parse_instruction_list, struct parse_instruction*);
|
||||
kv_decl(parse_argument_list, struct string*);
|
||||
kv_decl(pointer_list, uint32_t*);
|
||||
|
||||
struct parse_instruction {
|
||||
uint16_t opcode;
|
||||
parse_argument_list *args;
|
||||
};
|
||||
|
||||
parse_instruction_list *parsed_code;
|
||||
|
||||
KHASH_MAP_INIT_STR(label_table, uint32_t);
|
||||
khash_t(label_table) *label_table;
|
||||
}
|
||||
|
||||
%{
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "ainedit.h"
|
||||
#include "kvec.h"
|
||||
#include "system4.h"
|
||||
#include "system4/instructions.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
extern int yylex();
|
||||
void yyerror(const char *s) { ERROR("%s", s); }
|
||||
|
||||
static uint32_t instr_ptr;
|
||||
|
||||
static parse_instruction_list *make_program(void)
|
||||
{
|
||||
parse_instruction_list *program = xmalloc(sizeof(parse_instruction_list));
|
||||
kv_init(*program);
|
||||
instr_ptr = 0;
|
||||
return program;
|
||||
}
|
||||
|
||||
static void push_instruction(parse_instruction_list *program, struct parse_instruction *instr)
|
||||
{
|
||||
kv_push(struct parse_instruction*, *program, instr);
|
||||
instr_ptr += instruction_width(instr->opcode);
|
||||
}
|
||||
|
||||
static struct parse_instruction *make_instruction(struct string *name, parse_argument_list *args)
|
||||
{
|
||||
for (int i = 0; name->text[i]; i++) {
|
||||
name->text[i] = toupper(name->text[i]);
|
||||
}
|
||||
// check opcode
|
||||
struct instruction *info = asm_get_instruction(name->text);
|
||||
if (!info)
|
||||
ERROR("Invalid instruction: %s", name->text);
|
||||
// check argument count
|
||||
size_t nr_args = args ? kv_size(*args) : 0;
|
||||
if (nr_args != (size_t)info->nr_args) {
|
||||
fprintf(stderr, "In: '%s", info->name);
|
||||
for (size_t i = 0; i < nr_args; i++) {
|
||||
fprintf(stderr, " %s", kv_A(*args, i)->text);
|
||||
}
|
||||
fprintf(stderr, "'\n");
|
||||
ERROR("Wrong number of arguments for instruction '%s' (expected %d; got %lu)",
|
||||
name->text, info->nr_args, nr_args);
|
||||
}
|
||||
// NOTE: argument values checked on second pass
|
||||
|
||||
free_string(name);
|
||||
|
||||
struct parse_instruction *instr = xmalloc(sizeof (struct parse_instruction));
|
||||
instr->opcode = info->opcode;
|
||||
instr->args = args;
|
||||
return instr;
|
||||
}
|
||||
|
||||
static parse_argument_list *make_arglist(void)
|
||||
{
|
||||
parse_argument_list *args = xmalloc(sizeof(parse_argument_list));
|
||||
kv_init(*args);
|
||||
return args;
|
||||
}
|
||||
|
||||
static void push_arg(parse_argument_list *args, struct string *arg)
|
||||
{
|
||||
kv_push(struct string*, *args, arg);
|
||||
}
|
||||
|
||||
static void push_label(char *name)
|
||||
{
|
||||
int ret;
|
||||
khiter_t k = kh_put(label_table, label_table, name, &ret);
|
||||
if (!ret) {
|
||||
if (kh_value(label_table, k) != instr_ptr)
|
||||
ERROR("Duplicate label: %s", name);
|
||||
return;
|
||||
}
|
||||
kh_value(label_table, k) = instr_ptr;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%token <string> IDENTIFIER LABEL
|
||||
%token <token> NEWLINE
|
||||
|
||||
%type <program> lines
|
||||
%type <instr> line
|
||||
%type <args> args
|
||||
|
||||
%start program
|
||||
|
||||
%%
|
||||
|
||||
program : lines { parsed_code = $1; }
|
||||
;
|
||||
|
||||
lines : line { $$ = make_program(); if ($1) { push_instruction($$, $1); } }
|
||||
| lines line { if ($2) { push_instruction($1, $2); } }
|
||||
;
|
||||
|
||||
line : NEWLINE { $$ = NULL; }
|
||||
| LABEL { push_label($1->text); $$ = NULL; }
|
||||
| IDENTIFIER NEWLINE { $$ = make_instruction($1, NULL); }
|
||||
| IDENTIFIER args NEWLINE { $$ = make_instruction($1, $2); }
|
||||
|
||||
args : IDENTIFIER { $$ = make_arglist(); push_arg($$, $1); }
|
||||
| args IDENTIFIER { push_arg($1, $2); }
|
||||
;
|
||||
|
||||
%%
|
||||
@@ -0,0 +1,521 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "cJSON.h"
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
|
||||
static bool cJSON_GetObjectBool(const cJSON * const o, const char * const name, bool def)
|
||||
{
|
||||
cJSON *v = cJSON_GetObjectItem(o, name);
|
||||
if (v && !cJSON_IsBool(v))
|
||||
ERROR("Expected a boolean for '%s'", name);
|
||||
return v ? v->valueint : def;
|
||||
}
|
||||
|
||||
static int cJSON_GetObjectInteger(const cJSON * const o, const char * const name, int def)
|
||||
{
|
||||
cJSON *v = cJSON_GetObjectItem(o, name);
|
||||
if (v && !cJSON_IsNumber(v))
|
||||
ERROR("Expected a number for '%s'", name);
|
||||
return v ? v->valueint : def;
|
||||
}
|
||||
|
||||
static int cJSON_GetObjectInteger_NonNull(const cJSON * const o, const char * const name)
|
||||
{
|
||||
cJSON *v = cJSON_GetObjectItem(o, name);
|
||||
if (!v || !cJSON_IsNumber(v))
|
||||
ERROR("Expected a number for '%s'", name);
|
||||
return v->valueint;
|
||||
}
|
||||
|
||||
static char *cJSON_GetObjectString(const cJSON * const o, const char * const name)
|
||||
{
|
||||
cJSON *v = cJSON_GetObjectItem(o, name);
|
||||
if (v && !cJSON_IsString(v))
|
||||
ERROR("Expected string for '%s'", name);
|
||||
return v ? strdup(v->valuestring) : NULL;
|
||||
}
|
||||
|
||||
static char *cJSON_GetObjectString_NonNull(const cJSON * const o, const char * const name)
|
||||
{
|
||||
cJSON *v = cJSON_GetObjectItem(o, name);
|
||||
if (!v || !cJSON_IsString(v))
|
||||
ERROR("Expected string for '%s'", name);
|
||||
return strdup(v->valuestring);
|
||||
}
|
||||
|
||||
static cJSON *cJSON_GetObjectArray(const cJSON * const o, const char * const name)
|
||||
{
|
||||
cJSON *v = cJSON_GetObjectItem(o, name);
|
||||
if (v && !cJSON_IsArray(v))
|
||||
ERROR("Expected an array for '%s'", name);
|
||||
return v;
|
||||
}
|
||||
|
||||
static cJSON *cJSON_GetObjectArray_NonNull(const cJSON * const o, const char * const name)
|
||||
{
|
||||
cJSON *v = cJSON_GetObjectItem(o, name);
|
||||
if (!v || !cJSON_IsArray(v))
|
||||
ERROR("Expected an array for '%s'", name);
|
||||
return v;
|
||||
}
|
||||
|
||||
static void _read_type_declaration(cJSON *decl, struct ain_type *dst)
|
||||
{
|
||||
dst->data = cJSON_GetArrayItem(decl, 0)->valueint;
|
||||
dst->struc = cJSON_GetArrayItem(decl, 1)->valueint;
|
||||
dst->rank = cJSON_GetArrayItem(decl, 2)->valueint;
|
||||
}
|
||||
|
||||
static void read_type_declaration(cJSON *decl, struct ain_type *dst)
|
||||
{
|
||||
int size = cJSON_GetArraySize(decl);
|
||||
if (size < 3 || size > 4)
|
||||
ERROR("Invalid type declaration (array size = %d)", size);
|
||||
|
||||
_read_type_declaration(decl, dst);
|
||||
if (size == 4) {
|
||||
int i;
|
||||
cJSON *v, *a = cJSON_GetArrayItem(decl, 4);
|
||||
if (!cJSON_IsArray(a))
|
||||
ERROR("Non-array in array-type slot");
|
||||
if (cJSON_GetArraySize(a) == 0)
|
||||
return;
|
||||
|
||||
dst->array_type = xcalloc(cJSON_GetArraySize(a), sizeof(struct ain_variable));
|
||||
cJSON_ArrayForEachIndex(i, v, a) {
|
||||
if (!cJSON_IsArray(v))
|
||||
ERROR("Non-array in array-type list");
|
||||
if (cJSON_GetArraySize(v) != 3)
|
||||
ERROR("Invalid type declaration (array size = %d)", cJSON_GetArraySize(v));
|
||||
_read_type_declaration(v, &dst->array_type[i]);
|
||||
dst->array_type[i].array_type = &dst->array_type[i+1];
|
||||
}
|
||||
dst->array_type[i-1].array_type = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_variable_declaration(cJSON *decl, struct ain_variable *dst)
|
||||
{
|
||||
dst->name = cJSON_GetObjectString_NonNull(decl, "name");
|
||||
dst->name2 = cJSON_GetObjectString(decl, "name2");
|
||||
read_type_declaration(cJSON_GetObjectArray_NonNull(decl, "type"), &dst->type);
|
||||
|
||||
cJSON *v = cJSON_GetObjectItem(decl, "initval");
|
||||
if (v) {
|
||||
switch (dst->type.data) {
|
||||
case AIN_STRING:
|
||||
if (!cJSON_IsString(v))
|
||||
ERROR("Non-string initval for string variable");
|
||||
dst->initval.s = strdup(v->valuestring);
|
||||
break;
|
||||
case AIN_FLOAT:
|
||||
if (!cJSON_IsNumber(v))
|
||||
ERROR("Non-number initval for float variable");
|
||||
dst->initval.f = v->valuedouble;
|
||||
break;
|
||||
default:
|
||||
if (!cJSON_IsNumber(v))
|
||||
ERROR("Non-number initval for variable");
|
||||
dst->initval.i = v->valueint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dst->group_index = cJSON_GetObjectInteger(decl, "group-index", -1);
|
||||
}
|
||||
|
||||
static void read_function_declaration(cJSON *decl, struct ain_function *dst)
|
||||
{
|
||||
int i;
|
||||
cJSON *args, *vars, *v;
|
||||
|
||||
dst->address = cJSON_GetObjectInteger(decl, "address", 0);
|
||||
dst->name = cJSON_GetObjectString_NonNull(decl, "name");
|
||||
dst->is_label = cJSON_GetObjectBool(decl, "is-label", 0);
|
||||
read_type_declaration(cJSON_GetObjectArray_NonNull(decl, "return-type"), &dst->return_type);
|
||||
dst->is_lambda = cJSON_GetObjectInteger(decl, "unknown-bool", 0);
|
||||
dst->crc = cJSON_GetObjectInteger(decl, "crc", 0);
|
||||
|
||||
args = cJSON_GetObjectArray_NonNull(decl, "arguments");
|
||||
vars = cJSON_GetObjectArray_NonNull(decl, "variables");
|
||||
dst->nr_args = cJSON_GetArraySize(args);
|
||||
dst->nr_vars = dst->nr_args + cJSON_GetArraySize(vars);
|
||||
|
||||
i = 0;
|
||||
dst->vars = xcalloc(dst->nr_vars, sizeof(struct ain_variable));
|
||||
cJSON_ArrayForEach(v, args) {
|
||||
if (!cJSON_IsObject(v))
|
||||
ERROR("Non-object in argument list");
|
||||
read_variable_declaration(v, &dst->vars[i]);
|
||||
i++;
|
||||
}
|
||||
cJSON_ArrayForEach(v, vars) {
|
||||
if (!cJSON_IsObject(v))
|
||||
ERROR("Non-object in variable list");
|
||||
read_variable_declaration(v, &dst->vars[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_function_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
int i;
|
||||
cJSON *f;
|
||||
struct ain_function *functions = xcalloc(cJSON_GetArraySize(decl), sizeof(struct ain_function));
|
||||
cJSON_ArrayForEachIndex(i, f, decl) {
|
||||
if (!cJSON_IsObject(f))
|
||||
ERROR("Non-object in function list");
|
||||
read_function_declaration(f, &functions[i]);
|
||||
}
|
||||
|
||||
ain_free_functions(ain);
|
||||
ain->functions = functions;
|
||||
ain->nr_functions = i;
|
||||
}
|
||||
|
||||
static struct ain_variable *read_variable_declarations(cJSON *decl, int *n)
|
||||
{
|
||||
cJSON *v;
|
||||
int i;
|
||||
struct ain_variable *vars = xcalloc(cJSON_GetArraySize(decl), sizeof(struct ain_variable));
|
||||
cJSON_ArrayForEachIndex(i, v, decl) {
|
||||
if (!cJSON_IsObject(v))
|
||||
ERROR("Non-object in variable list");
|
||||
read_variable_declaration(v, &vars[i]);
|
||||
}
|
||||
*n = i;
|
||||
return vars;
|
||||
}
|
||||
|
||||
static void read_global_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
ain_free_globals(ain);
|
||||
ain->globals = read_variable_declarations(decl, &ain->nr_globals);
|
||||
}
|
||||
|
||||
static struct ain_interface *read_interface_list(cJSON *decl, int32_t *n)
|
||||
{
|
||||
*n = cJSON_GetArraySize(decl);
|
||||
struct ain_interface *iface = xcalloc(*n, sizeof(struct ain_interface));
|
||||
|
||||
cJSON *v;
|
||||
int i;
|
||||
cJSON_ArrayForEachIndex(i, v, decl) {
|
||||
if (!cJSON_IsArray(v))
|
||||
ERROR("Non-array in interface list");
|
||||
if (cJSON_GetArraySize(v) != 2)
|
||||
ERROR("Wrong size array in interface list");
|
||||
iface[i].struct_type = cJSON_GetArrayItem(v, 0)->valueint;
|
||||
iface[i].uk = cJSON_GetArrayItem(v, 1)->valueint;
|
||||
}
|
||||
|
||||
*n = i;
|
||||
return iface;
|
||||
}
|
||||
|
||||
static void read_structure_declaration(cJSON *decl, struct ain_struct *dst)
|
||||
{
|
||||
cJSON *a;
|
||||
dst->name = cJSON_GetObjectString_NonNull(decl, "name");
|
||||
if ((a = cJSON_GetObjectArray(decl, "interfaces"))) {
|
||||
dst->interfaces = read_interface_list(a, &dst->nr_interfaces);
|
||||
}
|
||||
dst->constructor = cJSON_GetObjectInteger(decl, "constructor", -1);
|
||||
dst->destructor = cJSON_GetObjectInteger(decl, "destructor", -1);
|
||||
if ((a = cJSON_GetObjectArray(decl, "members"))) {
|
||||
dst->members = read_variable_declarations(a, &dst->nr_members);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_structure_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
int i;
|
||||
cJSON *s;
|
||||
struct ain_struct *structs = xcalloc(cJSON_GetArraySize(decl), sizeof(struct ain_struct));
|
||||
cJSON_ArrayForEachIndex(i, s, decl) {
|
||||
if (!cJSON_IsObject(s))
|
||||
ERROR("Non-object in structure list");
|
||||
read_structure_declaration(s, &structs[i]);
|
||||
}
|
||||
|
||||
ain_free_structures(ain);
|
||||
ain->structures = structs;
|
||||
ain->nr_structures = i;
|
||||
}
|
||||
|
||||
static void read_library_declaration(cJSON *decl, struct ain_library *dst)
|
||||
{
|
||||
dst->name = cJSON_GetObjectString_NonNull(decl, "name");
|
||||
|
||||
int i;
|
||||
cJSON *f, *jfuns = cJSON_GetObjectArray_NonNull(decl, "functions");
|
||||
struct ain_hll_function *funs = xcalloc(cJSON_GetArraySize(jfuns), sizeof(struct ain_hll_function));
|
||||
cJSON_ArrayForEachIndex(i, f, jfuns) {
|
||||
funs[i].name = cJSON_GetObjectString_NonNull(f, "name");
|
||||
funs[i].data_type = cJSON_GetObjectInteger_NonNull(f, "return-type");
|
||||
|
||||
int j;
|
||||
cJSON *arg, *jargs = cJSON_GetObjectArray_NonNull(f, "arguments");
|
||||
struct ain_hll_argument *args = xcalloc(cJSON_GetArraySize(jargs), sizeof(struct ain_hll_argument));
|
||||
cJSON_ArrayForEachIndex(j, arg, jargs) {
|
||||
args[j].name = cJSON_GetObjectString_NonNull(arg, "name");
|
||||
args[j].data_type = cJSON_GetObjectInteger_NonNull(arg, "type");
|
||||
}
|
||||
funs[i].nr_arguments = j;
|
||||
funs[i].arguments = args;
|
||||
}
|
||||
|
||||
dst->nr_functions = i;
|
||||
dst->functions = funs;
|
||||
}
|
||||
|
||||
static void read_library_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
int i;
|
||||
cJSON *lib;
|
||||
struct ain_library *libs = xcalloc(cJSON_GetArraySize(decl), sizeof(struct ain_library));
|
||||
cJSON_ArrayForEachIndex(i, lib, decl) {
|
||||
if (!cJSON_IsObject(lib))
|
||||
ERROR("Non-object in library list");
|
||||
read_library_declaration(lib, &libs[i]);
|
||||
}
|
||||
|
||||
ain_free_libraries(ain);
|
||||
ain->libraries = libs;
|
||||
ain->nr_libraries = i;
|
||||
}
|
||||
|
||||
static void read_switch_declaration(cJSON *decl, struct ain_switch *dst)
|
||||
{
|
||||
dst->case_type = cJSON_GetObjectInteger_NonNull(decl, "case-type");
|
||||
dst->default_address = cJSON_GetObjectInteger_NonNull(decl, "default-address");
|
||||
|
||||
int i;
|
||||
cJSON *v, *a = cJSON_GetObjectArray_NonNull(decl, "cases");
|
||||
struct ain_switch_case *cases = xcalloc(cJSON_GetArraySize(a), sizeof(struct ain_switch_case));
|
||||
cJSON_ArrayForEachIndex(i, v, a) {
|
||||
if (!cJSON_IsObject(v))
|
||||
ERROR("Non-object in switch case list");
|
||||
cases[i].value = cJSON_GetObjectInteger_NonNull(v, "value");
|
||||
cases[i].address = cJSON_GetObjectInteger_NonNull(v, "address");
|
||||
}
|
||||
dst->cases = cases;
|
||||
dst->nr_cases = i;
|
||||
}
|
||||
|
||||
static void read_switch_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
int i;
|
||||
cJSON *s;
|
||||
struct ain_switch *switches = xcalloc(cJSON_GetArraySize(decl), sizeof(struct ain_switch));
|
||||
cJSON_ArrayForEachIndex(i, s, decl) {
|
||||
if (!cJSON_IsObject(s))
|
||||
ERROR("Non-object in switch list");
|
||||
read_switch_declaration(s, &switches[i]);
|
||||
}
|
||||
|
||||
ain_free_switches(ain);
|
||||
ain->switches = switches;
|
||||
ain->nr_switches = i;
|
||||
}
|
||||
|
||||
static char **read_string_array(cJSON *decl, int *n)
|
||||
{
|
||||
int i;
|
||||
cJSON *str;
|
||||
char **strings = xcalloc(cJSON_GetArraySize(decl), sizeof(char*));
|
||||
cJSON_ArrayForEachIndex(i, str, decl) {
|
||||
if (!cJSON_IsString(str))
|
||||
ERROR("Non-string in string list");
|
||||
strings[i] = strdup(str->valuestring);
|
||||
}
|
||||
*n = i;
|
||||
return strings;
|
||||
}
|
||||
|
||||
static void read_filename_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
ain_free_filenames(ain);
|
||||
ain->filenames = read_string_array(decl, &ain->nr_filenames);
|
||||
}
|
||||
|
||||
static void read_function_type_declaration(cJSON *decl, struct ain_function_type *dst)
|
||||
{
|
||||
dst->name = cJSON_GetObjectString_NonNull(decl, "name");
|
||||
read_type_declaration(cJSON_GetObjectArray_NonNull(decl, "return-type"), &dst->return_type);
|
||||
|
||||
int i = 0;
|
||||
cJSON *v;
|
||||
cJSON *args = cJSON_GetObjectArray_NonNull(decl, "arguments");
|
||||
cJSON *vars = cJSON_GetObjectArray_NonNull(decl, "variables");
|
||||
dst->nr_arguments = cJSON_GetArraySize(args);
|
||||
dst->nr_variables = cJSON_GetArraySize(vars) + dst->nr_arguments;
|
||||
struct ain_variable *variables = xcalloc(dst->nr_variables, sizeof(struct ain_variable));
|
||||
cJSON_ArrayForEach(v, args) {
|
||||
if (!cJSON_IsObject(v))
|
||||
ERROR("Non-object in argument list");
|
||||
read_variable_declaration(v, &variables[i]);
|
||||
i++;
|
||||
}
|
||||
cJSON_ArrayForEach(v, vars) {
|
||||
if (!cJSON_IsObject(v))
|
||||
ERROR("Non-object in variable list");
|
||||
read_variable_declaration(v, &variables[i]);
|
||||
i++;
|
||||
}
|
||||
dst->variables = variables;
|
||||
}
|
||||
|
||||
static struct ain_function_type *_read_function_type_declarations(cJSON *decl, int *n)
|
||||
{
|
||||
int i;
|
||||
cJSON *f;
|
||||
struct ain_function_type *types = xcalloc(cJSON_GetArraySize(decl), sizeof(struct ain_function_type));
|
||||
cJSON_ArrayForEachIndex(i, f, decl) {
|
||||
if (!cJSON_IsObject(f))
|
||||
ERROR("Non-object in function type list");
|
||||
read_function_type_declaration(f, &types[i]);
|
||||
}
|
||||
*n = i;
|
||||
return types;
|
||||
}
|
||||
|
||||
static void read_function_type_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
ain_free_function_types(ain);
|
||||
ain->function_types = _read_function_type_declarations(decl, &ain->nr_function_types);
|
||||
}
|
||||
|
||||
static void read_delegate_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
ain_free_delegates(ain);
|
||||
ain->delegates = _read_function_type_declarations(decl, &ain->nr_delegates);
|
||||
}
|
||||
|
||||
static void read_global_group_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
ain_free_global_groups(ain);
|
||||
ain->global_group_names = read_string_array(decl, &ain->nr_global_groups);
|
||||
}
|
||||
|
||||
static void read_enum_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
int i;
|
||||
cJSON *e;
|
||||
struct ain_enum *enums = xcalloc(cJSON_GetArraySize(decl), sizeof(struct ain_enum));
|
||||
cJSON_ArrayForEachIndex(i, e, decl) {
|
||||
if (!cJSON_IsObject(e))
|
||||
ERROR("Non-object in enum list");
|
||||
enums[i].name = cJSON_GetObjectString_NonNull(e, "name");
|
||||
|
||||
int j;
|
||||
cJSON *s, *syms = cJSON_GetObjectArray_NonNull(e, "values");
|
||||
char **symbols = xcalloc(cJSON_GetArraySize(syms), sizeof(char*));
|
||||
cJSON_ArrayForEachIndex(j, s, syms) {
|
||||
if (!cJSON_IsString(s))
|
||||
ERROR("Non-string in enum symbol list");
|
||||
symbols[i] = strdup(s->valuestring);
|
||||
}
|
||||
enums[i].nr_symbols = j;
|
||||
enums[i].symbols = symbols;
|
||||
}
|
||||
|
||||
ain_free_enums(ain);
|
||||
ain->enums = enums;
|
||||
ain->nr_enums = i;
|
||||
}
|
||||
|
||||
static void read_json_declarations(cJSON *decl, struct ain *ain)
|
||||
{
|
||||
cJSON *v;
|
||||
|
||||
// VERS
|
||||
ain->version = cJSON_GetObjectInteger(decl, "version", 0);
|
||||
// KEYC
|
||||
ain->keycode = cJSON_GetObjectInteger(decl, "keycode", 0);
|
||||
// FUNC
|
||||
if ((v = cJSON_GetObjectArray(decl, "functions")))
|
||||
read_function_declarations(v, ain);
|
||||
// GLOB
|
||||
if ((v = cJSON_GetObjectArray(decl, "globals")))
|
||||
read_global_declarations(v, ain);
|
||||
// STRT
|
||||
if ((v = cJSON_GetObjectArray(decl, "structures")))
|
||||
read_structure_declarations(v, ain);
|
||||
// MAIN
|
||||
ain->main = cJSON_GetObjectInteger(decl, "main", 0);
|
||||
// MSGF
|
||||
ain->msgf = cJSON_GetObjectInteger(decl, "msgf", 0);
|
||||
// HLL0
|
||||
if ((v = cJSON_GetObjectArray(decl, "libraries")))
|
||||
read_library_declarations(v, ain);
|
||||
// SWI0
|
||||
if ((v = cJSON_GetObjectArray(decl, "switches")))
|
||||
read_switch_declarations(v, ain);
|
||||
// GVER
|
||||
ain->game_version = cJSON_GetObjectInteger(decl, "game-version", 0);
|
||||
// FNAM
|
||||
if ((v = cJSON_GetObjectArray(decl, "filenames")))
|
||||
read_filename_declarations(v, ain);
|
||||
// OJMP
|
||||
ain->ojmp = cJSON_GetObjectInteger(decl, "ojmp", 0);
|
||||
// FNCT
|
||||
if ((v = cJSON_GetObjectArray(decl, "function-types")))
|
||||
read_function_type_declarations(v, ain);
|
||||
// DELG
|
||||
if ((v = cJSON_GetObjectArray(decl, "delegates")))
|
||||
read_delegate_declarations(v, ain);
|
||||
// OBJG
|
||||
if ((v = cJSON_GetObjectArray(decl, "global-groups")))
|
||||
read_global_group_declarations(v, ain);
|
||||
// ENUM
|
||||
if ((v = cJSON_GetObjectArray(decl, "enums")))
|
||||
read_enum_declarations(v, ain);
|
||||
}
|
||||
|
||||
void read_declarations(const char *filename, struct ain *ain)
|
||||
{
|
||||
FILE *f;
|
||||
long len;
|
||||
char *buf;
|
||||
|
||||
if (!(f = fopen(filename, "r")))
|
||||
ERROR("Failed to open '%s': %s", filename, strerror(errno));
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
buf = xmalloc(len + 1);
|
||||
if (fread(buf, len, 1, f) != 1)
|
||||
ERROR("Failed to read '%s': %s", filename, strerror(errno));
|
||||
|
||||
if (fclose(f))
|
||||
ERROR("Failed to close '%s': %s", filename, strerror(errno));
|
||||
|
||||
cJSON *j = cJSON_Parse(buf);
|
||||
if (!j)
|
||||
ERROR("Failed to parse JSON file '%s'", filename);
|
||||
|
||||
read_json_declarations(j, ain);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# sources for ainedit
|
||||
ainedit = ['../cJSON.c',
|
||||
'ainedit.c',
|
||||
'asm.c',
|
||||
'json.c',
|
||||
'repack.c',
|
||||
#'ainedit/parser.c',
|
||||
#'ainedit/tokens.c',
|
||||
]
|
||||
|
||||
if flex.found()
|
||||
lgen = generator(flex,
|
||||
output : '@BASENAME@.yy.c',
|
||||
arguments : ['-o', '@OUTPUT@', '@INPUT@'])
|
||||
lfiles = lgen.process('asm_lexer.l')
|
||||
else
|
||||
lfiles = 'asm_lexer.yy.c'
|
||||
endif
|
||||
|
||||
if bison.found()
|
||||
pgen = generator(bison,
|
||||
output: ['@BASENAME@.tab.c', '@BASENAME@.tab.h'],
|
||||
arguments : ['@INPUT@', '--defines=@OUTPUT1@', '--output=@OUTPUT0@'])
|
||||
pfiles = pgen.process('asm_parser.y')
|
||||
else
|
||||
pfiles = ['asm_parser.tab.c', 'asm_parser.tab.h']
|
||||
endif
|
||||
|
||||
executable('ainedit', ainedit, lfiles, pfiles,
|
||||
dependencies : [libm, zlib],
|
||||
c_args : ['-Wno-unused-parameter'],
|
||||
include_directories : incdir,
|
||||
link_with : libsys4)
|
||||
@@ -0,0 +1,439 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zlib.h>
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
struct ain_buffer {
|
||||
uint8_t *buf;
|
||||
size_t size;
|
||||
size_t ptr;
|
||||
};
|
||||
|
||||
static void alloc_ainbuf(struct ain_buffer *out, size_t size)
|
||||
{
|
||||
if (out->ptr + size < out->size)
|
||||
return;
|
||||
|
||||
size_t new_size = out->ptr + size;
|
||||
while (out->size <= new_size)
|
||||
out->size *= 2;
|
||||
out->buf = xrealloc(out->buf, out->size);
|
||||
}
|
||||
|
||||
static void _write_int32(uint8_t *buf, uint32_t v)
|
||||
{
|
||||
buf[0] = (v & 0x000000FF);
|
||||
buf[1] = (v & 0x0000FF00) >> 8;
|
||||
buf[2] = (v & 0x00FF0000) >> 16;
|
||||
buf[3] = (v & 0xFF000000) >> 24;
|
||||
}
|
||||
|
||||
static void write_int32(struct ain_buffer *out, uint32_t v)
|
||||
{
|
||||
alloc_ainbuf(out, 4);
|
||||
out->buf[out->ptr++] = (v & 0x000000FF);
|
||||
out->buf[out->ptr++] = (v & 0x0000FF00) >> 8;
|
||||
out->buf[out->ptr++] = (v & 0x00FF0000) >> 16;
|
||||
out->buf[out->ptr++] = (v & 0xFF000000) >> 24;
|
||||
}
|
||||
|
||||
static void write_bytes(struct ain_buffer *out, const uint8_t *bytes, size_t len)
|
||||
{
|
||||
alloc_ainbuf(out, len);
|
||||
memcpy(out->buf + out->ptr, bytes, len);
|
||||
out->ptr += len;
|
||||
}
|
||||
|
||||
static void write_string(struct ain_buffer *out, const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
write_bytes(out, (const uint8_t*)s, len+1);
|
||||
}
|
||||
|
||||
static void write_header(struct ain_buffer *out, const char *s)
|
||||
{
|
||||
write_bytes(out, (const uint8_t*)s, 4);
|
||||
}
|
||||
|
||||
static void write_array_type(struct ain_buffer *out, possibly_unused struct ain *ain, struct ain_type *t)
|
||||
{
|
||||
for (int i = 0; i < t[0].rank+1; i++) {
|
||||
write_int32(out, t[i].data);
|
||||
write_int32(out, t[i].struc);
|
||||
write_int32(out, t[i].rank);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_variable_type(struct ain_buffer *out, struct ain *ain, struct ain_type *t)
|
||||
{
|
||||
write_int32(out, t->data);
|
||||
write_int32(out, t->struc);
|
||||
write_int32(out, t->rank);
|
||||
|
||||
if (t->data == AIN_ARRAY || t->data == AIN_REF_ARRAY || t->data == AIN_ITERATOR || t->data == AIN_ENUM1)
|
||||
write_array_type(out, ain, t->array_type);
|
||||
}
|
||||
|
||||
static void write_return_type(struct ain_buffer *out, struct ain *ain, struct ain_type *t)
|
||||
{
|
||||
if (ain->version >= 11) {
|
||||
write_variable_type(out, ain, t);
|
||||
return;
|
||||
}
|
||||
|
||||
write_int32(out, t->data);
|
||||
write_int32(out, t->struc);
|
||||
}
|
||||
|
||||
static void write_variable(struct ain_buffer *out, struct ain *ain, struct ain_variable *v)
|
||||
{
|
||||
write_string(out, v->name);
|
||||
if (ain->version >= 12)
|
||||
write_string(out, v->name2);
|
||||
write_variable_type(out, ain, &v->type);
|
||||
if (ain->version >= 8) {
|
||||
write_int32(out, v->has_initval);
|
||||
if (v->has_initval) {
|
||||
switch (v->type.data) {
|
||||
case AIN_STRING:
|
||||
write_string(out, v->initval.s);
|
||||
case AIN_DELEGATE:
|
||||
case AIN_REF_TYPE:
|
||||
break;
|
||||
default:
|
||||
write_int32(out, v->initval.i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_function(struct ain_buffer *out, struct ain *ain, struct ain_function *f)
|
||||
{
|
||||
write_int32(out, f->address);
|
||||
write_string(out, f->name);
|
||||
if (ain->version > 0 && ain->version < 7)
|
||||
write_int32(out, f->is_label);
|
||||
write_return_type(out, ain, &f->return_type);
|
||||
write_int32(out, f->nr_args);
|
||||
write_int32(out, f->nr_vars);
|
||||
if (ain->version >= 11)
|
||||
write_int32(out, f->is_lambda);
|
||||
if (ain->version > 0)
|
||||
write_int32(out, f->crc);
|
||||
for (int i = 0; i < f->nr_vars; i++) {
|
||||
write_variable(out, ain, &f->vars[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_global(struct ain_buffer *out, struct ain *ain, struct ain_variable *g)
|
||||
{
|
||||
write_string(out, g->name);
|
||||
if (ain->version >= 12)
|
||||
write_string(out, g->name2);
|
||||
write_variable_type(out, ain, &g->type);
|
||||
if (ain->version >= 5)
|
||||
write_int32(out, g->group_index);
|
||||
}
|
||||
|
||||
static void write_initval(struct ain_buffer *out, possibly_unused struct ain *ain, struct ain_initval *v)
|
||||
{
|
||||
write_int32(out, v->global_index);
|
||||
write_int32(out, v->data_type);
|
||||
if (v->data_type == AIN_STRING)
|
||||
write_string(out, v->string_value);
|
||||
else
|
||||
write_int32(out, v->int_value);
|
||||
}
|
||||
|
||||
static void write_structure(struct ain_buffer *out, struct ain *ain, struct ain_struct *s)
|
||||
{
|
||||
write_string(out, s->name);
|
||||
if (ain->version >= 11) {
|
||||
write_int32(out, s->nr_interfaces);
|
||||
for (int i = 0; i < s->nr_interfaces; i++) {
|
||||
write_int32(out, s->interfaces[i].struct_type);
|
||||
write_int32(out, s->interfaces[i].uk);
|
||||
}
|
||||
}
|
||||
write_int32(out, s->constructor);
|
||||
write_int32(out, s->destructor);
|
||||
write_int32(out, s->nr_members);
|
||||
for (int i = 0; i < s->nr_members; i++) {
|
||||
write_variable(out, ain, &s->members[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_library(struct ain_buffer *out, possibly_unused struct ain *ain, struct ain_library *lib)
|
||||
{
|
||||
write_string(out, lib->name);
|
||||
write_int32(out, lib->nr_functions);
|
||||
for (int i = 0; i < lib->nr_functions; i++) {
|
||||
write_string(out, lib->functions[i].name);
|
||||
write_int32(out, lib->functions[i].data_type);
|
||||
write_int32(out, lib->functions[i].nr_arguments);
|
||||
for (int j = 0; j < lib->functions[i].nr_arguments; j++) {
|
||||
write_string(out, lib->functions[i].arguments[j].name);
|
||||
write_int32(out, lib->functions[i].arguments[j].data_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_switch(struct ain_buffer *out, possibly_unused struct ain *ain, struct ain_switch *s)
|
||||
{
|
||||
write_int32(out, s->case_type);
|
||||
write_int32(out, s->default_address);
|
||||
write_int32(out, s->nr_cases);
|
||||
for (int i = 0; i < s->nr_cases; i++) {
|
||||
write_int32(out, s->cases[i].value);
|
||||
write_int32(out, s->cases[i].address);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_function_type(struct ain_buffer *out, struct ain *ain, struct ain_function_type *f)
|
||||
{
|
||||
write_string(out, f->name);
|
||||
write_return_type(out, ain, &f->return_type);
|
||||
write_int32(out, f->nr_arguments);
|
||||
write_int32(out, f->nr_variables);
|
||||
for (int i = 0; i < f->nr_variables; i++) {
|
||||
write_variable(out, ain, &f->variables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_msg1_string(struct ain_buffer *out, possibly_unused struct ain *ain, struct string *msg)
|
||||
{
|
||||
write_int32(out, msg->size);
|
||||
|
||||
uint8_t *buf = xmalloc(msg->size);
|
||||
for (int i = 0; i < msg->size; i++) {
|
||||
buf[i] = msg->text[i];
|
||||
buf[i] += 0x60;
|
||||
buf[i] += (uint8_t)i;
|
||||
}
|
||||
|
||||
write_bytes(out, buf, msg->size);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static uint8_t *ain_flatten(struct ain *ain, size_t *len)
|
||||
{
|
||||
struct ain_buffer out = {
|
||||
.buf = xmalloc(256),
|
||||
.size = 256,
|
||||
.ptr = 0
|
||||
};
|
||||
|
||||
// VERS
|
||||
write_header(&out, "VERS");
|
||||
write_int32(&out, ain->version);
|
||||
// KEYC
|
||||
if (ain->KEYC.present) {
|
||||
write_header(&out, "KEYC");
|
||||
write_int32(&out, ain->keycode);
|
||||
}
|
||||
// CODE
|
||||
if (ain->CODE.present) {
|
||||
write_header(&out, "CODE");
|
||||
write_int32(&out, ain->code_size);
|
||||
write_bytes(&out, ain->code, ain->code_size);
|
||||
}
|
||||
// FUNC
|
||||
if (ain->FUNC.present) {
|
||||
write_header(&out, "FUNC");
|
||||
write_int32(&out, ain->nr_functions);
|
||||
for (int i = 0; i < ain->nr_functions; i++) {
|
||||
write_function(&out, ain, &ain->functions[i]);
|
||||
}
|
||||
}
|
||||
// GLOB
|
||||
if (ain->GLOB.present) {
|
||||
write_header(&out, "GLOB");
|
||||
write_int32(&out, ain->nr_globals);
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
write_global(&out, ain, &ain->globals[i]);
|
||||
}
|
||||
}
|
||||
// GSET
|
||||
if (ain->GSET.present) {
|
||||
write_header(&out, "GSET");
|
||||
write_int32(&out, ain->nr_initvals);
|
||||
for (int i = 0; i < ain->nr_initvals; i++) {
|
||||
write_initval(&out, ain, &ain->global_initvals[i]);
|
||||
}
|
||||
}
|
||||
// STRT
|
||||
if (ain->STRT.present) {
|
||||
write_header(&out, "STRT");
|
||||
write_int32(&out, ain->nr_structures);
|
||||
for (int i = 0; i < ain->nr_structures; i++) {
|
||||
write_structure(&out, ain, &ain->structures[i]);
|
||||
}
|
||||
}
|
||||
// MSG0
|
||||
if (ain->MSG0.present) {
|
||||
write_header(&out, "MSG0");
|
||||
write_int32(&out, ain->nr_messages);
|
||||
for (int i = 0; i < ain->nr_messages; i++) {
|
||||
write_bytes(&out, (uint8_t*)ain->messages[i]->text, ain->messages[i]->size+1);
|
||||
}
|
||||
}
|
||||
// MSG1
|
||||
if (ain->MSG1.present) {
|
||||
write_header(&out, "MSG1");
|
||||
write_int32(&out, ain->nr_messages);
|
||||
write_int32(&out, ain->msg1_uk);
|
||||
for (int i = 0; i < ain->nr_messages; i++) {
|
||||
write_msg1_string(&out, ain, ain->messages[i]);
|
||||
}
|
||||
}
|
||||
// MAIN
|
||||
if (ain->MAIN.present) {
|
||||
write_header(&out, "MAIN");
|
||||
write_int32(&out, ain->main);
|
||||
}
|
||||
// MSGF
|
||||
if (ain->MSGF.present) {
|
||||
write_header(&out, "MSGF");
|
||||
write_int32(&out, ain->msgf);
|
||||
}
|
||||
// HLL0
|
||||
if (ain->HLL0.present) {
|
||||
write_header(&out, "HLL0");
|
||||
write_int32(&out, ain->nr_libraries);
|
||||
for (int i = 0; i < ain->nr_libraries; i++) {
|
||||
write_library(&out, ain, &ain->libraries[i]);
|
||||
}
|
||||
}
|
||||
// SWI0
|
||||
if (ain->SWI0.present) {
|
||||
write_header(&out, "SWI0");
|
||||
write_int32(&out, ain->nr_switches);
|
||||
for (int i = 0; i < ain->nr_switches; i++) {
|
||||
write_switch(&out, ain, &ain->switches[i]);
|
||||
}
|
||||
}
|
||||
// GVER
|
||||
if (ain->GVER.present) {
|
||||
write_header(&out, "GVER");
|
||||
write_int32(&out, ain->game_version);
|
||||
}
|
||||
// STR0
|
||||
if (ain->STR0.present) {
|
||||
write_header(&out, "STR0");
|
||||
write_int32(&out, ain->nr_strings);
|
||||
for (int i = 0; i < ain->nr_strings; i++) {
|
||||
write_bytes(&out, (uint8_t*)ain->strings[i]->text, ain->strings[i]->size+1);
|
||||
}
|
||||
}
|
||||
// FNAM
|
||||
if (ain->FNAM.present) {
|
||||
write_header(&out, "FNAM");
|
||||
write_int32(&out, ain->nr_filenames);
|
||||
for (int i = 0; i < ain->nr_filenames; i++) {
|
||||
write_string(&out, ain->filenames[i]);
|
||||
}
|
||||
}
|
||||
// OJMP
|
||||
if (ain->OJMP.present) {
|
||||
write_header(&out, "OJMP");
|
||||
write_int32(&out, ain->ojmp);
|
||||
}
|
||||
// FNCT
|
||||
if (ain->FNCT.present) {
|
||||
write_header(&out, "FNCT");
|
||||
write_int32(&out, ain->fnct_size);
|
||||
write_int32(&out, ain->nr_function_types);
|
||||
for (int i = 0; i < ain->nr_function_types; i++) {
|
||||
write_function_type(&out, ain, &ain->function_types[i]);
|
||||
}
|
||||
}
|
||||
// DELG
|
||||
if (ain->DELG.present) {
|
||||
write_header(&out, "DELG");
|
||||
write_int32(&out, ain->delg_size);
|
||||
write_int32(&out, ain->nr_delegates);
|
||||
for (int i = 0; i < ain->nr_delegates; i++) {
|
||||
write_function_type(&out, ain, &ain->delegates[i]);
|
||||
}
|
||||
}
|
||||
// OBJG
|
||||
if (ain->OBJG.present) {
|
||||
write_header(&out, "OBJG");
|
||||
write_int32(&out, ain->nr_global_groups);
|
||||
for (int i = 0; i < ain->nr_global_groups; i++) {
|
||||
write_string(&out, ain->global_group_names[i]);
|
||||
}
|
||||
}
|
||||
// ENUM
|
||||
if (ain->ENUM.present) {
|
||||
write_header(&out, "ENUM");
|
||||
write_int32(&out, ain->nr_enums);
|
||||
for (int i = 0; i < ain->nr_enums; i++) {
|
||||
write_string(&out, ain->enums[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
*len = out.ptr;
|
||||
return out.buf;
|
||||
}
|
||||
|
||||
static uint8_t *ain_compress(uint8_t *buf, size_t *len)
|
||||
{
|
||||
unsigned long dst_len = *len * 1.001 + 12;
|
||||
uint8_t *dst = xmalloc(dst_len + 16);
|
||||
|
||||
memcpy(dst, "AI2\0\0\0\0", 8);
|
||||
_write_int32(dst+8, *len);
|
||||
|
||||
int r = compress2(dst+16, &dst_len, buf, *len, 1);
|
||||
if (r != Z_OK) {
|
||||
ERROR("compress failed");
|
||||
}
|
||||
free(buf);
|
||||
|
||||
_write_int32(dst+12, dst_len);
|
||||
*len = dst_len + 16;
|
||||
return dst;
|
||||
}
|
||||
|
||||
void ain_write(const char *filename, struct ain *ain)
|
||||
{
|
||||
size_t len;
|
||||
uint8_t *buf = ain_flatten(ain, &len);
|
||||
|
||||
if (ain->version <= 5)
|
||||
ain_decrypt(buf, len); // NOTE: this actually encrypts the buffer
|
||||
else
|
||||
buf = ain_compress(buf, &len);
|
||||
|
||||
FILE *out = fopen(filename, "w");
|
||||
if (!out)
|
||||
ERROR("Failed to open '%s': %s", filename, strerror(errno));
|
||||
if (fwrite(buf, len, 1, out) != 1)
|
||||
ERROR("Failed to write to '%s': %s", filename, strerror(errno));
|
||||
if (fclose(out))
|
||||
ERROR("Failed to close '%s': %s", filename, strerror(errno));
|
||||
|
||||
free(buf);
|
||||
}
|
||||
+17
-8
@@ -69,6 +69,16 @@ const struct syscall syscalls[NR_SYSCALLS] = {
|
||||
|
||||
// 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, \
|
||||
@@ -200,12 +210,12 @@ struct instruction instructions[NR_OPCODES] = {
|
||||
JMP ( SWITCH, 1, T_INT ),
|
||||
JMP ( STRSWITCH, 1, T_INT ),
|
||||
OP ( FUNC, 1, T_FUNC ),
|
||||
OP ( _EOF, 1, T_FILE ),
|
||||
_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_INT ),
|
||||
OP ( SH_STRUCTREF, 1, T_MEMB ),
|
||||
OP ( S_LENGTH, 0 ),
|
||||
OP ( S_LENGTHBYTE, 0 ),
|
||||
OP ( I_STRING, 0 ),
|
||||
@@ -220,7 +230,7 @@ struct instruction instructions[NR_OPCODES] = {
|
||||
OP ( S_GTE, 0 ),
|
||||
OP ( S_LENGTH2, 0 ),
|
||||
TODO ( S_LENGTHBYTE2, 0 ),
|
||||
TODO ( NEW, 2, T_INT, T_INT ),
|
||||
TODO ( NEW, 2, T_STRUCT, T_INT ), // FIXME: 2nd arg is T_FUNC *OR* -1
|
||||
OP ( DELETE, 0 ),
|
||||
TODO ( CHECKUDO, 0 ),
|
||||
OP ( A_REF, 0 ),
|
||||
@@ -280,7 +290,6 @@ struct instruction instructions[NR_OPCODES] = {
|
||||
OP ( A_FIND, 0 ),
|
||||
OP ( A_REVERSE, 0 ),
|
||||
|
||||
// FIXME: all types are guesses
|
||||
TODO ( SH_SR_ASSIGN, 0 ),
|
||||
TODO ( SH_MEM_ASSIGN_LOCAL, 2, T_MEMB, T_LOCAL ),
|
||||
TODO ( A_NUMOF_GLOB_1, 1, T_GLOBAL ),
|
||||
@@ -364,8 +373,8 @@ struct instruction instructions[NR_OPCODES] = {
|
||||
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_0X102, 0 ),
|
||||
TODO ( OP_0X103, 0 ),
|
||||
TODO ( OP_0X104, 0 ),
|
||||
TODO ( OP_0X105, 1, T_STRUCT ),
|
||||
};
|
||||
|
||||
+4
-10
@@ -46,12 +46,8 @@ xsystem4 = ['audio.c',
|
||||
'hll/SystemServiceEx.c',
|
||||
]
|
||||
|
||||
# sources for aindump
|
||||
aindump = ['cJSON.c',
|
||||
'aindump/aindump.c',
|
||||
'aindump/dasm.c',
|
||||
'aindump/json.c',
|
||||
]
|
||||
flex = find_program('flex', required: false)
|
||||
bison = find_program('bison', required: false)
|
||||
|
||||
xsystem4_deps = [libm, zlib, sdl2, sdl2ttf, sdl2mixer, ffi, gl, glew]
|
||||
if chibi.found()
|
||||
@@ -70,7 +66,5 @@ executable('xsystem4', xsystem4,
|
||||
include_directories : incdir,
|
||||
link_with : libsys4)
|
||||
|
||||
executable('aindump', aindump,
|
||||
dependencies : [libm, zlib],
|
||||
include_directories : incdir,
|
||||
link_with : libsys4)
|
||||
subdir('aindump')
|
||||
subdir('ainedit')
|
||||
|
||||
Reference in New Issue
Block a user