/* $Id: openttd.c 3380 2006-01-07 10:15:46Z truelight $ */

//#include <dirent.h>
//#include <stat.h>
//#include <fcntl.h>
//#pragma fourbyteints on
#include "palmdep.h"
//#include "palm5way.h"
//#include <tapwave.h>
//#include <SoundMgr.h>
//#include "TwKeys.h"
#include "stdafx.h"
#include "string.h"
#include "table/strings.h"
#include "debug.h"
#include "driver.h"
#include "saveload.h"
#include "./strings.h"
#include "map.h"
#include "tile.h"

#define VARDEF
#include "openttd.h"
#include "functions.h"
#include "mixer.h"
#include "spritecache.h"
#include "gfx.h"
#include "gfxinit.h"
#include "gui.h"
#include "station.h"
#include "vehicle.h"
#include "viewport.h"
#include "window.h"
#include "player.h"
#include "command.h"
#include "town.h"
#include "industry.h"
#include "news.h"
#include "engine.h"
#include "sound.h"
#include "economy.h"
#include "fileio.h"
#include "hal.h"
#include "airport.h"
#include "console.h"
#include "screenshot.h"
#include "network.h"
#include "signs.h"
#include "depot.h"
#include "waypoint.h"
#include "ai/ai.h"
// MYCODE EDIT
#include "bridge.h"
#include "bridge_map.h"
#include "station_map.h"
#include "road_map.h"
#include "tunnel_map.h"
#include "town_map.h"
#include "void_map.h"
#include "train.h"

#include "PNOMain.h"

#include <TwKeys.h>
#undef keyBitNavLeft
#define keyBitNavLeft           0x01000000
#undef keyBitNavRight
#define keyBitNavRight          0x02000000
#undef keyBitNavSelect
#define keyBitNavSelect         0x04000000

#define TwGfxMakeDisplayRGB_LittleEndian(_r,_g,_b) \
  ( (((_r) & 0xF8) << 8) | (((_g) & 0xFC) << 3) | (((_b) & 0xF8) >> 3) )

#define TwGfxMakeDisplayRGB(_r,_g,_b) TwGfxMakeDisplayRGB_LittleEndian(_r,_g,_b)

/*
 * This macro converts a RGBColorType structure to a frame buffer
 * compatible display value.
 */
#define TwGfxRGBToDisplayRGB(_rgb) TwGfxMakeDisplayRGB((_rgb).r, (_rgb).g, (_rgb).b)
/*
 * Format information for the rgb565 display buffer
 */
#define twGfxRShift     11
#define twGfxRMask      0xF800
#define twGfxGShift     5
#define twGfxGMask      0x07E0
#define twGfxBShift     0
#define twGfxBMask      0x001F

// for TwMidi.h
//#include <tapwave.h>
typedef struct TwSmfType* TwSmfHandle;

/* Error codes */
#define twMidiErrorBase         (0x7000 + 0x500)
#define twMidiErrorUnavailable          (0 + twMidiErrorBase)
#define twMidiErrorNullPointer          (1 + twMidiErrorBase)
#define twMidiErrorInvalidHandle        (2 + twMidiErrorBase)
#define twMidiErrorAlreadyPlaying       (3 + twMidiErrorBase)
#define twMidiErrorDriverError          (4 + twMidiErrorBase)
#define twMidiErrorInvalidFormat        (5 + twMidiErrorBase)
#define twMidiErrorBadParam             (6 + twMidiErrorBase)
#define twMidiErrorInvalidVolume        (7 + twMidiErrorBase)
#define twMidiErrorAllocFailed          (8 + twMidiErrorBase)

/* Limit names */
#define twMidiLimitMaxSmfHandles        0
#define twMidiLimitMaxMidiHandles       1


#ifdef GPMI
#include <gpmi.h>
#include <gpmi/packages/paths.h>
#endif /* GPMI */

void GenerateWorld(int mode, uint size_x, uint size_y);
void CallLandscapeTick(void);
void IncreaseDate(void);
void DoPaletteAnimations(void);
void MusicLoop(void);
void ResetMusic(void);
void InitializeStations(void);
void DeleteAllPlayerStations(void);
void CallDrawSurfaceToScreen();

extern void SetDifficultyLevel(int mode, GameOptions *gm_opt);
extern void DoStartupNewPlayer(bool is_ai);
extern void ShowOSErrorBox(const char *buf);


//extern void HalGameLoop(void);
FiosItem *_fios_list;
int _fios_num;
int _saveload_mode;

const char _openttd_revision[] = "- PalmTTD R4451 By SpookySoft";

#ifdef WITH_TWAPI
	UInt16 *displaySurfaceP = NULL;
	extern TwGfxType* 	gGfx;
	//TwGfxSurfaceType*	gSurface = NULL;
	TwGfxSurfaceType* 	displaySurface;
#else
	//UInt16 *screenBufferP = NULL;
#endif

byte *_zod_video_mem;
static UInt16 pal[256];
Rect screenrect;
int _num_dirty_rects;
Rect _dirty_rects[MAX_DIRTY_RECTS];

uint32 _pixels_redrawn;
static byte _os_version = 0;

SndStreamRef streamRef = NULL;
typedef struct {uint32 __r9;uint32 __r10;} reg_sys_t;
static reg_sys_t reg_sys;

inline
UInt16 PalmGfxRGBToDisplayRGB(UInt8 r, UInt8 g, UInt8 b)
{
	return (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3));
}
	
/*
// local definition of the emulation state structure
struct EmulStateType 
{
	UInt32 instr;
	UInt32 regData[8];
	UInt32 regAddress[8];
	UInt32 regPC;
};

static const struct EmulStateType	*g_emulStateP = NULL;
static Call68KFuncType	*g_call68KFuncP = NULL;

void InitPACEInterface(
	const void *emulStateP, Call68KFuncType *call68KFuncP)
{
	g_emulStateP = (const struct EmulStateType*)((static const struct EmulStateType*)emulStateP);//const_cast<EmulStateType *>(static_cast<const EmulStateType *>(emulStateP));
	g_call68KFuncP = call68KFuncP;
}
*/

/* TODO: usrerror() for errors which are not of an internal nature but
 * caused by the user, i.e. missing files or fatal configuration errors.
 * Post-0.4.0 since Celestar doesn't want this in SVN before. --pasky */

void CDECL error(const char* s, ...)
{
	va_list va;
	char buf[512];
	va_start(va, s);
	StrVPrintF(buf, s, va);
	va_end(va);

	ShowInfo(buf);

	_exit_game = true;
}

void CDECL ShowInfoF(const char *str, ...)
{
	va_list va;
	char buf[512];
	va_start(va, str);
	StrVPrintF(buf, str, va);
	va_end(va);
	ShowInfo(buf);
}


void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
{
	FILE* in;
	byte *mem;
	size_t len;

	in = fopen(filename, "rb");
		
	if (in == NULL)
	{
ErrDisplay(filename);	
		return NULL;
	}
		
	fseek(in, 0, SEEK_END);
	len = ftell(in);
	fseek(in, 0, SEEK_SET);
	
	if (len > maxsize || (mem = malloc(len + 1)) == NULL) 
	{
ErrDisplay("Error reading file to memory.");	
		fclose(in);
		return NULL;
	}
		
	mem[len] = 0;
		
	if (fread(mem, len, 1, in) != 1) 
	{	
ErrDisplay("Error reading file to memory 2!");	
		fclose(in);
		free(mem);
		return NULL;
	}
	fclose(in);

	*lenp = len;
	return mem;
}

/*
static void showhelp(void)
{
	char buf[4096], *p;
	const DriverClass *dc = _driver_classes;
	const DriverDesc *dd;
	int i;

	p = strecpy(buf,
		"Command line options:\n"
		"  -v drv              = Set video driver (see below)\n"
		"  -s drv              = Set sound driver (see below)\n"
		"  -m drv              = Set music driver (see below)\n"
		"  -r res              = Set resolution (for instance 800x600)\n"
		"  -h                  = Display this help text\n"
		"  -t date             = Set starting date\n"
		"  -d [[fac=]lvl[,...]]= Debug mode\n"
		"  -l lng              = Select Language\n"
		"  -e                  = Start Editor\n"
		"  -g [savegame]       = Start new/save game immediately\n"
		"  -G seed             = Set random seed\n"
		"  -n [ip#player:port] = Start networkgame\n"
		"  -D                  = Start dedicated server\n"
		#if !defined(__MORPHOS__) && !defined(__AMIGA__)
		"  -f                  = Fork into the background (dedicated only)\n"
		#endif
		"  -i                  = Force to use the DOS palette (use this if you see a lot of pink)\n"
		"  -p #player          = Player as #player (deprecated) (network only)\n"
		"  -c config_file      = Use 'config_file' instead of 'openttd.cfg'\n",
		lastof(buf)
	);

	for(i=0; i!=lengthof(_driver_classes); i++,dc++) {
		p += StrPrintF(p, "List of %s drivers:\n", dc->name);
		dd = dc->descs;
		do {
			p += StrPrintF(p, "%10s: %s\n", dd->name, dd->longname);
		} while ((++dd)->name);
	}

	ShowInfo(buf);
}
*/
/*
const char *GetDriverParam(const char * const *parm, const char *name)
{
	const char *p;
	int len = StrLen(name);
	while ((p = *parm++) != NULL) {
		if (!strncmp(p,name,len)) {
			if (p[len] == '=') return p + len + 1;
			if (p[len] == 0)   return p + len;
		}
	}
	return NULL;
}

bool GetDriverParamBool(const char * const *parm, const char *name)
{
	const char *p = GetDriverParam(parm, name);
	return p != NULL;
}

int GetDriverParamInt(const char * const *parm, const char *name, int def)
{
	const char *p = GetDriverParam(parm, name);
	return p != NULL ? atoi(p) : def;
}
*/
/*
typedef struct {
	char *opt;
	int numleft;
	char **argv;
	const char *options;
	char *cont;
} MyGetOptData;

static void MyGetOptInit(MyGetOptData *md, int argc, char **argv, const char *options)
{
	md->cont = NULL;
	md->numleft = argc;
	md->argv = argv;
	md->options = options;
}

static int MyGetOpt(MyGetOptData *md)
{
	char *s,*r,*t;

	s = md->cont;
	if (s != NULL)
		goto md_continue_here;

	for (;;) {
		if (--md->numleft < 0) return -1;

		s = *md->argv++;
		if (*s == '-') {
md_continue_here:;
			s++;
			if (*s != 0) {
				// Found argument, try to locate it in options.
				if (*s == ':' || (r = strchr(md->options, *s)) == NULL) {
					// ERROR!
					return -2;
				}
				if (r[1] == ':') {
					// Item wants an argument. Check if the argument follows, or if it comes as a separate arg.
					if (!*(t = s + 1)) {
						// It comes as a separate arg. Check if out of args?
						if (--md->numleft < 0 || *(t = *md->argv) == '-') {
							// Check if item is optional?
							if (r[2] != ':')
								return -2;
							md->numleft++;
							t = NULL;
						} else {
							md->argv++;
						}
					}
					md->opt = t;
					md->cont = NULL;
					return *s;
				}
				md->opt = NULL;
				md->cont = s;
				return *s;
			}
		} else {
			// This is currently not supported.
			return -2;
		}
	}
}
*/

