/***************************************************************************

	vidhrdw.c

	NEOGEO CDZ video emulation

***************************************************************************/

#include "ncdz.h"


/***************************************************************************
	Global variables
 **************************************************************************/

int neogeo_blitter;

u16 neogeo_vidram16[0x20000 >> 1];
u16 neogeo_vidram16_offset;
u16 neogeo_vidram16_modulo;
u32 neogeo_palette_index;
u16 *neogeo_paletteram16;
u16 neogeo_palettebank16[2][0x2000 >> 1];

u16 *video_palette16;
u16 video_palettebank16[2][0x2000 >> 1];
u16 video_clut16[0x8000];

u8 video_fix_usage[0x1000];
u8 video_spr_usage[0x8000];

int video_enable;
int spr_disable;
int fix_disable;


/***************************************************************************
	Private variables
 **************************************************************************/

static int snap_no;
static int next_update_first_line;

#include "zoom.c"


/***************************************************************************
	Function prototypes
 **************************************************************************/

static void neogeo_init_sprite(void);
static void patch_vram_rbff2(void);
static void patch_vram_adkworld(void);
static void patch_vram_crsword2(void);


/*------------------------------------------------------
	Initialize video emulation
 -----------------------------------------------------*/

void neogeo_video_init(void)
{
	int i, r, g, b;

	for (r = 0; r < 32; r++)
	{
		for (g = 0; g < 32; g++)
		{
			for (b = 0; b < 32; b++)
			{
				int r1 = (r << 3) | (r >> 2);
				int g1 = (g << 3) | (g >> 2);
				int b1 = (b << 3) | (b >> 2);

				u16 color = ((r & 1) << 14) | ((r & 0x1e) << 7)
						  | ((g & 1) << 13) | ((g & 0x1e) << 3)
						  | ((b & 1) << 12) | ((b & 0x1e) >> 1);

				video_clut16[color] = MAKECOL15(r1, g1, b1);
			}
		}
	}

	memset(neogeo_palettebank16, 0, sizeof(neogeo_palettebank16));
	memset(video_palettebank16, 0, sizeof(video_palettebank16));

	for (i = 0; i < 0x1000; i += 16)
	{
		video_palettebank16[0][i] = 0x8000;
		video_palettebank16[1][i] = 0x8000;
	}

	neogeo_video_reset();
}


/*------------------------------------------------------
	Reset video emulation
 -----------------------------------------------------*/

void neogeo_video_reset(void)
{
	neogeo_palette_index = 0;
	neogeo_paletteram16 = neogeo_palettebank16[0];
	video_palette16 = video_palettebank16[0];

	neogeo_vidram16_modulo = 0;
	neogeo_vidram16_offset = 0;

	next_update_first_line = 0;

	video_enable = 1;
	spr_disable = 0;
	fix_disable = 0;

	neogeo_sprite_reset();
}


/*------------------------------------------------------
	Draw fixed sprites
 -----------------------------------------------------*/

static void draw_fix(void)
{
	u16 x, y, code, color;

	for (y = 16/8; y < 240/8; y++)
	{
		for (x = 1/8; x < 312/8; x++)
		{
			code  = neogeo_vidram16[(0xe000 >> 1) + y + (x << 5)];
			color = (code & 0xf000) >> 12;
			code  &= 0x0fff;

			if (video_fix_usage[code])
			{
				u32 key = MAKE_HASH_KEY(code, color);

				blit_drawfix(
					x << 3,
					y << 3,
					&video_palette16[color << 4],
					(u32 *)&neogeo_fix_memory[code << 5],
					key
				);
			}
		}
	}
}


/*------------------------------------------------------
	Draw sprites
 -----------------------------------------------------*/

