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

	state.c

	ステートセーブ/ロード

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

#ifdef SAVE_STATE

#include "emumain.h"
#include <time.h>


/******************************************************************************
	グローバル変数
******************************************************************************/

char date_str[16];
char time_str[16];
char stver_str[16];
int  state_version;
#if (EMU_SYSTEM == MVS)
int  state_reload_bios;
#endif


/******************************************************************************
	ローカル変数
******************************************************************************/

#if (EMU_SYSTEM == CPS1)
static const char *current_version = "CPS1SV01";
#elif (EMU_SYSTEM == CPS2)
static const char *current_version = "CPS2SV04";
#elif (EMU_SYSTEM == MVS)
static const char *current_version = "MVSSV001";
#endif


/******************************************************************************
	ローカル関数
******************************************************************************/

/*------------------------------------------------------
	サムネイルをワーク領域からファイルに保存
------------------------------------------------------*/

static void save_thumbnail(FILE *fp)
{
	int x, y, w, h;
	u16 *src = video_frame_addr(tex_frame, 152, 0);

	if (machine_screen_type)
	{
		w = 112;
		h = 152;
	}
	else
	{
		w = 152;
		h = 112;
	}

	for (y = 0; y < h; y++)
	{
		for (x = 0; x < w; x++)
		{
			fwrite(&src[x], 1, 2, fp);
		}
		src += BUF_WIDTH;
	}
}


/*------------------------------------------------------
	サムネイルをファイルからワーク領域に読み込み
------------------------------------------------------*/

static void load_thumbnail(FILE *fp)
{
	int x, y, w, h;
	u16 *dst = video_frame_addr(tex_frame, 0, 0);

	if (machine_screen_type)
	{
		w = 112;
		h = 152;
	}
	else
	{
		w = 152;
		h = 112;
	}

	for (y = 0; y < h; y++)
	{
		for (x = 0; x < w; x++)
		{
			fread(&dst[x], 1, 2, fp);
		}
		dst += BUF_WIDTH;
	}
}


/*------------------------------------------------------
	ワーク領域のサムネイルをクリア
------------------------------------------------------*/

static void clear_thumbnail(void)
{
	int x, y, w, h;
	u16 *dst = video_frame_addr(tex_frame, 0, 0);

	if (machine_screen_type)
	{
		w = 112;
		h = 152;
	}
	else
	{
		w = 152;
		h = 112;
	}

	for (y = 0; y < h; y++)
	{
		for (x = 0; x < w; x++)
		{
			dst[x] = 0;
		}
		dst += BUF_WIDTH;
	}
}


/******************************************************************************
	ステートセーブ/ロード関数
******************************************************************************/

/*------------------------------------------------------
	ステートセーブ
------------------------------------------------------*/


int memory_size = 0;
int m68000_size = 0;
int z80_size = 0;
int input_size = 0;
int timer_size = 0;
int driver_size = 0;
int video_size = 0;
int qsound_size = 0;
int eeprom_size = 0;
int ym2151_size = 0;

int get_save_state_size(void){
	
	int ssize = 0;
	
	memory_size = 0;
	m68000_size = 0;
	z80_size = 0;
	input_size = 0;
	timer_size = 0;
	driver_size = 0;
	video_size = 0;
	qsound_size = 0;
	eeprom_size = 0;
	ym2151_size = 0;
	
	memory_size = get_memory_size();//state_save_memory(fp); 
	m68000_size = get_m68000_size();//state_save_m68000(fp);
	z80_size = get_z80_size();//state_save_z80(fp);
	input_size = get_input_size();//state_save_input(fp);
	timer_size = get_timer_size();//state_save_timer(fp);
	driver_size = get_driver_size(); //state_save_driver(fp);
	video_size = get_video_size();//state_save_video(fp);
	
	switch (machine_driver_type)
	{
	case MACHINE_qsound:
		qsound_size = get_qsound_size();//state_save_qsound(fp);
		eeprom_size = get_eeprom_size();//state_save_eeprom(fp);
		break;
	case MACHINE_pang3:
		eeprom_size = get_eeprom_size();//state_save_eeprom(fp);
	default:
		ym2151_size = get_ym2151_size();//state_save_ym2151(fp);
		break;
	}
	
	ssize = memory_size +
		m68000_size +
		z80_size +
		input_size +
		timer_size +
		driver_size +
		video_size +
		qsound_size +
		eeprom_size +
		ym2151_size;
	
	return ssize;
	
}