static void ParseResolution(int res[2], const char* s)
{
	char *t = strchr(s, 'x');
	if (t == NULL) {
		ShowInfoF("Invalid resolution '%s'", s);
		return;
	}

	res[0] = clamp(strtoul(s, NULL, 0), 64, MAX_SCREEN_WIDTH);
	res[1] = clamp(strtoul(t + 1, NULL, 0), 64, MAX_SCREEN_HEIGHT);
}

static void InitializeDynamicVariables(void)
{
	/* Dynamic stuff needs to be initialized somewhere... */
	_station_sort  = NULL;
	_town_sort     = NULL;
	_industry_sort = NULL;
}

static void UnInitializeDynamicVariables(void)
{
	/* Dynamic stuff needs to be free'd somewhere... */
	CleanPool(&_town_pool);
	CleanPool(&_industry_pool);
	CleanPool(&_station_pool);
	CleanPool(&_vehicle_pool);
	CleanPool(&_sign_pool);
	CleanPool(&_order_pool);

	free(_station_sort);
	free(_town_sort);
	free(_industry_sort);
}

static void UnInitializeGame(void)
{
	UnInitWindowSystem();

	free(_config_file);
}

static void LoadIntroGame(void)
{
	char filename[256];

	_game_mode = GM_MENU;
	CLRBITS(_display_opt, DO_TRANS_BUILDINGS); // don't make buildings transparent in intro
	_opt_ptr = &_opt_newgame;

	GfxLoadSprites();
	LoadStringWidthTable();

	// Setup main window
	ResetWindowSystem();
	SetupColorsAndInitialWindow();

	// Generate a world.
	sprintf(filename, "%sopntitle.dat",  _path.data_dir);
	if (SaveOrLoad(filename, SL_LOAD) != SL_OK) {
//#if defined SECOND_DATA_DIR
//		StrPrintF(filename, "%sOPNTITLE.DAT",  _path.second_data_dir);
//		if (SaveOrLoad(filename, SL_LOAD) != SL_OK)
//#endif
			GenerateWorld(GW_EMPTY, 64, 64); // if failed loading, make empty world.
	}

	_pause = 0;
	_local_player = 0;
	MarkWholeScreenDirty();
		
	// MYCODE DEBUG added: ResetMusic();	
}

#if defined(UNIX) && !defined(__MORPHOS__)
extern void DedicatedFork(void);
#endif

unsigned long Run(void)
{
	//MyGetOptData mgo;
	//int i;
	bool network = false;
	char *network_conn = NULL;
	char *language = NULL;
	//const char *optformat;
	char musicdriver[16], sounddriver[16], videodriver[16];
	int resolution[2] = {0,0};
	uint startdate = -1;
	bool dedicated;
	
	// MYCODE EDIT added rng here instead of main()
	// MYCODE NOTE should normally be TimGetTicks but use seconds
	// since UInt32 will overflow
	_random_seeds[0][0] = TimGetTicks();
	_random_seeds[0][1] = _random_seeds[0][0] * 0x1234567;	

	//_random_seeds[0][1] = _random_seeds[0][0] = time(NULL);
	
	musicdriver[0] = sounddriver[0] = videodriver[0] = 0;
	
	_game_mode = GM_MENU;
	_switch_mode = SM_MENU;
	_switch_mode_errorstr = INVALID_STRING_ID;
	_dedicated_forks = false;
	dedicated = false;
	_config_file = NULL;

	DeterminePaths();	

	DeterminePaths();
	CheckExternalFiles();

#ifdef GPMI
	/* Set the debug proc */
	gpmi_debug_proc    = &gpmi_debug_openttd;

	/* Initialize GPMI */
	gpmi_init();

	/* Add our paths so we can find our own packages */
	gpmi_path_append(&gpmi_path_modules, "gpmi/modules");
	gpmi_path_append(&gpmi_path_packages, "gpmi/packages");
#endif /* GPMI */

#ifdef UNIX
	// We must fork here, or we'll end up without some resources we need (like sockets)
	if (_dedicated_forks)
		DedicatedFork();
#endif

	LoadFromConfig();
	CheckConfig();
	LoadFromHighScore();

	// override config?
	if (musicdriver[0]) ttd_strlcpy(_ini_musicdriver, musicdriver, sizeof(_ini_musicdriver));
	if (sounddriver[0]) ttd_strlcpy(_ini_sounddriver, sounddriver, sizeof(_ini_sounddriver));
	if (videodriver[0]) ttd_strlcpy(_ini_videodriver, videodriver, sizeof(_ini_videodriver));
	if (resolution[0]) { _cur_resolution[0] = resolution[0]; _cur_resolution[1] = resolution[1]; }
	if (startdate != (uint)-1) _patches.starting_date = startdate;

	if (_dedicated_forks && !dedicated)
		_dedicated_forks = false;

	// enumerate language files
	InitializeLanguagePacks();

	// initialize screenshot formats
	InitializeScreenshotFormats();

	// initialize airport state machines
	InitializeAirports();

	/* initialize all variables that are allocated dynamically */
	InitializeDynamicVariables();

	/* start the AI */
	AI_Initialize();

	// Sample catalogue
	DEBUG(misc, 1) ("Loading sound effects...");
	MxInitialize(11025);
	SoundInitialize("sample.cat");

	// This must be done early, since functions use the InvalidateWindow* calls
	InitWindowSystem();

	GfxLoadSprites();
	LoadStringWidthTable();

	DEBUG(misc, 1) ("Loading drivers...");
	// Sound Init asm
	__asm 
	{
  		ldr  r0, = reg_sys
	  	add	 r0,r0,r10
	  	str	 r9,[r0]
	  	str	 r10,[r0,#4]
	} 
	LoadDriver(SOUND_DRIVER, _ini_sounddriver);
	LoadDriver(MUSIC_DRIVER, _ini_musicdriver);
	LoadDriver(VIDEO_DRIVER, _ini_videodriver); // load video last, to prevent an empty window while sound and music loads
	_savegame_sort_order = SORT_BY_DATE | SORT_DESCENDING;

#ifdef ENABLE_NETWORK
	// initialize network-core
	NetworkStartUp();
#endif /* ENABLE_NETWORK */

	_opt_ptr = &_opt_newgame;

	/* XXX - ugly hack, if diff_level is 9, it means we got no setting from the config file */
	if (_opt_newgame.diff_level == 9) SetDifficultyLevel(0, &_opt_newgame);

	// MYCODE EDIT no console
	// initialize the ingame console
	//IConsoleInit();
	//InitializeGUI();
	//IConsoleCmdExec("exec scripts/autoexec.scr 0");

	GenerateWorld(GW_EMPTY, 64, 64); // Make the viewport initialization happy

#ifdef ENABLE_NETWORK
	if ((network) && (_network_available)) {
		if (network_conn != NULL) {
			const char *port = NULL;
			const char *player = NULL;
			uint16 rport;

			rport = NETWORK_DEFAULT_PORT;

			ParseConnectionString(&player, &port, network_conn);

			if (player != NULL) _network_playas = atoi(player);
			if (port != NULL) rport = atoi(port);

			LoadIntroGame();
			_switch_mode = SM_NONE;
			NetworkClientConnectGame(network_conn, rport);
		}
	}
#endif /* ENABLE_NETWORK */
	_video_driver->main_loop();

	WaitTillSaved();
//	IConsoleFree();

#ifdef ENABLE_NETWORK
	if (_network_available) {
		// Shut down the network and close any open connections
		NetworkDisconnect();
		NetworkUDPClose();
		NetworkShutDown();
	}
#endif /* ENABLE_NETWORK */

	_video_driver->stop();
	_music_driver->stop();
	_sound_driver->stop();

	SaveToConfig();
	SaveToHighScore();

	// uninitialize airport state machines
	UnInitializeAirports();

	/* uninitialize variables that are allocated dynamic */
	UnInitializeDynamicVariables();

	/* stop the AI */
	AI_Uninitialize();
	
	/* Close all and any open filehandles */
	FioCloseAll();
	UnInitializeGame();

	return 0;
}

static void ShowScreenshotResult(bool b)
{
	if (b) {
		SetDParamStr(0, _screenshot_name);
		ShowErrorMessage(INVALID_STRING_ID, STR_031B_SCREENSHOT_SUCCESSFULLY, 0, 0);
	} else {
		ShowErrorMessage(INVALID_STRING_ID, STR_031C_SCREENSHOT_FAILED, 0, 0);
	}

}

static void MakeNewGame(void)
{
	_game_mode = GM_NORMAL;

	// Copy in game options
	_opt_ptr = &_opt;
	memcpy(_opt_ptr, &_opt_newgame, sizeof(*_opt_ptr));

	GfxLoadSprites();

	// Reinitialize windows
	ResetWindowSystem();
	LoadStringWidthTable();

	SetupColorsAndInitialWindow();

	// Randomize world
	GenerateWorld(GW_NEWGAME, 1<<_patches.map_x, 1<<_patches.map_y);

	// In a dedicated server, the server does not play
	if (_network_dedicated) {
		_local_player = OWNER_SPECTATOR;
	} else {
		// Create a single player
		DoStartupNewPlayer(false);

		_local_player = 0;
		_current_player = _local_player;
		DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE);
	}

	MarkWholeScreenDirty();
}

static void MakeNewEditorWorld(void)
{
	_game_mode = GM_EDITOR;

	// Copy in game options
	_opt_ptr = &_opt;
	memcpy(_opt_ptr, &_opt_newgame, sizeof(GameOptions));

	GfxLoadSprites();

	// Re-init the windowing system
	ResetWindowSystem();

	// Create toolbars
	SetupColorsAndInitialWindow();

	// Startup the game system
	GenerateWorld(GW_EMPTY, 1 << _patches.map_x, 1 << _patches.map_y);

	_local_player = OWNER_NONE;
	MarkWholeScreenDirty();
}