static void draw_spr(int start, int end, struct rectangle *clip)
{
	int sx, sy, my, zx, zy, rzy;
	int offs, count, drawn_lines;
	int tileno, tileatr, t1, t3;
	int fullmode = 0, y, sy2;

	for (count = start; count < end; count++)
	{
		t3 = neogeo_vidram16[(0x10000 >> 1) + count];
		t1 = neogeo_vidram16[(0x10400 >> 1) + count];

		if (t1 & 0x40)
		{
			sx += zx + 1;
		}
      	else
		{
			rzy = t3 & 0xff;

			sy = 0x200 - (t1 >> 7);

			my = t1 & 0x3f;

			if (my > 0x20)
			{
				fullmode = 1;
				my = 0x20;

				if (sy > 0x100) sy -= 0x200;
				while (sy < -16) sy += 2 * (rzy + 1);
			}
			else
				fullmode = 0;

			sx = neogeo_vidram16[(0x10800 >> 1) + count] >> 7;
		}

		if (sx >= 0x1f0) sx -= 0x200;

		zx = (t3 >> 8) & 0x0f;

		if (sx + zx < 8 || sx > 311) continue;
		if (!my || !rzy) continue;

		offs = count << 6;

		if (fullmode)
		{
			sy2 = sy;

			for (y = 0; y < my; y++)
			{
				if ((zy = yskip[rzy][y]))
				{
					if (sy2 + zy >= clip->min_y && sy2 <= clip->max_y)
					{
						tileno  = neogeo_vidram16[offs + 0];
						tileatr = neogeo_vidram16[offs + 1];

						if (tileatr & 0x8)
							tileno = (tileno & ~7) | (neogeo_frame_counter & 7);
						else if (tileatr & 0x4)
							tileno = (tileno & ~3) | (neogeo_frame_counter & 3);

						if (video_spr_usage[tileno])
						{
							u16 color = (tileatr & 0xff00) >> 8;
							u32 key = MAKE_HASH_KEY(tileno, color);

							blit_drawspr(
								sx, sy2,
								zx + 1, zy,
								tileatr & 0x03,
								&video_palette16[color << 4],
								(u32 *)&neogeo_spr_memory[tileno << 7],
								key
							);
						}
					}

					sy2 += zy;
					if (sy2 >= 248) sy2 -= (rzy + 1) << 1;
				}

				offs += 2;
			}
		}
		else
		{
			y = 0;
			drawn_lines = 0;

			while (drawn_lines < my * 0x10)
			{
				if ((zy = yskip[rzy][y]))
				{
					sy2 = (sy + drawn_lines) & 0x1ff;

					if (sy2 + zy >= clip->min_y && sy2 <= clip->max_y)
					{
						tileno  = neogeo_vidram16[offs + 0];
						tileatr = neogeo_vidram16[offs + 1];

						if (tileatr & 0x8)
							tileno = (tileno & ~7) | (neogeo_frame_counter & 7);
						else if (tileatr & 0x4)
							tileno = (tileno & ~3) | (neogeo_frame_counter & 3);

						if (video_spr_usage[tileno])
						{
							u16 color = (tileatr & 0xff00) >> 8;
							u32 key = MAKE_HASH_KEY(tileno, color);

							blit_drawspr(
								sx, sy2,
								zx + 1, zy,
								tileatr & 0x03,
								&video_palette16[color << 4],
								(u32 *)&neogeo_spr_memory[tileno << 7],
								key
							);
						}
					}

					drawn_lines += zy;
				}

				offs += 2;

				y++;
				if (y == 0x10 || y == 0x20)
					drawn_lines += (0xff - rzy) << 1;
			}
		}
	}
}


/*------------------------------------------------------
	Draw sprites
 -----------------------------------------------------*/

static void draw_spr_prio(void)
{
	int start = 0, end = 0x300 >> 1;

	if ((neogeo_vidram16[(0x10400 + 4) >> 1] & 0x40) == 0 &&
		(neogeo_vidram16[(0x10400 + 6) >> 1] & 0x40) != 0)
	{
	    start = 6 >> 1;

		while ((neogeo_vidram16[(0x10400 >> 1) + start] & 0x40) != 0)
		    start++;

		if (start == 6 >> 1) start = 0;
	}

    do
    {
    	draw_spr(start, end, &visible_area);
		end = start;
		start = 0;

	} while (end != 0);
}


