//////////////////////////////
// ProSonic Engine			//
// Written by Damian Grove	//
//////////////////////////////

// TO-DO LIST:
// 
// - Create working MMF-like scripting language
// - Create working background layer
// - Create configuration file
// - Allow sprite/animation and sound files to be loaded by external list
// - Allow block art to be animated
// - Get water working
// - Add object layout editor
// - *** Make the engine faster ***

// BUGS:
// 
// - Fix block editor
// - Fix collision editor

// NOTE:
//
// Look into optimizing draw.c by keeping track of X and Y screen drawing position

// TEST:
// 
// CPU		700 MHz Celeron
// RAM		128 MB
// OS		TinyXP rev 09
// DATE		07/02/2009
// FPS RATES (with vsync off)
//		256 x 192: 45
//		320 x 200: 38
//		320 x 224: 35
//		400 x 300: 23
//		512 x 384: 15
//		640 x 480: 10
//		800 x 600: 07

// GOAL: Get 932x700 running 60 fps on P4 machine
// Frame rate as of 08/05/2009 -- 37 fps

#include	<stdio.h>
#include	<stdlib.h>		// used for exit()
#include	<string.h>
#include	<math.h>
#include	<allegro.h>

#ifndef ALLEGRO_DOS
	#include	<nl.h>
#endif

//#include	<winalleg.h>
//#include	<windows.h>

#include	"zlib.h"
#include	"draw.h"
#include	"tools.h"
#include	"mouse.h"
#include	"strings.h"
#include	"error.h"
#include	"menus.h"
#include	"objects.h"
#include	"sprites.h"
#include	"m68k.h"
#include	"obj01.h"
#include	"script.h"
#include	"network.h"

#include	"ym2612.h"
#include	"psg.h"



void TicUpdate(void)	// runs every 1/60 seconds
{
	ticUpdated = 1;
	
	if(!Game_paused){
		ticCounterR++;
		ticCounter60R++;
		
		if(ticCounter60R >= 60){
			ticCounter60R = 0;
			fps = ticCounter60L;
			ticCounter60L = 0;
		}
	}
	
	#ifdef ENABLE_MOUSE
		if(waitCounter > 0)
			waitCounter--;
	#endif
}
END_OF_FUNCTION(TicUpdate);



#ifndef ALLEGRO_DOS
void close_button_handler(void){
	close_button_pressed = TRUE;
}
END_OF_FUNCTION(close_button_handler);
#endif



void quit(int e){
	#ifdef ALLEGRO_DOS
	set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
	system("CLS");
	#endif
	
	if(e != 0){
		switch(e){
			case ERROR_OBJECT_BUFFER:
				allegro_message("%s", STR_ERR_OBJECT_BUFFER);
				break;
			case ERROR_DIVIDE_ZERO:
				allegro_message("%s", STR_ERR_DIVIDE_ZERO);
				break;
			case ERROR_INIT_SCREEN:
				allegro_message("%s", STR_ERR_INIT_SCREEN);
				break;
			case ERROR_NO_DEMO_FILES:
				allegro_message("%s", STR_ERR_NO_DEMO_FILES);
				break;
			default:
				allegro_message("%s", STR_ERR_UNKNOWN);
		}
	}
	
	if(program_filename)	free(program_filename);
	
	#ifdef AUDIO_REC_ENABLED
		i = ftell(pcm_out) - 8;
		fseek(pcm_out, 0x4, SEEK_SET);
		fwrite(&i, 4, 1, pcm_out);
		i -= 0x24;
		fseek(pcm_out, 0x28, SEEK_SET);
		fwrite(&i, 4, 1, pcm_out);
		fclose(pcm_out);
	#endif
	remove_timer();	// needed to prevent crashes
	stop_audio_stream(stream);
	YM2612_End();
	if(fm_data)		free(fm_data);
	if(pcm_data)	free(pcm_data);
	
	#ifdef ENABLE_SRAM_LOG_ANALYSIS
	if(SRAMFile != NULL)
		fclose(SRAMFile);
	#endif
	if(DemoFile != NULL)
		fclose(DemoFile);
	
	for(i=0; i<4; i++){
		if(LayerB[i]) destroy_bitmap(LayerB[i]);
		if(LayerL[i]) destroy_bitmap(LayerL[i]);
		if(LayerH[i]) destroy_bitmap(LayerH[i]);
		if(LayerSH[i]) destroy_bitmap(LayerSH[i]);
	}
	if(LayerM) destroy_bitmap(LayerM);
	
	DeleteZone(&z);
	if(COMPILED_MAP)		free(COMPILED_MAP);
	if(COMPILED_BACKMAP)	free(COMPILED_BACKMAP);
	if(titlecard_data)		free(titlecard_data);
	if(demodata)			free(demodata);
	if(script)				free(script);
	if(obj_script)			free(obj_script);
	if(Objects)				free(Objects);
	ClearSprites();
	
	if(message)				free(message);
	if(message_time)		free(message_time);
	#ifndef ALLEGRO_DOS
	if(prosonicnet)			nlGroupDestroy(prosonicnet);
	if(sock_server != NL_INVALID)	nlClose(sock_server);
	if(sock_client != NL_INVALID)	nlClose(sock_client);
	if(socket[0] != NL_INVALID)		nlClose(socket[0]);
	if(socket[1] != NL_INVALID)		nlClose(socket[1]);
	if(socket[2] != NL_INVALID)		nlClose(socket[2]);
	if(socket[3] != NL_INVALID)		nlClose(socket[3]);
	nlShutdown();
	#endif
	
	if(e == 0){
		set_config_int(config_section[num_of_frames-1], "digi_card", digi_card);
		set_config_int(config_section[num_of_frames-1], "audio_volume", audio_volume);
		set_config_int(config_section[num_of_frames-1], "smart_mix", smart_mix);
		set_config_int(config_section[num_of_frames-1], "vsync_enable", vsync_enable);
		set_config_int(config_section[num_of_frames-1], "frame_skip", frame_skip);
		set_config_int(config_section[num_of_frames-1], "screen_resolution_x", screen_resolution_x);
		set_config_int(config_section[num_of_frames-1], "screen_resolution_y", screen_resolution_y);
		set_config_int(config_section[num_of_frames-1], "screen_padding_x", screen_padding_x);
		set_config_int(config_section[num_of_frames-1], "screen_padding_y", screen_padding_y);
		set_config_int(config_section[num_of_frames-1], "screen_double_x", screen_double_x);
		set_config_int(config_section[num_of_frames-1], "screen_double_y", screen_double_y);
		set_config_int(config_section[num_of_frames-1], "full_screen", full_screen);
		for(i=0; i < 4; i++){
			set_config_int(config_section[i], "bind_up", key_bind[i].bind_up);
			set_config_int(config_section[i], "bind_down", key_bind[i].bind_down);
			set_config_int(config_section[i], "bind_left", key_bind[i].bind_left);
			set_config_int(config_section[i], "bind_right", key_bind[i].bind_right);
			set_config_int(config_section[i], "bind_b", key_bind[i].bind_b);
			set_config_int(config_section[i], "bind_c", key_bind[i].bind_c);
			set_config_int(config_section[i], "bind_a", key_bind[i].bind_a);
			set_config_int(config_section[i], "bind_start", key_bind[i].bind_start);
		}
	}
	
	allegro_exit();
	
	exit(1);
}



////////// START ---- * NETWORK STUFF * //////////
// Move all network code into a seperate file in the future //
#ifndef ALLEGRO_DOS
void GetNetworkData(){
	unsigned char type;
	unsigned char sender;
	unsigned char ready;
	
	if(net_type == 2){			// client
		//allegro_message("GetNetworkData() -- client");
		type = 0;
		while(type != 0xFF){
			i = nlRead(sock_client, &type, 1);
			if(i > 0){
				switch(type){
					case 0x01:
						n = 0;
						while(n != 1){
							n = nlRead(sock_client, &sender, 1);
							if(key[KEY_ESC])
								quit(0);
						}
						n = 0;
						while(n != 63){
							n = nlRead(sock_client, talk_incoming, 63);
							if(key[KEY_ESC])
								quit(0);
						}
						allegro_message("CLIENT INCOMING: %s", talk_incoming);
						break;
					case 0xFF:
						nlRead(sock_client, &sender, 1);
						break;
				}
				//allegro_message("GetNetworkData() -- client\ntype = %i\nsender = %i", type, sender);
			}
			if(key[KEY_7])
				allegro_message("GetNetworkData() -- client");
			if(key[KEY_ESC])
				quit(0);
		}
	}
	else if(net_type == 1){		// server
		//allegro_message("GetNetworkData() -- server");
		i = 1;
		type = 0;
		while(i > 0){
			i = nlRead(socket[0], &type, 1);
			if(i > 0){
				switch(type){
					case 1:
						n = 0;
						while(n != 1){
							n = nlRead(socket[0], &sender, 1);
							if(key[KEY_ESC])
								quit(0);
						}
						n = 0;
						while(n != 63){
							n = nlRead(socket[0], talk_incoming, 63);
							if(key[KEY_ESC])
								quit(0);
						}
						allegro_message("SERVER INCOMING: %s", talk_incoming);
						break;
				}
				//allegro_message("GetNetworkData() -- server\ntype = %i\nsender = %i", type, sender);
			}
			if(key[KEY_7])
				allegro_message("GetNetworkData() -- server");
			if(key[KEY_ESC])
				quit(0);
		}
	}
}

void ManageNetwork(){
	int sock;
	unsigned char type;
	unsigned char sender;
	unsigned char ready = 1;
	
	//allegro_message("ManageNetwork()");
	while(ready != 0x3){
		for(sock=0; sock < 2; sock++){
			i = 1;
			type = 0;
			while(i > 0){
				i = nlRead(socket[sock], &type, 1);
				if(i > 0){
					switch(type){
						case 0x01:
							n = 0;
							while(n != 1){
								n = nlRead(socket[sock], &sender, 1);
								if(key[KEY_ESC])
									quit(0);
							}
							n = 0;
							while(n != 63){
								n = nlRead(socket[sock], talk_incoming, 63);
								if(key[KEY_ESC])
									quit(0);
							}
							allegro_message("type = 0x%02X\nsender = 0x%02X\ntalk_incoming = %s", type, sender, talk_incoming);
							if(sender == 0){
								// Relay message from player 1 to player 2
								n = 0;
								while(n != 1){
									n = nlWrite(socket[1], &type, 1);
									if(key[KEY_ESC])
										quit(0);
								}
								n = 0;
								while(n != 1){
									n = nlWrite(socket[1], &sender, 1);
									if(key[KEY_ESC])
										quit(0);
								}
								n = 0;
								while(n != 63){
									n = nlWrite(socket[1], talk_incoming, 63);
									if(key[KEY_ESC])
										quit(0);
								}
							}
							else if(sender == 1){
								// Relay message from player 2 to player 1
								n = 0;
								while(n != 1){
									n = nlWrite(socket[0], &type, 1);
									if(key[KEY_ESC])
										quit(0);
								}
								n = 0;
								while(n != 1){
									n = nlWrite(socket[0], &sender, 1);
									if(key[KEY_ESC])
										quit(0);
								}
								n = 0;
								while(n != 63){
									n = nlWrite(socket[0], talk_incoming, 63);
									if(key[KEY_ESC])
										quit(0);
								}
							}
							break;
						case 0xFF:
							n = 0;
							while(n != 1){
								n = nlRead(socket[sock], &sender, 1);
								if(key[KEY_ESC])
									quit(0);
							}
							ready |= (1 << sender);
							break;
					}
					//allegro_message("ManageNetwork()\ntype = %i\nsender = %i", type, sender);
				}
				if(key[KEY_7])
					allegro_message("ManageNetwork()");
				if(key[KEY_ESC])
					quit(0);
			}
		}
	}
	i = 0xFF;
	n = 0;
	while(n != 1){
		n = nlWrite(prosonicnet, &i, 1);
		if(key[KEY_ESC])
			quit(0);
	}
	n = 0;
	while(n != 1){
		n = nlWrite(prosonicnet, &net_id[0], 1);
		if(key[KEY_ESC])
			quit(0);
	}
}

int check_commands(char *string){
	if(string[0] != '/')
		return 0;
	
	for(i=1; i<64; i++){
		if(string[i] >= 'a' && string[i] <= 'z')
			string[i] -= 0x20;	// convert to uppercase
	}
	
	if(strcmp("/QUIT\n", string) == 0)
		return 1;
	
	return 0;
}

int open_socket(NLsocket *s, short local_port, short net_port, const char *net_ip, ...){
	*s = nlOpen(local_port, NL_RELIABLE);
	
	if(*s == NL_INVALID){
		allegro_message("ERROR: Invalid socket!\n");
		nlClose(*s);
		return 1;
	}
	
	nlGetAddrFromName(net_ip, &addr_client);
	nlSetAddrPort(&addr_client, net_port);
	
	if(nlConnect(*s, &addr_client) == NL_FALSE){
		i = nlGetError();
		switch(i){
			case NL_INVALID_SOCKET:
				allegro_message("NL_INVALID_SOCKET\n");
				nlClose(*s);
				return;
			case NL_NULL_POINTER:
				allegro_message("NL_NULL_POINTER\n");
				nlClose(*s);
				return;
			case NL_SYSTEM_ERROR:
				allegro_message("NL_SYSTEM_ERROR: %s\n", nlGetSystemErrorStr(nlGetSystemError()));
				nlClose(*s);
				return;
			case NL_CON_REFUSED:
				allegro_message("NL_CON_REFUSED\n");
				nlClose(*s);
				return;
			case NL_WRONG_TYPE:
				allegro_message("NL_WRONG_TYPE\n");
				nlClose(*s);
				return;
			default:
				allegro_message("ERROR i = 0x%X\n", i);
				//system("PAUSE");
		}
		return 1;
	}
	
	return 0;
}

void serverside_setup(){
	sock_server = nlOpen(port, NL_RELIABLE);
	
	if(sock_server == NL_INVALID){
		allegro_message("ERROR: Invalid socket!\n");
		nlClose(sock_server);
		return;
	}
	
	if(nlListen(sock_server) == NL_FALSE){
		allegro_message("ERROR: Unable to listen!\n");
		nlClose(sock_server);
		return;
	}
	
	prosonicnet = nlGroupCreate();
	if(prosonicnet == NL_INVALID){
		allegro_message("ERROR: Unable to create group!\n");
		nlClose(sock_server);
		return;
	}
	
	//printf("Listening...\n");
	i=0;
	while(i==0){
		socket[1] = nlAcceptConnection(sock_server);
		if(socket[1] == NL_INVALID){
			i = nlGetError();
			switch(i){
				case NL_NOT_LISTEN:
					allegro_message("NL_NOT_LISTEN\n"); nlClose(sock_server); return;
				case NL_NO_PENDING:
				/*	allegro_message("NL_NO_PENDING\n");*/ break;
				case NL_WRONG_TYPE:
					allegro_message("NL_WRONG_TYPE\n"); nlClose(sock_server); return;
				default:
					allegro_message("ERROR i = 0x%X\n", i); //system("PAUSE");
			}
			i = 0;	// continue loop
		}
		else{
			// Verify network connection and ProSonic version
			strcpy(talk_outgoing, "PROSONIC\n");
			strcat(talk_outgoing, __DATE__);
			strcat(talk_outgoing, "\n");
			strcat(talk_outgoing, __TIME__);
			nlWrite(socket[1], talk_outgoing, 30);
			i = 0;
			while(i == 0){
				i = nlRead(socket[1], talk_incoming, 30);
				if(i == 30){
					if(strcmp(talk_incoming, talk_outgoing) != 0){
						allegro_message("ERROR: Client has incompatible version");
						nlClose(socket[1]); return;
					}
				}
				else if(i != 0){
					allegro_message("ERROR: Unexpected data recieved");
					nlClose(socket[1]); return;
				}
			}
			for(i=0; i<64; i++)
				talk_incoming[i] = 0;
			for(i=0; i<64; i++)
				talk_outgoing[i] = 0;
			
			if(open_socket(&socket[0], 0, 727, "127.0.0.1") == 0){
				//nlGroupAddSocket(prosonicnet, socket[0]);
				nlGroupAddSocket(prosonicnet, socket[1]);
				net_id[0] = 0;
				allegro_message("Connection successful!");
			}
			else{
				nlClose(socket[1]); return;
			}
		}
	}
}

void clientside_setup(){
	sock_client = nlOpen(0, NL_RELIABLE);
	
	if(sock_client == NL_INVALID){
		allegro_message("ERROR: Invalid socket!\n");
		nlClose(sock_client);
		return;
	}
	
	prosonicnet = nlGroupCreate();
	if(prosonicnet == NL_INVALID){
		allegro_message("ERROR: Unable to create group!\n");
		nlClose(sock_client);
		return;
	}
	
	nlGetAddrFromName(ip_input, &addr_client);
	nlSetAddrPort(&addr_client, port);
	
	//printf("Connecting...\n");
	i=0;
	while(i==0){
		if(nlConnect(sock_client, &addr_client) == NL_FALSE){
			i = nlGetError();
			switch(i){
				case NL_INVALID_SOCKET:
					allegro_message("NL_INVALID_SOCKET\n"); nlClose(sock_client); return;
				case NL_NULL_POINTER:
					allegro_message("NL_NULL_POINTER\n"); nlClose(sock_client); return;
				case NL_SYSTEM_ERROR:
					allegro_message("NL_SYSTEM_ERROR: %s\n", nlGetSystemErrorStr(nlGetSystemError())); nlClose(sock_client); return;
				case NL_CON_REFUSED:
					allegro_message("NL_CON_REFUSED\n"); nlClose(sock_client); return;
				case NL_WRONG_TYPE:
					allegro_message("NL_WRONG_TYPE\n"); nlClose(sock_client); return;
				default:
					allegro_message("ERROR i = 0x%X\n", i); //system("PAUSE");
			}
			i = 0;	// continue loop
		}
		else{
			// Verify network connection and ProSonic version
			strcpy(talk_outgoing, "PROSONIC\n");
			strcat(talk_outgoing, __DATE__);
			strcat(talk_outgoing, "\n");
			strcat(talk_outgoing, __TIME__);
			while(i==0){
				i = nlRead(sock_client, talk_incoming, 30);
				if(i == 30){
					if(strcmp(talk_incoming, talk_outgoing) != 0){
						nlWrite(sock_client, talk_outgoing, 30);
						allegro_message("ERROR: Incompatible version");
						nlClose(sock_client); return;
					}
				}
				else if(i != 0){
					nlWrite(sock_client, talk_outgoing, 31);
					allegro_message("ERROR: Unexpected data recieved");
					nlClose(sock_client); return;
				}
			}
			nlWrite(sock_client, talk_outgoing, 30);
			for(i=0; i<64; i++)
				talk_incoming[i] = 0;
			for(i=0; i<64; i++)
				talk_outgoing[i] = 0;
			nlGroupAddSocket(prosonicnet, sock_client);
			net_id[0] = 1;
			allegro_message("Connection successful!");
		}
	}
}
#endif
////////// END ---- * NETWORK STUFF * //////////