void StartupPlayers(void);
void StartupDisasters(void);

/**
 * Start Scenario starts a new game based on a scenario.
 * Eg 'New Game' --> select a preset scenario
 * This starts a scenario based on your current difficulty settings
 */
static void StartScenario(void)
{
	_game_mode = GM_NORMAL;

	// invalid type
	if (_file_to_saveload.mode == SL_INVALID) {
		printf("Savegame is obsolete or invalid format: %s\n", _file_to_saveload.name);
		ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
		_game_mode = GM_MENU;
		return;
	}

	GfxLoadSprites();

	// Reinitialize windows
	ResetWindowSystem();
	LoadStringWidthTable();

	SetupColorsAndInitialWindow();

	// Load game
	if (SaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode) != SL_OK) {
		LoadIntroGame();
		ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
	}

	_opt_ptr = &_opt;
	memcpy(&_opt_ptr->diff, &_opt_newgame.diff, sizeof(GameDifficulty));
	_opt.diff_level = _opt_newgame.diff_level;

	// Inititalize data
	StartupPlayers();
	StartupEngines();
	StartupDisasters();

	_local_player = 0;
	_current_player = _local_player;
	DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE);

	MarkWholeScreenDirty();
}

bool SafeSaveOrLoad(const char *filename, int mode, int newgm)
{
	byte ogm = _game_mode;
	int r;

	_game_mode = newgm;
	r = SaveOrLoad(filename, mode);
	if (r == SL_REINIT) {
		switch (ogm) {
			case GM_MENU:   LoadIntroGame();      break;
			case GM_EDITOR: MakeNewEditorWorld(); break;
			default:        MakeNewGame();        break;
		}
		return false;
	} else if (r != SL_OK) {
		_game_mode = ogm;
		return false;
	} else
		return true;
}

void SwitchMode(int new_mode)
{
#ifdef ENABLE_NETWORK
	// If we are saving something, the network stays in his current state
	if (new_mode != SM_SAVE) {
		// If the network is active, make it not-active
		if (_networking) {
			if (_network_server && (new_mode == SM_LOAD || new_mode == SM_NEWGAME)) {
				NetworkReboot();
				NetworkUDPClose();
			} else {
				NetworkDisconnect();
				NetworkUDPClose();
			}
		}

		// If we are a server, we restart the server
		if (_is_network_server) {
			// But not if we are going to the menu
			if (new_mode != SM_MENU) {
				NetworkServerStart();
			} else {
				// This client no longer wants to be a network-server
				_is_network_server = false;
			}
		}
	}
#endif /* ENABLE_NETWORK */

	switch (new_mode) {
	case SM_EDITOR: /* Switch to scenario editor */
		MakeNewEditorWorld();
		break;

	case SM_NEWGAME: /* New Game --> 'Random game' */
#ifdef ENABLE_NETWORK
		if (_network_server)
			snprintf(_network_game_info.map_name, NETWORK_NAME_LENGTH, "Random Map");
#endif /* ENABLE_NETWORK */
		MakeNewGame();
		break;

	case SM_START_SCENARIO: /* New Game --> Choose one of the preset scenarios */
		#ifdef ENABLE_NETWORK
			if (_network_server)
				snprintf(_network_game_info.map_name, NETWORK_NAME_LENGTH, "%s (Loaded scenario)", _file_to_saveload.title);
		#endif /* ENABLE_NETWORK */
		StartScenario();
		break;

	case SM_LOAD: { /* Load game, Play Scenario */
		_opt_ptr = &_opt;

		if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) 
		{		
			LoadIntroGame();
			ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
		} else {
			_local_player = 0;
			DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // decrease pause counter (was increased from opening load dialog)
#ifdef ENABLE_NETWORK
			if (_network_server)
				snprintf(_network_game_info.map_name, NETWORK_NAME_LENGTH, "%s (Loaded game)", _file_to_saveload.title);
#endif /* ENABLE_NETWORK */
		}
		break;
	}

	case SM_LOAD_SCENARIO: { /* Load scenario from scenario editor */
		if (SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR)) {
			PlayerID i;

			_opt_ptr = &_opt;

			_local_player = OWNER_NONE;
			_generating_world = true;
			// delete all players.
			for (i = 0; i != MAX_PLAYERS; i++) {
				ChangeOwnershipOfPlayerItems(i, OWNER_SPECTATOR);
				_players[i].is_active = false;
			}
			_generating_world = false;
			// delete all stations owned by a player
			DeleteAllPlayerStations();
		} 
		else
		{
ErrDisplay("Load scenario failed!");		
			ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
		}
		break;
	}


	case SM_MENU: /* Switch to game intro menu */
		LoadIntroGame();
		break;

	case SM_SAVE: /* Save game */
		if (SaveOrLoad(_file_to_saveload.name, SL_SAVE) != SL_OK)
			ShowErrorMessage(INVALID_STRING_ID, STR_4007_GAME_SAVE_FAILED, 0, 0);
		else
			DeleteWindowById(WC_SAVELOAD, 0);
		break;

	case SM_GENRANDLAND: /* Generate random land within scenario editor */
		GenerateWorld(GW_RANDOM, 1<<_patches.map_x, 1<<_patches.map_y);
		// XXX: set date
		_local_player = OWNER_NONE;
		MarkWholeScreenDirty();
		break;
	}

	if (_switch_mode_errorstr != INVALID_STRING_ID)
		ShowErrorMessage(INVALID_STRING_ID,_switch_mode_errorstr,0,0);
}


// State controlling game loop.
// The state must not be changed from anywhere
// but here.
// That check is enforced in DoCommand.
void StateGameLoop(void)
{
	// dont execute the state loop during pause
	if (_pause) return;

	if (_game_mode == GM_EDITOR) {
		RunTileLoop();
		CallVehicleTicks();
		CallLandscapeTick();
		CallWindowTickEvent();
		NewsLoop();
	} else {
		// All these actions has to be done from OWNER_NONE
		//  for multiplayer compatibility
		PlayerID p = _current_player;
		_current_player = OWNER_NONE;

		AnimateAnimatedTiles();
		IncreaseDate();
		RunTileLoop();
		CallVehicleTicks();
		CallLandscapeTick();

		AI_RunGameLoop();

		CallWindowTickEvent();
		NewsLoop();
		_current_player = p;
	}
}

static void DoAutosave(void)
{
	char buf[200];

	if (_patches.keep_all_autosave && _local_player != OWNER_SPECTATOR) {
		const Player *p = GetPlayer(_local_player);
		char *s;
		sprintf(buf, "%s%s", _path.autosave_dir, PATHSEP);

		SetDParam(0, p->name_1);
		SetDParam(1, p->name_2);
		SetDParam(2, _date);
		s = GetString(buf + strlen(_path.autosave_dir) + strlen(PATHSEP), STR_4004);
		strcpy(s, ".sav");
	} else { /* generate a savegame name and number according to _patches.max_num_autosaves */
		sprintf(buf, "%s%sautosave%d.sav", _path.autosave_dir, PATHSEP, _autosave_ctr);

		_autosave_ctr++;
		if (_autosave_ctr >= _patches.max_num_autosaves) {
			// we reached the limit for numbers of autosaves. We will start over
			_autosave_ctr = 0;
		}
	}

	DEBUG(misc, 2) ("Autosaving to %s", buf);
	if (SaveOrLoad(buf, SL_SAVE) != SL_OK)
		ShowErrorMessage(INVALID_STRING_ID, STR_AUTOSAVE_FAILED, 0, 0);
}

static void ScrollMainViewport(int x, int y)
{
	if (_game_mode != GM_MENU) {
		Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
		assert(w);
		
		// MYCODE EDIT graphics optimization
		_num_dirty_rects = MAX_DIRTY_RECTS;
		WP(w,vp_d).scrollpos_x += x << w->viewport->zoom;
		WP(w,vp_d).scrollpos_y += y << w->viewport->zoom;
	}
}

static const int8 scrollamt[16][2] = {
	{ 0, 0},
	{-2, 0}, // 1:left
	{ 0,-2}, // 2:up
	{-2,-1}, // 3:left + up
	{ 2, 0}, // 4:right
	{ 0, 0}, // 5:left + right
	{ 2,-1}, // 6:right + up
	{ 0,-2}, // 7:left + right + up = up
	{ 0 ,2}, // 8:down
	{-2 ,1}, // 9:down+left
	{ 0, 0}, // 10:impossible
	{-2, 0}, // 11:left + up + down = left
	{ 2, 1}, // 12:down+right
	{ 0, 2}, // 13:left + right + down = down
	{ 0,-2}, // 14:left + right + up = up
	{ 0, 0}, // 15:impossible
};

static void HandleKeyScrolling(void)
{
	if (_dirkeys && !_no_scroll) {
		int factor = _shift_pressed ? 50 : 10;
		ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
	}
}

void GameLoop(void)
{
	int m;

	// autosave game?
	if (_do_autosave) {
		_do_autosave = false;
		DoAutosave();
		RedrawAutosave();
	}

	// handle scrolling of the main window
	if (_dirkeys) HandleKeyScrolling();

	// make a screenshot?
	if (_make_screenshot != 0) {
		switch (_make_screenshot) {
			case 1: // make small screenshot
				UndrawMouseCursor();
				ShowScreenshotResult(MakeScreenshot());
				break;

			case 2: // make large screenshot
				ShowScreenshotResult(MakeWorldScreenshot(-(int)MapMaxX() * TILE_PIXELS, 0, (MapMaxX() + MapMaxY()) * TILE_PIXELS, (MapMaxX() + MapMaxY()) * TILE_PIXELS >> 1, 0));
				break;
		}
		_make_screenshot = 0;
	}

	// switch game mode?
	if (_switch_mode != SM_NONE) {
		SwitchMode(_switch_mode);
		_switch_mode = SM_NONE;
	}

	IncreaseSpriteLRU();
	InteractiveRandom();

	if (_scroller_click_timeout > 3) {
		_scroller_click_timeout -= 3;
	} else {
		_scroller_click_timeout = 0;
	}

	_caret_timer += 3;
	_timer_counter += 8;
	CursorTick();

#ifdef ENABLE_NETWORK
	// Check for UDP stuff
	NetworkUDPGameLoop();

	if (_networking) {
		// Multiplayer
		NetworkGameLoop();
	} else {
		if (_network_reconnect > 0 && --_network_reconnect == 0) {
			// This means that we want to reconnect to the last host
			// We do this here, because it means that the network is really closed
			NetworkClientConnectGame(_network_last_host, _network_last_port);
		}
		// Singleplayer
		StateGameLoop();
	}
#else
	StateGameLoop();
#endif /* ENABLE_NETWORK */

	if (!_pause && _display_opt & DO_FULL_ANIMATION) DoPaletteAnimations();

	if (!_pause || _cheats.build_in_pause.value) MoveAllTextEffects();

	InputLoop();

	MusicLoop();
}