int save_state_to_mem(unsigned char *stor){
	
	
	//ssize += get_memory_size();//state_save_memory(fp); 
	
	if( memory_size > 0){
		memory_size = save_memory_to_mem(stor);
		stor += memory_size;
	}
	
	//ssize += get_m68000_size();//state_save_m68000(fp);
	if( m68000_size > 0){
		m68000_size = save_m68000_to_mem(stor);
		stor += m68000_size;
	}
	
	
	//ssize += get_z80_size();//state_save_z80(fp);
	if( z80_size > 0){
		z80_size = save_z80_to_mem(stor);
		stor += z80_size;
	}
	
	//ssize += get_input_size();//state_save_input(fp);
	if( input_size > 0 ){
		input_size = save_input_to_mem(stor);
		stor += input_size;
	}
	
	
	//ssize += get_timer_size();//state_save_timer(fp);
	if( timer_size > 0){
		timer_size = save_timer_to_mem(stor);
		stor += timer_size;
	}
	
	//ssize += get_driver_size(); //state_save_driver(fp);
	if( driver_size > 0){
		driver_size = save_driver_to_mem(stor);
		stor += driver_size;
	}
	
	//ssize += get_video_size();//state_save_video(fp);
	if( video_size > 0 ){
		video_size = save_video_to_mem(stor);
		stor += video_size;
	}

	switch (machine_driver_type)
	{
	case MACHINE_qsound:
		//ssize += get_qsound_size();//state_save_qsound(fp);
		if( qsound_size > 0 ){
			qsound_size = save_qsound_to_mem(stor);
			stor += qsound_size;
		}
		
		//ssize += get_eeprom_size();//state_save_eeprom(fp);
		if( eeprom_size > 0 ){
			eeprom_size = save_eeprom_to_mem(stor);
			stor += eeprom_size;
		}
		
		break;
	case MACHINE_pang3:
		//ssize += get_eeprom_size();//state_save_eeprom(fp);
		if( eeprom_size > 0 ){
			eeprom_size = save_eeprom_to_mem(stor);
			stor += eeprom_size;
		}
	default:
		//ssize += get_ym2151_size();//state_save_ym2151(fp);
		if( ym2151_size > 0 ){
			ym2151_size = save_ym2152_to_mem(stor);
			stor += ym2151_size;
		}
		break;
	}
	
	
	return 0;
}


int state_save(int slot)
{
	FILE *fp = NULL;
	char path[MAX_PATH];

	sprintf(path, "%sstate/%s.sv%d", launchDir, game_name, slot);
	remove(path);

	if ((fp = fopen(path, "wb")) != NULL)
	{
		pspTime nowtime;

		sceRtcGetCurrentClockLocalTime(&nowtime);

		// ヘッダ (0x20 bytes)
		state_draw_progress(2);
		state_save_byte(current_version, 8);
		state_save_byte(&nowtime, 16);

		// サムネイル (152 * 112 * 2 bytes)
		state_draw_progress(3);
		save_thumbnail(fp);

		// ステートデータ
		state_draw_progress(4);
		state_save_memory(fp);
		state_save_m68000(fp);
		state_save_z80(fp);
		state_save_input(fp);
		state_save_timer(fp);
		state_save_driver(fp);
		state_save_video(fp);
#if (EMU_SYSTEM == CPS1)
		switch (machine_driver_type)
		{
		case MACHINE_qsound:
			state_save_qsound(fp);
			state_save_eeprom(fp);
			break;

		case MACHINE_pang3:
			state_save_eeprom(fp);

		default:
			state_save_ym2151(fp);
			break;
		}
#elif (EMU_SYSTEM == CPS2)
		state_save_qsound(fp);
		state_save_eeprom(fp);
#elif (EMU_SYSTEM == MVS)
		state_save_ym2610(fp);
		state_save_pd4990a(fp);
#endif
		fclose(fp);

		state_draw_progress(5);
		return 1;
	}

	ui_popup("Error: Could not open file \"%s.sv%d\"", game_name, slot);

	return 0;
}


/*------------------------------------------------------
	ステートロード
------------------------------------------------------*/



int load_state_from_mem(unsigned char *stor){
	
	//ssize += get_memory_size();//state_load_memory(fp); 
	if( memory_size > 0){
		memory_size = load_memory_from_mem(stor);
		stor += memory_size;
	}
	
	//ssize += get_m68000_size();//state_load_m68000(fp);
	if( m68000_size > 0){
		m68000_size = load_m68000_from_mem(stor);
		stor += m68000_size;
	}
	
	
	//ssize += get_z80_size();//state_load_z80(fp);
	if( z80_size > 0){
		z80_size = load_z80_from_mem(stor);
		stor += z80_size;
	}
	
	//ssize += get_input_size();//state_load_input(fp);
	if( input_size > 0 ){
		input_size = load_input_from_mem(stor);
		stor += input_size;
	}
	
	
	//ssize += get_timer_size();//state_load_timer(fp);
	if( timer_size > 0){
		timer_size = load_timer_from_mem(stor);
		stor += timer_size;
	}
	
	//ssize += get_driver_size(); //state_load_driver(fp);
	if( driver_size > 0){
		driver_size = load_driver_from_mem(stor);
		stor += driver_size;
	}
	
	//ssize += get_video_size();//state_load_video(fp);
	if( video_size > 0 ){
		video_size = load_video_from_mem(stor);
		stor += video_size;
	}

	switch (machine_driver_type)
	{
	case MACHINE_qsound:
		//ssize += get_qsound_size();//state_load_qsound(fp);
		if( qsound_size > 0 ){
			qsound_size = load_qsound_from_mem(stor);
			stor += qsound_size;
		}
		
		//ssize += get_eeprom_size();//state_load_eeprom(fp);
		if( eeprom_size > 0 ){
			eeprom_size = load_eeprom_from_mem(stor);
			stor += eeprom_size;
		}
		
		break;
	case MACHINE_pang3:
		//ssize += get_eeprom_size();//state_load_eeprom(fp);
		if( eeprom_size > 0 ){
			eeprom_size = load_eeprom_from_mem(stor);
			stor += eeprom_size;
		}
	default:
		//ssize += get_ym2151_size();//state_load_ym2151(fp);
		if( ym2151_size > 0 ){
			ym2151_size = load_ym2152_from_mem(stor);
			stor += ym2151_size;
		}
		break;
	}
	
	
	return 0;
}