////////// START ---- * SOUND STUFF * //////////
// Move all sound code into a seperate file in the future //
void ResetSound(){
	fm_data_pos[0] = 0;
	fm_data_pos[1] = 0;
	fm_data_pos[2] = 0;
	fm_data_pos[3] = 0;
	pcm_data_pos[0][0] = 0;
	pcm_data_pos[1][0] = 0;
	pcm_data_pos[0][1] = 0;
	pcm_data_pos[1][1] = 0;
	pcm_data_pos[0][2] = 0;
	pcm_data_pos[1][2] = 0;
	pcm_data_pos[0][3] = 0;
	pcm_data_pos[1][3] = 0;
}

char PauseSound(){
	short *temp;
	
	// This code will clear the buffer if the game is paused
	if(Game_paused && advance_frame == 0){
		temp = (short*) get_audio_stream_buffer(stream);
		if(temp){
			for( i = 0 ; i < 1470 ; i++ )
				temp[i] = 0x8000;
			free_audio_stream_buffer(stream);
		}
		return 1;
	}
	
	return 0;
}



inline void ProcessSound(){
	short *temp;
	short byte1=0;
	short byte2=0;
	short byte3=0;
	short byte4=0;
	double v;
	
	int audio_buffer_1[2][735];
	int audio_buffer_2[2][735];
	int audio_buffer_3[2][735];
	int audio_buffer_4[2][735];
	
	//int audio_buffer_1_used = (pcm_data_poll[0][0] || pcm_data_poll[1][0]);
	//int audio_buffer_2_used = (pcm_data_poll[0][1] || pcm_data_poll[1][1]);
	//int audio_buffer_3_used = (pcm_data_poll[0][2] || pcm_data_poll[1][2]);
	//int audio_buffer_4_used = (pcm_data_poll[0][3] || pcm_data_poll[1][3]);
	
	pcm_data_poll[0][0] = 0;
	pcm_data_poll[1][0] = 0;
	
	
	
	if(PauseSound())
		return;
	
	
	
	// Voice 1 (BGM)
	RestoreYM2612(0);
	RestorePSG(0);
	if(!fm_data_pos[0])
		StreamPCM(0, &pcm_data_pos[0][0], &pcm_data_pos[1][0], &pcm_data_poll[0][0], &pcm_data_poll[1][0], audio_buffer_1[0], audio_buffer_1[1]);
	else{
		StreamFM(0, &fm_data_pos[0], audio_buffer_1[0], audio_buffer_1[1]);
		BackupYM2612(0);
		BackupPSG(0);
	}
	
	// Voice 2
	RestoreYM2612(1);
	RestorePSG(1);
	if(!fm_data_pos[1])
		StreamPCM(1, &pcm_data_pos[0][1], &pcm_data_pos[1][1], &pcm_data_poll[0][1], &pcm_data_poll[1][1], audio_buffer_2[0], audio_buffer_2[1]);
	else{
		StreamFM(1, &fm_data_pos[1], audio_buffer_2[0], audio_buffer_2[1]);
		BackupYM2612(1);
		BackupPSG(1);
	}
	
	// Voice 3
	RestoreYM2612(2);
	RestorePSG(2);
	if(!fm_data_pos[2])
		StreamPCM(2, &pcm_data_pos[0][2], &pcm_data_pos[1][2], &pcm_data_poll[0][2], &pcm_data_poll[1][2], audio_buffer_3[0], audio_buffer_3[1]);
	else{
		StreamFM(2, &fm_data_pos[2], audio_buffer_3[0], audio_buffer_3[1]);
		BackupYM2612(2);
		BackupPSG(2);
	}
	
	// Voice 4
	RestoreYM2612(3);
	RestorePSG(3);
	if(!fm_data_pos[3])
		StreamPCM(3, &pcm_data_pos[0][3], &pcm_data_pos[1][3], &pcm_data_poll[0][3], &pcm_data_poll[1][3], audio_buffer_4[0], audio_buffer_4[1]);
	else{
		StreamFM(3, &fm_data_pos[3], audio_buffer_4[0], audio_buffer_4[1]);
		BackupYM2612(3);
		BackupPSG(3);
	}
	
	temp = (short*) get_audio_stream_buffer(stream);
	if(temp)  {
		for( i = 0 ; i < 1470 ; i++ ){
			byte1 = (short)(audio_buffer_1[i&1][i>>1]);
			byte2 = (short)(audio_buffer_2[i&1][i>>1]);
			byte3 = (short)(audio_buffer_3[i&1][i>>1]);
			byte4 = (short)(audio_buffer_4[i&1][i>>1]);
			
			byte1 *= ((float)(audio_volume+1) / 32);
			byte2 *= ((float)(audio_volume+1) / 32);
			byte3 *= ((float)(audio_volume+1) / 32);
			byte4 *= ((float)(audio_volume+1) / 32);
			
			if(smart_mix){
				n = byte1 + byte2 + byte3 + byte4;
				//if(abs(n) > audio_peak)
				//	audio_peak = n;
				
				// Compression level 0
				if(n < 32768 && n > -32769){
					mix_volume = 0;
					audio_compression = 0x10;
				}
				// Compression level 1
				else if(n < 65536 && n > -65537){
					mix_volume = -6.020600;
					audio_compression = 0x25;
				}
				// Compression level 2
				else{
					mix_volume = -9.542425;
					audio_compression = 0x3E;
				}
				
				// COMPRESSOR
				if(audio_compression != 0x10){
					byte1 ^= 0x8000;
					v = byte1;
					if(v < 0)
						v = pow(10, (log10(-v / 32767) * (float)((float)audio_compression / 16))) * -32767;
					else if(v > 0)
						v = pow(10, (log10(v / 32767) * (float)((float)audio_compression / 16))) * 32767;
					byte1 = v;
					byte1 ^= 0x8000;
					
					byte2 ^= 0x8000;
					v = byte2;
					if(v < 0)
						v = pow(10, (log10(-v / 32767) * (float)((float)audio_compression / 16))) * -32767;
					else if(v > 0)
						v = pow(10, (log10(v / 32767) * (float)((float)audio_compression / 16))) * 32767;
					byte2 = v;
					byte2 ^= 0x8000;
					
					byte3 ^= 0x8000;
					v = byte3;
					if(v < 0)
						v = pow(10, (log10(-v / 32767) * (float)((float)audio_compression / 16))) * -32767;
					else if(v > 0)
						v = pow(10, (log10(v / 32767) * (float)((float)audio_compression / 16))) * 32767;
					byte3 = v;
					byte3 ^= 0x8000;
					
					byte4 ^= 0x8000;
					v = byte4;
					if(v < 0)
						v = pow(10, (log10(-v / 32767) * (float)((float)audio_compression / 16))) * -32767;
					else if(v > 0)
						v = pow(10, (log10(v / 32767) * (float)((float)audio_compression / 16))) * 32767;
					byte4 = v;
					byte4 ^= 0x8000;
				}
				
				// MIXER
				if(mix_volume != 0){
					v = byte1;
					if(v < 0)
						v = pow(10, log10(-v / 32767) + (mix_volume / 20)) * -32767;
					else if(v > 0)
						v = pow(10, log10(v / 32767) + (mix_volume / 20)) * 32767;
					byte1 = v;
					
					v = byte2;
					if(v < 0)
						v = pow(10, log10(-v / 32767) + (mix_volume / 20)) * -32767;
					else if(v > 0)
						v = pow(10, log10(v / 32767) + (mix_volume / 20)) * 32767;
					byte2 = v;
					
					v = byte3;
					if(v < 0)
						v = pow(10, log10(-v / 32767) + (mix_volume / 20)) * -32767;
					else if(v > 0)
						v = pow(10, log10(v / 32767) + (mix_volume / 20)) * 32767;
					byte3 = v;
					
					v = byte4;
					if(v < 0)
						v = pow(10, log10(-v / 32767) + (mix_volume / 20)) * -32767;
					else if(v > 0)
						v = pow(10, log10(v / 32767) + (mix_volume / 20)) * 32767;
					byte4 = v;
				}
			}
			
			// Write the mix to the audio stream
			temp[i] = (int)(byte1 + byte2 + byte3 + byte4)^0x8000;
		}
		
		#ifdef AUDIO_REC_ENABLED
			RecordPCM(temp);
		#endif
		
		free_audio_stream_buffer(stream);
	}
	
	if(fm_fade_out[0])	fm_fader[0]++;
	if(fm_fade_out[1])	fm_fader[1]++;
	if(fm_fade_out[2])	fm_fader[2]++;
	if(fm_fade_out[3])	fm_fader[3]++;
	
	if(fm_fader[0] == 160){ PlayFM(0, 0, 0); }
	if(fm_fader[1] == 160){ PlayFM(0, 1, 0); }
	if(fm_fader[2] == 160){ PlayFM(0, 2, 0); }
	if(fm_fader[3] == 160){ PlayFM(0, 3, 0); }
}



inline void FadeOutFM(char buffer){
	fm_fade_out[buffer] = 1;
}



void LoadFM(unsigned char file, const char *string, char type, char dac, char repeat){
	char vgm_header[4];
	gzFile *gzf;
	int gzc_buffer_size;
	int gzu_buffer_size;
	char *gzc_buffer;
	char *gzu_buffer;
	
	#ifdef ALLEGRO_DOS
	allegro_message("Loading FM file \"%s\"...\n", string);
	#endif
	
	if(string[0] == '\0'){
		fm_data_type[file] = -1;
		fm_data_dac[file] = 0;
		fm_data_repeat[file] = 0;
		fm_data_address[file] = 0;
		fm_size += 4;
		if(fm_data == 0)
			fm_data = (char *)malloc(fm_size);
		else
			fm_data = (char *)realloc(fm_data, fm_size);
	}
	else{
		ym_log_file = fopen(string, "rb");
		if(ym_log_file == NULL){
			allegro_message("Could not open file \"%s\"...\n", string);
			return;
		}
		fm_data_type[file] = type;
		fm_data_dac[file] = dac;
		fm_data_repeat[file] = repeat;
		fm_data_address[file] = fm_size;
		
		if(type == FM_TYPE_VGM){
			// Read the header to see if it's compressed or not
			vgm_header[0] = getc(ym_log_file);
			vgm_header[1] = getc(ym_log_file);
			vgm_header[2] = getc(ym_log_file);
			vgm_header[3] = '\0';
		}
		
		if(type == FM_TYPE_VGM && strcmp(vgm_header, "Vgm") != 0){
			// The VGM file is compressed, so we must decompress it
			fseek(ym_log_file, 0, SEEK_END);
			gzc_buffer_size = ftell(ym_log_file);	// record size of GZ file
			rewind(ym_log_file);
			gzc_buffer = (char *)malloc(gzc_buffer_size);
			fread(gzc_buffer, 1, gzc_buffer_size, ym_log_file);
			fclose(ym_log_file);
			
			n = gzc_buffer_size * 10;	// guess at size of VGM file
			gzu_buffer_size = 0;
			gzu_buffer = (char *)malloc(n);
			gzf = gzopen(string, "rb");
			i = gzread(gzf, gzu_buffer, n);
			gzu_buffer_size += i;
			while(i == n){
				// As long as there's more to read, we keep resizing and reading
				gzu_buffer = (char *)realloc(gzu_buffer, gzu_buffer_size + i);
				i = gzread(gzf, &gzu_buffer[gzu_buffer_size], n);
				gzu_buffer_size += i;
			}
			gzclose(gzf);
			
			free(gzc_buffer);
			
			fm_size += (gzu_buffer_size+4);
			if(fm_data == 0)
				fm_data = (char *)malloc(fm_size);
			else
				fm_data = (char *)realloc(fm_data, fm_size);
			for(i=0; i < gzu_buffer_size; i++)
				fm_data[fm_offset+i] = gzu_buffer[i];
			
			free(gzu_buffer);
		}
		else{
			// The file is either a GYM, or an uncompressed VGM
			fseek(ym_log_file, 0, SEEK_END);
			fm_size += (ftell(ym_log_file)+4);
			if(fm_data == 0)
				fm_data = (char *)malloc(fm_size);
			else
				fm_data = (char *)realloc(fm_data, fm_size);
			rewind(ym_log_file);
			fread(&fm_data[fm_offset], 1, fm_size-4-fm_offset, ym_log_file);
			fclose(ym_log_file);
		}
	}
	
	fm_offset = fm_size-4;
	fm_data[fm_offset] = 0xFF;
	fm_data[fm_offset+1] = 'E';
	fm_data[fm_offset+2] = 'O';
	fm_data[fm_offset+3] = 'F';
	fm_offset += 4;
}

void LoadPCM(unsigned char file, const char *string, char stereo, char repeat){
	#ifdef ALLEGRO_DOS
	allegro_message("Loading PCM file \"%s\"...\n", string);
	#endif
	
	if(string[0] == '\0'){
		pcm_data_address[file] = 0;
		pcm_data_format_stereo[file] = 0;
		pcm_data_repeat[file] = 0;
		pcm_size += 4;
		if(pcm_data == 0)
			pcm_data = (char *)malloc(pcm_size);
		else
			pcm_data = (char *)realloc(pcm_data, pcm_size);
	}
	else{
		ym_log_file = fopen(string, "rb");
		if(ym_log_file == NULL){
			allegro_message("Could not open file \"%s\"...\n", string);
			return;
		}
		pcm_data_address[file] = pcm_size;
		pcm_data_format_stereo[file] = stereo;
		pcm_data_repeat[file] = repeat;
		fseek(ym_log_file, 0, SEEK_END);
		pcm_size += (ftell(ym_log_file)+4);
		if(pcm_data == 0)
			pcm_data = (char *)malloc(pcm_size);
		else
			pcm_data = (char *)realloc(pcm_data, pcm_size);
		rewind(ym_log_file);
		fread(&pcm_data[pcm_offset], 1, pcm_size-4-pcm_offset, ym_log_file);
		fclose(ym_log_file);
	}
	
	pcm_offset = pcm_size-4;
	pcm_data[pcm_offset] = 0xFF;
	pcm_data[pcm_offset+1] = 'E';
	pcm_data[pcm_offset+2] = 'O';
	pcm_data[pcm_offset+3] = 'F';
	pcm_offset += 4;
}



void PlayFM(short sound, char buffer, char mix){
	fm_fader[buffer] = 0;
	fm_fade_out[buffer] = 0;
	fm_reset[buffer] = 1;
	fm_track_number[buffer] = sound;
	
	fm_data_pos[buffer] = fm_data_address[sound];
	fm_data_pos_start[buffer] = fm_data_address[sound];
	fm_data_ch_repeat[buffer] = fm_data_repeat[sound];
	fm_data_ch_type[buffer] = fm_data_type[sound];
	if(fm_data_ch_type[buffer]){	// VGM file
	 	fm_data_pos_loop[buffer] = fm_data[fm_data_pos[buffer] + 0x1C]
		 + (fm_data[fm_data_pos[buffer] + 0x1D] << 8)
		 + (fm_data[fm_data_pos[buffer] + 0x1E] << 16)
		 + (fm_data[fm_data_pos[buffer] + 0x1F] << 24);
	 	fm_data_pos[buffer] += 0x40;
	}
}



void PlayPCM(short sound, char buffer, char mix){
	if(buffer < 0){
		switch(mix){
			case 1:
				// LEFT
				if(pcm_data_poll[0][1]){
					if(pcm_data_poll[0][2]){
						if(pcm_data_poll[0][3]){
							pcm_data_pos[0][1] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[0][1] = sound;
							pcm_data_ch_repeat[0][1] = pcm_data_repeat[sound];
							pcm_data_pos_start[0][1] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[0][1] = pcm_data_format_stereo[sound];
						}
						else{
							pcm_data_pos[0][3] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[0][3] = sound;
							pcm_data_ch_repeat[0][3] = pcm_data_repeat[sound];
							pcm_data_pos_start[0][3] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[0][3] = pcm_data_format_stereo[sound];
						}
					}
					else{
						pcm_data_pos[0][2] = pcm_data_address[sound] + 0x2C;
						pcm_data_poll[0][2] = sound;
						pcm_data_ch_repeat[0][2] = pcm_data_repeat[sound];
						pcm_data_pos_start[0][2] = pcm_data_address[sound];
						pcm_data_ch_format_stereo[0][2] = pcm_data_format_stereo[sound];
					}
				}
				else{
					pcm_data_pos[0][1] = pcm_data_address[sound] + 0x2C;
					pcm_data_poll[0][1] = sound;
					pcm_data_ch_repeat[0][1] = pcm_data_repeat[sound];
					pcm_data_pos_start[0][1] = pcm_data_address[sound];
					pcm_data_ch_format_stereo[0][1] = pcm_data_format_stereo[sound];
				}
				break;
				
			case 2:
				// RIGHT
				if(pcm_data_poll[1][1]){
					if(pcm_data_poll[1][2]){
						if(pcm_data_poll[1][2]){
							pcm_data_pos[1][1] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[1][1] = sound;
							pcm_data_ch_repeat[1][1] = pcm_data_repeat[sound];
							pcm_data_pos_start[1][1] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[1][1] = pcm_data_format_stereo[sound];
						}
						else{
							pcm_data_pos[1][3] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[1][3] = sound;
							pcm_data_ch_repeat[1][3] = pcm_data_repeat[sound];
							pcm_data_pos_start[1][3] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[1][3] = pcm_data_format_stereo[sound];
						}
					}
					else{
						pcm_data_pos[1][2] = pcm_data_address[sound] + 0x2C;
						pcm_data_poll[1][2] = sound;
						pcm_data_ch_repeat[1][2] = pcm_data_repeat[sound];
						pcm_data_pos_start[1][2] = pcm_data_address[sound];
						pcm_data_ch_format_stereo[1][2] = pcm_data_format_stereo[sound];
					}
				}
				else{
					pcm_data_pos[1][1] = pcm_data_address[sound] + 0x2C;
					pcm_data_poll[1][1] = sound;
					pcm_data_ch_repeat[1][1] = pcm_data_repeat[sound];
					pcm_data_pos_start[1][1] = pcm_data_address[sound];
					pcm_data_ch_format_stereo[1][1] = pcm_data_format_stereo[sound];
				}
				break;
				
			default:
				// STEREO
				if(pcm_data_poll[0][1] | pcm_data_poll[1][1]){
					if(pcm_data_poll[0][2] | pcm_data_poll[1][2]){
						if(pcm_data_poll[0][3] | pcm_data_poll[1][3]){
							pcm_data_pos[0][1] = pcm_data_address[sound] + 0x2C;
							pcm_data_pos[1][1] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[0][1] = sound;
							pcm_data_poll[1][1] = sound;
							pcm_data_ch_repeat[0][1] = pcm_data_repeat[sound];
							pcm_data_ch_repeat[1][1] = pcm_data_repeat[sound];
							pcm_data_pos_start[0][1] = pcm_data_address[sound];
							pcm_data_pos_start[1][1] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[0][1] = pcm_data_format_stereo[sound];
							pcm_data_ch_format_stereo[1][1] = pcm_data_format_stereo[sound];
						}
						else{
							pcm_data_pos[0][3] = pcm_data_address[sound] + 0x2C;
							pcm_data_pos[1][3] = pcm_data_address[sound] + 0x2C;
							pcm_data_poll[0][3] = sound;
							pcm_data_poll[1][3] = sound;
							pcm_data_ch_repeat[0][3] = pcm_data_repeat[sound];
							pcm_data_ch_repeat[1][3] = pcm_data_repeat[sound];
							pcm_data_pos_start[0][3] = pcm_data_address[sound];
							pcm_data_pos_start[1][3] = pcm_data_address[sound];
							pcm_data_ch_format_stereo[0][3] = pcm_data_format_stereo[sound];
							pcm_data_ch_format_stereo[1][3] = pcm_data_format_stereo[sound];
						}
					}
					else{
						pcm_data_pos[0][2] = pcm_data_address[sound] + 0x2C;
						pcm_data_pos[1][2] = pcm_data_address[sound] + 0x2C;
						pcm_data_poll[0][2] = sound;
						pcm_data_poll[1][2] = sound;
						pcm_data_ch_repeat[0][2] = pcm_data_repeat[sound];
						pcm_data_ch_repeat[1][2] = pcm_data_repeat[sound];
						pcm_data_pos_start[0][2] = pcm_data_address[sound];
						pcm_data_pos_start[1][2] = pcm_data_address[sound];
						pcm_data_ch_format_stereo[0][2] = pcm_data_format_stereo[sound];
						pcm_data_ch_format_stereo[1][2] = pcm_data_format_stereo[sound];
					}
				}
				else{
					pcm_data_pos[0][1] = pcm_data_address[sound] + 0x2C;
					pcm_data_pos[1][1] = pcm_data_address[sound] + 0x2C;
					pcm_data_poll[0][1] = sound;
					pcm_data_poll[1][1] = sound;
					pcm_data_ch_repeat[0][1] = pcm_data_repeat[sound];
					pcm_data_ch_repeat[1][1] = pcm_data_repeat[sound];
					pcm_data_pos_start[0][1] = pcm_data_address[sound];
					pcm_data_pos_start[1][1] = pcm_data_address[sound];
					pcm_data_ch_format_stereo[0][1] = pcm_data_format_stereo[sound];
					pcm_data_ch_format_stereo[1][1] = pcm_data_format_stereo[sound];
				}
		}
	}
	else{
		switch(mix){
			case 1:
				pcm_data_pos[0][buffer] = pcm_data_address[sound] + 0x2C;
				pcm_data_poll[0][buffer] = sound;
				pcm_data_ch_repeat[0][buffer] = pcm_data_repeat[sound];
				pcm_data_pos_start[0][buffer] = pcm_data_address[sound];
				pcm_data_ch_format_stereo[0][buffer] = pcm_data_format_stereo[sound];
				break;
				
			case 2:
				pcm_data_pos[1][buffer] = pcm_data_address[sound] + 0x2C;
				pcm_data_poll[1][buffer] = sound;
				pcm_data_ch_repeat[1][buffer] = pcm_data_repeat[sound];
				pcm_data_pos_start[1][buffer] = pcm_data_address[sound];
				pcm_data_ch_format_stereo[1][buffer] = pcm_data_format_stereo[sound];
				break;
				
			default:
				pcm_data_pos[0][buffer] = pcm_data_address[sound] + 0x2C;
				pcm_data_pos[1][buffer] = pcm_data_address[sound] + 0x2C;
				pcm_data_poll[0][buffer] = sound;
				pcm_data_poll[1][buffer] = sound;
				pcm_data_ch_repeat[0][buffer] = pcm_data_repeat[sound];
				pcm_data_ch_repeat[1][buffer] = pcm_data_repeat[sound];
				pcm_data_pos_start[0][buffer] = pcm_data_address[sound];
				pcm_data_pos_start[1][buffer] = pcm_data_address[sound];
				pcm_data_ch_format_stereo[0][buffer] = pcm_data_format_stereo[sound];
				pcm_data_ch_format_stereo[1][buffer] = pcm_data_format_stereo[sound];
		}
	}
}