void BeforeSaveGame(void)
{
	const Window* w = FindWindowById(WC_MAIN_WINDOW, 0);

	if (w != NULL) {
		_saved_scrollpos_x = WP(w, const vp_d).scrollpos_x;
		_saved_scrollpos_y = WP(w, const vp_d).scrollpos_y;
		_saved_scrollpos_zoom = w->viewport->zoom;
	}
}

static void ConvertTownOwner(void)
{
	TileIndex tile;

	for (tile = 0; tile != MapSize(); tile++) {
		if (IsTileType(tile, MP_STREET)) {
			if (IsLevelCrossing(tile) && GetCrossingRoadOwner(tile) & 0x80) {
				SetCrossingRoadOwner(tile, OWNER_TOWN);
			}

			if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN);
		} else if (IsTileType(tile, MP_TUNNELBRIDGE)) {
			if (GetTileOwner(tile) & 0x80) SetTileOwner(tile, OWNER_TOWN);
		}
	}
}

// before savegame version 4, the name of the company determined if it existed
static void CheckIsPlayerActive(void)
{
	Player* p;

	FOR_ALL_PLAYERS(p) {
		if (p->name_1 != 0) p->is_active = true;
	}
}

// since savegame version 4.1, exclusive transport rights are stored at towns
static void UpdateExclusiveRights(void)
{
	Town* t;

	FOR_ALL_TOWNS(t) {
		if (t->xy != 0) t->exclusivity = (byte)-1;
	}

	/* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete)
			could be implemented this way:
			1.) Go through all stations
					Build an array town_blocked[ town_id ][ player_id ]
				 that stores if at least one station in that town is blocked for a player
			2.) Go through that array, if you find a town that is not blocked for
					one player, but for all others, then give him exclusivity.
	*/
}

static const byte convert_currency[] = {
	 0,  1, 12,  8,  3,
	10, 14, 19,  4,  5,
	 9, 11, 13,  6, 17,
	16, 22, 21,  7, 15,
	18,  2, 20, };

// since savegame version 4.2 the currencies are arranged differently
static void UpdateCurrencies(void)
{
	_opt.currency = convert_currency[_opt.currency];
}

/* Up to revision 1413 the invisible tiles at the southern border have not been
 * MP_VOID, even though they should have. This is fixed by this function
 */
static void UpdateVoidTiles(void)
{
	uint i;

	for (i = 0; i < MapMaxY(); ++i) MakeVoid(i * MapSizeX() + MapMaxX());
	for (i = 0; i < MapSizeX(); ++i) MakeVoid(MapSizeX() * MapMaxY() + i);
}

// since savegame version 6.0 each sign has an "owner", signs without owner (from old games are set to 255)
static void UpdateSignOwner(void)
{
	SignStruct *ss;

	FOR_ALL_SIGNS(ss) ss->owner = OWNER_NONE;
}

extern void UpdateOldAircraft( void );
extern void UpdateOilRig( void );

bool AfterLoadGame(void)
{
	Window *w;
	ViewPort *vp;
	Player *p;

	// in version 2.1 of the savegame, town owner was unified.
	if (CheckSavegameVersionOldStyle(2, 1)) ConvertTownOwner();

	// from version 4.1 of the savegame, exclusive rights are stored at towns
	if (CheckSavegameVersionOldStyle(4, 1)) UpdateExclusiveRights();

	// from version 4.2 of the savegame, currencies are in a different order
	if (CheckSavegameVersionOldStyle(4, 2)) UpdateCurrencies();

	// from version 6.1 of the savegame, signs have an "owner"
	if (CheckSavegameVersionOldStyle(6, 1)) UpdateSignOwner();

	/* In old version there seems to be a problem that water is owned by
	    OWNER_NONE, not OWNER_WATER.. I can't replicate it for the current
	    (4.3) version, so I just check when versions are older, and then
	    walk through the whole map.. */
	if (CheckSavegameVersionOldStyle(4, 3)) {
		TileIndex tile = TileXY(0, 0);
		uint w = MapSizeX();
		uint h = MapSizeY();

		BEGIN_TILE_LOOP(tile_cur, w, h, tile)
			if (IsTileType(tile_cur, MP_WATER) && GetTileOwner(tile_cur) >= MAX_PLAYERS)
				SetTileOwner(tile_cur, OWNER_WATER);
		END_TILE_LOOP(tile_cur, w, h, tile)
	}

	// convert road side to my format.
	if (_opt.road_side) _opt.road_side = 1;

	// Load the sprites
	GfxLoadSprites();

	/* Connect front and rear engines of multiheaded trains and converts
	 * subtype to the new format */
	if (CheckSavegameVersionOldStyle(17, 1)) ConvertOldMultiheadToNew();

	/* Connect front and rear engines of multiheaded trains */
	ConnectMultiheadedTrains();

	// Update current year
	SetDate(_date);

	// reinit the landscape variables (landscape might have changed)
	InitializeLandscapeVariables(true);

	// Update all vehicles
	AfterLoadVehicles();

	// Update all waypoints
	if (CheckSavegameVersion(12)) FixOldWaypoints();

	UpdateAllWaypointSigns();

	// in version 2.2 of the savegame, we have new airports
	if (CheckSavegameVersionOldStyle(2, 2)) UpdateOldAircraft();

	UpdateAllStationVirtCoord();

	// Setup town coords
	AfterLoadTown();
	UpdateAllSignVirtCoords();

	// make sure there is a town in the game
	if (_game_mode == GM_NORMAL && !ClosestTownFromTile(0, (uint)-1)) {
		_error_message = STR_NO_TOWN_IN_SCENARIO;
		return false;
	}

	// Initialize windows
	ResetWindowSystem();
	SetupColorsAndInitialWindow();

	w = FindWindowById(WC_MAIN_WINDOW, 0);

	WP(w,vp_d).scrollpos_x = _saved_scrollpos_x;
	WP(w,vp_d).scrollpos_y = _saved_scrollpos_y;

	vp = w->viewport;
	vp->zoom = _saved_scrollpos_zoom;
	vp->virtual_width = vp->width << vp->zoom;
	vp->virtual_height = vp->height << vp->zoom;

	// in version 4.1 of the savegame, is_active was introduced to determine
	// if a player does exist, rather then checking name_1
	if (CheckSavegameVersionOldStyle(4, 1)) CheckIsPlayerActive();

	// the void tiles on the southern border used to belong to a wrong class (pre 4.3).
	if (CheckSavegameVersionOldStyle(4, 3)) UpdateVoidTiles();

	// If Load Scenario / New (Scenario) Game is used,
	//  a player does not exist yet. So create one here.
	// 1 exeption: network-games. Those can have 0 players
	//   But this exeption is not true for network_servers!
	if (!_players[0].is_active && (!_networking || (_networking && _network_server)))
		DoStartupNewPlayer(false);

	DoZoomInOutWindow(ZOOM_NONE, w); // update button status
	MarkWholeScreenDirty();

	// In 5.1, Oilrigs have been moved (again)
	if (CheckSavegameVersionOldStyle(5, 1)) UpdateOilRig();

	/* In version 6.1 we put the town index in the map-array. To do this, we need
	 *  to use m2 (16bit big), so we need to clean m2, and that is where this is
	 *  all about ;) */
	if (CheckSavegameVersionOldStyle(6, 1)) {
		BEGIN_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0) {
			if (IsTileType(tile, MP_HOUSE)) {
				_m[tile].m4 = _m[tile].m2;
				//XXX magic
				SetTileType(tile, MP_VOID);
				_m[tile].m2 = ClosestTownFromTile(tile,(uint)-1)->index;
				SetTileType(tile, MP_HOUSE);
			} else if (IsTileType(tile, MP_STREET)) {
				//XXX magic
				_m[tile].m4 |= (_m[tile].m2 << 4);
				if (IsTileOwner(tile, OWNER_TOWN)) {
					SetTileType(tile, MP_VOID);
					_m[tile].m2 = ClosestTownFromTile(tile,(uint)-1)->index;
					SetTileType(tile, MP_STREET);
				} else {
					SetTownIndex(tile, 0);
				}
			}
		} END_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0);
	}

	/* From version 9.0, we update the max passengers of a town (was sometimes negative
	 *  before that. */
	if (CheckSavegameVersion(9)) {
		Town *t;
		FOR_ALL_TOWNS(t) UpdateTownMaxPass(t);
	}

	/* From version 16.0, we included autorenew on engines, which are now saved, but
	 *  of course, we do need to initialize them for older savegames. */
	if (CheckSavegameVersion(16)) {
		FOR_ALL_PLAYERS(p) {
			p->engine_renew_list = NULL;
			p->engine_renew = false;
			p->engine_renew_months = -6;
			p->engine_renew_money = 100000;
		}
		if (_local_player < MAX_PLAYERS) {
			// Set the human controlled player to the patch settings
			// Scenario editor do not have any companies
			p = GetPlayer(_local_player);
			p->engine_renew = _patches.autorenew;
			p->engine_renew_months = _patches.autorenew_months;
			p->engine_renew_money = _patches.autorenew_money;
		}
	}

	/* Elrails got added in rev 24 */
	if (CheckSavegameVersion(24)) {
		Vehicle* v;
		uint i;
		TileIndex t;
		bool make_elrail = false;

		for (i = 0; i < lengthof(_engines); i++) {
			Engine* e = GetEngine(i);
			if (e->type == VEH_Train &&
					(e->railtype != RAILTYPE_RAIL || RailVehInfo(i)->engclass == 2)) {
				e->railtype++;
			}
		}

		FOR_ALL_VEHICLES(v) {
			if (v->type == VEH_Train) {
				RailType rt = GetEngine(v->engine_type)->railtype;

				v->u.rail.railtype = rt;
				if (rt == RAILTYPE_ELECTRIC) make_elrail = true;
			}
		}

		/* .. so we convert the entire map from normal to elrail (so maintain "fairness") */
		for (t = 0; t < MapSize(); t++) {
			switch (GetTileType(t)) {
				case MP_RAILWAY:
					if (GetRailType(t) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 0, 4, 1);
					break;

				case MP_STREET:
					if (IsLevelCrossing(t) && (GetRailTypeCrossing(t) > RAILTYPE_RAIL || make_elrail)) AB(_m[t].m4, 0, 4, 1);
					break;

				case MP_STATION:
					if (IsRailwayStation(t) && (GetRailType(t) > RAILTYPE_RAIL || make_elrail)) AB(_m[t].m3, 0, 4, 1);
					break;

				case MP_TUNNELBRIDGE:
					if (IsTunnel(t)) {
						if (GetTunnelTransportType(t) == TRANSPORT_RAIL) {
							if (GetRailType(t) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 0, 4, 1);
						}
					} else {
						if (GetBridgeTransportType(t) == TRANSPORT_RAIL) {
							if (IsBridgeRamp(t)) {
								if (GetRailType(t) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 0, 4, 1);
							} else {
								if (GetRailTypeOnBridge(t) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 4, 4, 1);
							}
						}
						if (IsBridgeMiddle(t) &&
								IsTransportUnderBridge(t) &&
								GetTransportTypeUnderBridge(t) == TRANSPORT_RAIL) {
							if (GetRailType(t) > RAILTYPE_RAIL || make_elrail) AB(_m[t].m3, 0, 4, 1);
						}
					}
					break;

				default:
					break;
			}
		}

		FOR_ALL_VEHICLES(v) {
			if (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v))) TrainConsistChanged(v);
		}

	}

	/* In version 16.1 of the savegame a player can decide if trains, which get
	 * replaced, shall keep their old length. In all prior versions, just default
	 * to false */
	if (CheckSavegameVersionOldStyle(16, 1)) {
		FOR_ALL_PLAYERS(p) {
			p->renew_keep_length = false;
		}
	}

	/* In version 17, ground type is moved from m2 to m4 for depots and
	 * waypoints to make way for storing the index in m2. The custom graphics
	 * id which was stored in m4 is now saved as a grf/id reference in the
	 * waypoint struct. */
	if (CheckSavegameVersion(17)) {
		Waypoint *wp;

		FOR_ALL_WAYPOINTS(wp) {
			if (wp->xy != 0 && wp->deleted == 0) {
				const StationSpec *spec = NULL;

				if (HASBIT(_m[wp->xy].m3, 4))
					spec = GetCustomStation(STAT_CLASS_WAYP, _m[wp->xy].m4 + 1);

				if (spec != NULL) {
					wp->stat_id = _m[wp->xy].m4 + 1;
					wp->grfid = spec->grfid;
					wp->localidx = spec->localidx;
				} else {
					// No custom graphics set, so set to default.
					wp->stat_id = 0;
					wp->grfid = 0;
					wp->localidx = 0;
				}

				// Move ground type bits from m2 to m4.
				_m[wp->xy].m4 = GB(_m[wp->xy].m2, 0, 4);
				// Store waypoint index in the tile.
				_m[wp->xy].m2 = wp->index;
			}
		}
	} else {
		/* As of version 17, we recalculate the custom graphic ID of waypoints
		 * from the GRF ID / station index. */
		UpdateAllWaypointCustomGraphics();
	}


	/* From version 15, we moved a semaphore bit from bit 2 to bit 3 in m4, making
	 *  room for PBS. Now in version 21 move it back :P. */
	if (CheckSavegameVersion(21) && !CheckSavegameVersion(15)) {
		BEGIN_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0) {
			if (IsTileType(tile, MP_RAILWAY)) {
				// Clear PBS signals, move back sempahore bit to 2
				if (HasSignals(tile)) {
					// convert PBS signals to combo-signals
					if (HASBIT(_m[tile].m4, 2)) SB(_m[tile].m4, 0, 2, 3);

					SB(_m[tile].m4, 2, 2, HASBIT(_m[tile].m4, 3));
					CLRBIT(_m[tile].m4, 3);
				}

				// Clear PBS reservation on track
				if (!IsTileDepotType(tile, TRANSPORT_RAIL))
					SB(_m[tile].m4, 4, 4, 0);
				else
					CLRBIT(_m[tile].m3, 6);
			}

			// Clear PBS reservation on crossing
			if (IsTileType(tile, MP_STREET) && IsLevelCrossing(tile))
				CLRBIT(_m[tile].m5, 0);

			// Clear PBS reservation on station
			if (IsTileType(tile, MP_STATION))
				CLRBIT(_m[tile].m3, 6);
		} END_TILE_LOOP(tile, MapSizeX(), MapSizeY(), 0);
	}

	if (CheckSavegameVersion(22))  UpdatePatches();

	if (CheckSavegameVersion(25)) {
		Vehicle *v;
		FOR_ALL_VEHICLES(v) {
			if (v->type == VEH_Road) {
				v->vehstatus &= ~0x40;
				v->u.road.slot = NULL;
				v->u.road.slot_age = 0;
			}
		}
	}

	FOR_ALL_PLAYERS(p) p->avail_railtypes = GetPlayerRailtypes(p->index);

	return true;
}

