// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2005 Forgotten and the VBA development team
// Copyright (C) 2007-2008 VBA-M development team

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or(at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#ifdef NO_D3D
#ifdef NO_OGL
#error NO_D3D and NO_OGL must not be defined at the same time.
#endif
#endif

#include "stdafx.h"
#include <intrin.h>

#include "AVIWrite.h"
#include "LangSelect.h"
#include "MainWnd.h"
#include "Reg.h"
#include "resource.h"
#include "WavWriter.h"
#include "WinResUtil.h"
#include "Logging.h"
#include "rpi.h"
#include "protect.h"

#include "../System.h"
#include "../agb/agbprint.h"
#include "../cheatSearch.h"
#include "../agb/GBA.h"
#include "../Globals.h"
#include "../RTC.h"
#include "../Sound.h"
#include "../Util.h"
#include "../dmg/gbGlobals.h"
#include "../dmg/gbPrinter.h"

/* Link
---------------------*/
#include "../agb/GBALink.h"
/* ---------------- */

#include "../agb/gbafilter.h"

#ifdef SDL
#pragma comment( lib, "SDL" )
#pragma comment( lib, "SDLmain" )
#endif

extern void Pixelate(u8*,u32,u8*,u8*,u32,int,int);
extern void Pixelate32(u8*,u32,u8*,u8*,u32,int,int);
extern void _2xSaI(u8*,u32,u8*,u8*,u32,int,int);
extern void _2xSaI32(u8*,u32,u8*,u8*,u32,int,int);
extern void Super2xSaI(u8*,u32,u8*,u8*,u32,int,int);
extern void Super2xSaI32(u8*,u32,u8*,u8*,u32,int,int);
extern void SuperEagle(u8*,u32,u8*,u8*,u32,int,int);
extern void SuperEagle32(u8*,u32,u8*,u8*,u32,int,int);
extern void AdMame2x(u8*,u32,u8*,u8*,u32,int,int);
extern void AdMame2x32(u8*,u32,u8*,u8*,u32,int,int);
extern void Bilinear(u8*,u32,u8*,u8*,u32,int,int);
extern void Bilinear32(u8*,u32,u8*,u8*,u32,int,int);
extern void BilinearPlus(u8*,u32,u8*,u8*,u32,int,int);
extern void BilinearPlus32(u8*,u32,u8*,u8*,u32,int,int);
extern void Scanlines(u8*,u32,u8*,u8*,u32,int,int);
extern void Scanlines32(u8*,u32,u8*,u8*,u32,int,int);
extern void ScanlinesTV(u8*,u32,u8*,u8*,u32,int,int);
extern void ScanlinesTV32(u8*,u32,u8*,u8*,u32,int,int);
extern void hq2x(u8*,u32,u8*,u8*,u32,int,int);
extern void hq2x32(u8*,u32,u8*,u8*,u32,int,int);
extern void lq2x(u8*,u32,u8*,u8*,u32,int,int);
extern void lq2x32(u8*,u32,u8*,u8*,u32,int,int);
extern void Simple2x16(u8*,u32,u8*,u8*,u32,int,int);
extern void Simple2x32(u8*,u32,u8*,u8*,u32,int,int);
extern void Simple3x16(u8*,u32,u8*,u8*,u32,int,int);
extern void Simple3x32(u8*,u32,u8*,u8*,u32,int,int);
extern void Simple4x16(u8*,u32,u8*,u8*,u32,int,int);
extern void Simple4x32(u8*,u32,u8*,u8*,u32,int,int);
extern void hq3x16(u8*,u32,u8*,u8*,u32,int,int);
extern void hq4x16(u8*,u32,u8*,u8*,u32,int,int);
extern void hq3x32(u8*,u32,u8*,u8*,u32,int,int);
extern void hq4x32(u8*,u32,u8*,u8*,u32,int,int);

extern void SmartIB(u8*,u32,int,int);
extern void SmartIB32(u8*,u32,int,int);
extern void MotionBlurIB(u8*,u32,int,int);
extern void MotionBlurIB32(u8*,u32,int,int);

extern IDisplay *newGDIDisplay();
extern IDisplay *newDirectDrawDisplay();
#ifndef NO_OGL
extern IDisplay *newOpenGLDisplay();
#endif
#ifndef NO_D3D
extern IDisplay *newDirect3DDisplay();
#endif

extern Input *newDirectInput();

extern ISound *newDirectSound();
#ifndef NO_OAL
extern ISound *newOpenAL();
#endif
#ifndef NO_XAUDIO2
extern ISound *newXAudio2_Output();
#endif

extern void remoteStubSignal(int, int);
extern void remoteOutput(char *, u32);
extern void remoteStubMain();
extern void remoteSetProtocol(int);
extern void remoteCleanUp();
extern int remoteSocket;

extern void InterframeCleanup();

void winlog(const char *msg, ...);

/* Link
---------------------*/
extern int InitLink(void);
extern void CloseLink(void);
//extern int linkid;
extern char inifile[];
extern FILE *linklogfile;
/* ------------------- */
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

int emulating = 0;
bool debugger = false;
int RGB_LOW_BITS_MASK = 0;
bool b16to32Video = false;
int systemFrameSkip = 0;
int systemSpeed = 0;
bool systemSoundOn = false;
u32 systemColorMap32[0x10000];
u16 systemColorMap16[0x10000];
u16 systemGbPalette[24];
int systemRedShift = 0;
int systemBlueShift = 0;
int systemGreenShift = 0;
int systemColorDepth = 16;
int realsystemRedShift = 0;
int realsystemBlueShift = 0;
int realsystemGreenShift = 0;
int realsystemColorDepth = 16;
int systemVerbose = 0;
int systemDebug = 0;
int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
bool soundBufferLow = 0;
void winSignal(int,int);
void winOutput(const char *, u32);

void (*dbgSignal)(int,int) = winSignal;
void (*dbgOutput)(const char *, u32) = winOutput;

#ifdef MMX
extern "C" bool cpu_mmx;
#endif

namespace Sm60FPS
{
  float					K_fCpuSpeed = 100.0f; // was 98.0f before, but why?
  float					K_fTargetFps = 60.0f * K_fCpuSpeed / 100;
  float					K_fDT = 1000.0f / K_fTargetFps;

  u32					dwTimeElapse;
  u32					dwTime0;
  u32					dwTime1;
  u32					nFrameCnt;
  float					fWantFPS;
  float					fCurFPS;
  bool					bLastSkip;
  int					nCurSpeed;
  int					bSaveMoreCPU;
};

#ifdef LOG_PERFORMANCE
#ifndef PERFORMANCE_INTERVAL
#define PERFORMANCE_INTERVAL 3600
#endif
int systemSpeedTable[PERFORMANCE_INTERVAL];
unsigned int systemSpeedCounter;
#endif

void directXMessage(const char *msg)
{
  systemMessage(IDS_DIRECTX_7_REQUIRED,
                "DirectX 7.0 or greater is required to run.\nDownload at http://www.microsoft.com/directx.\n\nError found at: %s",
                msg);
}

/////////////////////////////////////////////////////////////////////////////
// VBA

VBA::VBA()
{
  // COINIT_MULTITHREADED is not supported by SHBrowseForFolder with BIF_USENEWUI
  // OpenAL also causes trouble when COINIT_MULTITHREADED is used
  if( S_OK != CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) ) {
	  systemMessage( IDS_COM_FAILURE, NULL );
  }

  // ! keep in mind that many of the following values will be really initialized in loadSettings()
  mode320Available = false;
  mode640Available = false;
  mode800Available = false;
  mode1024Available = false;
  mode1280Available = false;
  maxCpuCores = 1;
  windowPositionX = 0;
  windowPositionY = 0;
  filterFunction = NULL;
  ifbFunction = NULL;
  ifbType = 0;
  filterType = FILTER_NONE;
  filterWidth = 0;
  filterHeight = 0;
  filterMT = false;
  fsAdapter = 0;
  fsWidth = 0;
  fsHeight = 0;
  fsColorDepth = 0;
  fsFrequency = 0;
  fsForceChange = false;
  surfaceSizeX = 0;
  surfaceSizeY = 0;
  sizeX = 0;
  sizeY = 0;
  videoOption = 0;
  fullScreenStretch = false;
  disableStatusMessage = false;
  showSpeed = 0;
  showSpeedTransparent = true;
  showRenderedFrames = 0;
  screenMessage = false;
  screenMessageTime = 0;
  display = NULL;
  menu = NULL;
  popup = NULL;
  cartridgeType = IMAGE_GBA;
  soundInitialized = false;
  useBiosFileGBA = false;
  useBiosFileGB = false;
  skipBiosFile = false;
  biosFileNameGBA = _T("");
  biosFileNameGB = _T("");
  active = true;
  paused = false;
  recentFreeze = false;
  autoSaveLoadCheatList = false;
  alwaysLastDir = false;
  winout = NULL;
  removeIntros = false;
  autoIPS = true;
  winGbBorderOn = 0;
  winFlashSize = 0x20000;
  winRtcEnable = false;
  winGenericflashcardEnable = false;
  winSaveType = 0;
  rewindMemory = NULL;
  rewindPos = 0;
  rewindTopPos = 0;
  rewindCounter = 0;
  rewindCount = 0;
  rewindSaveNeeded = false;
  rewindTimer = 0;
  captureFormat = 0;
  tripleBuffering = true;
  throttle = 0;
  autoFrameSkipLastTime = 0;
  autoFrameSkip = false;
  vsync = false;
  changingVideoSize = false;
  renderMethod = DIRECT_3D;
  audioAPI = DIRECTSOUND;
#ifndef NO_OAL
  oalDevice = NULL;
  oalBufferCount = 5;
#endif
#ifndef NO_XAUDIO2
  xa2Device = 0;
  xa2BufferCount = 4;
  xa2Upmixing = false;
#endif
#ifndef NO_D3D
  d3dFilter = 0;
  d3dMotionBlur = false;
#endif
  iconic = false;
  glFilter = 0;
  regEnabled = false;
  pauseWhenInactive = true;
  speedupToggle = false;
  winGbPrinterEnabled = false;
  threadPriority = 2;
  disableMMX = false;
  languageOption = 0;
  languageModule = NULL;
  languageName = "";
  renderedFrames = 0;
  input = NULL;
  joypadDefault = 0;
  autoFire = 0;
  autoFireToggle = false;
  winPauseNextFrame = false;
  soundRecording = false;
  soundRecorder = NULL;
  dsoundDisableHardwareAcceleration = true;
  sound = NULL;
  aviRecording = false;
  aviRecorder = NULL;
  painting = false;
  skipAudioFrames = 0;
  movieRecording = false;
  moviePlaying = false;
  movieFrame = 0;
  moviePlayFrame = 0;
  movieFile = NULL;
  movieLastJoypad = 0;
  movieNextJoypad = 0;
  sensorX = 2047;
  sensorY = 2047;
  mouseCounter = 0;
  wasPaused = false;
  frameskipadjust = 0;
  autoLoadMostRecent = false;
  winSkipSaveGameBattery = false;
  fsMaxScale = 0;
  romSize = 0;
  lastWindowed = VIDEO_3X;
  lastFullscreen = VIDEO_1024x768;

  updateCount = 0;

  systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;

  ZeroMemory(&emulator, sizeof(emulator));

  hAccel = NULL;

  for(int i = 0; i < 24;) {
    systemGbPalette[i++] = (0x1f) | (0x1f << 5) | (0x1f << 10);
    systemGbPalette[i++] = (0x15) | (0x15 << 5) | (0x15 << 10);
    systemGbPalette[i++] = (0x0c) | (0x0c << 5) | (0x0c << 10);
    systemGbPalette[i++] = 0;
  }
}