#ifdef AUDIO_REC_ENABLED
void RecordPCM(short *sample_data){
	int i;
	short sample1;
	short sample2;
	
	for(i=0; i < 735; i++){
		sample1 = sample_data[i<<1] ^ 0x8000;
		sample2 = sample_data[(i<<1)+1] ^ 0x8000;
		fwrite(&sample1, 2, 1, pcm_out);
		fwrite(&sample2, 2, 1, pcm_out);
	}
}
#endif



void StreamPCM(char ch, int *pos_left, int *pos_right, unsigned short *poll_left, unsigned short *poll_right, int *buffer_left, int *buffer_right){
	int position_left;
	int position_right;
	
	position_left = *pos_left;
	position_right = *pos_right;
	
	for(i=0; i < 735; i++){
		// LEFT STEREO
		if(pcm_data[position_left] == 0xFF && pcm_data[position_left+1] == 'E'
		 && pcm_data[position_left+2] == 'O' && pcm_data[position_left+3] == 'F'){
			if(pcm_data_ch_repeat[0][ch]){
				position_left = pcm_data_pos_start[0][ch] + 0x2C;
				buffer_left[i] = pcm_data[position_left] + (pcm_data[position_left+1] << 8);
				position_left += (2 << pcm_data_ch_format_stereo[0][ch]);
			}
			else{
				*poll_left = 0;
				buffer_left[i] = 0;
				// little optimization to write the end of the buffer with zeros
				if(position_left == position_right){
					*poll_right = 0;
					while(i < 735){
						buffer_left[i] = 0;
						buffer_right[i] = 0;
						i++;
					}
				}
			}
		}
		else{
			buffer_left[i] = pcm_data[position_left] + (pcm_data[position_left+1] << 8);
			position_left += (2 << pcm_data_ch_format_stereo[0][ch]);
		}
		// RIGHT STEREO
		if(pcm_data[position_right] == 0xFF && pcm_data[position_right+1] == 'E'
		 && pcm_data[position_right+2] == 'O' && pcm_data[position_right+3] == 'F'){
			if(pcm_data_ch_repeat[1][ch]){
				position_right = pcm_data_pos_start[1][ch] + 0x2C;
				buffer_right[i] = pcm_data[position_right] + (pcm_data[position_right+1] << 8);
				position_right += (2 << pcm_data_ch_format_stereo[1][ch]);
			}
			else{
				*poll_right = 0;
				buffer_right[i] = 0;
			}
		}
		else{
			buffer_right[i] = pcm_data[position_right+(2*pcm_data_ch_format_stereo[1][ch])] + (pcm_data[position_right+1+(2*pcm_data_ch_format_stereo[1][ch])] << 8);
			position_right += (2 << pcm_data_ch_format_stereo[1][ch]);
		}
	}
	
	*pos_left = position_left;
	*pos_right = position_right;
}



void StreamFM(char ch, int *pos, int *buffer_left, int *buffer_right){
	int position;
	short byte;
	//FILE *bgm;
	//FILE *sfx;
	
	int tempbuf[2][735];
	int *tempbufptr[2];
	int tempdac;
	//int sound_vol;
	int i;
	char n;
	
	tempbufptr[0] = tempbuf[0];
	tempbufptr[1] = tempbuf[1];
	memset(tempbuf, 0, 2*735*sizeof(int));	// Erase tempbuf
	
	position = *pos;
	
	if(bgm_enable){
		return;
	}
	
	if(fm_reset[ch] == 1){
		fm_reset[ch] = 0;
		
		// Silence YM2612 and PSG
		YM2612_Write(0, 0x28);
		YM2612_Write(1, 0x00);	// Channel 1
		YM2612_Write(1, 0x01);	// Channel 2
		YM2612_Write(1, 0x02);	// Channel 3
		YM2612_Write(1, 0x04);	// Channel 4
		YM2612_Write(1, 0x05);	// Channel 5
		YM2612_Write(1, 0x06);	// Channel 6
		PSG_Write(0x9F);	// Channel 1
		PSG_Write(0xBF);	// Channel 2
		PSG_Write(0xDF);	// Channel 3
		PSG_Write(0xFF);	// Channel 4 (noise)
		
		// Set DAC enable bit according to the track setting
		YM2612_Write(0, 0x2B);
		YM2612_Write(1, fm_data_dac[fm_track_number[ch]]);
	}
	
	if(fm_fade_out[ch]){
		if(fm_fader[ch] == 0){
			BackupVolume();		// PSG
			for(i=0; i < 6; i++){
				BackupTL(i);		// YM2612
				YM2612_Write(0, 0x2B);
				YM2612_Write(1, 0);
			}
		}
		else if(fm_fader[ch] >> 2){
			FadeVolumePSG(fm_fader[ch] >> 2);
			for(i=0; i < 6; i++){
				n = GetALG(i);
				switch(n){
					case 7:
						FadeVolumeYM2612(i, 0, fm_fader[ch] >> 2);
					case 6:
					case 5:
						FadeVolumeYM2612(i, 2, fm_fader[ch] >> 2);
					case 4:
						FadeVolumeYM2612(i, 1, fm_fader[ch] >> 2);
					case 3:
					case 2:
					case 1:
					case 0:
						FadeVolumeYM2612(i, 3, fm_fader[ch] >> 2);
				}
			}
		}
	}
	
	if(fm_data_ch_type[ch] == FM_TYPE_VGM){
		// VGM file
		i=0;
		while(i < 735){
			if(fm_data_wait[ch] > 0){
				while(fm_data_wait[ch] > 0 && i < 735){
					fm_data_wait[ch]--;
					
					PSG_Update(tempbufptr, 1);
					YM2612_Update(tempbufptr, 1);
					tempdac = (signed short)(fm_data_sample[ch]<<8);
					
					*tempbufptr[0] = (float)(*tempbufptr[0]) * FM_VOLUME;
					*tempbufptr[1] = (float)(*tempbufptr[1]) * FM_VOLUME;
					tempdac = (float)(tempdac) * DAC_VOLUME * GetDAC(ch);
					
					*tempbufptr[0] += tempdac;
					*tempbufptr[1] += tempdac;
					
					tempbufptr[0]++;
					tempbufptr[1]++;
					
					i++;
				}
			}
			else{
				byte = fm_data[position];
				switch(byte){
					case 0x4F:	// Game Gear PSG stereo
						// we simply ignore this
						position += 2;
						break;
					case 0x50:	// PSG write
						PSG_Write(fm_data[position+1]);
						position += 2;
						break;
					case 0x52:	// YM2612 port 0 write
						if(fm_data[position+1] == 0x2A){
							// Write to the DAC
							fm_data_sample[ch] = (fm_data[position+2]^0x80);
						}
						else{
							YM2612_Write(0, fm_data[position+1]);
							YM2612_Write(1, fm_data[position+2]);
						}
						position += 3;
						break;
					case 0x53:	// YM2612 port 1 write
						YM2612_Write(2, fm_data[position+1]);
						YM2612_Write(3, fm_data[position+2]);
						position += 3;
						break;
					case 0x61:	// Wait n samples (16-bit)
						fm_data_wait[ch] = fm_data[position+1] + (fm_data[position+2] << 8);
						position += 3;
						break;
					case 0x62:	// Wait 735 samples
						fm_data_wait[ch] = 735;
						position++;
						break;
					case 0x63:	// Wait 882 samples
						fm_data_wait[ch] = 882;
						position++;
						break;
					case 0x66:	// EOF
						if(fm_data_ch_repeat[ch]){
							if(fm_data_pos_loop[ch] != 0){
								position = fm_data_pos_start[ch] + fm_data_pos_loop[ch] + 0x1C;
								fm_data_pos_dac[ch] = fm_data_address_dac[ch];
							}
							else
								position = fm_data_pos_start[ch] + 0x40;
							i++;
						}
						else{
							position = 0;
							fm_data_ch_type[ch] = -1;
							for(; i < 735; i++){
								tempbuf[0][i] = 0;
								tempbuf[1][i] = 0;
							}
						}
						break;
					case 0x67:	// Initialize data block
						fm_data_address_dac[ch] = position + 7;
						position += (7 + fm_data[position+3] + (fm_data[position+4] << 8)
						 + (fm_data[position+5] << 16) + (fm_data[position+6] << 24));
						fm_data_pos_dac[ch] = fm_data_address_dac[ch];
						break;
					case 0xE0:	// Data block seek
						fm_data_pos_dac[ch] = fm_data_address_dac[ch] + fm_data[position+1] + (fm_data[position+2] << 8)
						 + (fm_data[position+3] << 16) + (fm_data[position+4] << 24);
						position += 5;
						break;
					default:
						if(byte >= 0x70 && byte < 0x80){
							// Wait n+1 samples (4-bit)
							fm_data_wait[ch] = (byte & 0xF) + 1;
							position++;
							break;
						}
						if(byte >= 0x80 && byte < 0x90){
							// YM2612 DAC write / wait n samples (4-bit)
							fm_data_sample[ch] = (fm_data[fm_data_pos_dac[ch]]^0x80);
							fm_data_pos_dac[ch]++;
							fm_data_wait[ch] = (byte & 0xF);
							position++;
							break;
						}
						allegro_message("ERROR: Unsupported VGM data 0x%02X found at 0x%06X", byte, position);
						i++;
						rest(250);
						position++;
				}
			}
		}
	
		for(i=0; i < 735; i++){
			buffer_left[i] = tempbuf[0][i];
			buffer_right[i] = tempbuf[1][i];
		}
	}
	else if(fm_data_ch_type[ch] == FM_TYPE_GYM){
		// GYM file
		byte = fm_data[position];
		position++;
		if(byte == 0xFF && fm_data[position] == 'E' &&
		 fm_data[position+1] == 'O' && fm_data[position+2] == 'F'){
			if(fm_data_ch_repeat[ch]){
				for(i = 255; i >= 0; i--){
					if(position+3 == fm_data_address[i+1]){
						position = fm_data_address[i];
						i = 0;
					}
				}
				byte = fm_data[position];
				position++;
			}
			else{
				position = 0;
				fm_data_ch_type[ch] = -1;
				for(i=0; i < 735; i++){
					tempbuf[0][i] = 0;
					tempbuf[1][i] = 0;
				}
			}
		}
		while(byte > 0){
			switch(byte){
				case 1:
					YM2612_Write(0, fm_data[position]);
					YM2612_Write(1, fm_data[position+1]);
					position += 2;
					break;
				case 2:
					YM2612_Write(2, fm_data[position]);
					YM2612_Write(3, fm_data[position+1]);
					position += 2;
					break;
				case 3:
					PSG_Write(fm_data[position]);
					position++;
			}
			
			byte = fm_data[position];
			position++;
		}
		
		PSG_Update(tempbufptr, 735);
		YM2612_Update(tempbufptr, 735);
	
		for(i=0; i < 735; i++){
			buffer_left[i] = (float)(tempbuf[0][i]) * FM_VOLUME;
			buffer_right[i] = (float)(tempbuf[1][i]) * FM_VOLUME;
		}
	}
	else{
		for(i=0; i < 735; i++){
			buffer_left[i] = 0;
			buffer_right[i] = 0;
		}
		return;
	}
	
	*pos = position;
}
////////// END ---- * SOUND STUFF * //////////