void UpdatePalette(uint start, uint count)
{
	unsigned long i;

	if (device_type & BF_ZODIAC) 
	{
		for (i = 0; i != count; i++) 
		{
			pal[start + i] = TwGfxRGBToDisplayRGB( _cur_palette[start + i] );
		}
	}
	else
	{
		for (i = 0; i != count; i++) 
		{
			pal[start + i] = PalmGfxRGBToDisplayRGB( _cur_palette[start + i].r, _cur_palette[start + i].g, _cur_palette[start + i].b );
		}	
	}
}

static void CheckPaletteAnim(void)
{
	if (_pal_last_dirty != -1) {
		UpdatePalette(_pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1);
		_pal_last_dirty = -1;
	}
}

//UInt32 device_type;

static const char *ZodVideoStart(const char * const *parm)
{
/*
	TwGfxSurfaceInfoType 	surfInfo;
	
	surfInfo.size = sizeof(surfInfo);
    surfInfo.width = 480;
    surfInfo.height = 320;
    surfInfo.location = twGfxLocationAcceleratorMemoryNoBackingStore;
    surfInfo.pixelFormat = twGfxPixelFormatRGB565; 
    TwGfxAllocSurface(gGfx, &gSurface, &surfInfo);
*/
	//UInt16 *drawBitmapP;
  	
	_screen.width = _cur_resolution[0] = 640;
	_screen.height = _cur_resolution[1] = 480;
	_screen.pitch = _screen.width; //GetScreenPitch();
	 
	_zod_video_mem = (byte*)malloc(_cur_resolution[0]*_cur_resolution[1]);
	
	screenrect.left = (_screen.width / 2) - (display_width / 2); // center
	screenrect.top = 0;
	screenrect.right = screenrect.left + display_width;
	screenrect.bottom = screenrect.top + display_height;
	
	#if 0
	// removed TWAPI
	if (device_type & BF_ZODIAC) 
	{
		screenBufferP=(UInt16*) stubzodiacblit ( 0,0,0,0 );
		if (!screenBufferP) ErrDisplay("Zodiac VRAM NULL ptr!");
	}
	else
	{
		screenBufferP = (UInt16*)g_pnobridge->vram;
	}
	#endif
	
	#if 0
	screenBufferP = (UInt16*)g_pnobridge->vram;
	#endif
	
	UpdatePalette(0, 256);
	
	MarkWholeScreenDirty();

	return NULL;
}

static void ZodVideoStop(void)
{
#ifdef WITH_TWAPI
    TwGfxUnlockSurface(displaySurface, true);    // DOLATER true needed?	
#endif
	free(_zod_video_mem);
#ifdef WITH_TWAPI
	TwGfxFreeSurface(displaySurface);
#else
	//FtrUnregister( 'ZttD', 4242 );
#endif
//	TwGfxFreeSurface(gSurface);
}

static void ZodVideoMainLoop(void)
{
	uint32 next_tick = TimGetTicks() + (SysTicksPerSecond() / 30);
	uint32 cur_ticks;
	int32 pal_tick = -1;
	//uint32 mod;
	//int numkeys;
	
	for (;;) 
	{
		uint32 keys = KeyCurrentState();

		InteractiveRandom(); // randomness
		if (_exit_game)
		{
		  	// flush System messages
			EventType myevent;
			while ( SysEventAvail() )
			{
			  	SysEventGet(&myevent,0);
				if (myevent.eType==nilEvent) break;
				switch (myevent.eType)
				{
					case appStopEvent:break;
					case frmUpdateEvent:break;
					default: SysHandleEvent(&myevent);
				}
			}	
			return;
		}
		
		cur_ticks = TimGetTicks();
		if ((_fast_forward && !_pause) || cur_ticks > next_tick)
			next_tick = cur_ticks;

		if (cur_ticks == next_tick) 
		{
			next_tick += SysTicksPerSecond() / 30;
			
			// determine which directional keys are down
			if( device_type & BF_ZODIAC )
			{
				_dirkeys =
					(keys & keyBitRockerLeft  	? 1 : 0) |
					(keys & keyBitRockerUp    	? 2 : 0) |
					(keys & keyBitRockerRight 	? 4 : 0) |
					(keys & keyBitRockerDown  	? 8 : 0);			
			}
			else
			{
				_dirkeys =
					(keys & keyBitRockerLeft  	? 1 : 0) |
					(keys & keyBitPageUp    	? 2 : 0) |
					(keys & keyBitRockerRight 	? 4 : 0) |
					(keys & keyBitPageDown  	? 8 : 0);
			}
			
			GameLoop(); 			
			_screen.dst_ptr = _zod_video_mem;						
			UpdateWindows();			
	
			if( _display_opt & DO_FULL_ANIMATION )
			{
				if (++pal_tick > 4) 
				{
					CheckPaletteAnim();
					_num_dirty_rects = MAX_DIRTY_RECTS;
					pal_tick = 1;
				}
			}
			
 			CallDrawSurfaceToScreen();
		} 
		else 
		{		
			//SysTaskDelay(1);
			_screen.dst_ptr = _zod_video_mem;
 			//DrawTextMessage();
 			DrawMouseCursor();		
 			CallDrawSurfaceToScreen();
 		}
	}
}

static void ZodVideoMakeDirty(int left, int top, int width, int height)
{
	if (_num_dirty_rects < MAX_DIRTY_RECTS) 
	{
		_dirty_rects[_num_dirty_rects].left = left;
		_dirty_rects[_num_dirty_rects].right = left + width;
		_dirty_rects[_num_dirty_rects].top = top;
		_dirty_rects[_num_dirty_rects].bottom = top + height;
		
		_num_dirty_rects++;
	}
}

