visualboyadvance-m/src/win32/VBA.cpp

2705 lines
61 KiB
C++

// 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 "../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;
menuToggle = true;
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;
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
iconic = false;
#ifndef NO_D3D
d3dFilter = 0;
d3dMotionBlur = false;
#endif
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;
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);
}
BOOL VBA::InitInstance()
{
#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
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();
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 TRUE;
}
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;
}
int menuSkip = 0;
if(videoOption >= VIDEO_320x240 && menuToggle) {
int m = GetSystemMetrics(SM_CYMENU);
menuSkip = m;
dest.bottom -=m;
}
if(videoOption > VIDEO_4X) {
int top = (fsHeight - surfaceSizeY) / 2;
int left = (fsWidth - surfaceSizeX) / 2;
dest.top += top;
dest.bottom += top;
dest.left += left;
dest.right += left;
if(fullScreenStretch) {
dest.top = 0+menuSkip;
dest.left = 0;
dest.right = fsWidth;
dest.bottom = fsHeight;
}
}
}
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", "Scale2x.rpi"));
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;
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;
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
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;
menuToggle = TRUE;
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)
{
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()
{
return updateRenderMethod(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( !updateRenderMethod0( 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) {
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("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("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
}
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);
}
}
}