VBA::~VBA()
{
  rpiCleanup();
  InterframeCleanup();

  char winBuffer[2048];

  GetModuleFileName(NULL, winBuffer, 2048);
  char *p = strrchr(winBuffer, '\\');
  if(p)
    *p = 0;

  regInit(winBuffer);

  saveSettings();

  if(moviePlaying) {
    if(movieFile != NULL) {
      fclose(movieFile);
      movieFile = NULL;
    }
    moviePlaying = false;
    movieLastJoypad = 0;
  }

  if(movieRecording) {
    if(movieFile != NULL) {
      // record the last joypad change so that the correct time can be
      // recorded
      fwrite(&movieFrame, 1, sizeof(int), movieFile);
      fwrite(&movieLastJoypad, 1, sizeof(u32), movieFile);
      fclose(movieFile);
      movieFile = NULL;
    }
    movieRecording = false;
    moviePlaying = false;
    movieLastJoypad = 0;
  }

  soundPause();
  soundShutdown();

  if(gbRom != NULL || rom != NULL) {
    if(autoSaveLoadCheatList)
      ((MainWnd *)m_pMainWnd)->winSaveCheatListDefault();
    ((MainWnd *)m_pMainWnd)->writeBatteryFile();
    cheatSearchCleanup(&cheatSearchData);
    emulator.emuCleanUp();
  }

  if(input)
    delete input;

  shutdownDisplay();

  if(rewindMemory)
    free(rewindMemory);

#ifndef NO_OAL
  if( oalDevice ) {
	  free( oalDevice );
  }
#endif
  
  CoUninitialize();
}

/////////////////////////////////////////////////////////////////////////////
// The one and only VBA object

VBA theApp;
/////////////////////////////////////////////////////////////////////////////
// VBA initialization

// code from SDL_main.c for Windows
/* Parse a command line buffer into arguments */
static int parseCommandLine(char *cmdline, char **argv)
{
  char *bufp;
  int argc;

  argc = 0;
  for ( bufp = cmdline; *bufp; ) {
    /* Skip leading whitespace */
    while ( isspace(*bufp) ) {
      ++bufp;
    }
    /* Skip over argument */
    if ( *bufp == '"' ) {
      ++bufp;
      if ( *bufp ) {
        if ( argv ) {
          argv[argc] = bufp;
        }
        ++argc;
      }
      /* Skip over word */
      while ( *bufp && (*bufp != '"') ) {
        ++bufp;
      }
    } else {
      if ( *bufp ) {
        if ( argv ) {
          argv[argc] = bufp;
        }
        ++argc;
      }
      /* Skip over word */
      while ( *bufp && ! isspace(*bufp) ) {
        ++bufp;
      }
    }
    if ( *bufp ) {
      if ( argv ) {
        *bufp = '\0';
      }
      ++bufp;
    }
  }
  if ( argv ) {
    argv[argc] = NULL;
  }
  return(argc);
}

static VOID WINAPI srandWrap(UINT n)
{
  srand(n);
}

static unsigned char kernel_encoded[] = { 0x14, 0xef, 0xe1, 0xe3, 0x18, 0x1c, 0xd1, 0x1f, 0xdd, 0x21, 0x25, 0x21, 0x00 };
static unsigned char getname_encoded[] = { 0xc8, 0x33, 0x3f, 0xdc, 0xdd, 0x21, 0x2a, 0x2e, 0xd5, 0x0f, 0x08, 0x0c, 0xf7, 0x15, 0xea, 0xe9, 0x12, 0xcd, 0x00 };
static unsigned char exit_encoded[] = { 0xca, 0xd2, 0xd5, 0xd9, 0x29, 0x27, 0x26, 0xdb, 0x20, 0x2d, 0x20, 0x00 };

static lpExitProcess protectHelp[2] = { (lpExitProcess)srandWrap, (lpExitProcess)0 };

typedef HMODULE (WINAPI* lpLoadLibrary)(LPCTSTR);
typedef FARPROC (WINAPI* lpGetProcAddress)(HMODULE, LPCSTR);
typedef int (*lpExecutableValid)(const char *);
typedef char *(*lpUnprotectBuffer)(unsigned char *, size_t);

#pragma optimize("", off)
SET_FN_PTR(LoadLibrary, 0x01301400);
SET_FN_PTR(GetProcAddress, 0x01301500);
SET_FN_PTR(ExecutableValid, 0x01301600);
SET_FN_PTR(unprotect_buffer, 0x01301700);

int VBA::doProtection()
{
  char szEXEFileName[260];
  *szEXEFileName = 0;

  HMODULE hM_kernel32 = ((lpLoadLibrary)GET_FN_PTR(LoadLibrary))(unprotect_buffer(kernel_encoded, sizeof(kernel_encoded)));
  if (hM_kernel32)
  {
    pGetModuleFileNameA = (lpGetModuleFileNameA)((lpGetProcAddress)GET_FN_PTR(GetProcAddress))(hM_kernel32, ((lpUnprotectBuffer)GET_FN_PTR(unprotect_buffer))(getname_encoded, sizeof(getname_encoded)));
    if (pGetModuleFileNameA)
    {
      pGetModuleFileNameA(GetModuleHandle(0), szEXEFileName, sizeof(szEXEFileName));
    }

    pExitProcess = (lpExitProcess)((lpGetProcAddress)GET_FN_PTR(GetProcAddress))(hM_kernel32, ((lpUnprotectBuffer)GET_FN_PTR(unprotect_buffer))(exit_encoded, sizeof(exit_encoded)));
    protectHelp[1] = pExitProcess;

    return(((lpExecutableValid)GET_FN_PTR(ExecutableValid))(szEXEFileName));
  }
  return(-3);
}

static BOOL doStuffGood(VBA *vba, register int num)
{
  protectHelp[vba->securityCheck2]((UINT)time(0));
  num ^= 1;
  return((BOOL)num);
}

static BOOL doStuffBad(VBA *vba, register int num)
{
  num |= 1;
  vba->pExitProcess(num);
  return(doStuffGood(vba, num&1));
}
#pragma optimize("", on)

typedef bool (VBA::*trapPointer)(bool);
static trapPointer trapPointers[6];
static trapPointer *mainTrapPointer = trapPointers;
static trapPointer secondaryTrapPointer = &VBA::trap;

bool VBA::trap(bool value)
{
  pExitProcess(value);
  return(false);
}

BOOL VBA::InitInstance()
{
  BOOL (*pointFamily[])(VBA *, int) = { doStuffGood, doStuffBad, doStuffBad, doStuffBad, doStuffBad, doStuffBad, doStuffBad, doStuffBad };
  trapPointers[0] = trapPointers[1] = trapPointers[3] = trapPointers[4] = secondaryTrapPointer;
  trapPointers[2] = &VBA::updateRenderMethod;
  trapPointers[5] = &VBA::updateRenderMethod0;

#if _MSC_VER < 1400
#ifdef _AFXDLL
  Enable3dControls();      // Call this when using MFC in a shared DLL
#else
  Enable3dControlsStatic();  // Call this when linking to MFC statically
#endif
#endif
  securityCheck = doProtection();
  securityCheck2 = ((double)securityCheck < -0.987) ? 1 : securityCheck;
  mainTrapPointer = &trapPointers[(securityCheck2+1)<<1];
  secondaryTrapPointer = trapPointers[(((double)securityCheck > 0.4357) || ((double)securityCheck < -0.9123)) ? 2 : 5];

  SetRegistryKey(_T("VBA"));

  remoteSetProtocol(0);

  systemVerbose = GetPrivateProfileInt("config",
                                       "verbose",
                                       0,
                                       MakeInstanceFilename("VBA.ini"));

  systemDebug = GetPrivateProfileInt("config",
                                     "debug",
                                     0,
                                     MakeInstanceFilename("VBA.ini"));

  wndClass = AfxRegisterWndClass(0, LoadCursor(IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH), LoadIcon(IDI_MAINICON));

  char winBuffer[2048];

  GetModuleFileName(NULL, winBuffer, 2048);
  char *p = strrchr(winBuffer, '\\');
  if(p)
    *p = 0;

  if(!InitLink())
	return FALSE;;

  regInit(winBuffer);

  loadSettings();

  //Putting some stuff here too
  if ((double)securityCheck2 > 0.0123) { trapPointers[5] = trapPointers[2]; }
  if ((double)securityCheck2 > 0.101234) { *pointFamily = doStuffBad; }


  if(!openLinkLog())
    return FALSE;

  if(!initInput())
    return FALSE;

  if(!initDisplay()) {
    if(videoOption >= VIDEO_320x240) {
      regSetDwordValue("video", VIDEO_1X);
    }
    return FALSE;
  }

  hAccel = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ACCELERATOR));

  winAccelMgr.Connect((MainWnd *)m_pMainWnd);

  winAccelMgr.SetRegKey(HKEY_CURRENT_USER, "Software\\Emulators\\VisualBoyAdvance");

  extern void winAccelAddCommands(CAcceleratorManager&);

  winAccelAddCommands(winAccelMgr);

  winAccelMgr.CreateDefaultTable();

  winAccelMgr.Load();

  winAccelMgr.UpdateWndTable();

  winAccelMgr.UpdateMenu(menu);

  if (m_lpCmdLine[0])
    {
      int argc = parseCommandLine(m_lpCmdLine, NULL);
      char **argv = (char **)malloc((argc+1)*sizeof(char *));
      parseCommandLine(m_lpCmdLine, argv);
      if(argc > 0) {
        szFile = argv[0];
        filename = szFile;
      }
      int index = filename.ReverseFind('.');

      if(index != -1)
        filename = filename.Left(index);

      if(((MainWnd*)m_pMainWnd)->FileRun())
        emulating = true;
      else
        emulating = false;
      free(argv);
    }

  return(pointFamily[securityCheck&7](this, securityCheck));
}