static bool ZodVideoChangeRes(int w, int h) { return false; }
static void ZodVideoFullScreen(bool fs) {}


const HalVideoDriver _zod_video_driver = {
	ZodVideoStart,
	ZodVideoStop,
	ZodVideoMakeDirty,
	ZodVideoMainLoop,
	ZodVideoChangeRes,
	ZodVideoFullScreen,
};

TwSmfHandle* smfHandle = NULL;
unsigned char* midiData = NULL;
Boolean musicInstalled;

unsigned char* readMidiData(Int32 VolRefNum, char* Path) 
{ 
	UInt32 size, nb;
	FileRef ref;
	unsigned char* buf = NULL;
	
	// Path might need to be edited due to volume being possibly inside it
	Err err = VFSFileOpen(VolRefNum, Path, vfsModeRead, &ref);
	
	if (!err) 
	{ 
		err = VFSFileSize(ref, &size); 
		if (!err) 
		{ 
			buf = (unsigned char*)malloc(size); 
			if (buf) 
			{ 
				err = VFSFileRead(ref, size, buf, &nb); 
			} 
		} 
		
		VFSFileClose(ref); 
	}
	
	return buf; 
}

void playMidiFile(Int32 Volume, Int32 VolRefNum, char* Path) 
{ 
	//TwMidiSetMasterVolume(Volume);		
	midiData = readMidiData(VolRefNum, Path); 
	
	if (midiData) 
	{ 
		UInt32 duration; 
		//TwSmfType* smfHandle; 
		Err err = stub_TwSmfOpen(&smfHandle, midiData, &duration); 
		if (!err) 
		{
			//Boolean isPlaying = false;
			err = stub_TwSmfPlay(smfHandle, NULL, NULL, NULL); 
			/*while( 	err == errNone && 
					isPlaying == false	)
			{
				stub_TwSmfIsPlaying(smfHandle, &isPlaying);
				SysTaskDelay(1);
			}
			*/
			musicInstalled = true;
		}
		else
		{
			if( err == twMidiErrorUnavailable )
				ErrDisplay("Midi error 1");
			if( err == twMidiErrorNullPointer )
				ErrDisplay("Midi error 2");
			if( err == twMidiErrorInvalidFormat )
				ErrDisplay("Midi error 3");
			if( err == twMidiErrorAllocFailed )
				ErrDisplay("Midi error 4");
				
			ErrDisplay("Midi error 5");
		}
		
		free( midiData );
		midiData = NULL;
	}
	else
	{
		// no music installed, fake song as playing
		musicInstalled = false;
	}
}

static const char * ZodMusicMidiStart(const char * const *parm)
{
	musicInstalled = false;
	return(0);
}

static void ZodMusicMidiStopSong(void)
{
	if ((device_type & BF_ZODIAC) &&
		(musicInstalled == true) )
	if ( true == musicInstalled )
	{
		//stub_TwSmfStop(smfHandle);
		if( smfHandle != NULL ) stub_TwSmfClose(smfHandle);
	}
}

static void ZodMusicMidiStop(void)
{
	if(	( device_type & BF_ZODIAC ) &&
		( true == musicInstalled ) )
	{
		ZodMusicMidiStopSong();
	}
}

static void ZodMusicMidiPlaySong(const char *filename)
{
	if (device_type & BF_ZODIAC)
	{

		UInt16 volume;
		char pathBuffer[MAX_PATH];
		
		UInt16 voliter = palm_vfs_iter();
		StrPrintF(pathBuffer, "%s", filename );
		playMidiFile(100, voliter, pathBuffer);
	}
}

static bool ZodMusicMidiIsSongPlaying(void)
{
	if (device_type & BF_ZODIAC)
	{
		Boolean isPlaying = false;
		
		// ugly global
		if( musicInstalled == false )
		{
			// if no music is installed fake it as always playing
			return true;
		}
		
		stub_TwSmfIsPlaying(smfHandle, &isPlaying);

		if( isPlaying == false )
		{
			stub_TwSmfClose(smfHandle);
			
			return false;
		}
		
		return true;
	}

	return true;
}

static void ZodMusicMidiSetVolume(byte vol)
{
	if ((device_type & BF_ZODIAC) &&
		(musicInstalled == true) )
	{
		stub_TwMidiSetMasterVolume(vol);
	}
}

const HalMusicDriver _zodmusic_midi_driver = {
	ZodMusicMidiStart,
	ZodMusicMidiStop,
	ZodMusicMidiPlaySong,
	ZodMusicMidiStopSong,
	ZodMusicMidiIsSongPlaying,
	ZodMusicMidiSetVolume,
};

//Err __declspec(pace_native_callback) fill_sound_buffer(void *userdata, SndStreamRef channel, void *buffer, UInt32 *bufferSizeP)
//Err /*__declspec(pace_native_callback)*/ fill_sound_buffer(void *userdata, SndStreamRef channel, void *buffer, UInt32 numberofframes)
#define MyAlwaysReadUnaligned32(addr)  \
	( ((((unsigned char *)(addr))[0])) | \
	  ((((unsigned char *)(addr))[1]) << 8) | \
	  ((((unsigned char *)(addr))[2]) << 16) | \
	  ((((unsigned char *)(addr))[3]) << 24) )

static inline uint32 ALWAYS_READ_LE_UINT32(const uint32 *b) 
{
	return (uint32)MyAlwaysReadUnaligned32(b);
}
  
