pinboid/src/gfx/src/sprite.c

233 lines
4.7 KiB
C

#include <gfx.h>
#include <psxgpu.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void gfx_sprite_init(struct gfx_sprite *const s)
{
extern unsigned int draw_mode_packet;
memset(s, 0, sizeof *s);
s->sznext.sz = (sizeof (*s) - sizeof s->sznext) / sizeof (uint32_t);
s->common.mask = draw_mode_packet;
s->cmd = 0x64;
s->r = s->g = s->b = NORMAL_LUMINANCE;
}
static void transfer_init(const uint16_t x, const uint16_t y,
const uint16_t w, const uint16_t h)
{
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 = (y << 16) | x;
GPU_DATA_PORT = (h << 16) | w;
}
struct tim_pos
{
uint16_t x, y, w, h;
};
static int transfer(const char *const path, 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))
{
printf("%s: could not read CLUT word %zu/%zu\n",
path, i, sz / sizeof pix);
return -1;
}
GPU_DATA_PORT = pix;
}
}
if (rem)
{
uint32_t pix = 0;
if (!fread(&pix, rem, 1, f))
{
printf("%s: failed reading remaining %zu bytes\n", path, rem);
return -1;
}
GPU_DATA_PORT = pix;
}
return 0;
}
static int upload_clut(const char *const path, struct gfx_sprite *const s,
FILE *const f)
{
int ret = -1;
struct
{
uint32_t sz;
struct tim_pos pos;
uint16_t colors;
uint16_t num;
} clut;
if (!fread(&clut, sizeof clut, 1, f))
{
printf("%s: No CLUT pos data found\n", path);
return -1;
}
transfer_init(clut.pos.x, clut.pos.y, clut.pos.w, clut.pos.h);
const size_t sz = clut.sz - sizeof clut + sizeof clut.sz;
if (transfer(path, 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(const char const *path, struct gfx_sprite *const s,
const enum bpp bpp, FILE *const f)
{
struct tim_pos imgpos;
if (!fread(&imgpos, sizeof imgpos, 1, f))
{
printf("%s: No CLUT pos data found\n", path);
return -1;
}
transfer_init(imgpos.x, imgpos.y, imgpos.w, imgpos.h);
/* ftell and fseek are limited to 2 GiB when sizeof (long) == 4,
* but that's already fine for PSX games. */
const long off = ftell(f);
fseek(f, 0, SEEK_END);
const long sz = ftell(f) - off;
fseek(f, off, SEEK_SET);
if (transfer(path, sz, f))
return -1;
switch (bpp)
{
case BPP_4:
s->w = imgpos.w * 4;
break;
case BPP_8:
s->w = imgpos.w * 2;
break;
case BPP_16:
s->w = imgpos.w;
break;
case BPP_24:
s->w = imgpos.w + (imgpos.w / 2);
break;
}
s->h = imgpos.h;
enum
{
VRAM_X = 1024,
VRAM_Y = 512,
TPAGE_WIDTH = 64
};
s->common.tpagex = imgpos.x / TPAGE_WIDTH;
s->common.tpagey = imgpos.y / (VRAM_Y / 2);
s->u = imgpos.x % TPAGE_WIDTH;
s->v = imgpos.y % (VRAM_Y / 2);
return 0;
}
int gfx_sprite_from_file(const char *const path, struct gfx_sprite *const s)
{
int ret = -1;
FILE *f = NULL;
if (!path)
{
errno = EINVAL;
goto end;
}
f = fopen(path, "rb");
if (!f)
{
printf("could not open %s: %s\n", path, strerror(errno));
goto end;
}
gfx_sprite_init(s);
struct
{
uint32_t version;
uint32_t bpp :3;
uint32_t has_clut :1;
uint32_t :28;
} h;
enum {VERSION_ID = 0x10};
if (!fread(&h, sizeof h, 1, f))
{
printf("%s: TIM header not found\n", path);
goto end;
}
else if (h.version != VERSION_ID)
{
printf("%s: invalid TIM header 0x%08X\n", h.version);
goto end;
}
else if (h.bpp == BPP_24)
{
printf("%s: 24-bit mode unsupported\n", path);
goto end;
}
else if (h.has_clut && upload_clut(path, s, f))
goto end;
else if (upload_img(path, s, h.bpp, f))
goto end;
s->common.bpp = h.bpp ? __builtin_ctz(h.bpp) + 1 : 0;
ret = 0;
end:
if (f)
{
fclose(f);
}
return ret;
}