rts/src/gfx/ps1/src/sprite.c

246 lines
4.5 KiB
C

#include <gfx.h>
#include <ps1/gfx_private.h>
#include <psxgpu.h>
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
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;
}