AppleWin/source/Applewin.cpp
Brett Vickers 9e5e21b8c9 Prevent uninitialized value bugs and improve string safety.
This change does two things:

1. Updates the registry APIs to reduce the likelihood of uninitialized
variables.

The code wasn't always checking the return value of registry load operations.
In some cases, this led to uninitialized memory being used, and crashes could
result. For example, LoadConfiguration in Applewin.cpp was using an
uninitialized value for the computer type if no registry variable for the
"Apple 2 type" was set.

New registry reading methods and macros have also been introduced, allowing
default value fallbacks for the cases where a registry variable is not found.
This makes registry access simpler and safer when a default value is known in
advance.

The registry code's style has also been updated to conform with the rest of
the code base (tabs instead of spaces, naming conventions, etc.)

2. Introduces string safety improvements.

A number of code paths have been modified to use safe-string functions instead
of their unsafe counterparts (e.g., strcpy, sprintf).  In the process, some
strings were converted from "char" to "TCHAR". This was done mostly for
consistency with the rest of the code-base.
2019-08-09 13:38:50 -07:00

1801 lines
51 KiB
C++

/*
AppleWin : An Apple //e emulator for Windows
Copyright (C) 1994-1996, Michael O'Brien
Copyright (C) 1999-2001, Oliver Schmidt
Copyright (C) 2002-2005, Tom Charlesworth
Copyright (C) 2006-2014, Tom Charlesworth, Michael Pohoreski
AppleWin 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 of the License, or
(at your option) any later version.
AppleWin 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 AppleWin; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Description: main
*
* Author: Various
*/
#include "StdAfx.h"
#include "Applewin.h"
#include "CPU.h"
#include "Debug.h"
#include "Disk.h"
#include "DiskImage.h"
#include "Frame.h"
#include "Harddisk.h"
#include "Joystick.h"
#include "Keyboard.h"
#include "LanguageCard.h"
#include "Log.h"
#include "Memory.h"
#include "Mockingboard.h"
#include "MouseInterface.h"
#include "ParallelPrinter.h"
#include "Registry.h"
#include "Riff.h"
#include "SaveState.h"
#include "SerialComms.h"
#include "SoundCore.h"
#include "Speaker.h"
#ifdef USE_SPEECH_API
#include "Speech.h"
#endif
#include "Video.h"
#include "RGBMonitor.h"
#include "NTSC.h"
#include "Configuration/About.h"
#include "Configuration/PropertySheet.h"
#include "Tfe/Tfe.h"
#define VERSIONSTRING_SIZE 16
static UINT16 g_AppleWinVersion[4] = {0};
static UINT16 g_OldAppleWinVersion[4] = {0};
TCHAR VERSIONSTRING[VERSIONSTRING_SIZE] = "xx.yy.zz.ww";
const TCHAR *g_pAppTitle = NULL;
eApple2Type g_Apple2Type = A2TYPE_APPLE2EENHANCED;
bool g_bFullSpeed = false;
//=================================================
// Win32
HINSTANCE g_hInstance = (HINSTANCE)0;
AppMode_e g_nAppMode = MODE_LOGO;
static bool g_bLoadedSaveState = false;
TCHAR g_sProgramDir[MAX_PATH] = TEXT(""); // Directory of where AppleWin executable resides
TCHAR g_sDebugDir [MAX_PATH] = TEXT(""); // TODO: Not currently used
TCHAR g_sScreenShotDir[MAX_PATH] = TEXT(""); // TODO: Not currently used
bool g_bCapturePrintScreenKey = true;
static bool g_bHookSystemKey = true;
static bool g_bHookAltTab = false;
static bool g_bHookAltGrControl = false;
TCHAR g_sCurrentDir[MAX_PATH] = TEXT(""); // Also Starting Dir. Debugger uses this when load/save
bool g_bRestart = false;
bool g_bRestartFullScreen = false;
DWORD g_dwSpeed = SPEED_NORMAL; // Affected by Config dialog's speed slider bar
double g_fCurrentCLK6502 = CLK_6502_NTSC; // Affected by Config dialog's speed slider bar
static double g_fMHz = 1.0; // Affected by Config dialog's speed slider bar
int g_nCpuCyclesFeedback = 0;
DWORD g_dwCyclesThisFrame = 0;
bool g_bDisableDirectInput = false;
bool g_bDisableDirectSound = false;
bool g_bDisableDirectSoundMockingboard = false;
int g_nMemoryClearType = MIP_FF_FF_00_00; // Note: -1 = random MIP in Memory.cpp MemReset()
IPropertySheet& sg_PropertySheet = * new CPropertySheet;
CSuperSerialCard sg_SSC;
CMouseInterface sg_Mouse;
Disk2InterfaceCard sg_Disk2Card;
SS_CARDTYPE g_Slot0 = CT_LanguageCard; // Just for Apple II or II+ or similar clones
SS_CARDTYPE g_Slot4 = CT_Empty;
SS_CARDTYPE g_Slot5 = CT_Empty;
SS_CARDTYPE g_SlotAux = CT_Extended80Col; // For Apple //e and above
HANDLE g_hCustomRomF8 = INVALID_HANDLE_VALUE; // Cmd-line specified custom ROM at $F800..$FFFF
static bool g_bCustomRomF8Failed = false; // Set if custom ROM file failed
static bool g_bEnableSpeech = false;
#ifdef USE_SPEECH_API
CSpeech g_Speech;
#endif
//===========================================================================
static DWORD dwLogKeyReadTickStart;
static bool bLogKeyReadDone = false;
void LogFileTimeUntilFirstKeyReadReset(void)
{
if (!g_fh)
return;
dwLogKeyReadTickStart = GetTickCount();
bLogKeyReadDone = false;
}
// Log the time from emulation restart/reboot until the first key read: BIT $C000
// . AZTEC.DSK (DOS 3.3) does prior LDY $C000 reads, but the BIT $C000 is at the "Press any key" message
// . Phasor1.dsk / ProDOS 1.1.1: PC=E797: B1 50: LDA ($50),Y / "Select an Option:" message
// . Rescue Raiders v1.3,v1.5: PC=895: LDA $C000 / boot to intro
void LogFileTimeUntilFirstKeyRead(void)
{
if (!g_fh || bLogKeyReadDone)
return;
if ( (mem[regs.pc-3] != 0x2C) // AZTEC: bit $c000
&& !((regs.pc-2) == 0xE797 && mem[regs.pc-2] == 0xB1 && mem[regs.pc-1] == 0x50) // Phasor1: lda ($50),y
&& !((regs.pc-3) == 0x0895 && mem[regs.pc-3] == 0xAD) // Rescue Raiders v1.3,v1.5: lda $c000
)
return;
DWORD dwTime = GetTickCount() - dwLogKeyReadTickStart;
LogFileOutput("Time from emulation reboot until first $C000 access: %d msec\n", dwTime);
bLogKeyReadDone = true;
}
//---------------------------------------------------------------------------
eApple2Type GetApple2Type(void)
{
return g_Apple2Type;
}
void SetApple2Type(eApple2Type type)
{
g_Apple2Type = type;
SetMainCpuDefault(type);
}
const UINT16* GetOldAppleWinVersion(void)
{
return g_OldAppleWinVersion;
}
bool GetLoadedSaveStateFlag(void)
{
return g_bLoadedSaveState;
}
void SetLoadedSaveStateFlag(const bool bFlag)
{
g_bLoadedSaveState = bFlag;
}
bool GetHookAltGrControl(void)
{
return g_bHookAltGrControl;
}
static void ResetToLogoMode(void)
{
g_nAppMode = MODE_LOGO;
SetLoadedSaveStateFlag(false);
}
//---------------------------------------------------------------------------
static bool g_bPriorityNormal = true;
// Make APPLEWIN process higher priority
void SetPriorityAboveNormal(void)
{
if (!g_bPriorityNormal)
return;
if ( SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS) )
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
g_bPriorityNormal = false;
}
}
// Make APPLEWIN process normal priority
void SetPriorityNormal(void)
{
if (g_bPriorityNormal)
return;
if ( SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS) )
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
g_bPriorityNormal = true;
}
}
//---------------------------------------------------------------------------
static UINT g_uModeStepping_Cycles = 0;
static bool g_uModeStepping_LastGetKey_ScrollLock = false;
static void ContinueExecution(void)
{
_ASSERT(g_nAppMode == MODE_RUNNING || g_nAppMode == MODE_STEPPING);
const double fUsecPerSec = 1.e6;
#if 1
const UINT nExecutionPeriodUsec = 1000; // 1.0ms
// const UINT nExecutionPeriodUsec = 100; // 0.1ms
const double fExecutionPeriodClks = g_fCurrentCLK6502 * ((double)nExecutionPeriodUsec / fUsecPerSec);
#else
const double fExecutionPeriodClks = 1800.0;
const UINT nExecutionPeriodUsec = (UINT) (fUsecPerSec * (fExecutionPeriodClks / g_fCurrentCLK6502));
#endif
//
bool bScrollLock_FullSpeed = false;
if (sg_PropertySheet.GetScrollLockToggle())
{
bScrollLock_FullSpeed = g_bScrollLock_FullSpeed;
}
else
{
if (g_nAppMode == MODE_RUNNING)
{
bScrollLock_FullSpeed = GetKeyState(VK_SCROLL) < 0;
}
else if (!IsDebugSteppingAtFullSpeed()) // Implicitly: MODE_STEPPING
{
// NB. For MODE_STEPPING: GetKeyState() is slow, so only call periodically
// . 0x3FFF is roughly the number of cycles in a video frame, which seems a reasonable rate to call GetKeyState()
if ((g_uModeStepping_Cycles & 0x3FFF) == 0)
g_uModeStepping_LastGetKey_ScrollLock = GetKeyState(VK_SCROLL) < 0;
bScrollLock_FullSpeed = g_uModeStepping_LastGetKey_ScrollLock;
}
}
const bool bWasFullSpeed = g_bFullSpeed;
g_bFullSpeed = (g_dwSpeed == SPEED_MAX) ||
bScrollLock_FullSpeed ||
(sg_Disk2Card.IsConditionForFullSpeed() && !Spkr_IsActive() && !MB_IsActive()) ||
IsDebugSteppingAtFullSpeed();
if (g_bFullSpeed)
{
if (!bWasFullSpeed)
VideoRedrawScreenDuringFullSpeed(0, true); // Init for full-speed mode
// Don't call Spkr_Mute() - will get speaker clicks
MB_Mute();
SysClk_StopTimer();
#ifdef USE_SPEECH_API
g_Speech.Reset(); // TODO: Put this on a timer (in emulated cycles)... otherwise CATALOG cuts out
#endif
g_nCpuCyclesFeedback = 0; // For the case when this is a big -ve number
// Switch to normal priority so that APPLEWIN process doesn't hog machine!
//. EG: No disk in Drive-1, and boot Apple: Windows will start to crawl!
SetPriorityNormal();
}
else
{
if (bWasFullSpeed)
VideoRedrawScreenAfterFullSpeed(g_dwCyclesThisFrame);
// Don't call Spkr_Demute()
MB_Demute();
SysClk_StartTimerUsec(nExecutionPeriodUsec);
// Switch to higher priority, eg. for audio (BUG #015394)
SetPriorityAboveNormal();
}
//
int nCyclesWithFeedback = (int) fExecutionPeriodClks + g_nCpuCyclesFeedback;
const UINT uCyclesToExecuteWithFeedback = (nCyclesWithFeedback >= 0) ? nCyclesWithFeedback
: 0;
const DWORD uCyclesToExecute = (g_nAppMode == MODE_RUNNING) ? uCyclesToExecuteWithFeedback
/* MODE_STEPPING */ : 0;
const bool bVideoUpdate = !g_bFullSpeed;
const DWORD uActualCyclesExecuted = CpuExecute(uCyclesToExecute, bVideoUpdate);
g_dwCyclesThisFrame += uActualCyclesExecuted;
sg_Disk2Card.UpdateDriveState(uActualCyclesExecuted);
JoyUpdateButtonLatch(nExecutionPeriodUsec); // Button latch time is independent of CPU clock frequency
PrintUpdate(uActualCyclesExecuted);
//
DWORD uSpkrActualCyclesExecuted = uActualCyclesExecuted;
bool bModeStepping_WaitTimer = false;
if (g_nAppMode == MODE_STEPPING && !IsDebugSteppingAtFullSpeed())
{
g_uModeStepping_Cycles += uActualCyclesExecuted;
if (g_uModeStepping_Cycles >= uCyclesToExecuteWithFeedback)
{
uSpkrActualCyclesExecuted = g_uModeStepping_Cycles;
g_uModeStepping_Cycles -= uCyclesToExecuteWithFeedback;
bModeStepping_WaitTimer = true;
}
}
// For MODE_STEPPING: do this speaker update periodically
// - Otherwise kills performance due to sound-buffer lock/unlock for every 6502 opcode!
if (g_nAppMode == MODE_RUNNING || bModeStepping_WaitTimer)
SpkrUpdate(uSpkrActualCyclesExecuted);
//
const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
if (g_dwCyclesThisFrame >= dwClksPerFrame)
{
g_dwCyclesThisFrame -= dwClksPerFrame;
if (g_bFullSpeed)
VideoRedrawScreenDuringFullSpeed(g_dwCyclesThisFrame);
else
VideoRefreshScreen(); // Just copy the output of our Apple framebuffer to the system Back Buffer
MB_EndOfVideoFrame();
}
if ((g_nAppMode == MODE_RUNNING && !g_bFullSpeed) || bModeStepping_WaitTimer)
{
SysClk_WaitTimer();
}
}
void SingleStep(bool bReinit)
{
if (bReinit)
{
g_uModeStepping_Cycles = 0;
g_uModeStepping_LastGetKey_ScrollLock = false;
}
ContinueExecution();
}
//===========================================================================
double Get6502BaseClock(void)
{
return (GetVideoRefreshRate() == VR_50HZ) ? CLK_6502_PAL : CLK_6502_NTSC;
}
void SetCurrentCLK6502(void)
{
static DWORD dwPrevSpeed = (DWORD) -1;
static VideoRefreshRate_e prevVideoRefreshRate = VR_NONE;
if (dwPrevSpeed == g_dwSpeed && GetVideoRefreshRate() == prevVideoRefreshRate)
return;
dwPrevSpeed = g_dwSpeed;
prevVideoRefreshRate = GetVideoRefreshRate();
// SPEED_MIN = 0 = 0.50 MHz
// SPEED_NORMAL = 10 = 1.00 MHz
// 20 = 2.00 MHz
// SPEED_MAX-1 = 39 = 3.90 MHz
// SPEED_MAX = 40 = ???? MHz (run full-speed, /g_fCurrentCLK6502/ is ignored)
if(g_dwSpeed < SPEED_NORMAL)
g_fMHz = 0.5 + (double)g_dwSpeed * 0.05;
else
g_fMHz = (double)g_dwSpeed / 10.0;
g_fCurrentCLK6502 = Get6502BaseClock() * g_fMHz;
//
// Now re-init modules that are dependent on /g_fCurrentCLK6502/
//
SpkrReinitialize();
MB_Reinitialize();
}
//===========================================================================
void EnterMessageLoop(void)
{
MSG message;
PeekMessage(&message, NULL, 0, 0, PM_NOREMOVE);
while (message.message!=WM_QUIT)
{
if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
while ((g_nAppMode == MODE_RUNNING) || (g_nAppMode == MODE_STEPPING))
{
if (PeekMessage(&message,0,0,0,PM_REMOVE))
{
if (message.message == WM_QUIT)
return;
TranslateMessage(&message);
DispatchMessage(&message);
}
else if (g_nAppMode == MODE_STEPPING)
{
DebugContinueStepping();
}
else
{
ContinueExecution();
if (g_nAppMode != MODE_DEBUG)
{
if (g_bFullSpeed)
ContinueExecution();
}
}
}
}
else
{
if (g_nAppMode == MODE_DEBUG)
DebuggerUpdate();
else if (g_nAppMode == MODE_PAUSED)
Sleep(1); // Stop process hogging CPU - 1ms, as need to fade-out speaker sound buffer
else if (g_nAppMode == MODE_LOGO)
Sleep(1); // Stop process hogging CPU (NB. don't delay for too long otherwise key input can be slow in other apps - GH#569)
}
}
}
//===========================================================================
void GetProgramDirectory(void)
{
GetModuleFileName((HINSTANCE)0, g_sProgramDir, MAX_PATH);
g_sProgramDir[MAX_PATH-1] = 0;
int loop = _tcslen(g_sProgramDir);
while (loop--)
{
if ((g_sProgramDir[loop] == TEXT('\\')) || (g_sProgramDir[loop] == TEXT(':')))
{
g_sProgramDir[loop+1] = 0;
break;
}
}
}
//===========================================================================
// Backwards compatibility with AppleWin <1.24.0
static void LoadConfigOldJoystick_v1(const UINT uJoyNum)
{
DWORD dwOldJoyType;
if (!REGLOAD(TEXT(uJoyNum==0 ? REGVALUE_OLD_JOYSTICK0_EMU_TYPE1 : REGVALUE_OLD_JOYSTICK1_EMU_TYPE1), &dwOldJoyType))
return; // EG. Old AppleWin never installed
UINT uNewJoyType;
switch (dwOldJoyType)
{
case 0: // Disabled
default:
uNewJoyType = J0C_DISABLED;
break;
case 1: // PC Joystick
uNewJoyType = J0C_JOYSTICK1;
break;
case 2: // Keyboard (standard)
uNewJoyType = J0C_KEYBD_NUMPAD;
sg_PropertySheet.SetJoystickCenteringControl(JOYSTICK_MODE_FLOATING);
break;
case 3: // Keyboard (centering)
uNewJoyType = J0C_KEYBD_NUMPAD;
sg_PropertySheet.SetJoystickCenteringControl(JOYSTICK_MODE_CENTERING);
break;
case 4: // Mouse
uNewJoyType = J0C_MOUSE;
break;
}
JoySetJoyType(uJoyNum, uNewJoyType);
}
//Reads configuration from the registry entries
void LoadConfiguration(void)
{
DWORD dwComputerType = 0;
eApple2Type apple2Type = A2TYPE_APPLE2EENHANCED;
if (REGLOAD(TEXT(REGVALUE_APPLE2_TYPE), &dwComputerType))
{
const DWORD dwLoadedComputerType = dwComputerType;
if ( (dwComputerType >= A2TYPE_MAX) ||
(dwComputerType >= A2TYPE_UNDEFINED && dwComputerType < A2TYPE_CLONE) ||
(dwComputerType >= A2TYPE_CLONE_A2_MAX && dwComputerType < A2TYPE_CLONE_A2E) )
dwComputerType = A2TYPE_APPLE2EENHANCED;
// Remap the bad Pravets models (before AppleWin v1.26)
if (dwComputerType == A2TYPE_BAD_PRAVETS82) dwComputerType = A2TYPE_PRAVETS82;
if (dwComputerType == A2TYPE_BAD_PRAVETS8M) dwComputerType = A2TYPE_PRAVETS8M;
// Remap the bad Pravets models (at AppleWin v1.26) - GH#415
if (dwComputerType == A2TYPE_CLONE) dwComputerType = A2TYPE_PRAVETS82;
if (dwLoadedComputerType != dwComputerType)
{
char sText[ 100 ];
_snprintf( sText, sizeof(sText)-1, "Unsupported Apple2Type(%d). Changing to %d", dwLoadedComputerType, dwComputerType);
LogFileOutput("%s\n", sText);
MessageBox(
GetDesktopWindow(), // NB. g_hFrameWindow is not yet valid
sText,
"Load Configuration",
MB_ICONSTOP | MB_SETFOREGROUND);
sg_PropertySheet.ConfigSaveApple2Type((eApple2Type)dwComputerType);
}
apple2Type = (eApple2Type) dwComputerType;
}
else if (REGLOAD(TEXT(REGVALUE_OLD_APPLE2_TYPE), &dwComputerType)) // Support older AppleWin registry entries
{
switch (dwComputerType)
{
// NB. No A2TYPE_APPLE2E (this is correct)
case 0: apple2Type = A2TYPE_APPLE2; break;
case 1: apple2Type = A2TYPE_APPLE2PLUS; break;
case 2: apple2Type = A2TYPE_APPLE2EENHANCED; break;
default: apple2Type = A2TYPE_APPLE2EENHANCED; break;
}
}
SetApple2Type(apple2Type);
//
DWORD dwMainCpuType;
REGLOAD_DEFAULT(TEXT(REGVALUE_CPU_TYPE), &dwMainCpuType, CPU_65C02);
if (dwMainCpuType != CPU_6502 && dwMainCpuType != CPU_65C02)
dwMainCpuType = CPU_65C02;
SetMainCpu((eCpuType)dwMainCpuType);
//
DWORD dwJoyType;
if (REGLOAD(TEXT(REGVALUE_JOYSTICK0_EMU_TYPE), &dwJoyType))
JoySetJoyType(JN_JOYSTICK0, dwJoyType);
else if (REGLOAD(TEXT(REGVALUE_OLD_JOYSTICK0_EMU_TYPE2), &dwJoyType)) // GH#434
JoySetJoyType(JN_JOYSTICK0, dwJoyType);
else
LoadConfigOldJoystick_v1(JN_JOYSTICK0);
if (REGLOAD(TEXT(REGVALUE_JOYSTICK1_EMU_TYPE), &dwJoyType))
JoySetJoyType(JN_JOYSTICK1, dwJoyType);
else if (REGLOAD(TEXT(REGVALUE_OLD_JOYSTICK1_EMU_TYPE2), &dwJoyType)) // GH#434
JoySetJoyType(JN_JOYSTICK1, dwJoyType);
else
LoadConfigOldJoystick_v1(JN_JOYSTICK1);
DWORD dwSoundType;
REGLOAD_DEFAULT(TEXT("Sound Emulation"), &dwSoundType, REG_SOUNDTYPE_NONE);
switch (dwSoundType)
{
case REG_SOUNDTYPE_NONE:
case REG_SOUNDTYPE_DIRECT: // Not supported from 1.26
case REG_SOUNDTYPE_SMART: // Not supported from 1.26
default:
soundtype = SOUND_NONE;
break;
case REG_SOUNDTYPE_WAVE:
soundtype = SOUND_WAVE;
break;
}
TCHAR serialPortName[CSuperSerialCard::SIZEOF_SERIALCHOICE_ITEM];
if (RegLoadString(
TEXT(REG_CONFIG),
TEXT(REGVALUE_SERIAL_PORT_NAME),
TRUE,
serialPortName,
CSuperSerialCard::SIZEOF_SERIALCHOICE_ITEM))
{
sg_SSC.SetSerialPortName(serialPortName);
}
REGLOAD_DEFAULT(TEXT(REGVALUE_EMULATION_SPEED), &g_dwSpeed, SPEED_NORMAL);
Config_Load_Video();
SetCurrentCLK6502(); // Pre: g_dwSpeed && Config_Load_Video()->SetVideoRefreshRate()
DWORD dwEnhanceDisk;
REGLOAD_DEFAULT(TEXT(REGVALUE_ENHANCE_DISK_SPEED), &dwEnhanceDisk, 1);
sg_Disk2Card.SetEnhanceDisk(dwEnhanceDisk ? true : false);
DWORD dwTfeEnabled;
REGLOAD_DEFAULT(TEXT("Uthernet Active"), &dwTfeEnabled, 0);
tfe_enabled = dwTfeEnabled ? 1 : 0;
//
DWORD dwTmp = 0;
if(REGLOAD(TEXT(REGVALUE_FS_SHOW_SUBUNIT_STATUS), &dwTmp))
SetFullScreenShowSubunitStatus(dwTmp ? true : false);
if(REGLOAD(TEXT(REGVALUE_THE_FREEZES_F8_ROM), &dwTmp))
sg_PropertySheet.SetTheFreezesF8Rom(dwTmp);
if(REGLOAD(TEXT(REGVALUE_SPKR_VOLUME), &dwTmp))
SpkrSetVolume(dwTmp, sg_PropertySheet.GetVolumeMax());
if(REGLOAD(TEXT(REGVALUE_MB_VOLUME), &dwTmp))
MB_SetVolume(dwTmp, sg_PropertySheet.GetVolumeMax());
if(REGLOAD(TEXT(REGVALUE_SAVE_STATE_ON_EXIT), &dwTmp))
g_bSaveStateOnExit = dwTmp ? true : false;
if(REGLOAD(TEXT(REGVALUE_DUMP_TO_PRINTER), &dwTmp))
g_bDumpToPrinter = dwTmp ? true : false;
if(REGLOAD(TEXT(REGVALUE_CONVERT_ENCODING), &dwTmp))
g_bConvertEncoding = dwTmp ? true : false;
if(REGLOAD(TEXT(REGVALUE_FILTER_UNPRINTABLE), &dwTmp))
g_bFilterUnprintable = dwTmp ? true : false;
if(REGLOAD(TEXT(REGVALUE_PRINTER_APPEND), &dwTmp))
g_bPrinterAppend = dwTmp ? true : false;
if(REGLOAD(TEXT(REGVALUE_HDD_ENABLED), &dwTmp))
HD_SetEnabled(dwTmp ? true : false);
if(REGLOAD(TEXT(REGVALUE_PDL_XTRIM), &dwTmp))
JoySetTrim((short)dwTmp, true);
if(REGLOAD(TEXT(REGVALUE_PDL_YTRIM), &dwTmp))
JoySetTrim((short)dwTmp, false);
if(REGLOAD(TEXT(REGVALUE_SCROLLLOCK_TOGGLE), &dwTmp))
sg_PropertySheet.SetScrollLockToggle(dwTmp);
if(REGLOAD(TEXT(REGVALUE_CURSOR_CONTROL), &dwTmp))
sg_PropertySheet.SetJoystickCursorControl(dwTmp);
if(REGLOAD(TEXT(REGVALUE_AUTOFIRE), &dwTmp))
sg_PropertySheet.SetAutofire(dwTmp);
if(REGLOAD(TEXT(REGVALUE_CENTERING_CONTROL), &dwTmp))
sg_PropertySheet.SetJoystickCenteringControl(dwTmp);
if(REGLOAD(TEXT(REGVALUE_MOUSE_CROSSHAIR), &dwTmp))
sg_PropertySheet.SetMouseShowCrosshair(dwTmp);
if(REGLOAD(TEXT(REGVALUE_MOUSE_RESTRICT_TO_WINDOW), &dwTmp))
sg_PropertySheet.SetMouseRestrictToWindow(dwTmp);
if(REGLOAD(TEXT(REGVALUE_SLOT4), &dwTmp))
g_Slot4 = (SS_CARDTYPE) dwTmp;
if(REGLOAD(TEXT(REGVALUE_SLOT5), &dwTmp))
g_Slot5 = (SS_CARDTYPE) dwTmp;
//
TCHAR szFilename[MAX_PATH];
RegLoadString(TEXT(REG_PREFS), TEXT(REGVALUE_PREF_HDV_START_DIR), 1, szFilename, MAX_PATH, TEXT(""));
if (szFilename[0] == '\0')
GetCurrentDirectory(sizeof(szFilename), szFilename);
SetCurrentImageDir(szFilename);
HD_LoadLastDiskImage(HARDDISK_1);
HD_LoadLastDiskImage(HARDDISK_2);
//
// Current/Starting Dir is the "root" of where the user keeps his disk images
RegLoadString(TEXT(REG_PREFS), TEXT(REGVALUE_PREF_START_DIR), 1, szFilename, MAX_PATH, TEXT(""));
if (szFilename[0] == '\0')
GetCurrentDirectory(sizeof(szFilename), szFilename);
SetCurrentImageDir(szFilename);
sg_Disk2Card.LoadLastDiskImage(DRIVE_1);
sg_Disk2Card.LoadLastDiskImage(DRIVE_2);
//
RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_SAVESTATE_FILENAME), 1, szFilename, MAX_PATH, TEXT(""));
Snapshot_SetFilename(szFilename); // If not in Registry than default will be used (ie. g_sCurrentDir + default filename)
RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_PRINTER_FILENAME), 1, szFilename, MAX_PATH, TEXT(""));
Printer_SetFilename(szFilename); // If not in Registry than default will be used
REGLOAD_DEFAULT(TEXT(REGVALUE_PRINTER_IDLE_LIMIT), &dwTmp, 10);
Printer_SetIdleLimit(dwTmp);
RegLoadString(TEXT(REG_CONFIG), TEXT("Uthernet Interface"), 1, szFilename, MAX_PATH, TEXT(""));
update_tfe_interface(szFilename, NULL);
if (REGLOAD(TEXT(REGVALUE_WINDOW_SCALE), &dwTmp))
SetViewportScale(dwTmp);
if (REGLOAD(TEXT(REGVALUE_CONFIRM_REBOOT), &dwTmp))
g_bConfirmReboot = dwTmp;
}
//===========================================================================
bool SetCurrentImageDir(const char* pszImageDir)
{
strcpy(g_sCurrentDir, pszImageDir);
int nLen = strlen( g_sCurrentDir );
if ((nLen > 0) && (g_sCurrentDir[ nLen - 1 ] != '\\'))
{
g_sCurrentDir[ nLen + 0 ] = '\\';
g_sCurrentDir[ nLen + 1 ] = 0;
}
if( SetCurrentDirectory(g_sCurrentDir) )
return true;
return false;
}
//===========================================================================
// TODO: Added dialog option of which file extensions to registry
static bool g_bRegisterFileTypes = true;
void RegisterExtensions(void)
{
TCHAR szCommandTmp[MAX_PATH];
GetModuleFileName((HMODULE)0,szCommandTmp,MAX_PATH);
TCHAR command[MAX_PATH];
wsprintf(command, "\"%s\"", szCommandTmp); // Wrap path & filename in quotes & null terminate
TCHAR icon[MAX_PATH];
wsprintf(icon,TEXT("%s,1"),(LPCTSTR)command);
_tcscat(command,TEXT(" \"%1\"")); // Append "%1"
// _tcscat(command,TEXT("-d1 %1\"")); // Append "%1"
// sprintf(command, "\"%s\" \"-d1 %%1\"", szCommandTmp); // Wrap path & filename in quotes & null terminate
// NB. Registry access to HKLM typically results in ErrorCode 5(ACCESS DENIED), as UAC requires elevated permissions (Run as administrator).
// . HKEY_CLASSES_ROOT\CLSID is a merged view of HKLM\SOFTWARE\Classes and HKCU\SOFTWARE\Classes
// NB. Reflect extensions in DELREG.INF
// RegSetValue(HKEY_CLASSES_ROOT,".bin",REG_SZ,"DiskImage",0); // Removed as .bin is too generic
const char* pValueName = ".bin";
LSTATUS res = RegDeleteValue(HKEY_CLASSES_ROOT, pValueName);
if (res != NOERROR && res != ERROR_FILE_NOT_FOUND) LogFileOutput("RegDeleteValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = ".do";
res = RegSetValue(HKEY_CLASSES_ROOT, pValueName ,REG_SZ,"DiskImage",0);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = ".dsk";
res = RegSetValue(HKEY_CLASSES_ROOT, pValueName, REG_SZ,"DiskImage",0);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = ".nib";
res = RegSetValue(HKEY_CLASSES_ROOT, pValueName, REG_SZ,"DiskImage",0);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = ".po";
res = RegSetValue(HKEY_CLASSES_ROOT, pValueName, REG_SZ,"DiskImage",0);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = ".woz";
res = RegSetValue(HKEY_CLASSES_ROOT, pValueName, REG_SZ,"DiskImage",0);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
// RegSetValue(HKEY_CLASSES_ROOT,".2mg",REG_SZ,"DiskImage",0); // Don't grab this, as not all .2mg images are supported (so defer to CiderPress)
// RegSetValue(HKEY_CLASSES_ROOT,".2img",REG_SZ,"DiskImage",0); // Don't grab this, as not all .2mg images are supported (so defer to CiderPress)
// RegSetValue(HKEY_CLASSES_ROOT,".aws.yaml",REG_SZ,"DiskImage",0); // NB. Can't grab this extension (even though it returns 0!) with embedded period (and .yaml is too generic) - GH#548
// RegSetValue(HKEY_CLASSES_ROOT,".hdv",REG_SZ,"DiskImage",0); // TO DO
pValueName = "DiskImage";
res = RegSetValue(HKEY_CLASSES_ROOT,
pValueName,
REG_SZ,"Disk Image",0);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = "DiskImage\\DefaultIcon";
res = RegSetValue(HKEY_CLASSES_ROOT,
pValueName,
REG_SZ,icon,0);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
// This key can interfere....
// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExt\.dsk
pValueName = "DiskImage\\shell\\open\\command";
res = RegSetValue(HKEY_CLASSES_ROOT,
pValueName,
REG_SZ,command,_tcslen(command)+1);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = "DiskImage\\shell\\open\\ddeexec";
res = RegSetValue(HKEY_CLASSES_ROOT,
pValueName,
REG_SZ,"%1",3);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = "DiskImage\\shell\\open\\ddeexec\\application";
res = RegSetValue(HKEY_CLASSES_ROOT,
pValueName,
REG_SZ,"applewin",_tcslen("applewin")+1);
// REG_SZ,szCommandTmp,_tcslen(szCommandTmp)+1);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
pValueName = "DiskImage\\shell\\open\\ddeexec\\topic";
res = RegSetValue(HKEY_CLASSES_ROOT,
pValueName,
REG_SZ,"system",_tcslen("system")+1);
if (res != NOERROR) LogFileOutput("RegSetValue(%s) failed (0x%08X)\n", pValueName, res);
}
//===========================================================================
// NB. On a restart, it's OK to call RegisterHotKey() again since the old g_hFrameWindow has been destroyed
static void RegisterHotKeys(void)
{
BOOL bStatus[3] = {0,0,0};
bStatus[0] = RegisterHotKey(
g_hFrameWindow , // HWND hWnd
VK_SNAPSHOT_560, // int id (user/custom id)
0 , // UINT fsModifiers
VK_SNAPSHOT // UINT vk = PrintScreen
);
bStatus[1] = RegisterHotKey(
g_hFrameWindow , // HWND hWnd
VK_SNAPSHOT_280, // int id (user/custom id)
MOD_SHIFT , // UINT fsModifiers
VK_SNAPSHOT // UINT vk = PrintScreen
);
bStatus[2] = RegisterHotKey(
g_hFrameWindow , // HWND hWnd
VK_SNAPSHOT_TEXT, // int id (user/custom id)
MOD_CONTROL , // UINT fsModifiers
VK_SNAPSHOT // UINT vk = PrintScreen
);
if ((!bStatus[0] || !bStatus[1] || !bStatus[2]))
{
std::string msg("Unable to register for PrintScreen key(s):\n");
if (!bStatus[0])
msg += "\n. PrintScreen";
if (!bStatus[1])
msg += "\n. Shift+PrintScreen";
if (!bStatus[2])
msg += "\n. Ctrl+PrintScreen";
if (g_bShowPrintScreenWarningDialog)
MessageBox( g_hFrameWindow, msg.c_str(), "Warning", MB_ICONASTERISK | MB_OK );
msg += "\n";
LogFileOutput(msg.c_str());
}
}
//---------------------------------------------------------------------------
static HINSTANCE g_hinstDLL = 0;
static HHOOK g_hhook = 0;
static HANDLE g_hHookThread = NULL;
static DWORD g_HookThreadId = 0;
// Pre: g_hFrameWindow must be valid
static bool HookFilterForKeyboard()
{
g_hinstDLL = LoadLibrary(TEXT("HookFilter.dll"));
_ASSERT(g_hFrameWindow);
typedef void (*RegisterHWNDProc)(HWND, bool, bool);
RegisterHWNDProc RegisterHWND = (RegisterHWNDProc) GetProcAddress(g_hinstDLL, "RegisterHWND");
if (RegisterHWND)
RegisterHWND(g_hFrameWindow, g_bHookAltTab, g_bHookAltGrControl);
HOOKPROC hkprcLowLevelKeyboardProc = (HOOKPROC) GetProcAddress(g_hinstDLL, "LowLevelKeyboardProc");
g_hhook = SetWindowsHookEx(
WH_KEYBOARD_LL,
hkprcLowLevelKeyboardProc,
g_hinstDLL,
0);
if (g_hhook != 0 && g_hFrameWindow != 0)
return true;
std::string msg("Failed to install hook filter for system keys");
DWORD dwErr = GetLastError();
MessageBox(GetDesktopWindow(), msg.c_str(), "Warning", MB_ICONASTERISK | MB_OK);
msg += "\n";
LogFileOutput(msg.c_str());
return false;
}
static void UnhookFilterForKeyboard()
{
UnhookWindowsHookEx(g_hhook);
FreeLibrary(g_hinstDLL);
}
static DWORD WINAPI HookThread(LPVOID lpParameter)
{
if (!HookFilterForKeyboard())
return -1;
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookFilterForKeyboard();
return 0;
}
static bool InitHookThread()
{
g_hHookThread = CreateThread(NULL, // lpThreadAttributes
0, // dwStackSize
(LPTHREAD_START_ROUTINE) HookThread,
0, // lpParameter
0, // dwCreationFlags : 0 = Run immediately
&g_HookThreadId); // lpThreadId
if (g_hHookThread == NULL)
return false;
return true;
}
static void UninitHookThread()
{
if (g_hHookThread)
{
if (!PostThreadMessage(g_HookThreadId, WM_QUIT, 0, 0))
{
_ASSERT(0);
return;
}
do
{
DWORD dwExitCode;
if (GetExitCodeThread(g_hHookThread, &dwExitCode))
{
if(dwExitCode == STILL_ACTIVE)
Sleep(10);
else
break;
}
}
while(1);
CloseHandle(g_hHookThread);
g_hHookThread = NULL;
g_HookThreadId = 0;
}
}
//===========================================================================
LPSTR GetCurrArg(LPSTR lpCmdLine)
{
if(*lpCmdLine == '\"')
lpCmdLine++;
return lpCmdLine;
}
LPSTR GetNextArg(LPSTR lpCmdLine)
{
int bInQuotes = 0;
while(*lpCmdLine)
{
if(*lpCmdLine == '\"')
{
bInQuotes ^= 1;
if(!bInQuotes)
{
*lpCmdLine++ = 0x00; // Assume end-quote is end of this arg
continue;
}
}
if((*lpCmdLine == ' ') && !bInQuotes)
{
*lpCmdLine++ = 0x00;
break;
}
lpCmdLine++;
}
return lpCmdLine;
}
//---------------------------------------------------------------------------
static std::string GetFullPath(LPCSTR szFileName)
{
std::string strPathName;
if (szFileName[0] == '\\' || szFileName[1] == ':')
{
// Abs pathname
strPathName = szFileName;
}
else
{
// Rel pathname
char szCWD[_MAX_PATH] = {0};
if (!GetCurrentDirectory(sizeof(szCWD), szCWD))
return "";
strPathName = szCWD;
strPathName.append("\\");
strPathName.append(szFileName);
}
return strPathName;
}
static bool DoDiskInsert(const int nDrive, LPCSTR szFileName)
{
std::string strPathName = GetFullPath(szFileName);
if (strPathName.empty()) return false;
ImageError_e Error = sg_Disk2Card.InsertDisk(nDrive, strPathName.c_str(), IMAGE_USE_FILES_WRITE_PROTECT_STATUS, IMAGE_DONT_CREATE);
return Error == eIMAGE_ERROR_NONE;
}
static bool DoHardDiskInsert(const int nDrive, LPCSTR szFileName)
{
std::string strPathName = GetFullPath(szFileName);
if (strPathName.empty()) return false;
BOOL bRes = HD_Insert(nDrive, strPathName.c_str());
return bRes ? true : false;
}
static void InsertFloppyDisks(LPSTR szImageName_drive[NUM_DRIVES], bool& bBoot)
{
if (!szImageName_drive[DRIVE_1] && !szImageName_drive[DRIVE_2])
return;
bool bRes = true;
if (szImageName_drive[DRIVE_1])
{
bRes = DoDiskInsert(DRIVE_1, szImageName_drive[DRIVE_1]);
LogFileOutput("Init: DoDiskInsert(D1), res=%d\n", bRes);
FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES); // floppy activity LEDs and floppy buttons
bBoot = true;
}
if (szImageName_drive[DRIVE_2])
{
bRes |= DoDiskInsert(DRIVE_2, szImageName_drive[DRIVE_2]);
LogFileOutput("Init: DoDiskInsert(D2), res=%d\n", bRes);
}
if (!bRes)
MessageBox(g_hFrameWindow, "Failed to insert floppy disk(s) - see log file", "Warning", MB_ICONASTERISK | MB_OK);
}
static void InsertHardDisks(LPSTR szImageName_harddisk[NUM_HARDDISKS], bool& bBoot)
{
if (!szImageName_harddisk[HARDDISK_1] && !szImageName_harddisk[HARDDISK_2])
return;
// Enable the Harddisk controller card
HD_SetEnabled(true);
DWORD dwTmp;
if (REGLOAD(TEXT(REGVALUE_HDD_ENABLED), &dwTmp))
{
if (!dwTmp)
REGSAVE(TEXT(REGVALUE_HDD_ENABLED), 1); // Config: HDD Enabled
}
//
bool bRes = true;
if (szImageName_harddisk[HARDDISK_1])
{
bRes = DoHardDiskInsert(HARDDISK_1, szImageName_harddisk[HARDDISK_1]);
LogFileOutput("Init: DoHardDiskInsert(HDD1), res=%d\n", bRes);
FrameRefreshStatus(DRAW_LEDS); // harddisk activity LED
bBoot = true;
}
if (szImageName_harddisk[HARDDISK_2])
{
bRes |= DoHardDiskInsert(HARDDISK_2, szImageName_harddisk[HARDDISK_2]);
LogFileOutput("Init: DoHardDiskInsert(HDD2), res=%d\n", bRes);
}
if (!bRes)
MessageBox(g_hFrameWindow, "Failed to insert harddisk(s) - see log file", "Warning", MB_ICONASTERISK | MB_OK);
}
static bool CheckOldAppleWinVersion(void)
{
TCHAR szOldAppleWinVersion[VERSIONSTRING_SIZE + 1];
RegLoadString(TEXT(REG_CONFIG), TEXT(REGVALUE_VERSION), 1, szOldAppleWinVersion, VERSIONSTRING_SIZE, TEXT(""));
const bool bShowAboutDlg = strcmp(szOldAppleWinVersion, VERSIONSTRING) != 0;
// version: xx.yy.zz.ww
char* p0 = szOldAppleWinVersion;
int len = strlen(szOldAppleWinVersion);
szOldAppleWinVersion[len] = '.'; // append a null terminator
szOldAppleWinVersion[len + 1] = '\0';
for (UINT i=0; i<4; i++)
{
char* p1 = strstr(p0, ".");
if (!p1)
break;
*p1 = 0;
g_OldAppleWinVersion[i] = atoi(p0);
p0 = p1+1;
}
return bShowAboutDlg;
}
//---------------------------------------------------------------------------
int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int)
{
bool bShutdown = false;
bool bSetFullScreen = false;
bool bBoot = false;
bool bChangedDisplayResolution = false;
bool bSlot0LanguageCard = false;
bool bSlot7Empty = false;
UINT bestWidth = 0, bestHeight = 0;
LPSTR szImageName_drive[NUM_DRIVES] = {NULL,NULL};
LPSTR szImageName_harddisk[NUM_HARDDISKS] = {NULL,NULL};
LPSTR szSnapshotName = NULL;
const std::string strCmdLine(lpCmdLine); // Keep a copy for log ouput
UINT uRamWorksExPages = 0;
UINT uSaturnBanks = 0;
int newVideoType = -1;
int newVideoStyleEnableMask = 0;
int newVideoStyleDisableMask = 0;
VideoRefreshRate_e newVideoRefreshRate = VR_NONE;
LPSTR szScreenshotFilename = NULL;
while (*lpCmdLine)
{
LPSTR lpNextArg = GetNextArg(lpCmdLine);
if (((strcmp(lpCmdLine, "-l") == 0) || (strcmp(lpCmdLine, "-log") == 0)) && (g_fh == NULL))
{
LogInit();
}
else if (strcmp(lpCmdLine, "-noreg") == 0)
{
g_bRegisterFileTypes = false;
}
else if (strcmp(lpCmdLine, "-d1") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
szImageName_drive[DRIVE_1] = lpCmdLine;
}
else if (strcmp(lpCmdLine, "-d2") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
szImageName_drive[DRIVE_2] = lpCmdLine;
}
else if (strcmp(lpCmdLine, "-h1") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
szImageName_harddisk[HARDDISK_1] = lpCmdLine;
}
else if (strcmp(lpCmdLine, "-h2") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
szImageName_harddisk[HARDDISK_2] = lpCmdLine;
}
else if (strcmp(lpCmdLine, "-s7") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "empty") == 0)
bSlot7Empty = true;
}
else if (strcmp(lpCmdLine, "-load-state") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
szSnapshotName = lpCmdLine;
}
else if (strcmp(lpCmdLine, "-f") == 0)
{
bSetFullScreen = true;
}
#define CMD_FS_HEIGHT "-fs-height="
else if (strncmp(lpCmdLine, CMD_FS_HEIGHT, sizeof(CMD_FS_HEIGHT)-1) == 0)
{
bSetFullScreen = true; // Implied
LPSTR lpTmp = lpCmdLine + sizeof(CMD_FS_HEIGHT)-1;
bool bRes = false;
if (strcmp(lpTmp, "best") == 0)
{
bRes = GetBestDisplayResolutionForFullScreen(bestWidth, bestHeight);
}
else
{
UINT userSpecifiedHeight = atoi(lpTmp);
if (userSpecifiedHeight)
bRes = GetBestDisplayResolutionForFullScreen(bestWidth, bestHeight, userSpecifiedHeight);
else
LogFileOutput("Invalid cmd-line parameter for -fs-height=x switch\n");
}
if (bRes)
LogFileOutput("Best resolution for -fs-height=x switch: Width=%d, Height=%d\n", bestWidth, bestHeight);
else
LogFileOutput("Failed to set parameter for -fs-height=x switch\n");
}
else if (strcmp(lpCmdLine, "-no-di") == 0)
{
g_bDisableDirectInput = true;
}
else if (strcmp(lpCmdLine, "-m") == 0)
{
g_bDisableDirectSound = true;
}
else if (strcmp(lpCmdLine, "-no-mb") == 0)
{
g_bDisableDirectSoundMockingboard = true;
}
else if (strcmp(lpCmdLine, "-memclear") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
g_nMemoryClearType = atoi(lpCmdLine);
if (g_nMemoryClearType < 0)
g_nMemoryClearType = 0;
else
if (g_nMemoryClearType >= NUM_MIP)
g_nMemoryClearType = NUM_MIP - 1;
}
#ifdef RAMWORKS
else if (strcmp(lpCmdLine, "-r") == 0) // RamWorks size [1..127]
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
uRamWorksExPages = atoi(lpCmdLine);
if (uRamWorksExPages > kMaxExMemoryBanks)
uRamWorksExPages = kMaxExMemoryBanks;
else
if (uRamWorksExPages < 1)
uRamWorksExPages = 1;
}
#endif
else if (strcmp(lpCmdLine, "-s0") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "saturn") == 0 || strcmp(lpCmdLine, "saturn128") == 0)
uSaturnBanks = Saturn128K::kMaxSaturnBanks;
else if (strcmp(lpCmdLine, "saturn64") == 0)
uSaturnBanks = Saturn128K::kMaxSaturnBanks/2;
else if (strcmp(lpCmdLine, "languagecard") == 0 || strcmp(lpCmdLine, "lc") == 0)
bSlot0LanguageCard = true;
}
else if (strcmp(lpCmdLine, "-f8rom") == 0) // Use custom 2K ROM at [$F800..$FFFF]
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (g_hCustomRomF8 != INVALID_HANDLE_VALUE) // Stop resource leak if -f8rom is specified twice!
CloseHandle(g_hCustomRomF8);
g_hCustomRomF8 = CreateFile(lpCmdLine, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
if ((g_hCustomRomF8 == INVALID_HANDLE_VALUE) || (GetFileSize(g_hCustomRomF8, NULL) != 0x800))
g_bCustomRomF8Failed = true;
}
else if (strcmp(lpCmdLine, "-videorom") == 0) // Use 2K (for II/II+). Use 4K,8K or 16K video ROM (for Enhanced //e)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (!ReadVideoRomFile(lpCmdLine))
{
std::string msg = "Failed to load video rom (not found or not exactly 2/4/8/16KiB)\n";
LogFileOutput("%s", msg.c_str());
MessageBox(g_hFrameWindow, msg.c_str(), TEXT("AppleWin Error"), MB_OK);
}
else
{
SetVideoRomRockerSwitch(true); // Use PAL char set
}
}
else if (strcmp(lpCmdLine, "-printscreen") == 0) // Turn on display of the last filename print screen was saved to
{
g_bDisplayPrintScreenFileName = true;
}
else if (strcmp(lpCmdLine, "-no-printscreen-key") == 0) // Don't try to capture PrintScreen key GH#469
{
g_bCapturePrintScreenKey = false;
}
else if (strcmp(lpCmdLine, "-no-printscreen-dlg") == 0) // Turn off the PrintScreen warning message dialog (if PrintScreen key can't be grabbed)
{
g_bShowPrintScreenWarningDialog = false;
}
else if (strcmp(lpCmdLine, "-no-hook-system-key") == 0) // Don't hook the System keys (eg. Left-ALT+ESC/SPACE/TAB) GH#556
{
g_bHookSystemKey = false;
}
else if (strcmp(lpCmdLine, "-hook-alt-tab") == 0) // GH#556
{
g_bHookAltTab = true;
}
else if (strcmp(lpCmdLine, "-hook-altgr-control") == 0) // GH#556
{
g_bHookAltGrControl = true;
}
else if (strcmp(lpCmdLine, "-altgr-sends-wmchar") == 0) // GH#625
{
KeybSetAltGrSendsWM_CHAR(true);
}
else if (strcmp(lpCmdLine, "-no-hook-alt") == 0) // GH#583
{
JoySetHookAltKeys(false);
}
else if (strcmp(lpCmdLine, "-spkr-inc") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
const int nErrorInc = atoi(lpCmdLine);
SoundCore_SetErrorInc( nErrorInc );
}
else if (strcmp(lpCmdLine, "-spkr-max") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
const int nErrorMax = atoi(lpCmdLine);
SoundCore_SetErrorMax( nErrorMax );
}
else if (strcmp(lpCmdLine, "-use-real-printer") == 0) // Enable control in Advanced config to allow dumping to a real printer
{
g_bEnableDumpToRealPrinter = true;
}
else if (strcmp(lpCmdLine, "-speech") == 0)
{
g_bEnableSpeech = true;
}
else if (strcmp(lpCmdLine, "-multimon") == 0)
{
g_bMultiMon = true;
}
else if ((strcmp(lpCmdLine, "-dcd") == 0) || (strcmp(lpCmdLine, "-modem") == 0)) // GH#386
{
sg_SSC.SupportDCD(true);
}
else if (strcmp(lpCmdLine, "-alt-enter=toggle-full-screen") == 0) // GH#556
{
SetAltEnterToggleFullScreen(true);
}
else if (strcmp(lpCmdLine, "-alt-enter=open-apple-enter") == 0) // GH#556
{
SetAltEnterToggleFullScreen(false);
}
else if (strcmp(lpCmdLine, "-video-mode=rgb-monitor") == 0) // GH#616
{
newVideoType = VT_COLOR_MONITOR_RGB;
}
else if (strcmp(lpCmdLine, "-video-style=vertical-blend") == 0) // GH#616
{
newVideoStyleEnableMask = VS_COLOR_VERTICAL_BLEND;
}
else if (strcmp(lpCmdLine, "-video-style=no-vertical-blend") == 0) // GH#616
{
newVideoStyleDisableMask = VS_COLOR_VERTICAL_BLEND;
}
else if (strcmp(lpCmdLine, "-rgb-card-invert-bit7") == 0) // GH#633
{
RGB_SetInvertBit7(true);
}
else if (strcmp(lpCmdLine, "-screenshot-and-exit") == 0) // GH#616: For testing - Use in combination with -load-state
{
szScreenshotFilename = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
}
else if (_stricmp(lpCmdLine, "-50hz") == 0) // (case-insensitive)
{
newVideoRefreshRate = VR_50HZ;
}
else if (_stricmp(lpCmdLine, "-60hz") == 0) // (case-insensitive)
{
newVideoRefreshRate = VR_60HZ;
}
else // unsupported
{
LogFileOutput("Unsupported arg: %s\n", lpCmdLine);
}
lpCmdLine = lpNextArg;
}
LogFileOutput("CmdLine: %s\n", strCmdLine.c_str());
#if 0
#ifdef RIFF_SPKR
RiffInitWriteFile("Spkr.wav", SPKR_SAMPLE_RATE, 1);
#endif
#ifdef RIFF_MB
RiffInitWriteFile("Mockingboard.wav", 44100, 2);
#endif
#endif
//-----
char szPath[_MAX_PATH];
if (0 == GetModuleFileName(NULL, szPath, sizeof(szPath)))
{
strcpy(szPath, __argv[0]);
}
// Extract application version and store in a global variable
DWORD dwHandle, dwVerInfoSize;
dwVerInfoSize = GetFileVersionInfoSize(szPath, &dwHandle);
if (dwVerInfoSize > 0)
{
char* pVerInfoBlock = new char[dwVerInfoSize];
if (GetFileVersionInfo(szPath, NULL, dwVerInfoSize, pVerInfoBlock))
{
VS_FIXEDFILEINFO* pFixedFileInfo;
UINT pFixedFileInfoLen;
VerQueryValue(pVerInfoBlock, TEXT("\\"), (LPVOID*) &pFixedFileInfo, (PUINT) &pFixedFileInfoLen);
// Construct version string from fixed file info block
unsigned long major = g_AppleWinVersion[0] = pFixedFileInfo->dwFileVersionMS >> 16;
unsigned long minor = g_AppleWinVersion[1] = pFixedFileInfo->dwFileVersionMS & 0xffff;
unsigned long fix = g_AppleWinVersion[2] = pFixedFileInfo->dwFileVersionLS >> 16;
unsigned long fix_minor = g_AppleWinVersion[3] = pFixedFileInfo->dwFileVersionLS & 0xffff;
StringCbPrintf(VERSIONSTRING, VERSIONSTRING_SIZE, "%d.%d.%d.%d", major, minor, fix, fix_minor);
}
delete [] pVerInfoBlock;
}
LogFileOutput("AppleWin version: %s\n", VERSIONSTRING);
//-----
// Initialize COM - so we can use CoCreateInstance
// . NB. DSInit() & DIMouse::DirectInputInit are done when g_hFrameWindow is created (WM_CREATE)
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
LogFileOutput("Init: CoInitializeEx(), hr=0x%08X\n", hr);
const bool bSysClkOK = SysClk_InitTimer();
LogFileOutput("Init: SysClk_InitTimer(), res=%d\n", bSysClkOK ? 1:0);
#ifdef USE_SPEECH_API
if (g_bEnableSpeech)
{
const bool bSpeechOK = g_Speech.Init();
LogFileOutput("Init: SysClk_InitTimer(), res=%d\n", bSpeechOK ? 1:0);
}
#endif
// DO ONE-TIME INITIALIZATION
g_hInstance = passinstance;
GdiSetBatchLimit(512);
LogFileOutput("Init: GdiSetBatchLimit()\n");
GetProgramDirectory();
LogFileOutput("Init: GetProgramDirectory()\n");
if (g_bRegisterFileTypes)
{
RegisterExtensions();
LogFileOutput("Init: RegisterExtensions()\n");
}
FrameRegisterClass();
LogFileOutput("Init: FrameRegisterClass()\n");
ImageInitialize();
LogFileOutput("Init: ImageInitialize()\n");
//
do
{
// DO INITIALIZATION THAT MUST BE REPEATED FOR A RESTART
g_bRestart = false;
ResetToLogoMode();
// NB. g_OldAppleWinVersion needed by LoadConfiguration() -> Config_Load_Video()
const bool bShowAboutDlg = CheckOldAppleWinVersion(); // Post: g_OldAppleWinVersion
LoadConfiguration();
LogFileOutput("Main: LoadConfiguration()\n");
if (newVideoType >= 0)
{
SetVideoType( (VideoType_e)newVideoType );
newVideoType = -1; // Don't reapply after a restart
}
SetVideoStyle( (VideoStyle_e) ((GetVideoStyle() | newVideoStyleEnableMask) & ~newVideoStyleDisableMask) );
if (newVideoRefreshRate != VR_NONE)
{
SetVideoRefreshRate(newVideoRefreshRate);
newVideoRefreshRate = VR_NONE; // Don't reapply after a restart
SetCurrentCLK6502();
}
// Apply the memory expansion switches after loading the Apple II machine type
#ifdef RAMWORKS
if (uRamWorksExPages)
{
SetRamWorksMemorySize(uRamWorksExPages);
SetExpansionMemType(CT_RamWorksIII);
uRamWorksExPages = 0; // Don't reapply after a restart
}
#endif
if (uSaturnBanks)
{
SetSaturnMemorySize(uSaturnBanks); // Set number of banks before constructing Saturn card
SetExpansionMemType(CT_Saturn128K);
uSaturnBanks = 0; // Don't reapply after a restart
}
if (bSlot0LanguageCard)
{
SetExpansionMemType(CT_LanguageCard);
bSlot0LanguageCard = false; // Don't reapply after a restart
}
DebugInitialize();
LogFileOutput("Main: DebugInitialize()\n");
JoyInitialize();
LogFileOutput("Main: JoyInitialize()\n");
VideoInitialize(); // g_pFramebufferinfo been created now
LogFileOutput("Main: VideoInitialize()\n");
LogFileOutput("Main: FrameCreateWindow() - pre\n");
FrameCreateWindow(); // g_hFrameWindow is now valid
LogFileOutput("Main: FrameCreateWindow() - post\n");
// Pre: may need g_hFrameWindow for MessageBox errors
// Post: may enable HDD, required for MemInitialize()->MemInitializeIO()
{
InsertFloppyDisks(szImageName_drive, bBoot);
szImageName_drive[DRIVE_1] = szImageName_drive[DRIVE_2] = NULL; // Don't insert on a restart
InsertHardDisks(szImageName_harddisk, bBoot);
szImageName_harddisk[HARDDISK_1] = szImageName_harddisk[HARDDISK_2] = NULL; // Don't insert on a restart
if (bSlot7Empty)
HD_SetEnabled(false);
}
MemInitialize();
LogFileOutput("Main: MemInitialize()\n");
// Show About dialog after creating main window (need g_hFrameWindow)
if (bShowAboutDlg)
{
if (!AboutDlg())
bShutdown = true; // Close everything down
else
RegSaveString(TEXT(REG_CONFIG), TEXT(REGVALUE_VERSION), 1, VERSIONSTRING); // Only save version after user accepts license
}
if (g_bCapturePrintScreenKey)
{
RegisterHotKeys(); // needs valid g_hFrameWindow
LogFileOutput("Main: RegisterHotKeys()\n");
}
if (g_bHookSystemKey)
{
if (InitHookThread()) // needs valid g_hFrameWindow (for message pump)
LogFileOutput("Main: HookFilterForKeyboard()\n");
}
// Need to test if it's safe to call ResetMachineState(). In the meantime, just call DiskReset():
sg_Disk2Card.Reset(true); // Switch from a booting A][+ to a non-autostart A][, so need to turn off floppy motor
LogFileOutput("Main: DiskReset()\n");
HD_Reset(); // GH#515
LogFileOutput("Main: HDDReset()\n");
if (!bSysClkOK)
{
MessageBox(g_hFrameWindow, "DirectX failed to create SystemClock instance", TEXT("AppleWin Error"), MB_OK);
bShutdown = true;
}
if (g_bCustomRomF8Failed)
{
std::string msg = "Failed to load custom F8 rom (not found or not exactly 2KiB)\n";
LogFileOutput("%s", msg.c_str());
MessageBox(g_hFrameWindow, msg.c_str(), TEXT("AppleWin Error"), MB_OK);
bShutdown = true;
}
tfe_init();
LogFileOutput("Main: tfe_init()\n");
if (szSnapshotName)
{
std::string strPathname(szSnapshotName);
int nIdx = strPathname.find_last_of('\\');
if (nIdx >= 0 && nIdx+1 < (int)strPathname.length())
{
std::string strPath = strPathname.substr(0, nIdx+1);
SetCurrentImageDir(strPath.c_str());
}
// Override value just loaded from Registry by LoadConfiguration()
// . NB. Registry value is not updated with this cmd-line value
Snapshot_SetFilename(szSnapshotName);
Snapshot_LoadState();
bBoot = true;
#if _DEBUG && 0 // Debug/test: Save a duplicate of the save-state file in tmp folder
std::string saveName = std::string("tmp\\") + std::string(szSnapshotName);
Snapshot_SetFilename(saveName);
g_bSaveStateOnExit = true;
bShutdown = true;
#endif
szSnapshotName = NULL;
}
else
{
Snapshot_Startup(); // Do this after everything has been init'ed
LogFileOutput("Main: Snapshot_Startup()\n");
}
if (szScreenshotFilename)
{
Video_RedrawAndTakeScreenShot(szScreenshotFilename);
bShutdown = true;
}
if (bShutdown)
{
PostMessage(g_hFrameWindow, WM_DESTROY, 0, 0); // Close everything down
// NB. If shutting down, then don't post any other messages (GH#286)
}
else
{
if (bSetFullScreen)
{
if (bestWidth && bestHeight)
{
DEVMODE devMode;
memset(&devMode, 0, sizeof(devMode));
devMode.dmSize = sizeof(devMode);
devMode.dmPelsWidth = bestWidth;
devMode.dmPelsHeight = bestHeight;
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
DWORD dwFlags = 0;
LONG res = ChangeDisplaySettings(&devMode, dwFlags);
if (res == 0)
bChangedDisplayResolution = true;
}
PostMessage(g_hFrameWindow, WM_USER_FULLSCREEN, 0, 0);
bSetFullScreen = false;
}
if (bBoot)
{
PostMessage(g_hFrameWindow, WM_USER_BOOT, 0, 0);
bBoot = false;
}
}
// ENTER THE MAIN MESSAGE LOOP
LogFileOutput("Main: EnterMessageLoop()\n");
EnterMessageLoop();
LogFileOutput("Main: LeaveMessageLoop()\n");
if (g_bRestart)
{
bSetFullScreen = g_bRestartFullScreen;
g_bRestartFullScreen = false;
}
MB_Reset();
LogFileOutput("Main: MB_Reset()\n");
sg_Mouse.Uninitialize(); // Maybe restarting due to switching slot-4 card from MouseCard to Mockingboard
sg_Mouse.Reset(); // Deassert any pending IRQs - GH#514
LogFileOutput("Main: sg_Mouse.Uninitialize()\n");
DSUninit();
LogFileOutput("Main: DSUninit()\n");
if (g_bHookSystemKey)
{
UninitHookThread();
LogFileOutput("Main: UnhookFilterForKeyboard()\n");
}
}
while (g_bRestart);
if (bChangedDisplayResolution)
ChangeDisplaySettings(NULL, 0); // restore default
// Release COM
SysClk_UninitTimer();
LogFileOutput("Exit: SysClk_UninitTimer()\n");
CoUninitialize();
LogFileOutput("Exit: CoUninitialize()\n");
tfe_shutdown();
LogFileOutput("Exit: tfe_shutdown()\n");
LogDone();
RiffFinishWriteFile();
if (g_hCustomRomF8 != INVALID_HANDLE_VALUE)
CloseHandle(g_hCustomRomF8);
return 0;
}