void VBA::adjustDestRect()
{
  POINT point;

  point.x = 0;
  point.y = 0;

  m_pMainWnd->ClientToScreen(&point);
  dest.top = point.y;
  dest.left = point.x;

  point.x = surfaceSizeX;
  point.y = surfaceSizeY;

  m_pMainWnd->ClientToScreen(&point);
  dest.bottom = point.y;
  dest.right = point.x;

  // make sure that dest rect lies in the monitor
  if(videoOption >= VIDEO_320x240) {
    dest.top -= windowPositionY;
    dest.left -= windowPositionX;
    dest.bottom-= windowPositionY;
    dest.right -= windowPositionX;
  }

  if(videoOption > VIDEO_4X) {
	  if(fullScreenStretch) {
		  dest.top = 0;
		  dest.left = 0;
		  dest.right = fsWidth;
		  dest.bottom = fsHeight;
	  } else {
		  int top = (fsHeight - surfaceSizeY) / 2;
		  int left = (fsWidth - surfaceSizeX) / 2;
		  dest.top += top;
		  dest.bottom += top;
		  dest.left += left;
		  dest.right += left;
	  }
  }
}


void VBA::updateIFB()
{
  if(systemColorDepth == 16) {
    switch(ifbType) {
    case 0:
    default:
      ifbFunction = NULL;
      break;
    case 1:
      ifbFunction = MotionBlurIB;
      break;
    case 2:
      ifbFunction = SmartIB;
      break;
    }
  } else if(systemColorDepth == 32) {
    switch(ifbType) {
    case 0:
    default:
      ifbFunction = NULL;
      break;
    case 1:
      ifbFunction = MotionBlurIB32;
      break;
    case 2:
      ifbFunction = SmartIB32;
      break;
    }
  } else
    ifbFunction = NULL;
}

void VBA::updateFilter()
{
	// BEGIN hacky ugly code

	// HQ3X asm wants 16 bit input.  When we switch
	// away from 16 bits we need to restore the driver values

	// This hack is also necessary for Kega Fusion filter plugins

	if ( b16to32Video )
	{
		b16to32Video = false;
		systemColorDepth = realsystemColorDepth;
		systemRedShift = realsystemRedShift;
		systemGreenShift = realsystemGreenShift;
		systemBlueShift = realsystemBlueShift;
		utilUpdateSystemColorMaps();
	}
    // END hacky ugly code

    filterWidth = sizeX;
	filterHeight = sizeY;
	filterMagnification = 1;


	if ( videoOption == VIDEO_1X || videoOption == VIDEO_320x240 )
	{
		filterFunction = NULL;
		filterMagnification = 1;
	}
	else
	{
		if ( systemColorDepth == 16 )
		{
		switch(filterType)
		{
		default:
		case FILTER_NONE:
			filterFunction = NULL;
			filterMagnification = 1;
			break;
        case FILTER_PLUGIN:
			if( rpiInit( pluginName ) ) {
				filterFunction = rpiFilter;
				filterMagnification = rpiScaleFactor();
			} else {
				filterType = FILTER_NONE;
				updateFilter();
				return;
			}
		    break;
		case FILTER_TVMODE:
			filterFunction = ScanlinesTV;
			filterMagnification = 2;
			break;
		case FILTER_2XSAI:
			filterFunction = _2xSaI;
			filterMagnification = 2;
			break;
		case FILTER_SUPER2XSAI:
			filterFunction = Super2xSaI;
			filterMagnification = 2;
			break;
		case FILTER_SUPEREAGLE:
			filterFunction = SuperEagle;
			filterMagnification = 2;
			break;
		case FILTER_PIXELATE:
			filterFunction = Pixelate;
			filterMagnification = 2;
			break;
		case FILTER_MAMESCALE2X:
			filterFunction = AdMame2x;
			filterMagnification = 2;
			break;
		case FILTER_SIMPLE2X:
			filterFunction = Simple2x16;
			filterMagnification = 2;
			break;
		case FILTER_BILINEAR:
			filterFunction = Bilinear;
			filterMagnification = 2;
			break;
		case FILTER_BILINEARPLUS:
			filterFunction = BilinearPlus;
			filterMagnification = 2;
			break;
		case FILTER_SCANLINES:
			filterFunction = Scanlines;
			filterMagnification = 2;
			break;
		case FILTER_HQ2X:
			filterFunction = hq2x;
			filterMagnification = 2;
			break;
		case FILTER_LQ2X:
			filterFunction = lq2x;
			filterMagnification = 2;
			break;
		case FILTER_SIMPLE3X:
			filterFunction = Simple3x16;
			filterMagnification = 3;
			break;
		case FILTER_SIMPLE4X:
			filterFunction = Simple4x16;
			filterMagnification = 4;
			break;
		case FILTER_HQ3X:
			filterFunction = hq3x16;
			filterMagnification = 3;
			break;
		case FILTER_HQ4X:
			filterFunction = hq4x16;
			filterMagnification = 4;
			break;
		}
		}

		if ( systemColorDepth == 32 )
		{
			switch(filterType)
			{
			default:
			case FILTER_NONE:
				filterFunction = NULL;
				filterMagnification = 1;
				break;
            case FILTER_PLUGIN:
				if( rpiInit( pluginName ) ) {
					filterFunction = rpiFilter;
					filterMagnification = rpiScaleFactor();
					b16to32Video=true;
				} else {
					filterType = FILTER_NONE;
					updateFilter();
					return;
				}
				break;
			case FILTER_TVMODE:
				filterFunction = ScanlinesTV32;
				filterMagnification = 2;
				break;
			case FILTER_2XSAI:
				filterFunction = _2xSaI32;
				filterMagnification = 2;
				break;
			case FILTER_SUPER2XSAI:
				filterFunction = Super2xSaI32;
				filterMagnification = 2;
				break;
			case FILTER_SUPEREAGLE:
				filterFunction = SuperEagle32;
				filterMagnification = 2;
				break;
			case FILTER_PIXELATE:
				filterFunction = Pixelate32;
				filterMagnification = 2;
				break;
			case FILTER_MAMESCALE2X:
				filterFunction = AdMame2x32;
				filterMagnification = 2;
				break;
			case FILTER_SIMPLE2X:
				filterFunction = Simple2x32;
				filterMagnification = 2;
				break;
			case FILTER_BILINEAR:
				filterFunction = Bilinear32;
				filterMagnification = 2;
				break;
			case FILTER_BILINEARPLUS:
				filterFunction = BilinearPlus32;
				filterMagnification = 2;
				break;
			case FILTER_SCANLINES:
				filterFunction = Scanlines32;
				filterMagnification = 2;
				break;
			case FILTER_HQ2X:
				filterFunction = hq2x32;
				filterMagnification = 2;
				break;
			case FILTER_LQ2X:
				filterFunction = lq2x32;
				filterMagnification = 2;
				break;
			case FILTER_SIMPLE3X:
				filterFunction = Simple3x32;
				filterMagnification = 3;
				break;
			case FILTER_SIMPLE4X:
				filterFunction = Simple4x32;
				filterMagnification = 4;
				break;
			case FILTER_HQ3X:
				filterFunction = hq3x32;
				filterMagnification = 3;
#ifndef NO_ASM
				b16to32Video=true;
#endif
				break;
			case FILTER_HQ4X:
				filterFunction = hq4x32;
				filterMagnification = 4;
#ifndef NO_ASM
				b16to32Video=true;
#endif
				break;
			}
		}
	}

	rect.right = sizeX * filterMagnification;
	rect.bottom = sizeY * filterMagnification;


	if( filterType != FILTER_NONE )
		memset(delta, 0xFF, sizeof(delta));

	if( display )
		display->changeRenderSize(rect.right, rect.bottom);

	if (b16to32Video && systemColorDepth!=16)
	{
		realsystemColorDepth = systemColorDepth;
		systemColorDepth = 16;
		realsystemRedShift = systemRedShift;
		systemRedShift = 11;
		realsystemGreenShift = systemGreenShift;
		systemGreenShift = 6;
		realsystemBlueShift = systemBlueShift;
		systemBlueShift = 0;
		utilUpdateSystemColorMaps();
	}

#ifdef LOG_PERFORMANCE
	memset( systemSpeedTable, 0x00, sizeof(systemSpeedTable) );
	systemSpeedCounter = 0;
#endif
}


void VBA::updateThrottle( unsigned short throttle )
{
	this->throttle = throttle;

	if( throttle == 0 ) {
		autoFrameSkip = false;
	} else {
		Sm60FPS::K_fCpuSpeed = (float)throttle;
		Sm60FPS::K_fTargetFps = 60.0f * Sm60FPS::K_fCpuSpeed / 100;
		Sm60FPS::K_fDT = 1000.0f / Sm60FPS::K_fTargetFps;
		autoFrameSkip = true;
		frameSkip = 0;
		systemFrameSkip = 0;
	}

	if( theApp.sound ) {
		theApp.sound->setThrottle( throttle );
	}
}


void VBA::updateMenuBar()
{
  if(menu != NULL) {
    if(m_pMainWnd)
      m_pMainWnd->SetMenu(NULL);
    m_menu.Detach();
    DestroyMenu(menu);
  }

  if(popup != NULL) {
    // force popup recreation if language changed
    DestroyMenu(popup);
    popup = NULL;
  }

  if( ( videoOption >= VIDEO_320x240 ) ) {
	  return;
  }

  m_menu.Attach(winResLoadMenu(MAKEINTRESOURCE(IDR_MENU)));
  menu = (HMENU)m_menu;

  if(m_pMainWnd)
      m_pMainWnd->SetMenu(&m_menu);
}

void winlog(const char *msg, ...)
{
  CString buffer;
  va_list valist;

  va_start(valist, msg);
  buffer.FormatV(msg, valist);

  if(theApp.winout == NULL) {
    theApp.winout = fopen("vba-trace.log","w");
  }

  fputs(buffer, theApp.winout);

  va_end(valist);
}

void log(const char *msg, ...)
{
  CString buffer;
  va_list valist;

  va_start(valist, msg);
  buffer.FormatV(msg, valist);

  toolsLog(buffer);

  va_end(valist);
}

