#include #include #include #include #include #include #include #include #include void sprite_free(struct sprite *const src) { } int sprite_clone(const struct sprite *const src, struct sprite *const dst) { *dst = *src; return 0; } static void sprite_init(struct sprite *const s) { memset(s, 0, sizeof *s); s->sznext.f.sz = (sizeof *s - sizeof s->sznext) / sizeof (uint32_t); s->common.f.cmd = DRAW_MODE; s->cmd = 0x65; } struct tim_pos { uint16_t x, y, w, h; }; static void transfer_init(const struct tim_pos *const p) { while (!(GPU_CONTROL_PORT & (1 << 0x1c))) ; GPU_CONTROL_PORT = 0x04000000; GPU_DATA_PORT = 0x01000000; GPU_DATA_PORT = 0xE6000000; GPU_DATA_PORT = 0xA0000000; GPU_DATA_PORT = (p->y << 16) | p->x; GPU_DATA_PORT = (p->h << 16) | p->w; } static int transfer(const size_t sz, FILE *const f) { const size_t rem = sz % sizeof (uint32_t); if (sz >= sizeof (uint32_t)) { for (size_t i = 0; i < sz / sizeof (uint32_t); i++) { uint32_t pix; if (!fread(&pix, sizeof pix, 1, f)) { fprintf(stderr, "could not read CLUT word %zu/%zu\n", i, sz / sizeof pix); return -1; } GPU_DATA_PORT = pix; } } if (rem) { uint32_t pix = 0; if (!fread(&pix, rem, 1, f)) { fprintf(stderr, "failed reading remaining %zu bytes\n", rem); return -1; } GPU_DATA_PORT = pix; } return 0; } struct header { uint32_t sz; struct tim_pos pos; }; static int upload_clut(struct sprite *const s, FILE *const f) { struct header clut; if (!fread(&clut, sizeof clut, 1, f)) { fprintf(stderr, "no CLUT pos data found\n"); return -1; } transfer_init(&clut.pos); const size_t sz = clut.sz - sizeof clut; if (transfer(sz, f)) return -1; s->clutid = get_clutid(clut.pos.x, clut.pos.y); return 0; } enum bpp { BPP_4 = 0, BPP_8 = 1, BPP_16 = 2, BPP_24 = 4 }; static int upload_img(struct sprite *const s, const enum bpp bpp, FILE *const f) { struct header img; if (!fread(&img, sizeof img, 1, f)) { fprintf(stderr, "could not extract image header\n"); return -1; } transfer_init(&img.pos); const size_t sz = img.sz - sizeof img; if (transfer(sz, f)) return -1; enum { VRAM_X = 1024, VRAM_Y = 512, TPAGE_WIDTH = 64 }; s->common.f.tpagex = img.pos.x / TPAGE_WIDTH; s->common.f.tpagey = img.pos.y / (VRAM_Y / 2); s->u = img.pos.x % TPAGE_WIDTH; s->v = img.pos.y % (VRAM_Y / 2); switch (bpp) { case BPP_4: s->w = img.pos.w * 4; s->u <<= 2; break; case BPP_8: s->w = img.pos.w * 2; s->u <<= 1; break; case BPP_16: s->w = img.pos.w; break; case BPP_24: s->w = img.pos.w + (img.pos.w / 2); break; } s->h = img.pos.h; return 0; } int sprite_from_fp(struct sprite *const s, FILE *const f) { int ret = -1; sprite_init(s); struct { uint32_t version; enum bpp bpp :3; bool has_clut :1; uint32_t :28; } h; enum {VERSION_ID = 0x10}; if (!fread(&h, sizeof h, 1, f)) { fprintf(stderr, "TIM header not found\n"); goto end; } else if (h.version != VERSION_ID) { fprintf(stderr, "%s: invalid TIM header %#" PRIx32 "\n", h.version); goto end; } else if (h.bpp == BPP_24) { fprintf(stderr, "24-bit mode unsupported\n"); goto end; } else if ((h.has_clut && upload_clut(s, f)) || upload_img(s, h.bpp, f)) goto end; s->common.f.bpp = h.bpp ? __builtin_ctz(h.bpp) + 1 : 0; ret = 0; end: return ret; } int sprite_from_file_ex(const char *const path, struct sprite *const s) { int ret = -1; FILE *f = NULL; if (!path) { errno = EINVAL; goto end; } f = fopen(path, "rb"); if (!f) { fprintf(stderr, "could not open %s: %s\n", path, strerror(errno)); goto end; } else if (sprite_from_fp(s, f)) { fprintf(stderr, "%s: sprite_from_fp failed\n", path); goto end; } end: if (f) fclose(f); return ret; }