From d3de009e1d70239c9e0004d1a8bbaf74999a2f92 Mon Sep 17 00:00:00 2001 From: Nunuhara Cabbage Date: Fri, 15 Nov 2019 16:28:07 -0800 Subject: [PATCH] Import CG reading code from xsystem35 Plus a bit of SACT2 implementation, and fix a bug where global arrays were not initialized properly. --- ald.c | 5 +- bmp.c | 254 +++++++++++++++++++++ bmp.h | 57 +++++ cg.c | 152 ++++++++++++- cg.h | 48 +++- graphics.h | 51 +++++ hll/OutputLog.c | 10 +- hll/SACT2.c | 202 +++++++++++++++-- hll/hll.h | 10 +- meson.build | 8 +- page.c | 5 + pms.c | 320 +++++++++++++++++++++++++++ pms.h | 70 ++++++ qnt.c | 262 ++++++++++++++++++++++ qnt.h | 48 ++++ queue.h | 574 ++++++++++++++++++++++++++++++++++++++++++++++++ sdl_core.h | 29 +++ sdl_private.h | 58 +++++ sdl_video.c | 93 ++++++++ system.c | 23 +- system4.c | 2 +- system4.h | 11 +- vm.c | 43 +++- vm.h | 7 + vsp.c | 247 +++++++++++++++++++++ vsp.h | 50 +++++ 26 files changed, 2583 insertions(+), 56 deletions(-) create mode 100644 bmp.c create mode 100644 bmp.h create mode 100644 graphics.h create mode 100644 pms.c create mode 100644 pms.h create mode 100644 qnt.c create mode 100644 qnt.h create mode 100644 queue.h create mode 100644 sdl_core.h create mode 100644 sdl_private.h create mode 100644 sdl_video.c create mode 100644 vsp.c create mode 100644 vsp.h diff --git a/ald.c b/ald.c index 0e5d423..33eb849 100644 --- a/ald.c +++ b/ald.c @@ -183,12 +183,15 @@ static int _ald_get(struct ald_archive *ar, int no, int *disk_out, int *dataptr_ bool ald_data_exists(struct ald_archive *ar, int no) { int disk, dataptr; - return !!_ald_get(ar, no, &disk, &dataptr); + return ar && !!_ald_get(ar, no, &disk, &dataptr); } /* Get a piece of data from an ALD archive. */ struct archive_data *ald_get(struct ald_archive *ar, int no) { + if (!ar) + return NULL; + uint8_t *data; struct archive_data *dfile; int disk, dataptr, ptr, size; diff --git a/bmp.c b/bmp.c new file mode 100644 index 0000000..3da938a --- /dev/null +++ b/bmp.c @@ -0,0 +1,254 @@ +/* + * bmp.c extract 8/24 bit BMP cg + * + * Copyright (C) 1999 TAJIRI,Yasuhiro + * rewrited 2000 CHIKAMA Masaki + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 0.00 99/01/04 初版 + * 0.01 99/01/20 パレットを読み込む際のrgbの並びを直した + * @version 1.1 00/09/17 rewrite for changing interface + * + */ + +#include +#include "little_endian.h" +#include "graphics.h" +#include "cg.h" +#include "bmp.h" + +/* + * static methods + */ +static struct bmp_header *extract_header(uint8_t *b); +static void getpal(Pallet256 *pal, uint8_t *b); +static void extract_8bit(struct bmp_header *bmp, uint8_t *pic, uint8_t *b); +static void extract_24bit(struct bmp_header *bmp, uint16_t *pic, uint8_t *b); + +/* + * Get information from cg header + * b: raw data (pointer to header) + * return: acquired bmp information object + */ +static struct bmp_header *extract_header(uint8_t *b) { + struct bmp_header *bmp = malloc(sizeof(struct bmp_header)); + + bmp->bmpSize = LittleEndian_getDW(b, 2); + /*data[4]-data[7]reserv*/ + bmp->bmpDp = LittleEndian_getDW(b, 10); + bmp->bmpXW = LittleEndian_getDW(b, 18); + bmp->bmpYW = LittleEndian_getDW(b, 22); + bmp->bmpBpp = LittleEndian_getW(b, 28); + bmp->bmpCp = LittleEndian_getDW(b, 30); + bmp->bmpImgSize = LittleEndian_getDW(b, 34); + bmp->bmpPp = (bmp->bmpBpp==24 ? 0 : + 14 + LittleEndian_getDW(b, 14)); + bmp->bmpTp = LittleEndian_getDW(b, 14) == 12 ? BMP_OS2 : BMP_WIN; + + return bmp; +} + +/* + * Get pallet from raw data + * pal: pallet to be stored + * b : raw data (pointer to pallet) + */ +static void getpal(Pallet256 *pal, uint8_t *b) { + int i; + + for (i = 0; i < 256; i++) { + pal->blue[i] = *b++; + pal->red[i] = *b++; + pal->green[i] = *b++; + } +} + +/* + * Do extract 8bit bmp image + * bmps: bmp header information + * pic: pixel to be stored + * b : raw data (pointer to pixel) + */ +static void extract_8bit(struct bmp_header *bmp, uint8_t *pic, uint8_t *b) { + int i, j; + int pos; + int LineNeed = (bmp->bmpXW * bmp->bmpBpp) / 8; + + if (LineNeed % 4) + LineNeed = (((LineNeed / 4) + 1) * 4); /* Duint16_t整列化 */ + + pos = LineNeed * (bmp->bmpYW); /* 最上行の位置 */ + for (i = 0; i < bmp->bmpYW; i++) { + const uint8_t* p; + pos -= LineNeed; + p = b + pos; + for (j = 0; j < bmp->bmpXW; j++) + *pic++ = *p++; + } +} + +/* + * Do extract 24bit bmp image + * pms: pms header information + * pic: pixel to be stored (16 bit converted) + * b : raw data (pointer to pixel) + */ +static void extract_24bit(struct bmp_header *bmp, uint16_t *pic, uint8_t *b) { + int i, j; + int pos; + int LineNeed = (bmp->bmpXW * bmp->bmpBpp) / 8; + + if (LineNeed % 4) + LineNeed = (((LineNeed / 4) + 1) * 4); /* Duint16_t整列化 */ + + pos = LineNeed * (bmp->bmpYW); /* 最上行の位置 */ + for (i = 0; i < bmp->bmpYW; i++) { + const uint8_t* p; + pos -= LineNeed; + p = b + pos; + + for (j = 0; j < bmp->bmpXW; j++) { + uint16_t r,g,b; + b = (*p++); + g = (*p++); + r = (*p++); + r >>= 3; + g >>= 2; + b >>= 3; + *pic = (r << 11 | g << 5 | b); + /* 本当はPIX24なんだろうけど、その後表示する関数ないし、 + とりあえず。 + *pic = PIX16(r,g,b); + */ + pic++; + } + } +} + +/* + * Check data is 8bit bmp format cg or not + * data: raw data (pointer to data top) + * return: true if data is bmp + */ +bool bmp256_checkfmt(uint8_t *data) { + int w, h, bpp; + if (data[0] != 'B' || data[1] != 'M') return false; + + w = LittleEndian_getDW(data, 18); + h = LittleEndian_getDW(data, 22); + bpp = LittleEndian_getW(data, 28); + + if (bpp != 8) return false; + if (w < 0 || h < 0) return false; + + return true; +} + +/* + * Extract 8bit bmp, header, pallet and pixel + * data: raw data (pointer to data top) + * return: extracted image data and information + */ +struct cg *bmp256_extract(uint8_t *data) { + struct bmp_header *bmp = extract_header(data); + struct cg *cg = calloc(1, sizeof(struct cg)); + + cg->pal = malloc(sizeof(Pallet256)); + getpal(cg->pal, data + bmp->bmpPp); + + /* +10: margin for broken cg */ + cg->pic = malloc(sizeof(uint8_t) * ((bmp->bmpXW + 10) * (bmp->bmpYW + 10))); + extract_8bit(bmp, cg->pic, data + bmp->bmpDp); + + cg->type = ALCG_BMP8; + cg->x = 0; + cg->y = 0; + cg->width = bmp->bmpXW; + cg->height = bmp->bmpYW; + cg->alpha = NULL; + + free(bmp); + + return cg; +} + +/* + * Check data is 24bit bmp format cg or not + * data: raw data (pointer to data top) + * return: true if data is pms + */ +bool bmp16m_checkfmt(uint8_t *data) { + int w, h, bpp; + + if (data[0] != 'B' || data[1] != 'M') return false; + + w = LittleEndian_getDW(data, 18); + h = LittleEndian_getDW(data, 22); + bpp = LittleEndian_getW(data, 28); + + if (bpp != 24) return false; + if (w < 0 || h < 0) return false; + + return true; +} + +/* + * Extract 24bit bmp, header, pallet and pixel + * data: raw data (pointer to data top) + * return: extracted image data and information + */ +struct cg *bmp16m_extract(uint8_t *data) { + struct cg *cg = calloc(1, sizeof(struct cg)); + struct bmp_header *bmp = extract_header(data); + + /* +10: margin for broken cg */ + cg->pic = (uint8_t *)malloc(sizeof(uint16_t) * (bmp->bmpXW + 10) * (bmp->bmpYW + 10)); + extract_24bit(bmp, (uint16_t *)cg->pic, data + bmp->bmpDp); + + cg->type = ALCG_BMP24; + cg->x = 0; + cg->y = 0; + cg->width = bmp->bmpXW; + cg->height = bmp->bmpYW; + cg->alpha = NULL; + cg->pal = NULL; + + free(bmp); + + return cg; +} + +/* + * Extract bmp pallet only + * data: raw data (pointer to data top) + * return: extracted pallet data + */ +struct cg *bmp_getpal(uint8_t *data) { + struct cg *cg = calloc(1, sizeof(struct cg)); + struct bmp_header *bmp = extract_header(data); + + cg->pal = malloc(sizeof(Pallet256)); + getpal(cg->pal, data + bmp->bmpPp); + + cg->type = ALCG_BMP8; + cg->pic = NULL; + cg->alpha = NULL; + + free(bmp); + + return cg; +} diff --git a/bmp.h b/bmp.h new file mode 100644 index 0000000..757c47d --- /dev/null +++ b/bmp.h @@ -0,0 +1,57 @@ +/* + * bmp.h extract 8/24 bit BMP cg + * + * Copyright (C) 1999 TAJIRI,Yasuhiro + * rewrited 2000 Masaki Chikam + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @version 1.1 00/09/17 rewrite for changeing interface + * +*/ +/* $Id: bmp.h,v 1.1 2000/09/20 10:33:14 chikama Exp $ */ + +#ifndef SYSTEM4_BMP_H +#define SYSTEM4_BMP_H + +#include +#include +#include "cg.h" + +enum bmp_type { + BMP_OS2 = 1, + BMP_WIN = 2 +}; + +struct bmp_header { + int bmpSize; // header + data size? + int bmpXW; // image width + int bmpYW; // image height + int bmpBpp; // image depth + int bmpCp; // pointer to comment ? + int bmpImgSize; // image size + int bmpDp; // pointer to data + int bmpPp; // pointer to pixel + enum bmp_type bmpTp; // bmp type +}; + +bool bmp256_checkfmt(uint8_t *data); +struct cg *bmp256_extract(uint8_t *data); +bool bmp16m_checkfmt(uint8_t *data); +struct cg *bmp16m_extract(uint8_t *data); +struct cg *bmp_getpal(uint8_t *data); + +#endif /* SYSTEM_BMP_H */ diff --git a/cg.c b/cg.c index 001729f..e317b44 100644 --- a/cg.c +++ b/cg.c @@ -14,11 +14,161 @@ * along with this program; if not, see . */ +#include +#include +#include #include "system4.h" -#include "cg.h" #include "ald.h" +#include "cg.h" +#include "bmp.h" +#include "pms.h" +#include "qnt.h" +#include "vsp.h" +#include "graphics.h" bool cg_exists(int no) { return ald[ALDFILE_CG] && ald_data_exists(ald[ALDFILE_CG], no); } + +/* + * Identify cg format + * data: pointer to compressed data + * return: cg type + */ +static enum cg_type check_cgformat(uint8_t *data) +{ + if (qnt_checkfmt(data)) { + return ALCG_QNT; + } else if (pms256_checkfmt(data)) { + return ALCG_PMS8; + } else if (pms64k_checkfmt(data)) { + return ALCG_PMS16; + } else if (bmp16m_checkfmt(data)) { + return ALCG_BMP24; + } else if (bmp256_checkfmt(data)) { + return ALCG_BMP8; + } else if (vsp_checkfmt(data)) { + return ALCG_VSP; + } + WARNING("Unknown CG type"); + return ALCG_UNKNOWN; +} + +/* + * Modify pixel accoding to pallet bank (vsp only) + * pic : pixel to be modifyied. + * bank : pallet bank (use only MSB 4bit) + * width : image width + * height: image height + */ +//static void set_vspbank(uint8_t *pic, int bank, int width, int height) { +// int pixels = width * height; +// +// while (pixels--) { +// *pic = (*pic & 0x0f) | (uint8_t)bank; pic++; +// } +//} + +/* + * Free CG data + * cg: object to free + */ +void cg_free(struct cg *cg) +{ + if (cg->pic) free(cg->pic); + if (cg->pal) free(cg->pal); + if (cg->alpha) free(cg->alpha); + free(cg); +} + +/* + * Load cg data from file or cache + * no: file no ( >= 0) + * return: cg object(extracted) +*/ +struct cg *cg_load(int no) +{ + struct archive_data *dfile; + struct cg *cg = NULL; + int type, possibly_unused size = 0; + + // search in cache + //if ((cg = (struct cg *)cache_foreach(cacheid, no))) + // return cg; + + // read from file + //if (NULL == (dfile = ald_getdata(DRIFILE_CG, no))) return NULL; + if (!(dfile = ald_get(ald[ALDFILE_CG], no))) + return NULL; + + // update load cg counter +// if (cg_loadCountVar != NULL) { +// (*(cg_loadCountVar + no + 1))++; +// } + + // check loaded cg format + type = check_cgformat(dfile->data); + + // extract cg + // size is only pixel data size + switch(type) { + case ALCG_VSP: + sys_message("extracting VSP"); + cg = vsp_extract(dfile->data); + size = cg->width * cg->height; + break; + case ALCG_PMS8: + sys_message("extracting PMS8"); + cg = pms256_extract(dfile->data); + size = cg->width * cg->height; + break; + case ALCG_PMS16: + sys_message("extracting PMS16"); + cg = pms64k_extract(dfile->data); + size = (cg->width * cg->height) * sizeof(uint16_t); + break; + case ALCG_BMP8: + sys_message("extracting BMP8"); + cg = bmp256_extract(dfile->data); + size = cg->width * cg->height; + break; + case ALCG_BMP24: + sys_message("extracting BMP24"); + cg = bmp16m_extract(dfile->data); + size = (cg->width * cg->height) * sizeof(uint16_t); + break; + case ALCG_QNT: + sys_message("extracting QNT"); + cg = qnt_extract(dfile->data); + size = (cg->width * cg->height) * 3; + break; + default: + break; + } + // insert to cache + //if (cg) + // cache_insert(cacheid, no, cg, size, NULL); + + // load pallet if not extracted + // XXXX うむ、こいつらどこで解放するんだ + switch(type) { + case ALCG_VSP: + cg = vsp_getpal(dfile->data); + break; + case ALCG_PMS8: + cg = pms_getpal(dfile->data); + break; + case ALCG_BMP8: + cg = bmp_getpal(dfile->data); + break; + default: + break; + } + + // ok to free + ald_free_data(dfile); + + return cg; +} + diff --git a/cg.h b/cg.h index 0392311..fa51fb6 100644 --- a/cg.h +++ b/cg.h @@ -1,4 +1,6 @@ -/* Copyright (C) 2019 Nunuhara Cabbage +/* Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * 2019 Nunuhara Cabbage * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,7 +20,51 @@ #define SYSTEM4_CG_H #include +#include +#include "graphics.h" + +/* + * Available CG formats + */ +enum cg_type { + ALCG_UNKNOWN = 1, + ALCG_VSP = 2, + ALCG_PMS8 = 3, + ALCG_PMS16 = 4, + ALCG_BMP8 = 5, + ALCG_BMP24 = 6, + ALCG_QNT = 7, + ALCG_AJP = 8 +}; + +/* + * Information for displaying CG data + */ +struct cg { + enum cg_type type; // cg format type + int x; // default display location x + int y; // default display location y + int z; // draw order + int width; // image width + int height; // image height + + uint8_t *pic; // extracted pixel data + uint8_t *alpha; // extracted alpha data if exists + Pallet256 *pal; // extracted pallet data if exists + + int vsp_bank; // pallet bank for vsp + int pms_bank; // pallet bank for pms + + int spritecolor; // sprite color for vsp and pms8 + int alphalevel; // alpha level of image + + int data_offset; // pic offset for clipping + + int r, g, b, a; + bool show; +}; bool cg_exists(int no); +struct cg *cg_load(int no); #endif /* SYSTEM4_CG_H */ diff --git a/graphics.h b/graphics.h new file mode 100644 index 0000000..1390e13 --- /dev/null +++ b/graphics.h @@ -0,0 +1,51 @@ +/* Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef SYSTEM4_GRAPHICS_H +#define SYSTEM4_GRAPHICS_H + +#include +#include + +typedef struct { + uint8_t red[256]; + uint8_t green[256]; + uint8_t blue[256]; +} Pallet256; + +typedef struct { + uint8_t r,g,b; + uint32_t pixel; +} Pallet; + +typedef SDL_Point Point; + +typedef struct { + int width; + int height; +} Dimension; + +typedef SDL_Rect Rectangle; + +typedef struct { + int width; + int height; + int depth; +} DispInfo; + +#endif /* SYSTEM4_GRAPHICS_H */ diff --git a/hll/OutputLog.c b/hll/OutputLog.c index b4fe4bf..4210b9e 100644 --- a/hll/OutputLog.c +++ b/hll/OutputLog.c @@ -18,7 +18,7 @@ #include "hll.h" #include "../utfsjis.h" -hll_warn_unimplemented(OutputLog, Create) +hll_warn_unimplemented(OutputLog, Create, 0) hll_defun(Output, args) { struct string *s = heap[args[1].i].s; @@ -28,10 +28,10 @@ hll_defun(Output, args) { hll_return(0); } -hll_warn_unimplemented(OutputLog, Clear) -hll_warn_unimplemented(OutputLog, Save) -hll_warn_unimplemented(OutputLog, EnableAutoSave) -hll_warn_unimplemented(OutputLog, DisableAutoSave) +hll_warn_unimplemented(OutputLog, Clear, 0) +hll_warn_unimplemented(OutputLog, Save, 0) +hll_warn_unimplemented(OutputLog, EnableAutoSave, 0) +hll_warn_unimplemented(OutputLog, DisableAutoSave, 0) hll_deflib(OutputLog) { hll_export(Create), diff --git a/hll/SACT2.c b/hll/SACT2.c index 9e4edd9..3f46473 100644 --- a/hll/SACT2.c +++ b/hll/SACT2.c @@ -19,10 +19,52 @@ #include "hll.h" #include "../system4.h" #include "../cg.h" +#include "../queue.h" +#include "../sdl_core.h" + +struct SACT2_sprite { + TAILQ_ENTRY(SACT2_sprite) entry; + struct cg *cg; + Point pos; + int z; + int width, height; + int r, g, b, a; + bool show; + bool used; +}; + +static struct SACT2_sprite *sprites = NULL; +static int nr_sprites = 0; + +TAILQ_HEAD(listhead, SACT2_sprite) visible_sprites = + TAILQ_HEAD_INITIALIZER(visible_sprites); + +static void sprite_set_show(struct SACT2_sprite *sp, bool show) +{ + if (!sp->show == !show) + return; + + sp->show = show; + + if (show) { + struct SACT2_sprite *p; + TAILQ_FOREACH(p, &visible_sprites, entry) { + if (p->z > sp->z) { + TAILQ_INSERT_BEFORE(p, sp, entry); + return; + } + } + TAILQ_INSERT_TAIL(&visible_sprites, sp, entry); + } else { + TAILQ_REMOVE(&visible_sprites, sp, entry); + } +} // int Init(imain_system pIMainSystem, int nCGCacheSize) hll_defun(Init, args) { + + sdl_initialize(); hll_return(1); } @@ -65,22 +107,77 @@ hll_unimplemented(SACT2, QuakeScreen) hll_unimplemented(SACT2, QUAKE_SET_CROSS) // void QUAKE_SET_ROTATION(int nAmp, int nCycle) hll_unimplemented(SACT2, QUAKE_SET_ROTATION) + // int SP_GetUnuseNum(int nMin) -hll_unimplemented(SACT2, SP_GetUnuseNum) +hll_defun(SP_GetUnuseNum, args) +{ + int nMin = args[0].i; + + for (int i = nMin; i < nr_sprites; i++) { + if (!sprites[i].used) + hll_return(i); + } + + int n = max(nMin, nr_sprites); + nr_sprites = n + 256; + sprites = xrealloc(sprites, sizeof(struct SACT2_sprite) * nr_sprites); + hll_return(n); +} + // int SP_Count(void) hll_unimplemented(SACT2, SP_Count) // int SP_Enum(ref array@int anSP) hll_unimplemented(SACT2, SP_Enum) // int SP_GetMaxZ(void) hll_unimplemented(SACT2, SP_GetMaxZ) + // int SP_SetCG(int nSP, int nCG) -hll_unimplemented(SACT2, SP_SetCG) +hll_defun(SP_SetCG, args) +{ + int sp = args[0].i; + int cg = args[1].i; + + WARNING("SACT2.SP_SetCG(%d, %d)", sp, cg); + sprites[sp].cg = cg_load(cg); + hll_return(!!sprites[sp].cg); +} + // int SP_SetCGFromFile(int nSP, string pIStringFileName) hll_unimplemented(SACT2, SP_SetCGFromFile) // int SP_SaveCG(int nSP, string pIStringFileName) hll_unimplemented(SACT2, SP_SaveCG) + // int SP_Create(int nSP, int nWidth, int nHeight, int nR, int nG, int nB, int nBlendRate) -hll_unimplemented(SACT2, SP_Create) +hll_defun(SP_Create, args) +{ + int sp = args[0].i; + int width = args[1].i; + int height = args[2].i; + int r = args[3].i; + int g = args[4].i; + int b = args[5].i; + int a = args[6].i; + + sprites[sp] = (struct SACT2_sprite) { + .pos = (Point) { + .x = 0, + .y = 0 + }, + .z = 0, + .width = width, + .height = height, + .r = r, + .g = g, + .b = b, + .a = a, + .cg = NULL, + .show = false, + .used = true + }; + sprite_set_show(&sprites[sp], true); + hll_return(1); +} + // int SP_CreatePixelOnly(int nSP, int nWidth, int nHeight) hll_unimplemented(SACT2, SP_CreatePixelOnly) // int SP_CreateCustom(int nSP) @@ -88,47 +185,114 @@ hll_unimplemented(SACT2, SP_CreateCustom) // int SP_Delete(int nSP) hll_unimplemented(SACT2, SP_Delete) // int SP_SetPos(int nSP, int nX, int nY) -hll_unimplemented(SACT2, SP_SetPos) +hll_defun(SP_SetPos, args) +{ + int sp = args[0].i; + sprites[sp].pos.x = args[1].i; + sprites[sp].pos.y = args[2].i; + hll_return(1); +} + // int SP_SetX(int nSP, int nX) -hll_unimplemented(SACT2, SP_SetX) +hll_defun(SP_SetX, args) +{ + sprites[args[0].i].pos.x = args[1].i; + hll_return(1); +} + // int SP_SetY(int nSP, int nY) -hll_unimplemented(SACT2, SP_SetY) +hll_defun(SP_SetY, args) +{ + sprites[args[0].i].pos.y = args[1].i; + hll_return(1); +} + // int SP_SetZ(int nSP, int nZ) -hll_unimplemented(SACT2, SP_SetZ) +hll_defun(SP_SetZ, args) +{ + sprites[args[0].i].z = args[1].i; + hll_return(1); +} + // int SP_SetBlendRate(int nSP, int nBlendRate) -hll_unimplemented(SACT2, SP_SetBlendRate) +hll_defun(SP_SetBlendRate, args) +{ + sprites[args[0].i].a = args[1].i; + hll_return(1); +} + // int SP_SetShow(int nSP, int nfShow) -hll_unimplemented(SACT2, SP_SetShow) +hll_defun(SP_SetShow, args) +{ + sprite_set_show(&sprites[args[0].i], args[1].i); + hll_return(1); +} + // int SP_SetDrawMethod(int nSP, int nMethod) hll_unimplemented(SACT2, SP_SetDrawMethod) // int SP_IsUsing(int nSP) hll_unimplemented(SACT2, SP_IsUsing) // int SP_ExistAlpha(int nSP) hll_unimplemented(SACT2, SP_ExistAlpha) + // int SP_GetPosX(int nSP) -hll_unimplemented(SACT2, SP_GetPosX) +hll_defun(SP_GetPosX, args) +{ + hll_return(sprites[args[0].i].pos.x); +} + // int SP_GetPosY(int nSP) -hll_unimplemented(SACT2, SP_GetPosY) +hll_defun(SP_GetPosY, args) +{ + hll_return(sprites[args[0].i].pos.y); +} + // int SP_GetWidth(int nSP) -hll_unimplemented(SACT2, SP_GetWidth) +hll_defun(SP_GetWidth, args) +{ + struct SACT2_sprite *sp = &sprites[args[0].i]; + if (sp->cg) + hll_return(sp->cg->width); + hll_return(sp->width); +} + // int SP_GetHeight(int nSP) -hll_unimplemented(SACT2, SP_GetHeight) +hll_defun(SP_GetHeight, args) +{ + struct SACT2_sprite *sp = &sprites[args[0].i]; + if (sp->cg) + hll_return(sp->cg->height); + hll_return(sp->width); +} + // int SP_GetZ(int nSP) -hll_unimplemented(SACT2, SP_GetZ) +hll_defun(SP_GetZ, args) +{ + hll_return(sprites[args[0].i].z); +} + // int SP_GetBlendRate(int nSP) -hll_unimplemented(SACT2, SP_GetBlendRate) +hll_defun(SP_GetBlendRate, args) +{ + hll_return(sprites[args[0].i].a); +} + // int SP_GetShow(int nSP) -hll_unimplemented(SACT2, SP_GetShow) +hll_defun(SP_GetShow, args) +{ + hll_return(sprites[args[0].i].show); +} + // int SP_GetDrawMethod(int nSP) -hll_unimplemented(SACT2, SP_GetDrawMethod) +hll_warn_unimplemented(SACT2, SP_GetDrawMethod, 0) // int SP_SetTextHome(int nSP, int nX, int nY) -hll_unimplemented(SACT2, SP_SetTextHome) +hll_warn_unimplemented(SACT2, SP_SetTextHome, 0) // int SP_SetTextLineSpace(int nSP, int nPx) hll_unimplemented(SACT2, SP_SetTextLineSpace) // int SP_SetTextCharSpace(int nSP, int nPx) hll_unimplemented(SACT2, SP_SetTextCharSpace) // int SP_SetTextPos(int nSP, int nX, int nY) -hll_unimplemented(SACT2, SP_SetTextPos) +hll_warn_unimplemented(SACT2, SP_SetTextPos, 0) // int SP_TextDraw(int nSP, string text, struct tm) hll_unimplemented(SACT2, SP_TextDraw) // int SP_TextClear(int nSP) diff --git a/hll/hll.h b/hll/hll.h index 9a5aa86..7ed46a2 100644 --- a/hll/hll.h +++ b/hll/hll.h @@ -26,22 +26,22 @@ #include "../system4.h" #define hll_defun(fname, args) \ - static union vm_value _hllfun_ ## fname(unused union vm_value *args); \ + static union vm_value _hllfun_ ## fname(possibly_unused union vm_value *args); \ static struct hll_function _hllstruct_ ## fname = { \ .name = #fname, \ .fun = _hllfun_ ## fname \ }; \ - static union vm_value _hllfun_ ## fname(unused union vm_value *args) + static union vm_value _hllfun_ ## fname(possibly_unused union vm_value *args) #define hll_unimplemented(libname, fname) \ hll_defun(fname, args) { \ - ERROR("Unimplemented HLL function: " #libname "." #fname); \ + VM_ERROR("Unimplemented HLL function: " #libname "." #fname); \ } -#define hll_warn_unimplemented(libname, fname) \ +#define hll_warn_unimplemented(libname, fname, rval) \ hll_defun(fname, args) { \ WARNING("Unimplemented HLL function: " #libname "." #fname); \ - hll_return(0); \ + hll_return(rval); \ } #define hll_export(fname) &_hllstruct_ ## fname diff --git a/meson.build b/meson.build index b62d5a6..317e9a5 100644 --- a/meson.build +++ b/meson.build @@ -7,8 +7,13 @@ system4 = ['ain.c', ] xsystem4 = ['ald.c', 'cg.c', + 'bmp.c', + 'pms.c', + 'qnt.c', + 'vsp.c', 'ini.c', 'page.c', + 'sdl_video.c', 'system4.c', 'vm.c', ] @@ -18,6 +23,7 @@ hll_libs = ['hll/Math.c', ] zdep = dependency('zlib') mdep = meson.get_compiler('c').find_library('m', required: false) +sdl2 = dependency('sdl2') -executable('system4', xsystem4 + system4 + hll_libs, dependencies: [mdep, zdep]) +executable('system4', xsystem4 + system4 + hll_libs, dependencies: [mdep, zdep, sdl2]) executable('aindump', ['aindump.c'] + system4, dependencies: [mdep, zdep]) diff --git a/page.c b/page.c index 48c4232..2b42539 100644 --- a/page.c +++ b/page.c @@ -178,6 +178,11 @@ void create_struct(int no, union vm_value *var) heap[memb].s = string_ref(&EMPTY_STRING); heap[slot].page->values[i].i = memb; break; + case AIN_ARRAY_TYPE: + memb = heap_alloc_slot(VM_PAGE); + heap[memb].page = NULL; + heap[slot].page->values[i].i = memb; + break; case AIN_STRUCT: create_struct(s->members[i].struct_type, &heap[slot].page->values[i]); break; diff --git a/pms.c b/pms.c new file mode 100644 index 0000000..ea62014 --- /dev/null +++ b/pms.c @@ -0,0 +1,320 @@ +/* + * pms.c extract 8/16 bit PMS cg + * + * Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * pms256.c + * 0.00 97/11/06 初版 + * 0.01 97/11/13 ヘッダ情報の返しかたの変更 + * 0.01-01 97/11/27 記憶子の変更 + * 0.02 98/02/26 デバッグ完了 + * pms64k.c + * 0.00 97/11/06 初版 + * 0.01 97/11/13 ヘッダ情報の返しかたの変更 + * 0.02 98/02/26 デバッグ完了 + * 0.03 98/07/27 うそぷ〜。透明色の展開まずってた。 + * @version 1.1 00/09/17 rewrite for changeing interface + */ + +#include +#include +#include "little_endian.h" +#include "graphics.h" +#include "cg.h" +#include "pms.h" + +/* + * static methods + */ +static struct pms_header *extract_header(uint8_t *b); +static void getpal(Pallet256 *pal, uint8_t *b); +static void extract_8bit(struct pms_header *pms, uint8_t *pic, uint8_t *b); +static void extract_16bit(struct pms_header *pms, uint16_t *pic, uint8_t *b); + +/* + * Get information from cg header + * b: raw data (pointer to header) + * return: acquired pms information object + */ +static struct pms_header *extract_header(uint8_t *b) { + struct pms_header *pms = malloc(sizeof(struct pms_header)); + + pms->pmsVer = LittleEndian_getW(b, 2); + pms->pmsHdrSize = LittleEndian_getW(b, 4); + pms->pmsBpp = b[6]; + pms->pmsBppS = b[7]; + pms->pmsSf = b[8]; + pms->pmsBf = LittleEndian_getW(b, 10); + pms->pmsX0 = LittleEndian_getDW(b, 16); + pms->pmsY0 = LittleEndian_getDW(b, 20); + pms->pmsXW = LittleEndian_getDW(b, 24); + pms->pmsYW = LittleEndian_getDW(b, 28); + pms->pmsDp = LittleEndian_getDW(b, 32); + pms->pmsPp = LittleEndian_getDW(b, 36); + pms->pmsCp = LittleEndian_getDW(b, 40); + + return pms; +} + +/* + * Get pallet from raw data + * pal: pallet to be stored + * b : raw data (pointer to pallet) + */ +static void getpal(Pallet256 *pal, uint8_t *b) { + int i; + + for (i = 0; i < 256; i++) { + pal->red[i] = *b++; + pal->green[i] = *b++; + pal->blue[i] = *b++; + } +} + +/* + * Do extract 8bit pms image + * pms: pms header information + * pic: pixel to be stored + * b : raw data (pointer to pixel) + */ +static void extract_8bit(struct pms_header *pms, uint8_t *pic, uint8_t *b) { + int c0, c1; + int x, y, loc, l, i; + int scanline = pms->pmsXW; + + for (y = 0; y < pms->pmsYW; y ++) { + for (x = 0; x < pms->pmsXW; ) { + loc = y * scanline + x; + c0 = *b++; + if (c0 <= 0xf7) { + *(pic + loc) = c0; x++; + } else if (c0 == 0xff) { + l = (*b) + 3; x+=l; b++; + memcpy(pic + loc, pic + loc - scanline, l); + } else if (c0 == 0xfe) { + l = (*b) + 3; x+=l; b++; + memcpy(pic + loc, pic + loc - scanline * 2, l); + } else if (c0 == 0xfd) { + l = (*b) + 4; x+=l; b++; + c0 = *b++; + memset(pic + loc, c0, l); + } else if (c0 == 0xfc) { + l = ((*b) + 3) * 2; x+=l; b++; + c0 = *b++; c1 = *b++; + for (i = 0; i < l; i+=2) { + *(pic + loc + i ) = c0; + *(pic + loc + i + 1) = c1; + } + } else { + *(pic + loc) = *b++; x++; + } + } + } +} + +/* + * Do extract 16bit pms image + * pms: pms header information + * pic: pixel to be stored + * b : raw data (pointer to pixel) + */ +static void extract_16bit(struct pms_header *pms, uint16_t *pic, uint8_t *b) { + int c0, c1, pc0, pc1; + int x, y, i, l, loc; + int scanline = pms->pmsXW; + + for (y = 0; y < pms->pmsYW; y++) { + for (x = 0; x < pms->pmsXW;) { + loc = y * scanline + x; + c0 = *b++; + if (c0 <= 0xf7) { + c1 = *b++; x++; + *(pic + loc) = c0 | (c1 << 8); + } else if (c0 == 0xff) { + l = (*b) + 2; x+=l; b++; + for (i = 0; i < l; i++) { + *(pic + loc + i) = *(pic + loc + i - scanline); + } + } else if (c0 == 0xfe) { + l = (*b) + 2; x+=l; b++; + for (i = 0; i < l; i++) { + *(pic + loc + i) = *(pic + loc + i - scanline * 2); + } + } else if (c0 == 0xfd) { + l = (*b) + 3; x+=l; b++; + c0 = *b++; c1 = *b++; + pc0 = c0 | (c1 << 8); + for (i = 0; i < l; i++) { + *(pic + loc + i) = pc0; + } + } else if (c0 == 0xfc) { + l = ((*b) + 2) * 2; x+=l; b++; + c0 = *b++; c1 = *b++; pc0 = c0 | (c1 << 8); + c0 = *b++; c1 = *b++; pc1 = c0 | (c1 << 8); + for (i = 0; i < l; i+=2) { + *(pic + loc + i ) = pc0; + *(pic + loc + i + 1) = pc1; + } + } else if (c0 == 0xfb) { + x++; + *(pic + loc) = *(pic + loc - scanline - 1); + } else if (c0 == 0xfa) { + x++; + *(pic + loc) = *(pic + loc - scanline + 1); + } else if (c0 == 0xf9) { + l = (*b) + 1; x+=l; b++; + c0 = *b++; c1 = *b++; + pc0 = ((c0 & 0xe0) << 8) + ((c0 & 0x18) << 6) + ((c0 & 0x07) << 2); + pc1 = ((c1 & 0xc0) << 5) + ((c1 & 0x3c) << 3) + (c1 & 0x03); + *(pic + loc) = pc0 + pc1; + for (i = 1; i < l; i++) { + c1 = *b++; + pc1 = ((c1 & 0xc0) << 5) + ((c1 & 0x3c) << 3) + (c1 & 0x03); + *(pic + loc + i) = pc0 | pc1; + } + } else { + c0 = *b++; c1 = *b++; x++; + *(pic + loc) = c0 | (c1 << 8); + } + } + } +} + +/* + * Check data is 8bit pms format cg or not + * data: raw data (pointer to data top) + * return: true if data is pms + */ +bool pms256_checkfmt(uint8_t *data) { + int x, y, w, h; + + if (data[0] != 0x50 || data[1] != 0x4d) return false; + if (data[6] != 8) return false; + + x = LittleEndian_getDW(data, 16); + y = LittleEndian_getDW(data, 20); + w = LittleEndian_getDW(data, 24); + h = LittleEndian_getDW(data, 28); + + if (x < 0 || y < 0 || w < 0 || h < 0) return false; + + return true; +} + +/* + * Extract 8bit pms, header, pallet and pixel + * data: raw data (pointer to data top) + * return: extracted image data and information +*/ +struct cg *pms256_extract(uint8_t *data) { + struct cg *cg = calloc(1, sizeof(struct cg)); + struct pms_header *pms = extract_header(data); + + cg->pal = malloc(sizeof(Pallet256)); + getpal(cg->pal, data + pms->pmsPp); + + // +10: margin for broken cg + cg->pic = malloc(sizeof(uint8_t) * ((pms->pmsXW + 10) * (pms->pmsYW + 10))); + extract_8bit(pms, cg->pic, data + pms->pmsDp); + + cg->type = ALCG_PMS8; + cg->x = pms->pmsX0; + cg->y = pms->pmsY0; + cg->width = pms->pmsXW; + cg->height = pms->pmsYW; + cg->pms_bank = pms->pmsBf; + cg->alpha = NULL; + + free(pms); + + return cg; +} + +/* + * Check data is 16bit pms format cg or not + * data: raw data (pointer to data top) + * return: true if data is pms + */ +bool pms64k_checkfmt(uint8_t *data) { + int x, y, w, h; + + if (data[0] != 0x50 || data[1] != 0x4d) return false; + if (data[6] != 16) return false; + + x = LittleEndian_getDW(data, 16); + y = LittleEndian_getDW(data, 20); + w = LittleEndian_getDW(data, 24); + h = LittleEndian_getDW(data, 28); + + if (x < 0 || y < 0 || w < 0 || h < 0) return false; + + return true; +} + +/* + * Extract 16bit pms, header, pallet and pixel + * data: raw data (pointer to data top) + * return: extracted image data and information + */ +struct cg *pms64k_extract(uint8_t *data) { + struct cg *cg = calloc(1, sizeof(struct cg)); + struct pms_header *pms = extract_header(data); + + // +10: margin for broken cg + cg->pic = (uint8_t *)malloc(sizeof(uint16_t) * ((pms->pmsXW + 10) * (pms->pmsYW + 10))); + extract_16bit(pms, (uint16_t *)cg->pic, data + pms->pmsDp); + + cg->alpha = NULL; + if (pms->pmsPp != 0) { + cg->alpha = malloc(sizeof(uint8_t) * ((pms->pmsXW + 10) * (pms->pmsYW + 10))); + extract_8bit(pms, cg->alpha, data + pms->pmsPp); + } + + cg->type = ALCG_PMS16; + cg->x = pms->pmsX0; + cg->y = pms->pmsY0; + cg->width = pms->pmsXW; + cg->height = pms->pmsYW; + cg->pal = NULL; + + free(pms); + + return cg; +} + +/* + * Extract pms pallet only + * data: raw data (pointer to data top) + * return: extracted pallet data + */ +struct cg *pms_getpal(uint8_t *data) { + struct cg *cg = calloc(1, sizeof(struct cg)); + struct pms_header *pms = extract_header(data); + + cg->pal = malloc(sizeof(Pallet256)); + getpal(cg->pal, data + pms->pmsPp); + + cg->type = ALCG_PMS8; + cg->pic = NULL; + cg->alpha = NULL; + + free(pms); + + return cg; +} diff --git a/pms.h b/pms.h new file mode 100644 index 0000000..03fbf67 --- /dev/null +++ b/pms.h @@ -0,0 +1,70 @@ +/* + * pms.h extract 8/16 bit PMS cg + * + * Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @version 1.1 00/09/17 rewrite for changeing interface + */ + +#ifndef SYSTEM4_PMS_H +#define SYSTEM4_PMS_H + +#include +#include +#include "cg.h" + +struct pms_header { + int pmsVer; // pms data version + int pmsHdrSize; // size of header + int pmsBpp; // pms data depth, 8 or 16 + int pmsBppS; // shadow data depth, if exists + int pmsSf; // sprite flag (not used?) + int pmsBf; // pallet bank + int pmsX0; // display location x + int pmsY0; // display location y + int pmsXW; // image width + int pmsYW; // image height + int pmsDp; // pointer to data + int pmsPp; // pointer to pallet or shadow + int pmsCp; // pointer to comment +}; +/* + * pmsBppS + * If pmsBpp is 16 and pmsPp is not 0, shadow (alpha data) is exists and + * pmsBppS shows shadow's depth (normally 8). + * + * pmsBf + * If pmsBpp is 8, pmsBf determine which pallets is used in this data and + * needed to copy. The pmsBf is mask and if bit is on then copy. + * For example, if pmsBf=0xff00, then copy pal[128] ~ pal[255]. + * + * pmsPp + * If pmsBpp is 8 then this is pointer to pallet. If pmsBpp is 16 and + * pmsPp is not 0, then shadow is exists and this is pointer to it's data. + * + */ + +bool pms256_checkfmt(uint8_t *data); +struct cg *pms256_extract(uint8_t *data); +bool pms64k_checkfmt(uint8_t *data); +struct cg *pms64k_extract(uint8_t *data); +struct cg *pms_getpal(uint8_t *data); + +#endif /* SYSTEM4_PMS_H */ + diff --git a/qnt.c b/qnt.c new file mode 100644 index 0000000..17640e2 --- /dev/null +++ b/qnt.c @@ -0,0 +1,262 @@ +/* + * qnt.c extract QNT cg + * + * Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @version 1.0 01/07/30 initial version + * @version 1.1 01/08/10 fix alpha map extraction + * @version 1.2 01/11/26 zlibの展開バッファが画像サイズよりも大きなものを + * 要求するときの workaround + * @version 1.2 01/11/28 format version 1 に対応 + * *(top + 4) is 0 -> version 0 + * *(top + 4) is 1 -> version 1 + * @version 1.3 03/02/22 縦横奇数サイズ時にpixel/alphaともおかしかったのをfix + * zlib展開バッファを縦横それぞれ1byteづつ多くとる + * ようにした + */ + +#include +#include + +#include "little_endian.h" +#include "cg.h" +#include "qnt.h" +#include "system4.h" + +/* + zlib の展開バッファで、幅×高さ×3に、さらにどれくらい余裕をとるか + (リクルスで 1164バイトというのがあった) +*/ +#define ZLIBBUF_MARGIN 5*1024 + +/* + static methods +*/ +static struct qnt_header *extract_header(uint8_t *b); +static void extract_pixel(struct qnt_header *qnt, uint8_t *pic, uint8_t *b); +static void extract_alpha(struct qnt_header *qnt, uint8_t *pic, uint8_t *b); + + +/* + * Get information from header + * + * b: raw data + * + * return: acquired qnt information object + */ +static struct qnt_header *extract_header(uint8_t *b) { + struct qnt_header *qnt = malloc(sizeof(struct qnt_header)); + int rsv0; + + rsv0 = LittleEndian_getDW(b, 4); + if (rsv0 == 0) { + qnt->hdr_size = 48; + qnt->x0 = LittleEndian_getDW(b, 8); + qnt->y0 = LittleEndian_getDW(b, 12); + qnt->width = LittleEndian_getDW(b, 16); + qnt->height = LittleEndian_getDW(b, 20); + qnt->bpp = LittleEndian_getDW(b, 24); + qnt->rsv = LittleEndian_getDW(b, 28); + qnt->pixel_size = LittleEndian_getDW(b, 32); + qnt->alpha_size = LittleEndian_getDW(b, 36); + } else { + qnt->hdr_size = LittleEndian_getDW(b, 8); + qnt->x0 = LittleEndian_getDW(b, 12); + qnt->y0 = LittleEndian_getDW(b, 16); + qnt->width = LittleEndian_getDW(b, 20); + qnt->height = LittleEndian_getDW(b, 24); + qnt->bpp = LittleEndian_getDW(b, 28); + qnt->rsv = LittleEndian_getDW(b, 32); + qnt->pixel_size = LittleEndian_getDW(b, 36); + qnt->alpha_size = LittleEndian_getDW(b, 40); + } + + return qnt; +} + +/* + * Do extract qnt pixel image + * + * qnt: qnt header information + * pic: pixel to be stored + * b : raw data (pointer to pixel) + */ +static void extract_pixel(struct qnt_header *qnt, uint8_t *pic, uint8_t *b) { + int i, j, x, y, w, h; + unsigned long ucbuf = (qnt->width+1) * (qnt->height+1) * 3 + ZLIBBUF_MARGIN; + uint8_t *raw = malloc(sizeof(uint8_t) * ucbuf); + + if (Z_OK != uncompress(raw, &ucbuf, b, qnt->pixel_size)) { + WARNING("uncompress failed\n"); + free(raw); + return; + } + + w = qnt->width; + h = qnt->height; + + j = 0; + for (i = 2; i >= 0; i--) { + for (y = 0; y < (h -1); y+=2) { + for (x = 0; x < (w -1); x+=2) { + pic[( y *w+x) *3 +i] = raw[j]; + pic[((y+1)*w+x) *3 +i] = raw[j+1]; + pic[( y *w+x+1)*3 +i] = raw[j+2]; + pic[((y+1)*w+x+1)*3 +i] = raw[j+3]; + j+=4; + } + if (x != w) { + pic[( y *w+x)*3 +i] = raw[j]; + pic[((y+1)*w+x)*3 +i] = raw[j+1]; + j+=4; + } + } + if (y != h) { + for (x = 0; x < (w -1); x+=2) { + pic[(y*w+x )*3+i] = raw[j]; + pic[(y*w+x+1)*3+i] = raw[j+2]; + j+=4; + } + if (x != w) { + pic[( y *w+x)*3 +i] = raw[j]; + j+=4; + } + } + } + + if (w > 1) { + for (x = 1; x < w; x++) { + pic[x*3 ] = pic[(x-1)*3 ] - pic[x*3 ]; + pic[x*3+1] = pic[(x-1)*3+1] - pic[x*3+1]; + pic[x*3+2] = pic[(x-1)*3+2] - pic[x*3+2]; + } + } + + if (h > 1) { + for (y = 1; y < h; y++) { + pic[(y*w)*3 ] = pic[((y-1)*w)*3 ] - pic[(y*w)*3 ]; + pic[(y*w)*3+1] = pic[((y-1)*w)*3+1] - pic[(y*w)*3+1]; + pic[(y*w)*3+2] = pic[((y-1)*w)*3+2] - pic[(y*w)*3+2]; + + for (x = 1; x < w; x++) { + int px, py; + py = pic[((y-1)*w+x )*3]; + px = pic[( y *w+x-1)*3]; + pic[(y*w+x)*3] = ((py+px)>>1) - pic[(y*w+x)*3]; + py = pic[((y-1)*w+x )*3+1]; + px = pic[( y *w+x-1)*3+1]; + pic[(y*w+x)*3+1] = ((py+px)>>1) - pic[(y*w+x)*3+1]; + py = pic[((y-1)*w+x )*3+2]; + px = pic[( y *w+x-1)*3+2]; + pic[(y*w+x)*3+2] = ((py+px)>>1) - pic[(y*w+x)*3+2]; + } + } + } + + free(raw); +} + +/* + * Do extract qnt alpha image + * + * qnt: qnt header information + * pic: pixel to be stored + * b : raw data (pointer to alpha pixel) + */ +static void extract_alpha(struct qnt_header *qnt, uint8_t *pic, uint8_t *b) { + int i, x, y, w, h; + unsigned long ucbuf = (qnt->width+1) * (qnt->height+1) + ZLIBBUF_MARGIN; + uint8_t *raw = malloc(sizeof(uint8_t) * ucbuf); + + if (Z_OK != uncompress(raw, &ucbuf, b, qnt->alpha_size)) { + WARNING("uncompress failed\n"); + free(raw); + return; + } + + w = qnt->width; + h = qnt->height; + + i = 1; + if (w > 1) { + pic[0] = raw[0]; + for (x = 1; x < w; x++) { + pic[x] = pic[x-1] - raw[i]; + i++; + } + if (w%2) i++; + } + + if (h > 1) { + for (y = 1; y < h; y++) { + pic[y*w] = pic[(y-1) *w] - raw[i]; i++; + for (x = 1; x < w; x++) { + int pax, pay; + pax = pic[ y * w + x -1]; + pay = pic[(y-1)* w + x ]; + pic[y*w+x] = ((pax+pay) >> 1) - raw[i]; + i++; + } + if (w%2) i++; + } + } + + free(raw); +} + +/* + * Check data is qnt format cg or not + * + * data: raw data (pointer to data top) + * + * return: true if data is qnt + */ +bool qnt_checkfmt(uint8_t *data) { + if (data[0] != 'Q' || data[1] != 'N' || data[2] != 'T') return false; + return true; +} + +/* + * Extract qnt header and pixel + * + * data: raw data (pointer to data top) + * + * return: extracted image data and information +*/ +struct cg *qnt_extract(uint8_t *data) { + struct cg *cg = calloc(1, sizeof(struct cg)); + struct qnt_header *qnt = extract_header(data); + + cg->pic = malloc(sizeof(uint8_t) * ((qnt->width+10) * (qnt->height+10) * 3)); + extract_pixel(qnt, cg->pic, data + qnt->hdr_size); + + if (qnt->alpha_size != 0) { + cg->alpha = malloc(sizeof(uint8_t) * ((qnt->width+10) * (qnt->height+10))); + extract_alpha(qnt, (uint8_t *)cg->alpha, data + qnt->hdr_size + qnt->pixel_size); + } + + cg->type = ALCG_QNT; + cg->x = qnt->x0; + cg->y = qnt->y0; + cg->width = qnt->width; + cg->height = qnt->height; + cg->pal = NULL; + + return cg; +} diff --git a/qnt.h b/qnt.h new file mode 100644 index 0000000..08003a6 --- /dev/null +++ b/qnt.h @@ -0,0 +1,48 @@ +/* + * qnt.h extract QNT cg + * + * Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @version 1.0 01/07/30 initial version + * @version 1.1 01/11/28 add header size information + */ + +#ifndef SYSTEM4_QNT_H +#define SYSTEM4_QNT_H + +#include +#include +#include "cg.h" + +struct qnt_header { + int hdr_size; // header size + int x0; // display location x + int y0; // display location y + int width; // image width + int height; // image height + int bpp; // image data depth + int rsv; // reserved data + int pixel_size; // compressed pixel size + int alpha_size; // compressed alpha pixel size +}; + +bool qnt_checkfmt(uint8_t *data); +struct cg *qnt_extract(uint8_t *data); + +#endif /* __QNT_H__ */ diff --git a/queue.h b/queue.h new file mode 100644 index 0000000..b714506 --- /dev/null +++ b/queue.h @@ -0,0 +1,574 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef SYSTEM4_QUEUE_H +#define SYSTEM4_QUEUE_H + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +/* + * Singly-linked List access methods. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == (void *)(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == (void *)(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) + +#endif /* sys/queue.h */ diff --git a/sdl_core.h b/sdl_core.h new file mode 100644 index 0000000..ddd387c --- /dev/null +++ b/sdl_core.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * Copyright (C) 2000- Fumihiko Murata + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef SYSTEM4_SDL_CORE_H +#define SYSTEM4_SDL_CORE_H + +#include + +int sdl_initialize(void); +void sdl_remove(void); + +void sdl_set_window_size(int x, int y, int w, int h); +void sdl_fullscreen(bool on); + +#endif /* SYSTEM4_SDL_CORE_H */ diff --git a/sdl_private.h b/sdl_private.h new file mode 100644 index 0000000..bf3f3a6 --- /dev/null +++ b/sdl_private.h @@ -0,0 +1,58 @@ +/* sdl_private.h SDL only private data + * + * Copyright (C) 2019 Nunuhara Cabbage + * Copyright (C) 2000- Fumihiko Murata + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef SYSTEM4_SDL_PRIVATE_H +#define SYSTEM4_SDL_PRIVATE_H + +#include +#include + +//#include "ags.h" +//#include "font.h" + +struct sdl_private_data { + SDL_Window *window; + SDL_Renderer *renderer; + SDL_Texture *texture; + SDL_Surface *display; /* toplevel surface */ + SDL_Surface *dib; /* offscreen surface */ + SDL_Color col[256]; /* color pallet */ + unsigned long white; /* white pixel */ + //agsurface_t *cimg; + SDL_Rect view; + //FONT *font; + bool dirty; + bool ms_active; /* mouse is active */ + bool fs_on; + int winoffset_x; /* draw offset in Window x */ + int winoffset_y; /* y */ +}; + +struct sdl_private_data sdl; + +extern void sdl_cursor_init(void); +extern void sdl_shadow_init(void); +extern int sdl_nearest_color(int r, int g, int b); + +#define SDL_AllocSurface(flags,w,h,d,r,g,b,a) SDL_CreateRGBSurface(0,w,h,d,r,g,b,a) + +#define setRect(r,xx,yy,ww,hh) (r).x=(xx),(r).y=(yy),(r).w=(ww),(r).h=(hh) +#define setOffset(s,x,y) (s->pixels) + (x) * (s->format->BytesPerPixel) + (y) * s->pitch + +#endif /* SYSTEM4_SDL_PRIVATE_H */ diff --git a/sdl_video.c b/sdl_video.c new file mode 100644 index 0000000..d7c0ae3 --- /dev/null +++ b/sdl_video.c @@ -0,0 +1,93 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * Copyright (C) 2000- Fumihiko Murata + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include "system4.h" +#include "sdl_private.h" + +int sdl_initialize(void) +{ + memset(&sdl, 0, sizeof(struct sdl_private_data)); + + // make toplevel window + SDL_Init(SDL_INIT_VIDEO); + sdl.window = SDL_CreateWindow("XSystem4", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + config.view_width, + config.view_height, + SDL_WINDOW_RESIZABLE); + sdl.renderer = SDL_CreateRenderer(sdl.window, -1, 0); + + // offscreen pixmap + //makeDIB(config.view_width, config.view_height, SYS4_DEFAULT_DEPTH); + + // init cursor + //sdl_cursor_init(); + + //sdl_setWindowSize(0, 0, config.view_width, config.view_height); + + //sdl_shadow_init(); + + //joy_open(); + return 0; +} + +void sdl_remove(void) +{ + if (!sdl.window) + return; + + //SDL_FreeSurface(sdl.dib); + SDL_DestroyRenderer(sdl.renderer); + //joy_close(); + SDL_Quit(); +} + +void sdl_fullscreen(bool on) +{ + if (on && !sdl.fs_on) { + sdl.fs_on = true; + SDL_SetWindowFullscreen(sdl.window, SDL_WINDOW_FULLSCREEN_DESKTOP); + } else if (!on && sdl.fs_on) { + sdl.fs_on = false; + SDL_SetWindowFullscreen(sdl.window, 0); + } +} + +void sdl_set_window_size(int x, int y, int w, int h) +{ + sdl.view.x = x; + sdl.view.y = y; + + if (w == sdl.view.w && h == sdl.view.h) + return; + + sdl.view.w = w; + sdl.view.h = h; + + SDL_SetWindowSize(sdl.window, w, h); + SDL_RenderSetLogicalSize(sdl.renderer, w, h); + if (sdl.display) + SDL_FreeSurface(sdl.display); + if (sdl.texture) + SDL_DestroyTexture(sdl.texture); + sdl.display = SDL_CreateRGBSurface(0, w, h, 32, 0, 0, 0, 0); + sdl.texture = SDL_CreateTexture(sdl.renderer, sdl.display->format->format, + SDL_TEXTUREACCESS_STATIC, w, h); +} diff --git a/system.c b/system.c index 56700d3..efecb52 100644 --- a/system.c +++ b/system.c @@ -57,25 +57,34 @@ mem_alloc char *_xstrdup(const char *in, const char *func) return out; } -noreturn void sys_error(char *fmt, ...) +noreturn void sys_verror(const char *fmt, va_list ap) { - va_list ap; - va_start(ap, fmt); vfprintf(stderr, fmt, ap); - va_end(ap); sys_exit(1); } -void sys_warning(char *fmt, ...) +noreturn void sys_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + sys_verror(fmt, ap); +} + +void sys_vwarning(const char *fmt, va_list ap) +{ + vfprintf(stderr, fmt, ap); +} + +void sys_warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + sys_vwarning(fmt, ap); va_end(ap); } -void sys_message(char *fmt, ...) +void sys_message(const char *fmt, ...) { va_list ap; va_start(ap, fmt); diff --git a/system4.c b/system4.c index 3318aa5..f42f12d 100644 --- a/system4.c +++ b/system4.c @@ -45,7 +45,7 @@ static char *config_strdup(const char *str) return out; } -static int config_handler(void *user, unused const char *section, const char *name, const char *value) +static int config_handler(void *user, possibly_unused const char *section, const char *name, const char *value) { struct config *config = (struct config*)user; if (!strcmp(name, "GameName")) { diff --git a/system4.h b/system4.h index 8e40674..79b0520 100644 --- a/system4.h +++ b/system4.h @@ -17,13 +17,14 @@ #ifndef SYSTEM4_H #define SYSTEM4_H +#include #include // TODO: should put in a separate header and guard with feature checks #define const_pure __attribute__((const)) #define mem_alloc __attribute__((malloc)) #define noreturn _Noreturn -#define unused __attribute__((unused)) +#define possibly_unused __attribute__((unused)) #define ERROR(fmt, ...) \ sys_error("*ERROR*(%s:%s:%d): " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__) @@ -34,9 +35,11 @@ #define NOTICE(fmt, ...) \ sys_message(fmt "\n", ##__VA_ARGS__) -noreturn void sys_error(char *fmt, ...); -void sys_warning(char *fmt, ...); -void sys_message(char *fmt, ...); +noreturn void sys_verror(const char *fmt, va_list ap); +noreturn void sys_error(const char *fmt, ...); +void sys_vwarning(const char *fmt, va_list ap); +void sys_warning(const char *fmt, ...); +void sys_message(const char *fmt, ...); noreturn void sys_exit(int code); diff --git a/vm.c b/vm.c index bdb6aa6..2ca8d74 100644 --- a/vm.c +++ b/vm.c @@ -34,9 +34,6 @@ // When the IP is set to VM_RETURN, the VM halts #define VM_RETURN 0xFFFFFFFF -#define EXECUTION_ERROR(msg, ...) \ - ERROR("%s (0x%X): " msg, current_instruction_name(), instr_ptr, ##__VA_ARGS__) - /* * NOTE: The current implementation is a simple bytecode interpreter. * System40.exe uses a JIT compiler, and we should too. @@ -66,7 +63,7 @@ static int32_t heap_free_ptr = 0; // Stack of function call frames static struct function_call call_stack[4096]; -static int32_t call_stack_ptr = 1; // 0 = imaginary frame before main() +static int32_t call_stack_ptr = 0; // 0 = imaginary frame before main() struct ain *ain; static size_t instr_ptr = 0; @@ -131,7 +128,7 @@ static const char *vm_ptrtype_string(enum vm_pointer_type type) { void heap_unref(int slot) { if (heap[slot].ref <= 0) { - EXECUTION_ERROR("double free of slot %d (%s)", slot, vm_ptrtype_string(heap[slot].type)); + VM_ERROR("double free of slot %d (%s)", slot, vm_ptrtype_string(heap[slot].type)); } if (--heap[slot].ref <= 0) { switch (heap[slot].type) { @@ -211,7 +208,7 @@ static union vm_value *stack_pop_var(void) int32_t page_index = stack_pop().i; int32_t heap_index = stack_pop().i; if (!heap[heap_index].page || page_index >= heap[heap_index].page->nr_vars) - EXECUTION_ERROR("Out of bounds page index: %d/%d", heap_index, page_index); + VM_ERROR("Out of bounds page index: %d/%d", heap_index, page_index); return &heap[heap_index].page->values[page_index]; } @@ -310,7 +307,7 @@ static void hll_call(int libno, int fno) { struct ain_hll_function *f = &ain->libraries[libno].functions[fno]; if (!f->fun) - EXECUTION_ERROR("Unimplemented HLL function: %s.%s", ain->libraries[libno].name, f->name); + VM_ERROR("Unimplemented HLL function: %s.%s", ain->libraries[libno].name, f->name); union vm_value args[HLL_MAX_ARGS]; for (int i = f->nr_arguments - 1; i >= 0; i--) { @@ -950,7 +947,7 @@ static void execute_instruction(int16_t opcode) b = stack_pop().i; a = stack_pop().i; if (a == -1) - EXECUTION_ERROR("Assignment to null-pointer"); + VM_ERROR("Assignment to null-pointer"); if (heap[a].page) delete_page(heap[a].page); heap[a].page = copy_page(heap[b].page); @@ -1050,7 +1047,7 @@ static void execute_instruction(int16_t opcode) case FUNC: break; default: - EXECUTION_ERROR("Unimplemented instruction"); + VM_ERROR("Unimplemented instruction"); } } @@ -1061,11 +1058,11 @@ static void vm_execute(void) if (instr_ptr == VM_RETURN) return; if (instr_ptr >= ain->code_size) { - EXECUTION_ERROR("Illegal instruction pointer: 0x%08lX", instr_ptr); + VM_ERROR("Illegal instruction pointer: 0x%08lX", instr_ptr); } opcode = get_opcode(instr_ptr); if (opcode >= NR_OPCODES) { - EXECUTION_ERROR("Illegal opcode: 0x%04X", opcode); + VM_ERROR("Illegal opcode: 0x%04X", opcode); } execute_instruction(opcode); instr_ptr += instructions[opcode].ip_inc; @@ -1140,6 +1137,7 @@ void vm_execute_ain(struct ain *program) // Initialize globals heap[0].page = alloc_page(GLOBAL_PAGE, 0, ain->nr_globals); for (int i = 0; i < ain->nr_globals; i++) { + int slot; switch (ain->globals[i].data_type) { case AIN_STRING: heap[0].page->values[i].i = heap_alloc_slot(VM_STRING); @@ -1147,6 +1145,11 @@ void vm_execute_ain(struct ain *program) case AIN_REF_TYPE: heap[0].page->values[i].i = -1; break; + case AIN_ARRAY_TYPE: + slot = heap_alloc_slot(VM_PAGE); + heap[0].page->values[i].i = slot; + heap[slot].page = NULL; + break; case AIN_STRUCT: create_struct(ain->globals[i].struct_type, &heap[0].page->values[i]); break; @@ -1172,3 +1175,21 @@ void vm_execute_ain(struct ain *program) vm_call(ain->main, -1); } +void vm_stack_trace(void) +{ + for (int i = call_stack_ptr - 1; i >= 0; i--) { + sys_warning("\t%s\n", ain->functions[call_stack[i].fno].name); + } +} + +noreturn void _vm_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + sys_vwarning(fmt, ap); + va_end(ap); + sys_warning("at %s (0x%X) in:\n", current_instruction_name(), instr_ptr); + vm_stack_trace(); + sys_exit(1); +} + diff --git a/vm.h b/vm.h index 54f59d1..5642133 100644 --- a/vm.h +++ b/vm.h @@ -19,6 +19,7 @@ #include #include +#include "system4.h" // Non-heap values. Stored in pages and on the stack. union vm_value { @@ -126,4 +127,10 @@ union vm_value vm_copy(union vm_value v, enum ain_data_type type); void vm_call(int fno, int struct_page); +void vm_stack_trace(void); +noreturn void _vm_error(const char *fmt, ...); + +#define VM_ERROR(fmt, ...) \ + _vm_error("*ERROR*(%s:%s:%d): " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__) + #endif /* SYSTEM4_VM_H */ diff --git a/vsp.c b/vsp.c new file mode 100644 index 0000000..9442db5 --- /dev/null +++ b/vsp.c @@ -0,0 +1,247 @@ +/* + * vsp.c extract VSP cg + * + * Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 0.00 97/11/06 初版 + * 0.01 97/11/12 TYPO + * 0.01-01 97/11/13 ヘッダ情報の返しかたの変更 + * 0.01-02 97/11/27 記憶子の変更 + * 0.01-03 97/12/06 ヘッダの誤認識の修正 + * 0.02 98/02/26 デバッグ済み + * 0.03 98/03/20 ヘッダチェックのミス + * 0.03 98/12/19 dalkで640x401なCGがあったのでその対策 + * @version 1.5 00/09/17 rewrite for changeing interface + */ + +#include +#include +#include +#include +#include "little_endian.h" +#include "graphics.h" +#include "cg.h" +#include "vsp.h" + +/* + * static methods + */ +static struct vsp_header *extract_header(uint8_t *b); +static void getpal(Pallet256 *pal, uint8_t *b); +static void extract(struct vsp_header *vsp, uint8_t *pic, uint8_t *b); + +/* + * extraction buffer + */ +static uint8_t _bc[4][480]; +static uint8_t _bp[4][480]; +static uint8_t *bc[4]; +static uint8_t *bp[4]; + +/* + * Get information from cg header + * b: raw data (pointer to header) + * return: acquired vsp information object +*/ +static struct vsp_header *extract_header(uint8_t *b) { + struct vsp_header *vsp = malloc(sizeof(struct vsp_header)); + + vsp->vspX0 = LittleEndian_getW(b, 0); + vsp->vspY0 = LittleEndian_getW(b, 2); + vsp->vspXW = LittleEndian_getW(b, 4) - vsp->vspX0; + vsp->vspYW = LittleEndian_getW(b, 6) - vsp->vspY0; + vsp->vspPb = b[9]; + vsp->vspPp = 0x0a; + vsp->vspDp = 0x3a; + + return vsp; +} + +/* + * Get pallet from raw data + * pal: pallet to be stored + * b : raw data (pointer to pallet) + */ +static void getpal(Pallet256 *pal, uint8_t *b) { + int red, green, blue, i; + + for (i = 0; i < 16; i++) { + blue = b[i * 3 + 0]; + red = b[i * 3 + 1]; + green = b[i * 3 + 2]; + pal->red[i] = (red << 4); + pal->green[i] = (green << 4); + pal->blue[i] = (blue << 4); + } +} + +/* + * Do extract vsp image + * vsp: vsp header information + * pic: pixel to be stored + * b : raw data (pointer to pixel) + */ +static void extract(struct vsp_header *vsp, uint8_t *pic, uint8_t *b) { + int c0; + uint8_t b0, b1, b2, b3, mask = 0; + uint8_t *bt; + int i, l, x, y, pl, loc; + + bp[0] = _bp[0]; bc[0] = _bc[0]; + bp[1] = _bp[1]; bc[1] = _bc[1]; + bp[2] = _bp[2]; bc[2] = _bc[2]; + bp[3] = _bp[3]; bc[3] = _bc[3]; + + for (x = 0; x < vsp->vspXW; x++) { + for (pl = 0; pl < 4; pl++) { + y = 0; + while(y < vsp->vspYW) { + c0 = *b++; + if (c0 >= 0x08) { + *(bc[pl] + y) = c0; y++; + } else if (c0 == 0x00) { + l = (*b) + 1; b++; + memcpy(bc[pl] + y, bp[pl] + y, l); + y+=l; + } else if (c0 == 0x01) { + l = (*b) + 1; b++; + b0 = *b++; + memset(bc[pl] + y, b0, l); + y+=l; + } else if (c0 == 0x02) { + l = (*b) + 1; b++; + b0 = *b++; b1 = *b++; + for (i = 0; i < l; i++) { + *(bc[pl] + y) = b0; y++; + *(bc[pl] + y) = b1; y++; + } + } else if (c0 == 0x03) { + l = (*b) + 1; b++; + for (i = 0; i < l; i++) { + *(bc[pl] + y) = (*(bc[0] + y) ^ mask); y++; + } + mask = 0; + } else if (c0 == 0x04) { + l = (*b) + 1; b++; + for (i = 0; i < l; i++) { + *(bc[pl] + y) = (*(bc[1] + y) ^ mask); y++; + } + mask = 0; + } else if (c0 == 0x05) { + l = (*b) + 1; b++; + for (i = 0; i < l; i++) { + *(bc[pl] + y) = (*(bc[2] + y) ^ mask); y++; + } + mask = 0; + } else if (c0 == 0x06) { + mask = 0xff; + } else if (c0 == 0x07) { + *(bc[pl] + y) = *b++ ; y++; + } + } + } + /* plane -> packed 展開 */ + for (y = 0; y < vsp->vspYW; y++) { + loc = (y * vsp->vspXW + x) * 8; + b0 = bc[0][y]; b1 = bc[1][y]; + b2 = bc[2][y]; b3 = bc[3][y]; + *(pic + loc + 0) = ((b0>>7)&0x01)|((b1>>6)&0x02)|((b2>>5)&0x04)|((b3>>4)&0x08); + *(pic + loc + 1) = ((b0>>6)&0x01)|((b1>>5)&0x02)|((b2>>4)&0x04)|((b3>>3)&0x08); + *(pic + loc + 2) = ((b0>>5)&0x01)|((b1>>4)&0x02)|((b2>>3)&0x04)|((b3>>2)&0x08); + *(pic + loc + 3) = ((b0>>4)&0x01)|((b1>>3)&0x02)|((b2>>2)&0x04)|((b3>>1)&0x08); + *(pic + loc + 4) = ((b0>>3)&0x01)|((b1>>2)&0x02)|((b2>>1)&0x04)|((b3 )&0x08); + *(pic + loc + 5) = ((b0>>2)&0x01)|((b1>>1)&0x02)|((b2 )&0x04)|((b3<<1)&0x08); + *(pic + loc + 6) = ((b0>>1)&0x01)|((b1 )&0x02)|((b2<<1)&0x04)|((b3<<2)&0x08); + *(pic + loc + 7) = ((b0 )&0x01)|((b1<<1)&0x02)|((b2<<2)&0x04)|((b3<<3)&0x08); + } + /* bc -> bpにコピー */ + bt = bp[0]; bp[0] = bc[0]; bc[0] = bt; + bt = bp[1]; bp[1] = bc[1]; bc[1] = bt; + bt = bp[2]; bp[2] = bc[2]; bc[2] = bt; + bt = bp[3]; bp[3] = bc[3]; bc[3] = bt; + } +} + +/* + * Check data is vsp format cg or not + * data: raw data (pointer to data top) + * return: true if data is vsp + */ +bool vsp_checkfmt(uint8_t *data) { + int x0 = LittleEndian_getW(data, 0); + int y0 = LittleEndian_getW(data, 2); + int w = LittleEndian_getW(data, 4) - x0; + int h = LittleEndian_getW(data, 6) - y0; + + if (x0 < 0 || x0 > 80 || y0 < 0 || y0 > 400) return false; + /* 401: for dalk's broken cg */ + if (w < 0 || w > 80 || h < 0 || h > 401) return false; + + return true; +} + +/* + * Extract vsp, header, pallet and pixel + * data: raw data (pointer to data top) + * return: extracted image data and information + */ +struct cg *vsp_extract(uint8_t *data) { + struct cg *cg = calloc(1, sizeof(struct cg)); + struct vsp_header *vsp = extract_header(data); + + cg->pal = malloc(sizeof(Pallet256)); + getpal(cg->pal, data + vsp->vspPp); + + /* +10: margin for broken cg */ + cg->pic = malloc(sizeof(uint8_t) * ((vsp->vspXW * 8 + 10) * (vsp->vspYW + 10))); + extract(vsp, cg->pic, data + vsp->vspDp); + + cg->type = ALCG_VSP; + cg->x = vsp->vspX0 * 8; + cg->y = vsp->vspY0; + cg->width = vsp->vspXW * 8; + cg->height = vsp->vspYW; + cg->vsp_bank = vsp->vspPb; + cg->alpha = NULL; + + free(vsp); + + return cg; +} + +/* + * Extract vsp pallet only + * data: raw data (pointer to data top) + * return: extracted pallet data + */ +struct cg *vsp_getpal(uint8_t *data) { + struct cg *cg = calloc(1, sizeof(struct cg)); + struct vsp_header *vsp = extract_header(data); + + cg->pal = malloc(sizeof(Pallet256)); + getpal(cg->pal, data + vsp->vspPp); + + cg->type = ALCG_VSP; + cg->pic = NULL; + cg->alpha = NULL; + + free(vsp); + + return cg; +} diff --git a/vsp.h b/vsp.h new file mode 100644 index 0000000..16758c6 --- /dev/null +++ b/vsp.h @@ -0,0 +1,50 @@ +/* + * vsp.h extract VSP cg + * + * Copyright (C) 1997-1998 Masaki Chikama (Wren) + * 1998- + * 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . +*/ + +#ifndef SYSTEM4_VSP_H +#define SYSTEM4_VSP_H + +#include +#include +#include "cg.h" + +struct vsp_header { + int vspX0; // display location x + int vspY0; // display location y + int vspXW; // image width + int vspYW; // image height + int vspPb; // default pallet bank + int vspPp; // pointer to pallet + int vspDp; // pointer to pixel data +}; +/* + * vspPb: + * VSP has only 16 pallets. When 256 pallets mode, this parameter determine + * where to copy these 16 pallets in 256 pallets. + * The parameter is from 0 to 15, and for example, if it is 1 then copy + * 16 palltes into pal[16] ~ pal[31]. + */ + +bool vsp_checkfmt(uint8_t *data); +struct cg *vsp_extract(uint8_t *data); +struct cg *vsp_getpal(uint8_t *data); + +#endif /* SYSTEM4_VSP_H */