void LoadScripts(){
	FILE *file_procode;
	int script_end;				// used for PROCODE scripts
	short data_counter;			// used for PROCODE scripts
	short data_order[256+8];	// used for PROCODE scripts
	char game_pc_file[1451];
	
	free(script);
	script_size = 0;
	for(i=0; i < 256; i++)
		obj_script_size[i] = 0;
	
	//////////// MOTOROLA FILES ////////////
	
	//LoadBinary(0x03, "pswap.bin");		// *
		LoadBinary(0x06, "cscrew.bin");		// Emerald Hill / Metropolis
		LoadBinary(0x0D, "signpost.bin");	// *
		LoadBinary(0x11, "bridge.bin");		// Emerald Hill / Hidden Palace
		LoadBinary(0x1B, "speed.bin");		// Chemical Plant
	//LoadBinary(0x1E, "spintube.bin");	// Chemical Plant
	//LoadBinary(0x25, "ring.bin");		// *
		LoadBinary(0x3F, "fan.bin");		// Oil Ocean
		LoadBinary(0x79, "starpost.bin");	// *
	
	//////////// PROCODE FILES ////////////
	
	// This is code that can be used for a "static" executable
	//file_procode = fopen(program_filename, "rb");
	//fseek(file_procode, -1451, SEEK_END);
	
	#ifdef ALLEGRO_DOS
	allegro_message("Loading ProCode script file \"game.pc\"...\n");
	#endif
	
	file_procode = fopen("game.pc", "rb");
	if(file_procode == NULL){
		allegro_message("Could not open file \"game.pc\". Program closing...\n");
		quit(0);
	}
	fread(&i, 4, 1, file_procode);
	fread(&n, 4, 1, file_procode);
	// add checks for "PROCODE" string
	// add checks for version byte
	
	data_counter = 0;
	fread(&script_end, 4, 1, file_procode);
	
	while(data_counter >= 0){
		data_order[data_counter] = 0;	// DO NOT DELETE THIS LINE!
		data_order[data_counter] = getc(file_procode);
		if(data_order[data_counter] != 0){	// pointer for an object
			fread(&i, 4, 1, file_procode);
			obj_script_type[data_order[data_counter]] = SCRIPT_TYPE_PROCODE;
			obj_script_start[data_order[data_counter]] = script_size + i;
			
			// set size for previous object
			if(data_counter > 0){
				if(data_order[data_counter-1] > 255){
					game_script_size[data_order[data_counter-1] >> 8] =
					 obj_script_start[data_order[data_counter]] - game_script_start[data_order[data_counter-1] >> 8];
				}
				else{
					obj_script_size[data_order[data_counter-1]] =
					 obj_script_start[data_order[data_counter]] - obj_script_start[data_order[data_counter-1]];
				}
			}
			data_counter++;
		}
		else{	// pointer for a generic function
			data_order[data_counter] = getc(file_procode);
			data_order[data_counter] <<= 8;
			if(data_order[data_counter] == 0){	// end of header
				game_script_start[data_order[data_counter] >> 8] = script_size + script_end;
				
				// set size for previous object
				if(data_counter > 0){
					if(data_order[data_counter-1] > 255){
						game_script_size[data_order[data_counter-1] >> 8] =
						 game_script_start[data_order[data_counter] >> 8] - game_script_start[data_order[data_counter-1] >> 8];
					}
					else{
						obj_script_size[data_order[data_counter-1]] =
						 game_script_start[data_order[data_counter] >> 8] - obj_script_start[data_order[data_counter-1]];
					}
				}
				data_counter = -1;
			}
			else{
				fread(&i, 4, 1, file_procode);
				game_script_start[data_order[data_counter] >> 8] = script_size + i;
				
				// set size for previous object
				if(data_counter > 0){
					if(data_order[data_counter-1] > 255){
						game_script_size[data_order[data_counter-1] >> 8] =
						 game_script_start[data_order[data_counter] >> 8] - game_script_start[data_order[data_counter-1] >> 8];
					}
					else{
						obj_script_size[data_order[data_counter-1]] =
						 game_script_start[data_order[data_counter] >> 8] - obj_script_start[data_order[data_counter-1]];
					}
				}
				data_counter++;
			}
		}
	}
	
	script = (char*)realloc(script, script_size + script_end);
	for(i = script_size; i < (script_size + script_end); i++)
		script[i] = getc(file_procode);
	fclose(file_procode);
	
	script_size = i;
	
	
	
	internal_gvar_ptrs[0] = (int *)&a0;
	internal_gvar_ptrs[1] = (int *)&a1;
	internal_gvar_ptrs[2] = (int *)&a2;
	internal_gvar_ptrs[3] = (int *)&a3;
	internal_gvar_ptrs[4] = (int *)&a4;
	internal_gvar_ptrs[5] = (int *)&a5;
	internal_gvar_ptrs[6] = (int *)&a6;
	internal_gvar_ptrs[7] = (int *)&a7;
	internal_gvar_ptrs[8] = (int *)&d0;
	internal_gvar_ptrs[9] = (int *)&d1;
	internal_gvar_ptrs[10] = (int *)&d2;
	internal_gvar_ptrs[11] = (int *)&d3;
	internal_gvar_ptrs[12] = (int *)&d4;
	internal_gvar_ptrs[13] = (int *)&d5;
	internal_gvar_ptrs[14] = (int *)&d6;
	internal_gvar_ptrs[15] = (int *)&d7;
	internal_gvar_ptrs[16] = (int *)&mouse_z_previous;
	internal_gvar_ptrs[17] = (int *)&camera_mode;
	internal_gvar_ptrs[18] = (int *)&fade_count;
	internal_gvar_ptrs[19] = (int *)&edit_mode;
	internal_gvar_ptrs[20] = (int *)&edit_selector;
	internal_gvar_ptrs[21] = (int *)&edit_clipboard;
	internal_gvar_ptrs[22] = (int *)&smart_mix;
	internal_gvar_ptrs[23] = (int *)&advance_frame;
	internal_gvar_ptrs[24] = (int *)&Game_paused;
	internal_gvar_ptrs[25] = (int *)&DemoCount;
	internal_gvar_ptrs[26] = (int *)&DemoPosition;
	internal_gvar_ptrs[27] = (int *)&DemoFadeDelay;
	internal_gvar_ptrs[28] = (int *)&zone;
	internal_gvar_ptrs[29] = (int *)&act;
	internal_gvar_ptrs[30] = (int *)&stage;
	internal_gvar_ptrs[31] = (int *)&demo;
	internal_gvar_ptrs[32] = (int *)&demo_mode;
	internal_gvar_ptrs[33] = (int *)&frame_skip;
	internal_gvar_ptrs[34] = (int *)&draw_frame;
	internal_gvar_ptrs[35] = (int *)&screen_resolution_x;
	internal_gvar_ptrs[36] = (int *)&screen_resolution_y;
	internal_gvar_ptrs[37] = (int *)&screen_padding_x;
	internal_gvar_ptrs[38] = (int *)&screen_padding_y;
	internal_gvar_ptrs[39] = (int *)&screen_offset_x;
	internal_gvar_ptrs[40] = (int *)&screen_offset_y;
	internal_gvar_ptrs[41] = (int *)&screen_frame_x;
	internal_gvar_ptrs[42] = (int *)&screen_frame_y;
	internal_gvar_ptrs[43] = (int *)&screen_buffer_x;
	internal_gvar_ptrs[44] = (int *)&screen_buffer_y;
	internal_gvar_ptrs[45] = (int *)&screen_scale_x;
	internal_gvar_ptrs[46] = (int *)&screen_scale_y;
	internal_gvar_ptrs[47] = (int *)&screen_double_x;
	internal_gvar_ptrs[48] = (int *)&screen_double_y;
	//internal_gvar_ptrs[49] = (int *)&screen_interlace_x;
	//internal_gvar_ptrs[50] = (int *)&screen_interlace_y;
	internal_gvar_ptrs[51] = (int *)&vsync_enable;
	internal_gvar_ptrs[52] = (int *)&close_button_pressed;
	internal_gvar_ptrs[53] = (int *)&number_of_objects_in_memory;
	internal_gvar_ptrs[54] = (int *)&ticCounter60L;
	internal_gvar_ptrs[55] = (int *)&fps;
	internal_gvar_ptrs[56] = (int *)&show_fps;
	internal_gvar_ptrs[57] = (int *)&ticCounterL;
	internal_gvar_ptrs[58] = (int *)&ticCounterR;
	internal_gvar_ptrs[59] = (int *)&ticCounter60R;
	internal_gvar_ptrs[60] = (int *)&ticUpdated;
	internal_gvar_ptrs[61] = (int *)&waitCounter;
	//internal_gvar_ptrs[62] = (int *)&frames;
	internal_gvar_ptrs[63] = (int *)&num_of_players;
	internal_gvar_ptrs[64] = (int *)&num_of_frames;
	internal_gvar_ptrs[65] = (int *)&frame;
	internal_gvar_ptrs[66] = (int *)&Level_Inactive_flag;
	internal_gvar_ptrs[67] = (int *)&Timer_frames;
	internal_gvar_ptrs[68] = (int *)&Debug_object;
	internal_gvar_ptrs[69] = (int *)&Debug_placement_mode;
	internal_gvar_ptrs[70] = (int *)&Debug_mode_flag;
	internal_gvar_ptrs[71] = (int *)&Emerald_count;
	internal_gvar_ptrs[72] = (int *)&Continue_count;
	internal_gvar_ptrs[73] = (int *)&Time_Over_flag;
	internal_gvar_ptrs[74] = (int *)&Timer_frames;
	internal_gvar_ptrs[75] = (int *)&Timer_minute_word;
	internal_gvar_ptrs[76] = (int *)&Timer_minute;
	internal_gvar_ptrs[77] = (int *)&Timer_second;
	internal_gvar_ptrs[78] = (int *)&Timer_millisecond;
	internal_gvar_ptrs[79] = (int *)&titlecard_sequence_end;
	internal_gvar_ptrs[80] = (int *)key; // already a pointer by itself
	internal_gvar_ptrs[81] = (int *)&key_shifts;
	internal_gvar_ptrs[82] = (int *)&prompt;
	internal_gvar_ptrs[83] = (int *)&full_screen;
	internal_gvar_ptrs[84] = (int *)&audio_volume;
	internal_gvar_ptrs[85] = (int *)&show_version;
	internal_gvar_ptrs[86] = (int *)&show_watermark;
	internal_gvar_ptrs[87] = (int *)&Update_HUD_lives;
	internal_gvar_ptrs[88] = (int *)&Update_HUD_rings;
	internal_gvar_ptrs[89] = (int *)&Update_HUD_timer;
	internal_gvar_ptrs[90] = (int *)&Update_HUD_score;
	
	for(i=0; i < 4; i++){
		internal_pvar_ptrs[i][0] = (int *)&player[i].character;
		internal_pvar_ptrs[i][1] = (int *)&player[i].sidekick;
		internal_pvar_ptrs[i][2] = (int *)&player[i].CtrlInput;
		internal_pvar_ptrs[i][3] = (int *)&player[i].render_flags;
		internal_pvar_ptrs[i][4] = (int *)&player[i].art_tile;
		internal_pvar_ptrs[i][5] = (int *)&player[i].x_pos;
		internal_pvar_ptrs[i][6] = (int *)&player[i].x_pos_pre;
		internal_pvar_ptrs[i][7] = (int *)&player[i].y_pos;
		internal_pvar_ptrs[i][8] = (int *)&player[i].y_pos_pre;
		internal_pvar_ptrs[i][9] = (int *)&player[i].x_vel;
		internal_pvar_ptrs[i][10] = (int *)&player[i].y_vel;
		internal_pvar_ptrs[i][11] = (int *)&player[i].inertia;
		internal_pvar_ptrs[i][12] = (int *)&player[i].x_radius;
		internal_pvar_ptrs[i][13] = (int *)&player[i].y_radius;
		internal_pvar_ptrs[i][14] = (int *)&player[i].priority;
		internal_pvar_ptrs[i][15] = (int *)&player[i].anim_frame_duration;
		internal_pvar_ptrs[i][16] = (int *)&player[i].anim_frame;
		internal_pvar_ptrs[i][17] = (int *)&player[i].anim;
		internal_pvar_ptrs[i][18] = (int *)&player[i].next_anim;
		internal_pvar_ptrs[i][19] = (int *)&player[i].status;
		internal_pvar_ptrs[i][20] = (int *)&player[i].routine;
		internal_pvar_ptrs[i][21] = (int *)&player[i].routine_secondary;
		internal_pvar_ptrs[i][22] = (int *)&player[i].angle;
		internal_pvar_ptrs[i][23] = (int *)&player[i].flip_angle;
		internal_pvar_ptrs[i][24] = (int *)&player[i].air_left;
		internal_pvar_ptrs[i][25] = (int *)&player[i].flip_turned;
		internal_pvar_ptrs[i][26] = (int *)&player[i].obj_control;
		internal_pvar_ptrs[i][27] = (int *)&player[i].status_secondary;
		internal_pvar_ptrs[i][28] = (int *)&player[i].flips_remaining;
		internal_pvar_ptrs[i][29] = (int *)&player[i].flip_speed;
		internal_pvar_ptrs[i][30] = (int *)&player[i].move_lock;
		internal_pvar_ptrs[i][31] = (int *)&player[i].invulnerable_time;
		internal_pvar_ptrs[i][32] = (int *)&player[i].invincibility_time;
		internal_pvar_ptrs[i][33] = (int *)&player[i].speedshoes_time;
		internal_pvar_ptrs[i][34] = (int *)&player[i].next_tilt;
		internal_pvar_ptrs[i][35] = (int *)&player[i].tilt;
		internal_pvar_ptrs[i][36] = (int *)&player[i].stick_to_convex;
		internal_pvar_ptrs[i][37] = (int *)&player[i].spindash_flag;
		internal_pvar_ptrs[i][38] = (int *)&player[i].spindash_counter;
		internal_pvar_ptrs[i][39] = (int *)&player[i].jumping;
		internal_pvar_ptrs[i][40] = (int *)&player[i].interact;
		internal_pvar_ptrs[i][41] = (int *)&player[i].layer;
		internal_pvar_ptrs[i][42] = (int *)&player[i].layer_plus;
		internal_pvar_ptrs[i][43] = (int *)&player[i].Sonic_top_speed;
		internal_pvar_ptrs[i][44] = (int *)&player[i].Sonic_acceleration;
		internal_pvar_ptrs[i][45] = (int *)&player[i].Sonic_deceleration;
		internal_pvar_ptrs[i][46] = (int *)&player[i].Ctrl_Held;
		internal_pvar_ptrs[i][47] = (int *)&player[i].Ctrl_Press;
		internal_pvar_ptrs[i][48] = (int *)&player[i].Ctrl_Held_Logical;
		internal_pvar_ptrs[i][49] = (int *)&player[i].Ctrl_Press_Logical;
		internal_pvar_ptrs[i][50] = (int *)&player[i].width_pixels;
		internal_pvar_ptrs[i][51] = (int *)&player[i].Sonic_Look_delay_counter;
		// Sonic_Stat_Record_Buf[128];
		// Sonic_Pos_Record_Buf[128];
		internal_pvar_ptrs[i][52] = (int *)&player[i].Camera_X_pos;
		internal_pvar_ptrs[i][53] = (int *)&player[i].Camera_Y_pos;
		internal_pvar_ptrs[i][54] = (int *)&player[i].Camera_Z_pos;
		internal_pvar_ptrs[i][55] = (int *)&player[i].Camera_Max_Y_pos;
		internal_pvar_ptrs[i][56] = (int *)&player[i].Camera_Min_X_pos;
		internal_pvar_ptrs[i][57] = (int *)&player[i].Camera_Max_X_pos;
		internal_pvar_ptrs[i][58] = (int *)&player[i].Camera_Min_Y_pos;
		internal_pvar_ptrs[i][59] = (int *)&player[i].Camera_Max_Y_pos_now;
		internal_pvar_ptrs[i][60] = (int *)&player[i].Camera_X_pos_coarse;
		internal_pvar_ptrs[i][61] = (int *)&player[i].Sonic_Pos_Record_Index;
		internal_pvar_ptrs[i][62] = (int *)&player[i].Camera_Y_pos_bias;
		internal_pvar_ptrs[i][63] = (int *)&player[i].Ring_count;
		internal_pvar_ptrs[i][64] = (int *)&player[i].Score;
		internal_pvar_ptrs[i][65] = (int *)&player[i].Life_count;
		internal_pvar_ptrs[i][66] = (int *)&player[i].Extra_life_flags;
		internal_pvar_ptrs[i][67] = (int *)&player[i].Last_star_pole_hit;
		internal_pvar_ptrs[i][68] = (int *)&player[i].Saved_Last_star_pole_hit;
		internal_pvar_ptrs[i][69] = (int *)&player[i].Saved_x_pos;
		internal_pvar_ptrs[i][70] = (int *)&player[i].Saved_y_pos;
		internal_pvar_ptrs[i][71] = (int *)&player[i].Saved_Ring_count;
		internal_pvar_ptrs[i][72] = (int *)&player[i].Saved_Timer;
		internal_pvar_ptrs[i][73] = (int *)&player[i].Saved_art_tile;
		internal_pvar_ptrs[i][74] = (int *)&player[i].Saved_layer;
		internal_pvar_ptrs[i][75] = (int *)&player[i].Saved_Camera_X_pos;
		internal_pvar_ptrs[i][76] = (int *)&player[i].Saved_Camera_Y_pos;
		internal_pvar_ptrs[i][77] = (int *)&player[i].Saved_Water_Level;
		internal_pvar_ptrs[i][78] = (int *)&player[i].Saved_Extra_life_flags;
		internal_pvar_ptrs[i][79] = (int *)&player[i].Saved_Extra_life_flags_2P;
		internal_pvar_ptrs[i][80] = (int *)&player[i].Saved_Camera_Max_Y_pos;
		internal_pvar_ptrs[i][81] = (int *)&player[i].Collision_addr;
		internal_pvar_ptrs[i][82] = (int *)&player[i].ram_EEB0;
		internal_pvar_ptrs[i][83] = (int *)&player[i].ram_EEB2;
		internal_pvar_ptrs[i][84] = (int *)&player[i].ram_EEBE;
		internal_pvar_ptrs[i][85] = (int *)&player[i].ram_EED0;
		internal_pvar_ptrs[i][86] = (int *)&player[i].ram_F65C;
		internal_pvar_ptrs[i][87] = (int *)&player[i].ram_F768;
		internal_pvar_ptrs[i][88] = (int *)&player[i].ram_F76A;
		internal_pvar_ptrs[i][89] = (int *)&player[i].ram_F7C7;
	}
	
	internal_gvar_size[0] = sizeof(a0);
	internal_gvar_size[1] = sizeof(a1);
	internal_gvar_size[2] = sizeof(a2);
	internal_gvar_size[3] = sizeof(a3);
	internal_gvar_size[4] = sizeof(a4);
	internal_gvar_size[5] = sizeof(a5);
	internal_gvar_size[6] = sizeof(a6);
	internal_gvar_size[7] = sizeof(a7);
	internal_gvar_size[8] = sizeof(d0);
	internal_gvar_size[9] = sizeof(d1);
	internal_gvar_size[10] = sizeof(d2);
	internal_gvar_size[11] = sizeof(d3);
	internal_gvar_size[12] = sizeof(d4);
	internal_gvar_size[13] = sizeof(d5);
	internal_gvar_size[14] = sizeof(d6);
	internal_gvar_size[15] = sizeof(d7);
	internal_gvar_size[16] = sizeof(mouse_z_previous);
	internal_gvar_size[17] = sizeof(camera_mode);
	internal_gvar_size[18] = sizeof(fade_count);
	internal_gvar_size[19] = sizeof(edit_mode);
	internal_gvar_size[20] = sizeof(edit_selector);
	internal_gvar_size[21] = sizeof(edit_clipboard);
	internal_gvar_size[22] = sizeof(smart_mix);
	internal_gvar_size[23] = sizeof(advance_frame);
	internal_gvar_size[24] = sizeof(Game_paused);
	internal_gvar_size[25] = sizeof(DemoCount);
	internal_gvar_size[26] = sizeof(DemoPosition);
	internal_gvar_size[27] = sizeof(DemoFadeDelay);
	internal_gvar_size[28] = sizeof(zone);
	internal_gvar_size[29] = sizeof(act);
	internal_gvar_size[30] = sizeof(stage);
	internal_gvar_size[31] = sizeof(demo);
	internal_gvar_size[32] = sizeof(demo_mode);
	internal_gvar_size[33] = sizeof(frame_skip);
	internal_gvar_size[34] = sizeof(draw_frame);
	internal_gvar_size[35] = sizeof(screen_resolution_x);
	internal_gvar_size[36] = sizeof(screen_resolution_y);
	internal_gvar_size[37] = sizeof(screen_padding_x);
	internal_gvar_size[38] = sizeof(screen_padding_y);
	internal_gvar_size[39] = sizeof(screen_offset_x);
	internal_gvar_size[40] = sizeof(screen_offset_y);
	internal_gvar_size[41] = sizeof(screen_frame_x);
	internal_gvar_size[42] = sizeof(screen_frame_y);
	internal_gvar_size[43] = sizeof(screen_buffer_x);
	internal_gvar_size[44] = sizeof(screen_buffer_y);
	internal_gvar_size[45] = sizeof(screen_scale_x);
	internal_gvar_size[46] = sizeof(screen_scale_y);
	internal_gvar_size[47] = sizeof(screen_double_x);
	internal_gvar_size[48] = sizeof(screen_double_y);
	//internal_gvar_size[49] = sizeof(screen_interlace_x);
	//internal_gvar_size[50] = sizeof(screen_interlace_y);
	internal_gvar_size[51] = sizeof(vsync_enable);
	internal_gvar_size[52] = sizeof(close_button_pressed);
	internal_gvar_size[53] = sizeof(number_of_objects_in_memory);
	internal_gvar_size[54] = sizeof(ticCounter60L);
	internal_gvar_size[55] = sizeof(fps);
	internal_gvar_size[56] = sizeof(show_fps);
	internal_gvar_size[57] = sizeof(ticCounterL);
	internal_gvar_size[58] = sizeof(ticCounterR);
	internal_gvar_size[59] = sizeof(ticCounter60R);
	internal_gvar_size[60] = sizeof(ticUpdated);
	internal_gvar_size[61] = sizeof(waitCounter);
	//internal_gvar_size[62] = sizeof(frames);
	internal_gvar_size[63] = sizeof(num_of_players);
	internal_gvar_size[64] = sizeof(num_of_frames);
	internal_gvar_size[65] = sizeof(frame);
	internal_gvar_size[66] = sizeof(Level_Inactive_flag);
	internal_gvar_size[67] = sizeof(Timer_frames);
	internal_gvar_size[68] = sizeof(Debug_object);
	internal_gvar_size[69] = sizeof(Debug_placement_mode);
	internal_gvar_size[70] = sizeof(Debug_mode_flag);
	internal_gvar_size[71] = sizeof(Emerald_count);
	internal_gvar_size[72] = sizeof(Continue_count);
	internal_gvar_size[73] = sizeof(Time_Over_flag);
	internal_gvar_size[74] = sizeof(Timer_frames);
	internal_gvar_size[75] = sizeof(Timer_minute_word);
	internal_gvar_size[76] = sizeof(Timer_minute);
	internal_gvar_size[77] = sizeof(Timer_second);
	internal_gvar_size[78] = sizeof(Timer_millisecond);
	internal_gvar_size[79] = sizeof(titlecard_sequence_end);
	internal_gvar_size[80] = sizeof(key[0]);
	internal_gvar_size[81] = sizeof(key_shifts);
	internal_gvar_size[82] = sizeof(prompt);
	internal_gvar_size[83] = sizeof(full_screen);
	internal_gvar_size[84] = sizeof(audio_volume);
	internal_gvar_size[85] = sizeof(show_version);
	internal_gvar_size[86] = sizeof(show_watermark);
	internal_gvar_size[87] = sizeof(Update_HUD_lives);
	internal_gvar_size[88] = sizeof(Update_HUD_rings);
	internal_gvar_size[89] = sizeof(Update_HUD_timer);
	internal_gvar_size[90] = sizeof(Update_HUD_score);
	
	internal_ovar_size[0] = 4;
	internal_ovar_size[1] = 1;
	internal_ovar_size[2] = 1;
	internal_ovar_size[3] = 1;
	internal_ovar_size[4] = 1;
	internal_ovar_size[5] = 1;
	internal_ovar_size[6] = 1;
	internal_ovar_size[7] = 2;
	internal_ovar_size[8] = 2;
	internal_ovar_size[9] = 1;
	internal_ovar_size[10] = 1;
	internal_ovar_size[11] = 1;
	internal_ovar_size[12] = 1;
	internal_ovar_size[13] = 1;
	internal_ovar_size[14] = 1;
	internal_ovar_size[15] = 1;
	internal_ovar_size[16] = 2;
	internal_ovar_size[17] = 1;
	internal_ovar_size[18] = 2;
	internal_ovar_size[19] = 2;
	internal_ovar_size[20] = 2;
	internal_ovar_size[21] = 2;
	internal_ovar_size[22] = 1;
	internal_ovar_size[23] = 1;
	
	internal_pvar_size[0] = sizeof(player[i].character);
	internal_pvar_size[1] = sizeof(player[i].sidekick);
	internal_pvar_size[2] = sizeof(player[i].CtrlInput);
	internal_pvar_size[3] = sizeof(player[i].render_flags);
	internal_pvar_size[4] = sizeof(player[i].art_tile);
	internal_pvar_size[5] = sizeof(player[i].x_pos);
	internal_pvar_size[6] = sizeof(player[i].x_pos_pre);
	internal_pvar_size[7] = sizeof(player[i].y_pos);
	internal_pvar_size[8] = sizeof(player[i].y_pos_pre);
	internal_pvar_size[9] = sizeof(player[i].x_vel);
	internal_pvar_size[10] = sizeof(player[i].y_vel);
	internal_pvar_size[11] = sizeof(player[i].inertia);
	internal_pvar_size[12] = sizeof(player[i].x_radius);
	internal_pvar_size[13] = sizeof(player[i].y_radius);
	internal_pvar_size[14] = sizeof(player[i].priority);
	internal_pvar_size[15] = sizeof(player[i].anim_frame_duration);
	internal_pvar_size[16] = sizeof(player[i].anim_frame);
	internal_pvar_size[17] = sizeof(player[i].anim);
	internal_pvar_size[18] = sizeof(player[i].next_anim);
	internal_pvar_size[19] = sizeof(player[i].status);
	internal_pvar_size[20] = sizeof(player[i].routine);
	internal_pvar_size[21] = sizeof(player[i].routine_secondary);
	internal_pvar_size[22] = sizeof(player[i].angle);
	internal_pvar_size[23] = sizeof(player[i].flip_angle);
	internal_pvar_size[24] = sizeof(player[i].air_left);
	internal_pvar_size[25] = sizeof(player[i].flip_turned);
	internal_pvar_size[26] = sizeof(player[i].obj_control);
	internal_pvar_size[27] = sizeof(player[i].status_secondary);
	internal_pvar_size[28] = sizeof(player[i].flips_remaining);
	internal_pvar_size[29] = sizeof(player[i].flip_speed);
	internal_pvar_size[30] = sizeof(player[i].move_lock);
	internal_pvar_size[31] = sizeof(player[i].invulnerable_time);
	internal_pvar_size[32] = sizeof(player[i].invincibility_time);
	internal_pvar_size[33] = sizeof(player[i].speedshoes_time);
	internal_pvar_size[34] = sizeof(player[i].next_tilt);
	internal_pvar_size[35] = sizeof(player[i].tilt);
	internal_pvar_size[36] = sizeof(player[i].stick_to_convex);
	internal_pvar_size[37] = sizeof(player[i].spindash_flag);
	internal_pvar_size[38] = sizeof(player[i].spindash_counter);
	internal_pvar_size[39] = sizeof(player[i].jumping);
	internal_pvar_size[40] = sizeof(player[i].interact);
	internal_pvar_size[41] = sizeof(player[i].layer);
	internal_pvar_size[42] = sizeof(player[i].layer_plus);
	internal_pvar_size[43] = sizeof(player[i].Sonic_top_speed);
	internal_pvar_size[44] = sizeof(player[i].Sonic_acceleration);
	internal_pvar_size[45] = sizeof(player[i].Sonic_deceleration);
	internal_pvar_size[46] = sizeof(player[i].Ctrl_Held);
	internal_pvar_size[47] = sizeof(player[i].Ctrl_Press);
	internal_pvar_size[48] = sizeof(player[i].Ctrl_Held_Logical);
	internal_pvar_size[49] = sizeof(player[i].Ctrl_Press_Logical);
	internal_pvar_size[50] = sizeof(player[i].width_pixels);
	internal_pvar_size[51] = sizeof(player[i].Sonic_Look_delay_counter);
	internal_pvar_size[52] = sizeof(player[i].Camera_X_pos);
	internal_pvar_size[53] = sizeof(player[i].Camera_Y_pos);
	internal_pvar_size[54] = sizeof(player[i].Camera_Z_pos);
	internal_pvar_size[55] = sizeof(player[i].Camera_Max_Y_pos);
	internal_pvar_size[56] = sizeof(player[i].Camera_Min_X_pos);
	internal_pvar_size[57] = sizeof(player[i].Camera_Max_X_pos);
	internal_pvar_size[58] = sizeof(player[i].Camera_Min_Y_pos);
	internal_pvar_size[59] = sizeof(player[i].Camera_Max_Y_pos_now);
	internal_pvar_size[60] = sizeof(player[i].Camera_X_pos_coarse);
	internal_pvar_size[61] = sizeof(player[i].Sonic_Pos_Record_Index);
	internal_pvar_size[62] = sizeof(player[i].Camera_Y_pos_bias);
	internal_pvar_size[63] = sizeof(player[i].Ring_count);
	internal_pvar_size[64] = sizeof(player[i].Score);
	internal_pvar_size[65] = sizeof(player[i].Life_count);
	internal_pvar_size[66] = sizeof(player[i].Extra_life_flags);
	internal_pvar_size[67] = sizeof(player[i].Last_star_pole_hit);
	internal_pvar_size[68] = sizeof(player[i].Saved_Last_star_pole_hit);
	internal_pvar_size[69] = sizeof(player[i].Saved_x_pos);
	internal_pvar_size[70] = sizeof(player[i].Saved_y_pos);
	internal_pvar_size[71] = sizeof(player[i].Saved_Ring_count);
	internal_pvar_size[72] = sizeof(player[i].Saved_Timer);
	internal_pvar_size[73] = sizeof(player[i].Saved_art_tile);
	internal_pvar_size[74] = sizeof(player[i].Saved_layer);
	internal_pvar_size[75] = sizeof(player[i].Saved_Camera_X_pos);
	internal_pvar_size[76] = sizeof(player[i].Saved_Camera_Y_pos);
	internal_pvar_size[77] = sizeof(player[i].Saved_Water_Level);
	internal_pvar_size[78] = sizeof(player[i].Saved_Extra_life_flags);
	internal_pvar_size[79] = sizeof(player[i].Saved_Extra_life_flags_2P);
	internal_pvar_size[80] = sizeof(player[i].Saved_Camera_Max_Y_pos);
	internal_pvar_size[81] = sizeof(player[i].Collision_addr);
	internal_pvar_size[82] = sizeof(player[i].ram_EEB0);
	internal_pvar_size[83] = sizeof(player[i].ram_EEB2);
	internal_pvar_size[84] = sizeof(player[i].ram_EEBE);
	internal_pvar_size[85] = sizeof(player[i].ram_EED0);
	internal_pvar_size[86] = sizeof(player[i].ram_F65C);
	internal_pvar_size[87] = sizeof(player[i].ram_F768);
	internal_pvar_size[88] = sizeof(player[i].ram_F76A);
	internal_pvar_size[89] = sizeof(player[i].ram_F7C7);
}	