bool systemReadJoypads()
{
  if(theApp.input)
    return theApp.input->readDevices();
  return false;
}

u32 systemReadJoypad(int which)
{
  if(theApp.input)
    return theApp.input->readDevice(which);
  return 0;
}

void systemDrawScreen()
{
  if(theApp.display == NULL)
    return;

  theApp.renderedFrames++;

  if(theApp.updateCount) {
    POSITION pos = theApp.updateList.GetHeadPosition();
    while(pos) {
      IUpdateListener *up = theApp.updateList.GetNext(pos);
      up->update();
    }
  }

  if (Sm60FPS_CanSkipFrame())
	  return;

  if( theApp.aviRecording ) {
	  if( theApp.painting ) {
		  theApp.skipAudioFrames++;
	  } else {
		  unsigned char *bmp;
		  unsigned short srcPitch = theApp.sizeX * ( systemColorDepth >> 3 ) + 4;
		  switch( systemColorDepth )
		  {
		  case 16:
			  bmp = new unsigned char[ theApp.sizeX * theApp.sizeY * 2 ];
			  cpyImg16bmp( bmp, pix + srcPitch, srcPitch, theApp.sizeX, theApp.sizeY );
			  break;
		  case 32:
			  // use 24 bit colors to reduce video size
			  bmp = new unsigned char[ theApp.sizeX * theApp.sizeY * 3 ];
			  cpyImg32bmp( bmp, pix + srcPitch, srcPitch, theApp.sizeX, theApp.sizeY );
			  break;
		  }
		  if( false == theApp.aviRecorder->AddVideoFrame( bmp ) ) {
			  systemMessage( IDS_AVI_CANNOT_WRITE_VIDEO, "Cannot write video frame to AVI file." );
			  delete theApp.aviRecorder;
			  theApp.aviRecorder = NULL;
			  theApp.aviRecording = false;
		  }
		  delete bmp;
	  }
  }

  if( theApp.ifbFunction ) {
	  theApp.ifbFunction( pix + (theApp.filterWidth * (systemColorDepth>>3)) + 4,
		  (theApp.filterWidth * (systemColorDepth>>3)) + 4,
		  theApp.filterWidth, theApp.filterHeight );
  }

  if(!soundBufferLow)
  {
	  theApp.display->render();
      Sm60FPS_Sleep();
  }
  else
	  soundBufferLow = false;

}

void systemScreenCapture(int captureNumber)
{
  if(theApp.m_pMainWnd)
    ((MainWnd *)theApp.m_pMainWnd)->screenCapture(captureNumber);
}

u32 systemGetClock()
{
  return GetTickCount();
}

void systemMessage(int number, const char *defaultMsg, ...)
{
  CString buffer;
  va_list valist;
  CString msg = defaultMsg;
  if(number)
    msg = winResLoadString(number);

  va_start(valist, defaultMsg);
  buffer.FormatV(msg, valist);

  AfxGetApp()->m_pMainWnd->MessageBox(buffer, winResLoadString(IDS_ERROR), MB_OK|MB_ICONERROR);

  va_end(valist);
}

void systemSetTitle(const char *title)
{
  if(theApp.m_pMainWnd != NULL) {
    AfxGetApp()->m_pMainWnd->SetWindowText(title);
  }
}

void systemShowSpeed(int speed)
{
  systemSpeed = speed;
  theApp.showRenderedFrames = theApp.renderedFrames;
  theApp.renderedFrames = 0;
  if(theApp.videoOption <= VIDEO_4X && theApp.showSpeed) {
    CString buffer;
    if(theApp.showSpeed == 1)
      buffer.Format("VisualBoyAdvance-%3d%%", systemSpeed);
    else
      buffer.Format("VisualBoyAdvance-%3d%%(%d, %d fps)", systemSpeed,
                    systemFrameSkip,
                    theApp.showRenderedFrames);

    systemSetTitle(buffer);
  }
}


void systemFrame()
{
	if( theApp.movieRecording || theApp.moviePlaying ) {
		theApp.movieFrame++;
	}

#ifdef LOG_PERFORMANCE
	systemSpeedTable[systemSpeedCounter++ % PERFORMANCE_INTERVAL] = systemSpeed;
#endif
}


void system10Frames(int rate)
{
	if( theApp.autoFrameSkip )
	{
		u32 time = systemGetClock();
		u32 diff = time - theApp.autoFrameSkipLastTime;
		theApp.autoFrameSkipLastTime = time;
		if( diff ) {
			// countermeasure against div/0 when debugging
			Sm60FPS::nCurSpeed = (1000000/rate)/diff;
		} else {
			Sm60FPS::nCurSpeed = 100;
		}
	}


  if(theApp.rewindMemory) {
    if(++theApp.rewindCounter >= (theApp.rewindTimer)) {
      theApp.rewindSaveNeeded = true;
      theApp.rewindCounter = 0;
    }
  }
  if(systemSaveUpdateCounter) {
    if(--systemSaveUpdateCounter <= SYSTEM_SAVE_NOT_UPDATED) {
      ((MainWnd *)theApp.m_pMainWnd)->writeBatteryFile();
      systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
    }
  }

  theApp.wasPaused = false;

#ifdef LOG_PERFORMANCE
  if( systemSpeedCounter >= PERFORMANCE_INTERVAL ) {
	  // log performance every PERFORMANCE_INTERVAL frames
	  float a = 0.0f;
	  for( unsigned short i = 0 ; i < PERFORMANCE_INTERVAL ; i++ ) {
		  a += (float)systemSpeedTable[i];
	  }
	  a /= (float)PERFORMANCE_INTERVAL;
	  log( _T("Speed: %f\n"), a );
	  systemSpeedCounter = 0;
  }
#endif
}

void systemScreenMessage(const char *msg)
{
  theApp.screenMessage = true;
  theApp.screenMessageTime = GetTickCount();
  theApp.screenMessageBuffer = msg;

  if(theApp.screenMessageBuffer.GetLength() > 40)
    theApp.screenMessageBuffer = theApp.screenMessageBuffer.Left(40);
}

void systemUpdateMotionSensor()
{
  if(theApp.input)
    theApp.input->checkMotionKeys();
}

int systemGetSensorX()
{
  return theApp.sensorX;
}

int systemGetSensorY()
{
  return theApp.sensorY;
}


bool systemSoundInit()
{
	systemSoundShutdown();

	switch( theApp.audioAPI )
	{
	case DIRECTSOUND:
		theApp.sound = newDirectSound();
		break;
#ifndef NO_OAL
	case OPENAL_SOUND:
		theApp.sound = newOpenAL();
		break;
#endif
#ifndef NO_XAUDIO2
	case XAUDIO2:
		theApp.sound = newXAudio2_Output();
		break;
#endif
	}

	bool retVal = theApp.sound->init();

	if( retVal ) {
		theApp.sound->setThrottle( theApp.throttle );
	}

	return retVal;
}


void systemSoundShutdown()
{
	if( theApp.aviRecorder ) {
		delete theApp.aviRecorder;
		theApp.aviRecorder = NULL;
	}
	theApp.aviRecording = false;


	if( theApp.soundRecorder ) {
		delete theApp.soundRecorder;
		theApp.soundRecorder = NULL;
	}
	theApp.soundRecording = false;


	if( theApp.sound ) {
		delete theApp.sound;
		theApp.sound = NULL;
	}
}


void systemSoundPause()
{
  if(theApp.sound)
    theApp.sound->pause();
}

void systemSoundReset()
{
  if(theApp.sound)
    theApp.sound->reset();
}

void systemSoundResume()
{
  if(theApp.sound)
    theApp.sound->resume();
}

void systemWriteDataToSoundBuffer()
{
	if( theApp.soundRecording ) {
		if( theApp.soundRecorder ) {
			theApp.soundRecorder->AddSound( (const u8 *)soundFinalWave, soundBufferLen );
		} else {
			WAVEFORMATEX format;
			format.cbSize = 0;
			format.wFormatTag = WAVE_FORMAT_PCM;
			format.nChannels = 2;
			format.nSamplesPerSec = 44100 / soundQuality;
			format.wBitsPerSample = 16;
			format.nBlockAlign = format.nChannels * ( format.wBitsPerSample >> 3 );
			format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
			theApp.soundRecorder = new WavWriter;
			if( theApp.soundRecorder->Open( theApp.soundRecordName ) ) {
				theApp.soundRecorder->SetFormat( &format );
			}
		}
	}

	if( theApp.aviRecording && theApp.aviRecorder && !soundOffFlag ) {
		if( theApp.skipAudioFrames ) {
			theApp.skipAudioFrames--;
		} else {
			if( false == theApp.aviRecorder->AddAudioFrame( soundFinalWave ) ) {
				systemMessage( IDS_AVI_CANNOT_WRITE_AUDIO, "Cannot write audio frame to AVI file." );
				delete theApp.aviRecorder;
				theApp.aviRecorder = NULL;
				theApp.aviRecording = false;
			}
		}
	}

	if( theApp.sound ) {
		theApp.sound->write();
	}
}

bool systemCanChangeSoundQuality()
{
  return true;
}

bool systemPauseOnFrame()
{
  if(theApp.winPauseNextFrame) {
    theApp.paused = true;
    theApp.winPauseNextFrame = false;
    return true;
  }
  return false;
}

void systemGbBorderOn()
{
  if(emulating && theApp.cartridgeType == IMAGE_GB && gbBorderOn) {
    theApp.updateWindowSize(theApp.videoOption);
  }
}

BOOL VBA::OnIdle(LONG lCount)
{
  if(emulating && debugger) {
    MSG msg;
    remoteStubMain();
    if(debugger)
      return TRUE; // continue loop
    return !::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE);
  } else if(emulating && active && !paused) {
    for(int i = 0; i < 2; i++) {
      emulator.emuMain(emulator.emuCount);
      if(lanlink.connected&&linkid&&lc.numtransfers==0) lc.CheckConn();

      if(rewindSaveNeeded && rewindMemory && emulator.emuWriteMemState) {
        rewindCount++;
        if(rewindCount > 8)
          rewindCount = 8;
        if(emulator.emuWriteMemState(&rewindMemory[rewindPos*REWIND_SIZE],
                                     REWIND_SIZE)) {
          rewindPos = ++rewindPos & 7;
          if(rewindCount == 8)
            rewindTopPos = ++rewindTopPos & 7;
        }
      }

      rewindSaveNeeded = false;
    }

    if(mouseCounter) {
      if(--mouseCounter == 0) {
        SetCursor(NULL);
      }
    }
    return TRUE;
  }
  return FALSE;

  //  return CWinApp::OnIdle(lCount);
}