/*------------------------------------------------------
	Update screen (NORMAL driver)
 -----------------------------------------------------*/

void neogeo_screenrefresh(void)
{
	if (!video_enable)
	{
		blit_start(0);
		blit_finish();
		return;
	}

	blit_start(video_palette16[4095]);

	if (!spr_disable)
	{
		if (NGH_NUMBER(0x0085))
		{
			draw_spr_prio();
		}
		else
		{
			switch (neogeo_ngh_number)
			{
			case 0x0054: patch_vram_crsword2(); break;
			case 0x0204: patch_vram_adkworld(); break;
			case 0x0240: patch_vram_rbff2(); break;
			}

			draw_spr(0, 0x300 >> 1, &visible_area);
		}
	}

	if (!fix_disable) draw_fix();

	blit_finish();
}


/*------------------------------------------------------
	Update screen (Raster driver)
 -----------------------------------------------------*/

#ifdef EMULATE_RASTER_EFFECT
void neogeo_partial_screenrefresh(int current_line)
{
	if (!spr_disable)
	{
		struct rectangle clip;

		clip.min_x = visible_area.min_x;
		clip.max_x = visible_area.max_x;
		clip.min_y = next_update_first_line;
		clip.max_y = current_line;

		if (clip.min_y < visible_area.min_y)
			clip.min_y = visible_area.min_y;
		if (clip.max_y > visible_area.max_y)
			clip.max_y = visible_area.max_y;

		if (clip.max_y >= clip.min_y)
		{
			if (clip.min_y == FIRST_VISIBLE_LINE)
				blit_partial_start(video_palette16[4095]);

			draw_spr(0, 0x300 >> 1, &clip);

			blit_partial_update(&clip);
		}
	}

	next_update_first_line = current_line + 1;
}


void neogeo_raster_screenrefresh(void)
{
	if (!video_enable)
	{
		blit_start(0);
		blit_finish();
		return;
	}

	neogeo_partial_screenrefresh(LAST_VISIBLE_LINE);
	next_update_first_line = 0;

	if (!fix_disable) draw_fix();

	blit_partial_finish();
}
#endif

/*------------------------------------------------------
	Update screen (CD-ROM load screen)
 -----------------------------------------------------*/

void neogeo_loading_screenrefresh(void)
{
	blit_start(0);

	if (!video_enable)
	{
		blit_finish();
		video_flip_screen(0);
		return;
	}

	if (!fix_disable) draw_fix();

	blit_finish();

	video_flip_screen(0);
}


/*------------------------------------------------------
	Patch VRAM (Realbout Fatal Fury 2)
 -----------------------------------------------------*/

static void patch_vram_rbff2(void)
{
	u16 offs;

	for (offs = 0; offs < ((0x300 >> 1) << 6) ; offs += 2)
	{
		u16 tileno  = neogeo_vidram16[offs];
		u16 tileatr = neogeo_vidram16[offs + 1];

		if (tileno == 0x7a00 && (tileatr == 0x4b00 || tileatr == 0x1400))
		{
			neogeo_vidram16[offs] = 0x7ae9;
			return;
		}
	}
}


/*------------------------------------------------------
	Patch VRAM (ADK World)
 -----------------------------------------------------*/

static void patch_vram_adkworld(void)
{
	u16 offs;

	for (offs = 0; offs < ((0x300 >> 1) << 6) ; offs += 2)
	{
		u16 tileno  = neogeo_vidram16[offs];
		u16 tileatr = neogeo_vidram16[offs + 1];

		if ((tileno == 0x14c0 || tileno == 0x1520) && tileatr == 0x0000)
			neogeo_vidram16[offs] = 0x0000;
	}
}


/*------------------------------------------------------
	Patch VRAM (Crossed Swords 2)
 -----------------------------------------------------*/

