2018-12-16 13:34:03 +01:00
|
|
|
|
|
|
|
/* screen drawing */
|
|
|
|
|
|
|
|
#include "game.h"
|
|
|
|
#include "resource.h"
|
|
|
|
#include "sys.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
#define MAX_SPRITES 288
|
|
|
|
#define MAX_SPRITESHEET_W 1024
|
|
|
|
#define MAX_SPRITESHEET_H 512
|
|
|
|
|
|
|
|
void video_draw_dot_pattern(int offset) {
|
|
|
|
static const int W = 144;
|
|
|
|
uint8_t *dst = g_res.vga + (GAME_SCREEN_H - PANEL_H) * GAME_SCREEN_W + offset;
|
|
|
|
for (int y = 0; y < PANEL_H; ++y) {
|
|
|
|
for (int x = 0; x < W; x += 2) {
|
|
|
|
dst[x + (y & 1)] = 0;
|
|
|
|
}
|
|
|
|
dst += GAME_SCREEN_W;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_draw_sprite(int num, int x, int y, int flag) {
|
2018-12-16 15:28:52 +01:00
|
|
|
g_sys.render_add_sprite(RENDER_SPR_GAME, num, x, y, flag != 0);
|
2018-12-16 13:34:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void video_draw_string(const char *s, int offset, int hspace) {
|
|
|
|
while (*s) {
|
|
|
|
uint8_t code = *s++;
|
|
|
|
if (code != 0x20) {
|
|
|
|
if (code >= 0x41) {
|
|
|
|
code -= 0x41;
|
|
|
|
} else {
|
|
|
|
code -= 0x16;
|
|
|
|
}
|
|
|
|
ja_decode_chr(g_res.font + code * 200, 200, g_res.tmp + 768 + offset, 320);
|
|
|
|
}
|
|
|
|
offset += hspace;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void video_copy_vga(int size) {
|
|
|
|
if (size == 0xB500) {
|
|
|
|
memcpy(g_res.background, g_res.tmp + 768, 64000);
|
|
|
|
} else {
|
|
|
|
assert(size == 0x7D00);
|
2018-12-16 15:28:52 +01:00
|
|
|
g_sys.set_screen_palette(g_res.tmp, 0, 256, 6);
|
2018-12-16 13:34:03 +01:00
|
|
|
const uint8_t *src = g_res.tmp + 768;
|
|
|
|
if (GAME_SCREEN_W * GAME_SCREEN_H == 64000) {
|
|
|
|
memcpy(g_res.vga, src, 64000);
|
|
|
|
} else {
|
|
|
|
memset(g_res.vga, 0, GAME_SCREEN_W * GAME_SCREEN_H);
|
|
|
|
for (int y = 0; y < MIN(200, GAME_SCREEN_H); ++y) {
|
|
|
|
memcpy(g_res.vga + y * GAME_SCREEN_W, src, MIN(320, GAME_SCREEN_W));
|
|
|
|
src += 320;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_sys.update_screen(g_res.vga, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-16 16:44:02 +01:00
|
|
|
void video_copy_backbuffer(int h) {
|
|
|
|
for (int y = 0; y < MIN(200, GAME_SCREEN_H) - h; ++y) {
|
|
|
|
for (int x = 0; x < GAME_SCREEN_W; x += 320) {
|
|
|
|
memcpy(g_res.vga + y * GAME_SCREEN_W + x, g_res.background + y * 320, MIN(320, GAME_SCREEN_W - x));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-16 13:34:03 +01:00
|
|
|
void ja_decode_spr(const uint8_t *src, int w, int h, uint8_t *dst, int dst_pitch, uint8_t pal_mask) {
|
|
|
|
const int bitplane_size = w * h;
|
|
|
|
for (int y = 0; y < h; ++y) {
|
|
|
|
for (int x = 0; x < w; ++x) {
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
|
int color = 0;
|
|
|
|
const int mask = 1 << (7 - i);
|
|
|
|
for (int bit = 0; bit < 4; ++bit) {
|
|
|
|
if (src[bit * bitplane_size] & mask) {
|
|
|
|
color |= 1 << bit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (color != 0) {
|
|
|
|
dst[x * 8 + i] = pal_mask | color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++src;
|
|
|
|
}
|
|
|
|
dst += dst_pitch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ja_decode_chr(const uint8_t *buffer, const int size, uint8_t *dst, int dst_pitch) {
|
|
|
|
for (int y = 0; y < 25; ++y) {
|
|
|
|
const int offset = y * 2;
|
|
|
|
const uint16_t p[] = {
|
|
|
|
READ_BE_UINT16(buffer + offset),
|
|
|
|
READ_BE_UINT16(buffer + offset + 50),
|
|
|
|
READ_BE_UINT16(buffer + offset + 100),
|
|
|
|
READ_BE_UINT16(buffer + offset + 150)
|
|
|
|
};
|
|
|
|
for (int x = 0; x < 16; ++x) {
|
|
|
|
const uint16_t mask = 1 << (15 - x);
|
|
|
|
uint8_t color = 0;
|
|
|
|
for (int b = 0; b < 4; ++b) {
|
|
|
|
if (p[b] & mask) {
|
|
|
|
color |= (1 << b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (color != 0) {
|
|
|
|
dst[x] = 0xD0 | color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dst += dst_pitch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ja_decode_tile(const uint8_t *buffer, uint8_t pal_mask, uint8_t *dst, int dst_pitch, int x_offset, int y_offset) {
|
|
|
|
int tile_w = 16;
|
|
|
|
if (x_offset < 0) {
|
|
|
|
tile_w += x_offset;
|
|
|
|
buffer -= x_offset / 2;
|
|
|
|
x_offset = 0;
|
|
|
|
}
|
|
|
|
if (x_offset + tile_w > TILEMAP_SCREEN_W) {
|
|
|
|
tile_w = TILEMAP_SCREEN_W - x_offset;
|
|
|
|
}
|
|
|
|
if (tile_w <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int tile_h = 16;
|
|
|
|
if (y_offset < 0) {
|
|
|
|
tile_h += y_offset;
|
|
|
|
buffer -= y_offset * 8;
|
|
|
|
y_offset = 0;
|
|
|
|
}
|
|
|
|
if (y_offset + tile_h > TILEMAP_SCREEN_H) {
|
|
|
|
tile_h = TILEMAP_SCREEN_H - y_offset;
|
|
|
|
}
|
|
|
|
if (tile_h <= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dst += y_offset * dst_pitch + x_offset;
|
|
|
|
for (int y = 0; y < tile_h; ++y) {
|
|
|
|
uint8_t *p = dst;
|
|
|
|
for (int x = 0; x < tile_w / 2; ++x) {
|
|
|
|
const uint8_t color1 = buffer[x] >> 4;
|
|
|
|
const uint8_t color2 = buffer[x] & 15;
|
|
|
|
if (color1 != 0) {
|
|
|
|
*p = pal_mask | color1;
|
|
|
|
}
|
|
|
|
++p;
|
|
|
|
if (color2 != 0) {
|
|
|
|
*p = pal_mask | color2;
|
|
|
|
}
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
buffer += 8;
|
|
|
|
dst += dst_pitch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-16 16:44:02 +01:00
|
|
|
static void decode_motif_scanline(const uint8_t *src, uint8_t *dst, uint8_t color) {
|
2018-12-16 15:28:52 +01:00
|
|
|
for (int x = 0; x < 40; ++x) {
|
|
|
|
for (int b = 0; b < 8; ++b) {
|
|
|
|
const uint8_t mask = 1 << (7 - b);
|
|
|
|
if (src[x] & mask) {
|
|
|
|
dst[x * 8 + b] = color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ja_decode_motif(int num, uint8_t color) {
|
|
|
|
const uint8_t *src = g_res.motif + 25 * 320 * num;
|
|
|
|
int y = 0;
|
|
|
|
for (int j = 0; j < 25; ++j) {
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
2018-12-16 16:44:02 +01:00
|
|
|
decode_motif_scanline(src + i * 40 + j * 320, g_res.vga + y * GAME_SCREEN_W, color);
|
2018-12-16 15:28:52 +01:00
|
|
|
++y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-16 13:34:03 +01:00
|
|
|
void video_load_sprites() {
|
|
|
|
struct sys_rect_t r[MAX_SPRITES];
|
|
|
|
uint8_t *data = (uint8_t *)calloc(MAX_SPRITESHEET_W * MAX_SPRITESHEET_H, 1);
|
|
|
|
if (data) {
|
|
|
|
int current_x = 0;
|
|
|
|
int max_w = 0;
|
|
|
|
int current_y = 0;
|
|
|
|
int max_h = 0;
|
|
|
|
|
|
|
|
int total_size = 0;
|
|
|
|
int count = 0;
|
|
|
|
uint8_t value;
|
|
|
|
for (int i = 0; (value = sprite_offsets[i] & 255) != 0; ++i, ++count) {
|
|
|
|
|
|
|
|
value = (value >> 3) | ((value & 7) << 5);
|
|
|
|
if ((value & 0xE0) != 0) {
|
|
|
|
value &= ~0xE0;
|
|
|
|
++value;
|
|
|
|
}
|
|
|
|
const int h = sprite_offsets[i] >> 8;
|
|
|
|
const int w = value * 8;
|
|
|
|
assert((sprite_offsets[i] & 255) == w);
|
|
|
|
const int size = (h * value) * 4;
|
|
|
|
|
|
|
|
if (current_x + w > MAX_SPRITESHEET_W) {
|
|
|
|
current_y += max_h;
|
|
|
|
if (current_x > max_w) {
|
|
|
|
max_w = current_x;
|
|
|
|
}
|
|
|
|
current_x = 0;
|
|
|
|
max_h = h;
|
|
|
|
} else {
|
|
|
|
if (h > max_h) {
|
|
|
|
max_h = h;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ja_decode_spr(g_res.sprites + total_size, w / 8, h, data + current_y * MAX_SPRITESHEET_W + current_x, MAX_SPRITESHEET_W, sprite_palettes[i] * 0x10);
|
|
|
|
total_size += size;
|
|
|
|
|
|
|
|
r[i].x = current_x;
|
|
|
|
r[i].y = current_y;
|
|
|
|
r[i].w = w;
|
|
|
|
r[i].h = h;
|
|
|
|
current_x += w;
|
|
|
|
}
|
|
|
|
assert(count <= MAX_SPRITES);
|
|
|
|
assert(max_w <= MAX_SPRITESHEET_W);
|
|
|
|
assert(current_y + max_h <= MAX_SPRITESHEET_H);
|
2018-12-16 15:28:52 +01:00
|
|
|
g_sys.render_unload_sprites(RENDER_SPR_GAME);
|
2021-12-19 01:09:02 +01:00
|
|
|
g_sys.render_load_sprites(RENDER_SPR_GAME, count, r, data, MAX_SPRITESHEET_W, current_y + max_h, 0x0, false);
|
2018-12-16 13:34:03 +01:00
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
}
|