#ifdef ENABLE_LOGGING
void TabLog(signed char tab){
	logtab += tab;
	if(logtab < 0)
		logtab = 0;
}

void WriteLog(const char *string, ...){
	if(logline < 100000){
		msglog_tabs[logline] = logtab;
		strncpy(msglog[logline], string, 32);
		msglog[logline][31] = '\0';
		logline++;
	}
}

void RamLog(unsigned short addr){
	char string[32];
	for(i=0; i < 64 && logline < 100000; i += 8){
		sprintf(
			string, "\t%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X",
			ram_68k[0xFFFF - addr - i-0], ram_68k[0xFFFF - addr - i-1],
			ram_68k[0xFFFF - addr - i-2], ram_68k[0xFFFF - addr - i-3],
			ram_68k[0xFFFF - addr - i-4], ram_68k[0xFFFF - addr - i-5],
			ram_68k[0xFFFF - addr - i-6], ram_68k[0xFFFF - addr - i-7]
		);
			
		if(string[0] == '\t'){
			strncpy(msglog[logline], string, 32);
			msglog[logline][31] = '\0';
			logline++;
		}
	}
}

void VarLog(short var){
	char string[32];
	if(logline < 100000){
		switch(var){
			// 68000 REGISTERS
			case REG_A0:
				sprintf(string, "a0 = 0x%08X", a0);
				break;
			case REG_A1:
				sprintf(string, "a1 = 0x%08X", a1);
				break;
			case REG_A2:
				sprintf(string, "a2 = 0x%08X", a2);
				break;
			case REG_A3:
				sprintf(string, "a3 = 0x%08X", a3);
				break;
			case REG_A4:
				sprintf(string, "a4 = 0x%08X", a4);
				break;
			case REG_A5:
				sprintf(string, "a5 = 0x%08X", a5);
				break;
			case REG_A6:
				sprintf(string, "a6 = 0x%08X", a6);
				break;
			case REG_A7:
				sprintf(string, "a7 = 0x%08X", a7);
				break;
			case REG_D0:
				sprintf(string, "d0 = 0x%08X", d0);
				break;
			case REG_D1:
				sprintf(string, "d1 = 0x%08X", d1);
				break;
			case REG_D2:
				sprintf(string, "d2 = 0x%08X", d2);
				break;
			case REG_D3:
				sprintf(string, "d3 = 0x%08X", d3);
				break;
			case REG_D4:
				sprintf(string, "d4 = 0x%08X", d4);
				break;
			case REG_D5:
				sprintf(string, "d5 = 0x%08X", d5);
				break;
			case REG_D6:
				sprintf(string, "d6 = 0x%08X", d6);
				break;
			case REG_D7:
				sprintf(string, "d7 = 0x%08X", d7);
				break;
			case REG_PC:
				sprintf(string, "pc = 0x%08X", pc);
				break;
			case REG_SR:
				sprintf(string, "sr = 0x%08X", sr);
				break;
				
			// VARIABLES
			case VAR_VAR_TEMP:
				sprintf(string, "var_temp = 0x%08X", var_temp);
				break;
			case VAR_ROUTINE:
				sprintf(string, "routine = 0x%02X", p->routine);
				break;
			case VAR_X_RADIUS:
				sprintf(string, "x_radius = 0x%02X", p->x_radius);
				break;
			case VAR_Y_RADIUS:
				sprintf(string, "y_radius = 0x%02X", p->y_radius);
				break;
			case VAR_X_POS:
				sprintf(string, "x_pos = 0x%04X", p->x_pos);
				break;
			case VAR_X_POS_PRE:
				sprintf(string, "x_pos_pre = 0x%04X", p->x_pos_pre);
				break;
			case VAR_Y_POS:
				sprintf(string, "y_pos = 0x%04X", p->y_pos);
				break;
			case VAR_Y_POS_PRE:
				sprintf(string, "y_pos_pre = 0x%04X", p->y_pos_pre);
				break;
			case VAR_X_VEL:
				sprintf(string, "x_vel = 0x%04X", p->x_vel);
				break;
			case VAR_Y_VEL:
				sprintf(string, "y_vel = 0x%04X", p->y_vel);
				break;
			case VAR_INERTIA:
				sprintf(string, "inertia = 0x%04X", p->inertia);
				break;
			case VAR_STATUS:
				sprintf(string, "status = 0x%02X", p->status);
				break;
			case VAR_ANGLE:
				sprintf(string, "angle = 0x%02X", p->angle);
				break;
			case VAR_ANIM:
				sprintf(string, "anim = 0x%02X", p->anim);
				break;
			case VAR_LAYER:
				sprintf(string, "layer = 0x%02X", p->layer);
				break;
			default:
				return;
		}
		
		msglog_tabs[logline] = logtab;
		msglog[logline][0] = '*';
		msglog[logline][1] = ' ';
		strncpy(&msglog[logline][2], string, 30);
		msglog[logline][31] = '\0';
		logline++;
	}
}

void RegLog(){
	WriteLog("*****************");
	VarLog(REG_A0);
	VarLog(REG_A1);
	VarLog(REG_A2);
	VarLog(REG_A3);
	VarLog(REG_A4);
	VarLog(REG_A5);
	VarLog(REG_A6);
	VarLog(REG_A7);
	VarLog(REG_D0);
	VarLog(REG_D1);
	VarLog(REG_D2);
	VarLog(REG_D3);
	VarLog(REG_D4);
	VarLog(REG_D5);
	VarLog(REG_D6);
	VarLog(REG_D7);
	WriteLog("*****************");
}

void DumpLog(const char *string, ...){
	int tab_space = 0x20202020;
	short endofline = 0x0A0D;
	short logy;
	char logx;
	FILE *dump;
	
	dump = fopen(string, "w+b");
	for(logy=0; logy < logline; logy++){
		for(logx=0; logx < msglog_tabs[logy]; logx++)
			fwrite(&tab_space, 4, 1, dump);
		for(logx=0; logx < 32; logx++){
			if(msglog[logy][logx] == '\0'){
				fwrite(&endofline, 2, 1, dump);
				break;
			}
			fwrite(&msglog[logy][logx], 1, 1, dump);
		}
	}
	fclose(dump);
}

void ResetLog(){
	logline = 0;
	logtab = 0;
}



#ifdef ENABLE_SRAM_LOG_ANALYSIS
void AnalyzeSRAM(){
	int i;
	int problem;
	
	unsigned char prosonic_ram[64] = {
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	};
	
	unsigned char ram_skip_table[64] = {
		// 9 means it should NOT be skipped, but is due to issues
		1, 9, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0,
		1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9
	};
	
	unsigned char ram_conversion_table[64] = {
		0x00, 0x01,		0x03, 0x02,		0x04, 0x05,		0x06, 0x07,
		0x09, 0x08,		0x0B, 0x0A,		0x0D, 0x0C,		0x0F, 0x0E,
		0x11, 0x10,		0x13, 0x12,		0x15, 0x14,		0x16, 0x17,
		0x18, 0x19,		0x1A, 0x1B,		0x1C, 0x1D,		0x1E, 0x1F,
		0x20, 0x21,		0x22, 0x23,		0x24, 0x25,		0x26, 0x27,
		0x28, 0x29,		0x2A, 0x2B,		0x2C, 0x2D,		0x2F, 0x2E,
		0x31, 0x30,		0x33, 0x32,		0x35, 0x34,		0x36, 0x37,
		0x38, 0x39,		0x3B, 0x3A,		0x3C, 0x3D,		0x3E, 0x3F
	};
	
	char number_string_conversion[] = "0123456789ABCDEF";
	char sram_frame_string[] = "sram_frame[0x00] = 0x00";
	char prosonic_ram_string[] = "prosonic_ram[0x00] = 0x00";
	
	prosonic_ram[0x01] = p->render_flags;	// bit 1 always off (broken), turn off
	*(short *)&prosonic_ram[0x02] = p->art_tile;
	*(short *)&prosonic_ram[0x08] = p->x_pos;
	*(short *)&prosonic_ram[0x0A] = p->x_pos_pre;
	*(short *)&prosonic_ram[0x0C] = p->y_pos;
	*(short *)&prosonic_ram[0x0E] = p->y_pos_pre;
	*(short *)&prosonic_ram[0x10] = p->x_vel;
	*(short *)&prosonic_ram[0x12] = p->y_vel;
	*(short *)&prosonic_ram[0x14] = p->inertia;
	prosonic_ram[0x16] = p->y_radius;
	prosonic_ram[0x17] = p->x_radius;
	prosonic_ram[0x18] = p->priority;
	prosonic_ram[0x1B] = p->anim_frame;
	prosonic_ram[0x1C] = p->anim;
	prosonic_ram[0x1D] = p->next_anim;
	prosonic_ram[0x1E] = p->anim_frame_duration;
	prosonic_ram[0x22] = p->status;
	prosonic_ram[0x24] = p->routine;
	prosonic_ram[0x25] = p->routine_secondary;
	prosonic_ram[0x26] = p->angle;
	prosonic_ram[0x27] = p->flip_angle;
	prosonic_ram[0x28] = p->air_left;
	prosonic_ram[0x29] = p->flip_turned;
	prosonic_ram[0x2A] = p->obj_control;
	prosonic_ram[0x2B] = p->status_secondary;
	prosonic_ram[0x2C] = p->flips_remaining;
	prosonic_ram[0x2D] = p->flip_speed;
	*(short *)&prosonic_ram[0x2E] = p->move_lock;
	*(short *)&prosonic_ram[0x30] = p->invulnerable_time;
	*(short *)&prosonic_ram[0x32] = p->invincibility_time;
	*(short *)&prosonic_ram[0x34] = p->speedshoes_time;
	prosonic_ram[0x36] = p->next_tilt;
	prosonic_ram[0x37] = p->tilt;
	prosonic_ram[0x38] = p->stick_to_convex;
	prosonic_ram[0x39] = p->spindash_flag;
	*(short *)&prosonic_ram[0x3A] = p->spindash_counter;
	prosonic_ram[0x3C] = p->jumping;
	prosonic_ram[0x3D] = p->interact;		// not yet used, turn off
	prosonic_ram[0x3E] = p->layer - 4;		// not quite right, turn off
	prosonic_ram[0x3F] = p->layer_plus - 4;	// not quite right, turn off
	
	problem = 0;
	for(i=0; i < 64; i++){
		if(!ram_skip_table[i]){
			n = ram_conversion_table[i];
			if(sram_frame[i] != prosonic_ram[n]){
				//allegro_message(
				//"sram_frame[0x%02X] = 0x%02X\nprosonic_ram[0x%02X] = 0x%02X",
				//i, sram_frame[i], i, prosonic_ram[n]);
				prosonic_ram_string[0x0F] = number_string_conversion[n >> 4];
				prosonic_ram_string[0x10] = number_string_conversion[n & 0xF];
				prosonic_ram_string[0x17] = number_string_conversion[prosonic_ram[n] >> 4];
				prosonic_ram_string[0x18] = number_string_conversion[prosonic_ram[n] & 0xF];
				sram_frame_string[0x0D] = number_string_conversion[i >> 4];
				sram_frame_string[0x0E] = number_string_conversion[i & 0xF];
				sram_frame_string[0x15] = number_string_conversion[sram_frame[i] >> 4];
				sram_frame_string[0x16] = number_string_conversion[sram_frame[i] & 0xF];
				WriteLog("\n");
				if(!problem){
					WriteLog("CALCULATION PROBLEMS:");
					WriteLog("\n");
				}
				WriteLog(prosonic_ram_string);
				WriteLog(sram_frame_string);
				problem++;
			}
		}
	}
	
	if(problem){
		DumpLog("obj01.log");
		if(problem == 1)
			allegro_message("A problem was discovered with 1 byte at frame 0x%X. A log file has been dumped.", ticCounterL);
		else
			allegro_message("Problems were discovered with %i bytes at frame 0x%X. A log file has been dumped.", problem, ticCounterL);
		rest(100);
	}
}
#endif
#endif


