// 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 #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); } } }