/*
 * vsp.c  extract VSP cg
 *
 * Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
 *               1998-                           <masaki-c@is.aist-nara.ac.jp>
 *
 * 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
*/
/* $Id: vsp.c,v 1.6 2000/11/25 18:31:49 chikama Exp $ */

#include <stdlib.h>
#include <string.h>
#include "portab.h"
#include "LittleEndian.h"
#include "cg.h"
#include "vsp.h"

/*
 * static methods
*/
static vsp_header *extract_header(uint8_t *b);
static void extract(vsp_header *vsp, uint8_t *pic, uint8_t *b);

/*
 * Get information from cg header
 *   b: raw data (pointer to header)
 *   return: acquired vsp information object
*/
static vsp_header *extract_header(uint8_t *b) {
	vsp_header *vsp = malloc(sizeof(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 palette from raw data
 *   b  : raw data (pointer to palette)
*/
static SDL_Color *getpal(uint8_t *b) {
	SDL_Color *pal = malloc(sizeof(SDL_Color) * 256);
	for (int i = 0; i < 16; i++) {
		pal[i].b = b[i * 3 + 0] * 17;
		pal[i].r = b[i * 3 + 1] * 17;
		pal[i].g = b[i * 3 + 2] * 17;
		pal[i].a = 255;
	}
	return pal;
}

/*
 * Do extract vsp image
 *   vsp: vsp header information
 *   pic: pixel to be stored
 *   b  : raw data (pointer to pixel)
*/
static void extract(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;

	// Extraction buffers.
	uint8_t *buf = malloc(vsp->vspYW * 8);
	uint8_t *bc[4];
	uint8_t *bp[4];
	for (int i = 0; i < 4; i++) {
		bc[i] = buf + vsp->vspYW * (i * 2);
		bp[i] = buf + vsp->vspYW * (i * 2 + 1);
	}
	
	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;
	}
	free(buf);
}

/*
 * 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, palette and pixel
 *   data: raw data (pointer to data top)
 *   return: extracted image data and information
*/
cgdata *vsp_extract(uint8_t *data) {
	cgdata *cg = calloc(1, sizeof(cgdata));
	vsp_header *vsp = extract_header(data);
	
	cg->pal = getpal(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->depth = 8;
	cg->vsp_bank = vsp->vspPb;
	cg->alpha = NULL;
	
	free(vsp);
	
	return cg;
}