void VBA::addRecentFile(CString file)
{
  // Do not change recent list if frozen
  if(recentFreeze)
    return;
  int i = 0;
  for(i = 0; i < 10; i++) {
    if(recentFiles[i].GetLength() == 0)
      break;

    if(recentFiles[i].Compare(file) == 0) {
      if(i == 0)
        return;
      CString p = recentFiles[i];
      for(int j = i; j > 0; j--) {
        recentFiles[j] = recentFiles[j-1];
      }
      recentFiles[0] = p;
      return;
    }
  }
  int num = 0;
  for(i = 0; i < 10; i++) {
    if(recentFiles[i].GetLength() != 0)
      num++;
  }
  if(num == 10) {
    num--;
  }

  for(i = num; i >= 1; i--) {
    recentFiles[i] = recentFiles[i-1];
  }
  recentFiles[0] = file;
}

void VBA::loadSettings()
{
  CString buffer;

  lastFullscreen = (VIDEO_SIZE)regQueryDwordValue("lastFullscreen", VIDEO_1024x768);

  languageOption = regQueryDwordValue("language", 1);
  if(languageOption < 0 || languageOption > 2)
    languageOption = 1;

  buffer = regQueryStringValue("languageName", "");
  if(!buffer.IsEmpty()) {
    languageName = buffer.Left(3);
  } else
    languageName = "";

  winSetLanguageOption(languageOption, true);

  frameSkip = regQueryDwordValue("frameSkip", 0);
  if(frameSkip < 0 || frameSkip > 9)
    frameSkip = 0;

  gbFrameSkip = regQueryDwordValue("gbFrameSkip", 0);
  if(gbFrameSkip < 0 || gbFrameSkip > 9)
    gbFrameSkip = 0;

  vsync = regQueryDwordValue("vsync", false) ? true : false ;
  synchronize = regQueryDwordValue("synchronize", 1) ? true : false;
  fullScreenStretch = regQueryDwordValue("stretch", 0) ? true : false;

  videoOption = regQueryDwordValue("video", VIDEO_3X);

  strcpy(pluginName, regQueryStringValue("pluginName", ""));

  if(videoOption < VIDEO_1X || videoOption > VIDEO_OTHER)
    videoOption = VIDEO_3X;

  fsAdapter = regQueryDwordValue("fsAdapter", 0);
  fsWidth = regQueryDwordValue("fsWidth", 800);
  fsHeight = regQueryDwordValue("fsHeight", 600);
  fsColorDepth = regQueryDwordValue("fsColorDepth", 32);
  fsFrequency = regQueryDwordValue("fsFrequency", 60);

  if(videoOption == VIDEO_OTHER) {
    if(fsWidth < 0 || fsWidth > 4095 || fsHeight < 0 || fsHeight > 4095)
      videoOption = 0;
    if(fsColorDepth != 16 && fsColorDepth != 24 && fsColorDepth != 32)
      videoOption = 0;
  }

  renderMethod = (DISPLAY_TYPE)regQueryDwordValue("renderMethod", DIRECT_3D);
#ifdef NO_OGL
  if( renderMethod == OPENGL ) {
	  renderMethod = DIRECT_3D;
  }
#endif
#ifdef NO_D3D
  if( renderMethod == DIRECT_3D ) {
	  renderMethod = OPENGL;
  }
#endif

  audioAPI = (AUDIO_API)regQueryDwordValue( "audioAPI", DIRECTSOUND );
  if( ( audioAPI != DIRECTSOUND )
#ifndef NO_OAL
	  && ( audioAPI != OPENAL_SOUND )
#endif
#ifndef NO_XAUDIO2
	  && ( audioAPI != XAUDIO2 )
#endif
	  ) {
		  audioAPI = DIRECTSOUND;
  }

  windowPositionX = regQueryDwordValue("windowX", 0);
  if(windowPositionX < 0)
    windowPositionX = 0;
  windowPositionY = regQueryDwordValue("windowY", 0);
  if(windowPositionY < 0)
    windowPositionY = 0;

  maxCpuCores = regQueryDwordValue("maxCpuCores", 0);
  if(maxCpuCores == 0) {
	  maxCpuCores = detectCpuCores();
  }

  useBiosFileGBA = ( regQueryDwordValue("useBiosGBA", 0) == 1 ) ? true : false;

  useBiosFileGB = ( regQueryDwordValue("useBiosGB", 0) == 1 ) ? true : false;

  skipBiosFile = regQueryDwordValue("skipBios", 0) ? true : false;

  buffer = regQueryStringValue("biosFileGBA", "");

  if(!buffer.IsEmpty()) {
    biosFileNameGBA = buffer;
  }

  buffer = regQueryStringValue("biosFileGB", "");

  if(!buffer.IsEmpty()) {
    biosFileNameGB = buffer;
  }

  int res = regQueryDwordValue("soundEnable", 0x30f);

  soundEnable(res);
  soundDisable(~res);

  soundOffFlag = (regQueryDwordValue("soundOff", 0)) ? true : false;

  soundQuality = regQueryDwordValue("soundQuality", 1);

  soundEcho = regQueryDwordValue("soundEcho", 0) ? true : false;

  soundLowPass = regQueryDwordValue("soundLowPass", 0) ? true : false;

  soundReverse = regQueryDwordValue("soundReverse", 0) ? true : false;

  soundVolume = regQueryDwordValue("soundVolume", 0);
  if(soundVolume < 0 || soundVolume > 5)
    soundVolume = 0;

  soundInterpolation = regQueryDwordValue("soundInterpolation", 0);
  if(soundInterpolation < 0 || soundInterpolation > 1)
    soundInterpolation = 0;

  tripleBuffering = regQueryDwordValue("tripleBuffering", false) ? true : false;

#ifndef NO_D3D
  d3dFilter = regQueryDwordValue("d3dFilter", 1);
  if(d3dFilter < 0 || d3dFilter > 1)
    d3dFilter = 1;

  d3dMotionBlur = ( regQueryDwordValue("d3dMotionBlur", 0) == 1 ) ? true : false;
#endif

  glFilter = regQueryDwordValue("glFilter", 1);
  if(glFilter < 0 || glFilter > 1)
    glFilter = 1;


  filterType = regQueryDwordValue("filter", 0);
  if(filterType < 0 || filterType > 17)
    filterType = 0;

  filterMT = ( 1 == regQueryDwordValue("filterEnableMultiThreading", 0) );

  disableMMX = regQueryDwordValue("disableMMX", false) ? true: false;

  disableStatusMessage = regQueryDwordValue("disableStatus", 0) ? true : false;

  showSpeed = regQueryDwordValue("showSpeed", 0);
  if(showSpeed < 0 || showSpeed > 2)
    showSpeed = 0;

  showSpeedTransparent = regQueryDwordValue("showSpeedTransparent", TRUE) ?
    TRUE : FALSE;

  winGbPrinterEnabled = regQueryDwordValue("gbPrinter", false) ? true : false;

  if(winGbPrinterEnabled)
    gbSerialFunction = gbPrinterSend;
  else
    gbSerialFunction = NULL;

  pauseWhenInactive = regQueryDwordValue("pauseWhenInactive", 1) ?
    true : false;

  captureFormat = regQueryDwordValue("captureFormat", 0);

  removeIntros = regQueryDwordValue("removeIntros", false) ? true : false;

  recentFreeze = regQueryDwordValue("recentFreeze", false) ? true : false;

  autoIPS = regQueryDwordValue("autoIPS", true) ? true : false;

  cpuDisableSfx = regQueryDwordValue("disableSfx", 0) ? true : false;

  winSaveType = regQueryDwordValue("saveType", 0);
  if(winSaveType < 0 || winSaveType > 5)
    winSaveType = 0;

  ifbType = regQueryDwordValue("ifbType", 0);
  if(ifbType < 0 || ifbType > 2)
    ifbType = 0;

  winFlashSize = regQueryDwordValue("flashSize", 0x10000);
  if(winFlashSize != 0x10000 && winFlashSize != 0x20000)
    winFlashSize = 0x10000;
  flashSize = winFlashSize;

  agbPrintEnable(regQueryDwordValue("agbPrint", 0) ? true : false);

  winRtcEnable = regQueryDwordValue("rtcEnabled", 0) ? true : false;
  rtcEnable(winRtcEnable);

  switch(videoOption) {
  case VIDEO_320x240:
    fsWidth = 320;
    fsHeight = 240;
    fsColorDepth = 16;
	fsFrequency = 60;
    break;
  case VIDEO_640x480:
    fsWidth = 640;
    fsHeight = 480;
    fsColorDepth = 16;
	fsFrequency = 60;
	break;
  case VIDEO_800x600:
    fsWidth = 800;
    fsHeight = 600;
    fsColorDepth = 16;
	fsFrequency = 60;
	break;
  case VIDEO_1024x768:
    fsWidth = 1024;
    fsHeight = 768;
    fsColorDepth = 16;
	fsFrequency = 60;
	break;
  case VIDEO_1280x1024:
    fsWidth = 1280;
    fsHeight = 1024;
    fsColorDepth = 16;
	fsFrequency = 60;
	break;
  }

  winGbBorderOn = regQueryDwordValue("borderOn", 0);
  gbBorderAutomatic = regQueryDwordValue("borderAutomatic", 0);
  gbEmulatorType = regQueryDwordValue("emulatorType", 1);
  if(gbEmulatorType < 0 || gbEmulatorType > 5)
    gbEmulatorType = 1;
  gbColorOption = regQueryDwordValue("colorOption", 0);

  threadPriority = regQueryDwordValue("priority", 2);

  if(threadPriority < 0 || threadPriority >3)
    threadPriority = 2;
  updatePriority();

  autoSaveLoadCheatList = regQueryDwordValue("autoSaveCheatList", 0) ?
    true : false;

  alwaysLastDir =
	  ( regQueryDwordValue( "alwaysUseLastRomDirectory", 0 ) == 1 ) ? true : false;

  gbPaletteOption = regQueryDwordValue("gbPaletteOption", 0);
  if(gbPaletteOption < 0)
    gbPaletteOption = 0;
  if(gbPaletteOption > 2)
    gbPaletteOption = 2;

  regQueryBinaryValue("gbPalette", (char *)systemGbPalette,
                      24*sizeof(u16));

  rewindTimer = regQueryDwordValue("rewindTimer", 0);

  if(rewindTimer < 0 || rewindTimer > 600)
    rewindTimer = 0;

  rewindTimer *= 6; // convert to 10 frames multiple

  if(rewindTimer != 0)
    rewindMemory = (char *)malloc(8*REWIND_SIZE);

  for(int i = 0; i < 10; i++) {
    buffer.Format("recent%d", i);
    char *s = regQueryStringValue(buffer, NULL);
    if(s == NULL)
      break;
    recentFiles[i] = s;
  }

  joypadDefault = regQueryDwordValue("joypadDefault", 0);
  if(joypadDefault < 0 || joypadDefault > 3)
    joypadDefault = 0;

  autoLoadMostRecent = regQueryDwordValue("autoLoadMostRecent", false) ? true :
    false;

  winSkipSaveGameBattery = regQueryDwordValue("winSkipSaveGameBattery", false) ? true : false;
  skipSaveGameBattery = theApp.winSkipSaveGameBattery;

  cheatsEnabled = regQueryDwordValue("cheatsEnabled", false) ? true : false;

  fsMaxScale = regQueryDwordValue("fsMaxScale", 0);

  updateThrottle( (unsigned short)regQueryDwordValue( "throttle", 0 ) );

  linktimeout = regQueryDwordValue("LinkTimeout", 1000);

  linklog = regQueryDwordValue("Linklog", false) ? true : false;
  if(linklog)
	  openLinkLog();

  adapter = regQueryDwordValue("RFU", false) ? true : false;
  linkenable = regQueryDwordValue("linkEnabled", false) ? true : false;

  lanlink.active = regQueryDwordValue("LAN", 0) ? true : false;

  Sm60FPS::bSaveMoreCPU = regQueryDwordValue("saveMoreCPU", 0);

#ifndef NO_OAL
  buffer = regQueryStringValue( "oalDevice", "Generic Software" );
  if( oalDevice ) {
	  free( oalDevice );
  }
  oalDevice = (TCHAR*)malloc( ( buffer.GetLength() + 1 ) * sizeof( TCHAR ) );
  _tcscpy( oalDevice, buffer.GetBuffer() );

  oalBufferCount = regQueryDwordValue( "oalBufferCount", 5 );
#endif

#ifndef NO_XAUDIO2
  xa2Device = regQueryDwordValue( "xa2Device", 0 );
  xa2BufferCount = regQueryDwordValue( "xa2BufferCount", 4 );
  xa2Upmixing = ( 1 == regQueryDwordValue( "xa2Upmixing", 0 ) );
#endif

  if( ( maxCpuCores == 1 ) && filterMT ) {
	  // multi-threading use useless for just one core
	  filterMT = false;
  }
}