int state_load(int slot)
{
	FILE *fp;
	char path[MAX_PATH];

	sprintf(path, "%sstate/%s.sv%d", launchDir, game_name, slot);

#if (EMU_SYSTEM == MVS)
	state_reload_bios = 0;
#endif

	if ((fp = fopen(path, "rb")) != NULL)
	{
		// ヘッダ (スキップ)
		state_draw_progress(2);
		state_load_skip((8+16));

		// サムネイル (スキップ)
		state_draw_progress(3);
		state_load_skip((152*112*2));

		// ステートデータ
		state_draw_progress(4);
		state_load_memory(fp);
		state_load_m68000(fp);
		state_load_z80(fp);
		state_load_input(fp);
		state_load_timer(fp);
		state_load_driver(fp);
		state_load_video(fp);
#if (EMU_SYSTEM == CPS1)
		switch (machine_driver_type)
		{
		case MACHINE_qsound:
			state_load_qsound(fp);
			state_load_eeprom(fp);
			break;

		case MACHINE_pang3:
			//state_load_ym2151(fp); por falta de break se carga dos veces ym2151
			state_load_eeprom(fp);

		default:
			state_load_ym2151(fp);
			break;
		}
#elif (EMU_SYSTEM == CPS2)
		state_load_qsound(fp);
		state_load_eeprom(fp);
#elif (EMU_SYSTEM == MVS)
		state_load_ym2610(fp);
		state_load_pd4990a(fp);
#endif
		fclose(fp);

#if (EMU_SYSTEM == CPS2)
		blit_update_all_cache();
#elif (EMU_SYSTEM == MVS)
//		blit_update_all_cache();

		if (state_reload_bios)
		{
			state_reload_bios = 0;

			if (!reload_bios())
			{
				fatalerror("Error: Could not reload BIOS.");
				Loop = LOOP_BROWSER;
				return 0;
			}
		}
#endif

		state_draw_progress(5);
		return 1;
	}

	ui_popup("Error: Could not open file \"%s.sv%d\"", game_name, slot);

	return 0;
}


/*------------------------------------------------------
	サムネイル作成
------------------------------------------------------*/

void state_make_thumbnail(void)
{
#if (EMU_SYSTEM == CPS1 || EMU_SYSTEM == CPS2)
	RECT clip1 = { 64, 16, 64 + 384, 16 + 224 };

	if (machine_screen_type)
	{
		RECT clip2 = { 152, 0, 152 + 112, 152 };
		video_copy_rect_rotate(work_frame, tex_frame, &clip1, &clip2);
	}
	else
	{
		RECT clip2 = { 152, 0, 152 + 152, 112 };
		video_copy_rect(work_frame, tex_frame, &clip1, &clip2);
	}
#elif (EMU_SYSTEM == MVS)
	RECT clip1 = { 8, 16, 8 + 304, 16 + 224 };
	RECT clip2 = { 152, 0, 152 + 152, 112 };

	video_copy_rect(work_frame, tex_frame, &clip1, &clip2);
#endif
}


/*------------------------------------------------------
	サムネイル読み込み
------------------------------------------------------*/

int state_load_thumbnail(int slot)
{
	FILE *fp;
	char path[MAX_PATH];

	clear_thumbnail();

	sprintf(path, "%sstate/%s.sv%d", launchDir, game_name, slot);

	if ((fp = fopen(path, "rb")) != NULL)
	{
		pspTime t;

		memset(stver_str, 0, 16);

		fread(stver_str, 1, 8, fp);
		fread(&t, 1, 16, fp);
		load_thumbnail(fp);
		fclose(fp);

		state_version = stver_str[7] - '0';

		sprintf(date_str, "%04d/%02d/%02d", t.year, t.month, t.day);
		sprintf(time_str, "%02d:%02d:%02d", t.hour, t.minutes, t.seconds);

		return 1;
	}

	ui_popup("Error: Could not open file \"%s.sv%d\"", game_name, slot);

	return 0;
}


/*------------------------------------------------------
	サムネイル消去
------------------------------------------------------*/

void state_clear_thumbnail(void)
{
	strcpy(date_str, "----/--/--");
	strcpy(time_str, "--:--:--");
	strcpy(stver_str, "--------");

	state_version = 0;

	clear_thumbnail();
}


#endif /* SAVE_STATE */