/*
void LoadSavestate(const char *string, ...){
	unsigned char ram[64];
	int i;
	FILE *savestate;
	savestate = fopen(string, "rb");
	if(savestate == NULL){
		allegro_message("Cannot open savestate \"%s\".", string);
		return;
	}
	fseek(savestate, 0xD478, SEEK_SET);
	
	for(i = 0; i < 64; i++)
		fread(&ram[i], 1, 1, savestate);
	
	p->render_flags = ram[1];
	p->art_tile = (ram[2] << 8) + ram[3];
	p->x_pos = (ram[8] << 8) + ram[9];
	p->x_pos_pre = (ram[0xA] << 8) + ram[0xB];
	p->y_pos = (ram[0xC] << 8) + ram[0xD];
	p->y_pos_pre = (ram[0xE] << 8) + ram[0xF];
	p->x_vel = (ram[0x10] << 8) + ram[0x11];
	p->y_vel = (ram[0x12] << 8) + ram[0x13];
	p->inertia = (ram[0x14] << 8) + ram[0x15];
	p->y_radius = ram[0x16];
	p->x_radius = ram[0x17];
	p->priority = ram[0x18];
	p->anim_frame = ram[0x1B];
	p->anim = ram[0x1C];
	p->next_anim = ram[0x1D];
	//p->anim_frame_duration = ram[0x1E];
	p->status = ram[0x22];
	p->routine = ram[0x24];
	p->routine_secondary = ram[0x25];
	p->angle = ram[0x26];
	p->flip_angle = ram[0x27];
	p->air_left = ram[0x28];
	p->flip_turned = ram[0x29];
	p->obj_control = ram[0x2A];
	p->status_secondary = ram[0x2B];
	p->flips_remaining = ram[0x2C];
	p->flip_speed = ram[0x2D];
	p->move_lock = (ram[0x2E] << 8) + ram[0x2F];
	p->invulnerable_time = (ram[0x30] << 8) + ram[0x31];
	p->invincibility_time = (ram[0x32] << 8) + ram[0x33];
	p->speedshoes_time = (ram[0x34] << 8) + ram[0x35];
	p->next_tilt = ram[0x36];
	p->tilt = ram[0x37];
	p->stick_to_convex = ram[0x38];
	p->spindash_flag = ram[0x39];
	p->spindash_counter = (ram[0x3A] << 8) + ram[0x3B];
	p->jumping = ram[0x3C];
	p->interact = ram[0x3D];
	p->layer = ram[0x3E] + 4;
	p->layer_plus = ram[0x3F] + 4;
	
	fclose(savestate);
}
*/


void FadeRGB(BITMAP *b){
	unsigned short x;
	unsigned short y;
	int color_r;
	int color_g;
	int color_b;
	int new_color_r;
	int new_color_g;
	int new_color_b;
	
	if(fade_count < 0){ // Fade to black
		for(y=0; y < screen_resolution_y; y++){
			for(x=16; x < screen_resolution_x+16; x++){
				color_r = (unsigned short)(b->line[y][(x<<1)+1]<<8) + (unsigned short)(b->line[y][x<<1]);
				color_g = (color_r >> 6) & 31;
				color_b = color_r & 31;
				color_r >>= 11;
				
				// Fade color R
				if(color_r + fade_count <= 0)
					new_color_r = 0;
				else
					new_color_r = color_r + fade_count;
				
				if(new_color_r == 0){
					// Fade color G
					if(color_g + color_r + fade_count <= 0)
						new_color_g = 0;
					else
						new_color_g = color_g + color_r + fade_count;
					
					if(new_color_g == 0){
						// Fade color B
						if(color_b + color_g + color_r + fade_count <= 0)
							new_color_b = 0;
						else
							new_color_b = color_b + color_g + color_r + fade_count;
					}
					else
						new_color_b = color_b;
				}
				else{
					new_color_g = color_g;
					new_color_b = color_b;
				}
				
				b->line[y][(x<<1)+1] = (new_color_r << 3) + (new_color_g >> 2);
				b->line[y][x<<1] = (new_color_g << 6) + new_color_b;
			}
		}
	}
	else{ // Fade to white
		for(y=0; y < screen_resolution_y; y++){
			for(x=16; x < screen_resolution_x+16; x++){
				color_r = (unsigned short)(b->line[y][(x<<1)+1]<<8) + (unsigned short)(b->line[y][x<<1]);
				color_g = (color_r >> 6) & 31;
				color_b = color_r & 31;
				color_r >>= 11;
				
				// Fade color R
				if(color_r + fade_count >= 31)
					new_color_r = 31;
				else
					new_color_r = color_r + fade_count;
				
				if(new_color_r == 31){
					// Fade color G
					if(color_g + fade_count - (31-color_r) >= 31)
						new_color_g = 31;
					else
						new_color_g = color_g + fade_count - (31-color_r);
					
					if(new_color_g == 31){
						// Fade color B
						if(color_b + fade_count - (31-color_r) + (31-color_g) >= 31)
							new_color_b = 31;
						else
							new_color_b = color_b + fade_count - (31-color_r) + (31-color_g);
					}
					else
						new_color_b = color_b;
				}
				else{
					new_color_g = color_g;
					new_color_b = color_b;
				}
				
				b->line[y][(x<<1)+1] = (new_color_r << 3) + (new_color_g >> 2);
				b->line[y][x<<1] = (new_color_g << 6) + new_color_b;
			}
		}
	}
}