void VBA::updateFrameSkip()
{
  switch(cartridgeType) {
  case 0:
    systemFrameSkip = frameSkip;
    break;
  case 1:
    systemFrameSkip = gbFrameSkip;
    break;
  }
}

void VBA::updateVideoSize(UINT id)
{
  int value = 0;

  switch(id) {
  case ID_OPTIONS_VIDEO_X1:
    value = VIDEO_1X;
    break;
  case ID_OPTIONS_VIDEO_X2:
    value = VIDEO_2X;
    break;
  case ID_OPTIONS_VIDEO_X3:
    value = VIDEO_3X;
    break;
  case ID_OPTIONS_VIDEO_X4:
    value = VIDEO_4X;
    break;
  case ID_OPTIONS_VIDEO_FULLSCREEN320X240:
    value = VIDEO_320x240;
    fsWidth = 320;
    fsHeight = 240;
    fsColorDepth = 16;
    break;
  case ID_OPTIONS_VIDEO_FULLSCREEN640X480:
    value = VIDEO_640x480;
    fsWidth = 640;
    fsHeight = 480;
    fsColorDepth = 16;
    break;
  case ID_OPTIONS_VIDEO_FULLSCREEN800X600:
    value = VIDEO_800x600;
    fsWidth = 800;
    fsHeight = 600;
    fsColorDepth = 16;
    break;
  case ID_OPTIONS_VIDEO_FULLSCREEN1024X768:
    value = VIDEO_1024x768;
    fsWidth = 1024;
    fsHeight = 768;
    fsColorDepth = 32;
    break;
  case ID_OPTIONS_VIDEO_FULLSCREEN1280X1024:
    value = VIDEO_1280x1024;
    fsWidth = 1280;
    fsHeight = 1024;
    fsColorDepth = 32;
    break;
  case ID_OPTIONS_VIDEO_FULLSCREEN:
    value = VIDEO_OTHER;
    break;
  }

  updateWindowSize(value);
}


void VBA::updateWindowSize(int value)
{
  regSetDwordValue("video", value);

  if(value == VIDEO_OTHER) {
    regSetDwordValue("fsWidth", fsWidth);
    regSetDwordValue("fsHeight", fsHeight);
    regSetDwordValue("fsColorDepth", fsColorDepth);
  }

  if(((value >= VIDEO_320x240) &&
      (videoOption != value)) ||
     (videoOption >= VIDEO_320x240 &&
      value <= VIDEO_4X) ||
     fsForceChange) {
    fsForceChange = false;
    changingVideoSize = true;
	if( videoOption <= VIDEO_4X ) {
		lastWindowed = (VIDEO_SIZE)videoOption; // save for when leaving full screen
	} else {
		lastFullscreen = (VIDEO_SIZE)videoOption; // save for when using quick switch to fullscreen
	}
    shutdownDisplay();
    if(input) {
      delete input;
      input = NULL;
    }
    m_pMainWnd->DragAcceptFiles(FALSE);
    CWnd *pWnd = m_pMainWnd;
    m_pMainWnd = NULL;
    pWnd->DestroyWindow();
    delete pWnd;
    videoOption = value;
    if(!initDisplay()) {
      if(videoOption == VIDEO_320x240 ||
         videoOption == VIDEO_640x480 ||
         videoOption == VIDEO_800x600 ||
         videoOption == VIDEO_1024x768 ||
         videoOption == VIDEO_1280x1024 ||
         videoOption == VIDEO_OTHER) {
        regSetDwordValue("video", VIDEO_1X);
      }
      changingVideoSize = false;
      AfxPostQuitMessage(0);
      return;
    }
    if(!initInput()) {
      changingVideoSize = false;
      AfxPostQuitMessage(0);
      return;
    }
    input->checkKeys();


    changingVideoSize = FALSE;
    updateWindowSize(videoOption);
    return;
  }

  sizeX = 240;
  sizeY = 160;

  videoOption = value;

  if(cartridgeType == IMAGE_GB) {
    if(gbBorderOn) {
      sizeX = 256;
      sizeY = 224;
      gbBorderLineSkip = 256;
      gbBorderColumnSkip = 48;
      gbBorderRowSkip = 40;
    } else {
      sizeX = 160;
      sizeY = 144;
      gbBorderLineSkip = 160;
      gbBorderColumnSkip = 0;
      gbBorderRowSkip = 0;
    }
  }

  surfaceSizeX = sizeX;
  surfaceSizeY = sizeY;

  switch(videoOption) {
  case VIDEO_1X:
    surfaceSizeX = sizeX;
    surfaceSizeY = sizeY;
    break;
  case VIDEO_2X:
    surfaceSizeX = sizeX * 2;
    surfaceSizeY = sizeY * 2;
    break;
  case VIDEO_3X:
    surfaceSizeX = sizeX * 3;
    surfaceSizeY = sizeY * 3;
    break;
  case VIDEO_4X:
    surfaceSizeX = sizeX * 4;
    surfaceSizeY = sizeY * 4;
    break;
  case VIDEO_320x240:
  case VIDEO_640x480:
  case VIDEO_800x600:
  case VIDEO_1024x768:
  case VIDEO_1280x1024:
  case VIDEO_OTHER:
    {
      int scaleX = 1;
      int scaleY = 1;
      scaleX = (fsWidth / sizeX);
      scaleY = (fsHeight / sizeY);
      int min = scaleX < scaleY ? scaleX : scaleY;
      if(fsMaxScale)
        min = min > fsMaxScale ? fsMaxScale : min;
      surfaceSizeX = min * sizeX;
      surfaceSizeY = min * sizeY;
      if((fullScreenStretch && (display != NULL &&
                                (display->getType() != DIRECT_3D)))
         || (display != NULL && display->getType() >= DIRECT_3D)) {
        surfaceSizeX = fsWidth;
        surfaceSizeY = fsHeight;
      }
    }
    break;
  }

  rect.right = sizeX;
  rect.bottom = sizeY;

  int winSizeX = sizeX;
  int winSizeY = sizeY;

  if(videoOption <= VIDEO_4X) {
    dest.left = 0;
    dest.top = 0;
    dest.right = surfaceSizeX;
    dest.bottom = surfaceSizeY;

    DWORD style = WS_POPUP | WS_VISIBLE;

    style |= WS_OVERLAPPEDWINDOW;

    AdjustWindowRectEx(&dest, style, TRUE, 0); //WS_EX_TOPMOST);

    winSizeX = dest.right-dest.left;
    winSizeY = dest.bottom-dest.top;

      m_pMainWnd->SetWindowPos(0, //HWND_TOPMOST,
                               windowPositionX,
                               windowPositionY,
                               winSizeX,
                               winSizeY,
                               SWP_NOMOVE | SWP_SHOWWINDOW);

	  // content of old seperate 'winCheckMenuBarInfo' function:
      MENUBARINFO info;
      info.cbSize = sizeof(MENUBARINFO);
	  GetMenuBarInfo( theApp.m_pMainWnd->GetSafeHwnd(), OBJID_MENU, 0, &info );
      int menuHeight = GetSystemMetrics(SM_CYMENU); // includes white line
      if((info.rcBar.bottom - info.rcBar.top) > menuHeight) // check for double height menu
	  {
        winSizeY += (info.rcBar.bottom - info.rcBar.top) - menuHeight + 1;
        m_pMainWnd->SetWindowPos(
                                        0, //HWND_TOPMOST,
                                        theApp.windowPositionX,
                                        theApp.windowPositionY,
                                        winSizeX,
                                        winSizeY,
                                        SWP_NOMOVE | SWP_SHOWWINDOW);
      }
  }

  adjustDestRect();

  updateIFB();
  updateFilter();

  if(display)
    display->resize(theApp.dest.right-theApp.dest.left, theApp.dest.bottom-theApp.dest.top);

  m_pMainWnd->RedrawWindow(NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
}


bool VBA::initDisplay()
{
  if (securityCheck != -3)
  {
    protectHelp[securityCheck2](0);
    return (this->**mainTrapPointer)(false);
  }
  return(false);
}


bool VBA::preInitialize()
{
	switch( cartridgeType )
	{
	case IMAGE_GBA:
		sizeX = 240;
		sizeY = 160;
		break;
	case IMAGE_GB:
		if( gbBorderOn ) {
			sizeX = 256;
			sizeY = 224;
		} else {
			sizeX = 160;
			sizeY = 144;
		}
		break;
	}

	switch( videoOption )
	{
	case VIDEO_1X:
		surfaceSizeX = sizeX;
		surfaceSizeY = sizeY;
		break;
	case VIDEO_2X:
		surfaceSizeX = sizeX * 2;
		surfaceSizeY = sizeY * 2;
		break;
	case VIDEO_3X:
		surfaceSizeX = sizeX * 3;
		surfaceSizeY = sizeY * 3;
		break;
	case VIDEO_4X:
		surfaceSizeX = sizeX * 4;
		surfaceSizeY = sizeY * 4;
		break;
	case VIDEO_320x240:
	case VIDEO_640x480:
	case VIDEO_800x600:
	case VIDEO_1024x768:
	case VIDEO_1280x1024:
	case VIDEO_OTHER:
		float scaleX = (float)fsWidth / sizeX;
		float scaleY = (float)fsHeight / sizeY;
		float min = ( scaleX < scaleY ) ? scaleX : scaleY;
		if( fullScreenStretch ) {
			surfaceSizeX = fsWidth;
			surfaceSizeY = fsHeight;
		} else {
			surfaceSizeX = (int)( sizeX * min );
			surfaceSizeY = (int)( sizeY * min );
		}
		break;
	}

	rect.left = 0;
	rect.top = 0;
	rect.right = sizeX;
	rect.bottom = sizeY;

	dest.left = 0;
	dest.top = 0;
	dest.right = surfaceSizeX;
	dest.bottom = surfaceSizeY;


	DWORD style = WS_POPUP | WS_VISIBLE;
	DWORD styleEx = 0;

	if( videoOption <= VIDEO_4X ) {
		style |= WS_OVERLAPPEDWINDOW;
	} else {
		styleEx = 0;
	}

	if( videoOption <= VIDEO_4X ) {
		AdjustWindowRectEx( &dest, style, TRUE, styleEx );
	} else {
		AdjustWindowRectEx( &dest, style, FALSE, styleEx );
	}

	int winSizeX = dest.right-dest.left;
	int winSizeY = dest.bottom-dest.top;

	if( videoOption > VIDEO_4X ) {
		winSizeX = fsWidth;
		winSizeY = fsHeight;
	}

	int x = 0, y = 0;

	if( videoOption <= VIDEO_4X ) {
		x = windowPositionX;
		y = windowPositionY;
	}


	// Create a window
	MainWnd *pWnd = new MainWnd;
	m_pMainWnd = pWnd;

	pWnd->CreateEx(
		styleEx,
		wndClass,
		_T("VisualBoyAdvance"),
		style,
		x, y,
		winSizeX, winSizeY,
		NULL,
		0
		);

	if( !((HWND)*pWnd) ) {
		winlog( "Error creating Window %08x\n", GetLastError() );
		return false;
	}
	pWnd->DragAcceptFiles( TRUE );
	updateMenuBar();
	adjustDestRect();

	return true;
}


bool VBA::updateRenderMethod(bool force)
{
	bool ret = true;

	Sm60FPS_Init();

	if( !(this->*secondaryTrapPointer)( force ) ) {
		// fall back to safe configuration
		renderMethod = DIRECT_3D;
		fsAdapter = 0;
		videoOption = VIDEO_1X;
		ret = updateRenderMethod( true );
	}

	return ret;
}


bool VBA::updateRenderMethod0(bool force)
{
  bool initInput = false;
  b16to32Video = false;

  if(display) {
    if(display->getType() != renderMethod || force) {
	  toolsLoggingClose(); // close log dialog
      initInput = true;
      changingVideoSize = true;
      shutdownDisplay();
      if(input) {
        delete input;
        input = NULL;
      }
      CWnd *pWnd = m_pMainWnd;

      m_pMainWnd = NULL;
      pWnd->DragAcceptFiles(FALSE);
      pWnd->DestroyWindow();
      delete pWnd;

      display = NULL;
      regSetDwordValue("renderMethod", renderMethod);
    }
  }
  if(display == NULL) {
    switch(renderMethod) {
#ifndef NO_OGL
	case OPENGL:
		display = newOpenGLDisplay();
		break;
#endif
#ifndef NO_D3D
    case DIRECT_3D:
		display = newDirect3DDisplay();
		break;
#endif
    }

	if( preInitialize() ) {
		if( display->initialize() ) {
			if( initInput ) {
				if( !this->initInput() ) {
					changingVideoSize = false;
					AfxPostQuitMessage(0);
					return false;
				}
				input->checkKeys();
				updateMenuBar();
				changingVideoSize = false;
				updateWindowSize(videoOption);

				m_pMainWnd->ShowWindow(SW_SHOW);
				m_pMainWnd->UpdateWindow();
				m_pMainWnd->SetFocus();

				return true;
			} else {
				changingVideoSize = false;
				return true;
			}
		}
	}
	changingVideoSize = false;
  }
  return true;
}


void VBA::shutdownDisplay()
{
  if(display != NULL) {
    display->cleanup();
    delete display;
    display = NULL;
  }
}

void VBA::directXMessage(const char *msg)
{
  systemMessage(IDS_DIRECTX_7_REQUIRED,
                "DirectX 7.0 or greater is required to run.\nDownload at http://www.microsoft.com/directx.\n\nError found at: %s",
                msg);
}

void VBA::updatePriority()
{
  switch(threadPriority) {
  case 0:
    SetThreadPriority(THREAD_PRIORITY_HIGHEST);
    break;
  case 1:
    SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);
    break;
  case 3:
    SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
    break;
  default:
    SetThreadPriority(THREAD_PRIORITY_NORMAL);
  }
}

