diff --git a/include/system4/ain.h b/include/system4/ain.h index ea1e5b4..0084006 100644 --- a/include/system4/ain.h +++ b/include/system4/ain.h @@ -155,6 +155,8 @@ struct ain_function { int32_t nr_vars; int32_t is_lambda; // XXX: if this = 1, then function is a lambda; but not all lambdas have this = 1 int32_t crc; + int32_t struct_type; + int32_t enum_type; struct ain_variable *vars; }; diff --git a/src/ain.c b/src/ain.c index 968a231..550342b 100644 --- a/src/ain.c +++ b/src/ain.c @@ -81,6 +81,69 @@ static void init_struct_ht(struct ain *ain) } } +static bool function_is_member_of(char *func_name, char *struct_name) +{ + while (*func_name && *struct_name && *func_name != '@' && *func_name == *struct_name) { + func_name++; + struct_name++; + } + return !*struct_name && *func_name == '@'; +} + +/* + * Returns true if func_name contains the '@' character. + * We can't use strchr here because '@' is valid as the second byte of a SJIS character. + */ +static bool is_member_function(char *func_name) +{ + while (*func_name) { + if (*func_name == '@') + return true; + if (SJIS_2BYTE(*func_name)) { + func_name++; + if (*func_name) + func_name++; + } else { + func_name++; + } + } + return false; +} + +/* + * Infer struct member functions from function names. + */ +static void init_member_functions(struct ain *ain) +{ + for (int f = 0; f < ain->nr_functions; f++) { + ain->functions[f].struct_type = -1; + ain->functions[f].enum_type = -1; + char *name = ain->functions[f].name; + if (!is_member_function(name)) + continue; + for (int s = 0; s < ain->nr_structures; s++) { + if (function_is_member_of(name, ain->structures[s].name)) { + ain->functions[f].struct_type = s; + break; + } + } + if (ain->functions[f].struct_type != -1) + continue; + // check enums + for (int e = 0; e < ain->nr_enums; e++) { + if (function_is_member_of(name, ain->enums[e].name)) { + ain->functions[f].enum_type = e; + break; + } + } + if (ain->functions[f].enum_type == -1) { + char *u = sjis2utf(name, 0); + WARNING("Failed to find struct type for function \"%s\"", u); + free(u); + } + } +} + static struct func_list *get_function(struct ain *ain, const char *name) { int ret; @@ -978,6 +1041,7 @@ struct ain *ain_open(const char *path, int *error) goto err; } distribute_initvals(ain); + init_member_functions(ain); free(buf); *error = AIN_SUCCESS; diff --git a/src/tools/aindump/macros.c b/src/tools/aindump/macros.c index 94fb7d3..40b50b5 100644 --- a/src/tools/aindump/macros.c +++ b/src/tools/aindump/macros.c @@ -254,6 +254,58 @@ static bool globalpage_macro(struct dasm_state *dasm) return true; } +static bool structassign_macro(struct dasm_state *dasm, struct ain_struct *s, struct ain_variable *m) +{ + int32_t val = dasm_arg(dasm, 0); + if (!check_next_opcode(dasm, ASSIGN)) + return false; + + fprintf(dasm->out, ".STRUCTASSIGN "); + dasm_print_identifier(dasm, s->name); + fputc(' ', dasm->out); + dasm_print_identifier(dasm, m->name); + fputc(' ', dasm->out); + fprintf(dasm->out, "%d\n", val); + return true; +} + +static bool structpage_macro(struct dasm_state *dasm) +{ + struct ain_function *f = &dasm->ain->functions[dasm->func]; + if (f->struct_type < 0) + return false; + if (!check_next_opcode(dasm, PUSH)) + return false; + + int32_t no = dasm_arg(dasm, 0); + struct ain_struct *s = &dasm->ain->structures[f->struct_type]; + if (no < 0 || no >= s->nr_members) + return false; + + struct ain_variable *m = &s->members[no]; + dasm_next(dasm); + if (check_opcode(dasm, REF)) { + fprintf(dasm->out, ".STRUCTREF "); + } else if (check_opcode(dasm, REFREF)) { + fprintf(dasm->out, ".STRUCTREFREF "); + } else if (check_opcode(dasm, INC)) { + fprintf(dasm->out, ".STRUCTINC "); + } else if (check_opcode(dasm, DEC)) { + fprintf(dasm->out, ".STRUCTDEC "); + } else if (check_opcode(dasm, PUSH)) { + return structassign_macro(dasm, s, m); + } else { + return false; + } + + dasm_print_identifier(dasm, s->name); + fputc(' ', dasm->out); + dasm_print_identifier(dasm, m->name); + fputc('\n', dasm->out); + + return true; +} + static bool _dasm_print_macro(struct dasm_state *dasm) { switch (dasm->instr->opcode) { @@ -261,6 +313,8 @@ static bool _dasm_print_macro(struct dasm_state *dasm) return localpage_macro(dasm); case PUSHGLOBALPAGE: return globalpage_macro(dasm); + case PUSHSTRUCTPAGE: + return structpage_macro(dasm); default: return false; } diff --git a/src/tools/ainedit/ainedit.c b/src/tools/ainedit/ainedit.c index 0ea7afe..6cd7265 100644 --- a/src/tools/ainedit/ainedit.c +++ b/src/tools/ainedit/ainedit.c @@ -40,6 +40,7 @@ static void usage(void) puts(" --inline-strings Read code in inline-strings mode"); puts(" --input-encoding Specify the text encoding of the input file(s) (default: UTF-8)"); puts(" --output-encoding Specify the text encoding of the output file (default: CP932)"); + puts(" --ain-encoding Specify the text encoding of the input AIN file"); //puts(" -p,--project Build AIN from project file"); } @@ -56,10 +57,12 @@ enum { LOPT_INLINE_STRINGS, LOPT_INPUT_ENCODING, LOPT_OUTPUT_ENCODING, + LOPT_AIN_ENCODING, }; iconv_t ain_conv; iconv_t print_conv; +iconv_t ain_input_conv; char *convert_text(iconv_t cd, const char *str); @@ -69,6 +72,16 @@ char *encode_text(const char *str) return convert_text(ain_conv, str); } +char *encode_text_to_input_format(char *str) +{ + return convert_text(ain_input_conv, str); +} + +char *encode_text_for_print(char *str) +{ + return convert_text(print_conv, str); +} + int main(int argc, char *argv[]) { initialize_instructions(); @@ -80,6 +93,7 @@ int main(int argc, char *argv[]) const char *output_file = NULL; const char *input_encoding = "UTF-8"; const char *output_encoding = "CP932"; + const char *ain_encoding = "CP932"; bool transcode = false; uint32_t flags = ASM_NO_STRINGS; while (1) { @@ -94,6 +108,7 @@ int main(int argc, char *argv[]) { "inline-strings", no_argument, 0, LOPT_INLINE_STRINGS }, { "input-encoding", required_argument, 0, LOPT_INPUT_ENCODING }, { "output-encoding", required_argument, 0, LOPT_OUTPUT_ENCODING }, + { "ain-encoding", required_argument, 0, LOPT_AIN_ENCODING }, }; int option_index = 0; int c; @@ -141,6 +156,9 @@ int main(int argc, char *argv[]) case LOPT_OUTPUT_ENCODING: output_encoding = optarg; break; + case LOPT_AIN_ENCODING: + ain_encoding = optarg; + break; case '?': ERROR("Unknown command line argument"); } @@ -161,7 +179,10 @@ int main(int argc, char *argv[]) if ((ain_conv = iconv_open(output_encoding, input_encoding)) == (iconv_t)-1) { ERROR("iconv_open: %s", strerror(errno)); } - if ((print_conv = iconv_open("UTF-8", output_encoding)) == (iconv_t)-1) { + if ((print_conv = iconv_open("UTF-8", input_encoding)) == (iconv_t)-1) { + ERROR("iconv_open: %s", strerror(errno)); + } + if ((ain_input_conv = iconv_open(ain_encoding, input_encoding)) == (iconv_t)-1) { ERROR("iconv_open: %s", strerror(errno)); } diff --git a/src/tools/ainedit/ainedit.h b/src/tools/ainedit/ainedit.h index cf27a49..c5ee5dd 100644 --- a/src/tools/ainedit/ainedit.h +++ b/src/tools/ainedit/ainedit.h @@ -44,6 +44,11 @@ enum asm_pseudo_opcode { PO_GLOBALINC, PO_GLOBALDEC, PO_GLOBALASSIGN, + PO_STRUCTREF, + PO_STRUCTREFREF, + PO_STRUCTINC, + PO_STRUCTDEC, + PO_STRUCTASSIGN, NR_PSEUDO_OPS }; @@ -67,5 +72,7 @@ void read_text(const char *filename, struct ain *ain); void ain_transcode(struct ain *ain); char *encode_text(const char *str); +char *encode_text_to_input_format(char *str); +char *encode_text_for_print(char *str); #endif /* AINEDIT_AINEDIT_H */ diff --git a/src/tools/ainedit/asm.c b/src/tools/ainedit/asm.c index 95c5cb1..909c70b 100644 --- a/src/tools/ainedit/asm.c +++ b/src/tools/ainedit/asm.c @@ -60,17 +60,22 @@ const struct instruction asm_pseudo_ops[NR_PSEUDO_OPS - PSEUDO_OP_OFFSET] = { PSEUDO_OP(PO_STR, ".STR", 2), PSEUDO_OP(PO_MSG, ".MSG", 2), MACRO(PO_LOCALREF, ".LOCALREF", 1, 10), - MACRO(PO_GLOBALREF, ".GLOBALREF", 1, 10), MACRO(PO_LOCALREFREF, ".LOCALREFREF", 1, 10), - MACRO(PO_GLOBALREFREF, ".GLOBALREFREF", 1, 10), MACRO(PO_LOCALINC, ".LOCALINC", 1, 10), MACRO(PO_LOCALDEC, ".LOCALDEC", 1, 10), MACRO(PO_LOCALASSIGN, ".LOCALASSIGN", 2, 16), MACRO(PO_LOCALDELETE, ".LOCALDELETE", 1, 24), MACRO(PO_LOCALCREATE, ".LOCALCREATE", 2, 34), + MACRO(PO_GLOBALREF, ".GLOBALREF", 1, 10), + MACRO(PO_GLOBALREFREF, ".GLOBALREFREF", 1, 10), MACRO(PO_GLOBALINC, ".GLOBALINC", 1, 10), MACRO(PO_GLOBALDEC, ".GLOBALDEC", 1, 10), MACRO(PO_GLOBALASSIGN, ".GLOBALASSIGN", 2, 16), + MACRO(PO_STRUCTREF, ".STRUCTREF", 2, 10), + MACRO(PO_STRUCTREFREF, ".STRUCTREFREF", 2, 10), + MACRO(PO_STRUCTINC, ".STRUCTINC", 2, 10), + MACRO(PO_STRUCTDEC, ".STRUCTDEC", 2, 10), + MACRO(PO_STRUCTASSIGN, ".STRUCTASSIGN", 3, 16), }; struct string_table { @@ -444,6 +449,29 @@ static void decompose_switch_index(struct asm_state *state, char *in, int *switc *case_out = n_case; } +static int get_member_no(struct asm_state *state, char *struct_name, char *_member_name) +{ + int member_no = -1; + int struct_no = asm_resolve_arg(state, PUSH, T_STRUCT, struct_name); + char *member_name = encode_text_to_input_format(_member_name); + + for (int i = 0; i < state->ain->structures[struct_no].nr_members; i++) { + if (!strcmp(member_name, state->ain->structures[struct_no].members[i].name)) { + member_no = i; + break; + } + } + free(member_name); + + if (member_no < 0) { + char *sname = encode_text_for_print(struct_name); + char *mname = encode_text_for_print(_member_name); + ASM_ERROR(state, "Unable to resolve struct member: %s.%s", sname, mname); + } + + return member_no; +} + void handle_pseudo_op(struct asm_state *state, struct parse_instruction *instr) { switch (instr->opcode) { @@ -597,6 +625,43 @@ void handle_pseudo_op(struct asm_state *state, struct parse_instruction *instr) asm_write_opcode(state, ASSIGN); break; } + case PO_STRUCTREF: { + asm_write_opcode(state, PUSHSTRUCTPAGE); + asm_write_opcode(state, PUSH); + asm_write_argument(state, get_member_no(state, kv_A(*instr->args, 0)->text, kv_A(*instr->args, 1)->text)); + asm_write_opcode(state, REF); + break; + } + case PO_STRUCTREFREF: { + asm_write_opcode(state, PUSHSTRUCTPAGE); + asm_write_opcode(state, PUSH); + asm_write_argument(state, get_member_no(state, kv_A(*instr->args, 0)->text, kv_A(*instr->args, 1)->text)); + asm_write_opcode(state, REFREF); + break; + } + case PO_STRUCTINC: { + asm_write_opcode(state, PUSHSTRUCTPAGE); + asm_write_opcode(state, PUSH); + asm_write_argument(state, get_member_no(state, kv_A(*instr->args, 0)->text, kv_A(*instr->args, 1)->text)); + asm_write_opcode(state, INC); + break; + } + case PO_STRUCTDEC: { + asm_write_opcode(state, PUSHSTRUCTPAGE); + asm_write_opcode(state, PUSH); + asm_write_argument(state, get_member_no(state, kv_A(*instr->args, 0)->text, kv_A(*instr->args, 1)->text)); + asm_write_opcode(state, DEC); + break; + } + case PO_STRUCTASSIGN: { + asm_write_opcode(state, PUSHSTRUCTPAGE); + asm_write_opcode(state, PUSH); + asm_write_argument(state, get_member_no(state, kv_A(*instr->args, 0)->text, kv_A(*instr->args, 1)->text)); + asm_write_opcode(state, PUSH); + asm_write_argument(state, asm_resolve_arg(state, PUSH, T_INT, kv_A(*instr->args, 2)->text)); + asm_write_opcode(state, ASSIGN); + break; + } } } @@ -687,10 +752,6 @@ void asm_assemble_jam(const char *filename, struct ain *ain, uint32_t flags) // 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, FUNC, 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); asm_enter_function(&state, asm_resolve_arg(&state, FUNC, T_INT, kv_A(*instr->args, 0)->text)); continue; } else if (idef->opcode == ENDFUNC) {