void TitleCard(){
	int addr;
	int tc_size;
	int i;
	int n;
	
	if(titlecard_sequence_end)	return;
	
	if(titlecard_counter[0] == 0xFF){
		titlecard_file = fopen("tcard.dat", "rb");
		if(titlecard_file == NULL){
			allegro_message("Could not open file \"tcard.dat\". Program closing...\n");
			quit(0);
		}
		fseek(titlecard_file, 0, SEEK_END);
		tc_size = ftell(titlecard_file);
		rewind(titlecard_file);
		
		if(titlecard_data) free(titlecard_data);
		titlecard_data = (char *)malloc(tc_size);
		fread(titlecard_data, 1, tc_size, titlecard_file);
		fclose(titlecard_file);
		
		for(i=0; i < titlecard_data[0]; i++){
			titlecard_phase[i] = -1;
			titlecard_counter[i] = 0;
			titlecard_xpos[i] = (titlecard_data[(i<<3)+5] << 8) + titlecard_data[(i<<3)+6];
			titlecard_ypos[i] = (titlecard_data[(i<<3)+7] << 8) + titlecard_data[(i<<3)+8];
		}
		if(titlecard_data[0] > 0)
			fade_count = titlecard_xpos[0];
		
		Update_HUD_timer = 0;
	}
	
	for(i=0; i < titlecard_data[0]; i++){
		if(titlecard_counter[i] != 0xFF){
			addr = (titlecard_data[(i<<3)+1] << 8) + titlecard_data[(i<<3)+2];
			if(i == 0){
				if(titlecard_counter[i] == 0){
					titlecard_phase[i]++;
					titlecard_counter[i] = titlecard_data[addr+(titlecard_phase[i]*2)];
				}
				if(titlecard_counter[i] != 0xFF){
					titlecard_counter[i]--;
					titlecard_xpos[i] += (signed char)titlecard_data[addr+(titlecard_phase[i]*2)+1];
					fade_count = titlecard_xpos[i];
				}
			}
			else{
				if(titlecard_counter[i] == 0){
					titlecard_phase[i]++;
					titlecard_counter[i] = titlecard_data[addr+(titlecard_phase[i]*3)];
				}
				if(titlecard_counter[i] != 0xFF){
					titlecard_counter[i]--;
					titlecard_xpos[i] += (signed char)titlecard_data[addr+(titlecard_phase[i]*3)+1];
					titlecard_ypos[i] += (signed char)titlecard_data[addr+(titlecard_phase[i]*3)+2];
					switch(num_of_frames){
						case 1:
							DrawSprite(&z, LayerB[frame-1], 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							break;
						case 2:
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							break;
						case 3:
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							break;
						case 4:
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
							DrawSprite(&z, LayerM, 0x103, titlecard_data[(i<<3)+3], NORMAL, titlecard_data[(i<<3)+4], titlecard_xpos[i]+16, titlecard_ypos[i]);
					}
				}
			}
		}
	}
	
	n=0;
	for(i=0; i < titlecard_data[0]; i++)
		n += titlecard_counter[i];
	if(titlecard_data[0] * 0xFF == n){
		titlecard_sequence_end = 1;
		free(titlecard_data);
		titlecard_data = 0; // DO NOT DELETE ME!! Prevents memory leaks!
		ticCounterL = 0;
		Update_HUD_timer = 1;
	}
}



void UpdateBlockLayout(ZONE *z){
	// This function is used with the editing tools so any changes made to the
	// level layout will automatically update the COMPILED_MAP array.
	
	unsigned short x;
	unsigned short y;
	
	if(!COMPILED_MAP)
		return;
	
	for(y=0; y < blocks_y; y++){
		for(x=0; x < blocks_x; x++)
			COMPILED_MAP[(y * blocks_x) + x] = ReadBlock(z, x<<4, y<<4);
	}
}



void CreateBlockLayout(ZONE *z){
	// This function maps out all the 16x16 blocks into an array so they can be
	// quickly referenced without having to read the tile layout. It uses extra
	// memory, but it provides a performance boost.
	
	unsigned short x;
	unsigned short y;
	
	if(COMPILED_MAP) free(COMPILED_MAP);
	COMPILED_MAP = malloc(blocks_x * blocks_y * 4);
	
	for(y=0; y < blocks_y; y++){
		for(x=0; x < blocks_x; x++)
			COMPILED_MAP[(y * blocks_x) + x] = ReadBlock(z, x<<4, y<<4);
	}
	
	if(COMPILED_BACKMAP) free(COMPILED_BACKMAP);
	COMPILED_BACKMAP = malloc(blocks_x * blocks_y * 2);
	
	for(y=0; y < blocks_y; y++){
		for(x=0; x < blocks_x; x++)
			COMPILED_BACKMAP[(y * blocks_x) + x] = ReadBackBlock(z, x<<4, y<<4);
	}
}



int ReadBlock(ZONE *z, unsigned short x, unsigned short y){
	unsigned short t;
	unsigned int b;
	// BMAP FORMAT
	// ////////uuuuuuus bptttttt fmBBBBBB BBBBBBBB OLD FORMAT!
	// ttttttuu uuuLbpBP fmNNNNNN NNNNNNNN
	// (NOTE: little endian!!)
	// 
	//
	// (N) block number
	// (m) mirror
	// (f) flip
	// (P) platform (layer 1)
	// (B) barrier (layer 1)
	// (p) platform (layer 2)
	// (b) barrier (layer 2)
	// (L) plane
	// (u) undefined
	// (t) translucency
	
	x %= ((z->TMAP[tmap_ptr-2]+1) << (5+tilesize));
	y %= ((z->TMAP[tmap_ptr-1]+1) << (5+tilesize));
	
	t = ((x / (32 << tilesize)) + (y / (32 << tilesize) * tiles_x_long)) << 1;
	t = *(short *)(&z->TMAP[tmap_ptr+t]);
	
	b = (((x & ((32 << tilesize) - 1)) >> 4) + (((y & ((32 << tilesize) - 1)) >> 4) * (2 << tilesize))) << 2;
	b = *(int *)(&z->BMAP[bmap_ptr + b + ((((2 << tilesize) * (2 << tilesize)) << 2) * t)]);
	
	return b;
}



short ReadBackBlock(ZONE *z, unsigned short x, unsigned short y){
	unsigned short t;
	unsigned int b;
	// BMAP FORMAT
	// ////////uuuuuuus bptttttt fmBBBBBB BBBBBBBB OLD FORMAT!
	// ttttttuu uuuLbpBP fmNNNNNN NNNNNNNN
	// (NOTE: little endian!!)
	// 
	//
	// (N) block number
	// (m) mirror
	// (f) flip
	// (P) platform (layer 1)
	// (B) barrier (layer 1)
	// (p) platform (layer 2)
	// (b) barrier (layer 2)
	// (L) plane
	// (u) undefined
	// (t) translucency
	
	x %= ((z->TMAP[tmap_ptr-2]+1) << (5+tilesize));
	y %= ((z->TMAP[tmap_ptr-1]+1) << (5+tilesize));
	
	x += (blocks_x<<4);
	
	t = ((x / (32 << tilesize)) + (y / (32 << tilesize) * tiles_x_long)) << 1;
	t = *(short *)(&z->TMAP[tmap_ptr+t]);
	
	b = (((x & ((32 << tilesize) - 1)) >> 4) + (((y & ((32 << tilesize) - 1)) >> 4) * (2 << tilesize))) << 2;
	b = *(short *)(&z->BMAP[bmap_ptr + b + ((((2 << tilesize) * (2 << tilesize)) << 2) * t)]);
	return b;
}



int Pause(){
	if(p->Life_count == 0){
		Game_paused = 0;
		return 0;
	}
	
	if(p->Ctrl_Press & 0x80){
		Game_paused ^= 1;
		return Game_paused;
	}
	
	if(Game_paused){
		#ifdef ENABLE_LOGGING
		if(p->Ctrl_Press & 0x40){
			DumpLog("obj01.log");
		}
		#endif
		if(p->Ctrl_Held & 0x10){
			if(p->Ctrl_Press & 0x10)
				p->Ctrl_Press ^= 0x10;
			advance_frame ^= 1;
			return advance_frame ^ 1;
		}
		if(p->Ctrl_Press & 0x20){
			p->Ctrl_Press ^= 0x20;
			advance_frame ^= 1;
			return advance_frame ^ 1;
		}
		if(advance_frame == 1)
			advance_frame = 0;
	}
	
	return Game_paused;
}



int main(int argc, char **argv)
{
	// Copy program filename
	i = strlen(argv[0]) + 1;
	program_filename = (char *)malloc(i);
	strcpy(program_filename, argv[0]);
	
	// Copy IP address
	#ifndef ALLEGRO_DOS
	if(argc > 1)
		strcpy(ip_input, argv[1]);
	else
		strcpy(ip_input, "127.0.0.1");
	#endif
	
	// Required //
	#ifdef ALLEGRO_DOS
	printf("Initializing Allegro...\n");
	#endif
	if(allegro_init() != 0){
		#ifdef ALLEGRO_DOS
		printf("Error: allegro_init() has failed!");
		#endif
		return 0;
	}
	
	#ifdef ALLEGRO_DOS
	allegro_message("Installing keyboard...\n");
	#endif
	if(install_keyboard() != 0){
		allegro_message("Error: install_keyboard() has failed!");
		allegro_exit();
		return 0;
	}
	
	#ifdef ALLEGRO_DOS
	allegro_message("Installing timer...\n");
	#endif
	if(install_timer() != 0){
		allegro_message("Error: install_timer() has failed!");
		allegro_exit();
		return 0;
	}
	
	#ifndef ALLEGRO_DOS
	if(nlInit() == NL_FALSE){
		allegro_message("Error: nlInit() has failed!");
		allegro_exit();
		return 0;
	}
	nlSelectNetwork(NL_IP);
	#endif
	
	#ifdef ENABLE_MOUSE
	#ifdef ALLEGRO_DOS
	allegro_message("Installing mouse...\n");
	#endif
	if(install_mouse() < 0){	// positive numbers and zero both pass
		allegro_message("Warning: install_mouse() has failed!");
	}
	#endif
	
	
	
	p = &player[0];
	
	
	
	// Just to be safe //
	for(i=0; i<4; i++){
		LayerB[i] = 0;
		LayerL[i] = 0;
		LayerH[i] = 0;
		LayerSH[i] = 0;
	}
	LayerM = 0;
	
	
	
	// Fix for Mac port (must create window before using dialog boxes)
	#ifndef ALLEGRO_DOS
	set_color_depth(16);
	screen_resolution_x = 320;
	screen_resolution_y = 224;
	screen_padding_x = 0;
	screen_padding_y = 16;
	screen_double_x = 0;
	screen_double_y = 0;
	full_screen = 0;
	screen_offset_x = screen_padding_x >> 1;
	screen_offset_y = screen_padding_y >> 1;
	SetGfxModeWindow();
	#endif
	
	
	
	#ifdef ALLEGRO_DOS
	system("CLS");
	allegro_message("\nSelect number of players:\n\n"
					"Press 1 for single player\n"
					"Press 2 for two players\n"
					"Press 3 for three players\n"
					"Press 4 for four players\n");
	#else
	allegro_message("Click OK, and then select number of players:\n\n"
					"Press 1 for single player\n"
					"Press 2 for two players\n"
					"Press 3 for three players\n"
					"Press 4 for four players\n");
	#endif
	
	while(!key[KEY_1] && !key[KEY_2] && !key[KEY_3] && !key[KEY_4]);
	if(key[KEY_1])	num_of_frames = 1;
	if(key[KEY_2])	num_of_frames = 2;
	if(key[KEY_3])	num_of_frames = 3;
	if(key[KEY_4])	num_of_frames = 4;
	
	
	
	set_config_file("setup.cfg");
	
	strcpy(config_section[0], "1_player");
	strcpy(config_section[1], "2_player");
	strcpy(config_section[2], "3_player");
	strcpy(config_section[3], "4_player");
	
	#ifdef ALLEGRO_DOS
	allegro_message("\nReading configuration file...\n");
	#endif
	
	#ifdef ALLEGRO_LINUX
	// We want it to default to "DIGI_ESD" on Linux due to issues with PulseAudio
	digi_card = get_config_int(config_section[num_of_frames-1], "digi_card", DIGI_ESD);
	#else
	digi_card = get_config_int(config_section[num_of_frames-1], "digi_card", DIGI_AUTODETECT);
	#endif
	
	audio_volume = get_config_int(config_section[num_of_frames-1], "audio_volume", 0x1F);
	smart_mix = get_config_int(config_section[num_of_frames-1], "smart_mix", 1);
	vsync_enable = get_config_int(config_section[num_of_frames-1], "vsync_enable", 0);
	frame_skip = get_config_int(config_section[num_of_frames-1], "frame_skip", 0);
	screen_resolution_x = get_config_int(config_section[num_of_frames-1], "screen_resolution_x", 320);
	screen_resolution_y = get_config_int(config_section[num_of_frames-1], "screen_resolution_y", 224);
	screen_padding_x = get_config_int(config_section[num_of_frames-1], "screen_padding_x", 0);
	screen_padding_y = get_config_int(config_section[num_of_frames-1], "screen_padding_y", 16);
	screen_double_x = get_config_int(config_section[num_of_frames-1], "screen_double_x", 0);
	screen_double_y = get_config_int(config_section[num_of_frames-1], "screen_double_y", 0);
	full_screen = get_config_int(config_section[num_of_frames-1], "full_screen", 0);
	key_bind[0].bind_up = get_config_int(config_section[0], "bind_up", 84);
	key_bind[0].bind_down = get_config_int(config_section[0], "bind_down", 85);
	key_bind[0].bind_left = get_config_int(config_section[0], "bind_left", 82);
	key_bind[0].bind_right = get_config_int(config_section[0], "bind_right", 83);
	key_bind[0].bind_b = get_config_int(config_section[0], "bind_b", 19);
	key_bind[0].bind_c = get_config_int(config_section[0], "bind_c", 4);
	key_bind[0].bind_a = get_config_int(config_section[0], "bind_a", 1);
	key_bind[0].bind_start = get_config_int(config_section[0], "bind_start", 67);
	for(i=1; i < 4; i++){
		key_bind[i].bind_up = get_config_int(config_section[i], "bind_up", 0);
		key_bind[i].bind_down = get_config_int(config_section[i], "bind_down", 0);
		key_bind[i].bind_left = get_config_int(config_section[i], "bind_left", 0);
		key_bind[i].bind_right = get_config_int(config_section[i], "bind_right", 0);
		key_bind[i].bind_b = get_config_int(config_section[i], "bind_b", 0);
		key_bind[i].bind_c = get_config_int(config_section[i], "bind_c", 0);
		key_bind[i].bind_a = get_config_int(config_section[i], "bind_a", 0);
		key_bind[i].bind_start = get_config_int(config_section[i], "bind_start", 0);
	}
	
	
	
	// Optional //
	#ifdef ALLEGRO_DOS
	allegro_message("Installing sound...\n");
	#endif
	if(install_sound(digi_card, MIDI_NONE, NULL) != 0){
		allegro_message("Warning: install_sound() has failed!");
		#ifdef ALLEGRO_DOS
		allegro_message("\n");
		#endif
	}
	set_volume(255, 0);
	set_volume_per_voice(0);
	audio_compression = 0x10;	// 1:1 ratio
	//audio_volume = 0x1F;
	
	
	
	// Initialize network variables
	#ifndef ALLEGRO_DOS
	sock_server = NL_INVALID;
	sock_client = NL_INVALID;
	socket[0] = NL_INVALID;
	socket[1] = NL_INVALID;
	socket[2] = NL_INVALID;
	socket[3] = NL_INVALID;
	prosonicnet = NL_INVALID;
	port = 727;
	#endif
	num_of_messages = 4;
	message_time_limit = 10 * 60;	// 10 seconds
	message = (char *)malloc(64 * (num_of_messages+1));		// +1 for overhead
	message_time = (short *)malloc(2 * (num_of_messages+1));
	for(i=0; i <= num_of_messages; i++)
		message_time[i] = 0;
	
	#ifndef ALLEGRO_DOS
	/*if(num_of_frames > 1){
		allegro_message("Select multiplayer mode:\n\n"
						"Press 0 for offline\n"
						"Press 1 for netplay (server)\n"
						"Press 2 for netplay (client)\n");
		while(!key[KEY_0] && !key[KEY_1] && !key[KEY_2]);
		if(key[KEY_0]){ net_type = NET_TYPE_OFFLINE; }
		else{
			if(key[KEY_1]){ net_type = NET_TYPE_SERVER; serverside_setup(); }
			if(key[KEY_2]){	net_type = NET_TYPE_CLIENT; clientside_setup(); }
		}
	}*/
	#endif
	
	
	
	fm_offset = 0;
	fm_size = 0;
	fm_data = 0;
	
	fm_data_pos[0] = 0;
	fm_data_pos[1] = 0;
	fm_data_pos[2] = 0;
	fm_data_pos[3] = 0;
	
	LoadFM(0x00, "", 0, 0, 0);
	LoadFM(0x01, "ehz.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_ON);
	LoadFM(0x02, "mcz2.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_ON);
	LoadFM(0x03, "launch.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_ON);
	LoadFM(0x04, "crystegg.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_ON);
	LoadFM(0x1A, "endofact.vgz", FM_TYPE_VGM, FM_DAC_ON, FM_REPEAT_OFF);
	LoadFM(0x20, "jump.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x21, "starpost.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x23, "pain.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x24, "brake.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x3C, "blastoff.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x3E, "spin.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x4C, "spring.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	LoadFM(0x4F, "sign1.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF); // signpost spin
	LoadFM(0x53, "starpost.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF); // signpost spin (2P)
	LoadFM(0x60, "spindash.vgz", FM_TYPE_VGM, FM_DAC_OFF, FM_REPEAT_OFF);
	
	
	
	pcm_offset = 0;
	pcm_size = 0;
	pcm_data = 0;
	
	pcm_data_pos[0][0] = 0;
	pcm_data_pos[0][1] = 0;
	pcm_data_pos[0][2] = 0;
	pcm_data_pos[0][3] = 0;
	pcm_data_pos[1][0] = 0;
	pcm_data_pos[1][1] = 0;
	pcm_data_pos[1][2] = 0;
	pcm_data_pos[1][3] = 0;
	
	LoadPCM(0x00, "", 0, 0);
	LoadPCM(0x35, "ring.wav", PCM_TYPE_MONO, PCM_REPEAT_OFF);
	
	
	
	#ifdef ALLEGRO_DOS
	allegro_message("Initializing YM2612...\n");
	#endif
	YM2612_Init(7670453, 44100, 1 );
	#ifdef ALLEGRO_DOS
	allegro_message("Initializing PSG...\n");
	#endif
	PSG_Init(3579545, 44100);
	stream = play_audio_stream(735, 16, 1, 44100, 255, 128);
	if(!stream) quit(0);
	BackupYM2612(0);
	BackupYM2612(1);
	BackupYM2612(2);
	BackupYM2612(3);
	BackupPSG(0);
	BackupPSG(1);
	BackupPSG(2);
	BackupPSG(3);
	
	mix_volume = 0;
	
	#ifdef AUDIO_REC_ENABLED
		pcm_out = fopen("output.wav", "wb");
		i = 0x46464952;		// "RIFF"
		fwrite(&i, 4, 1, pcm_out);
		i = 0;				// reserved for (size - 0x8)
		fwrite(&i, 4, 1, pcm_out);
		i = 0x45564157;		// "WAVE"
		fwrite(&i, 4, 1, pcm_out);
		i = 0x20746D66;		// "fmt "
		fwrite(&i, 4, 1, pcm_out);
		i = 0x10;
		fwrite(&i, 4, 1, pcm_out);
		i = 0x20001;
		fwrite(&i, 4, 1, pcm_out);
		i = 0xAC44;
		fwrite(&i, 4, 1, pcm_out);
		i = 0x2B110;
		fwrite(&i, 4, 1, pcm_out);
		i = 0x100004;
		fwrite(&i, 4, 1, pcm_out);
		i = 0x61746164;		// "data"
		fwrite(&i, 4, 1, pcm_out);
		i = 0;				// reserved for (size - 0x2C)
		fwrite(&i, 4, 1, pcm_out);
	#endif
	
	
	
	LoadFont0();
	
	InitLevelSelect();
	
	SpriteTable = 0;	// Needed to prevent memory leaks
	SpriteTableSize = 0;
	
	LoadSprite(0x101, "url.psf");
	LoadSprite(0x102, "panel.psf");
	LoadSprite(0x103, "tcard.psf");
	LoadSprite(0x104, "font1.psf");
	LoadSprite(0x105, "hud.psf");
	
	LoadSprite(0x00, "sonic.psf");
	LoadSprite(0x11, "sonic.psf");
	LoadSprite(0x25, "ring.psf");
	
	LoadScripts();
	
	
	
	for(i=0; i < 10; i++){
		strcpy(demo_file_list[i], "demo");
		demo_file_list[i][4] = i + 0x30;
		strcpy(&demo_file_list[i][5], ".psd");
		#ifdef ENABLE_SRAM_LOG_ANALYSIS
			strcpy(sram_file_list[i], "demo");
			sram_file_list[i][4] = i + 0x30;
			strcpy(&sram_file_list[i][5], ".srm");
		#endif
	}
	demo_number = 0;
	demo_mode = 0;
	DemoPosition = 0;
	DemoFile = NULL;
	DemoFadeDelay = 3;	// "3" for Sonic 1-2, "1" for Sonic 3
	
	
	
	frame = 1;
	
	ticCounter60L = 0;	// Logic
	ticCounterR = 0;	// Real-time
	ticCounter60R = 0;	// Real-time
	ticUpdated = 0;
	LOCK_VARIABLE(ticUpdated);
	LOCK_VARIABLE(ticCounterR);
	LOCK_VARIABLE(ticCounter60R);
	LOCK_VARIABLE(ticCounter60L);
	LOCK_VARIABLE(fps);
	LOCK_VARIABLE(waitCounter);
	LOCK_FUNCTION(TicUpdate);
	install_int_ex(&TicUpdate, BPS_TO_TIMER(60));
	
	
	
	set_color_depth(16);
	screen_offset_x = screen_padding_x >> 1;
	screen_offset_y = screen_padding_y >> 1;
	if(full_screen)
		SetGfxModeFull();
	else
		SetGfxModeWindow();
	
	#ifdef ENABLE_MOUSE
		show_mouse(screen);
	#endif
	
	
	
	close_button_pressed = FALSE;
	#ifndef ALLEGRO_DOS
	LOCK_FUNCTION(close_button_handler);
	set_close_button_callback(close_button_handler);
	#endif
	
	//LOCK_VARIABLE(fm_data_pos);
	//LOCK_FUNCTION(StreamFM);
	//LOCK_FUNCTION(ProcessSound);
	//install_int_ex(&ProcessSound, BPS_TO_TIMER(60));
	
	
	
	fade_count = -96;
	titlecard_counter[0] = 0xFF;	// required to start TitleCard()
	
	for(i=0; i < 256; i++){
		strcpy(zone_file_list[i], "zone");
		zone_file_list[i][4] = (i / 100) + 0x30;
		zone_file_list[i][5] = ((i / 10) % 10) + 0x30;
		zone_file_list[i][6] = (i % 10) + 0x30;
		strcpy(&zone_file_list[i][7], ".pzf");
	}
	
	CreateZone(&z);
	
	zone = 0;
	act = 0;
	stage = 0;
	
	Level_Inactive_flag = 1;
	LoadZone(&z, zone_file_list[zone]);
	InitDraw(&z);
	AllocateObjects(&z);
	CreateBlockLayout(&z);
	
	strcpy(TextBuffer, "ProSonic - ");
	strcat(TextBuffer, (&z)->LevelName1);
	strcat(TextBuffer, " ZONE");
	set_window_title(TextBuffer);
	
	
	
	// SET UP THE CELL DATA
	num_of_cells = z.FILTERCOMPOSITE[0];
	num_of_filters = z.FILTERCOMPOSITE[1];
	cells_ptr = *(short *)&z.FILTERCOMPOSITE[2];
	filters_ptr = *(short *)&z.FILTERCOMPOSITE[4];
	bg_rgb_color = *(short *)&z.FILTERCOMPOSITE[6];
	filter_a = z.FILTERCOMPOSITE[8];
	filter_b = z.FILTERCOMPOSITE[9];
	filter_a_speed = z.FILTERCOMPOSITE[10];
	filter_b_speed = z.FILTERCOMPOSITE[11];
	
	cell_data = (CELL*)(&z.FILTERCOMPOSITE[cells_ptr]);
	
	
	
	for(i=0; i < 4; i++){
		player[i].status = 0;
		player[i].layer = 0x10;
		player[i].Life_count = 3;
		
		player[i].x_pos = z.Act[act].Stage[stage].StartX[0];
		player[i].y_pos = z.Act[act].Stage[stage].StartY[0];
		player[i].Camera_X_pos = player[i].x_pos - (screen_resolution_x >> 1);
		player[i].Camera_Y_pos = player[i].y_pos - (screen_resolution_y >> 1);
		player[i].Camera_Min_X_pos = z.Act[act].Stage[stage].LevelXL;
		player[i].Camera_Max_X_pos = z.Act[act].Stage[stage].LevelXR;
		player[i].Camera_Min_Y_pos = z.Act[act].Stage[stage].LevelYT;
		player[i].Camera_Max_Y_pos = z.Act[act].Stage[stage].LevelYB;
		player[i].Camera_Max_Y_pos_now = z.Act[act].Stage[stage].LevelYB;
		player[i].Camera_Y_pos_bias = 0x60;
	}
	
	water_current_height = 0x24C00;
	water_direction = 1;
	water_accel = 0;
	z.Act[act].Stage[stage].WaterEnable = 0;
	
	counter_fe74 = 0x80;
	counter_fe74_direction = 1;
	counter_fe74_accel = 0;
	
	
	
	while(!key[KEY_ESC] && !close_button_pressed){
		if(demo_number >= 0){
			if(demo_mode == 0){
				demo_mode = 1;
				DemoFile = NULL;
				for(i=0; i < 10; i++){	// search for next existing demo file
					n = PlayDemo(demo_number);
					if(n == 0)
						i = 9;
					else if(n == -1 && i == 9)
						quit(ERROR_NO_DEMO_FILES);
					else{
						demo_number++;
						demo_number %= 10;
					}
				}
			}
		}
		for(frame=1; frame <= num_of_frames; frame++){
			p = &player[frame-1];
			if(show_menu == 0){
				if(demo_mode != 1 && titlecard_sequence_end){
					p->CtrlInput = 0;
					if(!typing){
						if(key[key_bind[frame-1].bind_up])		p->CtrlInput |= 1;
						if(key[key_bind[frame-1].bind_down])	p->CtrlInput |= 2;
						if(key[key_bind[frame-1].bind_left])	p->CtrlInput |= 4;
						if(key[key_bind[frame-1].bind_right])	p->CtrlInput |= 8;
						
						if(key[key_bind[frame-1].bind_b])		p->CtrlInput |= 0x10;
						if(key[key_bind[frame-1].bind_c])		p->CtrlInput |= 0x20;
						if(key[key_bind[frame-1].bind_a])		p->CtrlInput |= 0x40;
						
						if(key[key_bind[frame-1].bind_start])	p->CtrlInput |= 0x80;
					}
				}
				else
					p->CtrlInput = 0;
				
				if(demo_mode == 1)
					PlayDemo(demo_number);
				else if(demo_mode > 1)
					RecordDemo(9);
				
				#ifdef ENABLE_FRAME_CONTROL
					while(key[KEY_D]);
					while(!key[KEY_S] && !key[KEY_D]);
				#endif
				
				
				
				if(demo_mode == 1 && key[KEY_ENTER]){
					DemoPosition = 0;
					demo_mode = 0;
					if(demodata)
						free(demodata);
					demodata = 0;
					demo_size = 0;
					demo_number = -1;
					zone = 0;
					act = 0;
					stage = 0;
					Level_Inactive_flag = 1;
				}
				
				
				
				p->Ctrl_Press = 0;
				if(p->CtrlInput & 1){
					if((p->Ctrl_Held & 1) == 0)
						p->Ctrl_Press |= 1;
					p->Ctrl_Held |= 1;
				}
				else
					p->Ctrl_Held &= 0xFE;
				
				if(p->CtrlInput & 2){
					if((p->Ctrl_Held & 2) == 0)
						p->Ctrl_Press |= 2;
					p->Ctrl_Held |= 2;
				}
				else
					p->Ctrl_Held &= 0xFD;
				
				if(p->CtrlInput & 4){
					if((p->Ctrl_Held & 4) == 0)
						p->Ctrl_Press |= 4;
					p->Ctrl_Held |= 4;
				}
				else
					p->Ctrl_Held &= 0xFB;
				
				if(p->CtrlInput & 8){
					if((p->Ctrl_Held & 8) == 0)
						p->Ctrl_Press |= 8;
					p->Ctrl_Held |= 8;
				}
				else
					p->Ctrl_Held &= 0xF7;
				
				if(p->CtrlInput & 0x40){
					if((p->Ctrl_Held & 0x40) == 0)
						p->Ctrl_Press |= 0x40;
					p->Ctrl_Held |= 0x40;
				}
				else
					p->Ctrl_Held &= 0xBF;
				
				if(p->CtrlInput & 0x10){
					if((p->Ctrl_Held & 0x10) == 0)
						p->Ctrl_Press |= 0x10;
					p->Ctrl_Held |= 0x10;
				}
				else
					p->Ctrl_Held &= 0xEF;
				
				if(p->CtrlInput & 0x20){
					if((p->Ctrl_Held & 0x20) == 0)
						p->Ctrl_Press |= 0x20;
					p->Ctrl_Held |= 0x20;
				}
				else
					p->Ctrl_Held &= 0xDF;
				
				if(p->CtrlInput & 0x80){
					if((p->Ctrl_Held & 0x80) == 0)
						p->Ctrl_Press |= 0x80;
					p->Ctrl_Held |= 0x80;
				}
				else
					p->Ctrl_Held &= 0x7F;
				
				
			
				/*if(key[KEY_0])		LoadSavestate("sonic2.gs0");
				else if(key[KEY_1])	LoadSavestate("sonic2.gs1");
				else if(key[KEY_2])	LoadSavestate("sonic2.gs2");
				else if(key[KEY_3])	LoadSavestate("sonic2.gs3");
				else if(key[KEY_4])	LoadSavestate("sonic2.gs4");
				else if(key[KEY_5])	LoadSavestate("sonic2.gs5");
				else if(key[KEY_6])	LoadSavestate("sonic2.gs6");
				else if(key[KEY_7])	LoadSavestate("sonic2.gs7");
				else if(key[KEY_8])	LoadSavestate("sonic2.gs8");
				else if(key[KEY_9])	LoadSavestate("sonic2.gs9");*/
			}
			
			
			if(!typing){
				if(key[KEY_M]){
					if(key_m_previously_pressed == 0){
						show_menu ^= 1;
						key_m_previously_pressed = 1;
					}
				}
				else
					key_m_previously_pressed = 0;
				
				
				if(key[KEY_N]){
					if(key_n_previously_pressed == 0){
						debug_show_object_nodes ^= 1;
						key_n_previously_pressed = 1;
					}
				}
				else
					key_n_previously_pressed = 0;
			}
			
			
			if(frame == 1){
				if(prompt){
					LoadZonePrompt();
					prompt = 0;
				}
				
				if(!typing){
					if(key[KEY_SPACE]){
						edit_mode++;
						edit_mode %= 5;
						#ifdef ENABLE_MOUSE
							mouse_z_previous = mouse_z;
						#endif
						rest(200);
					}
					
					if(key[KEY_0]){
						rest(200);
						show_fps ^= 1;
					}
					if(key[KEY_9]){
						rest(200);
						vsync_enable ^= 1;
					}
				}
			}
			
			
			
			if(frame == 1){
				while(ticUpdated == 0);		// wait until 'ticUpdated' it 1
				ticUpdated = 0;
				draw_frame = 0;
			}
			
			if(ticCounterR % (frame_skip+1) == 0){
				draw_frame = 1;
				clear_to_color(LayerH[frame-1], MASK_COLOR_16);
				clear_to_color(LayerSH[frame-1], MASK_COLOR_16);
				if(num_of_frames > 1 && frame == 1) clear(LayerM);
			}
			
			if(Pause() == 0){
				if(camera_mode){
					if(p->Ctrl_Held & 1){
						p->Camera_Y_pos -= 8;
						if(key[KEY_LSHIFT] || key[KEY_RSHIFT])
							p->Camera_Y_pos -= 8;
					}
					else if(p->Ctrl_Held & 2){
						p->Camera_Y_pos += 8;
						if(key[KEY_LSHIFT] || key[KEY_RSHIFT])
							p->Camera_Y_pos += 8;
					}
					
					if(p->Ctrl_Held & 4){
						p->Camera_X_pos -= 8;
						if(key[KEY_LSHIFT] || key[KEY_RSHIFT])
							p->Camera_X_pos -= 8;
					}
					else if(p->Ctrl_Held & 8){
						p->Camera_X_pos += 8;
						if(key[KEY_LSHIFT] || key[KEY_RSHIFT])
							p->Camera_X_pos += 8;
					}
					
					if(p->Camera_X_pos < 0)
						p->Camera_X_pos = 0;
					if(p->Camera_Y_pos < 0)
						p->Camera_Y_pos = 0;
					
					#ifdef ENABLE_MOUSE
						player[0].x_pos = player[0].Camera_X_pos + ((mouse_x << screen_scale_x) >> screen_double_x);
						player[0].y_pos = player[0].Camera_Y_pos + ((mouse_y << screen_scale_y) >> screen_double_y);
					#endif
				}
				else{
					if(Update_HUD_timer == 1){
						if(Timer_minute == 9 && Timer_second == 59 && Timer_millisecond == 59){
							if(!Time_Over_flag){
								KillCharacter();
								Time_Over_flag = 1;
							}
						}
						else if(frame == 1){
							Timer_millisecond++;
							if(Timer_millisecond == 60){
								Timer_millisecond = 0;
								Timer_second++;
								if(Timer_second == 60){
									Timer_second = 0;
									Timer_minute++;
								}
							}
						}
					}
					else{
						if(p->anim == 5)
							p->anim_frame = 0;
					}
				}
				
				#ifdef ENABLE_SRAM_LOG_ANALYSIS
				if(frame == 1 && DemoPosition > 6 && /*ticCounterL >= sram_start && */titlecard_sequence_end)
					AnalyzeSRAM();
				#endif
				
				#ifdef ENABLE_LOGGING
					ResetLog();
				#endif
				
				RunFunction(1);
				
				if(!camera_mode)
					Obj01();
				
				if(!titlecard_sequence_end){
					p->x_vel = 0;
					p->y_vel = 0;
				}
				
				#ifdef ENABLE_LOGGING
					#ifndef ENABLE_SRAM_LOG_ANALYSIS
						DumpLog("obj01.log");
					#endif
				#endif
			}
			
			#ifndef ALLEGRO_DOS
			if(frame == 1){
				if(net_type > 0){
					if(net_type == 1)
						ManageNetwork();
					else{
						n = 0xFF;
						i = 0;
						while(i != 1){
							i = nlWrite(prosonicnet, &n, 1);		// player ready
							if(key[KEY_ESC])
								quit(0);
						}
						i = 0;
						while(i != 1){
							i = nlWrite(prosonicnet, &net_id[0], 1);	// who sent it
							if(key[KEY_ESC])
								quit(0);
						}
						//allegro_message("client sent data:\ni = %i\nnet_id[0] = %i", i, net_id[0]);
					}
					GetNetworkData();
				}
			}
			#endif
			
			if(p->Ctrl_Press & 0x10){
				camera_mode ^= 1;
				
				if(camera_mode){
					p->status = 0;
					p->routine = 0;
					p->layer = 0x10;
					p->x_pos_pre = 0;
					p->y_pos_pre = 0;
					p->x_vel = 0;
					p->y_vel = 0;
					p->inertia = 0;
					p->anim = 0;
					p->anim_frame = 0;
					p->anim_frame_duration = 0;
					p->next_anim = 0;
					p->Camera_Y_pos_bias = 0x60;
				}
			}
			
			if(camera_mode && frame == 1){
				p->x_pos = 0xFFFF;
				p->y_pos = 0xFFFF;
			}
			
			ManageObjectNodes(&z);
			
			RunFunction(2);
			
			if(draw_frame){
				DrawBackArea(LayerB[frame-1]);
				DrawPlayArea(LayerL[frame-1], LayerH[frame-1]);
			}
			
			RunFunction(3);
			
			if(show_fps)	DrawCounter10(LayerSH[frame-1], fps, 8, 0, 2, 1);
			
			if(titlecard_sequence_end){
				#ifndef ALLEGRO_DOS
				if(frame == 1){
					Talk();
				}
				#endif
				
				if(show_version){
					#ifdef ALLEGRO_LINUX
					DrawText(screen_resolution_x - (26 << 3), screen_resolution_y - 32, 0x10, "PROGRAMMED BY DAMIAN GROVE");
					DrawText(screen_resolution_x - (27 << 3), screen_resolution_y - 20, 0x10, "LINUX PORT BY KING INUYASHA");
					#else
						#ifdef ALLEGRO_MACOSX
						DrawText(screen_resolution_x - (26 << 3), screen_resolution_y - 32, 0x10, "PROGRAMMED BY DAMIAN GROVE");
						DrawText(screen_resolution_x - (24 << 3), screen_resolution_y - 20, 0x10, "MAC PORT BY CYBERKITSUNE");
						#else
							DrawText(screen_resolution_x - (26 << 3), screen_resolution_y - 20, 0x10, "PROGRAMMED BY DAMIAN GROVE");
						#endif
					#endif
					
					DrawText(screen_resolution_x - (11 << 3), screen_resolution_y - 8, 0x10, __DATE__);
				}
				
				if(edit_mode == 0){
					// SCORE
					DrawSprite(&z, LayerSH[frame-1], 0x105, 0x01, NORMAL, 0, 0x10+16, 0x08);
					DrawSprite(&z, LayerSH[frame-1], 0x105, 0x08, NORMAL, 0, 0x68+16, 0x08);
					//DrawCounter10(LayerSH[frame-1], p->Score, 0x60, 0x08, 0, 0);
					
					// TIME
					if(Timer_minute == 9){
						if(((Timer_second * 60) + Timer_millisecond) & 8)
							DrawSprite(&z, LayerSH[frame-1], 0x105, 0x03, NORMAL, 0, 0x10+16, 0x18);
						else
							DrawSprite(&z, LayerSH[frame-1], 0x105, 0x04, NORMAL, 0, 0x10+16, 0x18);
					}
					else
						DrawSprite(&z, LayerSH[frame-1], 0x105, 0x03, NORMAL, 0, 0x10+16, 0x18);
					DrawSprite(&z, LayerSH[frame-1], 0x105, 0x07, NORMAL, 0, 0x40+16, 0x18);
					//DrawCounter10(LayerSH[frame-1], Timer_minute, 0x38, 0x18, 1, 0);
					//DrawCounter10(LayerSH[frame-1], Timer_second, 0x50, 0x18, 2, 0);
					
					// RINGS
					if(p->Ring_count <= 0){
						if(((Timer_second * 60) + Timer_millisecond) & 8)
							DrawSprite(&z, LayerSH[frame-1], 0x105, 0x05, NORMAL, 0, 0x10+16, 0x28);
						else
							DrawSprite(&z, LayerSH[frame-1], 0x105, 0x06, NORMAL, 0, 0x10+16, 0x28);
					}
					else
						DrawSprite(&z, LayerSH[frame-1], 0x105, 0x05, NORMAL, 0, 0x10+16, 0x28);
					//DrawCounter10(LayerSH[frame-1], p->Ring_count, 0x50, 0x28, 1, 0);
					
					// LIVES
					DrawSprite(&z, LayerSH[frame-1], 0x105, 0x1C, NORMAL, 0, 0x10+16, screen_resolution_y - 24);
					//DrawCounter10(LayerSH[frame-1], p->Life_count, 0x38, screen_resolution_y - 16, 1, 1);
				}
				
				if(debug_show_object_nodes > 0){
					number_of_objects_in_memory = 0;
					for(i=0; i < (MAX_BUFFER_OBJECTS >> 3); i++){
						if(ObjectNodes[0+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[1+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[2+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[3+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[4+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[5+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[6+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;
						if(ObjectNodes[7+(i<<3)] < MAX_LEVEL_OBJECTS)
							number_of_objects_in_memory++;					
					}
					DrawCounter16(LayerH[frame-1], number_of_objects_in_memory, 0, screen_resolution_y-MAX_BUFFER_OBJECTS-16);
					
					for(i=0; i < (MAX_BUFFER_OBJECTS >> 3); i++){
						DrawCounter16(LayerH[frame-1], ObjectNodes[0+(i<<3)], 0, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[1+(i<<3)], 40, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[2+(i<<3)], 80, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[3+(i<<3)], 120, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[4+(i<<3)], 160, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[5+(i<<3)], 200, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[6+(i<<3)], 240, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
						DrawCounter16(LayerH[frame-1], ObjectNodes[7+(i<<3)], 280, screen_resolution_y-MAX_BUFFER_OBJECTS+(i<<3));
					}
				}
				
				// Draw URL in the lower-right portion of the screen
				if(show_watermark)
					DrawSprite(&z, LayerL[frame-1], 0x101, 0, NORMAL, 24, screen_resolution_x-147, screen_resolution_y-24);
				
				if(show_menu > 0)
					Menu();
				
				#ifdef ENABLE_MOUSE
					ShowBlockInfo(&z);
				#endif
			}
			
			
			
			#ifndef ENABLE_FRAME_CONTROL
			if(digi_card != DIGI_NONE){
				if(frame == num_of_frames)
					ProcessSound();
			}
			#endif
			
			
			
			if(num_of_frames > 1){
				switch(frame){
					case 1:
						if(num_of_frames == 2){
							blit(LayerB[frame-1], LayerM, 16, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerL[frame-1], LayerM, 16, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerH[frame-1], LayerM, 16, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerSH[frame-1], LayerM, 16, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
						}
						else{
							blit(LayerB[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerL[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerH[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerSH[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						}
						break;
					case 2:
						if(num_of_frames == 2){
							blit(LayerB[frame-1], LayerM, 16, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerL[frame-1], LayerM, 16, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerH[frame-1], LayerM, 16, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerSH[frame-1], LayerM, 16, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), screen_buffer_x-32, screen_buffer_y >> 1 << screen_double_y);
						}
						else{
							blit(LayerB[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerL[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerH[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
							masked_blit(LayerSH[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), screen_offset_y >> 1 << screen_double_y, (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						}
						break;
					case 3:
						blit(LayerB[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerL[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerH[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerSH[frame-1], LayerM, 8 << screen_double_x, 0, screen_offset_x+16, (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						break;
					case 4:
						blit(LayerB[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerL[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerH[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
						masked_blit(LayerSH[frame-1], LayerM, 8 << screen_double_x, 0, -((8 << screen_double_x)-16) + (((screen_resolution_x + screen_padding_x) >> 1) << screen_double_x) + (screen_padding_x >> 2 << screen_double_x) + (8 << screen_double_x), (((screen_resolution_y + screen_padding_y) >> 1) << screen_double_y) + (screen_padding_y >> 2 << screen_double_y), (screen_buffer_x-32) >> 1 << screen_double_x, screen_buffer_y >> 1 << screen_double_y);
				}
				
				if(frame == num_of_frames){
					if(fade_count != 0) FadeRGB(LayerM);
					TitleCard();
					if(vsync_enable)
						vsync();
					#ifndef ALLEGRO_DOS
					acquire_screen();	// used for "set_display_switch_mode()"
					#endif
					if(screen_double_x && num_of_frames == 2)
						stretch_blit(LayerM, screen, 16, 0, screen_resolution_x+screen_padding_x, (screen_resolution_y+screen_padding_y) << screen_double_y, 0, 0, (screen_resolution_x+screen_padding_x) << screen_double_x, (screen_resolution_y+screen_padding_y) << screen_double_y);
					else
						blit(LayerM, screen, 16, 0, 0, 0, (screen_resolution_x+screen_padding_x) << screen_double_x, (screen_resolution_y+screen_padding_y) << screen_double_y);
					#ifndef ALLEGRO_DOS
					release_screen();	// used for "set_display_switch_mode()"
					#endif
				}
			}
			else{
				masked_blit(LayerL[frame-1], LayerB[frame-1], 16, 0, 16, 0, screen_buffer_x-32, screen_buffer_y);
				masked_blit(LayerH[frame-1], LayerB[frame-1], 16, 0, 16, 0, screen_buffer_x-32, screen_buffer_y);
				masked_blit(LayerSH[frame-1], LayerB[frame-1], 16, 0, 16, 0, screen_buffer_x-32, screen_buffer_y);
				if(fade_count != 0) FadeRGB(LayerB[frame-1]);
				TitleCard();
				if(vsync_enable)
					vsync();
				#ifndef ALLEGRO_DOS
				acquire_screen();	// used for "set_display_switch_mode()"
				#endif
				if(screen_double_x || screen_double_y)
					stretch_blit(LayerB[frame-1], screen, 16, 0, screen_resolution_x, screen_resolution_y, screen_offset_x << screen_double_x, screen_offset_y << screen_double_y, screen_resolution_x << screen_double_x, screen_resolution_y << screen_double_y);
				else
					blit(LayerB[frame-1], screen, 16, 0, screen_offset_x, screen_offset_y, screen_resolution_x, screen_resolution_y);
				#ifndef ALLEGRO_DOS
				release_screen();	// used for "set_display_switch_mode()"
				#endif
			}
			
			
			
			
			
			
			if(frame == 1){		// shouldn't this be "frame == num_of_frames"??
				// Water movement
				if(water_direction == 1 && water_accel == 63){
					water_accel = 63;
					water_direction = 0;
				}
				else if(water_direction == 0 && water_accel == -63){
					water_accel = -63;
					water_direction = 1;
				}
				if(water_direction == 0)
					water_accel--;
				else
					water_accel++;
				water_current_height += water_accel;
				
				
				
				// Counter FE74
				if(counter_fe74_direction == 1 && counter_fe74_accel == 0xB0){
					counter_fe74_accel = 0xB0;
					counter_fe74_direction = 0;
				}
				else if(counter_fe74_direction == 0 && counter_fe74_accel == -0xB0){
					counter_fe74_accel = -0xB0;
					counter_fe74_direction = 1;
				}
				if(counter_fe74_direction == 0)
					counter_fe74_accel -= 8;
				else
					counter_fe74_accel += 8;
				counter_fe74 += counter_fe74_accel;
			}
			
			
			
			
			
			
			
			if(Level_Inactive_flag && frame == num_of_frames){
				if(fade_count == -96){
					if(zone)
						unlisted_zone = 0;
					ResetSound();
					Level_Inactive_flag = 0;
					LoadZone(&z, zone_file_list[zone + (unlisted_zone * 256)]);
					InitDraw(&z);
					AllocateObjects(&z);
					CreateBlockLayout(&z);
					
					strcpy(TextBuffer, "ProSonic - ");
					strcat(TextBuffer, z.LevelName1);
					strcat(TextBuffer, " ZONE");
					set_window_title(TextBuffer);
					
					// SET UP THE CELL DATA
					num_of_cells = z.FILTERCOMPOSITE[0];
					num_of_filters = z.FILTERCOMPOSITE[1];
					cells_ptr = *(short *)&z.FILTERCOMPOSITE[2];
					filters_ptr = *(short *)&z.FILTERCOMPOSITE[4];
					bg_rgb_color = *(short *)&z.FILTERCOMPOSITE[6];
					filter_a = z.FILTERCOMPOSITE[8];
					filter_b = z.FILTERCOMPOSITE[9];
					filter_a_speed = z.FILTERCOMPOSITE[10];
					filter_b_speed = z.FILTERCOMPOSITE[11];
					
					cell_data = (CELL*)(&z.FILTERCOMPOSITE[cells_ptr]);
					
					for(i=0; i < num_of_frames; i++){
						n = player[i].Life_count;
						d0 = player[i].Saved_Last_star_pole_hit;
						d1 = player[i].Saved_x_pos;
						d2 = player[i].Saved_y_pos;
						d3 = player[i].Saved_Ring_count;
						d4 = player[i].Saved_Timer;
						d5 = player[i].Saved_art_tile;
						d6 = player[i].Saved_layer;
						d7 = player[i].Saved_Camera_X_pos;
						a0 = player[i].Saved_Camera_Y_pos;
						a1 = player[i].Saved_Water_Level;
						a2 = player[i].Saved_Extra_life_flags;
						a3 = player[i].Saved_Extra_life_flags_2P;
						a4 = player[i].Saved_Camera_Max_Y_pos;
						
						/*
						player[i] = init_player;
						/*/
						player[i].status = 0;
						player[i].routine = 0;
						player[i].layer = 0x10;
						player[i].x_pos_pre = 0;
						player[i].y_pos_pre = 0;
						player[i].x_vel = 0;
						player[i].y_vel = 0;
						player[i].inertia = 0;
						player[i].anim = 0;
						player[i].anim_frame = 0;
						player[i].anim_frame_duration = 0;
						player[i].next_anim = 0;
						//*/
						
						player[i].Life_count = n;
						player[i].Saved_Last_star_pole_hit = d0;
						player[i].Saved_x_pos = d1;
						player[i].Saved_y_pos = d2;
						player[i].Saved_Ring_count = d3;
						player[i].Saved_Timer = d4;
						player[i].Saved_art_tile = d5;
						player[i].Saved_layer = d6;
						player[i].Saved_Camera_X_pos = d7;
						player[i].Saved_Camera_Y_pos = a0;
						player[i].Saved_Water_Level = a1;
						player[i].Saved_Extra_life_flags = a2;
						player[i].Saved_Extra_life_flags_2P = a3;
						player[i].Saved_Camera_Max_Y_pos = a4;
						
						//if(player[i].Saved_x_pos != 0){
						//	player[i].x_pos = player[i].Saved_x_pos;
						//	player[i].y_pos = player[i].Saved_y_pos;
						//}
						//else{
							player[i].x_pos = z.Act[act].Stage[stage].StartX[0];
							player[i].y_pos = z.Act[act].Stage[stage].StartY[0];
						//}
						
						player[i].Camera_X_pos = player[i].x_pos - (screen_resolution_x >> 1);
						player[i].Camera_Y_pos = player[i].y_pos - (screen_resolution_y >> 1) + 0x10;
						player[i].Camera_Min_X_pos = z.Act[act].Stage[stage].LevelXL;
						player[i].Camera_Max_X_pos = z.Act[act].Stage[stage].LevelXR;
						player[i].Camera_Min_Y_pos = z.Act[act].Stage[stage].LevelYT;
						player[i].Camera_Max_Y_pos = z.Act[act].Stage[stage].LevelYB;
						player[i].Camera_Max_Y_pos_now = z.Act[act].Stage[stage].LevelYB;
						player[i].Camera_Y_pos_bias = 0x60;
						
						player[i].render_flags |= 0x80;
						
						player[i].Ring_count = 0;
					}
					Timer_minute = 0;
					Timer_second = 0;
					Timer_millisecond = 0;
					
					water_accel = 0;
					water_direction = 1;
					counter_fe74_accel = 0;
					counter_fe74_direction = 1;
					counter_fe74 = 0x80;
					
					Game_paused = 0;
					
					titlecard_sequence_end = 0;
					
					RunFunction(4);
				}
				else{
					FadeOutFM(0);
					fade_count -= 4;
				}
			}
			
			
			
			
			
			
			
			if(!Game_paused && frame == num_of_frames){
				ticCounter60L++;
				ticCounterL++;
			}
		}
	}//END KEY_ESC
	
	quit(ERROR_NULL);
	return 0;	// Not really needed, this just stops the compiler from complaining ;)
}
END_OF_MAIN()
