Shipwright/ZAPDTR/lib/libgfxd/gfxd.c

864 lines
14 KiB
C

#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
# include <io.h>
# define read _read
# define write _write
#else
# include <unistd.h>
#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;
}