Err fill_sound_buffer(void *userdata, SndStreamRef channel, void *buffer, UInt32 *bufferSizeP)
{
	__asm 
	{
		stmfd  r13!,{r9,r10}
		ldr	   r9,[r0]
		ldr	   r10,[r0,#4]
	}
	
	MxMixSamples(buffer, ((*bufferSizeP) / 4));
	
	__asm 
	{
		ldmfd  r13!,{r9,r10}
	}
	
	return errNone;
}

static const char *ZodSoundStart(const char * const *parm)
{
#if 1
	Err err;	
	err = stub_SndStreamCreateExtended( &streamRef, sndOutput, sndFormatPCM, 11025, 
					 					sndInt16Little, sndStereo, fill_sound_buffer, &reg_sys, 1024,
					 					true );			 	
	
	//err = stub_SndStreamCreate( &streamRef, sndOutput, 11025, sndInt16Little, sndStereo, fill_sound_buffer, &reg_sys, 1024, 1);
	
	if( err != errNone )
	{
		ErrDisplay("Error creating sound!");	
	}
	
	err = stub_SndStreamSetVolume( streamRef, 512 );

	if( err != errNone )
	{
		ErrDisplay("Error setting volume!");	
	}
						
	err = stub_SndStreamStart(streamRef);	

	if( err != errNone )
	{
		ErrDisplay("Error starting sound!");	
	}	
/*

	stub_SndStreamCreate( &streamRef, sndOutput, 11025, sndInt16Little, sndStereo, fill_sound_buffer, 0, 0, 1);
	stub_SndStreamSetVolume( streamRef, 512 );
	stub_SndStreamStart(streamRef);
*/
#endif
	return NULL;
}

static void ZodSoundStop(void)
{
#if 1
	stub_SndStreamStop(streamRef);
	stub_SndStreamDelete(streamRef);
	streamRef=NULL;	
#endif
}

const HalSoundDriver _zod_sound_driver = {
	ZodSoundStart,
	ZodSoundStop,
};

void DeterminePaths(void)
{
	char *cfg;
    UInt32 voliter;
    //char path [ 256 ];
    //Err error;
	//UInt32 it = vfsIteratorStart;
	//Err err;
 	//FILE *myfile = (FILE *)MemPtrNew ( sizeof(FILE) );
	// iterate all volumes
	//UInt16 volume;


	_path.personal_dir = _path.game_data_dir = cfg = malloc(MAX_PATH);
	// MYCODE EDIT
	cfg = VFSPATH;
	
	_path.personal_dir = cfg;
	_path.game_data_dir = cfg;

	_path.save_dir = str_fmt("%sSAVE", cfg);
	_path.autosave_dir = str_fmt("%sSAVE", cfg);
	_path.scenario_dir = str_fmt("%sSCENARIO", cfg);
	_path.gm_dir = str_fmt("%sGM/", cfg);
	_path.data_dir = str_fmt("%sDATA/", cfg);
	_path.lang_dir = str_fmt("%sLANG/", cfg);

	if (_config_file == NULL)
		_config_file = str_fmt("%sOPENTTD.CFG", _path.personal_dir);

	_highscore_file = str_fmt("%sHS.DAT", _path.personal_dir);
	_log_file = str_fmt("%sOPENTTD.LOG", _path.personal_dir);

	// make (auto)save and scenario folder
	voliter = palm_vfs_iter();
	VFSDirCreate(voliter, _path.save_dir);
	VFSDirCreate(voliter, _path.autosave_dir);
	VFSDirCreate(voliter, _path.scenario_dir);
}

void ShowOSErrorBox(const char *buf)
{
#if defined(__APPLE__)
	// this creates an error in the console and then opens the console.
	// Colourcodes are not used in the console, so they are skipped here
	fprintf(stderr, "Error: %s", buf);
	system("/Applications/Utilities/Console.app/Contents/MacOS/Console &");
#else
	// all systems, but OSX
	ShowInfo(buf);
#endif
}

static char *_fios_path;
static char *_fios_save_path;
static char *_fios_scn_path;
static FiosItem *_fios_items;
static int _fios_count, _fios_alloc;

static FiosItem *FiosAlloc(void)
{
	if (_fios_count == _fios_alloc) {
		_fios_alloc += 256;
		_fios_items = realloc(_fios_items, _fios_alloc * sizeof(FiosItem));
	}
	return &_fios_items[_fios_count++];
}

int compare_FiosItems(const void *a, const void *b)
{
	const FiosItem *da = (const FiosItem *)a;
	const FiosItem *db = (const FiosItem *)b;
	int r;

	if (_savegame_sort_order < 2) // sort by date
		r = da->mtime < db->mtime ? -1 : 1;
	else
		r = StrCompare(da->title, db->title);

// MYCODE NOTE might need to be strcasecmp ^^^
	if (_savegame_sort_order & 1) r = -r;
	return r;
}

// Get a list of savegames
FiosItem *FiosGetSavegameList(int *num, int mode)
{
	FiosItem *fios;
	int sort_start;
	//char filename[MAX_PATH];
	//char buffer[256];
	Err			err = errNone;
	char		tmpName[128];
	int 		cur_entry=0;	
	int 		total_size=0;
	UInt16 		volRefNum;			
	UInt32 		volIterator; 
	
	int			g_num_entries = 512;
		
	if (_fios_save_path == NULL) {
		_fios_save_path = malloc(MAX_PATH);
		StrCopy(_fios_save_path, _path.save_dir);
	}

	_fios_path = _fios_save_path;

/*
	// Parent directory, only if not in root already.
	if (_fios_path[1] != '\0') {
		fios = FiosAlloc();
		fios->type = FIOS_TYPE_PARENT;
		fios->mtime = 0;
		StrCopy(fios->name, "..");
		StrCopy(fios->title, ".. (Parent directory)");
	}

	// Show subdirectories first

	dir = opendir(_fios_path[0] != '\0' ? _fios_path : "\\");
	if (dir != NULL) {
		while ((dirent = readdir(dir)) != NULL) 
		{
			char buffer[256];
			int volume;
			FileRef f;
			snprintf(filename, lengthof(filename), "%s/%s",
				_fios_path, dirent->d_name);
				
		    volume = __sys_path(filename, buffer, sizeof(buffer));
			VFSFileOpen(volume, buffer, vfsModeRead, &f);
			if (!fstat(f, &sb) && 
				S_ISDIR(sb.st_mode) &&
				dirent->d_name[0] != '.') 
			{
				fios = FiosAlloc();
				fios->type = FIOS_TYPE_DIR;
				fios->mtime = 0;
				ttd_strlcpy(fios->name, dirent->d_name, lengthof(fios->name));
				snprintf(fios->title, lengthof(fios->title),
					"%s\\ (Directory)", dirent->d_name);
			}
		}
		closedir(dir);
	}
*/
	{
		/* XXX ugly global variables ... */
		byte order = _savegame_sort_order;
		_savegame_sort_order = 2; // sort ascending by name
		qsort(_fios_items, _fios_count, sizeof(FiosItem), compare_FiosItems);
		_savegame_sort_order = order;
	}

	// this is where to start sorting
	sort_start = _fios_count;

	/* Show savegame files
	 * .SAV OpenTTD saved game
	 * .SS1 Transport Tycoon Deluxe preset game
	 * .SV1 Transport Tycoon Deluxe (Patch) saved game
	 * .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game
	 */

//	volIterator = vfsIteratorStart;
//	while (volIterator != vfsIteratorStop) 
//	{
//		err = VFSVolumeEnumerate(&volRefNum, &volIterator);
		volRefNum = palm_vfs_iter();
		if (!err) 
		{
			FileInfoType info;
			FileRef dirRef;
			UInt32 dirIterator;					
			// open the directory first, to get the directory reference
			// volRefNum must have already been defined
			char path[MAX_PATH];
			StrPrintF( path, "%s", _fios_path );
			err = VFSFileOpen(volRefNum, path, vfsModeRead, &dirRef);
			if(err == errNone) 
			{
				info.nameP = tmpName; // point to local buffer												
				info.nameBufLen = 127;
				dirIterator = vfsIteratorStart;
				while (dirIterator != vfsIteratorStop) 
				{
				
					// Get the next file
					err = VFSDirEntryEnumerate (dirRef, &dirIterator,&info);
					if (err == errNone) 
					{
						// Do something with the directory entry information
						// Pull the attributes from info.attributes
						// The file name is in fileName
							char *t;
							
							t = StrStr(info.nameP, ".sav");
							// MYCODE EDIT
							if (t == NULL ) 
							{
								t = StrStr(info.nameP, ".SAV");
							}
							
							if (t != NULL ) { // OpenTTD
								fios = FiosAlloc();
								fios->type = FIOS_TYPE_FILE;
								fios->mtime = 0;
								ttd_strlcpy(fios->name, info.nameP, lengthof(fios->name));
								*t = '\0'; // strip extension
								ttd_strlcpy(fios->title, info.nameP, lengthof(fios->title));
							} 
							/* MYCODE EDIT removed Old Save Game Support to save stackspace
							else if (mode == SLD_LOAD_GAME || mode == SLD_LOAD_SCENARIO) 
							{
								t = StrStr(dirent->d_name, ".SS1");
								if ( (t = StrStr(dirent->d_name, ".SS1")) != NULL ||
									 (t = StrStr(dirent->d_name, ".SV1")) != NULL ||
									 (t = StrStr(dirent->d_name, ".SV2")) != NULL )
								{ // TTDLX(Patch)
									fios = FiosAlloc();
									fios->type = FIOS_TYPE_OLDFILE;
									fios->mtime = sb.st_mtime;
									ttd_strlcpy(fios->name, dirent->d_name, lengthof(fios->name));
									GetOldSaveGameName(fios->title, filename);
								}
							}
							*/
					}
					else break;
				} 
			}
//			else 
//			{
				// handle error, possibly by breaking out of the loop
//
//				break;
//			}
			VFSFileClose (dirRef);
		} 
//		else 
//		{
			// handle directory open error here
//
//			break;
//		}
//		if (cur_entry>=g_num_entries) break;
//	}

	qsort(_fios_items + sort_start, _fios_count - sort_start, sizeof(FiosItem), compare_FiosItems);
	*num = _fios_count;
	return _fios_items;
}

// Get a list of scenarios
// FIXME: Gross code duplication with FiosGetSavegameList()
FiosItem *FiosGetScenarioList(int *num, int mode)
{
	FiosItem *fios;
	int sort_start;
	//char filename[MAX_PATH];
	//char buffer[256];
	Err			err = errNone;
	char		tmpName[128];
	int 		cur_entry=0;	
	int 		total_size=0;
	UInt16 		volRefNum;			
	UInt32 		volIterator; 
	
	int			g_num_entries = 512;
		
	if (_fios_scn_path == NULL) {
		_fios_scn_path = malloc(MAX_PATH);
		StrCopy(_fios_scn_path, _path.scenario_dir);
	}

	_fios_path = _fios_scn_path;

/*
	// Parent directory, only if not of the type C:\.
	if (_fios_path[1] != '\0' && mode != SLD_NEW_GAME) {
		fios = FiosAlloc();
		fios->type = FIOS_TYPE_PARENT;
		fios->mtime = 0;
		StrCopy(fios->title, ".. (Parent directory)");
	}
	
	// Show subdirectories first
	dir = opendir(_fios_path[0] ? _fios_path : "\\");
	if (dir != NULL) {
		while ((dirent = readdir(dir)) != NULL) 
		{
			char buffer[256];
			int volume;
			FileRef f;
			snprintf(filename, lengthof(filename), "%s\\%s",
				_fios_path, dirent->d_name);
				
		    volume = __sys_path(filename, buffer, sizeof(buffer));
			VFSFileOpen(volume, buffer, vfsModeRead, &f);
			if (!fstat(f, &sb) && 
				S_ISDIR(sb.st_mode) &&
				dirent->d_name[0] != '.') 
			{
				fios = FiosAlloc();
				fios->type = FIOS_TYPE_DIR;
				fios->mtime = 0;
				ttd_strlcpy(fios->name, dirent->d_name, lengthof(fios->name));
				snprintf(fios->title, lengthof(fios->title),
					"%s\\ (Directory)", dirent->d_name);
			}
		}
		closedir(dir);
	}
*/
	// this is where to start sorting
	sort_start = _fios_count;

	/* Show scenario files
	 * .SCN OpenTTD style scenario file
	 * .SV0 Transport Tycoon Deluxe (Patch) scenario
	 * .SS0 Transport Tycoon Deluxe preset scenario
	 */

//	volIterator = vfsIteratorStart;
//	while (volIterator != vfsIteratorStop) 
//	{
//		err = VFSVolumeEnumerate(&volRefNum, &volIterator);
		volRefNum = palm_vfs_iter();
		if (!err) 
		{
			FileInfoType info;
			FileRef dirRef;
			UInt32 dirIterator;					
			// open the directory first, to get the directory reference
			// volRefNum must have already been defined
			char path[MAX_PATH];
			StrPrintF( path, "%s", _fios_path );
			err = VFSFileOpen(volRefNum, path, vfsModeRead, &dirRef);
			if(err == errNone) 
			{
				info.nameP = tmpName; // point to local buffer												
				info.nameBufLen = 127;
				dirIterator = vfsIteratorStart;
				while (dirIterator != vfsIteratorStop) 
				{
				
					// Get the next file
					err = VFSDirEntryEnumerate (dirRef, &dirIterator,&info);
					if (err == errNone) 
					{
						// Do something with the directory entry information
						// Pull the attributes from info.attributes
						// The file name is in fileName
						char *t;
						
						t = StrStr(info.nameP, ".scn");
						// MYCODE EDIT
						if( t == NULL )
						{
							t = StrStr(info.nameP, ".SCN");
						}
						
						if (t != NULL ) 
						{ // OpenTTD
							fios = FiosAlloc();
							fios->type = FIOS_TYPE_SCENARIO;
							fios->mtime = 0;
							ttd_strlcpy(fios->name, info.nameP, lengthof(fios->name));

							*t  = '\0'; // strip extension
							ttd_strlcpy(fios->title, info.nameP, lengthof(fios->title));
						} 
					}
					else break;
				} 
			}
//			else 
//			{
				// handle error, possibly by breaking out of the loop
//
//				break;
//			}
			VFSFileClose (dirRef);
		} 
//		else 
//		{
			// handle directory open error here
//
//			break;
//		}
//		if (cur_entry>=g_num_entries) break;
//	}

	qsort(_fios_items + sort_start, _fios_count - sort_start, sizeof(FiosItem), compare_FiosItems);
	*num = _fios_count;
	return _fios_items;
}


// free the list of savegames
void FiosFreeSavegameList(void)
{
	free(_fios_items);
	_fios_items = NULL;
	_fios_alloc = _fios_count = 0;
}

// Browse to
char *FiosBrowseTo(const FiosItem *item)
{
	char *path = _fios_path;
	char *s;

	switch (item->type) {
		case FIOS_TYPE_PARENT:
// MYCODE EDIT no strrchr support without stdio.h		
			//s = strrchr(path, '\\');
			//if (s != NULL) *s = '\0';
			break;

		case FIOS_TYPE_DIR:
			s = StrChr(item->name, '\\');
			if (s != NULL) *s = '\0';
			StrCat(path, "\\");
			StrCat(path, item->name);
			break;

		case FIOS_TYPE_FILE:
		case FIOS_TYPE_OLDFILE:
		case FIOS_TYPE_SCENARIO:
		case FIOS_TYPE_OLD_SCENARIO: {
			static char str_buffr[MAX_PATH];

			StrPrintF(str_buffr, "%s\\%s", path, item->name);
			return str_buffr;
		}
	}

	return NULL;
}

/**
 * Get descriptive texts. Returns the path and free space
 * left on the device
 * @param path string describing the path
 * @param tfs total free space in megabytes, optional (can be NULL)
 * @return StringID describing the path (free space or failure)
 */
StringID FiosGetDescText(const char **path, uint32 *tot)
{
	uint32 free = 0;
	*path = _fios_path[0] != '\0' ? _fios_path : "/";

#ifdef HAS_STATVFS
	{
		struct statvfs s;

		if (statvfs(*path, &s) == 0) {
			free = (uint64)s.f_frsize * s.f_bavail >> 20;
		} else
			return STR_4006_UNABLE_TO_READ_DRIVE;
	}
#endif
	if (tot != NULL) *tot = free;
	return STR_4005_BYTES_FREE;
}

void FiosMakeSavegameName(char *buf, const char *name, size_t size)
{
	const char* extension;
	const char* period;

	if (_game_mode == GM_EDITOR)
		extension = ".scn";
	else
		extension = ".sav";

	// Don't append the extension, if it is already there
	
	// MYCODE EDIT
	//period = strrchr(name, '.');
	//if (period != NULL && StrCompare(period, extension) == 0) extension = "";
	
	period = StrStr(name, extension);
	if (period != NULL) extension = "";

	StrPrintF(buf, "%s\\%s%s", _fios_path, name, extension);
}

bool FiosDelete(const char *name)
{
	char path[MAX_PATH];
	snprintf(path, lengthof(path), "%s\\%s", _fios_path, name);
	if(errNone == remove(path))
	{
		return true;
	}
	
	return false;
}

/*
const DriverDesc _video_driver_descs[] = {
//	{"null",	"Null Video Driver",	&_null_video_driver,	0},
#if defined(WITH_SDL)
	{ "sdl",	"SDL Video Driver",		&_sdl_video_driver,		1},
#endif
	{ "zod",	"Zodiac Video Driver",		&_zod_video_driver,		1},
//	{ "dedicated", "Dedicated Video Driver", &_dedicated_video_driver, 0},
	{ NULL,		NULL,									NULL,									0}
};

const DriverDesc _sound_driver_descs[] = {
//	{"null",	"Null Sound Driver",	&_null_sound_driver,		0},
#if defined(WITH_SDL)
	{ "sdl",	"SDL Sound Driver",		&_sdl_sound_driver,			1},
#endif
	{ "zod",	"Zodiac Sound Driver",	&_zod_sound_driver,		1},
	{ NULL,		NULL,									NULL,										0}
};
*/
#if defined(__APPLE__)
#define EXTMIDI_PRI 2
#else
#define EXTMIDI_PRI 0
#endif
/*
const DriverDesc _music_driver_descs[] = {
#ifndef __BEOS__
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
// MorphOS and AmigaOS have no music support
//	{"extmidi",	"External MIDI Driver",	&_extmidi_music_driver,	EXTMIDI_PRI},
#endif
#endif
#ifdef __BEOS__
	{ "bemidi",	"BeOS MIDI Driver",			&_bemidi_music_driver,	1},
#endif
	{ 	"zodmidi", "Zodiac MIDI Driver",		&_zodmusic_midi_driver, 1},
//	{   "null",	"Null Music Driver",		&_null_music_driver,		1},
	{     NULL,	NULL,										NULL,										0}
};
*/

bool FileExists(const char *filename)
{
	return (file_exists(filename) == 0);
}

static int LanguageCompareFunc(const void *a, const void *b)
{
	return StrCompare(*(const char* const *)a, *(const char* const *)b);
}

int GetLanguageList(char **languages, int max)
{
	int num = 0;
	Err			err = errNone;
	char		tmpName[128];
	int 		cur_entry=0;	
	int 		total_size=0;
	UInt16 		volRefNum;			
	UInt32 		volIterator; 
	
	int			g_num_entries = 512;
	
//	volIterator = vfsIteratorStart;
//	while (volIterator != vfsIteratorStop) 
//	{
//		err = VFSVolumeEnumerate(&volRefNum, &volIterator);
		volRefNum = palm_vfs_iter();
		if (!err) 
		{
			FileInfoType info;
			FileRef dirRef;
			UInt32 dirIterator;					
			// open the directory first, to get the directory reference
			// volRefNum must have already been defined
			char path[MAX_PATH];
			StrPrintF( path, "%s", _path.lang_dir );
			err = VFSFileOpen(volRefNum, path, vfsModeRead, &dirRef);
			if(err == errNone) 
			{
				info.nameP = tmpName; // point to local buffer												
				info.nameBufLen = 127;
				dirIterator = vfsIteratorStart;
				while (dirIterator != vfsIteratorStop) 
				{
				
					// Get the next file
					err = VFSDirEntryEnumerate (dirRef, &dirIterator,&info);
					if (err == errNone) 
					{
						// Do something with the directory entry information
						// Pull the attributes from info.attributes
						// The file name is in fileName
						char *t = StrStr(info.nameP, ".");
						
						if (t != NULL)
						{			
							languages[num++] = strdup(info.nameP);
							if (num == max)
							{				
							 	break;
							}
						}
					}
					else break;
				} 
			}
//			else 
//			{
				// handle error, possibly by breaking out of the loop
//
//				break;
//			}
			VFSFileClose (dirRef);
		} 
//		else 
//		{
			// handle directory open error here
//
//			break;
//		}
//		if (cur_entry>=g_num_entries) break;
//	}

	qsort(languages, num, sizeof(char*), LanguageCompareFunc);
	return num;
}

void ShowInfo(const char *str)
{
/*
	bool old;
	_TCHAR strWide[MAX_WIDE_STR];

	ReleaseCapture();
	_left_button_clicked =_left_button_down = false;
	old = MyShowCursor(true);
	MULTI_TO_WIDE( str, strWide, sizeof(strWide) );
	MessageBox(GetActiveWindow(), strWide, TEXT("OpenTTD"), MB_ICONINFORMATION | MB_OKCANCEL);

	MyShowCursor(old);
*/

	ErrDisplay(str);
/*
	FileRef f;
	char strText[260];
	
	StrPrintF(strText, "%s%s", _path.personal_dir, "TTDERROR.TXT");
	f = fopen(strText,"a");
	if (f == NULL) 
	{
		ErrDisplay(str);
		return;
	}
	
	fwrite(str, StrLen(str), 1, f);
	
	fclose(f);
*/
}

/* Checks if rectA overlaps rectB */
__inline bool DoRectsOverlap(Rect rectA, Rect rectB)
{
	if( (rectA.left >= rectB.left 		&& // top left
		 rectA.left <= rectB.right 		&&
		 rectA.top >= rectB.top 		&&
		 rectA.top <= rectB.bottom) 	||
		(rectA.right >= rectB.left 		&& // top right
		 rectA.right <= rectB.right 	&&
		 rectA.top >= rectB.top 		&&
		 rectA.top <= rectB.bottom) 	|| 
		(rectA.left >= rectB.left 		&& // bottom left
		 rectA.left <= rectB.right 		&&
		 rectA.bottom >= rectB.top 		&&
		 rectA.bottom <= rectB.bottom) 	|| 
		(rectA.right >= rectB.left 		&& // bottom right
		 rectA.right <= rectB.right 	&&
		 rectA.bottom >= rectB.top 		&&
		 rectA.bottom <= rectB.bottom) 	)
	{
		// rects overlap
		return true;
	}
	
	// rects do not overlap
	return false; 
}
				
void CallDrawSurfaceToScreen()
{
	// TESTING
	UInt8* buffer;
	UInt32 x;
	UInt32 y;
	UInt32 i;
	
	if( _num_dirty_rects == 0 )
	{
		return;
	}
	
	if( _num_dirty_rects >= MAX_DIRTY_RECTS )
	{	
  		buffer = (UInt8*) g_pnobridge->vram;
		for( y = screenrect.top; y < screenrect.bottom; y++ )
		{	
			for ( x = screenrect.left; x < screenrect.right; x++ )
			{
				*buffer++ = (UInt8) pal[_zod_video_mem[_screen.width * y + x]];
				*buffer++ = (UInt8) ( pal[_zod_video_mem[_screen.width * y + x]] >> 8 );
			}
		}		
	}
	else
	{
		buffer = (UInt8*) g_pnobridge->vram;
		for(i = 0; i < _num_dirty_rects; i++)
		{
			// boundry check for one point of the dirty rect existing in the screenrect
			//if( DoRectsOverlap( _dirty_rects[i], screenrect ) )
			//{
				// clip rect
				Rect _clipped_rect;
				_clipped_rect.top = _dirty_rects[i].top - screenrect.top;
				_clipped_rect.bottom = _clipped_rect.top + (_dirty_rects[i].bottom - _dirty_rects[i].top);
				_clipped_rect.left = _dirty_rects[i].left - screenrect.left;
				_clipped_rect.right = _clipped_rect.left + (_dirty_rects[i].right - _dirty_rects[i].left);
				
				if( _clipped_rect.top > display_height ) _clipped_rect.top = display_height;
				if( _clipped_rect.top < 0 ) _clipped_rect.top = 0;
				
				if( _clipped_rect.bottom > display_height ) _clipped_rect.bottom = display_height;
				if( _clipped_rect.bottom < 0 ) _clipped_rect.bottom = 0;
				
				if( _clipped_rect.left > display_width ) _clipped_rect.left = display_width;
				if( _clipped_rect.left < 0 ) _clipped_rect.left = 0;
				
				if( _clipped_rect.right > display_width ) _clipped_rect.right = display_width;
				if( _clipped_rect.right < 0 ) _clipped_rect.right = 0;	

				for( y = _clipped_rect.top; y < _clipped_rect.bottom; y++ )
				{	
					for ( x = _clipped_rect.left; x < _clipped_rect.right; x++ )
					{
						*(UInt8 *)(buffer + (x * 2) + (y * (display_width * 2))) = (UInt8) pal[_zod_video_mem[_screen.width * (screenrect.top+y) + (screenrect.left+x)]];
						*(UInt8 *)(buffer + (x * 2) + (y * (display_width * 2)) + 1) = (UInt8) ( pal[_zod_video_mem[_screen.width * (screenrect.top+y) + (screenrect.left+x)]] >> 8 );
					}
				}
			//}
		}
	}
		
	_num_dirty_rects = 0;
}


bool InsertTextBufferClipboard(Textbuf *tb)
{
	return false;
}
