aboutsummaryrefslogtreecommitdiff
path: root/src/gfx
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2021-07-03 00:49:03 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2022-03-30 08:20:20 +0200
commit6b9f686913efc3725b2690033cd4f398e07076ba (patch)
treee9aa91a6b9f617d78123ebe7ad272fc42a60d306 /src/gfx
parentc9e6ae44a9aeb89b3f48f3443d6baa80103f7445 (diff)
downloadjancity-6b9f686913efc3725b2690033cd4f398e07076ba.tar.gz
Add project source code
Diffstat (limited to 'src/gfx')
-rw-r--r--src/gfx/CMakeLists.txt54
-rw-r--r--src/gfx/inc/gfx.h37
-rw-r--r--src/gfx/privinc/gfx_private.h22
-rw-r--r--src/gfx/ps1/inc/gfx/port.h142
-rw-r--r--src/gfx/ps1/privinc/ps1/gfx_private.h25
-rw-r--r--src/gfx/ps1/src/4line.c14
-rw-r--r--src/gfx/ps1/src/env.c42
-rw-r--r--src/gfx/ps1/src/heap.c19
-rw-r--r--src/gfx/ps1/src/init.c16
-rw-r--r--src/gfx/ps1/src/quad.c23
-rw-r--r--src/gfx/ps1/src/rect.c23
-rw-r--r--src/gfx/ps1/src/sort.c75
-rw-r--r--src/gfx/ps1/src/sprite.c244
-rw-r--r--src/gfx/sdl-1.2/inc/gfx/port.h52
-rw-r--r--src/gfx/sdl-1.2/privinc/sdl-1.2/gfx_private.h19
-rw-r--r--src/gfx/sdl-1.2/src/env.c82
-rw-r--r--src/gfx/sdl-1.2/src/heap.c11
-rw-r--r--src/gfx/sdl-1.2/src/line.c15
-rw-r--r--src/gfx/sdl-1.2/src/quad.c18
-rw-r--r--src/gfx/sdl-1.2/src/rect.c33
-rw-r--r--src/gfx/sdl-1.2/src/sort.c22
-rw-r--r--src/gfx/sdl-1.2/src/sprite.c75
-rw-r--r--src/gfx/src/heap.c56
23 files changed, 1119 insertions, 0 deletions
diff --git a/src/gfx/CMakeLists.txt b/src/gfx/CMakeLists.txt
new file mode 100644
index 0000000..c449be5
--- /dev/null
+++ b/src/gfx/CMakeLists.txt
@@ -0,0 +1,54 @@
+set(src "src/heap.c")
+set(inc "inc")
+set(privinc "privinc")
+set(deps util)
+
+if(PS1_BUILD)
+ set(src ${src}
+ "ps1/src/4line.c"
+ "ps1/src/env.c"
+ "ps1/src/heap.c"
+ "ps1/src/init.c"
+ "ps1/src/rect.c"
+ "ps1/src/sort.c"
+ "ps1/src/sprite.c"
+ "ps1/src/quad.c")
+ set(inc ${inc} "ps1/inc")
+ set(privinc ${privinc} "ps1/privinc")
+ set(privdeps ${privdeps} system)
+elseif(SDL1_2_BUILD)
+ set(inc ${inc} "sdl-1.2/inc")
+ set(privinc ${privinc} "sdl-1.2/privinc")
+ set(src ${src}
+ "sdl-1.2/src/env.c"
+ "sdl-1.2/src/heap.c"
+ "sdl-1.2/src/line.c"
+ "sdl-1.2/src/rect.c"
+ "sdl-1.2/src/sort.c"
+ "sdl-1.2/src/sprite.c"
+ "sdl-1.2/src/quad.c")
+ set(deps ${deps} SDL)
+endif()
+
+add_library(gfx ${src})
+target_include_directories(gfx PUBLIC ${inc})
+target_include_directories(gfx PRIVATE ${privinc})
+target_link_libraries(gfx PUBLIC ${deps} PRIVATE ${privdeps})
+
+if(PS1_BUILD)
+ set(modes VMODE_PAL VMODE_NTSC)
+
+ if(VIDEO_MODE)
+ if(NOT "${VIDEO_MODE}" IN_LIST modes)
+ message(FATAL_ERROR "Invalid video mode ${VIDEO_MODE}. Available options:\n"
+ "${modes}\n"
+ "Run CMake again using one of the available video modes e.g.: cmake .. -DVIDEO_MODE=VMODE_PAL")
+ endif()
+
+ target_compile_definitions(gfx PRIVATE VIDEO_MODE=${VIDEO_MODE})
+ else()
+ message(FATAL_ERROR "Please define video mode. Available options:\n"
+ "${modes}\n"
+ "Run CMake again using one of the available video modes e.g.: cmake .. -DVIDEO_MODE=VMODE_PAL")
+ endif()
+endif()
diff --git a/src/gfx/inc/gfx.h b/src/gfx/inc/gfx.h
new file mode 100644
index 0000000..0c30911
--- /dev/null
+++ b/src/gfx/inc/gfx.h
@@ -0,0 +1,37 @@
+#ifndef GFX_H
+#define GFX_H
+
+#include <gfx/port.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int gfx_init(void);
+void gfx_draw(void);
+void gfx_sync(void);
+void sprite_sort(struct sprite *s);
+int sprite_clone(const struct sprite *src, struct sprite *dst);
+void quad_sort(struct quad *q);
+void rect_sort(struct rect *r);
+void stp_4line_sort(struct stp_4line *l);
+int sprite_from_fp(struct sprite *s, FILE *f);
+struct sprite *sprite_get(void);
+struct quad *quad_get(void);
+struct rect *rect_get(bool semitrans);
+struct stp_4line *stp_4line_get(void);
+int quad_from_sprite(const struct sprite *s, struct quad *q);
+bool gfx_inside_drawenv(short x, short y, short w, short h);
+void gfx_deinit(void);
+void sprite_free(struct sprite *src);
+
+extern int screen_w, screen_h;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_H */
diff --git a/src/gfx/privinc/gfx_private.h b/src/gfx/privinc/gfx_private.h
new file mode 100644
index 0000000..0553362
--- /dev/null
+++ b/src/gfx/privinc/gfx_private.h
@@ -0,0 +1,22 @@
+#ifndef GFX_PRIVATE_H
+#define GFX_PRIVATE_H
+
+#include <gfx.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void rect_init(struct rect *r);
+void semitrans_rect_init(struct rect *r);
+void stp_4line_init(struct stp_4line *l);
+void *gfx_port_heap(size_t *n);
+void gfx_reset_heap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_PRIVATE_H */
diff --git a/src/gfx/ps1/inc/gfx/port.h b/src/gfx/ps1/inc/gfx/port.h
new file mode 100644
index 0000000..dec00c3
--- /dev/null
+++ b/src/gfx/ps1/inc/gfx/port.h
@@ -0,0 +1,142 @@
+#ifndef GFX_PS1_H
+#define GFX_PS1_H
+
+#include <util.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* 0-3 Texture page X Base (N*64) (ie. in 64-halfword steps) ;GPUSTAT.0-3
+ 4 Texture page Y Base (N*256) (ie. 0 or 256) ;GPUSTAT.4
+ 5-6 Semi Transparency (0=B/2+F/2, 1=B+F, 2=B-F, 3=B+F/4) ;GPUSTAT.5-6
+ 7-8 Texture page colors (0=4bit, 1=8bit, 2=15bit, 3=Reserved);GPUSTAT.7-8
+ 9 Dither 24bit to 15bit (0=Off/strip LSBs, 1=Dither Enabled) ;GPUSTAT.9
+ 10 Drawing to display area (0=Prohibited, 1=Allowed) ;GPUSTAT.10
+ 11 Texture Disable (0=Normal, 1=Disable if GP1(09h).Bit0=1) ;GPUSTAT.15
+ (Above might be chipselect for (absent) second VRAM chip?)
+ 12 Textured Rectangle X-Flip (BIOS does set this bit on power-up...?)
+ 13 Textured Rectangle Y-Flip (BIOS does set it equal to GPUSTAT.13...?)
+ 14-23 Not used (should be 0)
+ 24-31 Command (E1h)*/
+union gfx_common
+{
+ struct
+ {
+ uint32_t tpagex :4;
+ uint32_t tpagey :1;
+ uint32_t stp :2;
+ uint32_t bpp :2;
+ uint32_t dither :1;
+ uint32_t draw_to_disp :1;
+ uint32_t disable :1;
+ uint32_t xflip :1;
+ uint32_t yflip :1;
+ uint32_t :10;
+ uint8_t cmd;
+ } f;
+
+ uint32_t mask;
+};
+
+union gfx_sznext
+{
+ struct
+ {
+ uint32_t next :24;
+ uint32_t sz :8;
+ } f;
+
+ uint32_t cmd_next;
+};
+
+struct sprite
+{
+ union gfx_sznext sznext;
+ union gfx_common common;
+ uint8_t r, g, b;
+ uint8_t cmd;
+ int16_t x, y;
+ uint8_t u, v;
+ uint16_t clutid;
+ uint16_t w, h;
+};
+
+struct quad
+{
+ union gfx_sznext sznext;
+ uint8_t r, g, b;
+ uint8_t cmd;
+ int16_t x0, y0;
+ uint8_t u0, v0;
+ uint16_t clutid;
+ int16_t x1, y1;
+ uint8_t u1, v1;
+
+ union
+ {
+ struct
+ {
+ /* 0-8 Same as GP0(E1h).Bit0-8. */
+ uint16_t lb :9;
+ uint16_t :2;
+ /* 11 Same as GP0(E1h).Bit11. */
+ uint16_t hb :1;
+ uint16_t :4;
+ } bit;
+
+ uint16_t mask;
+ } tpage;
+
+ int16_t x2, y2;
+ uint8_t u2, v2;
+ uint16_t :16;
+ int16_t x3, y3;
+ uint8_t u3, v3;
+ uint16_t :16;
+};
+
+struct rect
+{
+ union gfx_sznext sznext;
+ union gfx_common common;
+ uint8_t r, g, b;
+ uint8_t cmd;
+ int16_t x, y;
+ uint16_t w, h;
+};
+
+struct stp_4line
+{
+ union gfx_sznext sznext;
+ union gfx_common common;
+ uint8_t r, g, b;
+ uint8_t cmd;
+ int16_t x, y;
+
+ struct stp_4line_vtx
+ {
+ uint32_t r :8;
+ uint32_t g :8;
+ uint32_t b :8;
+ uint32_t :8;
+
+ int16_t x, y;
+ } vertices[4];
+
+ uint32_t end;
+};
+
+enum {DRAW_MODE = 0xE1};
+
+int sprite_from_file_ex(const char *path, struct sprite *s);
+
+#define sprite_from_file(path, s) sprite_from_file_ex("cdrom:\\"path";1", s)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_PS1_H */
diff --git a/src/gfx/ps1/privinc/ps1/gfx_private.h b/src/gfx/ps1/privinc/ps1/gfx_private.h
new file mode 100644
index 0000000..fd5a790
--- /dev/null
+++ b/src/gfx/ps1/privinc/ps1/gfx_private.h
@@ -0,0 +1,25 @@
+#ifndef GFX_PS1_PRIVATE_H
+#define GFX_PS1_PRIVATE_H
+
+#include <gfx.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum
+{
+ SCREEN_W = 368,
+ SCREEN_H = 240
+};
+
+void gfx_swapheap(void);
+void gfx_initenvs(void);
+void gfx_swapbuffers(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_PS1_PRIVATE_H */
diff --git a/src/gfx/ps1/src/4line.c b/src/gfx/ps1/src/4line.c
new file mode 100644
index 0000000..955277e
--- /dev/null
+++ b/src/gfx/ps1/src/4line.c
@@ -0,0 +1,14 @@
+#include <gfx.h>
+#include <psxgpu.h>
+#include <stdint.h>
+#include <string.h>
+
+void stp_4line_init(struct stp_4line *const l)
+{
+ memset(l, 0, sizeof *l);
+ l->sznext.f.sz = (sizeof *l - sizeof l->sznext) / sizeof (uint32_t);
+ l->common.f.cmd = DRAW_MODE;
+ l->cmd = 0x5A;
+ enum {TERMINATION_CODE = 0x55555555};
+ l->end = TERMINATION_CODE;
+}
diff --git a/src/gfx/ps1/src/env.c b/src/gfx/ps1/src/env.c
new file mode 100644
index 0000000..c12002b
--- /dev/null
+++ b/src/gfx/ps1/src/env.c
@@ -0,0 +1,42 @@
+#include <gfx.h>
+#include <gfx_private.h>
+#include <ps1/gfx_private.h>
+#include <psxgpu.h>
+#include <stdbool.h>
+
+static GsDispEnv dispenv;
+enum {ENV_Y = SCREEN_H + 16};
+
+static GsDrawEnv drawenv =
+{
+ .y = ENV_Y,
+ .w = SCREEN_W,
+ .h = SCREEN_H
+};
+
+int screen_w = SCREEN_W, screen_h = SCREEN_H;
+
+void gfx_swapbuffers(void)
+{
+ const short y = drawenv.y;
+
+ drawenv.y = dispenv.y;
+ dispenv.y = y;
+ GsSetDrawEnv(&drawenv);
+ GsSetDispEnv(&dispenv);
+}
+
+void gfx_initenvs(void)
+{
+ GsSetDrawEnv(&drawenv);
+ GsSetDispEnv(&dispenv);
+}
+
+bool gfx_inside_drawenv(const short x, const short y, const short w,
+ const short h)
+{
+ return (x + w >= 0)
+ && x < drawenv.w
+ && (y + h >= 0)
+ && y < drawenv.h;
+}
diff --git a/src/gfx/ps1/src/heap.c b/src/gfx/ps1/src/heap.c
new file mode 100644
index 0000000..b67ee3e
--- /dev/null
+++ b/src/gfx/ps1/src/heap.c
@@ -0,0 +1,19 @@
+#include <gfx.h>
+#include <gfx_private.h>
+#include <stddef.h>
+
+static unsigned int sel;
+
+void gfx_swapheap(void)
+{
+ sel ^= 1;
+}
+
+void *gfx_port_heap(size_t *const n)
+{
+ enum {HEAP_SZ = 5120, N_HEAPS = 2};
+ static char heaps[N_HEAPS][HEAP_SZ];
+
+ *n = sizeof *heaps / sizeof **heaps;
+ return heaps[sel];
+}
diff --git a/src/gfx/ps1/src/init.c b/src/gfx/ps1/src/init.c
new file mode 100644
index 0000000..15ecd3e
--- /dev/null
+++ b/src/gfx/ps1/src/init.c
@@ -0,0 +1,16 @@
+#include <gfx_private.h>
+#include <ps1/gfx_private.h>
+#include <psxgpu.h>
+
+void gfx_deinit(void)
+{
+}
+
+int gfx_init(void)
+{
+ GsInit();
+ GsClearMem();
+ GsSetVideoMode(SCREEN_W, SCREEN_H, VIDEO_MODE);
+ gfx_initenvs();
+ return 0;
+}
diff --git a/src/gfx/ps1/src/quad.c b/src/gfx/ps1/src/quad.c
new file mode 100644
index 0000000..649426f
--- /dev/null
+++ b/src/gfx/ps1/src/quad.c
@@ -0,0 +1,23 @@
+#include <gfx.h>
+#include <gfx/port.h>
+#include <stdint.h>
+#include <string.h>
+
+static void quad_init(struct quad *const q)
+{
+ memset(q, 0, sizeof *q);
+ q->sznext.f.sz = (sizeof *q - sizeof q->sznext) / sizeof (uint32_t);
+ q->cmd = 0x2d;
+}
+
+int quad_from_sprite(const struct sprite *const s, struct quad *const q)
+{
+ quad_init(q);
+ q->tpage.mask = s->common.mask;
+ q->clutid = s->clutid;
+ q->u0 = q->u2 = s->u;
+ q->v0 = q->v1 = s->v;
+ q->u1 = q->u3 = s->u + s->w - 1;
+ q->v2 = q->v3 = s->v + s->h - 1;
+ return 0;
+}
diff --git a/src/gfx/ps1/src/rect.c b/src/gfx/ps1/src/rect.c
new file mode 100644
index 0000000..7e593b7
--- /dev/null
+++ b/src/gfx/ps1/src/rect.c
@@ -0,0 +1,23 @@
+#include <gfx.h>
+#include <psxgpu.h>
+#include <stdint.h>
+#include <string.h>
+
+static void common_init(struct rect *const r)
+{
+ memset(r, 0, sizeof *r);
+ r->sznext.f.sz = (sizeof *r - sizeof r->sznext) / sizeof (uint32_t);
+ r->common.f.cmd = DRAW_MODE;
+}
+
+void rect_init(struct rect *const r)
+{
+ common_init(r);
+ r->cmd = 0x60;
+}
+
+void semitrans_rect_init(struct rect *const r)
+{
+ common_init(r);
+ r->cmd = 0x62;
+}
diff --git a/src/gfx/ps1/src/sort.c b/src/gfx/ps1/src/sort.c
new file mode 100644
index 0000000..5cc14aa
--- /dev/null
+++ b/src/gfx/ps1/src/sort.c
@@ -0,0 +1,75 @@
+#include <gfx.h>
+#include <gfx_private.h>
+#include <ps1/gfx_private.h>
+#include <system/port.h>
+#include <psxgpu.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+static union gfx_sznext *first, *last;
+
+static void add_to_list(union gfx_sznext *const p)
+{
+ if (!first)
+ first = p;
+ else if (last)
+ last->f.next = (uint32_t)p;
+
+ last = p;
+}
+
+void sprite_sort(struct sprite *const s)
+{
+ add_to_list(&s->sznext);
+}
+
+void quad_sort(struct quad *const q)
+{
+ add_to_list(&q->sznext);
+}
+
+void rect_sort(struct rect *const r)
+{
+ add_to_list(&r->sznext);
+}
+
+void stp_4line_sort(struct stp_4line *const l)
+{
+ add_to_list(&l->sznext);
+}
+
+void gfx_draw(void)
+{
+ static union gfx_sznext term = {.cmd_next = 0xffffff};
+
+ add_to_list(&term);
+
+ void gpu_ctrl(unsigned int command, unsigned int param);
+
+ gfx_sync();
+ gfx_swapbuffers();
+ gfx_swapheap();
+ gpu_ctrl(4, 2);
+ D2_MADR = (uint32_t)first;
+ D2_BCR = 0;
+ D2_CHCR = (1 << 0xa) | 1 | (1 << 0x18);
+ first = NULL;
+ gfx_reset_heap();
+}
+
+void gfx_sync(void)
+{
+ /* Wait for the GPU to finish drawing primitives. */
+ while (!(GPU_CONTROL_PORT & (1 << 0x1a)))
+ ;
+
+ /* Wait for the GPU to be free. */
+ while (!(GPU_CONTROL_PORT & (1 << 0x1c)))
+ ;
+
+ while (!vblank_set)
+ ;
+
+ vblank_set = false;
+}
diff --git a/src/gfx/ps1/src/sprite.c b/src/gfx/ps1/src/sprite.c
new file mode 100644
index 0000000..3e35a70
--- /dev/null
+++ b/src/gfx/ps1/src/sprite.c
@@ -0,0 +1,244 @@
+#include <gfx.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;
+}
diff --git a/src/gfx/sdl-1.2/inc/gfx/port.h b/src/gfx/sdl-1.2/inc/gfx/port.h
new file mode 100644
index 0000000..ec1093e
--- /dev/null
+++ b/src/gfx/sdl-1.2/inc/gfx/port.h
@@ -0,0 +1,52 @@
+#ifndef GFX_SDL_H
+#define GFX_SDL_H
+
+#include <SDL/SDL.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct sprite
+{
+ SDL_Surface *s;
+ short x, y, w, h;
+ unsigned char u, v;
+};
+
+struct quad
+{
+ unsigned char r, g, b;
+ short x0, x1, x2, x3;
+ short y0, y1, y2, y3;
+ unsigned char u0, u1, u2, u3;
+ unsigned char v0, v1, v2, v3;
+ SDL_Surface *s;
+};
+
+struct rect
+{
+ unsigned char r, g, b;
+ short x, y, w, h;
+};
+
+struct stp_4line
+{
+ short x, y;
+ unsigned char r, g, b;
+
+ struct stp_4line_vtx
+ {
+ unsigned char r, g, b;
+ short x, y;
+ } vertices[4];
+};
+
+SDL_Surface *gfx_port_screen(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_SDL_H */
diff --git a/src/gfx/sdl-1.2/privinc/sdl-1.2/gfx_private.h b/src/gfx/sdl-1.2/privinc/sdl-1.2/gfx_private.h
new file mode 100644
index 0000000..c067af7
--- /dev/null
+++ b/src/gfx/sdl-1.2/privinc/sdl-1.2/gfx_private.h
@@ -0,0 +1,19 @@
+#ifndef GFX_SDL_12_PRIVATE_H
+#define GFX_SDL_12_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum
+{
+ SCREEN_W = 320,
+ SCREEN_H = 240
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_SDL_12_PRIVATE_H */
diff --git a/src/gfx/sdl-1.2/src/env.c b/src/gfx/sdl-1.2/src/env.c
new file mode 100644
index 0000000..6589d84
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/env.c
@@ -0,0 +1,82 @@
+#include <gfx.h>
+#include <sdl-1.2/gfx_private.h>
+#include <SDL/SDL.h>
+#include <stdbool.h>
+
+int screen_w = SCREEN_W, screen_h = SCREEN_H;
+static bool fullscreen;
+static SDL_Surface *screen;
+
+void gfx_deinit(void)
+{
+ if (screen)
+ SDL_FreeSurface(screen);
+
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+static int resize_screen(const int w, const int h, const bool full_screen)
+{
+ Uint32 flags = SDL_HWSURFACE | SDL_RESIZABLE | SDL_DOUBLEBUF;
+
+ const SDL_VideoInfo *const info = SDL_GetVideoInfo();
+
+ if (!info)
+ {
+ fprintf(stderr, "SDL_GetVideoInfo: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ if (fullscreen)
+ flags |= SDL_FULLSCREEN;
+
+#if 0
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+#endif
+
+ const int bpp = info->vfmt->BitsPerPixel;
+
+ if (screen)
+ SDL_FreeSurface(screen);
+
+ if (!(screen = SDL_SetVideoMode(screen_w, screen_h, bpp, flags)))
+ {
+ fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ screen_w = w;
+ screen_h = h;
+ fullscreen = full_screen;
+ return 0;
+}
+
+int gfx_init(void)
+{
+ if (SDL_InitSubSystem(SDL_INIT_VIDEO))
+ {
+ fprintf(stderr, "%s: SDL_InitSubSystem: %s\n",
+ __func__, SDL_GetError());
+ return -1;
+ }
+
+ return resize_screen(screen_w, screen_h, fullscreen);
+}
+
+bool gfx_inside_drawenv(const short x, const short y, const short w,
+ const short h)
+{
+ return (x + w >= 0)
+ && x < screen_w
+ && (y + h >= 0)
+ && y < screen_h;
+}
+
+SDL_Surface *gfx_port_screen(void)
+{
+ return screen;
+}
diff --git a/src/gfx/sdl-1.2/src/heap.c b/src/gfx/sdl-1.2/src/heap.c
new file mode 100644
index 0000000..b84760a
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/heap.c
@@ -0,0 +1,11 @@
+#include <gfx.h>
+#include <gfx_private.h>
+#include <stddef.h>
+
+void *gfx_port_heap(size_t *const n)
+{
+ static char heap[5120];
+
+ *n = sizeof heap / sizeof *heap;
+ return heap;
+}
diff --git a/src/gfx/sdl-1.2/src/line.c b/src/gfx/sdl-1.2/src/line.c
new file mode 100644
index 0000000..aeeda7b
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/line.c
@@ -0,0 +1,15 @@
+#include <gfx.h>
+#include <gfx/port.h>
+#include <stddef.h>
+
+void stp_4line_init(struct stp_4line *const l)
+{
+}
+
+void semitrans_stp_4line_init(struct stp_4line *r)
+{
+}
+
+void stp_4line_sort(struct stp_4line *const r)
+{
+}
diff --git a/src/gfx/sdl-1.2/src/quad.c b/src/gfx/sdl-1.2/src/quad.c
new file mode 100644
index 0000000..fe1b39f
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/quad.c
@@ -0,0 +1,18 @@
+#include <gfx.h>
+#include <gfx/port.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+int quad_from_sprite(const struct sprite *const s, struct quad *const q)
+{
+ q->s = s->s;
+ q->u0 = q->u2 = s->u;
+ q->v0 = q->v1 = s->v;
+ q->u1 = q->u3 = s->u + s->w - 1;
+ q->v2 = q->v3 = s->v + s->h - 1;
+ return 0;
+}
+
+void quad_sort(struct quad *const q)
+{
+}
diff --git a/src/gfx/sdl-1.2/src/rect.c b/src/gfx/sdl-1.2/src/rect.c
new file mode 100644
index 0000000..ee50378
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/rect.c
@@ -0,0 +1,33 @@
+#include <gfx.h>
+#include <gfx/port.h>
+#include <SDL/SDL.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+void rect_sort(struct rect *const r)
+{
+ SDL_Rect rct =
+ {
+ .x = r->x,
+ .y = r->y,
+ .w = r->w,
+ .h = r->h
+ };
+
+ SDL_Surface *const screen = gfx_port_screen();
+ const Uint32 map = SDL_MapRGB(screen->format, r->r, r->g, r->b);
+
+ if (SDL_FillRect(screen, &rct, map))
+ {
+ fprintf(stderr, "SDL_FillRect: %s\n", SDL_GetError());
+ return;
+ }
+}
+
+void semitrans_rect_init(struct rect *const r)
+{
+}
+
+void rect_init(struct rect *const r)
+{
+}
diff --git a/src/gfx/sdl-1.2/src/sort.c b/src/gfx/sdl-1.2/src/sort.c
new file mode 100644
index 0000000..2cc4e75
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/sort.c
@@ -0,0 +1,22 @@
+#include <gfx.h>
+#include <gfx_private.h>
+#include <gfx/port.h>
+#include <SDL/SDL.h>
+#include <stdio.h>
+
+void gfx_draw(void)
+{
+ enum {FPS = 50, REFRESH_MS = 1000 / FPS};
+ static Uint32 prev;
+ const Uint32 cur = SDL_GetTicks();
+
+ if (cur - prev < REFRESH_MS)
+ SDL_Delay(REFRESH_MS - (cur - prev));
+
+ prev = SDL_GetTicks();
+
+ if (SDL_Flip(gfx_port_screen()))
+ fprintf(stderr, "SDL_Flip: %s\n", SDL_GetError());
+
+ gfx_reset_heap();
+}
diff --git a/src/gfx/sdl-1.2/src/sprite.c b/src/gfx/sdl-1.2/src/sprite.c
new file mode 100644
index 0000000..9d65c0d
--- /dev/null
+++ b/src/gfx/sdl-1.2/src/sprite.c
@@ -0,0 +1,75 @@
+#include <gfx.h>
+#include <gfx/port.h>
+#include <SDL/SDL.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+void sprite_free(struct sprite *const s)
+{
+ if (s && s->s)
+ SDL_FreeSurface(s->s);
+}
+
+int sprite_clone(const struct sprite *const src, struct sprite *const dst)
+{
+ *dst = *src;
+ return 0;
+}
+
+int sprite_from_fp(struct sprite *const s, FILE *const f)
+{
+ int ret = -1;
+ SDL_RWops *ops = NULL;
+ SDL_Surface *ts = NULL;
+
+ if (!(ops = SDL_RWFromFP(f, 0)))
+ {
+ fprintf(stderr, "SDL_RWFromFP: %s\n", SDL_GetError());
+ goto end;
+ }
+ if (!(ts = SDL_LoadBMP_RW(ops, 0)))
+ {
+ fprintf(stderr, "SDL_LoadBMP_RW: %s\n", SDL_GetError());
+ goto end;
+ }
+ /* Magenta as transparent. */
+ else if (SDL_SetColorKey(ts, SDL_SRCCOLORKEY, SDL_MapRGB(ts->format, 255, 0, 255)))
+ {
+ fprintf(stderr, "SDL_SetColorKey: %s\n", SDL_GetError());
+ goto end;
+ }
+ else if (!(s->s = SDL_DisplayFormat(ts)))
+ {
+ fprintf(stderr, "SDL_DisplayFormat: %s\n", SDL_GetError());
+ goto end;
+ }
+
+ s->w = ts->w;
+ s->h = ts->h;
+ ret = 0;
+
+end:
+ SDL_FreeRW(ops);
+ SDL_FreeSurface(ts);
+ return ret;
+}
+
+void sprite_sort(struct sprite *const s)
+{
+ SDL_Rect r =
+ {
+ .x = s->x,
+ .y = s->y
+ };
+
+ SDL_Rect clip =
+ {
+ .x = s->u,
+ .y = s->v,
+ .w = s->w,
+ .h = s->h
+ };
+
+ if (SDL_BlitSurface(s->s, &clip, gfx_port_screen(), &r))
+ fprintf(stderr, "SDL_BlitSurface: %s\n", SDL_GetError());
+}
diff --git a/src/gfx/src/heap.c b/src/gfx/src/heap.c
new file mode 100644
index 0000000..40a856c
--- /dev/null
+++ b/src/gfx/src/heap.c
@@ -0,0 +1,56 @@
+#include <gfx.h>
+#include <gfx_private.h>
+#include <stddef.h>
+
+static size_t heap_i;
+
+void gfx_reset_heap(void)
+{
+ heap_i = 0;
+}
+
+static void *get_element(const size_t sz)
+{
+ size_t n;
+ char *ret = gfx_port_heap(&n);
+
+ if (ret && heap_i + sz < n)
+ {
+ ret += heap_i;
+ heap_i += sz;
+ }
+ else
+ return NULL;
+
+ return ret;
+}
+
+struct sprite *sprite_get(void)
+{
+ return get_element(sizeof (struct sprite));
+}
+
+struct quad *quad_get(void)
+{
+ return get_element(sizeof (struct quad));
+}
+
+struct rect *rect_get(const bool semitrans)
+{
+ struct rect *const r = get_element(sizeof (struct rect));
+
+ if (r)
+ semitrans ? semitrans_rect_init(r) : rect_init(r);
+
+ return r;
+}
+
+struct stp_4line *stp_4line_get(void)
+{
+ struct stp_4line *const l = get_element(sizeof (struct stp_4line));
+
+ if (l)
+ stp_4line_init(l);
+
+ return l;
+}