Files
AppleWin/source/Windows/AppleWin.cpp
T
Kelvin Lee 926c592f75 Replace some BOOL usages with proper bool (PR #1500)
1. Registry functions
   The set of functions are changed to consistently use bool arguments.
   The macros are changed to inline functions to improve type checking.
   RegSaveValue() are overloaded with help of templates to reduce
   typecasting at call sites. For instance, bool value would be converted
   consistently instead of relying on call sites to convert bool to int values.
2. Memory functions
   BOOL usages were unnecessary. Mostly internal usages which can simply be bool.
3. FrameBase
   Some member variables are changed to bool.
   BOOL was used because of those Registry functions.

Additional changes:
- Some expressions are forced to be bool when assigning to bool variables.
2026-06-06 14:12:42 +01:00

1049 lines
32 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 "Windows/AppleWin.h"
#include "Windows/HookFilter.h"
#include "Interface.h"
#include "Utilities.h"
#include "CmdLine.h"
#include "Debug.h"
#include "Keyboard.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 "Speaker.h"
#include "LanguageCard.h"
#include "CardManager.h"
#ifdef USE_SPEECH_API
#include "Speech.h"
#endif
#include "Windows/Win32Frame.h"
#include "Windows/DXSoundBuffer.h"
#include "RGBMonitor.h"
#include "NTSC.h"
#include "Configuration/About.h"
#include "Configuration/PropertySheet.h"
//=================================================
static bool g_bLoadedSaveState = false;
static bool g_bSysClkOK = false;
bool g_bRestartFullScreen = false;
static bool g_fullScreenResolutionChangedByUser = false;
//===========================================================================
bool GetLoadedSaveStateFlag(void)
{
return g_bLoadedSaveState;
}
void Win32Frame::SetLoadedSaveStateFlag(const bool bFlag)
{
g_bLoadedSaveState = bFlag;
}
bool GetHookAltTab(void)
{
return g_bHookAltTab;
}
bool GetHookAltGrControl(void)
{
return g_bHookAltGrControl;
}
bool GetFullScreenResolutionChangedByUser(void)
{
return g_fullScreenResolutionChangedByUser;
}
static void ResetToLogoMode(void)
{
g_nAppMode = MODE_LOGO;
GetFrame().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)
{
#ifdef LOG_PERF_TIMINGS
PerfMarker* pPerfMarkerTotal = new PerfMarker(g_timeTotal);
#endif
_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 (GetPropertySheet().GetScrollLockToggle())
{
bScrollLock_FullSpeed = Win32Frame::GetWin32Frame().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 ||
(GetCardMgr().GetDisk2CardMgr().IsConditionForFullSpeed() && !Spkr_IsActive() && !GetCardMgr().GetMockingboardCardMgr().IsActiveToPreventFullSpeed()) ||
IsDebugSteppingAtFullSpeed();
if (g_bFullSpeed)
{
if (!bWasFullSpeed)
GetFrame().VideoRedrawScreenDuringFullSpeed(0, true); // Init for full-speed mode
// Don't call Spkr_Mute() - will get speaker clicks
GetCardMgr().GetMockingboardCardMgr().MuteControl(true);
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)
GetFrame().VideoRedrawScreenAfterFullSpeed(g_dwCyclesThisFrame);
// Don't call Spkr_Unmute()
GetCardMgr().GetMockingboardCardMgr().MuteControl(false);
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 uint32_t uCyclesToExecute = (g_nAppMode == MODE_RUNNING) ? uCyclesToExecuteWithFeedback
/* MODE_STEPPING */ : 0;
const bool bVideoUpdate = !g_bFullSpeed;
const uint32_t uActualCyclesExecuted = CpuExecute(uCyclesToExecute, bVideoUpdate);
g_dwCyclesThisFrame += uActualCyclesExecuted;
GetCardMgr().Update(uActualCyclesExecuted);
//
uint32_t 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 && !GetVideo().VideoGetVblBarEx(g_dwCyclesThisFrame))
{
#ifdef LOG_PERF_TIMINGS
PerfMarker perfMarkerVideoRefresh(g_timeVideoRefresh);
#endif
g_dwCyclesThisFrame -= dwClksPerFrame;
if (g_bFullSpeed)
GetFrame().VideoRedrawScreenDuringFullSpeed(g_dwCyclesThisFrame);
else
GetFrame().VideoPresentScreen(); // Just copy the output of our Apple framebuffer to the system Back Buffer
}
#ifdef LOG_PERF_TIMINGS
delete pPerfMarkerTotal; // Explicitly call dtor *before* SysClk_WaitTimer()
#endif
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();
}
//===========================================================================
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)
}
}
}
//===========================================================================
static void GetProgramDirectory(void)
{
char programDir[MAX_PATH];
GetModuleFileName((HINSTANCE)0, programDir, MAX_PATH);
programDir[MAX_PATH-1] = 0;
g_sProgramDir = programDir;
size_t loop = g_sProgramDir.size();
while (loop--)
{
if ((g_sProgramDir[loop] == PATH_SEPARATOR) || (g_sProgramDir[loop] == ':'))
{
g_sProgramDir.resize(loop + 1); // this reduces the size
break;
}
}
}
//===========================================================================
void RegisterExtensions(void)
{
char szModuleFileName[MAX_PATH];
GetModuleFileName(static_cast<HMODULE>(NULL), szModuleFileName, sizeof(szModuleFileName));
// Wrap path & filename in quotes & null terminate
std::string command = std::string("\"") + szModuleFileName + '"';
std::string const icon = command + ",1";
command += " \"%1\""; // Append ' "%1"'
// command += " -d1 \"%1\""; // Append ' -d1 "%1"'
// command += " \"-d1 %1\""; // Append ' "-d1 %1"'
// 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.c_str(), 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.c_str(), (uint32_t)(command.length() + 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", (uint32_t)(strlen("applewin") + 1));
// REG_SZ, szModuleFileName, strlen(szModuleFileName)+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", (uint32_t)(strlen("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(
GetFrame().g_hFrameWindow , // HWND hWnd
VK_SNAPSHOT_560, // int id (user/custom id)
0 , // UINT fsModifiers
VK_SNAPSHOT // UINT vk = PrintScreen
);
bStatus[1] = RegisterHotKey(
GetFrame().g_hFrameWindow , // HWND hWnd
VK_SNAPSHOT_280, // int id (user/custom id)
MOD_SHIFT , // UINT fsModifiers
VK_SNAPSHOT // UINT vk = PrintScreen
);
bStatus[2] = RegisterHotKey(
GetFrame().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 (GetFrame().GetShowPrintScreenWarningDialog())
SHMessageBoxCheck( GetFrame().g_hFrameWindow, msg.c_str(), "Warning", MB_ICONASTERISK | MB_OK, MB_OK, "AppleWin-75097740-8e59-444c-bc94-2d4915132599" );
msg += "\n";
LogFileOutput(msg.c_str());
}
}
//---------------------------------------------------------------------------
static void ExceptionHandler(const char* pError)
{
GetFrame().FrameMessageBox(
pError,
"Runtime Exception",
MB_ICONEXCLAMATION | MB_SETFOREGROUND);
LogFileOutput("Runtime Exception: %s\n", pError);
}
//---------------------------------------------------------------------------
static void GetAppleWinVersion(void);
static void OneTimeInitialization(HINSTANCE passinstance);
static void RepeatInitialization(void);
static void Shutdown(void);
int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int)
{
char startDir[_MAX_PATH];
GetCurrentDirectory(sizeof(startDir), startDir);
g_sStartDir = startDir;
if (*(g_sStartDir.end()-1) != PATH_SEPARATOR) g_sStartDir += PATH_SEPARATOR;
if (!ProcessCmdLine(lpCmdLine))
return 0;
LogFileOutput("g_sStartDir = %s\n", g_sStartDir.c_str());
GetAppleWinVersion();
OneTimeInitialization(passinstance);
try
{
do
{
g_bRestart = false;
RepeatInitialization();
// ENTER THE MAIN MESSAGE LOOP
LogFileOutput("Main: EnterMessageLoop()\n");
EnterMessageLoop();
LogFileOutput("Main: LeaveMessageLoop()\n");
if (g_bRestart)
{
g_cmdLine.setFullScreen = g_bRestartFullScreen ? 1 : 0;
g_bRestartFullScreen = false;
CMouseInterface* pMouseCard = GetCardMgr().GetMouseCard();
if (pMouseCard)
{
// dtor removes event from g_SynchronousEventMgr - do before g_SynchronousEventMgr.Reset()
GetCardMgr().Remove( pMouseCard->GetSlot(), false );
LogFileOutput("Main: CMouseInterface::dtor\n");
}
_ASSERT(g_SynchronousEventMgr.GetHead() == NULL);
g_SynchronousEventMgr.Reset();
}
DSUninit();
LogFileOutput("Main: DSUninit()\n");
if (g_bHookSystemKey)
{
GetHookFilter().UninitHookThread();
LogFileOutput("Main: UnhookFilterForKeyboard()\n");
}
}
while (g_bRestart);
Shutdown();
}
catch (const std::exception& exception)
{
ExceptionHandler(exception.what());
}
return 0;
}
static void GetAppleWinVersion(void)
{
char szPath[_MAX_PATH];
if (0 == GetModuleFileName(NULL, szPath, sizeof(szPath)))
strcpy_s(szPath, sizeof(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, "\\", (LPVOID*) &pFixedFileInfo, (PUINT) &pFixedFileInfoLen);
// Construct version string from fixed file info block
UINT16 major = pFixedFileInfo->dwFileVersionMS >> 16;
UINT16 minor = pFixedFileInfo->dwFileVersionMS & 0xffff;
UINT16 fix = pFixedFileInfo->dwFileVersionLS >> 16;
UINT16 fix_minor = pFixedFileInfo->dwFileVersionLS & 0xffff;
SetAppleWinVersion(major, minor, fix, fix_minor);
}
delete [] pVerInfoBlock;
}
LogFileOutput("%s\n", GetAppleWinVersionAndBuild().c_str());
}
// DO ONE-TIME INITIALIZATION
static void OneTimeInitialization(HINSTANCE passinstance)
{
// Currently only support one RIFF file
if (!g_cmdLine.wavFileSpeaker.empty())
{
if (RiffInitWriteFile(g_cmdLine.wavFileSpeaker.c_str(), SPKR_SAMPLE_RATE, Spkr_GetNumChannels()))
Spkr_OutputToRiff();
}
else if (!g_cmdLine.wavFileMockingboard.empty())
{
if (RiffInitWriteFile(g_cmdLine.wavFileMockingboard.c_str(), MockingboardCard::SAMPLE_RATE, MockingboardCard::NUM_MB_CHANNELS))
GetCardMgr().GetMockingboardCardMgr().OutputToRiff();
}
// Initialize COM - so we can use CoCreateInstance
// . DSInit() & DIMouse::DirectInputInit are done when g_hFrameWindow is created (WM_CREATE)
// . DDInit() is done in RepeatInitialization() by GetVideo().Initialize()
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
LogFileOutput("Init: CoInitializeEx(), hr=0x%08X\n", hr);
g_bSysClkOK = SysClk_InitTimer();
LogFileOutput("Init: SysClk_InitTimer(), res=%d\n", g_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
GetFrame().g_hInstance = passinstance;
GdiSetBatchLimit(512);
LogFileOutput("Init: GdiSetBatchLimit()\n");
GetProgramDirectory();
LogFileOutput("Init: GetProgramDirectory()\n");
if (g_bRegisterFileTypes)
{
RegisterExtensions();
LogFileOutput("Init: RegisterExtensions()\n");
}
Win32Frame::GetWin32Frame().FrameRegisterClass();
LogFileOutput("Init: FrameRegisterClass()\n");
}
// DO INITIALIZATION THAT MUST BE REPEATED FOR A RESTART
static void RepeatInitialization(void)
{
KeybReset();
GetVideo().SetVidHD(false); // Set true later (eg. by LoadConfiguration() or cmd-line) if VidHDCard is instantiated
ResetToLogoMode();
// NB. g_OldAppleWinVersion needed by LoadConfiguration() -> Config_Load_Video()
const bool bShowAboutDlg = CheckOldAppleWinVersion(); // Post: g_OldAppleWinVersion
// Load configuration from Registry (+ will insert cards)
{
bool loadImages = g_cmdLine.szSnapshotName == NULL; // don't load floppy/harddisk images if a snapshot is to be loaded later on
LoadConfiguration(loadImages);
LogFileOutput("Main: LoadConfiguration()\n");
}
if (g_cmdLine.model != A2TYPE_MAX)
{
SetApple2Type(g_cmdLine.model);
GetPropertySheet().ConfigSaveApple2Type(g_cmdLine.model);
g_cmdLine.model = A2TYPE_MAX; // Don't reapply after a restart
}
RGB_SetVideocard(g_cmdLine.rgbCard, g_cmdLine.rgbCardForegroundColor, g_cmdLine.rgbCardBackgroundColor);
if (g_cmdLine.newVideoType >= 0)
{
GetVideo().SetVideoType( (VideoType_e)g_cmdLine.newVideoType );
g_cmdLine.newVideoType = -1; // Don't reapply after a restart
}
GetVideo().SetVideoStyle( (VideoStyle_e) ((GetVideo().GetVideoStyle() | g_cmdLine.newVideoStyleEnableMask) & ~g_cmdLine.newVideoStyleDisableMask) );
if (g_cmdLine.newVideoRefreshRate != VR_NONE)
{
GetVideo().SetVideoRefreshRate(g_cmdLine.newVideoRefreshRate);
g_cmdLine.newVideoRefreshRate = VR_NONE; // Don't reapply after a restart
SetCurrentCLK6502();
}
UseClockMultiplier(g_cmdLine.clockMultiplier);
g_cmdLine.clockMultiplier = 0.0;
// Apply the memory expansion switches after loading the Apple II machine type
if (IsApple2PlusOrClone(GetApple2Type()))
{
if (g_cmdLine.slotInfo[SLOT0].card == CT_Saturn128K)
{
Saturn128K::SetSaturnMemorySizeSlot0(g_cmdLine.uSaturnBanks); // Set number of banks before constructing Saturn card
SetExpansionMemType(CT_Saturn128K);
dynamic_cast<Saturn128K&>(GetCardMgr().GetRef(SLOT0)).SetSaturnMemorySize(g_cmdLine.uSaturnBanks);
}
else if (g_cmdLine.slotInfo[SLOT0].card == CT_LanguageCard)
{
SetExpansionMemType(CT_LanguageCard);
}
}
if (g_cmdLine.bSwapButtons0and1)
{
GetPropertySheet().SetButtonsSwapState(true);
// Reapply after a restart - TODO: grey-out the Config UI for "Swap 0/1" when this cmd line is passed in
}
JoyInitialize();
LogFileOutput("Main: JoyInitialize()\n");
// Init palette color
VideoSwitchVideocardPalette(RGB_GetVideocard(), GetVideo().GetVideoType());
// Allow the slots to be configured as empty
// . remove all first, so that single-instance cards can can switch slots
// NB. this state is persisted to the Registry/conf.ini
for (UINT i = SLOT0; i < NUM_SLOTS; i++)
{
if (g_cmdLine.slotInfo[i].card == CT_Empty)
GetCardMgr().Remove(i);
}
for (UINT i = SLOT0; i < NUM_SLOTS; i++)
{
if (g_cmdLine.slotInfo[i].card != CT_Empty && g_cmdLine.slotInfo[i].card != CT_Undefined)
{
if (GetCardMgr().QuerySlot(i) != g_cmdLine.slotInfo[i].card)
GetCardMgr().Insert(i, g_cmdLine.slotInfo[i].card);
}
g_cmdLine.slotInfo[i].card = CT_Undefined; // Don't reapply after a restart
}
if (g_cmdLine.supportDCD && GetCardMgr().IsSSCInstalled())
{
GetCardMgr().GetSSC()->SupportDCD(true);
}
if (g_cmdLine.enableDumpToRealPrinter && GetCardMgr().IsParallelPrinterCardInstalled())
{
GetCardMgr().GetParallelPrinterCard()->SetEnableDumpToRealPrinter(true);
}
for (UINT i = SLOT1; i < NUM_SLOTS; i++)
{
if (GetCardMgr().QuerySlot(i) == CT_Disk2 && g_cmdLine.slotInfo[i].isDiskII13)
{
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(i)).Set13SectorFirmware(true);
}
else if (GetCardMgr().QuerySlot(i) == CT_GenericHDD)
{
dynamic_cast<HarddiskInterfaceCard&>(GetCardMgr().GetRef(i)).SetUserNumBlocks(g_cmdLine.uHarddiskNumBlocks);
if (g_cmdLine.useHdcFirmwareV1)
dynamic_cast<HarddiskInterfaceCard&>(GetCardMgr().GetRef(i)).UseHdcFirmwareV1();
if (g_cmdLine.useHdcFirmwareV2)
dynamic_cast<HarddiskInterfaceCard&>(GetCardMgr().GetRef(i)).UseHdcFirmwareV2();
if (g_cmdLine.slotInfo[i].useHdcFirmwareMode != HdcUndefinedFromCmdLine)
{
dynamic_cast<HarddiskInterfaceCard&>(GetCardMgr().GetRef(i)).SetHdcFirmwareMode(g_cmdLine.slotInfo[i].useHdcFirmwareMode);
g_cmdLine.slotInfo[i].useHdcFirmwareMode = HdcUndefinedFromCmdLine; // Don't reapply after a restart
}
}
else if (GetCardMgr().GetMockingboardCardMgr().IsMockingboard(i))
{
if (g_cmdLine.slotInfo[i].useBad6522A)
dynamic_cast<MockingboardCard&>(GetCardMgr().GetRef(i)).UseBad6522A();
if (g_cmdLine.slotInfo[i].useBad6522B)
dynamic_cast<MockingboardCard&>(GetCardMgr().GetRef(i)).UseBad6522B();
for (UINT socket = 0; socket < NUM_SSI263; socket++)
{
const SSI263Type type = g_cmdLine.slotInfo[i].socketSSI263[socket];
if (type != SSI263Unknown)
dynamic_cast<MockingboardCard&>(GetCardMgr().GetRef(i)).SetSocketSSI263(socket, type);
}
const SSI263Type type = g_cmdLine.slotInfo[i].socketSC01;
if (type != SSI263Unknown)
dynamic_cast<MockingboardCard&>(GetCardMgr().GetRef(i)).SetSocketSC01(type);
}
}
// Aux slot
if (g_cmdLine.auxSlotCard != CT_Undefined)
{
if (g_cmdLine.auxSlotCard == CT_Empty)
{
GetCardMgr().RemoveAux();
SetExpansionMemType(CT_Empty);
}
else
{
if (GetCardMgr().QueryAux() != g_cmdLine.auxSlotCard) // Ignore if already got this card type in aux slot, ie. via LoadConfiguration()
{
GetCardMgr().InsertAux(g_cmdLine.auxSlotCard);
SetExpansionMemType(g_cmdLine.auxSlotCard);
}
#ifdef RAMWORKS
if (g_cmdLine.uRamWorksExPages)
{
_ASSERT(g_cmdLine.auxSlotCard == CT_RamWorksIII); // Implicitly set when using cmd line "-r banks"
SetRamWorksMemorySize(g_cmdLine.uRamWorksExPages);
g_cmdLine.uRamWorksExPages = 0; // Don't reapply after a restart
}
#endif
}
g_cmdLine.auxSlotCard = CT_Undefined; // Don't reapply after a restart
}
// Create window after inserting/removing VidHD card (as it affects width & height)
{
Win32Frame::GetWin32Frame().SetViewportScale(Win32Frame::GetWin32Frame().GetViewportScale(), true);
GetFrame().Initialize(true); // g_pFramebufferinfo been created now & COM init'ed
LogFileOutput("Main: VideoInitialize()\n");
LogFileOutput("Main: FrameCreateWindow() - pre\n");
Win32Frame::GetWin32Frame().FrameCreateWindow(); // GetFrame().g_hFrameWindow is now valid
LogFileOutput("Main: FrameCreateWindow() - post\n");
}
// Set best W,H resolution after inserting/removing VidHD card
if (g_cmdLine.bestFullScreenResolution || g_cmdLine.userSpecifiedWidth || g_cmdLine.userSpecifiedHeight)
{
bool res = false;
UINT bestWidth = 0, bestHeight = 0;
if (g_cmdLine.bestFullScreenResolution)
res = GetFrame().GetBestDisplayResolutionForFullScreen(bestWidth, bestHeight);
else
res = GetFrame().GetBestDisplayResolutionForFullScreen(bestWidth, bestHeight, g_cmdLine.userSpecifiedWidth, g_cmdLine.userSpecifiedHeight);
if (res)
LogFileOutput("Best resolution for -fs-width/height=x switch(es): Width=%d, Height=%d\n", bestWidth, bestHeight);
else
LogFileOutput("Failed to set parameter for -fs-width/height=x switch(es)\n");
if (res)
g_fullScreenResolutionChangedByUser = true;
}
// Pre: may need g_hFrameWindow for MessageBox errors
// Post: may enable HDC card, required for MemInitialize()->MemInitializeIO(); and may enable DiskII card
for (UINT i = SLOT1; i < NUM_SLOTS; i++)
{
bool temp = false;
InsertFloppyDisks(i, g_cmdLine.szImageName_drive[i], g_cmdLine.driveConnected[i], i == SLOT6 ? g_cmdLine.bBoot : temp);
g_cmdLine.szImageName_drive[i][DRIVE_1] = g_cmdLine.szImageName_drive[i][DRIVE_2] = NULL; // Don't insert on a restart
InsertHardDisks(i, g_cmdLine.szImageName_harddisk[i], i == SLOT7 ? g_cmdLine.bBoot : temp);
for (UINT j = HARDDISK_1; j < NUM_HARDDISKS; j++)
g_cmdLine.szImageName_harddisk[i][j] = NULL; // Don't insert on a restart
}
Snapshot_UpdatePath(); // If save-state's filename is a harddisk, and the floppy is in the same path, then the filename won't be updated
// Set *after* InsertFloppyDisks() & InsertHardDisks(), which both update g_sCurrentDir
if (!g_cmdLine.strCurrentDir.empty())
SetCurrentImageDir(g_cmdLine.strCurrentDir);
if (g_cmdLine.bRemoveNoSlotClock)
MemRemoveNoSlotClock();
if (g_cmdLine.supportExtraMBCardTypes)
GetCardMgr().GetMockingboardCardMgr().SetEnableExtraCardTypes(true);
if (g_cmdLine.noDisk2StepperDefer)
GetCardMgr().GetDisk2CardMgr().SetStepperDefer(false);
if (g_cmdLine.useAltCpuEmulation)
ForceAltCpuEmulation();
if (!g_cmdLine.debuggerAutoRunScriptFilename.empty())
DebugSetAutoRunScript(g_cmdLine.debuggerAutoRunScriptFilename);
// Call DebugInitialize() after SetCurrentImageDir()
DebugInitialize();
LogFileOutput("Main: DebugInitialize()\n");
MemInitialize();
LogFileOutput("Main: MemInitialize()\n");
// Show About dialog after creating main window (need g_hFrameWindow)
if (bShowAboutDlg)
{
if (!AboutDlg())
g_cmdLine.bShutdown = true; // Close everything down
else
RegSaveString(REG_CONFIG, REGVALUE_VERSION, true, g_VERSIONSTRING); // Only save version after user accepts license
}
if (g_bCapturePrintScreenKey)
{
RegisterHotKeys(); // needs valid g_hFrameWindow
LogFileOutput("Main: RegisterHotKeys()\n");
}
if (g_bHookSystemKey)
{
if (GetHookFilter().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 Reset() all cards:
// . Switch from a booting A][+ to a non-autostart A][, so need to turn off floppy motor; and GH#515 (for HDCs)
GetCardMgr().Reset(true);
LogFileOutput("Main: GetCardMgr().Reset()\n");
if (!g_bSysClkOK)
{
GetFrame().FrameMessageBox("DirectX failed to create SystemClock instance", "AppleWin Error", MB_OK);
g_cmdLine.bShutdown = true;
}
if (g_bCustomRomF8Failed || g_bCustomRomFailed || (g_hCustomRomF8 != INVALID_HANDLE_VALUE && g_hCustomRom != INVALID_HANDLE_VALUE))
{
std::string msg = g_bCustomRomF8Failed ? "Failed to load custom F8 rom (not found or not exactly 2KiB)\n"
: g_bCustomRomFailed ? "Failed to load custom rom (not found or not exactly 12KiB or 16KiB)\n"
: "Unsupported -rom and -f8rom being used at the same time\n";
LogFileOutput("%s", msg.c_str());
GetFrame().FrameMessageBox(msg.c_str(), "AppleWin Error", MB_OK);
g_cmdLine.bShutdown = true;
}
if (g_cmdLine.szSnapshotName)
{
std::string strPathname(g_cmdLine.szSnapshotName);
size_t nIdx = strPathname.find_last_of(PATH_SEPARATOR);
if (nIdx >= 0 && nIdx+1 < (int)strPathname.length()) // path exists?
{
const std::string strPath = strPathname.substr(0, nIdx+1);
SetCurrentImageDir(strPath);
}
// Override value just loaded from Registry by LoadConfiguration()
// . NB. Registry value is not updated with this cmd-line value
Snapshot_SetFilename(g_cmdLine.szSnapshotName);
Snapshot_SetIgnoreHdcFirmware(g_cmdLine.snapshotIgnoreHdcFirmware);
Snapshot_LoadState();
g_cmdLine.bBoot = true;
g_cmdLine.szSnapshotName = NULL;
}
else
{
Snapshot_Startup(); // Do this after everything has been init'ed
LogFileOutput("Main: Snapshot_Startup()\n");
}
if (g_cmdLine.szScreenshotFilename)
{
GetFrame().Video_RedrawAndTakeScreenShot(g_cmdLine.szScreenshotFilename);
g_cmdLine.bShutdown = true;
}
if (g_cmdLine.bShutdown)
{
PostMessage(GetFrame().g_hFrameWindow, WM_DESTROY, 0, 0); // Close everything down
// NB. If shutting down, then don't post any other messages (GH#286)
}
else
{
if (g_cmdLine.setFullScreen > 0)
{
PostMessage(GetFrame().g_hFrameWindow, WM_USER_FULLSCREEN, 0, 0);
g_cmdLine.setFullScreen = 0;
}
if (g_cmdLine.bBoot)
{
PostMessage(GetFrame().g_hFrameWindow, WM_USER_BOOT, 0, 0);
g_cmdLine.bBoot = false;
}
}
}
static void Shutdown(void)
{
// NB. WM_CLOSE has already called SetNormalMode() to exit full screen mode & restore default resolution
// Release COM
SysClk_UninitTimer();
LogFileOutput("Exit: SysClk_UninitTimer()\n");
CoUninitialize();
LogFileOutput("Exit: CoUninitialize()\n");
LogDone();
RiffFinishWriteFile();
if (g_hCustomRomF8 != INVALID_HANDLE_VALUE)
CloseHandle(g_hCustomRomF8);
if (g_hCustomRom != INVALID_HANDLE_VALUE)
CloseHandle(g_hCustomRom);
if (g_cmdLine.bSlot7EmptyOnExit)
GetCardMgr().Remove(SLOT7);
}
IPropertySheet& GetPropertySheet(void)
{
static CPropertySheet sg_PropertySheet;
return sg_PropertySheet;
}
FrameBase& GetFrame(void)
{
static Win32Frame sg_Win32Frame;
return sg_Win32Frame;
}
Video& GetVideo(void)
{
static Video video;
return video;
}