#include #include #include #include #include #ifdef _WIN32 # include # define read _read # define write _write #else # include #endif #include "gbi.h" #include "gfxd.h" #include "priv.h" static TLOCAL struct gfxd_state state; static int buffer_input_fn(void *buf, int count) { if (count > config.input_buf_size) count = config.input_buf_size; memcpy(buf, config.input_buf, count); config.input_buf += count; config.input_buf_size -= count; return count; } static int buffer_output_fn(const char *buf, int count) { if (count > config.output_buf_size) count = config.output_buf_size; memcpy(config.output_buf, buf, count); config.output_buf += count; config.output_buf_size -= count; return count; } static int fd_input_fn(void *buf, int count) { return read(config.input_fd, buf, count); } static int fd_output_fn(const char *buf, int count) { return write(config.output_fd, buf, count); } static void swap_words(Gfx *gfx) { uint8_t b[8]; uint8_t *pw = (void *) gfx; uint8_t *pb = b; int endian = config.endian; int wordsize = config.wordsize; for (int i = 0; i < 8 / wordsize; i++) { if (endian == gfxd_endian_host) { switch (wordsize) { case 1: { uint8_t w = *(uint8_t *) pw; *pb++ = w >> 0; break; } case 2: { uint16_t w = *(uint16_t *) pw; *pb++ = w >> 8; *pb++ = w >> 0; break; } case 4: { uint32_t w = *(uint32_t *) pw; *pb++ = w >> 24; *pb++ = w >> 16; *pb++ = w >> 8; *pb++ = w >> 0; break; } case 8: { uint64_t w = *(uint64_t *) pw; *pb++ = w >> 56; *pb++ = w >> 48; *pb++ = w >> 40; *pb++ = w >> 32; *pb++ = w >> 24; *pb++ = w >> 16; *pb++ = w >> 8; *pb++ = w >> 0; break; } } } else { for (int j = 0; j < wordsize; j++) { if (endian == gfxd_endian_little) *pb++ = pw[wordsize - 1 - j]; else *pb++ = pw[j]; } } pw += wordsize; } gfx->hi = ((uint32_t) b[0] << 24) | ((uint32_t) b[1] << 16) | ((uint32_t) b[2] << 8) | ((uint32_t) b[3] << 0); gfx->lo = ((uint32_t) b[4] << 24) | ((uint32_t) b[5] << 16) | ((uint32_t) b[6] << 8) | ((uint32_t) b[7] << 0); } static void get_more_input(void) { if (state.end_input != 0) return; char *recv_buf = (void *) &state.gfx[0]; while (state.n_gfx < sizeof(state.gfx) / sizeof(state.gfx[0])) { int n_read = sizeof(state.gfx) - state.n_byte; n_read = config.input_fn(&recv_buf[state.n_byte], n_read); if (n_read == 0) return; state.n_byte += n_read; while (state.n_gfx < state.n_byte / sizeof(Gfx)) { Gfx gfx = state.gfx[state.n_gfx]; gfxd_macro_t *m = &state.macro[state.n_gfx]; swap_words(&gfx); int ret = config.ucode->disas_fn(m, gfx.hi, gfx.lo); if (ret != 0 && config.stop_on_invalid != 0) { state.end_input = 1; state.ret = ret; return; } state.n_gfx++; } } } static int32_t typed_arg_i(int type, int idx) { const gfxd_value_t *v = gfxd_value_by_type(type, idx); if (v != NULL) return v->i; else return -1; } static uint32_t typed_arg_u(int type, int idx) { const gfxd_value_t *v = gfxd_value_by_type(type, idx); if (v != NULL) return v->u; else return 0; } TLOCAL struct gfxd_config config = { .ucode = NULL, .endian = gfxd_endian_big, .wordsize = 4, .arg = NULL, .stop_on_invalid = 1, .stop_on_end = 1, .emit_dec_color = 0, .emit_q_macro = 0, .emit_ext_macro = 0, .input_buf = NULL, .input_buf_size = 0, .input_fn = &buffer_input_fn, .output_buf = NULL, .output_buf_size = 0, .output_fn = &buffer_output_fn, .macro_fn = &gfxd_macro_dflt, .arg_fn = &gfxd_arg_dflt, .tlut_fn = NULL, .timg_fn = NULL, .cimg_fn = NULL, .zimg_fn = NULL, .dl_fn = NULL, .mtx_fn = NULL, .lookat_fn = NULL, .light_fn = NULL, .seg_fn = NULL, .vtx_fn = NULL, .vp_fn = NULL, .uctext_fn = NULL, .ucdata_fn = NULL, .dram_fn = NULL, }; void gfxd_input_buffer(const void *buf, int size) { config.input_buf = buf; config.input_buf_size = size; config.input_fn = &buffer_input_fn; } void gfxd_output_buffer(char *buf, int size) { config.output_buf = buf; config.output_buf_size = size; config.output_fn = &buffer_output_fn; } void gfxd_input_fd(int fd) { config.input_fd = fd; config.input_fn = &fd_input_fn; } void gfxd_output_fd(int fd) { config.output_fd = fd; config.output_fn = &fd_output_fn; } void gfxd_input_callback(gfxd_input_fn_t *fn) { if (fn != NULL) config.input_fn = fn; else gfxd_input_buffer(NULL, 0); } void gfxd_output_callback(gfxd_output_fn_t *fn) { if (fn != NULL) config.output_fn = fn; else gfxd_output_buffer(NULL, 0); } void gfxd_macro_fn(gfxd_macro_fn_t *fn) { if (fn != NULL) config.macro_fn = fn; else config.macro_fn = gfxd_macro_dflt; } void gfxd_arg_fn(gfxd_arg_fn_t *fn) { if (fn != NULL) config.arg_fn = fn; else config.arg_fn = gfxd_arg_dflt; } int gfxd_write(const void *buf, int count) { return config.output_fn(buf, count); } int gfxd_puts(const char *str) { return gfxd_write(str, strlen(str)); } int gfxd_printf(const char *fmt, ...) { char s[256]; va_list arg; va_start(arg, fmt); int n = vsnprintf(s, sizeof(s), fmt, arg); va_end(arg); if (n > sizeof(s) - 1) n = sizeof(s) - 1; return gfxd_write(s, n); } int gfxd_print_value(int type, const gfxd_value_t *value) { return config.ucode->arg_tbl[type].fn(value); } int gfxd_macro_dflt(void) { gfxd_macro_t *m = &state.macro[0]; const gfxd_macro_type_t *t = &config.ucode->macro_tbl[m->id]; const char *name = gfxd_macro_name(); if (name == NULL) { if (config.arg != NULL) { gfxd_puts(config.arg); gfxd_puts(" = "); } gfxd_puts("(Gfx){"); } else { gfxd_puts(name); gfxd_puts("("); if (config.arg != NULL) { gfxd_puts(config.arg); if (t->n_arg != 0) gfxd_puts(", "); } } for (int i = 0; i < t->n_arg; i++) { if (i != 0) gfxd_puts(", "); config.arg_fn(i); } if (name == NULL) gfxd_puts("}"); else gfxd_puts(")"); return 0; } int gfxd_arg_callbacks(int arg_num) { int id = gfxd_macro_id(); switch (gfxd_arg_type(arg_num)) { case gfxd_Tlut: { if (config.tlut_fn != NULL) { int32_t num; if (id == gfxd_DPLoadTLUT_pal16) num = 16; else if (id == gfxd_DPLoadTLUT_pal256) num = 256; else num = typed_arg_i(gfxd_Num, 0); return config.tlut_fn( typed_arg_u(gfxd_Tlut, 0), typed_arg_i(gfxd_Pal, 0), num); } break; } case gfxd_Timg: { if (config.timg_fn != NULL) { int32_t siz = typed_arg_i(gfxd_Siz, 0); if (siz == -1) siz = G_IM_SIZ_4b; return config.timg_fn( typed_arg_u(gfxd_Timg, 0), typed_arg_i(gfxd_Fmt, 0), siz, typed_arg_i(gfxd_Dim, 0), typed_arg_i(gfxd_Dim, 1), typed_arg_i(gfxd_Pal, 0)); } break; } case gfxd_Cimg: { if (config.cimg_fn != NULL) { return config.cimg_fn( typed_arg_u(gfxd_Cimg, 0), typed_arg_i(gfxd_Fmt, 0), typed_arg_i(gfxd_Siz, 0), typed_arg_i(gfxd_Dim, 0)); } break; } case gfxd_Zimg: { if (config.zimg_fn != NULL) { return config.zimg_fn( typed_arg_u(gfxd_Zimg, 0)); } break; } case gfxd_Dl: { if (config.dl_fn != NULL) { return config.dl_fn( typed_arg_u(gfxd_Dl, 0)); } break; } case gfxd_Mtxptr: { if (config.mtx_fn != NULL) { return config.mtx_fn( typed_arg_u(gfxd_Mtxptr, 0)); } break; } case gfxd_Lookatptr: { if (config.lookat_fn != NULL) { int32_t num; if (id == gfxd_SPLookAt) num = 2; else num = 1; return config.lookat_fn( typed_arg_u(gfxd_Lookatptr, 0), num); } break; } case gfxd_Lightptr: { if (config.light_fn != NULL) { int32_t num; if (id == gfxd_SPSetLights1) num = 1; else if (id == gfxd_SPSetLights2) num = 2; else if (id == gfxd_SPSetLights3) num = 3; else if (id == gfxd_SPSetLights4) num = 4; else if (id == gfxd_SPSetLights5) num = 5; else if (id == gfxd_SPSetLights6) num = 6; else if (id == gfxd_SPSetLights7) num = 7; else num = 1; return config.light_fn( typed_arg_u(gfxd_Lightptr, 0), num); } break; } case gfxd_Segptr: { if (config.seg_fn != NULL) { return config.seg_fn( typed_arg_u(gfxd_Segptr, 0), typed_arg_i(gfxd_Seg, 0)); } break; } case gfxd_Vtxptr: { if (config.vtx_fn != NULL) { return config.vtx_fn( typed_arg_u(gfxd_Vtxptr, 0), typed_arg_i(gfxd_Num, 0)); } break; } case gfxd_Vpptr: { if (config.vp_fn != NULL) { return config.vp_fn( typed_arg_u(gfxd_Vpptr, 0)); } break; } case gfxd_Uctext: { if (config.uctext_fn != NULL) { return config.uctext_fn( typed_arg_u(gfxd_Uctext, 0), 0x1000); } break; } case gfxd_Ucdata: { if (config.ucdata_fn != NULL) { uint32_t size; if (id == gfxd_SPLoadUcodeEx) size = typed_arg_u(gfxd_Size, 0); else size = 0x800; return config.ucdata_fn( typed_arg_u(gfxd_Ucdata, 0), size); } break; } case gfxd_Dram: { if (config.dram_fn != NULL) { return config.dram_fn( typed_arg_u(gfxd_Dram, 0), typed_arg_u(gfxd_Size, 0)); } break; } } return 0; } void gfxd_arg_dflt(int arg_num) { if (gfxd_arg_callbacks(arg_num) == 0) { gfxd_arg_t *a = &state.macro[0].arg[arg_num]; gfxd_print_value(a->type, &a->value); } } void gfxd_tlut_callback(gfxd_tlut_fn_t *fn) { config.tlut_fn = fn; } void gfxd_timg_callback(gfxd_timg_fn_t *fn) { config.timg_fn = fn; } void gfxd_cimg_callback(gfxd_cimg_fn_t *fn) { config.cimg_fn = fn; } void gfxd_zimg_callback(gfxd_zimg_fn_t *fn) { config.zimg_fn = fn; } void gfxd_dl_callback(gfxd_dl_fn_t *fn) { config.dl_fn = fn; } void gfxd_mtx_callback(gfxd_mtx_fn_t *fn) { config.mtx_fn = fn; } void gfxd_lookat_callback(gfxd_lookat_fn_t *fn) { config.lookat_fn = fn; } void gfxd_light_callback(gfxd_light_fn_t *fn) { config.light_fn = fn; } void gfxd_seg_callback(gfxd_seg_fn_t *fn) { config.seg_fn = fn; } void gfxd_vtx_callback(gfxd_vtx_fn_t *fn) { config.vtx_fn = fn; } void gfxd_vp_callback(gfxd_vp_fn_t *fn) { config.vp_fn = fn; } void gfxd_uctext_callback(gfxd_uctext_fn_t *fn) { config.uctext_fn = fn; } void gfxd_ucdata_callback(gfxd_ucdata_fn_t *fn) { config.ucdata_fn = fn; } void gfxd_dram_callback(gfxd_dram_fn_t *fn) { config.dram_fn = fn; } void gfxd_target(gfxd_ucode_t ucode) { config.ucode = ucode; } void gfxd_endian(int endian, int wordsize) { config.endian = endian; config.wordsize = wordsize; } void gfxd_dynamic(const char *arg) { config.arg = arg; } void gfxd_enable(int cap) { switch (cap) { case gfxd_stop_on_invalid: config.stop_on_invalid = 1; break; case gfxd_stop_on_end: config.stop_on_end = 1; break; case gfxd_emit_dec_color: config.emit_dec_color = 1; break; case gfxd_emit_q_macro: config.emit_q_macro = 1; break; case gfxd_emit_ext_macro: config.emit_ext_macro = 1; break; } } void gfxd_disable(int cap) { switch (cap) { case gfxd_stop_on_invalid: config.stop_on_invalid = 0; return; case gfxd_stop_on_end: config.stop_on_end = 0; return; case gfxd_emit_dec_color: config.emit_dec_color = 0; break; case gfxd_emit_q_macro: config.emit_q_macro = 0; break; case gfxd_emit_ext_macro: config.emit_ext_macro = 0; break; } } void gfxd_udata_set(void *ptr) { config.udata = ptr; } void *gfxd_udata_get(void) { return config.udata; } int gfxd_execute(void) { state.macro_offset = 0; state.n_byte = 0; state.n_gfx = 0; state.end_input = 0; state.ret = 0; for (;;) { get_more_input(); if (state.n_gfx == 0) break; gfxd_macro_t *m = &state.macro[0]; config.ucode->combine_fn(m, state.n_gfx); const gfxd_macro_type_t *t = &config.ucode->macro_tbl[m->id]; if (t->ext != 0 && config.emit_ext_macro == 0) { Gfx gfx = state.gfx[0]; swap_words(&gfx); t = &config.ucode->macro_tbl[gfxd_Invalid]; t->disas_fn(m, gfx.hi, gfx.lo); } int ret = config.macro_fn(); if (ret != 0) { state.ret = ret; break; } if (config.stop_on_end != 0 && (m->id == gfxd_SPBranchList || m->id == gfxd_SPEndDisplayList)) { break; } int n_pop = config.ucode->macro_tbl[m->id].n_gfx; int n_rem = state.n_gfx - n_pop; { int n_byte = n_rem * sizeof(gfxd_macro_t); memmove(&state.macro[0], &state.macro[n_pop], n_byte); state.n_gfx = n_rem; } { int n_byte = n_rem * sizeof(Gfx); memmove(&state.gfx[0], &state.gfx[n_pop], n_byte); state.n_byte = n_byte; } state.macro_offset += n_pop * sizeof(Gfx); } return state.ret; } int gfxd_macro_offset(void) { return state.macro_offset; } int gfxd_macro_packets(void) { return config.ucode->macro_tbl[state.macro[0].id].n_gfx; } const void *gfxd_macro_data(void) { return state.gfx; } int gfxd_macro_id(void) { return state.macro[0].id; } const char *gfxd_macro_name(void) { int id = state.macro[0].id; const gfxd_macro_type_t *t = &config.ucode->macro_tbl[id]; if (t->prefix == NULL && t->suffix == NULL) { return NULL; } else { static TLOCAL char buf[32]; char *p = buf; if (t->prefix != NULL) { const char *s = t->prefix; while (*s != '\0') *p++ = *s++; } *p++ = 'g'; if (config.arg == NULL) *p++ = 's'; if (t->suffix != NULL) { const char *s = t->suffix; while (*s != '\0') *p++ = *s++; } *p++ = '\0'; return buf; } } int gfxd_arg_count(void) { return config.ucode->macro_tbl[state.macro[0].id].n_arg; } int gfxd_arg_type(int arg_num) { return state.macro[0].arg[arg_num].type; } const char *gfxd_arg_name(int arg_num) { return state.macro[0].arg[arg_num].name; } int gfxd_arg_fmt(int arg_num) { return config.ucode->arg_tbl[state.macro[0].arg[arg_num].type].fmt; } const gfxd_value_t *gfxd_arg_value(int arg_num) { return &state.macro[0].arg[arg_num].value; } const gfxd_value_t *gfxd_value_by_type(int type, int idx) { gfxd_macro_t *m = &state.macro[0]; const gfxd_macro_type_t *t = &config.ucode->macro_tbl[m->id]; for (int i = 0; i < t->n_arg; i++) { gfxd_arg_t *a = &m->arg[i]; if (a->type == type) { if (idx == 0) return &a->value; else idx--; } } return NULL; } int gfxd_arg_valid(int arg_num) { return state.macro[0].arg[arg_num].bad == 0; }