static void patch_vram_crsword2(void)
{
	u16 offs;

	for (offs = 0; offs < ((0x300 >> 1) << 6) ; offs += 2)
	{
		u16 tileno  = neogeo_vidram16[offs];
		u16 tileatr = neogeo_vidram16[offs + 1];

		if (tileno == 0x52a0 && tileatr == 0x0000)
			neogeo_vidram16[offs] = 0x0000;
	}
}


/*------------------------------------------------------
	Initialize snapshot number
 -----------------------------------------------------*/

void snapshot_init(void)
{
	FILE *fp;
	char path[MAX_PATH];

	snap_no = 0;

	sprintf(path, "%ssnap/%s_%02d.png", launchDir, game_name, snap_no++);

	while ((fp = fopen(path, "r")) != NULL)
	{
		fclose(fp);
		snap_no++;
		sprintf(path, "%ssnap/%s_%02d.png", launchDir, game_name, snap_no++);
	}
}


/*------------------------------------------------------
	Save snapshot
 -----------------------------------------------------*/

void save_snapshot(void)
{
	char path[MAX_PATH];

	sprintf(path, "%ssnap/%s_%02d.png", launchDir, game_name, snap_no++);
	neogeo_sound_mute(1);
	mp3_pause(1);
	save_png(path);
	mp3_pause(0);
	neogeo_sound_mute(0);
}


#ifdef SAVE_STATE
/*------------------------------------------------------
	Save video hardware state
 -----------------------------------------------------*/

void state_save_vidhrdw(u8 *state_buf)
{
	u8 *in = state_buf;

	state_save_word(neogeo_vidram16, 0x10000);
	state_save_word(neogeo_palettebank16[0], 0x1000);
	state_save_word(neogeo_palettebank16[1], 0x1000);
	state_save_word(&neogeo_vidram16_offset, 1);
	state_save_word(&neogeo_vidram16_modulo, 1);
	state_save_long(&neogeo_palette_index, 1);
	state_save_long(&video_enable, 1);
	state_save_long(&fix_disable, 1);
	state_save_long(&spr_disable, 1);
}


/*------------------------------------------------------
	Load video hardware state
 -----------------------------------------------------*/

void state_load_vidhrdw(u8 *state_buf)
{
	u8 *out = state_buf;
	int i;

	state_load_word(neogeo_vidram16, 0x10000);
	state_load_word(neogeo_palettebank16[0], 0x1000);
	state_load_word(neogeo_palettebank16[1], 0x1000);
	state_load_word(&neogeo_vidram16_offset, 1);
	state_load_word(&neogeo_vidram16_modulo, 1);
	state_load_long(&neogeo_palette_index, 1);
	state_load_long(&video_enable, 1);
	state_load_long(&fix_disable, 1);
	state_load_long(&spr_disable, 1);

	for (i = 0; i < 0x1000; i++)
	{
		if (i & 0x0f)
		{
			video_palettebank16[0][i] = video_clut16[neogeo_palettebank16[0][i] & 0x7fff];
			video_palettebank16[1][i] = video_clut16[neogeo_palettebank16[1][i] & 0x7fff];
		}
	}

	neogeo_paletteram16 = neogeo_palettebank16[neogeo_palette_index];
	video_palette16 = video_palettebank16[neogeo_palette_index];
}


/*------------------------------------------------------
	Update screen (redraw screen)
 -----------------------------------------------------*/

void state_screenrefresh(void)
{
	blit_start(video_palette16[4095]);

	if (NGH_NUMBER(0x0085))
	{
		draw_spr_prio();
	}
	else
	{
		switch (neogeo_ngh_number)
		{
		case 0x0054: patch_vram_crsword2(); break;
		case 0x0204: patch_vram_adkworld(); break;
		case 0x0240: patch_vram_rbff2(); break;
		}

		draw_spr(0, 0x300 >> 1, &visible_area);
	}

	draw_fix();
}

#endif