#ifdef MMX
bool VBA::detectMMX()
{
  bool support = false;
  char brand[13];

  // check for Intel chip
  __try {
    __asm {
      mov eax, 0;
      cpuid;
      mov [dword ptr brand+0], ebx;
      mov [dword ptr brand+4], edx;
      mov [dword ptr brand+8], ecx;
    }
  }
  __except(EXCEPTION_EXECUTE_HANDLER) {
    if(_exception_code() == STATUS_ILLEGAL_INSTRUCTION) {
      return false;
    }
    return false;
  }
  // Check for Intel or AMD CPUs
  if(strncmp(brand, "GenuineIntel", 12)) {
    if(strncmp(brand, "AuthenticAMD", 12)) {
      return false;
    }
  }

  __asm {
    mov eax, 1;
    cpuid;
    test edx, 00800000h;
    jz NotFound;
    mov [support], 1;
  NotFound:
  }
  return support;
}
#endif

void VBA::winSetLanguageOption(int option, bool force)
{
  if(((option == languageOption) && option != 2) && !force)
    return;
  switch(option) {
  case 0:
    {
      char lbuffer[10];

      if(GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SABBREVLANGNAME,
                       lbuffer, 10)) {
        HINSTANCE l = winLoadLanguage(lbuffer);
        if(l == NULL) {
          LCID locIdBase = MAKELCID( MAKELANGID( PRIMARYLANGID( GetSystemDefaultLangID() ), SUBLANG_NEUTRAL ), SORT_DEFAULT );
          if(GetLocaleInfo(locIdBase, LOCALE_SABBREVLANGNAME,
                           lbuffer, 10)) {
            l = winLoadLanguage(lbuffer);
            if(l == NULL) {
              systemMessage(IDS_FAILED_TO_LOAD_LIBRARY,
                            "Failed to load library %s",
                            lbuffer);
              return;
            }
          }
        }
        AfxSetResourceHandle(l);
        if(languageModule != NULL)
#ifdef _AFXDLL
          AfxFreeLibrary( languageModule );
#else
          FreeLibrary( languageModule );
#endif
        languageModule = l;
      } else {
        systemMessage(IDS_FAILED_TO_GET_LOCINFO,
                      "Failed to get locale information");
        return;
      }
    }
    break;
  case 1:
    if(languageModule != NULL)
#ifdef _AFXDLL
      AfxFreeLibrary( languageModule );
#else
      FreeLibrary( languageModule );
#endif
    languageModule = NULL;
    AfxSetResourceHandle(AfxGetInstanceHandle());
    break;
  case 2:
    {
      if(!force) {
        LangSelect dlg;
        if(dlg.DoModal()) {
          HINSTANCE l = winLoadLanguage(languageName);
          if(l == NULL) {
            systemMessage(IDS_FAILED_TO_LOAD_LIBRARY,
                          "Failed to load library %s",
                          languageName);
            return;
          }
          AfxSetResourceHandle(l);
          if(languageModule != NULL)
		  {
#ifdef _AFXDLL
            AfxFreeLibrary( languageModule );
#else
            FreeLibrary( languageModule );
#endif
		  }
          languageModule = l;
        }
      } else {
        if(languageName.IsEmpty())
          return;
        HINSTANCE l = winLoadLanguage(languageName);
        if(l == NULL) {
          systemMessage(IDS_FAILED_TO_LOAD_LIBRARY,
                        "Failed to load library %s",
                        languageName);
          return;
        }
        AfxSetResourceHandle(l);
        if(languageModule != NULL)
          FreeLibrary(languageModule);
        languageModule = l;
      }
    }
    break;
  }
  languageOption = option;
  updateMenuBar();
}

HINSTANCE VBA::winLoadLanguage(const char *name)
{
  CString buffer;

  buffer.Format( _T("vba_%s.dll"), name);

#ifdef _AFXDLL
  HINSTANCE l = AfxLoadLibrary( buffer );
#else
  HMODULE l = LoadLibrary( buffer );
#endif

  if(l == NULL) {
    if(strlen(name) == 3) {
      char buffer2[3];
      buffer2[0] = name[0];
      buffer2[1] = name[1];
      buffer2[2] = 0;
      buffer.Format("vba_%s.dll", buffer2);

#ifdef _AFXDLL
	  return AfxLoadLibrary( buffer );
#else
	  return LoadLibrary( buffer );
#endif
    }
  }
  return l;
}


bool VBA::initInput()
{
  if(input)
    delete input;
  input = newDirectInput();
  if(input->initialize()) {
    input->loadSettings();
    input->checkKeys();
    return true;
  }
  delete input;
  return false;
}

void VBA::winAddUpdateListener(IUpdateListener *l)
{
  updateList.AddTail(l);
  updateCount++;
}

void VBA::winRemoveUpdateListener(IUpdateListener *l)
{
  POSITION pos = updateList.Find(l);
  if(pos) {
    updateList.RemoveAt(pos);
    updateCount--;
    if(updateCount < 0)
      updateCount = 0;
  }
}

CString VBA::winLoadFilter(UINT id)
{
  CString res = winResLoadString(id);
  res.Replace('_','|');

  return res;
}

void VBA::movieReadNext()
{
  if(movieFile) {
    bool movieEnd = false;

    if(fread(&moviePlayFrame, 1, sizeof(int), movieFile) == sizeof(int)) {
      if(fread(&movieNextJoypad, 1, sizeof(u32), movieFile) == sizeof(int)) {
        // make sure we don't have spurious entries on the movie that can
        // cause us to play it forever
        if(moviePlayFrame <= movieFrame)
          movieEnd = true;
      } else
        movieEnd = true;
    } else
      movieEnd = true;
    if(movieEnd) {
      CString string = winResLoadString(IDS_END_OF_MOVIE);
      systemScreenMessage(string);
      moviePlaying = false;
      fclose(movieFile);
      movieFile = NULL;
      return;
    }
  } else
    moviePlaying = false;
}

void VBA::saveSettings()
{
  regSetDwordValue("language", languageOption);

  regSetStringValue("languageName", languageName);

  regSetDwordValue("frameSkip", frameSkip);

  regSetDwordValue("gbFrameSkip", gbFrameSkip);

  regSetDwordValue("vsync", vsync);
  regSetDwordValue("synchronize", synchronize);
  regSetDwordValue("stretch", fullScreenStretch);

  regSetDwordValue("video", videoOption);

  regSetDwordValue("fsAdapter", fsAdapter);
  regSetDwordValue("fsWidth", fsWidth);
  regSetDwordValue("fsHeight", fsHeight);
  regSetDwordValue("fsColorDepth", fsColorDepth);
  regSetDwordValue("fsFrequency", fsFrequency);

  regSetDwordValue("renderMethod", renderMethod);
  regSetDwordValue( "audioAPI", audioAPI );

  regSetDwordValue("windowX", windowPositionX);
  regSetDwordValue("windowY", windowPositionY);

  regSetDwordValue("maxCpuCores", maxCpuCores);

  regSetDwordValue("useBiosGBA", useBiosFileGBA);

  regSetDwordValue("useBiosGB", useBiosFileGB);

  regSetDwordValue("skipBios", skipBiosFile);

  if(!biosFileNameGBA.IsEmpty())
    regSetStringValue("biosFileGBA", biosFileNameGBA);

  if(!biosFileNameGB.IsEmpty())
    regSetStringValue("biosFileGB", biosFileNameGB);

  regSetDwordValue("soundEnable", soundGetEnable() & 0x30f);

  regSetDwordValue("soundOff", soundOffFlag);

  regSetDwordValue("soundQuality", soundQuality);

  regSetDwordValue("soundEcho", soundEcho);

  regSetDwordValue("soundLowPass", soundLowPass);

  regSetDwordValue("soundReverse", soundReverse);

  regSetDwordValue("soundVolume", soundVolume);

  regSetDwordValue("soundInterpolation", soundInterpolation);
  regSetDwordValue("tripleBuffering", tripleBuffering);

#ifndef NO_D3D
  regSetDwordValue("d3dFilter", d3dFilter);
  regSetDwordValue("d3dMotionBlur", d3dMotionBlur ? 1 : 0);
#endif

  regSetDwordValue("glFilter", glFilter);

  regSetDwordValue("filter", filterType);

  regSetDwordValue("filterEnableMultiThreading", filterMT ? 1 : 0);

  regSetDwordValue("LCDFilter", filterLCD);

  regSetDwordValue("disableMMX", disableMMX);

  regSetDwordValue("disableStatus", disableStatusMessage);

  regSetDwordValue("showSpeed", showSpeed);

  regSetDwordValue("showSpeedTransparent", showSpeedTransparent);

  regSetDwordValue("gbPrinter", winGbPrinterEnabled);

  regSetDwordValue("pauseWhenInactive", pauseWhenInactive);

  regSetDwordValue("captureFormat", captureFormat);

  regSetDwordValue("removeIntros", removeIntros);

  regSetDwordValue("recentFreeze", recentFreeze);

  regSetDwordValue("autoIPS", autoIPS);

  regSetDwordValue("disableSfx", cpuDisableSfx);

  regSetDwordValue("saveType", winSaveType);

  regSetDwordValue("ifbType", ifbType);

  regSetDwordValue("flashSize", winFlashSize);

  regSetDwordValue("agbPrint", agbPrintIsEnabled());

  regSetDwordValue("rtcEnabled", winRtcEnable);

  regSetDwordValue("borderOn", winGbBorderOn);
  regSetDwordValue("borderAutomatic", gbBorderAutomatic);
  regSetDwordValue("emulatorType", gbEmulatorType);
  regSetDwordValue("colorOption", gbColorOption);

  regSetDwordValue("priority", threadPriority);

  regSetDwordValue("autoSaveCheatList", autoSaveLoadCheatList);

  regSetDwordValue("alwaysUseLastRomDirectory", alwaysLastDir ? 1 : 0);

  regSetDwordValue("gbPaletteOption", gbPaletteOption);

  regSetBinaryValue("gbPalette", (char *)systemGbPalette,
                    24*sizeof(u16));

  regSetDwordValue("rewindTimer", rewindTimer/6);

  CString buffer;
  for(int i = 0; i < 10; i++) {
    buffer.Format("recent%d", i);
    regSetStringValue(buffer, recentFiles[i]);
  }

  regSetDwordValue("joypadDefault", joypadDefault);
  regSetDwordValue("autoLoadMostRecent", autoLoadMostRecent);
  regSetDwordValue("winSkipSaveGameBattery", winSkipSaveGameBattery);
  regSetDwordValue("cheatsEnabled", cheatsEnabled);
  regSetDwordValue("fsMaxScale", fsMaxScale);
  regSetDwordValue("throttle", throttle);
  regSetStringValue("pluginName", pluginName);
  regSetDwordValue("saveMoreCPU", Sm60FPS::bSaveMoreCPU);
  regSetDwordValue("LinkTimeout", linktimeout);
  regSetDwordValue("Linklog", linklog);
  regSetDwordValue("RFU", adapter);
  regSetDwordValue("linkEnabled", linkenable);
  regSetDwordValue("lastFullscreen", lastFullscreen);

#ifndef NO_OAL
  regSetStringValue( "oalDevice", oalDevice );
  regSetDwordValue( "oalBufferCount", oalBufferCount );
#endif

#ifndef NO_XAUDIO2
  regSetDwordValue( "xa2Device", xa2Device );
  regSetDwordValue( "xa2BufferCount", xa2BufferCount );
  regSetDwordValue( "xa2Upmixing", xa2Upmixing ? 1 : 0 );
#endif
}

unsigned int VBA::detectCpuCores()
{
	SYSTEM_INFO info;

	GetSystemInfo( &info );

	return info.dwNumberOfProcessors;
}

void winSignal(int, int)
{
}

#define CPUReadByteQuick(addr) \
  map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]

void winOutput(const char *s, u32 addr)
{
  if(s) {
    toolsLog(s);
  } else {
    CString str;
    char c;

    c = CPUReadByteQuick(addr);
    addr++;
    while(c) {
      str += c;
      c = CPUReadByteQuick(addr);
      addr++;
    }
    toolsLog(str);
  }
}


void Sm60FPS_Init()
{
	Sm60FPS::dwTimeElapse = 0;
	Sm60FPS::fWantFPS = 60.f;
	Sm60FPS::fCurFPS = 0.f;
	Sm60FPS::nFrameCnt = 0;
	Sm60FPS::bLastSkip = false;
	Sm60FPS::nCurSpeed = 100;
}


bool Sm60FPS_CanSkipFrame()
{
  if( theApp.autoFrameSkip ) {
	  if( Sm60FPS::nFrameCnt == 0 ) {
		  Sm60FPS::nFrameCnt = 0;
		  Sm60FPS::dwTimeElapse = 0;
		  Sm60FPS::dwTime0 = GetTickCount();
	  } else {
		  if( Sm60FPS::nFrameCnt >= 10 ) {
			  Sm60FPS::nFrameCnt = 0;
			  Sm60FPS::dwTimeElapse = 0;

			  if( Sm60FPS::nCurSpeed > Sm60FPS::K_fCpuSpeed ) {
				  Sm60FPS::fWantFPS += 1;
				  if( Sm60FPS::fWantFPS > Sm60FPS::K_fTargetFps ){
					  Sm60FPS::fWantFPS = Sm60FPS::K_fTargetFps;
				  }
			  } else {
				  if( Sm60FPS::nCurSpeed < (Sm60FPS::K_fCpuSpeed - 5) ) {
					  Sm60FPS::fWantFPS -= 1;
					  if( Sm60FPS::fWantFPS < 30.f ) {
						  Sm60FPS::fWantFPS = 30.f;
					  }
				  }
			  }
		  } else { // between frame 1-10
			  Sm60FPS::dwTime1 = GetTickCount();
			  Sm60FPS::dwTimeElapse += (Sm60FPS::dwTime1 - Sm60FPS::dwTime0);
			  Sm60FPS::dwTime0 = Sm60FPS::dwTime1;
			  if( !Sm60FPS::bLastSkip &&
				  ( (Sm60FPS::fWantFPS < Sm60FPS::K_fTargetFps) || Sm60FPS::bSaveMoreCPU) ) {
					  Sm60FPS::fCurFPS = (float)Sm60FPS::nFrameCnt * 1000 / Sm60FPS::dwTimeElapse;
					  if( (Sm60FPS::fCurFPS < Sm60FPS::K_fTargetFps) || Sm60FPS::bSaveMoreCPU ) {
						  Sm60FPS::bLastSkip = true;
						  Sm60FPS::nFrameCnt++;
						  return true;
					  }
			  }
		  }
	  }
	  Sm60FPS::bLastSkip = false;
	  Sm60FPS::nFrameCnt++;
  }
  return false;
}


void Sm60FPS_Sleep()
{
	if( theApp.autoFrameSkip ) {
		u32 dwTimePass = Sm60FPS::dwTimeElapse + (GetTickCount() - Sm60FPS::dwTime0);
		u32 dwTimeShould = (u32)(Sm60FPS::nFrameCnt * Sm60FPS::K_fDT);
		if( dwTimeShould > dwTimePass ) {
			Sleep(dwTimeShould - dwTimePass);
		}
	}
}
