From 7d7d406f947863cd0f5e0638ea53886945a7ca4a Mon Sep 17 00:00:00 2001 From: bwmott Date: Tue, 8 Jan 2002 05:01:08 +0000 Subject: [PATCH] Initial import of the source code for version 1.1.3a of the DirectX based Windows port of Stella. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@16 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- stella/src/ui/win32/AboutPage.cxx | 36 + stella/src/ui/win32/AboutPage.hxx | 34 + stella/src/ui/win32/AudioStream.cxx | 570 +++++++++ stella/src/ui/win32/AudioStream.hxx | 117 ++ stella/src/ui/win32/BrowseForFolder.cxx | 226 ++++ stella/src/ui/win32/BrowseForFolder.hxx | 175 +++ stella/src/ui/win32/ConfigPage.cxx | 154 +++ stella/src/ui/win32/ConfigPage.hxx | 37 + stella/src/ui/win32/ControlHost.cxx | 1276 +++++++++++++++++++ stella/src/ui/win32/ControlHost.hxx | 246 ++++ stella/src/ui/win32/CoolCaption.cxx | 284 +++++ stella/src/ui/win32/CoolCaption.hxx | 51 + stella/src/ui/win32/DirectDraw.cxx | 470 +++++++ stella/src/ui/win32/DirectDraw.hxx | 53 + stella/src/ui/win32/DirectInput.cxx | 536 ++++++++ stella/src/ui/win32/DirectInput.hxx | 158 +++ stella/src/ui/win32/DirectXFullScreen.cxx | 1044 +++++++++++++++ stella/src/ui/win32/DirectXFullScreen.hxx | 84 ++ stella/src/ui/win32/DirectXWindow.cxx | 1416 +++++++++++++++++++++ stella/src/ui/win32/DirectXWindow.hxx | 91 ++ stella/src/ui/win32/DocPage.cxx | 153 +++ stella/src/ui/win32/DocPage.hxx | 57 + stella/src/ui/win32/FileDialog.cxx | 69 + stella/src/ui/win32/FileDialog.hxx | 51 + stella/src/ui/win32/GlobalData.cxx | 182 +++ stella/src/ui/win32/GlobalData.hxx | 163 +++ stella/src/ui/win32/HeaderCtrl.cxx | 200 +++ stella/src/ui/win32/HeaderCtrl.hxx | 54 + stella/src/ui/win32/HyperLink.cxx | 352 +++++ stella/src/ui/win32/HyperLink.hxx | 66 + stella/src/ui/win32/MainDlg.cxx | 943 ++++++++++++++ stella/src/ui/win32/MainDlg.hxx | 259 ++++ stella/src/ui/win32/PropertySheet.cxx | 260 ++++ stella/src/ui/win32/PropertySheet.hxx | 111 ++ stella/src/ui/win32/RoundButton.cxx | 625 +++++++++ stella/src/ui/win32/RoundButton.hxx | 39 + stella/src/ui/win32/SoundWin32.cxx | 173 +++ stella/src/ui/win32/SoundWin32.hxx | 109 ++ stella/src/ui/win32/Stella.dsp | 675 ++++++++++ stella/src/ui/win32/Stella.dsw | 29 + stella/src/ui/win32/StellaXMain.cxx | 397 ++++++ stella/src/ui/win32/StellaXMain.hxx | 41 + stella/src/ui/win32/TextButton3d.cxx | 186 +++ stella/src/ui/win32/TextButton3d.hxx | 33 + stella/src/ui/win32/Timer.cxx | 89 ++ stella/src/ui/win32/Timer.hxx | 46 + stella/src/ui/win32/Wnd.cxx | 75 ++ stella/src/ui/win32/Wnd.hxx | 39 + stella/src/ui/win32/debug.cxx | 75 ++ stella/src/ui/win32/debug.hxx | 99 ++ stella/src/ui/win32/main.cxx | 185 +++ stella/src/ui/win32/misc/ConvPro.cxx | 185 +++ stella/src/ui/win32/misc/ConvPro.dsp | 105 ++ stella/src/ui/win32/misc/ConvPro.dsw | 29 + stella/src/ui/win32/misc/StdAfx.cxx | 8 + stella/src/ui/win32/misc/StdAfx.hxx | 21 + stella/src/ui/win32/misc/stellapro.h | 704 ++++++++++ stella/src/ui/win32/pch.cxx | 88 ++ stella/src/ui/win32/pch.hxx | 139 ++ stella/src/ui/win32/resource.h | 85 ++ stella/src/ui/win32/stella.ico | Bin 0 -> 1078 bytes stella/src/ui/win32/stella.ini | 49 + stella/src/ui/win32/stella.rc | 353 +++++ stella/src/ui/win32/tile.bmp | Bin 0 -> 27318 bytes 64 files changed, 14659 insertions(+) create mode 100644 stella/src/ui/win32/AboutPage.cxx create mode 100644 stella/src/ui/win32/AboutPage.hxx create mode 100644 stella/src/ui/win32/AudioStream.cxx create mode 100644 stella/src/ui/win32/AudioStream.hxx create mode 100644 stella/src/ui/win32/BrowseForFolder.cxx create mode 100644 stella/src/ui/win32/BrowseForFolder.hxx create mode 100644 stella/src/ui/win32/ConfigPage.cxx create mode 100644 stella/src/ui/win32/ConfigPage.hxx create mode 100644 stella/src/ui/win32/ControlHost.cxx create mode 100644 stella/src/ui/win32/ControlHost.hxx create mode 100644 stella/src/ui/win32/CoolCaption.cxx create mode 100644 stella/src/ui/win32/CoolCaption.hxx create mode 100644 stella/src/ui/win32/DirectDraw.cxx create mode 100644 stella/src/ui/win32/DirectDraw.hxx create mode 100644 stella/src/ui/win32/DirectInput.cxx create mode 100644 stella/src/ui/win32/DirectInput.hxx create mode 100644 stella/src/ui/win32/DirectXFullScreen.cxx create mode 100644 stella/src/ui/win32/DirectXFullScreen.hxx create mode 100644 stella/src/ui/win32/DirectXWindow.cxx create mode 100644 stella/src/ui/win32/DirectXWindow.hxx create mode 100644 stella/src/ui/win32/DocPage.cxx create mode 100644 stella/src/ui/win32/DocPage.hxx create mode 100644 stella/src/ui/win32/FileDialog.cxx create mode 100644 stella/src/ui/win32/FileDialog.hxx create mode 100644 stella/src/ui/win32/GlobalData.cxx create mode 100644 stella/src/ui/win32/GlobalData.hxx create mode 100644 stella/src/ui/win32/HeaderCtrl.cxx create mode 100644 stella/src/ui/win32/HeaderCtrl.hxx create mode 100644 stella/src/ui/win32/HyperLink.cxx create mode 100644 stella/src/ui/win32/HyperLink.hxx create mode 100644 stella/src/ui/win32/MainDlg.cxx create mode 100644 stella/src/ui/win32/MainDlg.hxx create mode 100644 stella/src/ui/win32/PropertySheet.cxx create mode 100644 stella/src/ui/win32/PropertySheet.hxx create mode 100644 stella/src/ui/win32/RoundButton.cxx create mode 100644 stella/src/ui/win32/RoundButton.hxx create mode 100644 stella/src/ui/win32/SoundWin32.cxx create mode 100644 stella/src/ui/win32/SoundWin32.hxx create mode 100644 stella/src/ui/win32/Stella.dsp create mode 100644 stella/src/ui/win32/Stella.dsw create mode 100644 stella/src/ui/win32/StellaXMain.cxx create mode 100644 stella/src/ui/win32/StellaXMain.hxx create mode 100644 stella/src/ui/win32/TextButton3d.cxx create mode 100644 stella/src/ui/win32/TextButton3d.hxx create mode 100644 stella/src/ui/win32/Timer.cxx create mode 100644 stella/src/ui/win32/Timer.hxx create mode 100644 stella/src/ui/win32/Wnd.cxx create mode 100644 stella/src/ui/win32/Wnd.hxx create mode 100644 stella/src/ui/win32/debug.cxx create mode 100644 stella/src/ui/win32/debug.hxx create mode 100644 stella/src/ui/win32/main.cxx create mode 100644 stella/src/ui/win32/misc/ConvPro.cxx create mode 100644 stella/src/ui/win32/misc/ConvPro.dsp create mode 100644 stella/src/ui/win32/misc/ConvPro.dsw create mode 100644 stella/src/ui/win32/misc/StdAfx.cxx create mode 100644 stella/src/ui/win32/misc/StdAfx.hxx create mode 100644 stella/src/ui/win32/misc/stellapro.h create mode 100644 stella/src/ui/win32/pch.cxx create mode 100644 stella/src/ui/win32/pch.hxx create mode 100644 stella/src/ui/win32/resource.h create mode 100644 stella/src/ui/win32/stella.ico create mode 100644 stella/src/ui/win32/stella.ini create mode 100644 stella/src/ui/win32/stella.rc create mode 100644 stella/src/ui/win32/tile.bmp diff --git a/stella/src/ui/win32/AboutPage.cxx b/stella/src/ui/win32/AboutPage.cxx new file mode 100644 index 000000000..fc9231311 --- /dev/null +++ b/stella/src/ui/win32/AboutPage.cxx @@ -0,0 +1,36 @@ +// +// StellaX +// Jeff Miller 04/27/2000 +// +#include "pch.hxx" +#include "AboutPage.hxx" +#include "resource.h" + +CHelpPage::CHelpPage( + ) : \ + CPropertyPage(IDD_ABOUT_PAGE) +{ +} + +BOOL CHelpPage::OnInitDialog( + HWND hwnd + ) +{ + m_hlMail_JSM.SubclassDlgItem( hwnd, IDC_EMAIL_JEFFMILL ); + m_hlMail_JSM.SetURL( _T("mailto:miller@zipcon.net?Subject=StellaX") ); + + m_hlWWW_JSM.SubclassDlgItem( hwnd, IDC_WEB_JEFFMILL ); + m_hlWWW_JSM.SetURL( _T("http://www.emuunlim.com/stellax/") ); + + m_hlMail_Stella.SubclassDlgItem( hwnd, IDC_EMAIL_STELLA ); + m_hlMail_Stella.SetURL( _T("mailto:stella@csc.ncsu.edu") ); + + m_hlWWW_Stella.SubclassDlgItem( hwnd, IDC_WEB_STELLA ); + m_hlWWW_Stella.SetURL( _T("http://stella.atari.org/") ); + + m_hlWWW_Mame.SubclassDlgItem( hwnd, IDC_WWW_MAME ); + m_hlWWW_Mame.SetURL( _T("http://www.classicgaming.com/mame32qa/") ); + + // return FALSE if SetFocus is called + return TRUE; +} diff --git a/stella/src/ui/win32/AboutPage.hxx b/stella/src/ui/win32/AboutPage.hxx new file mode 100644 index 000000000..47f84b44d --- /dev/null +++ b/stella/src/ui/win32/AboutPage.hxx @@ -0,0 +1,34 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef ABOUTPG_H +#define ABOUTPG_H +#pragma once + +#include "PropertySheet.hxx" +#include "HyperLink.hxx" + +class CHelpPage : public CPropertyPage +{ +public: + + CHelpPage(); + +protected: + + virtual BOOL OnInitDialog( HWND hwnd ); + +private: + + CHyperLink m_hlMail_JSM; + CHyperLink m_hlWWW_JSM; + CHyperLink m_hlMail_Stella; + CHyperLink m_hlWWW_Stella; + CHyperLink m_hlWWW_Mame; + + CHelpPage( const CHelpPage& ); // no implementation + void operator=( const CHelpPage& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/AudioStream.cxx b/stella/src/ui/win32/AudioStream.cxx new file mode 100644 index 000000000..b22d1ec84 --- /dev/null +++ b/stella/src/ui/win32/AudioStream.cxx @@ -0,0 +1,570 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// + +#define DIRECTSOUND_VERSION 0x700 + +#include "pch.hxx" +#include "AudioStream.hxx" +#include "Timer.hxx" + +#include "SoundWin32.hxx" + +// +// see "Streaming Wave Files with DirectSound" (Mark McCulley) +// http://msdn.microsoft.com/library/techart/msdn_streams3.htm +// +// Modified for infinite streaming by Jeff Miller 25-Apr-2000 +// + +// The following constants are the defaults for our streaming buffer operation. + +// default buffer length in msec +const UINT AudioStream::DefBufferLength = 250; + +// default buffer service interval in msec +const UINT AudioStream::DefBufferServiceInterval = 50; + +AudioStreamServices::AudioStreamServices( + void + ) : + m_pds( NULL ), + m_hwnd( NULL ) +{ + TRACE ("AudioStreamServices::AudioStreamServices"); +} + + +AudioStreamServices::~AudioStreamServices( + void + ) +{ + TRACE ( "AudioStreamServices::~AudioStreamServices" ); + + if ( m_pds ) + { + m_pds->Release(); + m_pds = NULL; + } +} + + +BOOL AudioStreamServices::Initialize( + HWND hwnd + ) +{ + TRACE( "AudioStreamServices::Initialize" ); + + ASSERT( hwnd != NULL ); + + HRESULT hr; + BOOL fRtn = TRUE; + + if ( m_pds != NULL ) + { + return TRUE; + } + + if ( hwnd == NULL ) + { + // Error, invalid hwnd + + TRACE ( "ERROR: Invalid hwnd, unable to initialize services" ); + return FALSE; + } + + m_hwnd = hwnd; + + // Create DirectSound object + + hr = ::CoCreateInstance( CLSID_DirectSound, + NULL, + CLSCTX_SERVER, + IID_IDirectSound, + (void**)&m_pds ); + if ( FAILED(hr) ) + { + TRACE( "CCI IDirectSound failed" ); + return FALSE; + } + + hr = m_pds->Initialize( NULL ); + if ( FAILED(hr) ) + { + TRACE( "IDS::Initialize failed" ); + m_pds->Release(); + m_pds = NULL; + return FALSE; + } + + // Set cooperative level for DirectSound. Normal means our + // sounds will be silenced when our window loses input focus. + + if ( m_pds->SetCooperativeLevel( m_hwnd, DSSCL_NORMAL ) == DS_OK ) + { + // Any additional initialization goes here + } + else + { + // Error + TRACE ("ERROR: Unable to set cooperative level\n\r"); + m_pds->Release(); + m_pds = NULL; + fRtn = FALSE; + } + + return fRtn; +} + + + +// +// AudioStream class implementation +// +//////////////////////////////////////////////////////////// + + + +AudioStream::AudioStream( + void + ) +{ + TRACE( "AudioStream::AudioStream" ); + + // Initialize data members + + m_pass = NULL; + m_pwavefile = NULL; + m_pdsb = NULL; + m_ptimer = NULL; + m_fPlaying = m_fCued = FALSE; + m_lInService = FALSE; + m_dwWriteCursor = 0; + m_nBufLength = DefBufferLength; + m_cbBufSize = 0; + m_nBufService = DefBufferServiceInterval; + m_nTimeStarted = 0; + m_nTimeElapsed = 0; + m_pbTempData = NULL; +} + + +AudioStream::~AudioStream( + void + ) +{ + TRACE( "AudioStream::~AudioStream" ); + + Destroy(); +} + +BOOL AudioStream::Create( + AudioStreamServices* pass + ) +{ + BOOL fRtn = TRUE; // assume TRUE + + ASSERT( pass ); + + TRACE( "AudioStream::Create" ); + + // pass points to AudioStreamServices object + + m_pass = pass; + + if ( m_pass ) + { + // Create a new WaveFile object + + if ( m_pwavefile = new WaveFile ) + { + // Open given file + + if ( m_pwavefile->Open( ) ) + { + // Calculate sound buffer size in bytes + // Buffer size is average data rate times length of buffer + // No need for buffer to be larger than wave data though + + m_cbBufSize = (m_pwavefile->GetAvgDataRate () * m_nBufLength) / 1000; + + m_pbTempData = new BYTE[ m_cbBufSize ]; + if ( m_pbTempData == NULL ) + { + delete m_pwavefile; + return FALSE; + } + + TRACE1( "average data rate = %d", m_pwavefile->GetAvgDataRate () ); + TRACE1( "m_cbBufSize = %d", m_cbBufSize ); + + // Create sound buffer + + HRESULT hr; + memset (&m_dsbd, 0, sizeof (DSBUFFERDESC)); + m_dsbd.dwSize = sizeof (DSBUFFERDESC); + m_dsbd.dwBufferBytes = m_cbBufSize; + m_dsbd.lpwfxFormat = m_pwavefile->GetWaveFormatEx(); + + hr = m_pass->GetPDS()->CreateSoundBuffer( &m_dsbd, &m_pdsb, NULL ); + if (hr == DS_OK) + { + // Cue for playback + + Cue(); + } + else + { + // Error, unable to create DirectSound buffer + + TRACE ("Error, unable to create DirectSound buffer\n\r"); + if (hr == DSERR_BADFORMAT) + { + TRACE (" Bad format (probably ADPCM)\n\r"); + } + + fRtn = FALSE; + } + } + else + { + // Error opening file + + delete m_pwavefile; + m_pwavefile = NULL; + fRtn = FALSE; + } + + } + else + { + // Error, unable to create WaveFile object + + fRtn = FALSE; + } + } + else + { + // Error, passed invalid parms + + fRtn = FALSE; + } + + return fRtn; +} + + +// Destroy + +BOOL AudioStream::Destroy( + void + ) +{ + BOOL fRtn = TRUE; + + TRACE ("AudioStream::Destroy"); + + // Stop playback + + Stop(); + + // Release DirectSound buffer + + if (m_pdsb) + { + m_pdsb->Release(); + m_pdsb = NULL; + } + + // Delete WaveFile object + + delete m_pwavefile; + m_pwavefile = NULL; + + delete[] m_pbTempData; + m_pbTempData = NULL; + + return fRtn; +} + +// WriteWaveData +// +// Writes wave data to sound buffer. This is a helper method used by Create and +// ServiceBuffer; it's not exposed to users of the AudioStream class. + +BOOL AudioStream::WriteWaveData( + DWORD cbWriteBytes + ) +{ + HRESULT hr; + + LPVOID pvAudioPtr1 = NULL; + DWORD cbAudioBytes1 = 0; + LPVOID pvAudioPtr2 = NULL; + DWORD cbAudioBytes2 = 0; + + BOOL fRtn = TRUE; + + // Lock the sound buffer + + hr = m_pdsb->Lock( m_dwWriteCursor, + cbWriteBytes, + &pvAudioPtr1, &cbAudioBytes1, + &pvAudioPtr2, &cbAudioBytes2, + 0 ); + if ( hr == DS_OK ) + { + // Write data to sound buffer. Because the sound buffer is circular, we may have to + // do two write operations if locked portion of buffer wraps around to start of buffer. + + ASSERT ( pvAudioPtr1 != NULL ); + + m_pwavefile->Read( m_pbTempData, cbWriteBytes ); + + memcpy( pvAudioPtr1, m_pbTempData, cbAudioBytes1 ); + memcpy( pvAudioPtr2, m_pbTempData + cbAudioBytes1, cbAudioBytes2 ); + + // Update our buffer offset and unlock sound buffer + + m_dwWriteCursor = ( m_dwWriteCursor + cbAudioBytes1 + cbAudioBytes2 ) % m_cbBufSize; + + m_pdsb->Unlock( pvAudioPtr1, cbAudioBytes1, + pvAudioPtr2, cbAudioBytes2 ); + } + else + { + // Error locking sound buffer + + TRACE("Error, unable to lock sound buffer" ); + fRtn = FALSE; + } + + return fRtn; +} + + + +// GetMaxWriteSize +// +// Helper function to calculate max size of sound buffer write operation, i.e. how much +// free space there is in buffer. + +DWORD AudioStream::GetMaxWriteSize( + void + ) +{ + DWORD dwCurrentPlayCursor; + DWORD dwCurrentWriteCursor; + DWORD dwMaxSize; + + // Get current play position + + if ( m_pdsb->GetCurrentPosition( &dwCurrentPlayCursor, + &dwCurrentWriteCursor) == DS_OK) + { + if ( m_dwWriteCursor <= dwCurrentPlayCursor ) + { + // Our write position trails play cursor + + dwMaxSize = dwCurrentPlayCursor - m_dwWriteCursor; + } + + else // (m_dwWriteCursor > dwCurrentPlayCursor) + { + // Play cursor has wrapped + + dwMaxSize = m_cbBufSize - m_dwWriteCursor + dwCurrentPlayCursor; + } + + } + else + { + // GetCurrentPosition call failed + ASSERT (0); + dwMaxSize = 0; + } + + return dwMaxSize; +} + + +// ServiceBuffer +// +// Routine to service buffer requests initiated by periodic timer. +// +// Returns TRUE if buffer serviced normally; otherwise returns FALSE. + +BOOL AudioStream::ServiceBuffer( + void + ) +{ + BOOL fRtn = TRUE; + + // Check for reentrance + + if ( ::InterlockedExchange( &m_lInService, TRUE ) == FALSE ) + { + // Not reentered, proceed normally + + // Maintain elapsed time count + + m_nTimeElapsed = timeGetTime () - m_nTimeStarted; + + // All of sound not played yet, send more data to buffer + + DWORD dwFreeSpace = GetMaxWriteSize (); + + // Determine free space in sound buffer + + if (dwFreeSpace) + { + // Enough wave data remains to fill free space in buffer + // Fill free space in buffer with wave data + + if ( WriteWaveData( dwFreeSpace ) == FALSE ) + { + // Error writing wave data + + fRtn = FALSE; + ASSERT (0); + TRACE ("WriteWaveData failed\n\r"); + } + } + else + { + // No free space in buffer for some reason + + fRtn = FALSE; + } + + // Reset reentrancy semaphore + + ::InterlockedExchange( &m_lInService, FALSE ); + } + else + { + // Service routine reentered. Do nothing, just return + + fRtn = FALSE; + } + + return fRtn; +} + + +void AudioStream::Cue( + void + ) +{ + TRACE ( "AudioStream::Cue" ); + + if ( !m_fCued ) + { + // Reset buffer ptr + + m_dwWriteCursor = 0; + + // Reset file ptr, etc + + m_pwavefile->Cue(); + + // Reset DirectSound buffer + + m_pdsb->SetCurrentPosition( 0 ); + + // Fill buffer with wave data + + WriteWaveData( m_cbBufSize ); + + m_fCued = TRUE; + } +} + + +void AudioStream::Play( + void + ) +{ + if ( m_pdsb ) + { + // If playing, stop + + if (m_fPlaying) + { + Stop(); + } + + // Cue for playback if necessary + + if (!m_fCued) + { + Cue(); + } + + // Begin DirectSound playback + + HRESULT hr = m_pdsb->Play( 0, 0, DSBPLAY_LOOPING ); + if (hr == DS_OK) + { + m_nTimeStarted = timeGetTime(); + + // Kick off timer to service buffer + + m_ptimer = new CMmTimer; + if ( m_ptimer ) + { + m_ptimer->Create( m_nBufService, m_nBufService, (DWORD)this, TimerCallback ); + } + else + { + Stop(); + return; + } + + // Playback begun, no longer cued + + m_fPlaying = TRUE; + m_fCued = FALSE; + } + else + { + TRACE ("Error, play failed\n\r"); + } + } +} + + +BOOL AudioStream::TimerCallback( + DWORD dwUser + ) +{ + // dwUser contains ptr to AudioStream object + + AudioStream* pas = (AudioStream *)dwUser; + + return pas->ServiceBuffer(); +} + + +void AudioStream::Stop( + void + ) +{ + TRACE ("AudioStream::Stop"); + + if (m_fPlaying) + { + // Stop DirectSound playback + + m_pdsb->Stop (); + + // Delete Timer object + + delete m_ptimer; + m_ptimer = NULL; + + m_fPlaying = FALSE; + } +} + diff --git a/stella/src/ui/win32/AudioStream.hxx b/stella/src/ui/win32/AudioStream.hxx new file mode 100644 index 000000000..482515aff --- /dev/null +++ b/stella/src/ui/win32/AudioStream.hxx @@ -0,0 +1,117 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef AUDIOSTREAM_H +#define AUDIOSTREAM_H + +#include + +class CMmTimer; +class WaveFile; + +// Classes + +// AudioStreamServices +// +// DirectSound apportions services on a per-window basis to allow +// sound from background windows to be muted. The AudioStreamServices +// class encapsulates the initialization of DirectSound services. +// +// Each window that wants to create AudioStream objects must +// first create and initialize an AudioStreamServices object. +// All AudioStream objects must be destroyed before the associated +// AudioStreamServices object is destroyed. + +class AudioStreamServices +{ +public: + AudioStreamServices( void ); + ~AudioStreamServices( void ); + + BOOL Initialize ( HWND hwnd ); + + LPDIRECTSOUND GetPDS(void) + { + return m_pds; + } + +protected: + HWND m_hwnd; + LPDIRECTSOUND m_pds; + +private: + + AudioStreamServices( const AudioStreamServices& ); // no implementation + void operator=( const AudioStreamServices& ); // no implementation + +}; + + +// AudioStream +// +// Audio stream interface class for playing WAV files using DirectSound. +// Users of this class must create AudioStreamServices object before +// creating an AudioStream object. +// +// Public Methods: +// +// Public Data: +// + +class AudioStream +{ +public: + AudioStream(void); + ~AudioStream(void); + + BOOL Create( AudioStreamServices * pass ); + + BOOL Destroy( void ); + + void Play( void ); + void Stop( void ); + +protected: + + void Cue (void); + BOOL WriteWaveData( DWORD cbWriteBytes ); + + DWORD GetMaxWriteSize (void); + BOOL ServiceBuffer (void); + + static BOOL TimerCallback (DWORD dwUser); + + AudioStreamServices * m_pass; // ptr to AudioStreamServices object + LPDIRECTSOUNDBUFFER m_pdsb; // sound buffer + + WaveFile * m_pwavefile; // ptr to WaveFile object + CMmTimer * m_ptimer; // ptr to Timer object + + BOOL m_fCued; // semaphore (stream cued) + BOOL m_fPlaying; // semaphore (stream playing) + + DSBUFFERDESC m_dsbd; // sound buffer description + + LONG m_lInService; // reentrancy semaphore + UINT m_dwWriteCursor; // last write position + UINT m_nBufLength; // length of sound buffer in msec + UINT m_cbBufSize; // size of sound buffer in bytes + UINT m_nBufService; // service interval in msec + UINT m_nDuration; // duration of wave file + UINT m_nTimeStarted; // time (in system time) playback started + UINT m_nTimeElapsed; // elapsed time in msec since playback started + + BYTE* m_pbTempData; // Cache so we dont call Read twice for overlap + + static const UINT DefBufferLength; + static const UINT DefBufferServiceInterval; + +private: + + AudioStream( const AudioStream& ); // no implementation + void operator=( const AudioStream& ); // no implementation + +}; + +#endif // _INC_AUDIOSTREAM \ No newline at end of file diff --git a/stella/src/ui/win32/BrowseForFolder.cxx b/stella/src/ui/win32/BrowseForFolder.cxx new file mode 100644 index 000000000..4f72c395d --- /dev/null +++ b/stella/src/ui/win32/BrowseForFolder.cxx @@ -0,0 +1,226 @@ +////////////////////////////////////////////////////////////////////// +// +// ShellBrowser.cpp: implementation of the CShellBrowser class. +// + +#include "pch.hxx" +#include "BrowseForFolder.hxx" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// +// Construction/Destruction +// + +#ifdef MFC +CBrowseForFolder::CBrowseForFolder( + const HWND hParent /*= NULL*/, + const LPITEMIDLIST pidl /*= NULL*/, + const int nTitleID /*= 0*/ + ) +{ + m_hwnd = NULL; + SetOwner(hParent); + SetRoot(pidl); + SetTitle(nTitleID); + m_bi.lpfn = BrowseCallbackProc; + m_bi.lParam = reinterpret_cast( this ); + m_bi.pszDisplayName = m_szSelected; +} +#endif + +CBrowseForFolder::CBrowseForFolder( + const HWND hParent, + const LPITEMIDLIST pidl, + LPCTSTR strTitle) +{ + m_hwnd = NULL; + + SetOwner(hParent); + + SetRoot(pidl); + + SetTitle(strTitle); + + m_bi.lpfn = BrowseCallbackProc; + m_bi.lParam = reinterpret_cast( this ); + m_bi.pszDisplayName = m_szSelected; +} + +CBrowseForFolder::~CBrowseForFolder() +{ + +} + +////////////////////////////////////////////////////////////////////// +// +// Implementation +// + +void CBrowseForFolder::SetOwner(const HWND hwndOwner) +{ + if (m_hwnd != NULL) + return; + + m_bi.hwndOwner = hwndOwner; +} + +void CBrowseForFolder::SetRoot(const LPITEMIDLIST pidl) +{ + if (m_hwnd != NULL) + return; + + m_bi.pidlRoot = pidl; +} + +LPCTSTR CBrowseForFolder::GetTitle() const +{ + return m_bi.lpszTitle; +} + +bool CBrowseForFolder::SetTitle( + LPCTSTR strTitle + ) +{ + if (m_hwnd != NULL) + return false; + + if ( strTitle == NULL ) + { + return false; + } + + if ( ! m_pchTitle.Set( strTitle ) ) + { + return false; + } + + m_bi.lpszTitle = m_pchTitle.Get(); + + return true; +} + +#ifdef MFC +bool CBrowseForFolder::SetTitle(const int nTitle) +{ + if (nTitle <= 0) + return false; + + CString strTitle; + if(!strTitle.LoadString(static_cast(nTitle))) + { + return false; + } + SetTitle(strTitle); + return true; +} +#endif + +void CBrowseForFolder::SetFlags(const UINT ulFlags) +{ + if (m_hwnd != NULL) + return; + + m_bi.ulFlags = ulFlags; +} + +LPCTSTR CBrowseForFolder::GetSelectedFolder( + void + ) const +{ + return m_szSelected; +} + +bool CBrowseForFolder::SelectFolder() +{ + bool bRet = false; + + LPITEMIDLIST pidl; + if ((pidl = ::SHBrowseForFolder(&m_bi)) != NULL) + { + m_strPath.Set( _T("") ); + if (SUCCEEDED(::SHGetPathFromIDList(pidl, m_szSelected))) + { + bRet = true; + m_strPath.Set( m_szSelected ); + } + + LPMALLOC pMalloc; + //Retrieve a pointer to the shell's IMalloc interface + if (SUCCEEDED(SHGetMalloc(&pMalloc))) + { + // free the PIDL that SHBrowseForFolder returned to us. + pMalloc->Free(pidl); + // release the shell's IMalloc interface + (void)pMalloc->Release(); + } + } + m_hwnd = NULL; + + return bRet; +} + +void CBrowseForFolder::OnInit() const +{ + +} + +void CBrowseForFolder::OnSelChanged(const LPITEMIDLIST pidl) const +{ + (void)pidl; +} + +void CBrowseForFolder::EnableOK(const bool bEnable) const +{ + if (m_hwnd == NULL) + return; + + // (void)SendMessage(m_hwnd, BFFM_ENABLEOK, static_cast(bEnable), NULL); + (void)SendMessage( m_hwnd, BFFM_ENABLEOK, NULL, static_cast(bEnable) ); +} + +void CBrowseForFolder::SetSelection(const LPITEMIDLIST pidl) const +{ + if (m_hwnd == NULL) + return; + + (void)SendMessage(m_hwnd, BFFM_SETSELECTION, FALSE, reinterpret_cast(pidl)); +} + +void CBrowseForFolder::SetSelection( + LPCTSTR strPath + ) const +{ + if (m_hwnd == NULL) + return; + + (void)SendMessage(m_hwnd, BFFM_SETSELECTION, TRUE, reinterpret_cast(strPath)); +} + +void CBrowseForFolder::SetStatusText( + LPCTSTR strText + ) const +{ + if (m_hwnd == NULL) + return; + + (void)SendMessage(m_hwnd, BFFM_SETSTATUSTEXT, NULL, reinterpret_cast(strText)); +} + +int __stdcall CBrowseForFolder::BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + CBrowseForFolder* pbff = reinterpret_cast( lpData ); + pbff->m_hwnd = hwnd; + if (uMsg == BFFM_INITIALIZED) + pbff->OnInit(); + else if (uMsg == BFFM_SELCHANGED) + pbff->OnSelChanged( reinterpret_cast( lParam )); + + return 0; +} + diff --git a/stella/src/ui/win32/BrowseForFolder.hxx b/stella/src/ui/win32/BrowseForFolder.hxx new file mode 100644 index 000000000..58cf1d638 --- /dev/null +++ b/stella/src/ui/win32/BrowseForFolder.hxx @@ -0,0 +1,175 @@ +////////////////////////////////////////////////////////////////////// +// +// ShellBrowser.h: interface for the CShellBrowser class. +// +// Copyright 1998 Scott D. Killen +// +////////////////////////////////////////////////////////////////////// + +#ifndef __SHELLBROWSER_H__ +#define __SHELLBROWSER_H__ + +#if _MSC_VER >= 1000 +#pragma once +#endif // _MSC_VER >= 1000 + +#include + +///////////////////////////////////////////////////////////////////// +// +// CShellBrowser +// + +class CBrowseForFolder +{ +public: +#ifdef MFC + CBrowseForFolder(const HWND hParent = NULL, + const LPITEMIDLIST pidl = NULL, + const int nTitleID = 0); +#endif + + CBrowseForFolder(const HWND hParent = NULL, + const LPITEMIDLIST pidl = NULL, + LPCTSTR strTitle = NULL ); + + virtual ~CBrowseForFolder(); + + // + // Set the handle of the owner window for the dialog box. + // + void SetOwner(const HWND hwndOwner); + + // + // Set the root of the heirarchy that will be browsed. Get pidl from + // SHGetSpecialFolderLocation. This can be set to NULL to use the Virtual Folder + // that represents the Windows Desktop. + // + void SetRoot(const LPITEMIDLIST pidl); + + // + // Access a string that is displayed above the tree view control in the dialog box. + // This string can be used to specify instructions to the user. strTitle is a + // CString containing the text to be displayed. nTitle is the index of a string + // resource to be loaded. The return value is false if the resource could not be + // loaded. + // + LPCTSTR GetTitle() const; + bool SetTitle(LPCTSTR strTitle); +#ifdef MFC + bool SetTitle(const int nTitle); +#endif + + // + // ulFlags = Value specifying the types of folders to be listed in the dialog box + // as well as other options. This member can include zero or more of the following + // values: + // + // BIF_BROWSEFORCOMPUTER Only returns computers. If the user selects + // anything other than a computer, the OK button + // is grayed. + // + // BIF_BROWSEFORPRINTER Only returns printers. If the user selects + // anything other than a printer, the OK button + // is grayed. + // + // BIF_DONTGOBELOWDOMAIN Does not include network folders below the + // domain level in the tree view control. + // + // BIF_RETURNFSANCESTORS Only returns file system ancestors. If the user + // selects anything other than a file system + // ancestor, the OK button is grayed. + // + // BIF_RETURNONLYFSDIRS Only returns file system directories. If the + // user selects folders that are not part of the + // file system, the OK button is grayed. + // + // BIF_STATUSTEXT Includes a status area in the dialog box. The + // callback function can set the status text by + // sending messages to the dialog box. + // + UINT GetFlags() const; + void SetFlags(const UINT ulFlags); + + // + // Call GetSelectedFolder to retrieve the folder selected by the user. + // + LPCTSTR GetSelectedFolder() const; + + // + // Function to retreive the image associated with the selected folder. The image is + // specified as an index to the system image list. + // + int GetImage() const; + + // + // Call SelectFolder to display the dialog and get a selection from the user. Use + // GetSelectedFolder and GetImage to get the results of the dialog. + // + bool SelectFolder(); + +protected: + // + // OnInit is called before the dialog is displayed on the screen. + // + virtual void OnInit() const; + + // + // OnSelChanged is called whenever the user selects a different directory. pidl is + // the LPITEMIDLIST of the new selection. Use SHGetPathFromIDList to retrieve the + // path of the selection. + // + virtual void OnSelChanged(const LPITEMIDLIST pidl) const; + + // + // Call EnableOK to enable the OK button on the active dialog. If bEnable is true + // then the button is enabled, otherwise it is disabled. + // NOTE -- This function should only be called within overrides of OnInit and + // OnSelChanged. + // + void EnableOK(const bool bEnable) const; + + // + // Call SetSelection to set the selection in the active dialog. pidl is the + // LPITEMIDLIST + // of the path to be selected. strPath is a CString containing the path to be + // selected. + // NOTE -- This function should only be called within overrides of OnInit and + // OnSelChanged. + // + void SetSelection(const LPITEMIDLIST pidl) const; + void SetSelection(LPCTSTR strPath) const; + + // + // Call SetStatusText to set the text in the Status area in the active dialog. + // strText is the text to be displayed. + // NOTE -- This function should only be called within overrides of OnInit and + // OnSelChanged. + // + void SetStatusText(LPCTSTR strText) const; + +private: + static int __stdcall BrowseCallbackProc(HWND hwnd, + UINT uMsg, + LPARAM lParam, + LPARAM lpData); + + CSimpleString m_pchTitle; + + BROWSEINFO m_bi; + char m_szSelected[MAX_PATH]; + CSimpleString m_strPath; + HWND m_hwnd; +}; + +inline UINT CBrowseForFolder::GetFlags() const +{ + return m_bi.ulFlags; +} + +inline int CBrowseForFolder::GetImage() const +{ + return m_bi.iImage; +} + +#endif // __SHELLBROWSER_H__ diff --git a/stella/src/ui/win32/ConfigPage.cxx b/stella/src/ui/win32/ConfigPage.cxx new file mode 100644 index 000000000..5c976a4dc --- /dev/null +++ b/stella/src/ui/win32/ConfigPage.cxx @@ -0,0 +1,154 @@ +// +// StellaX +// Jeff Miller 05/07/2000 +// + +#include "pch.hxx" +#include "ConfigPage.hxx" +#include "resource.h" + +#include "BrowseForFolder.hxx" + +CConfigPage::CConfigPage( + CGlobalData& rGlobalData + ) : \ + m_rGlobalData( rGlobalData ), + CPropertyPage( IDD_CONFIG_PAGE ) +{ +} + +BOOL CConfigPage::OnInitDialog( + HWND hwnd + ) +{ + // return FALSE if SetFocus is called + + m_hwnd = hwnd; + + HWND hwndCtrl; + + // + // Set up ROMPATH + // + + hwndCtrl = ::GetDlgItem( hwnd, IDC_ROMPATH ); + ::SendMessage( hwndCtrl, EM_LIMITTEXT, MAX_PATH, 0 ); + ::SetWindowText( hwndCtrl, m_rGlobalData.RomDir() ); + + // + // Set up PADDLE + // + + hwndCtrl = ::GetDlgItem( hwnd, IDC_PADDLE ); + + LPTSTR psz = _T("0"); + TCHAR i; + for ( i = 0; i < 4; ++i ) + { + psz[0] = _T('0') + i; + ::SendMessage( hwndCtrl, CB_INSERTSTRING, (WPARAM)-1, (LPARAM)psz ); + } + + ::SendMessage( hwndCtrl, CB_SETCURSEL, + m_rGlobalData.PaddleMode(), 0 ); + + // + // Set up SOUND + // + + hwndCtrl = ::GetDlgItem( hwnd, IDC_SOUND ); + ::SendMessage( hwndCtrl, BM_SETCHECK, + m_rGlobalData.NoSound() ? BST_CHECKED : BST_UNCHECKED, + 0 ); + + // + // Set up AutoSelectVideoMode + // + + + hwndCtrl = ::GetDlgItem( hwnd, IDC_AUTO_SELECT_VIDEOMODE ); + ::SendMessage( hwndCtrl, BM_SETCHECK, + m_rGlobalData.AutoSelectVideoMode() ? BST_CHECKED : BST_UNCHECKED, + 0 ); + + // + // Set up JOYSTICK + // + + hwndCtrl = ::GetDlgItem( hwnd, IDC_JOYSTICK ); + ::SendMessage( hwndCtrl, BM_SETCHECK, + m_rGlobalData.DisableJoystick() ? BST_CHECKED : BST_UNCHECKED, + 0 ); + + return TRUE; +} + +void CConfigPage::OnDestroy( + void + ) +{ +} + +LONG CConfigPage::OnApply( + LPPSHNOTIFY lppsn + ) +{ + UNUSED_ALWAYS( lppsn ); + + // + // Apply the changes + // + + // HWND hwnd = lppsn->hdr.hwndFrom; <<-- points to the sheet! + + HWND hwndCtrl; + + hwndCtrl = ::GetDlgItem( m_hwnd, IDC_ROMPATH ); + ASSERT( hwndCtrl ); + ::GetWindowText( hwndCtrl, m_rGlobalData.m_pszRomDir, MAX_PATH ); + + hwndCtrl = ::GetDlgItem( m_hwnd, IDC_PADDLE ); + ASSERT( hwndCtrl ); + m_rGlobalData.m_nPaddleMode = ::SendMessage( hwndCtrl, CB_GETCURSEL, 0, 0 ); + + hwndCtrl = ::GetDlgItem( m_hwnd, IDC_SOUND ); + ASSERT( hwndCtrl ); + m_rGlobalData.m_fNoSound = ( ::SendMessage( hwndCtrl, BM_GETCHECK, 0, 0 ) + == BST_CHECKED ); + + hwndCtrl = ::GetDlgItem( m_hwnd, IDC_AUTO_SELECT_VIDEOMODE ); + ASSERT( hwndCtrl ); + m_rGlobalData.m_fAutoSelectVideoMode = ( ::SendMessage( hwndCtrl, BM_GETCHECK, 0, 0 ) + == BST_CHECKED ); + + hwndCtrl = ::GetDlgItem( m_hwnd, IDC_JOYSTICK ); + ASSERT( hwndCtrl ); + m_rGlobalData.m_fDisableJoystick= ( ::SendMessage( hwndCtrl, BM_GETCHECK, 0, 0 ) + == BST_CHECKED ); + + m_rGlobalData.SetModified(); + + return PSNRET_NOERROR; +} + +BOOL CConfigPage::OnCommand( + WORD wNotifyCode, + WORD wID, + HWND hwndCtl + ) +{ + UNUSED_ALWAYS( wNotifyCode ); + UNUSED_ALWAYS( hwndCtl ); + + if ( wID == IDC_BROWSE ) + { + CBrowseForFolder bff( m_hwnd ); + bff.SetFlags( BIF_RETURNONLYFSDIRS ); + if ( bff.SelectFolder() ) + { + ::SetDlgItemText( m_hwnd, IDC_ROMPATH, bff.GetSelectedFolder() ); + } + } + + return FALSE; +} diff --git a/stella/src/ui/win32/ConfigPage.hxx b/stella/src/ui/win32/ConfigPage.hxx new file mode 100644 index 000000000..5c9ab3af5 --- /dev/null +++ b/stella/src/ui/win32/ConfigPage.hxx @@ -0,0 +1,37 @@ +// +// StellaX +// Jeff Miller 05/07/2000 +// + +#ifndef CONFIGPG_H +#define CONFIGPG_H +#pragma once + +#include "PropertySheet.hxx" +#include "GlobalData.hxx" + +class CConfigPage : public CPropertyPage +{ +public: + + CConfigPage( CGlobalData& rGlobalData ); + +protected: + + virtual BOOL OnInitDialog( HWND hwnd ); + virtual void OnDestroy(); + virtual LONG OnApply( LPPSHNOTIFY lppsn ); + + virtual BOOL OnCommand( WORD /* wNotifyCode */, WORD /* wID */, HWND /* hwndCtl */ ); + +private: + + CGlobalData& m_rGlobalData; + HWND m_hwnd; + + CConfigPage( const CConfigPage& ); // no implementation + void operator=( const CConfigPage& ); // no implementation + +}; + +#endif diff --git a/stella/src/ui/win32/ControlHost.cxx b/stella/src/ui/win32/ControlHost.cxx new file mode 100644 index 000000000..278abd33b --- /dev/null +++ b/stella/src/ui/win32/ControlHost.cxx @@ -0,0 +1,1276 @@ +// +// StellaX +// Jeff Miller 05/02/2000 +// + +#include "pch.hxx" +#include "ControlHost.hxx" + +#include +#include + +#pragma comment(lib, "oleaut32") + +CActiveXControl::CActiveXControl( + ) : \ + m_piDispatch( NULL ) +{ + TRACE( "CActiveXControl::CActiveXControl" ); +} + +CActiveXControl::~CActiveXControl( + ) +{ + TRACE( "CActiveXControl::~CActiveXControl" ); + + if ( m_piDispatch ) + { + m_piDispatch->Release(); + m_piDispatch = NULL; + } +} + +HRESULT CActiveXControl::CreateInstance( + void + ) +{ + TRACE( "CActiveXControl::CreateInstance" ); + + if ( m_piDispatch != NULL ) + { + return S_OK; + } + + return ::CoCreateInstance(GetCLSID(), NULL, + CLSCTX_INPROC_SERVER, IID_IDispatch, (void**)&m_piDispatch); +} + +void CActiveXControl::OnInitialUpdate( + void + ) +{ + TRACE( "CActiveXControl::OnInitialUpdate" ); +} + +HRESULT CActiveXControl::PutPropertyByName( + LPCOLESTR lpsz, + VARIANT* pVar + ) +{ + TRACE( "CActiveXControl::PutPropertyByName" ); + + if (m_piDispatch == NULL) + return E_UNEXPECTED; + + if (pVar == NULL) + return E_INVALIDARG; + + DISPID dwDispID; + HRESULT hr = GetIDOfName(lpsz, &dwDispID); + if (SUCCEEDED(hr)) + { + hr = PutProperty(m_piDispatch, dwDispID, pVar); + } + + return hr; +} + +HRESULT CActiveXControl::GetIDOfName( + LPCOLESTR lpsz, + DISPID* pdispid + ) +{ + TRACE( "CActiveXControl::GetIDOfName" ); + + if (m_piDispatch == NULL) + { + return E_UNEXPECTED; + } + + return m_piDispatch->GetIDsOfNames( IID_NULL, + (LPOLESTR*)&lpsz, + 1, + LOCALE_USER_DEFAULT, + pdispid ); +} + +HRESULT CActiveXControl::PutProperty( + IDispatch* pDisp, + DISPID dwDispID, + VARIANT* pVar + ) +{ + TRACE( "CActiveXControl::PutProperty" ); + + DISPPARAMS dispparams = {NULL, NULL, 1, 1}; + dispparams.rgvarg = pVar; + DISPID dispidPut = DISPID_PROPERTYPUT; + dispparams.rgdispidNamedArgs = &dispidPut; + + if (pVar->vt == VT_UNKNOWN || pVar->vt == VT_DISPATCH || + (pVar->vt & VT_ARRAY) || (pVar->vt & VT_BYREF)) + { + HRESULT hr = pDisp->Invoke(dwDispID, IID_NULL, + LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, + &dispparams, NULL, NULL, NULL); + if (SUCCEEDED(hr)) + return hr; + } + + return pDisp->Invoke( dwDispID, + IID_NULL, + LOCALE_USER_DEFAULT, + DISPATCH_PROPERTYPUT, + &dispparams, + NULL, + NULL, + NULL ); +} + +HRESULT CActiveXControl::GetProperty( + IDispatch* pDisp, + DISPID dwDispID, + VARIANT* pVar + ) +{ + TRACE( "CActiveXControl::GetProperty" ); + + DISPPARAMS dispparamsNoArgs = { NULL, NULL, 0, 0 }; + + return pDisp->Invoke( dwDispID, + IID_NULL, + LOCALE_USER_DEFAULT, + DISPATCH_PROPERTYGET, + &dispparamsNoArgs, + pVar, + NULL, + NULL ); +} + +// Invoke a method by DISPID with a single parameter + +HRESULT CActiveXControl::Invoke1( + DISPID dispid, + VARIANT* pvarParam1, + VARIANT* pvarRet /* = NULL */ + ) +{ + TRACE( "CActiveXControl::Invoke1" ); + + if ( m_piDispatch == NULL ) + { + return E_FAIL; + } + + DISPPARAMS dispparams = { pvarParam1, NULL, 1, 0 }; + + return m_piDispatch->Invoke( dispid, + IID_NULL, + LOCALE_USER_DEFAULT, + DISPATCH_METHOD, + &dispparams, + pvarRet, + NULL, + NULL ); +} + +// Invoke a method by name with a single parameter + +HRESULT CActiveXControl::Invoke1( + LPCOLESTR lpszName, + VARIANT* pvarParam1, + VARIANT* pvarRet /* = NULL */ + ) +{ + TRACE( "CActiveXControl::Invoke1" ); + + HRESULT hr; + DISPID dispid; + + hr = GetIDOfName( lpszName, &dispid ); + if ( SUCCEEDED(hr) ) + { + hr = Invoke1( dispid, pvarParam1, pvarRet ); + } + + return hr; +} + +// --------------------------------------------------------------------------- + +CControlHost::CControlHost( + CActiveXControl* pControl + ) : \ + _hwnd(NULL), + _punkOuter(NULL), + _cRef(0), + m_pControl(pControl) +{ + TRACE( "CControlHost::CControlHost" ); +} + +CControlHost::~CControlHost( + ) +{ + TRACE( "CControlHost::~CControlHost" ); + + // The extra addref is done here to make sure that the last Release + // call in DeleteControl doesn't do the delete this + + AddRef(); + + DeleteControl(); // insurance +} + + +HRESULT CControlHost::SetHwnd( + HWND hwnd + ) +{ + TRACE( "CControlHost::SetHwnd" ); + + _hwnd = hwnd; + + if ( ! GetClientRect( _hwnd, &_rcPos ) ) + { + SetRectEmpty( &_rcPos ); + } + + return S_OK; +} + +HRESULT CControlHost::CreateControl( + void + ) +{ + TRACE( "CControlHost::CreateControl" ); + + HRESULT hr; // standard ole return code + IPersistStreamInit *pps; // IPersistStreamInit interface pointer + + if (m_pControl == NULL) + { + return E_UNEXPECTED; + } + + hr = m_pControl->CreateInstance(); + if (hr != S_OK) + { + return hr; + } + + IOleObject* piOleObject = NULL; + hr = m_pControl->GetDispInterfaceNoAddRef()-> + QueryInterface( IID_IOleObject, + (void**)&piOleObject ); + if (hr != S_OK) + { + return hr; + } + + AddRef(); // addref the container + + piOleObject->SetClientSite(this); + + // Controls like to know they have been initialized by getting + // called on their InitNew method. + + hr = piOleObject->QueryInterface( IID_IPersistStreamInit, + (LPVOID *)&pps ); + if (SUCCEEDED(hr)) + { + pps->InitNew(); + pps->Release(); + } + + // The control creation succeeded, lets create the model. + + m_pControl->OnInitialUpdate(); + + // Tell the control to activate and show itself. + + piOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, _hwnd, + &_rcPos); + piOleObject->DoVerb(OLEIVERB_SHOW, NULL, this, 0, _hwnd, &_rcPos); + + + // cache some frequently needed interface pointers + // cache the control's window handle + + _hwndControl = NULL; + IOleInPlaceActiveObject *pipao = NULL; + hr = piOleObject->QueryInterface(IID_IOleInPlaceActiveObject, + (void**) &pipao); + if (SUCCEEDED(hr)) + { + pipao->GetWindow(&_hwndControl); + pipao->Release(); + } + + piOleObject->Release(); + + return hr; +} + +HRESULT CControlHost::DeleteControl( + void + ) +{ + TRACE( "CControlHost::DeleteControl" ); + + // Close the Control and release the cached pointers. + + if (m_pControl != NULL) + { + IOleObject* piOleObject = NULL; + + if (m_pControl->GetDispInterfaceNoAddRef()->QueryInterface( + IID_IOleObject, (LPVOID *)&piOleObject) == S_OK) + { + piOleObject->DoVerb(OLEIVERB_HIDE, NULL, this, 0, _hwnd, NULL); + piOleObject->Close(OLECLOSE_NOSAVE); + piOleObject->SetClientSite(NULL); + + piOleObject->Release(); + } + + Release(); + + delete m_pControl; + m_pControl = NULL; + } + + return (S_OK); +} + +HRESULT CControlHost::QueryObject( + REFIID riid, + void **ppvObject + ) +{ + TRACE( "CControlHost::QueryObject" ); + + HRESULT hr = E_POINTER; + + if (ppvObject) + { + if (m_pControl) + { + IDispatch* piDispatch = NULL; + if (m_pControl->GetDispInterface(&piDispatch) == S_OK) + { + hr = piDispatch->QueryInterface(riid, ppvObject); + piDispatch->Release(); + } + } + else + { + *ppvObject = NULL; + hr = OLE_E_NOCONNECTION; + } + } + + return hr; +} + +////////////////////////////////////////////////////////////////////////// +// IUnknown methods + +HRESULT CControlHost::QueryInterface( + REFIID riid, + LPVOID* ppvObj + ) +{ + LPVOID pvObj = NULL; + +#ifdef _DEBUG + LPOLESTR psz; + if ( StringFromIID( riid, &psz ) == S_OK ) + { + TRACE( "CControlHost::QueryInterface - riid = %S", psz ); + CoTaskMemFree( psz ); + } +#endif + + if ( IsEqualIID( riid, IID_IOleInPlaceSite ) ) + { + pvObj = (IOleInPlaceSite *)this; + } +#ifdef IMPLEMENT_IOLECONTROLSITE + else if ( IsEqualIID( riid, IID_IOleControlSite ) ) + { + pvObj = (IOleControlSite *)this; + } +#endif +#ifdef IMPLEMENT_IOLEINPLACESITEWINDOWLESS + else if ( IsEqualIID( riid, IID_IOleInPlaceSiteWindowless ) ) + { + pvObj = (IOleInPlaceSiteWindowless *)this; + } +#endif + else if ( IsEqualIID( riid, IID_IDispatch ) ) + { + pvObj = (IDispatch *)this; + } + + if( pvObj ) + { + AddRef(); + } + + *ppvObj = pvObj; + + return pvObj ? S_OK : E_NOINTERFACE; +} + +ULONG CControlHost::AddRef( + void + ) +{ + OutputDebugString("CControlHost::AddRef\n"); + + return (_cRef++); // Not thread safe +} + +ULONG CControlHost::Release( + void + ) +{ + OutputDebugString("CControlHost::Release\n"); + + _cRef--; // Not thread safe + + if (_cRef > 0) + { + return _cRef; + } + + _cRef = 0; + delete this; + + return 0; +} + +////////////////////////////////////////////////////////////////////////// +// IOleClientSite methods + +HRESULT CControlHost::SaveObject( + void + ) +{ + TRACE( "CControlHost::SaveObject" ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::GetMoniker( + DWORD dwAssign, + DWORD dwWhichMoniker, + LPMONIKER * ppMk + ) +{ + TRACE( "CControlHost::GetMoniker" ); + + UNUSED_ALWAYS( dwAssign ); + UNUSED_ALWAYS( dwWhichMoniker ); + + if ( ppMk ) + { + *ppMk = NULL; + } + + return E_NOTIMPL; +} + +HRESULT CControlHost::GetContainer( + LPOLECONTAINER * ppContainer + ) +{ + TRACE( "CControlHost::GetContainer" ); + + if ( ppContainer ) + { + *ppContainer = NULL; + } + + return E_NOTIMPL; +} + +HRESULT CControlHost::ShowObject( + void + ) +{ + TRACE( "CControlHost::ShowObject" ); + + return S_OK; +} + +HRESULT CControlHost::OnShowWindow( + BOOL fShow + ) +{ + TRACE( "CControlHost::OnShowWindow" ); + + UNUSED_ALWAYS( fShow ); + + return S_OK; +} + +HRESULT CControlHost::RequestNewObjectLayout( + void + ) +{ + TRACE( "CControlHost::RequestNewObjectLayout" ); + + return E_NOTIMPL; +} + +////////////////////////////////////////////////////////////////////////// +// IOleWindow methods + +HRESULT CControlHost::GetWindow( + HWND* lphwnd + ) +{ + TRACE( "CControlHost::GetWindow" ); + + *lphwnd = _hwnd; + + return ( _hwnd == NULL ) ? S_FALSE : S_OK; +} + +HRESULT CControlHost::ContextSensitiveHelp( + BOOL fEnterMode + ) +{ + TRACE( "CControlHost::ContextSensitiveHelp" ); + + UNUSED_ALWAYS( fEnterMode ); + + return E_NOTIMPL; +} + +////////////////////////////////////////////////////////////////////////// +// IOleInPlaceSite methods + +HRESULT CControlHost::CanInPlaceActivate( + void + ) +{ + TRACE( "CControlHost::CanInPlaceActivate" ); + + return S_OK; +} + +HRESULT CControlHost::OnInPlaceActivate( + void + ) +{ + TRACE( "CControlHost::OnInPlaceActivate" ); + + return S_OK; +} + +HRESULT CControlHost::OnUIActivate( + void + ) +{ + TRACE( "CControlHost::OnUIActivate" ); + + return S_OK; +} + +HRESULT CControlHost::GetWindowContext( + IOleInPlaceFrame** ppFrame, + IOleInPlaceUIWindow** ppIIPUIWin, + LPRECT lprcPosRect, + LPRECT lprcClipRect, + LPOLEINPLACEFRAMEINFO lpFrameInfo + ) +{ + TRACE( "CControlHost::GetWindowContext" ); + + *ppFrame = this; + AddRef(); + + *ppIIPUIWin = NULL; + + CopyRect(lprcPosRect, &_rcPos); + CopyRect(lprcClipRect, &_rcPos); + + lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO); + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->hwndFrame = _hwnd; + lpFrameInfo->haccel = 0; + lpFrameInfo->cAccelEntries = 0; + + return S_OK; +} + +HRESULT CControlHost::Scroll( + SIZE scrollExtent + ) +{ + TRACE( "CControlHost::Scroll" ); + + UNUSED_ALWAYS( scrollExtent ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::OnUIDeactivate( + BOOL fUndoable + ) +{ + TRACE( "CControlHost::OnUIDeactivate" ); + + UNUSED_ALWAYS( fUndoable ); + + return S_OK; +} + +HRESULT CControlHost::OnInPlaceDeactivate( + void + ) +{ + TRACE( "CControlHost::OnInPlaceDeactivate" ); + + return S_OK; +} + +HRESULT CControlHost::DiscardUndoState( + void + ) +{ + TRACE( "CControlHost::DiscardUndoState" ); + + return S_OK; +} + +HRESULT CControlHost::DeactivateAndUndo( + void + ) +{ + TRACE( "CControlHost::DeactivateAndUndo" ); + + return S_OK; +} + +HRESULT CControlHost::OnPosRectChange( + LPCRECT lprcPosRect + ) +{ + TRACE( "CControlHost::OnPosRectChange" ); + + RECT rcPos, rcClient; + HWND hwnd; + GetWindow(&hwnd); + GetClientRect(hwnd, &rcClient); + GetWindowRect(hwnd, &rcPos); + + if(rcClient.bottom < lprcPosRect->bottom) + rcPos.bottom += lprcPosRect->bottom - rcClient.bottom; + else + rcPos.bottom -= rcClient.bottom - lprcPosRect->bottom; + + if(rcClient.right < lprcPosRect->right) + rcPos.right += lprcPosRect->right - rcClient.right; + else + rcPos.right -= rcClient.right - lprcPosRect->right; + + MoveWindow( hwnd, + rcPos.left, + rcPos.top, + rcPos.right - rcPos.left, + rcPos.bottom - rcPos.top, + TRUE); + + return S_OK; +} + +#ifdef IMPLEMENT_IOLEINPLACESITEWINDOWLESS + +////////////////////////////////////////////////////////////////////////// +// IOleInPlaceSiteEx methods + +HRESULT CControlHost::OnInPlaceActivateEx( + BOOL *pfNoRedraw, + DWORD dwFlags + ) +{ + TRACE( "CControlHost::OnInPlaceActivateEx" ); + + UNUSED_ALWAYS( pfNoRedraw ); + UNUSED_ALWAYS( dwFlags ); + + if ( m_pControl == NULL ) + { + return E_FAIL; + } + + OleLockRunning( m_pControl->GetDispInterfaceNoAddRef(), + TRUE, + FALSE ); + + return S_OK; +} + +HRESULT CControlHost::OnInPlaceDeactivateEx( + BOOL fNoRedraw + ) +{ + TRACE( "CControlHost::OnInPlaceDeactivateEx" ); + + UNUSED_ALWAYS( fNoRedraw ); + + if ( m_pControl == NULL ) + { + return E_FAIL; + } + + OleLockRunning( m_pControl->GetDispInterfaceNoAddRef(), + FALSE, + FALSE ); + + return S_OK; +} + + +HRESULT CControlHost::RequestUIActivate( + void + ) +{ + TRACE( "CControlHost::RequestUIActivate" ); + + return S_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// IOleInPlaceSiteWindowless methods + +HRESULT CControlHost::CanWindowlessActivate( + void + ) +{ + TRACE( "CControlHost::CanWindowlessActivate" ); + + return TRUE; // m_bCanWindowlessActivate); +} + +HRESULT CControlHost::GetCapture( + void + ) +{ + TRACE( "CControlHost::GetCapture" ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::SetCapture( + BOOL fCapture + ) +{ + TRACE( "CControlHost::SetCapture" ); + + if (fCapture) + { + ::SetCapture(_hwnd); + _bCapture = TRUE; + } + else + { + ::ReleaseCapture(); + _bCapture = FALSE; + } + + return S_OK; +} + +HRESULT CControlHost::GetFocus( + void + ) +{ + TRACE( "CControlHost::GetFocus" ); + + return S_OK; +} + +HRESULT CControlHost::SetFocus( + BOOL fFocus + ) +{ + TRACE( "CControlHost::SetFocus" ); + + UNUSED_ALWAYS( fFocus ); + + return S_OK; +} + +HRESULT CControlHost::GetDC( + LPCRECT pRect, + DWORD grfFlags, + HDC* phDC + ) +{ + TRACE( "CControlHost::GetDC" ); + + UNUSED_ALWAYS( pRect ); + UNUSED_ALWAYS( grfFlags ); + + if (!phDC) + { + return E_POINTER; + } + + *phDC = ::GetDC(_hwnd); + + return S_OK; +} + +HRESULT CControlHost::ReleaseDC( + HDC hDC + ) +{ + TRACE( "CControlHost::ReleaseDC" ); + + ::ReleaseDC(_hwnd, hDC); + + return S_OK; +} + +HRESULT CControlHost::InvalidateRect( + LPCRECT pRect, + BOOL fErase + ) +{ + TRACE( "CControlHost::InvalidateRect" ); + + ::InvalidateRect(_hwnd, pRect, fErase); + + return S_OK; +} + +HRESULT CControlHost::InvalidateRgn( + HRGN hRGN, + BOOL fErase + ) +{ + TRACE( "CControlHost::InvalidateRgn" ); + + ::InvalidateRgn(_hwnd, hRGN, fErase); + + return S_OK; +} + +HRESULT CControlHost::ScrollRect( + INT dx, + INT dy, + LPCRECT pRectScroll, + LPCRECT pRectClip + ) +{ + TRACE( "CControlHost::ScrollRect" ); + + UNUSED_ALWAYS( dx ); + UNUSED_ALWAYS( dy ); + UNUSED_ALWAYS( pRectScroll ); + UNUSED_ALWAYS( pRectClip ); + + return S_OK; +} + +HRESULT CControlHost::AdjustRect( + LPRECT prc + ) +{ + TRACE( "CControlHost::AdjustRect" ); + + UNUSED_ALWAYS( prc ); + + return S_OK; +} + +HRESULT CControlHost::OnDefWindowMessage( + UINT msg, + WPARAM wParam, + LPARAM lParam, + LRESULT* plResult + ) +{ + TRACE( "CControlHost::OnDefWindowMessage" ); + + *plResult = ::DefWindowProc(_hwnd, msg, wParam, lParam); + + return S_OK; +} + +#endif + +////////////////////////////////////////////////////////////////////////// +// IOleInPlaceUIWindow methods + +HRESULT CControlHost::GetBorder( + LPRECT lprectBorder + ) +{ + TRACE( "CControlHost::GetBorder" ); + + UNUSED_ALWAYS( lprectBorder ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::RequestBorderSpace( + LPCBORDERWIDTHS lpborderwidths + ) +{ + TRACE( "CControlHost::RequestBorderSpace" ); + + UNUSED_ALWAYS( lpborderwidths ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::SetBorderSpace( + LPCBORDERWIDTHS lpborderwidths + ) +{ + TRACE( "CControlHost::SetBorderSpace" ); + + if( _punkOuter ) + { + return ((CControlHost*)_punkOuter)->SetBorderSpace( lpborderwidths ); + } + + return E_NOTIMPL; +} + +HRESULT CControlHost::SetActiveObject( + IOleInPlaceActiveObject* pActiveObject, + LPCOLESTR lpszObjName + ) +{ + TRACE( "CControlHost::SetActiveObject" ); + + UNUSED_ALWAYS( pActiveObject ); + UNUSED_ALWAYS( lpszObjName ); + + return E_NOTIMPL; +} + +////////////////////////////////////////////////////////////////////////// +// IOleInPlaceFrame methods + +HRESULT CControlHost::InsertMenus( + HMENU hmenuShared, + LPOLEMENUGROUPWIDTHS lpMenuWidths + ) +{ + TRACE( "CControlHost::InsertMenus" ); + + UNUSED_ALWAYS( hmenuShared ); + UNUSED_ALWAYS( lpMenuWidths ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::SetMenu( + HMENU hmenuShared, + HOLEMENU holemenu, + HWND hwndActiveObject + ) +{ + TRACE( "CControlHost::SetMenu" ); + + UNUSED_ALWAYS( hmenuShared ); + UNUSED_ALWAYS( holemenu ); + UNUSED_ALWAYS( hwndActiveObject ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::RemoveMenus( + HMENU hmenuShared + ) +{ + TRACE( "CControlHost::RemoveMenus" ); + + UNUSED_ALWAYS( hmenuShared ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::SetStatusText( + LPCOLESTR pszStatusText + ) +{ + TRACE( "CControlHost::SetStatusText" ); + + UNUSED_ALWAYS( pszStatusText ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::EnableModeless( + BOOL fEnable + ) +{ + TRACE( "CControlHost::EnableModeless" ); + + UNUSED_ALWAYS( fEnable ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::TranslateAccelerator( + LPMSG lpmsg, + WORD wID + ) +{ + TRACE( "CControlHost::TranslateAccelerator" ); + + UNUSED_ALWAYS( lpmsg ); + UNUSED_ALWAYS( wID ); + + return E_NOTIMPL; +} + +#ifdef IMPLEMENT_IOLECONTROLSITE + +////////////////////////////////////////////////////////////////////////// +// IOleControlSite methods + +HRESULT CControlHost::OnControlInfoChanged( + void + ) +{ + TRACE( "CControlHost::OnControlInfoChanged" ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::LockInPlaceActive( + BOOL fLock + ) +{ + TRACE( "CControlHost::LockInPlaceActive" ); + + UNUSED_ALWAYS( fLock ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::GetExtendedControl( + IDispatch** ppDisp + ) +{ + TRACE( "CControlHost::GetExtendedControl" ); + + if (ppDisp == NULL) + { + return E_INVALIDARG; + } + + *ppDisp = (IDispatch *)this; + AddRef(); + + return S_OK; +} + +HRESULT CControlHost::TransformCoords( + POINTL *pptlHimetric, + POINTF *pptfContainer, + DWORD dwFlags + ) +{ + TRACE( "CControlHost::TransformCoords" ); + + UNUSED_ALWAYS( pptlHimetric ); + UNUSED_ALWAYS( pptfContainer ); + UNUSED_ALWAYS( dwFlags ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::TranslateAccelerator( + LPMSG pMsg, + DWORD grfModifiers + ) +{ + TRACE( "CControlHost::TranslateAccelerator" ); + + UNUSED_ALWAYS( pMsg ); + UNUSED_ALWAYS( grfModifiers ); + + return S_FALSE; +} + +HRESULT CControlHost::OnFocus( + BOOL fGotFocus + ) +{ + TRACE( "CControlHost::OnFocus" ); + + UNUSED_ALWAYS( fGotFocus ); + + return E_NOTIMPL; +} + +HRESULT CControlHost::ShowPropertyFrame( + void + ) +{ + TRACE( "CControlHost::ShowPropertyFrame" ); + + return E_NOTIMPL; +} + +#endif + +////////////////////////////////////////////////////////////////////////// +// IDispatch methods + +HRESULT CControlHost::GetIDsOfNames( + REFIID riid, + OLECHAR FAR* FAR* rgszNames, + unsigned int cNames, + LCID lcid, + DISPID FAR* rgdispid + ) +{ + UNUSED_ALWAYS( riid ); + UNUSED_ALWAYS( lcid ); + + HRESULT hr; // standard ole return code + LPOLESTR pName; + DISPID *pdispid; + + hr = S_OK; + pName = *rgszNames; + pdispid = rgdispid; + + for (UINT i=0; ibstrVal) + pvarResult->bstrVal = ::SysAllocString(L""); + + hr = S_OK; + + // If we STILL don't have a bstrVal. Clean up and return an empty variant. + if (!pvarResult->bstrVal) + { + VariantInit(pvarResult); + hr = E_FAIL; + } + + break; + + case DISPID_AMBIENT_USERMODE: + case DISPID_AMBIENT_MESSAGEREFLECT: + pvarResult->vt = VT_BOOL; + pvarResult->boolVal = TRUE; + hr = S_OK; + break; + + case DISPID_AMBIENT_SHOWHATCHING: + case DISPID_AMBIENT_SHOWGRABHANDLES: + case DISPID_AMBIENT_SUPPORTSMNEMONICS: + pvarResult->vt = VT_BOOL; + pvarResult->boolVal = FALSE; + hr = S_OK; + break; + + // Not yet implemented! + case DISPID_AMBIENT_BACKCOLOR: + case DISPID_AMBIENT_FORECOLOR: + case DISPID_AMBIENT_UIDEAD: + case DISPID_AMBIENT_AUTOCLIP: + hr = S_OK; + break; + + // Extender Properties + case DISPID_BASEHREF: + case DISPID_ALIGN: + hr = DISP_E_MEMBERNOTFOUND; + break; + + default: + hr = DISP_E_MEMBERNOTFOUND; + } + + return hr; +} + diff --git a/stella/src/ui/win32/ControlHost.hxx b/stella/src/ui/win32/ControlHost.hxx new file mode 100644 index 000000000..7e005e4d6 --- /dev/null +++ b/stella/src/ui/win32/ControlHost.hxx @@ -0,0 +1,246 @@ +// +// StellaX +// Jeff Miller 05/02/2000 +// + +#ifndef _CONTROLHOST_H_ +#define _CONTROLHOST_H_ + +// +// This code was based from the MSDN DRAGGEOCNTRL sample +// NOTE: PDF control requires UI Deactivate/UI Activate to resize +// + +#include + +#define BASE_EXTENDED_PROPERTY 0x80010000 +#define DISPID_NAME (BASE_EXTENDED_PROPERTY | 0x00) +#define DISPID_ALIGN (BASE_EXTENDED_PROPERTY | 0x01) +#define DISPID_BASEHREF (BASE_EXTENDED_PROPERTY | 0x02) + +#define IMPLEMENT_IOLECONTROLSITE +#define IMPLEMENT_IOLEINPLACESITEWINDOWLESS + +class CActiveXControl +{ +public: + + CActiveXControl(); + virtual ~CActiveXControl(); + + HRESULT CreateInstance( void ); + HRESULT GetDispInterface( IDispatch** piDispatch ); + IDispatch* GetDispInterfaceNoAddRef( void ); + + virtual REFCLSID GetCLSID( void ) = 0; + virtual void OnInitialUpdate( void ); + +protected: + + // Dispatch helpers + + HRESULT PutPropertyByName( LPCOLESTR lpsz, VARIANT* pVar ); + HRESULT GetIDOfName( LPCOLESTR lpsz, DISPID* pdispid ); + + static HRESULT GetProperty( IDispatch* pDisp, DISPID dwDispID, + VARIANT* pVar ); + static HRESULT PutProperty( IDispatch* pDisp, DISPID dwDispID, + VARIANT* pVar ); + + // Invoke a method by DISPID with a single parameter + + HRESULT Invoke1( DISPID dispid, VARIANT* pvarParam1, + VARIANT* pvarRet = NULL ); + + // Invoke a method by name with a single parameter + + HRESULT Invoke1( LPCOLESTR lpszName, VARIANT* pvarParam1, + VARIANT* pvarRet = NULL ); + +private: + + IDispatch* m_piDispatch; + + CActiveXControl( const CActiveXControl& ); // no implementation + void operator=( const CActiveXControl& ); // no implementation + +}; + +inline HRESULT CActiveXControl::GetDispInterface( + IDispatch** piDispatch + ) +{ + if (piDispatch == NULL) + { + return E_INVALIDARG; + } + + m_piDispatch->AddRef(); + *piDispatch = m_piDispatch; + + return S_OK; +} + +inline IDispatch* CActiveXControl::GetDispInterfaceNoAddRef( + void + ) +{ + return m_piDispatch; +} + +// --------------------------------------------------------------------------- + +class CControlHost : \ + public IDispatch, +#ifdef IMPLEMENT_IOLEINPLACESITEWINDOWLESS + public IOleInPlaceSiteWindowless, +#endif + public IOleInPlaceFrame, + public IOleControlSite, + public IOleClientSite +{ +public: + + CControlHost( CActiveXControl* pControl ); + ~CControlHost( ); + + HRESULT SetHwnd( HWND hwnd ); + HRESULT CreateControl( void ); + HRESULT DeleteControl( void ); + HRESULT QueryObject( REFIID riid, void **ppvObject ); + + HWND GetControlHWND( void ) const + { + return _hwndControl; + } + +protected: + + HWND _hwnd; // container window handle + HWND _hwndControl; // Control's window handle + UINT _cRef; // IUnknown ref count + BOOL _bCapture; // mouse capture flag + RECT _rcPos; + + void* _punkOuter; // parent container + + CActiveXControl* m_pControl; + +public: + + // *** IUnknown Methods *** + + STDMETHODIMP QueryInterface( REFIID riid, LPVOID * ppvObj ); + STDMETHODIMP_(ULONG) AddRef( void ); + STDMETHODIMP_(ULONG) Release( void ); + + // *** IDispatch Methods *** + + STDMETHOD (GetIDsOfNames)( REFIID riid, OLECHAR FAR* FAR* rgszNames, + unsigned int cNames, LCID lcid, + DISPID FAR* rgdispid ); + STDMETHOD (GetTypeInfo)( unsigned int itinfo, LCID lcid, + ITypeInfo FAR* FAR* pptinfo ); + STDMETHOD (GetTypeInfoCount)( unsigned int FAR * pctinfo ); + STDMETHOD (Invoke)( DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, + EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr ); + + // *** IOleClientSite methods *** + + STDMETHOD (SaveObject)( void ); + STDMETHOD (GetMoniker)( DWORD, DWORD, LPMONIKER * ); + STDMETHOD (GetContainer)( LPOLECONTAINER * ); + STDMETHOD (ShowObject)( void ); + STDMETHOD (OnShowWindow)( BOOL ); + STDMETHOD (RequestNewObjectLayout)( void ); + + // *** IOleWindow Methods *** + + STDMETHOD (GetWindow)( HWND * phwnd ); + STDMETHOD (ContextSensitiveHelp)( BOOL fEnterMode ); + + // *** IOleInPlaceSite Methods *** + + STDMETHOD (CanInPlaceActivate)( void ); + STDMETHOD (OnInPlaceActivate)( void ); + STDMETHOD (OnUIActivate)( void ); + STDMETHOD (GetWindowContext)( IOleInPlaceFrame ** ppFrame, + IOleInPlaceUIWindow ** ppDoc, + LPRECT lprcPosRect, LPRECT lprcClipRect, + LPOLEINPLACEFRAMEINFO lpFrameInfo ); + STDMETHOD (Scroll)( SIZE scrollExtent ); + STDMETHOD (OnUIDeactivate)( BOOL fUndoable ); + STDMETHOD (OnInPlaceDeactivate)( void ); + STDMETHOD (DiscardUndoState)( void ); + STDMETHOD (DeactivateAndUndo)( void ); + STDMETHOD (OnPosRectChange)( LPCRECT lprcPosRect ); + +#ifdef IMPLEMENT_IOLEINPLACESITEWINDOWLESS + + // *** IOleInPlaceSiteEx Methods *** + + STDMETHOD (OnInPlaceActivateEx)( BOOL *pfNoRedraw, DWORD dwFlags ); + STDMETHOD (OnInPlaceDeactivateEx)( BOOL fNoRedraw ); + STDMETHOD (RequestUIActivate)( void ); + + // *** IOleInPlaceSiteWindowless Methods *** + + STDMETHOD (CanWindowlessActivate)( void ); + STDMETHOD (GetCapture)( void ); + STDMETHOD (SetCapture)( BOOL fCapture ); + STDMETHOD (GetFocus)( void ); + STDMETHOD (SetFocus)( BOOL fFocus ); + STDMETHOD (GetDC)( LPCRECT pRect, DWORD grfFlags, HDC *phDC ); + STDMETHOD (ReleaseDC)( HDC hDC ); + STDMETHOD (InvalidateRect)( LPCRECT pRect, BOOL fErase ); + STDMETHOD (InvalidateRgn)( HRGN hRGN, BOOL fErase ); + STDMETHOD (ScrollRect)( INT dx, INT dy, LPCRECT pRectScroll, + LPCRECT pRectClip ); + STDMETHOD (AdjustRect)( LPRECT prc ); + STDMETHOD (OnDefWindowMessage)( UINT msg, WPARAM wParam, LPARAM lParam, + LRESULT *plResult ); + +#endif + + // *** IOleInPlaceUIWindow Methods *** + + STDMETHOD (GetBorder)(LPRECT lprectBorder); + STDMETHOD (RequestBorderSpace)(LPCBORDERWIDTHS lpborderwidths); + STDMETHOD (SetBorderSpace)(LPCBORDERWIDTHS lpborderwidths); + STDMETHOD (SetActiveObject)(IOleInPlaceActiveObject * pActiveObject, + LPCOLESTR lpszObjName); + + // *** IOleInPlaceFrame Methods *** + + STDMETHOD (InsertMenus)(HMENU hmenuShared, + LPOLEMENUGROUPWIDTHS lpMenuWidths); + STDMETHOD (SetMenu)(HMENU hmenuShared, HOLEMENU holemenu, + HWND hwndActiveObject); + STDMETHOD (RemoveMenus)(HMENU hmenuShared); + STDMETHOD (SetStatusText)(LPCOLESTR pszStatusText); + STDMETHOD (EnableModeless)(BOOL fEnable); + STDMETHOD (TranslateAccelerator)(LPMSG lpmsg, WORD wID); + +#ifdef IMPLEMENT_IOLECONTROLSITE + + // *** IOleControlSite Methods *** + + STDMETHOD (OnControlInfoChanged)(void); + STDMETHOD (LockInPlaceActive)(BOOL fLock); + STDMETHOD (GetExtendedControl)(IDispatch **ppDisp); + STDMETHOD (TransformCoords)(POINTL *pptlHimetric, POINTF *pptfContainer, + DWORD dwFlags); + STDMETHOD (TranslateAccelerator)(LPMSG pMsg, DWORD grfModifiers); + STDMETHOD (OnFocus)(BOOL fGotFocus); + STDMETHOD (ShowPropertyFrame)(void); + +#endif + +private: + + CControlHost( const CControlHost& ); // no implementation + void operator=( const CControlHost& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/CoolCaption.cxx b/stella/src/ui/win32/CoolCaption.cxx new file mode 100644 index 000000000..f619590f0 --- /dev/null +++ b/stella/src/ui/win32/CoolCaption.cxx @@ -0,0 +1,284 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// + +#include "pch.hxx" +#include "CoolCaption.hxx" + +// MAKE SURE STYLE IS JUST "TITLE BAR" ! + +CCoolCaption::CCoolCaption( + ) : \ + m_hfont( NULL ), + m_tszCaption( NULL ), + m_fIsActive( FALSE ), + m_hDlg( NULL ) +{ +} + +void CCoolCaption::OnInitDialog( + HWND hDlg + ) +{ + m_hDlg = hDlg; + + CalculateNCArea(); +} + +void CCoolCaption::OnDestroy( + void + ) +{ + if ( m_hDlg == NULL ) + { + return; + } + + if ( m_hfont ) + { + ::DeleteObject( m_hfont ); + } + + delete[] m_tszCaption; + m_tszCaption = NULL; +} + +void CCoolCaption::CalculateNCArea( + ) +{ + if ( m_hDlg == NULL ) + { + return; + } + + ::GetWindowRect(m_hDlg, &m_rcWindow); + m_cxWindow = (m_rcWindow.right - m_rcWindow.left); + m_cyWindow = (m_rcWindow.bottom - m_rcWindow.top); + + m_cxFrame = ::GetSystemMetrics(SM_CXFRAME); + m_cyFrame = ::GetSystemMetrics(SM_CYFRAME); + + m_cxButtonSize = ::GetSystemMetrics(SM_CXSIZE); + + const int cyCaption = ::GetSystemMetrics(SM_CYCAPTION); + + // This is the size of the caption + + ::SetRect(&m_rcCaption, 0, 0, m_cxWindow, cyCaption + m_cyFrame); + + // calculate position of buttons + + SetRect(&m_rcClose, + m_cxWindow - m_cxFrame - m_cxButtonSize + 1, + m_rcCaption.top + m_cyFrame + 1, + m_cxWindow - m_cxFrame - 1, + m_rcCaption.bottom - m_cyFrame + ); + + CopyRect(&m_rcMin, &m_rcClose); + OffsetRect(&m_rcMin, -(m_cxButtonSize-2), 0); + + // figure out union of all buttons + + RECT rcButtons; + UnionRect(&rcButtons, &m_rcClose, &m_rcMin); + + SetRect(&m_rcTextArea, m_cxFrame, m_rcCaption.top+m_cyFrame, + rcButtons.left-m_cxFrame, m_rcCaption.bottom-m_cyFrame); + + // Make bold caption font + + m_hfont = (HFONT)SendMessage(m_hDlg, WM_GETFONT, 0, 0); + LOGFONT lf; + GetObject(m_hfont, sizeof(lf), &lf); + lf.lfWeight = FW_BOLD; + m_hfont = CreateFontIndirect(&lf); + + // Get caption text + + int nCaptionLength = GetWindowTextLength(m_hDlg); + m_tszCaption = new TCHAR[nCaptionLength + 2]; + GetWindowText(m_hDlg, m_tszCaption, nCaptionLength+1); +} + + +void CCoolCaption::OnNcPaint( + HRGN hrgn + ) +{ + UNUSED_ALWAYS( hrgn ); + + if ( m_hDlg == NULL ) + { + return; + } + + HDC hdc = GetWindowDC(m_hDlg); + + // Fill in the NC area with the proper color + + HPEN hpen, hpenOld; + + hpen = CreatePen(PS_SOLID, 1, m_fIsActive ? + GetSysColor(COLOR_ACTIVECAPTION) : \ + GetSysColor(COLOR_INACTIVECAPTION)); + hpenOld = (HPEN)SelectObject(hdc, hpen); + + for (long x = 0; x < m_cxWindow; ++x) + { + if (x < m_cxFrame-1 || x > m_cxWindow-m_cxFrame) + { + // left, right must go from top to bottom + + MoveToEx(hdc, x, 0, NULL); + LineTo(hdc, x, m_rcWindow.bottom); + } + else + { + // otherwise just do top and bottom + + MoveToEx(hdc, x, 0, NULL); + LineTo(hdc, x, m_rcCaption.bottom); + + MoveToEx(hdc, x, m_cyWindow-m_cyFrame, NULL); + LineTo(hdc, x, m_cyWindow); + } + } + + SelectObject(hdc, hpenOld); + DeleteObject(hpen); + + // Draw the close button + + DrawFrameControl(hdc, &m_rcClose, DFC_CAPTION, DFCS_CAPTIONCLOSE); + + // draw the minimize button + + DrawFrameControl(hdc, &m_rcMin, DFC_CAPTION, DFCS_CAPTIONMIN); + + // Draw the caption text + + SetTextColor(hdc, m_fIsActive ? GetSysColor(COLOR_CAPTIONTEXT) : \ + GetSysColor(COLOR_INACTIVECAPTIONTEXT)); + SetBkMode(hdc, TRANSPARENT); + + HFONT hfontOld = (HFONT)SelectObject(hdc, m_hfont); + + // calculate width / height of text + + SIZE sizeText; + GetTextExtentPoint(hdc, m_tszCaption, lstrlen(m_tszCaption), &sizeText); + + DrawText(hdc, m_tszCaption, -1, &m_rcTextArea, + DT_END_ELLIPSIS | DT_EXTERNALLEADING | + DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER); + + SelectObject(hdc, hfontOld); + + // Draw the docking grippers + + const int nTextRight = m_rcTextArea.left + sizeText.cx + m_cxFrame; + + // right top + + COLORREF cr3dHilight = GetSysColor(COLOR_3DHILIGHT); + COLORREF cr3dShadow = GetSysColor(COLOR_3DSHADOW); + + Draw3dRect(hdc, nTextRight+m_cxFrame, 8, + m_rcTextArea.right-(nTextRight+m_cxFrame), 3, + cr3dHilight, cr3dShadow); + + // right bottom + + Draw3dRect(hdc, nTextRight+m_cxFrame, 12, + m_rcTextArea.right-(nTextRight+m_cxFrame), 3, + cr3dHilight, cr3dShadow); + + ReleaseDC(m_hDlg, hdc); +} + +void CCoolCaption::OnNcActivate( + BOOL fActive + ) +{ + if (m_hDlg == NULL) + return; + + m_fIsActive = fActive; + + // This fixes a problem where alt-tab wont repaint the NC area + + RedrawWindow(m_hDlg, NULL, NULL, + RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_ERASE); +} + +BOOL CCoolCaption::OnNCLButtonDown( + INT nHitTest, + POINTS pts + ) +{ + UNUSED_ALWAYS( nHitTest ); + + if ( m_hDlg == NULL ) + { + return FALSE; + } + + // Get the point in client units + + RECT rcWindow; + GetWindowRect(m_hDlg, &rcWindow); + + POINT pt = { pts.x, pts.y }; + pt.x -= rcWindow.left; + pt.y -= rcWindow.top; + + if (PtInRect(&m_rcClose, pt)) + { + // This isn't closing it! + // SendMessage(m_hDlg, WM_CLOSE, 0, 0); + EndDialog(m_hDlg, IDCANCEL); + return TRUE; + } + + if (PtInRect(&m_rcMin, pt)) + { + ShowWindow(m_hDlg, SW_MINIMIZE); + return TRUE; + } + + return FALSE; +} + +void CCoolCaption::FillSolidRect( + HDC hdc, + int x, + int y, + int cx, + int cy, + COLORREF clr + ) +{ + COLORREF cr = SetBkColor(hdc, clr); + RECT rect = { x, y, x+cx, y+cy }; + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + SetBkColor(hdc, cr); +} + +void CCoolCaption::Draw3dRect( + HDC hdc, + int x, + int y, + int cx, + int cy, + COLORREF clrTopLeft, + COLORREF clrBottomRight + ) +{ + FillSolidRect(hdc, x, y, cx - 1, 1, clrTopLeft); + FillSolidRect(hdc, x, y, 1, cy - 1, clrTopLeft); + FillSolidRect(hdc, x + cx, y, -1, cy, clrBottomRight); + FillSolidRect(hdc, x, y + cy, cx, -1, clrBottomRight); +} + diff --git a/stella/src/ui/win32/CoolCaption.hxx b/stella/src/ui/win32/CoolCaption.hxx new file mode 100644 index 000000000..c10d78eb4 --- /dev/null +++ b/stella/src/ui/win32/CoolCaption.hxx @@ -0,0 +1,51 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// + +#ifndef COOLCAP_H +#define COOLCAP_H +#pragma once + +class CCoolCaption +{ +public: + CCoolCaption(); + + void OnInitDialog(HWND hDlg); + void OnDestroy(); + + void OnNcPaint(HRGN); + void OnNcActivate(BOOL); + BOOL OnNCLButtonDown(INT, POINTS); + +protected: + + HWND m_hDlg; + +private: + + void CalculateNCArea(void); + void FillSolidRect(HDC hdc, int x, int y, int cx, int cy, COLORREF clr); + void Draw3dRect(HDC hdc, int x, int y, int cx, int cy, + COLORREF clrTopLeft, COLORREF clrBottomRight); + + BOOL m_fIsActive; + RECT m_rcWindow; + int m_cxWindow; + int m_cyWindow; + int m_cxFrame; + int m_cyFrame; + int m_cxButtonSize; + RECT m_rcClose; + RECT m_rcMin; + RECT m_rcCaption; + RECT m_rcTextArea; + HFONT m_hfont; + LPTSTR m_tszCaption; + + CCoolCaption( const CCoolCaption& ); // no implementation + void operator=( const CCoolCaption& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/DirectDraw.cxx b/stella/src/ui/win32/DirectDraw.cxx new file mode 100644 index 000000000..12ae115e5 --- /dev/null +++ b/stella/src/ui/win32/DirectDraw.cxx @@ -0,0 +1,470 @@ +// +// StellaX +// Jeff Miller 05/10/2000 +// + +#include "pch.hxx" +#include "DirectDraw.hxx" +#include "resource.h" + +CDirectDraw::CDirectDraw( + const SIZE& sizeSurface + ) :\ + m_fInitialized( FALSE ), + m_piDD(NULL), + m_piDDSurface(NULL), + m_piDDSurfaceBack(NULL), + m_piDDPalette(NULL) +{ + m_sizeScreen.cx = 0; + m_sizeScreen.cy = 0; + + m_sizeSurface.cx = sizeSurface.cx; + m_sizeSurface.cy = sizeSurface.cy; + + // TRACE( "m_sizeSurface = %d x %d", m_sizeSurface.cx, m_sizeSurface.cy ); +} + +CDirectDraw::~CDirectDraw( + ) +{ + TRACE("CDirectDraw::~CDirectDraw"); + + Cleanup(); +} + +HRESULT CDirectDraw::Initialize( + HWND hwnd, + const ULONG* pulPalette, + int cx /* = 0 */, + int cy /* = 0 */ + ) +{ + HRESULT hr = S_OK; + UINT uMsg = 0; // Message to show if FAILED(hr) + + m_hwnd = hwnd; + + hr = ::CoCreateInstance( CLSID_DirectDraw, + NULL, + CLSCTX_SERVER, + IID_IDirectDraw, + (void**)&m_piDD ); + if ( FAILED(hr) ) + { + TRACE( "CCI on DirectDraw failed, hr=%x", hr ); + uMsg = IDS_NODIRECTDRAW; + goto cleanup; + } + + // + // Initialize it + // This method takes the driver GUID parameter that the DirectDrawCreate + // function typically uses (NULL is active display driver) + // + + hr = m_piDD->Initialize( NULL ); + if ( FAILED(hr) ) + { + TRACE( "DDraw::Initialize failed, hr=%x", hr ); + uMsg = IDS_DD_INIT_FAILED; + goto cleanup; + } + + + // + // Get the best video mode for game width + // + + m_sizeScreen.cx = cx; + m_sizeScreen.cy = cy; + + if ( cx == 0 || cy == 0 ) + { + hr = m_piDD->EnumDisplayModes( 0, NULL, this, EnumModesCallback ); + if ( FAILED(hr) ) + { + TRACE( "EnumDisplayModes failed" ); + uMsg = IDS_DD_ENUMMODES_FAILED; + goto cleanup; + } + } + + if (m_sizeScreen.cx == 0 || m_sizeScreen.cy == 0) + { + TRACE("No good video mode found"); + uMsg = IDS_NO_VID_MODE; + hr = E_INVALIDARG; + goto cleanup; + } + + TRACE("Video Mode Selected: %d x %d", m_sizeScreen.cx, m_sizeScreen.cy); + + // compute blit offset to center image + +#ifdef DOUBLE_WIDTH + m_ptBlitOffset.x = ((m_sizeScreen.cx - m_sizeSurface.cx*2) / 2); +#else + m_ptBlitOffset.x = ((m_sizeScreen.cx - m_sizeSurface.cx) / 2); +#endif + + m_ptBlitOffset.y = ((m_sizeScreen.cy - m_sizeSurface.cy) / 2); + + TRACE("Game dimensions: %dx%d (blit offset = %d, %d)", + m_sizeSurface.cx, m_sizeSurface.cy, m_ptBlitOffset.x, m_ptBlitOffset.y); + + // Set cooperative level + + hr = m_piDD->SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ); + if ( FAILED(hr) ) + { + TRACE( "IDD:SetCooperativeLevel failed, hr=%x", hr ); + uMsg = IDS_DDSCL_FAILED; + goto cleanup; + } + + hr = m_piDD->SetDisplayMode( m_sizeScreen.cx, m_sizeScreen.cy, 8 ); + if ( FAILED(hr) ) + { + TRACE( "IDD:SetDisplayMode failed, hr=%x", hr ); + uMsg = IDS_DDSDM_FAILED; + goto cleanup; + } + + hr = CreateSurfacesAndPalette(); + if ( FAILED(hr) ) + { + TRACE( "CreateSurfacesAndPalette failed, hr=%X", hr ); + uMsg = IDS_DDCS_FAILED; + goto cleanup; + } + + hr = InitPalette( pulPalette ); + if ( FAILED(hr) ) + { + TRACE( "InitPalette failed, hr=%X", hr ); + uMsg = IDS_DDCP_FAILED; + goto cleanup; + } + + m_fInitialized = TRUE; + +cleanup: + + if ( FAILED(hr) ) + { + Cleanup(); + + if ( uMsg != 0 ) + { + MessageBox( (HINSTANCE)::GetWindowLong( hwnd, GWL_HINSTANCE ), + hwnd, uMsg ); + } + } + + return hr; +} + +void CDirectDraw::Cleanup( + void + ) +{ + // release all of the objects + + if (m_piDDSurfaceBack) + { + m_piDDSurfaceBack->Release(); + m_piDDSurfaceBack = NULL; + } + + if (m_piDDSurface) + { + m_piDDSurface->Release(); + m_piDDSurface = NULL; + } + + if (m_piDDPalette) + { + m_piDDPalette->Release(); + m_piDDPalette = NULL; + } + + if (m_piDD) + { + m_piDD->Release(); + m_piDD = NULL; + } + + m_fInitialized = FALSE; +} + + +HRESULT WINAPI CDirectDraw::EnumModesCallback( + LPDDSURFACEDESC lpDDSurfaceDesc, + LPVOID lpContext + ) +{ + CDirectDraw* pThis = (CDirectDraw*)lpContext; + + DWORD dwWidthReq = pThis->m_sizeSurface.cx; +#ifdef DOUBLE_WIDTH + dwWidthReq *= 2; +#endif + DWORD dwHeightReq = pThis->m_sizeSurface.cy; + + DWORD dwWidth = lpDDSurfaceDesc->dwWidth; + DWORD dwHeight = lpDDSurfaceDesc->dwHeight; + DWORD dwRGBBitCount = lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount; + + // must be 8 bit mode + + if (dwRGBBitCount != 8) + { + return DDENUMRET_OK; + } + + // TRACE( "EnumModesCallback found: %ld x %ld x %ld", dwWidth, dwHeight, dwRGBBitCount ); + + // must be larger then required screen size + + if ( dwWidth < dwWidthReq || dwHeight < dwHeightReq ) + { + return DDENUMRET_OK; + } + + if ( pThis->m_sizeScreen.cx != 0 && pThis->m_sizeScreen.cy != 0 ) + { + // check to see if this is better than the previous choice + + if ( (dwWidth - dwWidthReq) > (pThis->m_sizeScreen.cx - dwWidthReq) ) + { + return DDENUMRET_OK; + } + + if ( (dwHeight - dwHeightReq) > (pThis->m_sizeScreen.cy - dwHeightReq) ) + { + return DDENUMRET_OK; + } + } + + + // use it! + + pThis->m_sizeScreen.cx = dwWidth; + pThis->m_sizeScreen.cy = dwHeight; + // TRACE( "\tEnumModesCallback likes this mode!" ); + + return DDENUMRET_OK; +} + + +HRESULT CDirectDraw::CreateSurfacesAndPalette( + void + ) +{ + TRACE("CDirectDraw::CreateSurfacesAndPalette"); + + HRESULT hr; + DDSURFACEDESC ddsd; + HDC hdc; + + // Create the primary surface + + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + hr = m_piDD->CreateSurface(&ddsd, &m_piDDSurface, NULL); + if (FAILED(hr)) + { + TRACE( "CreateSurface failed, hr=%X", hr ); + return hr; + } + + hr = m_piDDSurface->GetDC(&hdc); + if (hr == DD_OK) + { + ::SetBkColor(hdc, RGB(0, 0, 0)); + RECT rc = { 0, 0, m_sizeScreen.cx, m_sizeScreen.cy }; + + ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); + + m_piDDSurface->ReleaseDC(hdc); + } + + // Create the offscreen surface + + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; +#ifdef DOUBLE_WIDTH + ddsd.dwWidth = m_sizeSurface.cx * 2; +#else + ddsd.dwWidth = m_sizeSurface.cx; +#endif + + ddsd.dwHeight = m_sizeSurface.cy; + hr = m_piDD->CreateSurface(&ddsd, &m_piDDSurfaceBack, NULL); + if (FAILED(hr)) + { + TRACE( "CreateSurface failed, hr=%x", hr ); + return hr; + } + + // Erase the surface + + hr = m_piDDSurfaceBack->GetDC(&hdc); + if (hr == DD_OK) + { + ::SetBkColor(hdc, RGB(0, 0, 0)); +#ifdef DOUBLE_WIDTH + m_sizeScreen.cx = m_sizeSurface.cx * 2; + m_sizeScreen.cy = m_sizeSurface.cy; +#else + m_sizeScreen.cx = m_sizeSurface.cx; + m_sizeScreen.cy = m_sizeSurface.cy; +#endif + RECT rc = { 0, 0, m_sizeScreen.cx, m_sizeScreen.cy }; + + ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL); + + m_piDDSurfaceBack->ReleaseDC(hdc); + } + + return S_OK; +} + +HRESULT CDirectDraw::InitPalette( + const ULONG* pulPalette + ) +{ + TRACE("CDirectDraw::InitPalette"); + + // Create the palette and attach it to the primary surface + + PALETTEENTRY pe[256]; + + int i; + for ( i = 0; i < 256; ++i ) + { + pe[i].peRed = (BYTE)( (pulPalette[i] & 0x00FF0000) >> 16 ); + pe[i].peGreen = (BYTE)( (pulPalette[i] & 0x0000FF00) >> 8 ); + pe[i].peBlue = (BYTE)( (pulPalette[i] & 0x000000FF) ); + pe[i].peFlags = 0; + } + + HRESULT hr = S_OK; + + hr = m_piDD->CreatePalette( DDPCAPS_8BIT, + pe, + &m_piDDPalette, + NULL ); + if ( FAILED(hr) ) + { + TRACE( "IDD::CreatePalette failed, hr=%X", hr); + goto cleanup; + } + + hr = m_piDDSurface->SetPalette( m_piDDPalette ); + if ( FAILED(hr) ) + { + TRACE( "SetPalette failed, hr=%x", hr ); + goto cleanup; + } + +cleanup: + + if ( FAILED(hr) ) + { + if (m_piDDPalette) + { + m_piDDPalette->Release(); + m_piDDPalette = NULL; + } + } + + return hr; +} + +HRESULT CDirectDraw::Lock( + BYTE** ppSurface, + LONG* plPitch + ) +{ + if (ppSurface == NULL || plPitch == NULL) + { + return E_INVALIDARG; + } + + HRESULT hr; + + DDSURFACEDESC ddsd; + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + + hr = m_piDDSurfaceBack->Lock( + NULL, + &ddsd, + DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, + NULL ); + if (hr != S_OK) + { + TRACE("Lock failed on back surface"); + return hr; + } + + *ppSurface = (BYTE*)ddsd.lpSurface; + *plPitch = ddsd.lPitch; + + return S_OK; +} + +HRESULT CDirectDraw::Unlock( + BYTE* pSurface + ) +{ + if (pSurface == NULL) + { + return E_INVALIDARG; + } + + return m_piDDSurfaceBack->Unlock( pSurface ); +} + + +HRESULT CDirectDraw::BltFast( + const RECT* prc + ) +{ + HRESULT hr; + + for ( ; ; ) + { + hr = m_piDDSurface->BltFast( + m_ptBlitOffset.x, + m_ptBlitOffset.y, + m_piDDSurfaceBack, + const_cast( prc ), + DDBLTFAST_NOCOLORKEY ); + if (hr == DD_OK) + { + break; + } + + if (hr == DDERR_SURFACELOST) + { + m_piDDSurface->Restore(); + m_piDDSurfaceBack->Restore(); + } + else if (hr != DDERR_WASSTILLDRAWING) + { + // FATAL ERROR + TRACE("IDDS:BltFast failed, hr = %08X", hr); + return E_FAIL; + } + } + + return S_OK; +} diff --git a/stella/src/ui/win32/DirectDraw.hxx b/stella/src/ui/win32/DirectDraw.hxx new file mode 100644 index 000000000..e16fbc923 --- /dev/null +++ b/stella/src/ui/win32/DirectDraw.hxx @@ -0,0 +1,53 @@ +// +// StellaX +// Jeff Miller 05/06/2000 +// + +#ifndef DIRECTDR_H +#define DIRECTDR_H +#pragma once + +class CDirectDraw +{ +public: + + CDirectDraw( const SIZE& sizeSurface ); + ~CDirectDraw(); + + HRESULT Initialize( HWND hwnd, const ULONG* pulPalette, + int cx = 0, int cy = 0 ); + + HRESULT Lock( BYTE** ppSurface, LONG* lPitch ); + HRESULT Unlock( BYTE* pSurface ); + HRESULT BltFast( const RECT* prc ); + +private: + + HRESULT CreateSurfacesAndPalette( void ); + + // param should really be a const BYTE* + HRESULT InitPalette( const ULONG* pulPalette ); + + void Cleanup( void ); + static HRESULT WINAPI EnumModesCallback( LPDDSURFACEDESC lpDDSurfaceDesc, + LPVOID lpContext); + + BOOL m_fInitialized; + + IDirectDraw* m_piDD; + IDirectDrawSurface* m_piDDSurface; + IDirectDrawSurface* m_piDDSurfaceBack; + IDirectDrawPalette* m_piDDPalette; + + SIZE m_sizeScreen; + SIZE m_sizeSurface; // blit surface size + POINT m_ptBlitOffset; + + HWND m_hwnd; + + CDirectDraw( const CDirectDraw& ); // no implementation + void operator=( const CDirectDraw& ); // no implementation + +}; + +#endif diff --git a/stella/src/ui/win32/DirectInput.cxx b/stella/src/ui/win32/DirectInput.cxx new file mode 100644 index 000000000..fd8354e5a --- /dev/null +++ b/stella/src/ui/win32/DirectInput.cxx @@ -0,0 +1,536 @@ +// +// StellaX +// Jeff Miller 05/13/2000 +// + +#define DIRECTINPUT_VERSION 0x700 + +#include "pch.hxx" +#include "DirectInput.hxx" +#include "resource.h" + +// +// CDirectInput +// + +CDirectInput::CDirectInput( + HWND hwnd, + DWORD dwDevType, + int nButtonCount + ) : \ + m_hwnd( hwnd ), + m_piDID(NULL), + m_dwDevType(dwDevType), + m_nButtonCount(nButtonCount), + m_pButtons(NULL), + m_lX(0), + m_lY(0), + m_fInitialized( FALSE ) +{ + TRACE("CDirectInput::CDirectInput"); +} + +CDirectInput::~CDirectInput( + ) +{ + TRACE("CDirectInput::~CDirectInput"); + + Cleanup(); +} + +HRESULT CDirectInput::Initialize( + void + ) +{ + TRACE("CDirectInput::Initialize"); + + HINSTANCE hInstance = (HINSTANCE)::GetWindowLong( m_hwnd, GWL_HINSTANCE ); + + if ( m_fInitialized ) + { + return S_OK; + } + + if ( m_hwnd == NULL ) + { + // This is for CDisabledJoystick + + return S_OK; + } + + HRESULT hr = S_OK; + UINT uMsg = 0; // if ( FAILED(hr) ) + + hr = ::CoCreateInstance( CLSID_DirectInput, + NULL, + CLSCTX_SERVER, + IID_IDirectInput, + (void**)&m_piDI ); + if ( FAILED(hr) ) + { + TRACE( "WARNING: CCI on DirectInput failed, error=%X", hr ); + + // + // Note -- I don't fail here so that machines with NT4 (which doesn't + // have DirectX 5.0) don't fail + // + // For this to work, Update() must begin with + // if (GetDevice() == NULL) { return E_FAIL; } + // + + // uMsg = IDS_NODIRECTINPUT; + hr = S_FALSE; + goto cleanup; + } + + // + // Initialize it + // + + hr = m_piDI->Initialize( hInstance, DIRECTINPUT_VERSION ); + if ( FAILED(hr) ) + { + TRACE("IDI::Initialize failed"); + uMsg = IDS_DI_INIT_FAILED; + goto cleanup; + } + + // + // enumerate to find proper device + // The callback will set m_piDID + // + + TRACE("\tCalling EnumDevices"); + + hr = m_piDI->EnumDevices( m_dwDevType, + EnumDevicesProc, + this, + DIEDFL_ATTACHEDONLY ); + if ( m_piDID ) + { + TRACE("\tGot a device!"); + + (void)m_piDID->SetCooperativeLevel( m_hwnd, + DISCL_NONEXCLUSIVE + | DISCL_FOREGROUND); + + hr = GetDevice()->Acquire(); + if ( hr == DIERR_OTHERAPPHASPRIO ) + { + return S_FALSE; + } + } + + m_pButtons = new BYTE[GetButtonCount()]; + if ( m_pButtons == NULL ) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + m_fInitialized = TRUE; + +cleanup: + + if ( FAILED(hr) ) + { + Cleanup(); + + if ( uMsg != 0 ) + { + MessageBox( hInstance, m_hwnd, uMsg ); + } + } + + return hr; +} + +void CDirectInput::Cleanup( + void + ) +{ + TRACE("CDirectInput::Cleanup"); + + delete[] m_pButtons; + + if (m_piDID) + { + m_piDID->Unacquire(); + m_piDID->Release(); + m_piDID = NULL; + } + + if (m_piDI) + { + m_piDI->Release(); + m_piDI = NULL; + } + + m_fInitialized = FALSE; +} + +BOOL CALLBACK CDirectInput::EnumDevicesProc +( + const DIDEVICEINSTANCE* lpddi, + LPVOID pvRef +) +{ + CDirectInput* pThis = (CDirectInput*)pvRef; + ASSERT(pThis); + + const DIDATAFORMAT* pdidf = NULL; + + switch(pThis->m_dwDevType) + { + case DIDEVTYPE_MOUSE: + TRACE("EnumDevicesProc (mouse)"); + pdidf = &c_dfDIMouse; + break; + + case DIDEVTYPE_KEYBOARD: + TRACE("EnumDevicesProc (keyboard)"); + pdidf = &c_dfDIKeyboard; + break; + + case DIDEVTYPE_JOYSTICK: + TRACE("EnumDevicesProc (joystick)"); + pdidf = &c_dfDIJoystick; + break; + + default: + ASSERT(FALSE); + return DIENUM_STOP; + }; + + HRESULT hr; + + IDirectInputDevice* piDID; + hr = pThis->m_piDI->CreateDevice(lpddi->guidInstance, &piDID, + NULL); + ASSERT(hr == DI_OK && "IDI::CreateDevice failed"); + if (hr != DI_OK) + { + return DIENUM_CONTINUE; + } + + hr = piDID->SetDataFormat(pdidf); + ASSERT(hr == DI_OK && "IDID::SetDataFormat failed"); + if (hr != DI_OK) + { + piDID->Release(); + return DIENUM_CONTINUE; + } + + hr = piDID->QueryInterface(IID_IDirectInputDevice2, + (void**)&(pThis->m_piDID)); + if (hr != S_OK) + { + piDID->Release(); + return DIENUM_CONTINUE; + } + + // undo the addref that QI did (CreateDevice did an addref) + + pThis->m_piDID->Release(); + +#ifdef _DEBUG + DIDEVICEINSTANCE didi; + didi.dwSize = sizeof(didi); + piDID->GetDeviceInfo(&didi); + TRACE("Using device: %s", didi.tszProductName); +#endif + + return DIENUM_STOP; +} + +BOOL CDirectInput::IsButtonPressed +( + int nButton +) const +{ + if ( nButton > GetButtonCount() ) + { + return FALSE; + } + + return ( m_pButtons[nButton] ) ? 1 : 0; +} + + +// --------------------------------------------------------------------------- + +CDirectKeyboard::CDirectKeyboard( + HWND hwnd + ) : \ + CDirectInput( hwnd, DIDEVTYPE_KEYBOARD, 256 ) +{ + TRACE( "CDirectKeyboard::CDirectKeyboard" ); +} + +HRESULT CDirectKeyboard::Update( + void + ) +{ + if ( GetDevice() == NULL ) + { + return E_FAIL; + } + + HRESULT hr; + + GetDevice()->Poll(); + + hr = GetDevice()->GetDeviceState( GetButtonCount(), m_pButtons ); + if ( hr == DIERR_INPUTLOST || + hr == DIERR_NOTACQUIRED ) + { + hr = GetDevice()->Acquire(); + if ( hr == DIERR_OTHERAPPHASPRIO ) + { + return S_FALSE; + } + + TRACE( "Acquire = %X", hr ); + + GetDevice()->Poll(); + hr = GetDevice()->GetDeviceState( GetButtonCount(), m_pButtons ); + } + + ASSERT(hr == S_OK && "Keyboard GetDeviceState failed"); + return hr; +} + +// --------------------------------------------------------------------------- + +CDirectJoystick::CDirectJoystick( + HWND hwnd + ) : \ + CDirectInput( hwnd, DIDEVTYPE_JOYSTICK, 32 ) +{ + TRACE( "CDirectJoystick::CDirectJoystick" ); +} + +HRESULT CDirectJoystick::Initialize( + void + ) +{ + TRACE( "CDirectJoystick::Initialize" ); + + HRESULT hr; + + hr = CDirectInput::Initialize(); + if ( FAILED(hr) ) + { + return hr; + } + + if ( GetDevice() == NULL ) + { + TRACE("No joystick was found"); + return S_FALSE; + } + + // set X-axis range to (-1000 ... +1000) + // This lets us test against 0 to see which way the stick is pointed. + + DIPROPRANGE dipr; + + dipr.diph.dwSize = sizeof(dipr); + dipr.diph.dwHeaderSize = sizeof(dipr.diph); + dipr.diph.dwHow = DIPH_BYOFFSET; + dipr.lMin = -1000; + dipr.lMax = +1000; + dipr.diph.dwObj = DIJOFS_X; + hr = GetDevice()->SetProperty( DIPROP_RANGE, &dipr.diph ); + if ( FAILED(hr) ) + { + TRACE( "SetProperty(DIPROP_RANGE,x) failed, hr=%X", hr ); + return hr; + } + + // And again for Y-axis range + + dipr.diph.dwSize = sizeof(dipr); + dipr.diph.dwHeaderSize = sizeof(dipr.diph); + dipr.diph.dwHow = DIPH_BYOFFSET; + dipr.lMin = -1000; + dipr.lMax = +1000; + dipr.diph.dwObj = DIJOFS_Y; + hr = GetDevice()->SetProperty( DIPROP_RANGE, &dipr.diph ); + if ( FAILED(hr) ) + { + TRACE( "SetProperty(DIPROP_RANGE,y) failed, hr=%X", hr ); + return hr; + } + + + // set dead zone to 50% + + DIPROPDWORD dipdw; + + dipdw.diph.dwSize = sizeof(dipdw); + dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); + dipdw.diph.dwHow = DIPH_BYOFFSET; + dipdw.dwData = 5000; + dipdw.diph.dwObj = DIJOFS_X; + hr = GetDevice()->SetProperty( DIPROP_DEADZONE, &dipdw.diph ); + if ( FAILED(hr) ) + { + TRACE( "SetProperty(DIPROP_DEADZONE,x) failed, hr=%X", hr ); + return hr; + } + + dipdw.diph.dwSize = sizeof(dipdw); + dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); + dipdw.diph.dwHow = DIPH_BYOFFSET; + dipdw.dwData = 5000; + dipdw.diph.dwObj = DIJOFS_Y; + hr = GetDevice()->SetProperty( DIPROP_DEADZONE, &dipdw.diph ); + if ( FAILED(hr) ) + { + TRACE( "SetProperty(DIPROP_DEADZONE,y) failed, hr=%X", hr ); + return hr; + } + + return S_OK; +} + +HRESULT CDirectJoystick::Update( + void + ) +{ + if ( GetDevice() == NULL ) + { + return E_FAIL; + } + + HRESULT hr; + + DIJOYSTATE dijs; + + GetDevice()->Poll(); + + hr = GetDevice()->GetDeviceState( sizeof(dijs), &dijs ); + if ( hr == DIERR_INPUTLOST || + hr == DIERR_NOTACQUIRED ) + { + hr = GetDevice()->Acquire(); + if ( hr == DIERR_OTHERAPPHASPRIO ) + { + return S_FALSE; + } + + GetDevice()->Poll(); + hr = GetDevice()->GetDeviceState( sizeof(dijs), &dijs ); + } + + ASSERT(hr == DI_OK && "Joystick GetDeviceState failed"); + + if ( hr == DI_OK ) + { + m_lX = dijs.lX; + m_lY = dijs.lY; + + memcpy( m_pButtons, + dijs.rgbButtons, + sizeof(dijs.rgbButtons) ); + } + + return hr; +} + + +// --------------------------------------------------------------------------- + +CDisabledJoystick::CDisabledJoystick( + HWND hwnd + ) : \ + CDirectInput( NULL, 0, 0 ) +{ + UNUSED_ALWAYS( hwnd ); + + TRACE( "CDisabledJoystick::CDisabledJoystick" ); +} + +HRESULT CDisabledJoystick::Update( + void + ) +{ + return S_FALSE; +} + +// --------------------------------------------------------------------------- + +CDirectMouse::CDirectMouse( + HWND hwnd + ) : \ + CDirectInput( hwnd, DIDEVTYPE_MOUSE, 4 ) +{ + TRACE( "CDirectMouse::CDirectMouse" ); +} + +HRESULT CDirectMouse::Update( + void + ) +{ + if (GetDevice() == NULL) + { + return E_FAIL; + } + + HRESULT hr; + + DIMOUSESTATE dims; + + GetDevice()->Poll(); + + hr = GetDevice()->GetDeviceState( sizeof(dims), &dims ); + if ( hr == DIERR_INPUTLOST || + hr == DIERR_NOTACQUIRED ) + { + hr = GetDevice()->Acquire(); + if ( hr == DIERR_OTHERAPPHASPRIO ) + { + return S_FALSE; + } + + GetDevice()->Poll(); + hr = GetDevice()->GetDeviceState( sizeof(dims), &dims ); + } + + ASSERT( hr == DI_OK && "Mouse GetDeviceState failed" ); + + if ( hr == DI_OK ) + { + // Because the mouse is returning relative positions, + // force X and Y to go between 0 ... 999 + + m_lX += dims.lX; + + if (m_lX < 0) + { + m_lX = 0; + } + else if (m_lX > 999) + { + m_lX = 999; + } + + m_lY += dims.lY; + + if (m_lY < 0) + { + m_lY = 0; + } + else if (m_lY > 999) + { + m_lY = 999; + } + + memcpy( m_pButtons, + dims.rgbButtons, + sizeof(dims.rgbButtons) ); + } + + return hr; +} diff --git a/stella/src/ui/win32/DirectInput.hxx b/stella/src/ui/win32/DirectInput.hxx new file mode 100644 index 000000000..188d6e018 --- /dev/null +++ b/stella/src/ui/win32/DirectInput.hxx @@ -0,0 +1,158 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef DIRECTIN_H +#define DIRECTIN_H + +class CDirectInput +{ +public: + + CDirectInput( HWND hwnd, DWORD dwDevType, int nButtonCount ); + virtual ~CDirectInput( ); + + virtual HRESULT Initialize( void ); + + virtual HRESULT Update( void ) = 0; + + void GetPos( LONG* pX, LONG* pY ) const; + + virtual BOOL IsButtonPressed( int nButton ) const; + virtual int GetButtonCount( void ) const; + + // I need IDID2 for the Poll method + + IDirectInputDevice2* GetDevice( void ) const; + +protected: + + LONG m_lX; + LONG m_lY; + BYTE* m_pButtons; + +private: + + void Cleanup(); + + static BOOL CALLBACK EnumDevicesProc( const DIDEVICEINSTANCE* lpddi, + LPVOID pvRef ); + + IDirectInput* m_piDI; + + HWND m_hwnd; + IDirectInputDevice2* m_piDID; + DWORD m_dwDevType; + + const int m_nButtonCount; + + BOOL m_fInitialized; + + CDirectInput( const CDirectInput& ); // no implementation + void operator=( const CDirectInput& ); // no implementation + +}; + +inline int CDirectInput::GetButtonCount( + void + ) const +{ + return m_nButtonCount; +} + + +inline IDirectInputDevice2* CDirectInput::GetDevice( + void + ) const +{ + // 060499: Dont assert here, as it's okay if a device isn't available + // (client must check for NULL return) + return m_piDID; +} + +inline void CDirectInput::GetPos( + LONG* pX, + LONG* pY + ) const +{ + if (pX != NULL) + { + *pX = m_lX; + } + + if (pY != NULL) + { + *pY = m_lY; + } +} + + +// --------------------------------------------------------------------------- + +class CDirectMouse : public CDirectInput +{ +public: + + CDirectMouse( HWND hwnd ); + + HRESULT Update( void ); + +private: + + CDirectMouse( const CDirectMouse& ); // no implementation + void operator=( const CDirectMouse& ); // no implementation + +}; + + +// --------------------------------------------------------------------------- + +class CDirectJoystick : public CDirectInput +{ +public: + + CDirectJoystick( HWND hwnd ); + + HRESULT Initialize( void ); + HRESULT Update( void ); + +private: + + CDirectJoystick( const CDirectJoystick& ); // no implementation + void operator=( const CDirectJoystick& ); // no implementation + +}; + +class CDisabledJoystick : public CDirectInput +{ +public: + + CDisabledJoystick( HWND hwnd ); + + HRESULT Update( void ); + +private: + + CDisabledJoystick( const CDisabledJoystick& ); // no implementation + void operator=( const CDisabledJoystick& ); // no implementation + +}; + +// --------------------------------------------------------------------------- + +class CDirectKeyboard : public CDirectInput +{ +public: + + CDirectKeyboard( HWND hwnd ); + + HRESULT Update( void ); + +private: + + CDirectKeyboard( const CDirectKeyboard& ); // no implementation + void operator=( const CDirectKeyboard& ); // no implementation + +}; + +#endif diff --git a/stella/src/ui/win32/DirectXFullScreen.cxx b/stella/src/ui/win32/DirectXFullScreen.cxx new file mode 100644 index 000000000..03a53c4a2 --- /dev/null +++ b/stella/src/ui/win32/DirectXFullScreen.cxx @@ -0,0 +1,1044 @@ +// +// StellaX +// Jeff Miller 05/12/2000 +// +#include "pch.hxx" +#include "DirectXFullScreen.hxx" +#include "resource.h" + +#include "DirectInput.hxx" + +#include "bspf.hxx" +#include "Console.hxx" +#include "Event.hxx" +#include "MediaSrc.hxx" + +/* + +CDirectXFullScreen + +Handles Full Screen DirectX +It's responsible for updating all DirectInput devices as well + +The message loop (See the Run method) will do the processing in +idle time + +*/ + +LPCTSTR CDirectXFullScreen::pszClassName = _T("StellaXClass"); + +CDirectXFullScreen::CDirectXFullScreen( + const CGlobalData& rGlobalData, + const Console* pConsole, + Event& rEvent + ) : \ + m_rGlobalData( rGlobalData ), + m_pConsole( pConsole ), + m_rEvent( rEvent ), + + m_fInitialized( FALSE ), + m_hwnd( NULL ), + + m_piDD( NULL ), + m_piDDSPrimary( NULL ), + m_piDDSBack( NULL ), + m_piDDPalette( NULL ), + + m_fActiveWindow( TRUE ), + + m_pDirectMouse( NULL ), + m_pDirectJoystick( NULL ), + m_pDirectKeyboard( NULL ) +{ + TRACE("CDirectXFullScreen::CDirectXFullScreen"); + + // Get the game's width and height + + m_sizeGame.cx = m_pConsole->mediaSource().width(); + m_sizeGame.cy = m_pConsole->mediaSource().height(); + TRACE("m_sizeGame cx=%ld, cy=%ld", m_sizeGame.cx, m_sizeGame.cy); + + // Initialize the pixel data table + + for (int j = 0; j < 256; ++j) + { + m_rgbPixelDataTable[j] = j | (j << 8); + } + +} + +CDirectXFullScreen::~CDirectXFullScreen +( +) +{ + TRACE("CDirectXFullScreen::~CDirectXFullScreen"); + + Cleanup(); +} + +HRESULT CDirectXFullScreen::Initialize( + int cx /* = 0 */, + int cy /* = 0 */ + ) +{ + TRACE( "CDirectXFullScreen::Initialize" ); + + if ( m_fInitialized ) + { + return S_OK; + } + + HRESULT hr = S_OK; + const unsigned int* pPalette = m_pConsole->mediaSource().palette(); + + // Create the windows class, see: + // mk:@MSITStore:E:\MSDN\DXFound.chm::/devdoc/good/directx/ddoverv_0l2v.htm + + TRACE( "CDirectXFullScreen RegisterClassEx" ); + + WNDCLASSEX wcex; + ZeroMemory( &wcex, sizeof(wcex) ); + wcex.cbSize = sizeof( wcex ); + wcex.hInstance = m_rGlobalData.ModuleInstance(); + wcex.lpszClassName = pszClassName; + wcex.lpfnWndProc = StaticWindowProc; + wcex.style = CS_OWNDC; + wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION ); + wcex.hIconSm = LoadIcon( NULL, IDI_WINLOGO ); + wcex.hCursor = LoadCursor( NULL, IDC_ARROW ); + wcex.hbrBackground = (HBRUSH)GetStockObject( NULL_BRUSH ); + if ( ! ::RegisterClassEx( &wcex ) ) + { + hr = HRESULT_FROM_WIN32( ::GetLastError() ); + goto cleanup; + } + + + TRACE("CDirectXFullScreen CreateWindowEx"); + + // 060499: pass this into the createparams to make sure it's available + // if command line param given + + m_hwnd = ::CreateWindowEx( WS_EX_TOPMOST, + pszClassName, + _T("StellaX"), + WS_VISIBLE | WS_POPUP, + 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), + NULL, + NULL, + m_rGlobalData.ModuleInstance(), + this ); + if ( m_hwnd == NULL ) + { + TRACE( "CreateWindow failed" ); + hr = HRESULT_FROM_WIN32( GetLastError() ); + MessageBox( m_rGlobalData.ModuleInstance(), NULL, IDS_CW_FAILED ); + goto cleanup; + } + + ::SetFocus( m_hwnd ); + ::ShowWindow( m_hwnd, SW_SHOW ); + ::UpdateWindow( m_hwnd ); + + ::ShowCursor( FALSE ); + + // + // Initialize DirectDraw + // + + hr = ::CoCreateInstance( CLSID_DirectDraw, + NULL, + CLSCTX_SERVER, + IID_IDirectDraw, + (void**)&m_piDD ); + if ( FAILED(hr) ) + { + TRACE( "CCI DirectDraw failed" ); + goto cleanup; + } + + // + // Initialize it + // This method takes the driver GUID parameter that the DirectDrawCreate + // function typically uses (NULL is active display driver) + // + + hr = m_piDD->Initialize( NULL ); + if ( FAILED(hr) ) + { + TRACE( "DDraw::Initialize failed, hr=%x", hr ); + goto cleanup; + } + + // + // Get the best video mode for game width + // + + ::SetRect( &m_rectScreen, 0, 0, cx, cy ); + + if ( cx == 0 || cy == 0 ) + { + hr = m_piDD->EnumDisplayModes( 0, NULL, this, EnumModesCallback ); + if ( FAILED(hr) ) + { + TRACE( "EnumDisplayModes failed" ); + goto cleanup; + } + } + + if ( m_rectScreen.right == 0 || m_rectScreen.bottom == 0 ) + { + TRACE("No good video mode found"); + hr = E_INVALIDARG; + goto cleanup; + } + + TRACE( "Video Mode Selected: %d x %d", m_rectScreen.right, m_rectScreen.bottom ); + + // compute blit offset to center image + + m_ptBlitOffset.x = ( ( m_rectScreen.right - m_sizeGame.cx * 2 ) / 2 ); // WIDTH_FACTOR + m_ptBlitOffset.y = ( ( m_rectScreen.bottom - m_sizeGame.cy ) / 2 ); + + TRACE("Game dimensions: %dx%d (blit offset = %d, %d)", + m_sizeGame.cx, m_sizeGame.cy, m_ptBlitOffset.x, m_ptBlitOffset.y); + + // Set cooperative level + + hr = m_piDD->SetCooperativeLevel( m_hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ); + if ( FAILED(hr) ) + { + TRACE( "IDD:SetCooperativeLevel failed, hr=%x", hr ); + goto cleanup; + } + + hr = m_piDD->SetDisplayMode( m_rectScreen.right, m_rectScreen.bottom, 8 ); + if ( FAILED(hr) ) + { + TRACE( "IDD:SetDisplayMode failed, hr=%x", hr ); + goto cleanup; + } + + // + // Create the primary surface + // + + DDSURFACEDESC ddsd; + + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + hr = m_piDD->CreateSurface(&ddsd, &m_piDDSPrimary, NULL); + if (FAILED(hr)) + { + TRACE( "CreateSurface failed, hr=%X", hr ); + goto cleanup; + } + + // + // Create the offscreen surface + // + + ZeroMemory(&ddsd, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + ddsd.dwWidth = m_sizeGame.cx * 2; // WIDTH_FACTOR + ddsd.dwHeight = m_sizeGame.cy; + hr = m_piDD->CreateSurface(&ddsd, &m_piDDSBack, NULL); + if (FAILED(hr)) + { + TRACE( "CreateSurface failed, hr=%x", hr ); + goto cleanup; + } + + // + // Erase the surface + // + + HDC hdc; + hr = m_piDDSBack->GetDC( &hdc ); + if ( hr == DD_OK ) + { + ::SetBkColor( hdc, RGB(0, 0, 0) ); + RECT rc; + ::SetRect( &rc, 0, 0, + m_sizeGame.cx * 2, // WIDTH_FACTOR + m_sizeGame.cy ); + ::ExtTextOut( hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL ); + + (void)m_piDDSBack->ReleaseDC( hdc ); + } + + // + // Create Palette + // + + PALETTEENTRY pe[256]; + + int i; + for ( i = 0; i < 256; ++i ) + { + pe[i].peRed = (BYTE)( (pPalette[i] & 0x00FF0000) >> 16 ); + pe[i].peGreen = (BYTE)( (pPalette[i] & 0x0000FF00) >> 8 ); + pe[i].peBlue = (BYTE)( (pPalette[i] & 0x000000FF) ); + pe[i].peFlags = 0; + } + + hr = m_piDD->CreatePalette( DDPCAPS_8BIT, + pe, + &m_piDDPalette, + NULL ); + if ( FAILED(hr) ) + { + TRACE( "IDD::CreatePalette failed, hr=%X", hr); + goto cleanup; + } + + hr = m_piDDSPrimary->SetPalette( m_piDDPalette ); + if ( FAILED(hr) ) + { + TRACE( "SetPalette failed, hr=%x", hr ); + goto cleanup; + } + + // + // Initialize DirectInput + // + + m_pDirectMouse = new CDirectMouse( m_hwnd ); + if (m_pDirectMouse == NULL) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + hr = m_pDirectMouse->Initialize(); + if ( FAILED(hr) ) + { + TRACE( "Directmouse initialzie failed, hr = %X\n", hr ); + goto cleanup; + } + + if ( m_rGlobalData.DisableJoystick() ) + { + m_pDirectJoystick = new CDisabledJoystick( m_hwnd ); + } + else + { + m_pDirectJoystick = new CDirectJoystick( m_hwnd ); + } + if (m_pDirectJoystick == NULL) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + hr = m_pDirectJoystick->Initialize( ); + if ( FAILED(hr) ) + { + TRACE( "joystick initialzie failed, hr=%X", hr ); + goto cleanup; + } + + m_pDirectKeyboard = new CDirectKeyboard( m_hwnd ); + if (m_pDirectKeyboard == NULL) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + hr = m_pDirectKeyboard->Initialize(); + if ( FAILED(hr) ) + { + TRACE( "keyboard init failed. hr=%X", hr ); + goto cleanup; + } + + m_fInitialized = TRUE; + +cleanup: + + if ( FAILED(hr) ) + { + Cleanup(); + } + + return hr; +} + +void CDirectXFullScreen::Cleanup( + void + ) +{ + TRACE( "CDirectXFullScreen::Cleanup" ); + + ShowCursor(TRUE); + + delete m_pDirectMouse; + m_pDirectMouse = NULL; + + delete m_pDirectJoystick; + m_pDirectJoystick = NULL; + + delete m_pDirectKeyboard; + m_pDirectKeyboard = NULL; + + if (m_piDDPalette) + { + m_piDDPalette->Release(); + m_piDDPalette = NULL; + } + + if ( m_piDDSBack ) + { + m_piDDSBack->Release(); + m_piDDSBack = NULL; + } + + if ( m_piDD ) + { + if ( m_piDDSPrimary ) + { + m_piDDSPrimary->Release(); + m_piDDSPrimary = NULL; + } + + m_piDD->Release( ); + m_piDD = NULL; + } + + if ( m_hwnd ) + { + ::DestroyWindow( m_hwnd ); + + // + // Remove the WM_QUIT which will be in the message queue + // so that the main window doesn't exit + // + + MSG msg; + ::PeekMessage( &msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE ); + + m_hwnd = NULL; + } + + ::UnregisterClass( pszClassName, m_rGlobalData.ModuleInstance() ); + + m_fInitialized = FALSE; +} + +LRESULT CALLBACK CDirectXFullScreen::StaticWindowProc( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + CDirectXFullScreen* pThis; + + if ( uMsg == WM_CREATE ) + { + pThis = reinterpret_cast( + reinterpret_cast( lParam )->lpCreateParams ); + + ::SetWindowLong( hwnd, + GWL_USERDATA, + reinterpret_cast( pThis ) ); + } + else + { + pThis = reinterpret_cast( + ::GetWindowLong( hwnd, GWL_USERDATA ) ); + } + + if ( pThis ) + { + if ( pThis->WndProc( uMsg, wParam, lParam ) ) + { + // + // Handled message + // + + return 0L; + } + } + + // + // Unhandled message + // + + return ::DefWindowProc( hwnd, uMsg, wParam, lParam ); +} + + +BOOL CDirectXFullScreen::WndProc( + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_ACTIVATE: + m_fActiveWindow = ( wParam != WA_INACTIVE ); + break; + + case WM_DESTROY: + ::PostQuitMessage( 0 ); + break; + + case WM_KEYDOWN: + switch ( (int)wParam ) + { + case VK_ESCAPE: + // + // Escape = Exit + // + + ::PostMessage( m_hwnd, WM_CLOSE, 0, 0 ); + break; + + case VK_F12: + { + // + // F12 = write screenshot + // + + TCHAR pszFileName[ MAX_PATH ]; + int i; + + for ( i = 0; i < 99 ; ++i ) + { + wsprintf( pszFileName, _T("stella%02d.bmp"), i ); + if ( ::GetFileAttributes( pszFileName ) == 0xFFFFFFFF ) + { + break; + } + } + +// TODO: Get the BMP writing code added to the core +// m_pConsole->mediaSource().writeBmp( pszFileName ); + } + break; + } + break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + ::BeginPaint( m_hwnd, &ps ); + ::EndPaint( m_hwnd, &ps ); + } + break; + + default: + // + // Unhandled message + // + + return FALSE; + } + + // + // Handled message + // + + return TRUE; +} + +DWORD CDirectXFullScreen::Run( + void + ) +{ + TRACE("CDirectXFullScreen::Run"); + + // + // Get the initial tick count + // + + UINT uFrameCount = 0; + + unsigned __int64 uiStartRun; + ::QueryPerformanceCounter( (LARGE_INTEGER*)&uiStartRun ); + + // + // Find out how many ticks occur per second + // + + unsigned __int64 uiCountsPerSecond; + ::QueryPerformanceFrequency( (LARGE_INTEGER*)&uiCountsPerSecond ); + + const unsigned __int64 uiCountsPerFrame = + ( uiCountsPerSecond / m_rGlobalData.DesiredFrameRate() ); + + TRACE( "uiCountsPerSecond=%ld", uiCountsPerSecond ); + TRACE( "uiCountsPerFrame = %ld", uiCountsPerFrame ); + + unsigned __int64 uiFrameStart; + unsigned __int64 uiFrameCurrent; + + // + // Get Stella's media source + // + + MediaSource& rMediaSource = m_pConsole->mediaSource(); + + // + // Main message loop + // + + MSG msg; + + for ( ; ; ) + { + if ( ::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + { + if ( msg.message == WM_QUIT ) + { + TRACE("WM_QUIT received"); + break; + } + + ::TranslateMessage( &msg ); + ::DispatchMessage( &msg ); + } + else if (m_fActiveWindow) + { + // + // idle time -- do stella updates + // + + ++uFrameCount; + + ::QueryPerformanceCounter( (LARGE_INTEGER*)&uiFrameStart ); + + // + // ask media source to prepare the next frame + // + + rMediaSource.update(); + + // + // update the display + // + + if ( ! UpdateDisplay( rMediaSource ) ) + { + // fatal error! + + ::MessageBox(NULL, "Fatal error updating display", "Error", MB_OK); + ::PostQuitMessage( 0 ); + break; + } + + // + // Check for keyboard, mouse, joystick input + // + + UpdateEvents(); + + // + // waste time to to meet desired frame rate + // + + for ( ; ; ) + { + ::QueryPerformanceCounter( (LARGE_INTEGER*)&uiFrameCurrent ); + + if ( ( uiFrameCurrent - uiFrameStart ) >= uiCountsPerFrame ) + { + break; + } + } + } + else + { + // if no new message, and we're not the active window, then sleep + + ::WaitMessage(); + } + } + + TRACE("Message loop done"); + + // Main message loop done + + if ( m_rGlobalData.ShowFPS() ) + { + // get number of scanlines in last frame + + uInt32 uScanLines = rMediaSource.scanlines(); + + // Get the final tick count + + unsigned __int64 uiEndRun; + ::QueryPerformanceCounter( (LARGE_INTEGER*)&uiEndRun ); + + // Get number of ticks + + DWORD secs = (DWORD)( ( uiEndRun - uiStartRun ) / uiCountsPerSecond ); + + DWORD fps = (secs == 0) ? 0 : (uFrameCount / secs); + + TCHAR pszBuf[1024]; + wsprintf( pszBuf, _T("Frames drawn: %ld\nFPS: %ld\nScanlines in last frame: %ld\n"), + uFrameCount, + fps, + uScanLines ); + MessageBox( NULL, pszBuf, _T("Statistics"), MB_OK ); + } + + return msg.wParam; +} + +// --------------------------------------------------------------------------- +// Event Handling +// VK_* is defined in winuser.h + +struct KeyEventStruct +{ + int nVirtKey; + int nAsyncVirtKey; + Event::Type eventCode; +}; + + +static KeyEventStruct KeyEventMap[] = +{ + /* -------------------------------------------------------------------- */ + /* left keypad */ + + { DIK_1, '1', Event::KeyboardZero1 }, + { DIK_2, '2', Event::KeyboardZero2 }, + { DIK_3, '3', Event::KeyboardZero3 }, + { DIK_Q, 'Q', Event::KeyboardZero4 }, + { DIK_W, 'W', Event::KeyboardZero5 }, + { DIK_E, 'E', Event::KeyboardZero6 }, + { DIK_A, 'A', Event::KeyboardZero7 }, + { DIK_S, 'S', Event::KeyboardZero8 }, + { DIK_D, 'D', Event::KeyboardZero9 }, + { DIK_Z, 'Z', Event::KeyboardZeroStar }, + { DIK_X, 'X', Event::KeyboardZero0 }, + { DIK_C, 'C', Event::KeyboardZeroPound }, + + /* -------------------------------------------------------------------- */ + /* right keypad */ + + { DIK_8, '8', Event::KeyboardOne1 }, + { DIK_9, '9', Event::KeyboardOne2 }, + { DIK_0, '0', Event::KeyboardOne3 }, + { DIK_I, 'I', Event::KeyboardOne4 }, + { DIK_O, 'O', Event::KeyboardOne5 }, + { DIK_P, 'P', Event::KeyboardOne6 }, + { DIK_K, 'K', Event::KeyboardOne7 }, + { DIK_L, 'L', Event::KeyboardOne8 }, + { DIK_SEMICOLON,';', Event::KeyboardOne9 }, + { DIK_COMMA, ',', Event::KeyboardOneStar }, + { DIK_PERIOD, '.', Event::KeyboardOne0 }, + { DIK_SLASH, '/', Event::KeyboardOnePound }, + + /* -------------------------------------------------------------------- */ + /* left joystick */ + + { DIK_DOWN, VK_DOWN, Event::JoystickZeroDown }, + { DIK_UP, VK_UP, Event::JoystickZeroUp }, + { DIK_LEFT, VK_LEFT, Event::JoystickZeroLeft }, + { DIK_RIGHT, VK_RIGHT, Event::JoystickZeroRight }, + { DIK_SPACE, VK_SPACE, Event::JoystickZeroFire }, + + /* left joystick (alt.) */ + + { DIK_W, 'W', Event::JoystickZeroUp }, + { DIK_S, 'S', Event::JoystickZeroDown }, + { DIK_A, 'A', Event::JoystickZeroLeft }, + { DIK_D, 'D', Event::JoystickZeroRight }, + { DIK_TAB, VK_TAB, Event::JoystickZeroFire }, + + /* I added this one (for my powerramp joystick) */ + + { DIK_LCONTROL, VK_CONTROL, Event::JoystickZeroFire }, + + /* left joystick booster-grip */ + + { DIK_Z, 'Z', Event::BoosterGripZeroTrigger }, + { DIK_X, 'X', Event::BoosterGripZeroBooster }, + + /* left joystick booster-grip (alt.) */ + + { DIK_1, '1', Event::BoosterGripZeroTrigger }, + { DIK_2, '2', Event::BoosterGripZeroBooster }, + + /* -------------------------------------------------------------------- */ + /* right joystick */ + + { DIK_L, 'L', Event::JoystickOneDown }, + { DIK_O, 'O', Event::JoystickOneUp }, + { DIK_K, 'K', Event::JoystickOneLeft }, + { DIK_SEMICOLON,';', Event::JoystickOneRight }, + { DIK_J, 'J', Event::JoystickOneFire }, + + /* right joystick booster-grip */ + + { DIK_N, 'N', Event::BoosterGripOneTrigger }, + { DIK_M, 'M', Event::BoosterGripOneBooster }, + + /* -------------------------------------------------------------------- */ + /* console controls */ + + { DIK_F1, VK_F1, Event::ConsoleSelect }, + { DIK_F2, VK_F2, Event::ConsoleReset }, + { DIK_F3, VK_F3, Event::ConsoleColor }, + { DIK_F4, VK_F4, Event::ConsoleBlackWhite }, + { DIK_F5, VK_F5, Event::ConsoleLeftDifficultyA }, + { DIK_F6, VK_F6, Event::ConsoleLeftDifficultyB }, + { DIK_F7, VK_F7, Event::ConsoleRightDifficultyA }, + { DIK_F8, VK_F8, Event::ConsoleRightDifficultyB } +}; + +void CDirectXFullScreen::UpdateEvents( + void + ) +{ + // + // I do this because an event may appear multiple times in the map + // and I don't want to undo a set i may have done earlier in the loop + // + + const int nEventCount = Event::LastType; + long rgEventState[ nEventCount ]; + ZeroMemory( rgEventState, nEventCount * sizeof(long) ); + + int i; + + // + // Update keyboard + // + + if (m_pDirectKeyboard->Update() == S_OK) + { + int nSize = _countof(KeyEventMap); + + for (i = 0; i < nSize; ++i) + { + rgEventState[KeyEventMap[i].eventCode] |= + m_pDirectKeyboard->IsButtonPressed(KeyEventMap[i].nVirtKey); + } + } + else + { + // Fallback -- if Keyboard update failed (most likely due to + // DirectInput not being available), then use the old async + + int nSize = _countof(KeyEventMap); + + for (i = 0; i < nSize; ++i) + { + rgEventState[KeyEventMap[i].eventCode] |= + (::GetAsyncKeyState(KeyEventMap[i].nAsyncVirtKey) ? 1: 0); + } + } + + // + // Update joystick + // + + if (m_pDirectJoystick->Update() == S_OK) + { + rgEventState[Event::JoystickZeroFire] |= + m_pDirectJoystick->IsButtonPressed(0); + + LONG x; + LONG y; + m_pDirectJoystick->GetPos( &x, &y ); + + if (x < 0) + { + rgEventState[Event::JoystickZeroLeft] = 1; + } + else if (x > 0) + { + rgEventState[Event::JoystickZeroRight] = 1; + } + if (y < 0) + { + rgEventState[Event::JoystickZeroUp] = 1; + } + else if (y > 0) + { + rgEventState[Event::JoystickZeroDown] = 1; + } + } + + // + // Update mouse + // + + if (m_pDirectMouse->Update() == S_OK) + { + // NOTE: Mouse::GetPos returns a value from 0..999 + + LONG x; + m_pDirectMouse->GetPos( &x, NULL ); + + // Mouse resistance is measured between 0...1000000 + + rgEventState[ m_rGlobalData.PaddleResistanceEvent() ] = (999-x)*1000; + + rgEventState[ m_rGlobalData.PaddleFireEvent() ] |= m_pDirectMouse->IsButtonPressed(0); + } + + // + // Write new event state + // + + for (i = 0; i < nEventCount; ++i) + { + m_rEvent.set( (Event::Type)i, rgEventState[i] ); + } +} + +// --------------------------------------------------------------------------- +// Display Update + +BOOL CDirectXFullScreen::UpdateDisplay( + MediaSource& rMediaSource + ) +{ + if ( m_piDDSPrimary == NULL ) + { + return FALSE; + } + + if ( ! m_fActiveWindow ) + { + return TRUE; + } + + HRESULT hr; + + // uInt32* current = (uInt32*)rMediaSource.currentFrameBuffer(); + // uInt32* previous = (uInt32*)rMediaSource.previousFrameBuffer(); + + const BYTE* current = rMediaSource.currentFrameBuffer(); + const BYTE* previous = rMediaSource.previousFrameBuffer(); + + // + // acquire pointer to linear video ram + // + + DDSURFACEDESC ddsd; + ZeroMemory( &ddsd, sizeof(ddsd) ); + ddsd.dwSize = sizeof(ddsd); + + hr = m_piDDSBack->Lock( NULL, + &ddsd, + /* DDLOCK_SURFACEMEMORYPTR | */ DDLOCK_WAIT, + NULL ); + // BUGBUG: Check for error + + BYTE* pbBackBytes = (BYTE*)ddsd.lpSurface; + + register int y; + for ( y = 0; y < m_sizeGame.cy; ++y ) + { + const WORD bufofsY = ( y * m_sizeGame.cx ); + const DWORD screenofsY = ( y * ddsd.lPitch ); + + register int x; + for ( x = 0; x < m_sizeGame.cx; ++x ) + { + const WORD bufofs = bufofsY + x; + BYTE v = current[ bufofs ]; + if ( v == previous[ bufofs ] ) + { + continue; + } + + // x << 1 is times 2 ( doubling width ) WIDTH_FACTOR + + const DWORD pos = screenofsY + ( x << 1 ); + + pbBackBytes[ pos + 0 ] = + pbBackBytes[ pos + 1 ] = m_rgbPixelDataTable[ v ]; + } + } + + (void)m_piDDSBack->Unlock( ddsd.lpSurface ); + + // Blit offscreen to onscreen + + + RECT rc = { 0, 0, + m_sizeGame.cx*2, // WIDTH_FACTOR + m_sizeGame.cy }; + + hr = m_piDDSPrimary->BltFast( m_ptBlitOffset.x, m_ptBlitOffset.y, + m_piDDSBack, + &rc, + // DDBLTFAST_WAIT |DDBLTFAST_DESTCOLORKEY ); + DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT ); + + return TRUE; +} + +HRESULT WINAPI CDirectXFullScreen::EnumModesCallback( + LPDDSURFACEDESC lpDDSurfaceDesc, + LPVOID lpContext + ) +{ + CDirectXFullScreen* pThis = (CDirectXFullScreen*)lpContext; + + DWORD dwWidthReq = pThis->m_sizeGame.cx * 2; // WIDTH_FACTOR + DWORD dwHeightReq = pThis->m_sizeGame.cy; + + DWORD dwWidth = lpDDSurfaceDesc->dwWidth; + DWORD dwHeight = lpDDSurfaceDesc->dwHeight; + DWORD dwRGBBitCount = lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount; + + // + // must be 8 bit mode + // + + if (dwRGBBitCount != 8) + { + return DDENUMRET_OK; + } + + // + // must be larger then required screen size + // + + if ( dwWidth < dwWidthReq || dwHeight < dwHeightReq ) + { + return DDENUMRET_OK; + } + + if ( pThis->m_rectScreen.right != 0 && pThis->m_rectScreen.bottom != 0 ) + { + // + // check to see if this is better than the previous choice + // + + if ( (dwWidth - dwWidthReq) > (pThis->m_rectScreen.right - dwWidthReq) ) + { + return DDENUMRET_OK; + } + + if ( (dwHeight - dwHeightReq) > (pThis->m_rectScreen.bottom - dwHeightReq) ) + { + return DDENUMRET_OK; + } + } + + // + // use it! + // + + pThis->m_rectScreen.right = dwWidth; + pThis->m_rectScreen.bottom = dwHeight; + + return DDENUMRET_OK; +} diff --git a/stella/src/ui/win32/DirectXFullScreen.hxx b/stella/src/ui/win32/DirectXFullScreen.hxx new file mode 100644 index 000000000..7ffe81172 --- /dev/null +++ b/stella/src/ui/win32/DirectXFullScreen.hxx @@ -0,0 +1,84 @@ +// +// StellaX +// Jeff Miller 05/12/2000 +// +#ifndef DXFS_H +#define DXFS_H +#pragma once + +class Console; +class MediaSource; + +class CDirectInput; + +#include "Event.hxx" +#include "GlobalData.hxx" + +class CDirectXFullScreen +{ +public: + + CDirectXFullScreen( const CGlobalData& rGlobalData, + const Console* pConsole, + Event& rEvent ); + ~CDirectXFullScreen(); + + HRESULT Initialize( int cx = 0, int cy = 0 ); + + DWORD Run(); + + operator HWND( void ) const + { + return m_hwnd; + } + +private: + + BOOL m_fInitialized; + + static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ); + BOOL WndProc( UINT uMsg, WPARAM wParam, LPARAM lParam ); + + static HRESULT WINAPI EnumModesCallback( LPDDSURFACEDESC lpDDSurfaceDesc, + LPVOID lpContext); + + void Cleanup(); + void UpdateEvents(); + BOOL UpdateDisplay( MediaSource& rMediaSource ); + + HWND m_hwnd; + BOOL m_fActiveWindow; + + RECT m_rectScreen; + POINT m_ptBlitOffset; + + // Stella objects + + const Console* m_pConsole; + Event& m_rEvent; + + const CGlobalData& m_rGlobalData; + SIZE m_sizeGame; + BYTE m_rgbPixelDataTable[256]; + + // + // DirectX + // + + IDirectDraw* m_piDD; + IDirectDrawSurface* m_piDDSPrimary; + IDirectDrawSurface* m_piDDSBack; + IDirectDrawPalette* m_piDDPalette; + + CDirectInput* m_pDirectMouse; + CDirectInput* m_pDirectJoystick; + CDirectInput* m_pDirectKeyboard; + + static LPCTSTR pszClassName; + + CDirectXFullScreen( const CDirectXFullScreen& ); // no implementation + void operator=( const CDirectXFullScreen& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/DirectXWindow.cxx b/stella/src/ui/win32/DirectXWindow.cxx new file mode 100644 index 000000000..d37633a33 --- /dev/null +++ b/stella/src/ui/win32/DirectXWindow.cxx @@ -0,0 +1,1416 @@ +// +// StellaX +// Jeff Miller 05/15/2000 +// +#include "pch.hxx" +#include "DirectXWindow.hxx" +#include "resource.h" + +#include "DirectInput.hxx" + +#include "bspf.hxx" +#include "Console.hxx" +#include "Event.hxx" +#include "MediaSrc.hxx" + +/* + +CDirectXWindow + +Handles Windowed DirectX +It's responsible for updating all DirectInput devices as well + +The message loop (See the Run method) will do the processing in +idle time + +*/ + +// BUGBUG: Colors are off in 8bit mode +// BUGBUG: 16-bit mode not supported +// BUGBUG: Should detect invalid video bpp modes +// BUGBUG: Re-add 640x480 forced mode +// BUGBUG: F11 switch doesn't work (note: should be Alt-Enter) +// BUGBUG: Maximize, minimize, restore -- width will be off +// TODO: Add cool caption +// BUGBUG: Error in Initialize causes AV & not showing error dialog + +#define WIDTH_FACTOR 2 + +LPCTSTR CDirectXWindow::pszClassName = _T("StellaXClass"); + +static inline void ClientToScreen( + HWND hWnd, + LPRECT lpRect + ) +{ + ASSERT( ::IsWindow( hWnd ) ); + ::ClientToScreen( hWnd, (LPPOINT)lpRect ); + ::ClientToScreen( hWnd, ((LPPOINT)lpRect)+1 ); +} + +CDirectXWindow::CDirectXWindow( + const CGlobalData& rGlobalData, + const Console* pConsole, + Event& rEvent + ) : \ + m_rGlobalData( rGlobalData ), + m_pConsole( pConsole ), + m_rEvent( rEvent ), + + m_fReady( FALSE ), + m_fWindowed( TRUE ), + m_fActive( FALSE ), + + m_piDD( NULL ), + m_piDDSPrimary( NULL ), + m_piDDSBack( NULL ), + m_piDDPalette( NULL ), + + m_hwnd( NULL ), + + m_pDirectMouse( NULL ), + m_pDirectJoystick( NULL ), + m_pDirectKeyboard( NULL ) +{ + TRACE("CDirectXWindow::CDirectXWindow"); + + m_sizeGame.cx = m_pConsole->mediaSource().width(); + m_sizeGame.cy = m_pConsole->mediaSource().height(); + TRACE("m_sizeGame cx=%ld, cy=%ld", m_sizeGame.cx, m_sizeGame.cy); + + // + // Initialize the pixel data table + // + + for (int j = 0; j < 256; ++j) + { + m_rgbPixelDataTable[j] = j | (j << 8); + } + +} + +CDirectXWindow::~CDirectXWindow +( +) +{ + TRACE("CDirectXWindow::~CDirectXWindow"); + + ReleaseAllObjects(); + + delete m_pDirectMouse; + m_pDirectMouse = NULL; + + delete m_pDirectJoystick; + m_pDirectJoystick = NULL; + + delete m_pDirectKeyboard; + m_pDirectKeyboard = NULL; + + if (m_piDDPalette) + { + m_piDDPalette->Release(); + m_piDDPalette = NULL; + } + + if ( m_piDD ) + { + m_piDD->Release( ); + m_piDD = NULL; + } + + ::UnregisterClass( pszClassName, m_rGlobalData.ModuleInstance() ); +} + +HRESULT CDirectXWindow::Initialize( + HWND hwndParent, + LPCTSTR pszTitle + ) +{ + TRACE( "CDirectXWindow::Initialize" ); + + if ( m_hwnd ) + { + return S_OK; + } + + HRESULT hr; + WNDCLASS wc; + RECT rc; + const unsigned int* pPalette = m_pConsole->mediaSource().palette(); + TCHAR pszStellaTitle[ MAX_PATH ]; + + wc.lpszClassName = pszClassName; + wc.lpfnWndProc = StaticWindowProc; + wc.style = CS_HREDRAW | CS_VREDRAW; // CS_OWNDC + wc.hInstance = m_rGlobalData.ModuleInstance(); + wc.hIcon = ::LoadIcon( m_rGlobalData.ModuleInstance(), + MAKEINTRESOURCE( IDI_APP ) ); + wc.hCursor = ::LoadCursor( NULL, IDC_ARROW ); + wc.hbrBackground = (HBRUSH)::GetStockObject( NULL_BRUSH ); + wc.lpszMenuName = NULL; // TODO + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + if ( ! ::RegisterClass( &wc ) ) + { + TRACE( "RegisterClass failed" ); + + hr = HRESULT_FROM_WIN32( ::GetLastError() ); + goto cleanup; + } + + // + // Create and show the main window + // + + lstrcpy( pszStellaTitle, pszTitle ); + lstrcat( pszStellaTitle, _T(" - StellaX") ); + + m_hwnd = ::CreateWindowEx( 0, + pszClassName, + pszStellaTitle, + // WS_OVERLAPPEDWINDOW, + WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | + WS_MINIMIZEBOX | WS_MAXIMIZEBOX | + WS_BORDER, + 0, 0, 0, 0, + NULL, + NULL, + m_rGlobalData.ModuleInstance(), + this ); + if ( m_hwnd == NULL ) + { + TRACE( "CreateWindow failed" ); + hr = HRESULT_FROM_WIN32( GetLastError() ); + MessageBox( m_rGlobalData.ModuleInstance(), NULL, IDS_CW_FAILED ); + goto cleanup; + } + + // + // Move the window into position + // + + ::SetRect( &rc, + 0, 0, + m_sizeGame.cx * WIDTH_FACTOR, + m_sizeGame.cy ); + ::AdjustWindowRectEx( &rc, + GetWindowStyle( m_hwnd ), + ::GetMenu( m_hwnd ) != NULL, + GetWindowExStyle( m_hwnd ) ); + + ::MoveWindow( m_hwnd, + CW_USEDEFAULT, CW_USEDEFAULT, + rc.right - rc.left, + rc.bottom - rc.top, + FALSE ); + + ::SetFocus( m_hwnd ); + ::ShowWindow( m_hwnd, SW_SHOW ); + ::UpdateWindow( m_hwnd ); + + // + // m_rectWin will hold the window size/pos for switching modes + // + + ::GetWindowRect( m_hwnd, &m_rcWindow ); + + // TODO: Load accelerators + + // + // Initialize DirectDraw + // + + hr = ::CoCreateInstance( CLSID_DirectDraw, + NULL, + CLSCTX_SERVER, + IID_IDirectDraw, + (void**)&m_piDD ); + if ( FAILED(hr) ) + { + TRACE( "CCI DirectDraw failed" ); + goto cleanup; + } + + // + // Initialize it + // This method takes the driver GUID parameter that the DirectDrawCreate + // function typically uses (NULL is active display driver) + // + + hr = m_piDD->Initialize( NULL ); + if ( FAILED(hr) ) + { + TRACE( "DDraw::Initialize failed, hr=%x", hr ); + goto cleanup; + } + + // + // Get the best video mode for game width + // + + m_sizeFS.cx = 0; + m_sizeFS.cy = 0; + + hr = m_piDD->EnumDisplayModes( 0, NULL, this, EnumModesCallback ); + if ( FAILED(hr) ) + { + TRACE( "EnumDisplayModes failed" ); + goto cleanup; + } + + if ( m_sizeFS.cx == 0 || m_sizeFS.cy == 0 ) + { + TRACE("No good video mode found"); + hr = E_INVALIDARG; + goto cleanup; + } + + // m_sizeFS.cx = 640; m_sizeFS.cy = 480; + TRACE( "Video Mode Selected: %d x %d", m_sizeFS.cx, m_sizeFS.cy ); + + // + // Create palette + // + + int i; + for ( i = 0; i < 256; ++i ) + { + m_rgpe[i].peRed = (BYTE)( (pPalette[i] & 0x00FF0000) >> 16 ); + m_rgpe[i].peGreen = (BYTE)( (pPalette[i] & 0x0000FF00) >> 8 ); + m_rgpe[i].peBlue = (BYTE)( (pPalette[i] & 0x000000FF) ); + m_rgpe[i].peFlags = 0; + } + + // + // Initialize surfaces + // + + hr = InitSurfaces( ); + if ( FAILED(hr) ) + { + TRACE( "InitSurfaces failed, hr=%x", hr ); + goto cleanup; + } + + // + // Initialize DirectInput + // + + m_pDirectMouse = new CDirectMouse( m_hwnd ); + if (m_pDirectMouse == NULL) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + hr = m_pDirectMouse->Initialize(); + if ( FAILED(hr) ) + { + TRACE( "Directmouse initialzie failed, hr = %X\n", hr ); + goto cleanup; + } + + if ( m_rGlobalData.DisableJoystick() ) + { + m_pDirectJoystick = new CDisabledJoystick( m_hwnd ); + } + else + { + m_pDirectJoystick = new CDirectJoystick( m_hwnd ); + } + if (m_pDirectJoystick == NULL) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + hr = m_pDirectJoystick->Initialize( ); + if ( FAILED(hr) ) + { + TRACE( "joystick initialzie failed, hr=%X", hr ); + goto cleanup; + } + + m_pDirectKeyboard = new CDirectKeyboard( m_hwnd ); + if (m_pDirectKeyboard == NULL) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + hr = m_pDirectKeyboard->Initialize(); + if ( FAILED(hr) ) + { + TRACE( "keyboard init failed. hr=%X", hr ); + goto cleanup; + } + +cleanup: + + if ( FAILED(hr) ) + { + ReleaseAllObjects(); + SendMessage( m_hwnd, WM_CLOSE, 0, 0 ); + } + + return hr; +} + +HRESULT WINAPI CDirectXWindow::EnumModesCallback( + LPDDSURFACEDESC lpDDSurfaceDesc, + LPVOID lpContext + ) +{ + CDirectXWindow* pThis = (CDirectXWindow*)lpContext; + + DWORD dwWidthReq = pThis->m_sizeGame.cx * WIDTH_FACTOR; + DWORD dwHeightReq = pThis->m_sizeGame.cy; + + DWORD dwWidth = lpDDSurfaceDesc->dwWidth; + DWORD dwHeight = lpDDSurfaceDesc->dwHeight; + DWORD dwRGBBitCount = lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount; + + // + // must be 8 bit mode + // + + if ( dwRGBBitCount != 8 ) + { + return DDENUMRET_OK; + } + + // + // must be larger then required screen size + // + + if ( dwWidth < dwWidthReq || dwHeight < dwHeightReq ) + { + return DDENUMRET_OK; + } + + if ( pThis->m_sizeFS.cx != 0 && + pThis->m_sizeFS.cy != 0 ) + { + // + // check to see if this is better than the previous choice + // + + if ( ( dwWidth - dwWidthReq ) > ( pThis->m_sizeFS.cx - dwWidthReq ) ) + { + return DDENUMRET_OK; + } + + if ( ( dwHeight - dwHeightReq ) > ( pThis->m_sizeFS.cy - dwHeightReq ) ) + { + return DDENUMRET_OK; + } + } + + // + // use it! + // + + pThis->m_sizeFS.cx = dwWidth; + pThis->m_sizeFS.cy = dwHeight; + + return DDENUMRET_OK; +} + +HRESULT CDirectXWindow::InitSurfaces( + void + ) +{ + if ( m_hwnd == NULL || m_piDD == NULL ) + { + return E_UNEXPECTED; + } + + HRESULT hr; + DDSURFACEDESC ddsd; + DDPIXELFORMAT ddpf; + const unsigned int* pPalette = m_pConsole->mediaSource().palette(); + + if ( m_fWindowed ) + { + // + // Get normal windowed mode + // + + hr = m_piDD->SetCooperativeLevel( m_hwnd, DDSCL_NORMAL ); + if ( hr != DD_OK ) + { + return hr; + } + + // + // Get the dimensions of the viewport and the screen bounds + // + + ::GetClientRect( m_hwnd, &m_rcScreen ); + ::ClientToScreen( m_hwnd, &m_rcScreen ); + TRACE( "m_rcScreen = { %d,%d,%d,%d }", + m_rcScreen.left, m_rcScreen.right, m_rcScreen.top, m_rcScreen.bottom ); + } + else + { + // + // Get exclusive (full screen) mode + // + + hr = m_piDD->SetCooperativeLevel( m_hwnd, + DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ); + if ( hr != DD_OK ) + { + return hr; + } + + hr = m_piDD->SetDisplayMode( m_sizeFS.cx, m_sizeFS.cy, 8 ); + if ( hr != DD_OK ) + { + return hr; + } + + // + // Store the dimensions of the viewport and screen bounds + // + + ::SetRect( &m_rcScreen, 0, 0, m_sizeFS.cx, m_sizeFS.cy ); + TRACE( "m_rcScreen = { %d,%d,%d,%d }", + m_rcScreen.left, m_rcScreen.right, m_rcScreen.top, m_rcScreen.bottom ); + } + + // + // Create the primary surface + // + + ZeroMemory( &ddsd, sizeof(ddsd) ); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + hr = m_piDD->CreateSurface( &ddsd, &m_piDDSPrimary, NULL ); + if ( hr != DD_OK ) + { + return hr; + } + + if ( m_fWindowed ) + { + // + // Create a clipper object + // + + IDirectDrawClipper* piDDC; + + hr = m_piDD->CreateClipper( 0, &piDDC, NULL ); + if ( hr != DD_OK ) + { + return hr; + } + + // + // Associate the clippper with the window + // + + (void)piDDC->SetHWnd( 0, m_hwnd ); + (void)m_piDDSPrimary->SetClipper( piDDC ); + (void)piDDC->Release(); + piDDC = NULL; + } + + // + // Set palette (if necessary) + // + + ddpf.dwSize = sizeof( ddpf ); + if ( ( m_piDDSPrimary->GetPixelFormat( &ddpf ) == DD_OK ) && + ( ddpf.dwFlags & DDPF_PALETTEINDEXED8 ) ) + { + + hr = m_piDD->CreatePalette( DDPCAPS_8BIT, + m_rgpe, + &m_piDDPalette, + NULL ); + if ( FAILED(hr) ) + { + TRACE( "IDD::CreatePalette failed, hr=%X", hr); + return hr; + } + + + hr = m_piDDSPrimary->SetPalette( m_piDDPalette ); + if ( FAILED(hr) ) + { + TRACE( "SetPalette failed, hr=%x", hr ); + return hr; + } + } + + // + // Get the back buffer + // + + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + ddsd.dwWidth = m_sizeGame.cx; + ddsd.dwHeight = m_sizeGame.cy; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + hr = m_piDD->CreateSurface( &ddsd, &m_piDDSBack, NULL ); + if ( hr != DD_OK ) + { + return hr; + } + + // + // Erase all surfaces + // + + DDBLTFX ddbltfx; + ZeroMemory( &ddbltfx, sizeof(ddbltfx) ); + ddbltfx.dwSize = sizeof(ddbltfx); + ddbltfx.dwFillColor = 0; + + (void)m_piDDSBack->Blt( NULL, NULL, NULL, + DDBLT_COLORFILL | DDBLT_WAIT, + &ddbltfx ); + + return S_OK; +} + +void CDirectXWindow::ReleaseAllObjects( + void + ) +{ + TRACE( "CDirectXWindow::ReleaseAllObjects" ); + + if ( m_piDD ) + { + (void)m_piDD->SetCooperativeLevel( m_hwnd, DDSCL_NORMAL ); + } + + if ( m_piDDSBack ) + { + (void)m_piDDSBack->Release(); + m_piDDSBack = NULL; + } + + if ( m_piDDSPrimary ) + { + (void)m_piDDSPrimary->SetPalette( NULL ); + + (void)m_piDDSPrimary->Release(); + m_piDDSPrimary = NULL; + } + +} + + + +// BUGBUG: This class should derive from CWnd +// (fix up CWnd using this code) + +LRESULT CALLBACK CDirectXWindow::StaticWindowProc( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + CDirectXWindow* pThis = NULL; + + if ( uMsg == WM_CREATE ) + { + pThis = reinterpret_cast( + reinterpret_cast( lParam )->lpCreateParams ); + + ::SetWindowLong( hwnd, + GWL_USERDATA, + reinterpret_cast( pThis ) ); + } + else + { + pThis = reinterpret_cast( + ::GetWindowLong( hwnd, GWL_USERDATA ) ); + } + + if ( pThis ) + { + // + // Most handled messages return 0, but some return a different + // value + // + + LRESULT lResult = 0; + + if ( pThis->WndProc( uMsg, wParam, lParam, &lResult ) ) + { + // + // Handled message + // + + return lResult; + } + } + + // + // Unhandled message + // + + return ::DefWindowProc( hwnd, uMsg, wParam, lParam ); +} + +BOOL CDirectXWindow::WndProc( + UINT uMsg, + WPARAM wParam, + LPARAM lParam, + LRESULT* plResult // *plResult == 0 by default + ) +{ + switch ( uMsg ) + { + case WM_ACTIVATE: + // + // Pause if minimized + // + m_fActive = !( HIWORD(wParam) ); + TRACE( "WM_ACTIVATE: m_fActive = %d", m_fActive ); + break; + + // case WM_COMMAND: + // (menu commands) + + case WM_DESTROY: + ReleaseAllObjects( ); + ::PostQuitMessage( 0 ); + break; + + case WM_GETMINMAXINFO: + if ( m_fWindowed ) + { + MINMAXINFO* pmmi = (MINMAXINFO*)lParam; + + RECT rcWin; + ::GetWindowRect( m_hwnd, &rcWin ); + RECT rcCli; + ::GetClientRect( m_hwnd, &rcCli ); + + pmmi->ptMinTrackSize.x = ( rcWin.right - rcWin.left - rcCli.right ) + m_sizeGame.cx * WIDTH_FACTOR; + pmmi->ptMinTrackSize.y = ( rcWin.bottom - rcWin.top - rcCli.bottom ) + m_sizeGame.cy; + + pmmi->ptMaxTrackSize.x = pmmi->ptMinTrackSize.x * 2; + pmmi->ptMaxTrackSize.y = pmmi->ptMinTrackSize.y * 2; + } + break; + + + case WM_KEYDOWN: + switch ( (int)wParam ) + { + case VK_ESCAPE: + // + // Escape = Exit + // + ::PostMessage( m_hwnd, WM_CLOSE, 0, 0 ); + return TRUE; + + case VK_F11: + // + // F11 = toggle fullscreen/windowed + // + if ( m_fActive && m_fReady ) + { + if ( m_fWindowed ) + { + ::GetWindowRect( m_hwnd, &m_rcWindow ); + } + + ChangeCoopLevel( ); + } + break; + + + case VK_F12: + { + // + // F12 = write screenshot + // + + TCHAR pszFileName[ MAX_PATH ]; + int i; + + for ( i = 0; i < 99 ; ++i ) + { + wsprintf( pszFileName, _T("stella%02d.bmp"), i ); + if ( ::GetFileAttributes( pszFileName ) == 0xFFFFFFFF ) + { + break; + } + } + +// TODO: Get the BMP writing code added to the core +// m_pConsole->mediaSource().writeBmp( pszFileName ); + } + + default: + return FALSE; + } + break; + + case WM_MOVE: + // + // Retrieve the window position after a move + // + + if ( m_fActive && m_fReady && m_fWindowed ) + { + if ( ! ::IsIconic( m_hwnd ) ) + { + ::GetWindowRect( m_hwnd, &m_rcWindow ); + + ::GetClientRect( m_hwnd, &m_rcScreen ); + ClientToScreen( m_hwnd, &m_rcScreen ); + TRACE( "m_rcScreen = { %d,%d,%d,%d }", + m_rcScreen.left, m_rcScreen.right, m_rcScreen.top, m_rcScreen.bottom ); + + } + } + return FALSE; + + case WM_PAINT: + // + // Update the screen if we need to refresh + // + + if ( m_fWindowed && m_fReady ) + { + // + // If we are in windowed mode, perform a blit + // + + HRESULT hr; + + for ( ; ; ) + { + hr = m_piDDSPrimary->Blt( &m_rcScreen, // destrect + m_piDDSBack, + NULL, + DDBLT_WAIT, + NULL ); + if ( hr == DD_OK ) + { + break; + } + + if ( hr == DDERR_SURFACELOST ) + { + hr = m_piDDSPrimary->Restore(); + if ( hr != DD_OK ) + { + break; + } + } + if ( hr != DDERR_WASSTILLDRAWING ) + { + break; + } + } + } + return FALSE; + + case WM_PALETTECHANGED: + TRACE( "WM_PALETTECHANGED from dxwin" ); + // + // Check to see if we caused this + // + + if ( (HWND)wParam == m_hwnd ) + { + if ( m_piDDSPrimary && m_piDDPalette ) + { + // (void)m_piDDSPrimary->SetPalette( m_piDDPalette ); + } + } + break; + + case WM_QUERYNEWPALETTE: + TRACE( "WM_QUERYNEWPALETTE from dxwin" ); + if ( m_piDDSPrimary && m_piDDPalette ) + { + // (void)m_piDDSPrimary->SetPalette( m_piDDPalette ); + + *plResult = TRUE; + } + break; + + case WM_SETCURSOR: + if ( m_fActive && m_fReady && ! m_fWindowed ) + { + ::SetCursor( NULL ); + + *plResult = TRUE; + } + else + { + return FALSE; + } + break; + + case WM_SIZE: + m_fActive = ( wParam != SIZE_MAXHIDE && + wParam != SIZE_MINIMIZED ); + if ( ! ::IsIconic( m_hwnd ) ) + { + // ::GetWindowRect( m_hwnd, &m_rcWindow ); + + ::GetClientRect( m_hwnd, &m_rcScreen ); + ClientToScreen( m_hwnd, &m_rcScreen ); + TRACE( "m_rcScreen = { %d,%d,%d,%d }", + m_rcScreen.left, m_rcScreen.right, m_rcScreen.top, m_rcScreen.bottom ); + + } + + return FALSE; + + default: + // + // Unhandled message + // + + return FALSE; + } + + // + // Handled message + // + + return TRUE; +} + +DWORD CDirectXWindow::Run( + void + ) +{ + TRACE("CDirectXWindow::Run"); + + // + // Get the initial tick count + // + + UINT uFrameCount = 0; + + unsigned __int64 uiStartRun; + ::QueryPerformanceCounter( (LARGE_INTEGER*)&uiStartRun ); + + // + // Find out how many ticks occur per second + // + + unsigned __int64 uiCountsPerSecond; + ::QueryPerformanceFrequency( (LARGE_INTEGER*)&uiCountsPerSecond ); + + const unsigned __int64 uiCountsPerFrame = + ( uiCountsPerSecond / m_rGlobalData.DesiredFrameRate() ); + + TRACE( "uiCountsPerSecond=%ld", uiCountsPerSecond ); + TRACE( "uiCountsPerFrame = %ld", uiCountsPerFrame ); + + unsigned __int64 uiFrameStart; + unsigned __int64 uiFrameCurrent; + + // + // Get Stella's media source + // + + MediaSource& rMediaSource = m_pConsole->mediaSource(); + + // + // Main message loop + // + + m_fReady = TRUE; + + MSG msg; + + for ( ; ; ) + { + if ( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) + { + if ( ::GetMessage( &msg, NULL, 0, 0 ) <= 0 ) + { + break; + } + + ::TranslateMessage( &msg ); + ::DispatchMessage( &msg ); + + continue; + } + + if ( m_fActive && m_fReady ) + { + HRESULT hr; + + // + // idle time -- do stella updates + // + + ++uFrameCount; + + ::QueryPerformanceCounter( (LARGE_INTEGER*)&uiFrameStart ); + + // + // ask media source to prepare the next frame + // + + rMediaSource.update(); + + // + // update the display + // + + if ( ! UpdateDisplay( rMediaSource ) ) + { + // fatal error! + + ::MessageBox(NULL, "Fatal error updating display", "Error", MB_OK); + ::PostQuitMessage( 0 ); + break; + } + + for ( ; ; ) + { + hr = m_piDDSPrimary->Blt( &m_rcScreen, // destrect + m_piDDSBack, + NULL, + DDBLT_WAIT, + NULL ); + if ( hr == DD_OK ) + { + break; + } + if ( hr == DDERR_SURFACELOST ) + { + hr = m_piDDSPrimary->Restore(); + if ( hr != DD_OK ) + { + break; + } + } + if ( hr != DDERR_WASSTILLDRAWING ) + { + break; + } + } + + // + // Check for keyboard, mouse, joystick input + // + + UpdateEvents(); + + // + // waste time to to meet desired frame rate + // + + for ( ; ; ) + { + ::QueryPerformanceCounter( (LARGE_INTEGER*)&uiFrameCurrent ); + + if ( ( uiFrameCurrent - uiFrameStart ) >= uiCountsPerFrame ) + { + break; + } + } + + continue; + } + + // + // Sleep if we don't have anything to do + // + + ::WaitMessage(); + + } // for ( ; ; ) + + // Main message loop done + + if ( m_rGlobalData.ShowFPS() ) + { + // get number of scanlines in last frame + + uInt32 uScanLines = rMediaSource.scanlines(); + + // Get the final tick count + + unsigned __int64 uiEndRun; + ::QueryPerformanceCounter( (LARGE_INTEGER*)&uiEndRun ); + + // Get number of ticks + + DWORD secs = (DWORD)( ( uiEndRun - uiStartRun ) / uiCountsPerSecond ); + + DWORD fps = (secs == 0) ? 0 : (uFrameCount / secs); + + TCHAR pszBuf[1024]; + wsprintf( pszBuf, _T("Frames drawn: %ld\nFPS: %ld\nScanlines in last frame: %ld\n"), + uFrameCount, + fps, + uScanLines ); + MessageBox( NULL, pszBuf, _T("Statistics"), MB_OK ); + } + + return msg.wParam; +} + +// --------------------------------------------------------------------------- +// Event Handling +// VK_* is defined in winuser.h + +struct KeyEventStruct +{ + int nVirtKey; + int nAsyncVirtKey; + Event::Type eventCode; +}; + + +static KeyEventStruct KeyEventMap[] = +{ + /* -------------------------------------------------------------------- */ + /* left keypad */ + + { DIK_1, '1', Event::KeyboardZero1 }, + { DIK_2, '2', Event::KeyboardZero2 }, + { DIK_3, '3', Event::KeyboardZero3 }, + { DIK_Q, 'Q', Event::KeyboardZero4 }, + { DIK_W, 'W', Event::KeyboardZero5 }, + { DIK_E, 'E', Event::KeyboardZero6 }, + { DIK_A, 'A', Event::KeyboardZero7 }, + { DIK_S, 'S', Event::KeyboardZero8 }, + { DIK_D, 'D', Event::KeyboardZero9 }, + { DIK_Z, 'Z', Event::KeyboardZeroStar }, + { DIK_X, 'X', Event::KeyboardZero0 }, + { DIK_C, 'C', Event::KeyboardZeroPound }, + + /* -------------------------------------------------------------------- */ + /* right keypad */ + + { DIK_8, '8', Event::KeyboardOne1 }, + { DIK_9, '9', Event::KeyboardOne2 }, + { DIK_0, '0', Event::KeyboardOne3 }, + { DIK_I, 'I', Event::KeyboardOne4 }, + { DIK_O, 'O', Event::KeyboardOne5 }, + { DIK_P, 'P', Event::KeyboardOne6 }, + { DIK_K, 'K', Event::KeyboardOne7 }, + { DIK_L, 'L', Event::KeyboardOne8 }, + { DIK_SEMICOLON,';', Event::KeyboardOne9 }, + { DIK_COMMA, ',', Event::KeyboardOneStar }, + { DIK_PERIOD, '.', Event::KeyboardOne0 }, + { DIK_SLASH, '/', Event::KeyboardOnePound }, + + /* -------------------------------------------------------------------- */ + /* left joystick */ + + { DIK_DOWN, VK_DOWN, Event::JoystickZeroDown }, + { DIK_UP, VK_UP, Event::JoystickZeroUp }, + { DIK_LEFT, VK_LEFT, Event::JoystickZeroLeft }, + { DIK_RIGHT, VK_RIGHT, Event::JoystickZeroRight }, + { DIK_SPACE, VK_SPACE, Event::JoystickZeroFire }, + + /* left joystick (alt.) */ + + { DIK_W, 'W', Event::JoystickZeroUp }, + { DIK_S, 'S', Event::JoystickZeroDown }, + { DIK_A, 'A', Event::JoystickZeroLeft }, + { DIK_D, 'D', Event::JoystickZeroRight }, + { DIK_TAB, VK_TAB, Event::JoystickZeroFire }, + + /* I added this one (for my powerramp joystick) */ + + { DIK_LCONTROL, VK_CONTROL, Event::JoystickZeroFire }, + + /* left joystick booster-grip */ + + { DIK_Z, 'Z', Event::BoosterGripZeroTrigger }, + { DIK_X, 'X', Event::BoosterGripZeroBooster }, + + /* left joystick booster-grip (alt.) */ + + { DIK_1, '1', Event::BoosterGripZeroTrigger }, + { DIK_2, '2', Event::BoosterGripZeroBooster }, + + /* -------------------------------------------------------------------- */ + /* right joystick */ + + { DIK_L, 'L', Event::JoystickOneDown }, + { DIK_O, 'O', Event::JoystickOneUp }, + { DIK_K, 'K', Event::JoystickOneLeft }, + { DIK_SEMICOLON,';', Event::JoystickOneRight }, + { DIK_J, 'J', Event::JoystickOneFire }, + + /* right joystick booster-grip */ + + { DIK_N, 'N', Event::BoosterGripOneTrigger }, + { DIK_M, 'M', Event::BoosterGripOneBooster }, + + /* -------------------------------------------------------------------- */ + /* console controls */ + + { DIK_F1, VK_F1, Event::ConsoleSelect }, + { DIK_F2, VK_F2, Event::ConsoleReset }, + { DIK_F3, VK_F3, Event::ConsoleColor }, + { DIK_F4, VK_F4, Event::ConsoleBlackWhite }, + { DIK_F5, VK_F5, Event::ConsoleLeftDifficultyA }, + { DIK_F6, VK_F6, Event::ConsoleLeftDifficultyB }, + { DIK_F7, VK_F7, Event::ConsoleRightDifficultyA }, + { DIK_F8, VK_F8, Event::ConsoleRightDifficultyB } +}; + +void CDirectXWindow::UpdateEvents( + void + ) +{ + if ( ! ( m_fReady && m_fActive ) ) + { + return; + } + + // + // I do this because an event may appear multiple times in the map + // and I don't want to undo a set i may have done earlier in the loop + // + + const int nEventCount = Event::LastType; + long rgEventState[ nEventCount ]; + ZeroMemory( rgEventState, nEventCount * sizeof(long) ); + + int i; + + // + // Update keyboard + // + + if (m_pDirectKeyboard->Update() == S_OK) + { + int nSize = _countof(KeyEventMap); + + for (i = 0; i < nSize; ++i) + { + rgEventState[KeyEventMap[i].eventCode] |= + m_pDirectKeyboard->IsButtonPressed(KeyEventMap[i].nVirtKey); + } + } + else + { + // Fallback -- if Keyboard update failed (most likely due to + // DirectInput not being available), then use the old async + + int nSize = _countof(KeyEventMap); + + for (i = 0; i < nSize; ++i) + { + rgEventState[KeyEventMap[i].eventCode] |= + (::GetAsyncKeyState(KeyEventMap[i].nAsyncVirtKey) ? 1: 0); + } + } + + // + // Update joystick + // + + if (m_pDirectJoystick->Update() == S_OK) + { + rgEventState[Event::JoystickZeroFire] |= + m_pDirectJoystick->IsButtonPressed(0); + + LONG x; + LONG y; + m_pDirectJoystick->GetPos( &x, &y ); + + if (x < 0) + { + rgEventState[Event::JoystickZeroLeft] = 1; + } + else if (x > 0) + { + rgEventState[Event::JoystickZeroRight] = 1; + } + if (y < 0) + { + rgEventState[Event::JoystickZeroUp] = 1; + } + else if (y > 0) + { + rgEventState[Event::JoystickZeroDown] = 1; + } + } + + // + // Update mouse + // + + if (m_pDirectMouse->Update() == S_OK) + { + // NOTE: Mouse::GetPos returns a value from 0..999 + + LONG x; + m_pDirectMouse->GetPos( &x, NULL ); + + // Mouse resistance is measured between 0...1000000 + + rgEventState[ m_rGlobalData.PaddleResistanceEvent() ] = (999-x)*1000; + + rgEventState[ m_rGlobalData.PaddleFireEvent() ] |= m_pDirectMouse->IsButtonPressed(0); + } + + // + // Write new event state + // + + for (i = 0; i < nEventCount; ++i) + { + m_rEvent.set( (Event::Type)i, rgEventState[i] ); + } +} + +// --------------------------------------------------------------------------- +// Display Update + +BOOL CDirectXWindow::UpdateDisplay( + MediaSource& rMediaSource + ) +{ + HRESULT hr; + + if ( ! ( m_fActive && m_fReady ) ) + { + return TRUE; + } + + if ( m_piDDSBack == NULL ) + { + return FALSE; + } + + + const BYTE* current = rMediaSource.currentFrameBuffer(); + const BYTE* previous = rMediaSource.previousFrameBuffer(); + + // + // acquire pointer to linear video ram + // + + DDSURFACEDESC ddsd; + ZeroMemory( &ddsd, sizeof(ddsd) ); + ddsd.dwSize = sizeof(ddsd); + + hr = m_piDDSBack->Lock( NULL, + &ddsd, + /* DDLOCK_SURFACEMEMORYPTR | */ DDLOCK_WAIT, + NULL ); + // BUGBUG: Check for error + + + BYTE* pbBackBytes = (BYTE*)ddsd.lpSurface; + + register int y; + for ( y = 0; y < m_sizeGame.cy; ++y ) + { + DWORD oy = y * m_sizeGame.cx; + DWORD pitchY = y * ddsd.lPitch; + + register int x; + for ( x = 0; x < m_sizeGame.cx; ++x ) + { + DWORD o = oy + x; + BYTE v = current[ o ]; + if ( v == previous[ o ] ) + { + continue; + } + + BYTE c = m_rgbPixelDataTable[ v ]; + DWORD pos; + + switch ( ddsd.ddpfPixelFormat.dwRGBBitCount ) + { + case 32: + // x << 2 is times 4 ( 32 bit is 4 bytes per pixel ) + + pos = pitchY + ( x << 2 ); + + pbBackBytes[ pos + 0 ] = m_rgpe[ c ].peBlue; + pbBackBytes[ pos + 1 ] = m_rgpe[ c ].peGreen; + pbBackBytes[ pos + 2 ] = m_rgpe[ c ].peRed; + pbBackBytes[ pos + 3 ] = 0; //alpha + break; + + case 24: + // 3 bytes per pixel + + pos = pitchY + ( x * 3 ); + + pbBackBytes[ pos + 0 ] = m_rgpe[ c ].peBlue; + pbBackBytes[ pos + 1 ] = m_rgpe[ c ].peGreen; + pbBackBytes[ pos + 2 ] = m_rgpe[ c ].peRed; + break; + + // case 16: pos = pitchY + ( x << 1 ) + + case 8: + pos = pitchY + x; + + pbBackBytes[ pos ] = m_rgbPixelDataTable[ current[ y * m_sizeGame.cx + x ] ]; + break; + } + } + } + + (void)m_piDDSBack->Unlock( ddsd.lpSurface ); + +#ifdef _DEBUG + HDC hdc; + if ( m_piDDSBack->GetDC( &hdc ) == DD_OK ) + { + ::SetBkColor( hdc, RGB( 255, 255, 0 ) ); + ::TextOut( hdc, 5, 5, _T("DEBUG MODE"), 10 ); + m_piDDSBack->ReleaseDC( hdc ); + } +#endif + + return TRUE; +} + +HRESULT CDirectXWindow::ChangeCoopLevel( + void + ) +{ + TRACE( "ChangeCoopLevel m_fWindowed was %d", m_fWindowed ); + + HRESULT hr; + + if ( m_hwnd == NULL ) + { + return S_OK; + } + + m_fReady = FALSE; + + m_fWindowed = !m_fWindowed; + + ReleaseAllObjects(); + + if ( m_fWindowed ) + { + ::SetWindowPos( m_hwnd, HWND_NOTOPMOST, + m_rcWindow.left, m_rcWindow.top, + m_rcWindow.right-m_rcWindow.left, + m_rcWindow.bottom-m_rcWindow.top, + SWP_SHOWWINDOW ); + } + + hr = InitSurfaces( ); + + m_fReady = TRUE; + + return hr; +} diff --git a/stella/src/ui/win32/DirectXWindow.hxx b/stella/src/ui/win32/DirectXWindow.hxx new file mode 100644 index 000000000..3c54c8d73 --- /dev/null +++ b/stella/src/ui/win32/DirectXWindow.hxx @@ -0,0 +1,91 @@ +// +// StellaX +// Jeff Miller 05/15/2000 +// +#ifndef DXWIN_H +#define DXWIN_H +#pragma once + +class Console; +class MediaSource; + +class CDirectInput; + +#include "Event.hxx" +#include "GlobalData.hxx" + +class CDirectXWindow +{ +public: + + CDirectXWindow( const CGlobalData& rGlobalData, + const Console* pConsole, + Event& rEvent ); + ~CDirectXWindow(); + + HRESULT Initialize( HWND hwndParent, LPCSTR pszTitle ); + + DWORD Run(); + + operator HWND( void ) const { return m_hwnd; } + +private: + + void ReleaseAllObjects( void ); + HRESULT InitSurfaces( void ); + HRESULT ChangeCoopLevel( void ); + + HWND m_hwnd; + + BOOL m_fReady; + BOOL m_fWindowed; + BOOL m_fActive; + + RECT m_rcWindow; + RECT m_rcScreen; + + IDirectDraw* m_piDD; + IDirectDrawSurface* m_piDDSPrimary; + IDirectDrawSurface* m_piDDSBack; + + SIZE m_sizeFS; + + static HRESULT WINAPI EnumModesCallback( LPDDSURFACEDESC lpDDSurfaceDesc, + LPVOID lpContext); + + static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ); + BOOL WndProc( UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plResult ); + + void UpdateEvents(); + BOOL UpdateDisplay( MediaSource& rMediaSource ); + + PALETTEENTRY m_rgpe[256]; + + // Stella objects + + const Console* m_pConsole; + Event& m_rEvent; + + const CGlobalData& m_rGlobalData; + SIZE m_sizeGame; + BYTE m_rgbPixelDataTable[256]; + + + // + // DirectX + // + + IDirectDrawPalette* m_piDDPalette; + + CDirectInput* m_pDirectMouse; + CDirectInput* m_pDirectJoystick; + CDirectInput* m_pDirectKeyboard; + + static LPCTSTR pszClassName; + + CDirectXWindow( const CDirectXWindow& ); // no implementation + void operator=( const CDirectXWindow& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/DocPage.cxx b/stella/src/ui/win32/DocPage.cxx new file mode 100644 index 000000000..0aac1991d --- /dev/null +++ b/stella/src/ui/win32/DocPage.cxx @@ -0,0 +1,153 @@ +// +// StellaX +// Jeff Miller 05/12/2000 +// +#include "pch.hxx" +#include "DocPage.hxx" +#include "resource.h" + +#include + +static LPCTSTR g_ctszDocFile = _T("\\docs\\stella.pdf"); + +const CLSID CPDFControl::clsid = { 0xCA8A9780, 0x280D, 0x11CF, + { 0xA2, 0x4D, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00 } }; + +void CPDFControl::OnInitialUpdate( + void + ) +{ + // This will do a control.src = "c:\temp\a.pdf" + + IDispatch* piDispatch = NULL; + if (GetDispInterface(&piDispatch) != S_OK) + return; + + TCHAR tszDir[MAX_PATH + 1]; + ::GetCurrentDirectory(MAX_PATH, tszDir); + lstrcat(tszDir, g_ctszDocFile); + + VARIANT var; + VariantInit(&var); + var.vt = VT_BSTR; + +#if defined(_UNICODE) + BSTR bstr = ::SysAllocString(tszDir); + if (bstr == NULL) + return; +#else + int nLen = ::MultiByteToWideChar(CP_ACP, 0, tszDir, -1, NULL, NULL); + BSTR bstr = ::SysAllocStringLen(NULL, nLen); + if (bstr == NULL) + return; + ::MultiByteToWideChar(CP_ACP, 0, tszDir, -1, bstr, nLen); +#endif + + var.bstrVal = bstr; + + if (PutPropertyByName(L"src", &var) != S_OK) + { + VariantClear(&var); + piDispatch->Release(); + return; + } + + VariantClear(&var); + + // The following features are only in PDF 4.0 + + + VARIANT varRet; + VariantInit(&var); + + // This will do a control.setZoom(90) + + V_VT(&var) = VT_I1; + V_I1(&var) = 90; + + if (Invoke1(L"setZoom", &var, &varRet) != S_OK) + { + VariantClear(&var); + piDispatch->Release(); + return; + } + + VariantClear(&var); + VariantClear(&varRet); + + piDispatch->Release(); +} + +CDocPage::CDocPage( + ) : \ + CPropertyPage(IDD_DOC_PAGE), + m_pHost(NULL) +{ +} + +BOOL CDocPage::OnInitDialog( + HWND hwnd + ) +{ + TCHAR tszDir[MAX_PATH + 1]; + ::GetCurrentDirectory(MAX_PATH, tszDir); + lstrcat(tszDir, g_ctszDocFile); + + // verify file exists + + WIN32_FIND_DATA findFileData; + HANDLE hFind = ::FindFirstFile(tszDir, &findFileData); + if (hFind == INVALID_HANDLE_VALUE) + { + TCHAR tszMsg[MAX_PATH * 2]; + lstrcpy(tszMsg, _T("ERROR: Cannot find ")); + lstrcat(tszMsg, tszDir); + + HWND hwndInst = ::GetDlgItem(hwnd, IDC_INSTRUCTIONS); + ::SetWindowText(hwndInst, tszMsg); + ::ShowWindow(hwndInst, SW_SHOW); + + return TRUE; + } + ::FindClose(hFind); + + m_pHost = new CControlHost(new CPDFControl); + m_pHost->SetHwnd(hwnd); + + if (m_pHost->CreateControl() != S_OK) + { + ::ShowWindow(::GetDlgItem(hwnd, IDC_INSTRUCTIONS), SW_SHOW); + ::ShowWindow(::GetDlgItem(hwnd, IDC_ADOBE), SW_SHOW); + + m_hlAdobe.SubclassDlgItem(hwnd, IDC_ADOBE); + m_hlAdobe.SetURL(_T("http://www.adobe.com/prodindex/acrobat/alternate.html")); + + m_pHost = NULL; + } + + // return FALSE if SetFocus is called + return TRUE; +} + +void CDocPage::OnDestroy( + void + ) +{ + delete m_pHost; +} + +void CDocPage::OnActivate( + UINT state, + HWND hwndActDeact, + BOOL fMinimized + ) +{ + if ( state == WA_ACTIVE && !fMinimized && m_pHost ) + { + HWND hwnd = m_pHost->GetControlHWND(); + if ( hwnd ) + { + ::InvalidateRect( hwnd, NULL, TRUE ); + ::UpdateWindow( hwnd ); + } + } +} diff --git a/stella/src/ui/win32/DocPage.hxx b/stella/src/ui/win32/DocPage.hxx new file mode 100644 index 000000000..d32faa6d0 --- /dev/null +++ b/stella/src/ui/win32/DocPage.hxx @@ -0,0 +1,57 @@ +// +// StellaX +// Jeff Miller 05/12/2000 +// +#ifndef DOCPG_H +#define DOCPG_H +#pragma once + +#include "PropertySheet.hxx" +#include "ControlHost.hxx" +#include "HyperLink.hxx" + +class CPDFControl : public CActiveXControl +{ +public: + + CPDFControl() : CActiveXControl() + { + } + + REFCLSID GetCLSID() { return clsid; } + void OnInitialUpdate(); + +protected: + + static const CLSID clsid; + +private: + + CPDFControl( const CPDFControl& ); // no implementation + void operator=( const CPDFControl& ); // no implementation + +}; + +class CDocPage : public CPropertyPage +{ +public: + + CDocPage(); + +protected: + + virtual BOOL OnInitDialog(HWND hwnd); + virtual void OnDestroy(); + virtual void OnActivate( UINT state, HWND hwndActDeact, BOOL fMinimized ); + +private: + + CHyperLink m_hlAdobe; + CControlHost* m_pHost; + + CDocPage( const CDocPage& ); // no implementation + void operator=( const CDocPage& ); // no implementation + +}; + +#endif diff --git a/stella/src/ui/win32/FileDialog.cxx b/stella/src/ui/win32/FileDialog.cxx new file mode 100644 index 000000000..663a02804 --- /dev/null +++ b/stella/src/ui/win32/FileDialog.cxx @@ -0,0 +1,69 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#include "pch.hxx" +#include "FileDialog.hxx" + +CFileDialog::CFileDialog( + BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs + LPCTSTR lpszDefExt /* = NULL */, + LPCTSTR lpszFileName /* = NULL */, + DWORD dwFlags /* = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT */, + LPCTSTR lpszFilter /* = NULL */, + HWND hwndParent /* = NULL */ + ) +{ + UNUSED_ALWAYS( hwndParent ); + + memset(&m_ofn, 0, sizeof(m_ofn)); // initialize structure to 0/NULL + m_szFileName[0] = '\0'; + m_szFileTitle[0] = '\0'; + + m_bOpenFileDialog = bOpenFileDialog; + + m_ofn.lStructSize = sizeof(m_ofn); + m_ofn.lpstrFile = m_szFileName; + m_ofn.nMaxFile = _countof(m_szFileName); + m_ofn.lpstrDefExt = lpszDefExt; + m_ofn.lpstrFileTitle = (LPTSTR)m_szFileTitle; + m_ofn.nMaxFileTitle = _countof(m_szFileTitle); + m_ofn.Flags = (dwFlags | OFN_EXPLORER); + m_ofn.hInstance = NULL; + + // setup initial file name + if (lpszFileName != NULL) + lstrcpyn(m_szFileName, lpszFileName, _countof(m_szFileName)); + + // Translate filter into commdlg format (lots of \0) + if (lpszFilter != NULL) + { + lstrcpy(m_szFilter, lpszFilter); + LPTSTR pch = m_szFilter; // modify the buffer in place + // MFC delimits with '|' not '\0' + while ((pch = _tcschr(pch, '|')) != NULL) + *pch++ = '\0'; + m_ofn.lpstrFilter = m_szFilter; + // do not call ReleaseBuffer() since the string contains '\0' characters + } +} + +int CFileDialog::DoModal( + void + ) +{ + int nResult; + + if (m_bOpenFileDialog) + { + nResult = ::GetOpenFileName(&m_ofn); + } + else + { + nResult = ::GetSaveFileName(&m_ofn); + } + + return nResult ? nResult : IDCANCEL; +} + + diff --git a/stella/src/ui/win32/FileDialog.hxx b/stella/src/ui/win32/FileDialog.hxx new file mode 100644 index 000000000..65dcb4fd7 --- /dev/null +++ b/stella/src/ui/win32/FileDialog.hxx @@ -0,0 +1,51 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef FILEDLG_H +#define FILEDLG_H +#pragma once + +class CFileDialog +{ +public: + + OPENFILENAME m_ofn; + + CFileDialog(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs + LPCTSTR lpszDefExt = NULL, + LPCTSTR lpszFileName = NULL, + DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, + LPCTSTR lpszFilter = NULL, + HWND hwndParent = NULL); + + virtual int DoModal(); + + LPCTSTR GetPathName() const + { + return m_ofn.lpstrFile; + } + + LPCTSTR GetFileName() const + { + return m_ofn.lpstrFileTitle; + } + +protected: + + BOOL m_bOpenFileDialog; // TRUE for file open, FALSE for file save + + TCHAR m_szFilter[1024]; // filter string + // separate fields with '|', terminate with '||\0' + + TCHAR m_szFileTitle[64]; // contains file title after return + TCHAR m_szFileName[_MAX_PATH]; // contains full path name after return + +private: + + CFileDialog( const CFileDialog& ); // no implementation + void operator=( const CFileDialog& ); // no implementation + +}; + +#endif \ No newline at end of file diff --git a/stella/src/ui/win32/GlobalData.cxx b/stella/src/ui/win32/GlobalData.cxx new file mode 100644 index 000000000..b522c0707 --- /dev/null +++ b/stella/src/ui/win32/GlobalData.cxx @@ -0,0 +1,182 @@ +// +// StellaX +// Jeff Miller 05/06/2000 +// +#include "pch.hxx" +#include "GlobalData.hxx" + +#include "resource.h" + +static LPCTSTR g_pszIniFile = _T(".\\stella.ini"); +static LPCTSTR g_pszIniSection = _T("Options"); + +static LPCTSTR g_pszKeyNameRomPath = _T("RomPath"); +static LPCTSTR g_pszKeyNameFrameRate = _T("FrameRate"); +static LPCTSTR g_pszKeyNameShowFPS = _T("ShowFPS"); +static LPCTSTR g_pszKeyNameMute = _T("Mute"); +static LPCTSTR g_pszKeyNamePaddle = _T("Paddle"); +static LPCTSTR g_pszKeyNameDisableJoystick = _T("DisableJoystick"); +static LPCTSTR g_pszKeyNameAutoSelectVideoMode = _T("AutoSelectVideoMode"); + +BOOL WritePrivateProfileInt( + LPCTSTR lpAppName, // section name + LPCTSTR lpKeyName, // key name + int nValue, + LPCTSTR lpFileName // initialization file + ) +{ + TCHAR psz[ 50 ]; + + _itoa( nValue, psz, 10 ); + + return ::WritePrivateProfileString( lpAppName, + lpKeyName, + psz, + lpFileName ); +} + +CGlobalData::CGlobalData( + HINSTANCE hInstance + ) : \ + m_hInstance(hInstance), + m_fIsModified( FALSE ) +{ + m_pszPathName[0] = _T('\0'); + + // + // Read the ROM directory from the stella.ini file + // default to "ROMS" directory for compatibility with older StellaX + // + + ::GetPrivateProfileString( g_pszIniSection, + g_pszKeyNameRomPath, + _T("ROMS"), + m_pszRomDir, _MAX_PATH, + g_pszIniFile); + + // Read the desired frame rate + + m_nDesiredFrameRate = (int)::GetPrivateProfileInt( g_pszIniSection, + g_pszKeyNameFrameRate, + 60, + g_pszIniFile ); + if (m_nDesiredFrameRate < 1 || m_nDesiredFrameRate > 300) + { + m_nDesiredFrameRate = 60; + } + + // Read ShowFPS + + m_fShowFPS = (BOOL)::GetPrivateProfileInt( g_pszIniSection, + g_pszKeyNameShowFPS, + FALSE, + g_pszIniFile); + + // + // Read Mute + // + + m_fNoSound = (BOOL)::GetPrivateProfileInt( g_pszIniSection, + g_pszKeyNameMute, + FALSE, + g_pszIniFile ); + + // + // Get AutoSelectVideoMode + // + + m_fAutoSelectVideoMode = + (BOOL)::GetPrivateProfileInt( g_pszIniSection, + g_pszKeyNameAutoSelectVideoMode, + TRUE, + g_pszIniFile ); + + // + // Read the Paddle mode + // + + m_nPaddleMode = (int)::GetPrivateProfileInt( g_pszIniSection, + g_pszKeyNamePaddle, + 0, + g_pszIniFile); + if ( m_nPaddleMode < 0 || m_nPaddleMode > 3 ) + { + m_nPaddleMode = 0; + } + + // Read DisableJoystick + + m_fDisableJoystick = (BOOL)::GetPrivateProfileInt( g_pszIniSection, + g_pszKeyNameDisableJoystick, + FALSE, + g_pszIniFile ); + +} + +CGlobalData::~CGlobalData( + ) +{ + // + // Write out settings (if changed) + // + + if ( m_fIsModified ) + { + ::WritePrivateProfileString( g_pszIniSection, + g_pszKeyNameRomPath, + m_pszRomDir, + g_pszIniFile ); + + ::WritePrivateProfileInt( g_pszIniSection, + g_pszKeyNameFrameRate, + m_nDesiredFrameRate, + g_pszIniFile ); + + ::WritePrivateProfileInt( g_pszIniSection, + g_pszKeyNameMute, + m_fNoSound, + g_pszIniFile ); + + ::WritePrivateProfileInt( g_pszIniSection, + g_pszKeyNameAutoSelectVideoMode, + m_fAutoSelectVideoMode, + g_pszIniFile ); + + ::WritePrivateProfileInt( g_pszIniSection, + g_pszKeyNamePaddle, + m_nPaddleMode, + g_pszIniFile ); + + ::WritePrivateProfileInt( g_pszIniSection, + g_pszKeyNameDisableJoystick, + m_fDisableJoystick, + g_pszIniFile ); + } +} + +BOOL CGlobalData::ParseCommandLine( + int argc, + TCHAR** argv + ) +{ + // parse arguments + + for (int i = 1; i < argc; ++i) + { + LPCTSTR ctszArg = argv[i]; + + if (ctszArg && (ctszArg[0] != _T('-'))) + { + // assume this is the start rom name + + lstrcpy( m_pszPathName, ctszArg ); + } + else + { + return FALSE; + } + } + + return TRUE; +} + diff --git a/stella/src/ui/win32/GlobalData.hxx b/stella/src/ui/win32/GlobalData.hxx new file mode 100644 index 000000000..fb191a27d --- /dev/null +++ b/stella/src/ui/win32/GlobalData.hxx @@ -0,0 +1,163 @@ +// +// StellaX +// Jeff Miller 05/06/2000 +// +#ifndef GLOBALS_H +#define GLOBALS_H +#pragma once + +#include "Event.hxx" + +class CConfigPage; + +class CGlobalData +{ + friend CConfigPage; + +public: + + CGlobalData( HINSTANCE hInstance ); + ~CGlobalData( ); + + BOOL ParseCommandLine( int argc, TCHAR* argv[] ); + + int DesiredFrameRate( void ) const + { + return m_nDesiredFrameRate; + } + + // + // Booleans + // + + BOOL ShowFPS( void ) const + { + return m_fShowFPS; + } + + BOOL NoSound() const + { + return m_fNoSound; + } + + BOOL DisableJoystick( void ) const + { + return m_fDisableJoystick; + } + + BOOL AutoSelectVideoMode( void ) const + { + return m_fAutoSelectVideoMode; + } + + int PaddleMode( void ) const; + + Event::Type PaddleResistanceEvent( void ) const; + + Event::Type PaddleFireEvent( void ) const; + + LPCTSTR PathName( void ) const + { + if ( m_pszPathName[0] == _T('\0') ) + { + return NULL; + } + + return m_pszPathName; + } + + LPCTSTR RomDir( void ) const + { + return m_pszRomDir; + } + + HINSTANCE ModuleInstance( void ) const + { + return m_hInstance; + } + + // + // Modified flags + // + + void SetModified( void ) + { + m_fIsModified = TRUE; + } + + BOOL IsModified( void ) const + { + return m_fIsModified; + } + +private: + + // Basic options + + TCHAR m_pszRomDir[ MAX_PATH ]; + int m_nPaddleMode; + BOOL m_fNoSound; + BOOL m_fDisableJoystick; + + // Advanced options + + BOOL m_fShowFPS; + int m_nDesiredFrameRate; + BOOL m_fAutoSelectVideoMode; + + + HINSTANCE m_hInstance; + TCHAR m_pszPathName[ MAX_PATH ]; + + BOOL m_fIsModified; + + CGlobalData( const CGlobalData& ); // no implementation + void operator=( const CGlobalData& ); // no implementation +}; + +inline int CGlobalData::PaddleMode( + void + ) const +{ + return m_nPaddleMode; +} + +inline Event::Type CGlobalData::PaddleResistanceEvent( + void + ) const +{ + switch ( m_nPaddleMode ) + { + case 1: + return Event::PaddleOneResistance; + + case 2: + return Event::PaddleTwoResistance; + + case 3: + return Event::PaddleThreeResistance; + } + + return Event::PaddleZeroResistance; +} + +inline Event::Type CGlobalData::PaddleFireEvent( + void + ) const +{ + switch ( m_nPaddleMode ) + { + case 1: + return Event::PaddleOneFire; + + case 2: + return Event::PaddleTwoFire; + + case 3: + return Event::PaddleThreeFire; + } + + return Event::PaddleZeroFire; +} + +#endif diff --git a/stella/src/ui/win32/HeaderCtrl.cxx b/stella/src/ui/win32/HeaderCtrl.cxx new file mode 100644 index 000000000..d00f1a0c7 --- /dev/null +++ b/stella/src/ui/win32/HeaderCtrl.cxx @@ -0,0 +1,200 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#include "pch.hxx" +#include "HeaderCtrl.hxx" + +CHeaderCtrl::CHeaderCtrl( + ) : \ + m_nSortCol(0), + m_fSortAsc(TRUE) +{ +} + + +LRESULT CHeaderCtrl::WndProc( + HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam, + BOOL& rfHandled + ) +{ + switch ( msg ) + { + case WM_DRAWITEM: + rfHandled = TRUE; + OnDrawItem(hWnd, (UINT)wParam, (LPDRAWITEMSTRUCT)lParam); + return TRUE; + } + + return 0; +} + +void CHeaderCtrl::SetSortCol( + int nCol, + BOOL bAsc + ) +{ + m_nSortCol = nCol; + m_fSortAsc = bAsc; + + // change this item to owner draw + + HWND hwndHeader = ::GetDlgItem( *this, 0 ); + + HDITEM hdi; + hdi.mask = HDI_FORMAT; + Header_GetItem(hwndHeader, nCol, &hdi); + hdi.fmt |= HDF_OWNERDRAW; + Header_SetItem(hwndHeader, nCol, &hdi); + + // repaint the header + + ::InvalidateRect(hwndHeader, NULL, TRUE); +} + +void CHeaderCtrl::OnDrawItem( + HWND hwnd, + UINT idCtl, + LPDRAWITEMSTRUCT lpdis + ) +{ + UNUSED_ALWAYS( idCtl ); + + HDC hdc = lpdis->hDC; + + RECT rcLabel; + ::CopyRect( &rcLabel, &(lpdis->rcItem) ); + + /* save the DC */ + + int nSavedDC = ::SaveDC( hdc ); + + /* set clip region to column */ + + HRGN hrgn = ::CreateRectRgnIndirect( &rcLabel ); + SelectObject( hdc, hrgn ); + DeleteObject( hrgn ); + + /* draw the background */ + + ::FillRect( hdc, &rcLabel, ::GetSysColorBrush(COLOR_3DFACE) ); + + /* offset the label */ + + SIZE size; + ::GetTextExtentPoint32( hdc, _T(" "), 1, &size ); + int nOffset = size.cx * 2; + + /* get the column text and format */ + + TCHAR tszText[255 + 1]; + HDITEM hdi; + hdi.mask = HDI_TEXT | HDI_FORMAT; + hdi.pszText = tszText; + hdi.cchTextMax = 255; + Header_GetItem( GetDlgItem(hwnd, 0), lpdis->itemID, &hdi ); + + /* determine format for drawing label */ + + UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | + DT_VCENTER | DT_END_ELLIPSIS; + + /* determine justification */ + + if (hdi.fmt & HDF_CENTER) + { + uFormat |= DT_CENTER; + } + else if (hdi.fmt & HDF_RIGHT) + { + uFormat |= DT_RIGHT; + } + else + { + uFormat |= DT_LEFT; + } + + /* adjust the rect if selected */ + + if (lpdis->itemState & ODS_SELECTED) + { + rcLabel.left++; + rcLabel.top += 2; + rcLabel.right++; + } + + /* adjust rect for sort arrow */ + + if ( lpdis->itemID == m_nSortCol ) + { + rcLabel.right -= (3 * nOffset); + } + + rcLabel.left += nOffset; + rcLabel.right -= nOffset; + + /* draw label */ + + if ( rcLabel.left < rcLabel.right ) + { + ::DrawText(hdc, tszText, -1, &rcLabel, uFormat ); + } + + /* draw the arrow */ + + if ( lpdis->itemID == m_nSortCol ) + { + RECT rcIcon; + HPEN hpenLight, hpenShadow, hpenOld; + + ::CopyRect( &rcIcon, &(lpdis->rcItem) ); + + hpenLight = ::CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT) ); + hpenShadow = ::CreatePen( PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW) ); + hpenOld = (HPEN)::SelectObject( hdc, hpenLight ); + + if (m_fSortAsc) + { + /* draw triangle pointing up */ + + ::MoveToEx( hdc, rcIcon.right - 2 * nOffset, nOffset - 1, NULL ); + ::LineTo( hdc, rcIcon.right - 3 * nOffset / 2, + rcIcon.bottom - nOffset ); + ::LineTo( hdc, rcIcon.right - 5 * nOffset / 2 - 2, + rcIcon.bottom - nOffset ); + ::MoveToEx( hdc, rcIcon.right - 5 * nOffset / 2 - 1, + rcIcon.bottom - nOffset, NULL ); + + ::SelectObject( hdc, hpenShadow ); + ::LineTo( hdc, rcIcon.right - 2 * nOffset, nOffset - 2 ); + } + else + { + /* draw triangle pointing down */ + + ::MoveToEx( hdc, rcIcon.right - 3 * nOffset / 2, nOffset - 1, + NULL ); + ::LineTo( hdc, rcIcon.right - 2 * nOffset - 1, + rcIcon.bottom - nOffset ); + ::LineTo( hdc, rcIcon.right - 2 * nOffset - 1, + rcIcon.bottom - nOffset ); + ::MoveToEx( hdc, rcIcon.right - 2 * nOffset - 1, + rcIcon.bottom - nOffset, NULL ); + + ::SelectObject( hdc, hpenShadow ); + ::LineTo( hdc, rcIcon.right - 5 * nOffset / 2 - 1, + nOffset - 1 ); + ::LineTo( hdc, rcIcon.right - 3 * nOffset / 2, + nOffset - 1 ); + } + + ::SelectObject( hdc, hpenOld ); + ::DeleteObject( hpenShadow ); + ::DeleteObject( hpenLight ); + } + + ::RestoreDC( hdc, nSavedDC ); +} diff --git a/stella/src/ui/win32/HeaderCtrl.hxx b/stella/src/ui/win32/HeaderCtrl.hxx new file mode 100644 index 000000000..156ade0b3 --- /dev/null +++ b/stella/src/ui/win32/HeaderCtrl.hxx @@ -0,0 +1,54 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef HDRCTL_H +#define HDRCTL_H +#pragma once + +#include "Wnd.hxx" + +class CHeaderCtrl : public CWnd +{ +public: + + CHeaderCtrl(); + + void SetSortCol(int nCol, BOOL fAscending); + int GetSortCol(void) const; + BOOL GetSortAsc(void) const; + +protected: + + LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, + BOOL& rfHandled); + +private: + + void OnDrawItem(HWND hwnd, UINT idCtl, LPDRAWITEMSTRUCT lpdis); + + UINT m_nSortCol; + BOOL m_fSortAsc; + + CHeaderCtrl( const CHeaderCtrl& ); // no implementation + void operator=( const CHeaderCtrl& ); // no implementation + +}; + +inline int CHeaderCtrl::GetSortCol +( + void +) const +{ + return m_nSortCol; +} + +inline BOOL CHeaderCtrl::GetSortAsc +( + void +) const +{ + return m_fSortAsc; +} + +#endif diff --git a/stella/src/ui/win32/HyperLink.cxx b/stella/src/ui/win32/HyperLink.cxx new file mode 100644 index 000000000..5e597c7be --- /dev/null +++ b/stella/src/ui/win32/HyperLink.cxx @@ -0,0 +1,352 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#include "pch.hxx" +#include "HyperLink.hxx" + +#include + +CHyperLink::CHyperLink( + ) : \ + m_bOverControl(FALSE), + m_bVisited(FALSE), + m_hFont(NULL), + m_hLinkCursor(NULL) +{ + m_crLinkColor = RGB( 0, 0, 238); // Blue + m_crVisitedColor = RGB( 85, 26, 139); // Purple + m_crHoverColor = ::GetSysColor(COLOR_HIGHLIGHT); + + SetDefaultCursor(); +} + +CHyperLink::~CHyperLink( + ) +{ + if (m_hFont) + { + ::DeleteObject( m_hFont ); + m_hFont = NULL; + } +} + +void CHyperLink::SetURL( + LPCTSTR ctszURL + ) +{ + if (ctszURL == NULL) + return; + + lstrcpy(m_tszURL, ctszURL); + +} + +void CHyperLink::PreSubclassWindow( + HWND hwnd + ) +{ + ::GetWindowText( hwnd, m_tszText, MAX_HYPERLINK_TEXT_LEN ); + + m_hFont = (HFONT)::SendMessage(hwnd, WM_GETFONT, 0, 0); +} + +LRESULT CHyperLink::WndProc( + HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam, + BOOL& rfHandled + ) +{ + switch (msg) + { + case WM_PAINT: + rfHandled = TRUE; + return OnPaint(hwnd); + + case WM_SETCURSOR: + rfHandled = TRUE; + return OnSetCursor(hwnd, LOWORD(lParam), HIWORD(lParam)); + + case WM_LBUTTONUP: + rfHandled = TRUE; + return OnLButtonUp(hwnd, wParam, LOWORD(lParam), + HIWORD(lParam)); + + case WM_MOUSEMOVE: + rfHandled = TRUE; + return OnMouseMove(hwnd, wParam, LOWORD(lParam), + HIWORD(lParam)); + } + + return 0; +} + +LRESULT CHyperLink::OnPaint( + HWND hwnd + ) +{ + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + + SelectObject(hdc, m_hFont); + + if (m_bOverControl) + { + SetTextColor(hdc, m_crHoverColor); + } + else if (m_bVisited) + { + SetTextColor(hdc, m_crVisitedColor); + } + else + { + SetTextColor(hdc, m_crLinkColor); + } + + ::SetBkMode( hdc, TRANSPARENT ); + + ::TextOut( hdc, 0, 0, m_tszText, lstrlen(m_tszText) ); + + EndPaint(hwnd, &ps); + return 0; +} + +BOOL CHyperLink::OnSetCursor( + HWND hwnd, + WORD nHittest, + WORD wMouseMsg + ) +{ + UNUSED_ALWAYS( hwnd ); + UNUSED_ALWAYS( nHittest ); + UNUSED_ALWAYS( wMouseMsg ); + + if (m_hLinkCursor) + { + ::SetCursor(m_hLinkCursor); + return TRUE; + } + + return FALSE; +} + +LRESULT CHyperLink::OnLButtonUp( + HWND hwnd, + WPARAM fwKeys, + WORD xPos, + WORD yPos + ) +{ + UNUSED_ALWAYS( fwKeys ); + UNUSED_ALWAYS( xPos ); + UNUSED_ALWAYS( yPos ); + + int result = (int)GotoURL(m_tszURL, SW_SHOW); + m_bVisited = (result > HINSTANCE_ERROR); + if (!m_bVisited) + { + MessageBeep(MB_ICONEXCLAMATION); + ReportError(result); + } + else + { + m_bVisited = TRUE; + InvalidateRect(hwnd, NULL, FALSE); + } + + return 0; +} + +LRESULT CHyperLink::OnMouseMove( + HWND hwnd, + WPARAM fwKeys, + WORD xPos, + WORD yPos + ) +{ + UNUSED_ALWAYS( fwKeys ); + + // call defwindowproc? + + if (m_bOverControl) + { + RECT rc; + GetClientRect(hwnd, &rc); + + POINT pt = { xPos, yPos }; + if (!PtInRect(&rc, pt)) + { + m_bOverControl = FALSE; + ReleaseCapture(); + RedrawWindow(hwnd, NULL, NULL, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); + } + } + else + { + m_bOverControl = TRUE; + RedrawWindow(hwnd, NULL, NULL, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); + SetCapture(hwnd); + } + + return 0; +} + +// The following appeared in Paul DiLascia's Jan 1998 MSJ articles. +// It loads a "hand" cursor from the winhlp32.exe module + +void CHyperLink::SetDefaultCursor( + void + ) +{ + if (m_hLinkCursor == NULL) // No cursor handle - load our own + { + // Get the windows directory + + TCHAR tszWndDir[MAX_PATH + 1]; + GetWindowsDirectory(tszWndDir, MAX_PATH); + + lstrcat(tszWndDir, _T("\\winhlp32.exe")); + + // This retrieves cursor #106 from winhlp32.exe, which is a hand pointer + HMODULE hModule = LoadLibrary(tszWndDir); + if (hModule) { + HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106)); + if (hHandCursor) + m_hLinkCursor = CopyCursor(hHandCursor); + } + FreeLibrary(hModule); + } +} + +LONG CHyperLink::GetRegKey( + HKEY key, + LPCTSTR subkey, + LPTSTR retdata + ) +{ + HKEY hkey; + LONG retval = RegOpenKeyEx(key, subkey, 0, KEY_QUERY_VALUE, &hkey); + + if (retval == ERROR_SUCCESS) { + long datasize = MAX_PATH; + TCHAR data[MAX_PATH]; + RegQueryValue(hkey, NULL, data, &datasize); + lstrcpy(retdata,data); + RegCloseKey(hkey); + } + + return retval; +} + +void CHyperLink::ReportError( + int nError + ) +{ + TCHAR tsz[MAX_HYPERLINK_TEXT_LEN + 1]; + switch (nError) + { + case 0: + lstrcpy(tsz, _T("The operating system is out\nof memory or resources.")); + break; + + case SE_ERR_PNF: + lstrcpy(tsz, _T("The specified path was not found.")); + break; + + case SE_ERR_FNF: + lstrcpy(tsz, _T("The specified file was not found.")); + break; + + case ERROR_BAD_FORMAT: + lstrcpy(tsz, _T("The .EXE file is invalid\n(non-Win32 .EXE or error in .EXE image).")); + break; + + case SE_ERR_ACCESSDENIED: + lstrcpy(tsz, _T("The operating system denied\naccess to the specified file.")); + break; + + case SE_ERR_ASSOCINCOMPLETE: + lstrcpy(tsz,_T("The filename association is\nincomplete or invalid.")); + break; + + case SE_ERR_DDEBUSY: + lstrcpy(tsz, _T("The DDE transaction could not\nbe completed because other DDE transactions\nwere being processed.")); + break; + + case SE_ERR_DDEFAIL: + lstrcpy(tsz, _T("The DDE transaction failed.")); + break; + + case SE_ERR_DDETIMEOUT: + lstrcpy(tsz, _T("The DDE transaction could not\nbe completed because the request timed out.")); + break; + + case SE_ERR_DLLNOTFOUND: + lstrcpy(tsz, _T("The specified dynamic-link library was not found.")); + break; + + case SE_ERR_NOASSOC: + lstrcpy(tsz, __T("There is no application associated\nwith the given filename extension.")); + break; + + case SE_ERR_OOM: + lstrcpy(tsz, _T("There was not enough memory to complete the operation.")); + break; + + case SE_ERR_SHARE: + lstrcpy(tsz, _T("A sharing violation occurred.")); + break; + + default: + wsprintf(tsz, _T("Unknown error %d occurred."), nError); + break; + } + + TCHAR tszCaption[MAX_HYPERLINK_TEXT_LEN + 1]; + lstrcpy(tszCaption, _T("Unable to open hyperlink")); + + MessageBox(NULL, tsz, tszCaption, MB_ICONEXCLAMATION | MB_OK); +} + +HINSTANCE CHyperLink::GotoURL( + LPCTSTR url, + int showcmd + ) +{ + TCHAR key[MAX_PATH + MAX_PATH]; + + // First try ShellExecute() + HINSTANCE result = ShellExecute(NULL, _T("open"), url, NULL,NULL, showcmd); + + // If it failed, get the .htm regkey and lookup the program + if ((UINT)result <= HINSTANCE_ERROR) { + + if (GetRegKey(HKEY_CLASSES_ROOT, _T(".htm"), key) == ERROR_SUCCESS) { + lstrcat(key, _T("\\shell\\open\\command")); + + if (GetRegKey(HKEY_CLASSES_ROOT,key,key) == ERROR_SUCCESS) { + TCHAR *pos; + pos = _tcsstr(key, _T("\"%1\"")); + if (pos == NULL) { // No quotes found + pos = strstr(key, _T("%1")); // Check for %1, without quotes + if (pos == NULL) // No parameter at all... + pos = key+lstrlen(key)-1; + else + *pos = '\0'; // Remove the parameter + } + else + *pos = '\0'; // Remove the parameter + + lstrcat(pos, _T(" ")); + lstrcat(pos, url); + result = (HINSTANCE) WinExec(key,showcmd); + } + } + } + + return result; +} diff --git a/stella/src/ui/win32/HyperLink.hxx b/stella/src/ui/win32/HyperLink.hxx new file mode 100644 index 000000000..24646ccce --- /dev/null +++ b/stella/src/ui/win32/HyperLink.hxx @@ -0,0 +1,66 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef HYPERLINK_H +#define HYPERLINK_H +#pragma once + +#define MAX_HYPERLINK_TEXT_LEN 256 +#define MAX_HYPERLINK_URL 256 + +#include "Wnd.hxx" + +class CHyperLink : public CWnd +{ +public: + CHyperLink(); + ~CHyperLink(); + + void SetURL(LPCTSTR ctszURL); + +protected: + + virtual void PreSubclassWindow(HWND hwnd); + virtual LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, + BOOL& rfHandled); + +private: + + void SetDefaultCursor(); + LONG GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata); + void ReportError(int nError); + HINSTANCE GotoURL(LPCTSTR url, int showcmd); + + static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, + WPARAM wParam, LPARAM lParam); + + HBRUSH OnCtlColorStatic(HWND hwnd, HDC hdcStatic, HWND hwndStatic); + BOOL OnSetCursor(HWND hwnd, WORD nHittest, WORD wMouseMsg); + LRESULT OnPaint(HWND hwnd); + LRESULT OnLButtonUp(HWND, WPARAM, WORD, WORD); + LRESULT OnMouseMove(HWND, WPARAM, WORD, WORD); + + // Stuff from the control + + TCHAR m_tszText[MAX_HYPERLINK_TEXT_LEN + 1]; + HFONT m_hFont; + + // link specific + + COLORREF m_crLinkColor; + COLORREF m_crVisitedColor; + COLORREF m_crHoverColor; + + BOOL m_bOverControl; + BOOL m_bVisited; + + HCURSOR m_hLinkCursor; + + TCHAR m_tszURL[MAX_HYPERLINK_URL + 1]; + + CHyperLink( const CHyperLink& ); // no implementation + void operator=( const CHyperLink& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/MainDlg.cxx b/stella/src/ui/win32/MainDlg.cxx new file mode 100644 index 000000000..91ad0fb48 --- /dev/null +++ b/stella/src/ui/win32/MainDlg.cxx @@ -0,0 +1,943 @@ +// +// StellaX +// Jeff Miller 05/12/2000 +// +#include "pch.hxx" +#include "MainDlg.hxx" + +#include "GlobalData.hxx" + +#include "PropertySheet.hxx" +#include "AboutPage.hxx" +#include "DocPage.hxx" +#include "ConfigPage.hxx" + +#include "MD5.hxx" +#include "PropsSet.hxx" + +#include "resource.h" + +#define BKGND_BITMAP_TOP 64 +#define BKGND_BITMAP_BOTTOM 355 + +// NOTE: LVS_OWNERDATA doesn't support LVM_SORTITEMS! + +inline LPARAM ListView_GetItemData( + HWND hwndList, + int iItem + ) +{ + LVITEM lvi; + lvi.mask = LVIF_PARAM; + lvi.iItem = iItem; + lvi.iSubItem = 0; + ListView_GetItem(hwndList, &lvi); + return lvi.lParam; +} + +CMainDlg::CMainDlg( + CGlobalData& rGlobalData, + HINSTANCE hInstance + ) : \ + m_rGlobalData(rGlobalData), + m_hInstance(hInstance) +{ +} + +void CMainDlg::ClearList( + void + ) +{ + int nCount = ListView_GetItemCount( m_hwndList ); + + for (int i = 0; i < nCount; ++i) + { + delete (CListData*)ListView_GetItemData( m_hwndList, i ); + } + + ListView_DeleteAllItems( m_hwndList ); +} + +int CMainDlg::DoModal( + HWND hwndParent + ) +{ + return DialogBoxParam( m_hInstance, + MAKEINTRESOURCE(IDD), + hwndParent, + StaticDialogFunc, + (LPARAM)this ); +} + +BOOL CALLBACK CMainDlg::StaticDialogFunc( + HWND hDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + CMainDlg* pDlg; + + switch ( uMsg ) + { + case WM_INITDIALOG: + pDlg = reinterpret_cast( lParam ); + pDlg->m_hwnd = hDlg; + (void)::SetWindowLong( hDlg, + DWL_USER, + reinterpret_cast( pDlg ) ); + break; + + default: + pDlg = reinterpret_cast( + ::GetWindowLong( hDlg, DWL_USER ) ); + if ( pDlg == NULL ) + { + return FALSE; + } + break; + } + + return pDlg->DialogFunc( uMsg, wParam, lParam ); +} + +BOOL CALLBACK CMainDlg::DialogFunc( + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + BOOL b; + + switch (uMsg) + { + case WM_COMMAND: + return OnCommand( LOWORD(wParam), (HWND)lParam, HIWORD(wParam) ); + + case WM_CTLCOLORSTATIC: + b = (BOOL)OnCtlColorStatic( (HDC)wParam, (HWND)lParam ); + if (b) + { + return b; + } + break; + + case WM_ERASEBKGND: + if ( OnEraseBkgnd( (HDC)wParam ) ) + { + return TRUE; + } + break; + + case WM_INITDIALOG: + return OnInitDialog( ); + + case WM_NOTIFY: + return OnNotify( (int)wParam, (LPNMHDR)lParam ); + + case WM_PALETTECHANGED: + TRACE( "WM_PALETTECHANGED from maindlg" ); + return FALSE; + + case WM_QUERYNEWPALETTE: + TRACE( "WM_QUERYNEWPALETTE from maindlg" ); + return FALSE; + + // + // Cool caption handlers + // + + case WM_DESTROY: + OnDestroy( ); + break; + + case WM_DRAWITEM: + // Forward this onto the control + ::SendMessage( ((LPDRAWITEMSTRUCT)lParam)->hwndItem, WM_DRAWITEM, + wParam, lParam ); + return TRUE; + + case WM_NCPAINT: + // DefWindowProc(hDlg, uMsg, wParam, lParam); + OnNcPaint( (HRGN)wParam ); + return TRUE; + + case WM_NCACTIVATE: + OnNcActivate( (BOOL)wParam ); + // When the fActive parameter is FALSE, an application should return + // TRUE to indicate that the system should proceed with the default + // processing + SetWindowLong( m_hwnd, DWL_MSGRESULT, TRUE ); + return TRUE; + + case WM_NCLBUTTONDOWN: + return OnNcLButtonDown( (INT)wParam, MAKEPOINTS(lParam) ); + + case WM_SYSCOMMAND: + // Allow Alt-F4 to close the window + if ( wParam == SC_CLOSE ) + { + ::EndDialog( m_hwnd, IDCANCEL ); + } + break; + } + + // + // Message not handled + // + + return FALSE; +} + +BOOL CMainDlg::OnInitDialog( + void + ) +{ + DWORD dwRet; + + HWND hwnd = *this; + + dwRet = m_stella.Initialize(); + if ( dwRet != ERROR_SUCCESS ) + { + MessageBoxFromWinError( dwRet, _T("CStellaX::Initialize") ); + SendMessage( hwnd, WM_CLOSE, 0, 0 ); + return FALSE; + } + + // Set dialog icon + + HICON hicon = ::LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_APP)); + ::SendMessage( hwnd, WM_SETICON, ICON_BIG, (LPARAM)hicon ); + ::SendMessage( hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon ); + + // + // Make the Rom note have bold text + // + + HWND hwndCtrl; + + hwndCtrl = ::GetDlgItem( hwnd, IDC_ROMNOTE ); + + HFONT hfont = (HFONT)::SendMessage( hwndCtrl, WM_GETFONT, 0, 0 ); + + LOGFONT lf; + ::GetObject( hfont, sizeof(LOGFONT), &lf ); + lf.lfWeight = FW_BOLD; + + m_hfontRomNote = ::CreateFontIndirect( &lf ); + if ( m_hfontRomNote ) + { + ::SendMessage( hwndCtrl, WM_SETFONT, (WPARAM)m_hfontRomNote, 0 ); + } + + // Do subclassing + + m_CoolCaption.OnInitDialog( hwnd ); + m_header.SubclassDlgItem( hwnd, IDC_ROMLIST ); + m_btn3d.SubclassDlgItem( hwnd, IDC_TITLE ); + m_btnPlay.SubclassDlgItem( hwnd, IDC_PLAY ); + m_btnHelp.SubclassDlgItem( hwnd, IDC_ABOUT ); + m_btnConfig.SubclassDlgItem( hwnd, IDC_CONFIG ); + m_btnExit.SubclassDlgItem( hwnd, IDC_EXIT ); + + const int nMaxString = 256; + TCHAR psz[nMaxString + 1]; + + // Initialize the list view + + m_hwndList = ::GetDlgItem( hwnd, IDC_ROMLIST ); + ASSERT( m_hwndList ); + + // LVS_EX_ONECLICKACTIVATE was causing a/vs in kernel32 + + ::SendMessage( m_hwndList, + LVM_SETEXTENDEDLISTVIEWSTYLE, + 0, + LVS_EX_FULLROWSELECT ); + + RECT rc; + ::GetClientRect( m_hwndList, &rc ); + + LONG lTotalWidth = rc.right-rc.left - GetSystemMetrics(SM_CXVSCROLL); + int cx = lTotalWidth / CListData::GetColumnCount(); + + for (int i = 0; i < CListData::GetColumnCount(); ++i) + { + ::LoadString( m_hInstance, + CListData::GetColumnNameIdForColumn( i ), + psz, nMaxString ); + + LV_COLUMN lvc; + lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; + lvc.fmt = LVCFMT_LEFT; + lvc.cx = cx; + lvc.pszText = psz; + ListView_InsertColumn( m_hwndList, i, &lvc ); + } + + DWORD dwError = PopulateRomList(); + if ( dwError != ERROR_SUCCESS ) + { + MessageBoxFromWinError( dwError, _T("PopulateRomList") ); + return FALSE; + } + + // if items added, select first item and enable play button + + int nCount = ListView_GetItemCount( m_hwndList ); + if (nCount != 0) + { +#ifdef _DEBUG + DWORD dwStartTick = ::GetTickCount(); +#endif + + m_header.SetSortCol( 0, TRUE ); + ListView_SortItems( m_hwndList, + ListViewCompareFunc, + (LPARAM)this ); + +#ifdef _DEBUG + TRACE("\tElapsed ticks for ListView_SortItems = %ld", ::GetTickCount()-dwStartTick); +#endif + + ListView_SetItemState( m_hwndList, 0, LVIS_SELECTED | LVIS_FOCUSED, + LVIS_SELECTED | LVIS_FOCUSED ); + } + else + { + ::EnableWindow(::GetDlgItem( hwnd, IDC_PLAY), FALSE ); + } + + // + // Show status text + // + + TCHAR pszStatus[256 + 1]; + LoadString(m_hInstance, IDS_STATUSTEXT, pszStatus, 256); + wsprintf( psz, pszStatus, nCount ); + SetDlgItemText( hwnd, IDC_ROMCOUNT, psz ); + + // + // Show rom path + // + + SetDlgItemText( hwnd, IDC_ROMPATH, m_rGlobalData.RomDir() ); + + // + // Set default button + // + + ::SendMessage( hwnd, DM_SETDEFID, IDC_PLAY, 0 ); + + // return FALSE if SetFocus is called + return TRUE; +} + +BOOL CMainDlg::OnCommand( + int id, + HWND hwndCtl, + UINT codeNotify + ) +{ + UNUSED_ALWAYS( hwndCtl ); + UNUSED_ALWAYS( codeNotify ); + + HWND hwnd = *this; + CListData* pListData; + + int nItem; + + switch (id) + { + case IDC_PLAY: + + nItem = (int)::SendMessage( m_hwndList, + LVM_GETNEXTITEM, + (WPARAM)-1, + MAKELPARAM( LVNI_SELECTED, 0 ) ); + ASSERT( nItem != -1 ); + if ( nItem == -1 ) + { + ::MessageBox( m_hInstance, + hwnd, + IDS_NO_ITEM_SELECTED ); + return TRUE; + } + +#if 0 + TCHAR pszFile[MAX_PATH + 1]; + + // BUGBUG: On Win95b pszFile is coming back empty! + LVITEM lvi; + lvi.mask = LVIF_TEXT; + lvi.iItem = nItem; + lvi.iSubItem = CListData::FILENAME_COLUMN; + lvi.pszText = pszFile; + lvi.cchTextMax = MAX_PATH; + ::SendMessage( m_hwndList, LVM_GETITEM, 0, (LPARAM)&lvi ); +#endif + + pListData = (CListData*)ListView_GetItemData( m_hwndList, nItem ); + + TCHAR pszPathName[ MAX_PATH + 1 ]; + lstrcpy( pszPathName, m_rGlobalData.RomDir() ); + lstrcat( pszPathName, _T("\\") ); + lstrcat( pszPathName, + pListData->GetTextForColumn( CListData::FILENAME_COLUMN ) ); + + // Play the game! + + ::EnableWindow( hwnd, FALSE ); + + (void)m_stella.PlayROM( hwnd, + pszPathName, + pListData->GetTextForColumn( CListData::NAME_COLUMN ), + m_rGlobalData ); + + ::EnableWindow( hwnd, TRUE ); + + // Set focus back to the rom list + + ::SetFocus( m_hwndList ); + + return TRUE; + + case IDC_EXIT: + ClearList(); + ::EndDialog( hwnd, IDCANCEL ); + return TRUE; + + case IDC_CONFIG: + { + CPropertySheet ps( _T("Configure"), hwnd ); + + CConfigPage pgConfig( m_rGlobalData ); + ps.AddPage( &pgConfig ); + + (void)ps.DoModal(); + } + return TRUE; + + case IDC_ABOUT: + { + CPropertySheet ps(_T("Help"), hwnd); + + CHelpPage pgAbout; + ps.AddPage(&pgAbout); + + CDocPage pgDoc; + ps.AddPage(&pgDoc); + + ps.DoModal(); + } + return TRUE; + } + + return FALSE; +} + +BOOL CMainDlg::OnNotify( + int idCtrl, + LPNMHDR pnmh + ) +{ + UNUSED_ALWAYS( idCtrl ); + + switch ( pnmh->code ) + { + case LVN_GETDISPINFO: + OnGetDispInfo( (NMLVDISPINFO*)pnmh ); + return TRUE; + + case LVN_COLUMNCLICK: + OnColumnClick( (LPNMLISTVIEW)pnmh ); + return TRUE; + + case LVN_ITEMCHANGED: + OnItemChanged( (LPNMLISTVIEW)pnmh ); + return TRUE; + + case NM_DBLCLK: + // send out an ok click to play + ::SendDlgItemMessage( *this, IDC_PLAY, BM_CLICK, 0, 0 ); + return TRUE; + } + + // not handled + + return FALSE; +} + +static void ScreenToClient( + HWND hwnd, + LPRECT lpRect + ) +{ + ::ScreenToClient(hwnd, (LPPOINT)lpRect); + ::ScreenToClient(hwnd, ((LPPOINT)lpRect)+1); +} + +static void FillSolidRect( + HDC hdc, + LPCRECT lpRect, + COLORREF clr + ) +{ + COLORREF crOld = ::SetBkColor(hdc, clr); + ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, lpRect, NULL, 0, NULL); + ::SetBkColor(hdc, crOld); +} + +BOOL CMainDlg::OnEraseBkgnd( + HDC hdc + ) +{ + // don't do this in 256 color + + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) + { + return FALSE; + } + + RECT rcWindow; + ::GetWindowRect( *this, &rcWindow ); + ::ScreenToClient( *this, &rcWindow ); + + FillSolidRect( hdc, &rcWindow, ::GetSysColor( COLOR_3DFACE ) ); + + RECT rc; + ::SetRect( &rc, rcWindow.left, BKGND_BITMAP_TOP, + rcWindow.right, BKGND_BITMAP_BOTTOM ); + + long lWidth = rc.right - rc.left; + long lHeight = rc.bottom - rc.top; + + HDC hdcMem = CreateCompatibleDC(hdc); + + HBITMAP hbmpTile = LoadBitmap( m_hInstance, + MAKEINTRESOURCE(IDB_TILE) ); + + BITMAP bm; + GetObject(hbmpTile, sizeof(bm), &bm); + + HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmpTile); + + for (long x = 0; x < lWidth; x += bm.bmWidth) + { + for (long y = 0; y < lHeight; y += bm.bmHeight) + { + ::BitBlt( hdc, + rc.left + x, rc.top + y, + ( (rc.left + x + bm.bmWidth) > rc.right ) ? rc.right-(rc.left+x) : bm.bmWidth, + ( (rc.top + y + bm.bmHeight) > rc.bottom ) ? rc.bottom-(rc.top+y) : bm.bmHeight, + hdcMem, + 0, 0, SRCCOPY ); + } + } + + SelectObject(hdcMem, hbmpOld); + + DeleteObject(hbmpTile); + + DeleteDC(hdcMem); + + return TRUE; +} + +HBRUSH CMainDlg::OnCtlColorStatic( + HDC hdcStatic, + HWND hwndStatic + ) +{ + // don't do this in 256 color + + if (GetDeviceCaps(hdcStatic, RASTERCAPS) & RC_PALETTE) + { + return FALSE; + } + + if ((GetWindowLong(hwndStatic, GWL_STYLE) & SS_ICON) == SS_ICON) + { + return NULL; + } + + SetBkMode(hdcStatic, TRANSPARENT); + return (HBRUSH)GetStockObject(NULL_BRUSH); +} + +// --------------------------------------------------------------------------- + +DWORD CMainDlg::PopulateRomList( + void + ) +{ + + TRACE("CMainDlg::PopulateRomList"); + + DWORD dwRet; + +#ifdef _DEBUG + DWORD dwStartTick = ::GetTickCount(); +#endif + + ClearList(); + + // REVIEW: Support .zip files? + + TCHAR pszPath[ MAX_PATH ]; + + lstrcpy( pszPath, m_rGlobalData.RomDir() ); + lstrcat( pszPath, _T("\\*.bin") ); + + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFile( pszPath, &ffd ); + + ListView_SetItemCount( m_hwndList, 100 ); + + int iItem = 0; + + BOOL fDone = (hFind == INVALID_HANDLE_VALUE); + while (!fDone) + { + // + // File metadata will be read in ReadRomData() + // + + CListData* pListData = new CListData; + if( pListData == NULL ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + dwRet = pListData->Initialize(); + if ( dwRet != ERROR_SUCCESS ) + { + return dwRet; + } + + if ( ! pListData->m_strFileName.Set( ffd.cFileName ) ) + { + return FALSE; + } + + LV_ITEM lvi; + + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iItem = iItem++; + lvi.iSubItem = 0; + lvi.pszText = LPSTR_TEXTCALLBACK; + lvi.lParam = (LPARAM)pListData; + int nItem = ListView_InsertItem( m_hwndList, &lvi ); + + ASSERT( nItem != -1 ); + + ListView_SetItemText( m_hwndList, nItem, + CListData::FILENAME_COLUMN, LPSTR_TEXTCALLBACK ); + ListView_SetItemText(m_hwndList, nItem, + CListData::MANUFACTURER_COLUMN, LPSTR_TEXTCALLBACK); + ListView_SetItemText( m_hwndList, nItem, + CListData::RARITY_COLUMN, LPSTR_TEXTCALLBACK ); + + // go to the next rom file + + fDone = !FindNextFile(hFind, &ffd); + } + + if ( hFind != INVALID_HANDLE_VALUE ) + { + VERIFY( ::FindClose( hFind ) ); + } + +#ifdef _DEBUG + TRACE("\tElapsed ticks for PopulateRomList = %ld", ::GetTickCount()-dwStartTick); +#endif + + return ERROR_SUCCESS; +} + +DWORD CMainDlg::ReadRomData( + CListData* pListData + ) const +{ + // TODO: Move this method to ListData class (?) + + if ( pListData == NULL ) + { + ASSERT( FALSE ); + return ERROR_BAD_ARGUMENTS; + } + + // attempt to read the rom file + + TCHAR pszPath[MAX_PATH + 1]; + + lstrcpy( pszPath, m_rGlobalData.RomDir() ); + lstrcat( pszPath, _T("\\") ); + lstrcat( pszPath, pListData->GetTextForColumn( CListData::FILENAME_COLUMN ) ); + + HANDLE hFile; + + hFile = CreateFile( pszPath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if (hFile == INVALID_HANDLE_VALUE) + { + return GetLastError(); + } + + DWORD dwFileSize = ::GetFileSize( hFile, NULL ); + + BYTE* pImage = new BYTE[dwFileSize]; + if ( pImage == NULL ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + DWORD dwRead; + + if ( ::ReadFile( hFile, pImage, dwFileSize, &dwRead, NULL ) ) + { + // Read the file, now check the md5 + + std::string md5 = MD5( pImage, dwFileSize ); + + // search through the properties set for this MD5 + + PropertiesSet& propertiesSet = m_stella.GetPropertiesSet(); + + uInt32 setSize = propertiesSet.size(); + + for (uInt32 i = 0; i < setSize; ++i) + { + if (propertiesSet.get(i).get("Cartridge.MD5") == md5) + { + // got it! + break; + } + } + + if (i != setSize) + { + const Properties& properties = propertiesSet.get(i); + + if ( ! pListData->m_strManufacturer.Set( + properties.get("Cartridge.Manufacturer").c_str() ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if ( ! pListData->m_strName.Set( + properties.get("Cartridge.Name").c_str() ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if (! pListData->m_strRarity.Set( + properties.get("Cartridge.Rarity").c_str() ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if ( ! pListData->m_strNote.Set( + properties.get("Cartridge.Note").c_str() ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + } + else + { + // + // Any output here should be appended to the emucore\stella.pro file + // + + TRACE( "\"Cartridge.MD5\" \"%s\"\n\"Cartridge.Name\" \"%s\"\n\"\"\n", + md5.c_str(), + pListData->GetTextForColumn( CListData::FILENAME_COLUMN ) ); + } + } + + delete[] pImage; + + VERIFY( ::CloseHandle( hFile ) ); + + pListData->m_fPopulated = TRUE; + + return ERROR_SUCCESS; +} + +void CMainDlg::OnColumnClick( + LPNMLISTVIEW pnmv + ) +{ + HCURSOR hcur = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); + + m_header.SetSortCol(pnmv->iSubItem, TRUE); + ListView_SortItems(pnmv->hdr.hwndFrom, ListViewCompareFunc, (LPARAM)this); + + // ensure the selected item is visible + + int nItem = ListView_GetNextItem( m_hwndList, -1, LVNI_SELECTED ); + if (nItem != -1) + { + ListView_EnsureVisible( m_hwndList, nItem, TRUE ); + } + + ::SetCursor(hcur); +} + +void CMainDlg::OnItemChanged( + LPNMLISTVIEW pnmv + ) +{ + HWND hwnd = *this; + + HWND hwndNote = ::GetDlgItem( hwnd, IDC_ROMNOTE ); + + RECT rc; + ::GetWindowRect(hwndNote, &rc); + ::ScreenToClient( hwnd, (LPPOINT)&rc ); + ::ScreenToClient( hwnd, ((LPPOINT)&rc)+1 ); + + int iItem = ListView_GetNextItem(pnmv->hdr.hwndFrom, -1, LVNI_SELECTED); + if (iItem == -1) + { + ::SetWindowText( hwndNote, _T("") ); + ::EnableWindow( ::GetDlgItem( hwnd, IDC_PLAY ), FALSE ); + ::InvalidateRect( hwnd, &rc, TRUE ); + return; + } + + CListData* pListData = (CListData*)ListView_GetItemData( + pnmv->hdr.hwndFrom, + pnmv->iItem ); + + ::SetWindowText( hwndNote, pListData->GetNote() ); + ::InvalidateRect( hwnd, &rc, TRUE ); + ::EnableWindow( ::GetDlgItem( hwnd, IDC_PLAY ), TRUE ); +} + +// --------------------------------------------------------------------------- +// LPSTR_TEXTCALLBACK message handlers + +void CMainDlg::OnGetDispInfo( + NMLVDISPINFO* pnmv + ) +{ + // Provide the item or subitem's text, if requested. + + if ( ! (pnmv->item.mask & LVIF_TEXT ) ) + { + return; + } + + CListData* pListData = (CListData*)ListView_GetItemData( + pnmv->hdr.hwndFrom, + pnmv->item.iItem ); + ASSERT( pListData ); + if ( pListData == NULL ) + { + return; + } + + if ( ! pListData->IsPopulated() ) + { + ReadRomData( pListData ); + } + + pnmv->item.pszText = const_cast( pListData->GetTextForColumn(pnmv->item.iSubItem) ); + // ASSERT( pnmv->item.pszText ); +} + +int CALLBACK CMainDlg::ListViewCompareFunc( + LPARAM lParam1, + LPARAM lParam2, + LPARAM lParamSort + ) +{ + CMainDlg* pThis = reinterpret_cast( lParamSort ); + + // + // I assume that the metadata for column 0 is always available, + // while other column metadata requires a call to ReadRomData + // + + int nSortCol = pThis->m_header.GetSortCol(); + + CListData* pItem1 = reinterpret_cast( lParam1 ); + if ( ! pItem1->IsPopulated() && nSortCol != 0 ) + { + pThis->ReadRomData( pItem1 ); + } + + CListData* pItem2 = reinterpret_cast( lParam2 ); + if ( ! pItem2->IsPopulated() && nSortCol != 0 ) + { + pThis->ReadRomData( pItem2 ); + } + + LPCTSTR pszItem1 = pItem1->GetTextForColumn( nSortCol ); + LPCTSTR pszItem2 = pItem2->GetTextForColumn( nSortCol ); + + // + // put blank items last + // + + if ( pszItem1 == NULL || pszItem1[0] == _T('\0') ) + { + return 1; + } + + if ( pszItem2 == NULL || pszItem2[0] == _T('\0') ) + { + return -1; + } + + // + // Compare the specified column. + // + + return lstrcmpi( pszItem1, pszItem2 ); +} + +// --------------------------------------------------------------------------- +// Cool caption message handlers + +void CMainDlg::OnDestroy( + void + ) +{ + m_CoolCaption.OnDestroy(); + + if ( m_hfontRomNote ) + { + ::DeleteObject( m_hfontRomNote ); + m_hfontRomNote = NULL; + } +} + +void CMainDlg::OnNcPaint( + HRGN hrgn + ) +{ + m_CoolCaption.OnNcPaint( hrgn ); +} + +void CMainDlg::OnNcActivate( + BOOL fActive + ) +{ + m_CoolCaption.OnNcActivate( fActive ); +} + +BOOL CMainDlg::OnNcLButtonDown( + INT nHitTest, + POINTS pts + ) +{ + return m_CoolCaption.OnNCLButtonDown( nHitTest, pts ); +} diff --git a/stella/src/ui/win32/MainDlg.hxx b/stella/src/ui/win32/MainDlg.hxx new file mode 100644 index 000000000..98e29ebaa --- /dev/null +++ b/stella/src/ui/win32/MainDlg.hxx @@ -0,0 +1,259 @@ +// +// StellaX +// Jeff Miller 05/10/2000 +// +#ifndef MAINDLG_H +#define MAINDLG_H +#pragma once + +#include "resource.h" + +class CGlobalData; + +#include "StellaXMain.hxx" +#include "CoolCaption.hxx" +#include "TextButton3d.hxx" +#include "HeaderCtrl.hxx" +#include "RoundButton.hxx" + +class CMainDlg; + +class CListData +{ + friend CMainDlg; + +public: + + CListData() : + m_fPopulated( FALSE ) + { + } + + DWORD Initialize() + { + // + // ListView control doesn't like NULLs returned, so initialize all + // + + if ( ! m_strName.Set( _T("") ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if ( ! m_strManufacturer.Set( _T("") ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if ( ! m_strRarity.Set( _T("") ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if ( ! m_strFileName.Set( _T("") ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if ( ! m_strNote.Set( _T("") ) ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + return ERROR_SUCCESS; + } + + // + // MetaData + // + + static int GetColumnCount( void ) + { + return 4; + } + + enum ColumnIndex + { + FILENAME_COLUMN, + NAME_COLUMN, + MANUFACTURER_COLUMN, + RARITY_COLUMN, + }; + + static UINT GetColumnNameIdForColumn( int nCol ) + { + UINT uID = 0; + + switch ( nCol ) + { + case NAME_COLUMN: + uID = IDS_NAME; + break; + + case MANUFACTURER_COLUMN: + uID = IDS_MANUFACTURER; + break; + + case RARITY_COLUMN: + uID = IDS_RARITY; + break; + + case FILENAME_COLUMN: + uID = IDS_FILENAME; + break; + + default: + ASSERT(FALSE); + break; + } + + return uID; + } + + LPCTSTR GetTextForColumn( int nCol ) const + { + LPCTSTR pszText = NULL; + + switch (nCol) + { + case NAME_COLUMN: + pszText = m_strName.Get(); + break; + + case MANUFACTURER_COLUMN: + pszText = m_strManufacturer.Get(); + break; + + case RARITY_COLUMN: + pszText = m_strRarity.Get(); + break; + + case FILENAME_COLUMN: + pszText = m_strFileName.Get(); + break; + + default: + ASSERT( FALSE ); + break; + } + + return pszText; + } + + LPCTSTR GetNote( void ) const + { + return m_strNote.Get(); + } + + BOOL IsPopulated( void ) const + { + return m_fPopulated; + } + +private: + + CSimpleString m_strName; + CSimpleString m_strManufacturer; + CSimpleString m_strRarity; + CSimpleString m_strFileName; + CSimpleString m_strNote; + BOOL m_fPopulated; + +private: + + CListData( const CListData& ); // no implementation + void operator=( const CListData& ); // no implementation + +}; + +// --------------------------------------------------------------------------- + +class CMainDlg +{ +public: + + enum { IDD = IDD_MAIN }; + + CMainDlg( CGlobalData& rGlobalData, HINSTANCE hInstance ); + + virtual int DoModal( HWND hwndParent ); + + operator HWND( void ) const + { + return m_hwnd; + } + +private: + + HWND m_hwnd; + + CCoolCaption m_CoolCaption; + CTextButton3d m_btn3d; + CHeaderCtrl m_header; + CRoundButton m_btnPlay; + CRoundButton m_btnHelp; + CRoundButton m_btnConfig; + CRoundButton m_btnExit; + + // + // Message handlers + // + + BOOL OnInitDialog( void ); + BOOL OnCommand( int id, HWND hwndCtl, UINT codeNotify ); + BOOL OnNotify( int idCtrl, LPNMHDR pnmh ); + BOOL OnEraseBkgnd( HDC hdc ); + HBRUSH OnCtlColorStatic( HDC hdcStatic, HWND hwndStatic ); + + // + // wm_notify handlers + // + + void OnGetDispInfo( NMLVDISPINFO* pnmh ); + + void OnColumnClick( LPNMLISTVIEW pnmv ); + void OnItemChanged( LPNMLISTVIEW pnmv ); + + // + // cool caption handlers + // + + void OnDestroy( void ); + void OnNcPaint( HRGN hrgn ); + void OnNcActivate( BOOL fActive ); + BOOL OnNcLButtonDown( INT nHitTest, POINTS pts ); + + // + // callback methods + // + + BOOL CALLBACK DialogFunc( UINT uMsg, WPARAM wParam, LPARAM lParam ); + + static BOOL CALLBACK StaticDialogFunc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); + static int CALLBACK ListViewCompareFunc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort ); + + // internal data + + DWORD PopulateRomList(); + DWORD ReadRomData( CListData* ) const; + + HINSTANCE m_hInstance; + + // stuff in list + + HWND m_hwndList; + void ClearList(); + + HFONT m_hfontRomNote; + + // Stella stuff + + CGlobalData& m_rGlobalData; + CStellaXMain m_stella; + + CMainDlg( const CMainDlg& ); // no implementation + void operator=( const CMainDlg& ); // no implementation + +}; + +#endif diff --git a/stella/src/ui/win32/PropertySheet.cxx b/stella/src/ui/win32/PropertySheet.cxx new file mode 100644 index 000000000..66b8a52e7 --- /dev/null +++ b/stella/src/ui/win32/PropertySheet.cxx @@ -0,0 +1,260 @@ +// +// StellaX +// Jeff Miller 05/02/2000 +// +#include "pch.hxx" +#include "PropertySheet.hxx" + +#pragma comment(lib, "comctl32") + +#include +typedef struct DLGTEMPLATEEX +{ + WORD dlgVer; + WORD signature; + DWORD helpID; + DWORD exStyle; + DWORD style; + WORD cDlgItems; + short x; + short y; + short cx; + short cy; +} DLGTEMPLATEEX, *LPDLGTEMPLATEEX; +#include + +CPropertyPage::CPropertyPage( + UINT nIDTemplate, + UINT nIDCaption /* = 0 */ + ) +{ + UNUSED_ALWAYS( nIDCaption ); + + ZeroMemory(&m_psp, sizeof(m_psp)); + m_psp.dwSize = sizeof(m_psp); + m_psp.dwFlags = PSP_USECALLBACK; + // m_psp.hInstance = hInstance; + m_psp.pszTemplate = MAKEINTRESOURCE(nIDTemplate); + m_psp.pfnDlgProc = StaticDlgProc; + m_psp.lParam = (LPARAM)this; + m_psp.pfnCallback = StaticCallback; +} + +UINT CALLBACK CPropertyPage::StaticCallback( + HWND hwnd, + UINT uMsg, + LPPROPSHEETPAGE ppsp + ) +{ + UNUSED_ALWAYS( hwnd ); + UNUSED_ALWAYS( ppsp ); + + switch (uMsg) + { + case PSPCB_CREATE: + // ppsp->lParam holds create lParam + return TRUE; + + case PSPCB_RELEASE: + break; + } + + return 0; +} + +BOOL CALLBACK CPropertyPage::StaticDlgProc( + HWND hwnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam + ) +{ + CPropertyPage* pPage = NULL; + + switch ( uMsg ) + { + case WM_INITDIALOG: + pPage = reinterpret_cast( + reinterpret_cast( lParam )->lParam ); + (void)::SetWindowLong( hwnd, + DWL_USER, + reinterpret_cast( pPage ) ); + break; + + default: + pPage = reinterpret_cast( + ::GetWindowLong( hwnd, DWL_USER ) ); + if ( pPage == NULL ) + { + return FALSE; + } + break; + } + + return pPage->DlgProc( hwnd, uMsg, wParam, lParam ); +} + +BOOL CALLBACK CPropertyPage::DlgProc( + HWND hwnd, + UINT msg, + WPARAM wParam, + LPARAM lParam + ) +{ + switch (msg) + { + case WM_ACTIVATE: + OnActivate( (UINT)LOWORD(wParam), (HWND)lParam, (BOOL)HIWORD(wParam) ); + return TRUE; + + case WM_INITDIALOG: + return OnInitDialog( hwnd ); + + case WM_DESTROY: + OnDestroy( ); + return TRUE; + + case WM_COMMAND: + return OnCommand( HIWORD(wParam), LOWORD(wParam), (HWND)lParam ); + + case WM_NOTIFY: + // Handle PSN_QUERYCANCEL? + // Handle PSN_KILLACTIVE? + + switch (((LPNMHDR)lParam)->code) + { + case PSN_SETACTIVE: + ::SetWindowLong(hwnd, DWL_MSGRESULT, OnSetActive( (LPPSHNOTIFY)lParam ) ); + return TRUE; + + case PSN_KILLACTIVE: + ::SetWindowLong(hwnd, DWL_MSGRESULT, OnKillActive( (LPPSHNOTIFY)lParam ) ); + return TRUE; + + case PSN_APPLY: + ::SetWindowLong( hwnd, DWL_MSGRESULT, OnApply( (LPPSHNOTIFY)lParam) ); + return TRUE; + + case PSN_RESET: + OnReset( (LPPSHNOTIFY)lParam ); + return TRUE; + } + + return OnNotify( (int)wParam, (LPNMHDR)lParam ); + } + + return FALSE; +} + + +// +// CPropertySheet +// + +CPropertySheet::CPropertySheet +( + LPCTSTR pszCaption, + HWND hwndParent, + UINT nStartPage /* = 0 */ +) :\ + m_strCaption( pszCaption ) +{ + ZeroMemory(&m_psh, sizeof(m_psh)); + m_psh.dwSize = sizeof(m_psh); + m_psh.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_USECALLBACK; + m_psh.hwndParent = hwndParent; + m_psh.hInstance = (HINSTANCE)GetWindowLong(hwndParent, GWL_HINSTANCE); + m_psh.pszCaption = m_strCaption.c_str(); + m_psh.nStartPage = nStartPage; + m_psh.pfnCallback = StaticCallback; +} + +CPropertySheet::~CPropertySheet( + ) +{ + // BUGBUG: This is static! + + /* + if ( m_hfontLogo ) + { + ::DeleteObject( m_hfontLogo ); + m_hfontLogo = NULL; + } + */ +} + +void CPropertySheet::AddPage( + CPropertyPage* pPage + ) +{ + if (pPage) + { + m_pages.push_back(pPage); + } +} + +int CPropertySheet::DoModal( + void + ) +{ + int nSize = m_pages.size(); + + if (nSize == 0) + return IDCANCEL; + + PROPSHEETPAGE* pspage = new PROPSHEETPAGE[nSize]; + for (int i = 0; i < nSize; ++i) + { + CopyMemory(&(pspage[i]), m_pages[i]->GetPropSheetPage(), + sizeof(PROPSHEETPAGE)); + pspage[i].hInstance = m_psh.hInstance; + } + + m_psh.nPages = nSize; + m_psh.ppsp = pspage; + + int nRet = ::PropertySheet( &m_psh ); + + delete[] pspage; + + return nRet; +} + +int CALLBACK CPropertySheet::StaticCallback( + HWND hwnd, + UINT uMsg, + LPARAM lParam + ) +{ + UNUSED_ALWAYS( hwnd ); + + switch (uMsg) + { + case PSCB_INITIALIZED: + // Property sheet is being initialized + + return TRUE; + + case PSCB_PRECREATE: + // Property sheet is about to be created + + + // + // Remove the DS_CONTEXTHELP style from the dialog template + // (This will remove the "?" in the top right corner) + // + + if ( ( (LPDLGTEMPLATEEX)lParam )->signature == 0xFFFF) + { + ( (LPDLGTEMPLATEEX)lParam )->style &= ~DS_CONTEXTHELP; + } + else + { + ( (LPDLGTEMPLATE)lParam )->style &= ~DS_CONTEXTHELP; + } + return TRUE; + + } + + return 0; +} diff --git a/stella/src/ui/win32/PropertySheet.hxx b/stella/src/ui/win32/PropertySheet.hxx new file mode 100644 index 000000000..561f9def0 --- /dev/null +++ b/stella/src/ui/win32/PropertySheet.hxx @@ -0,0 +1,111 @@ +// +// StellaX +// Jeff Miller 05/02/2000 +// +#ifndef PROPSHT_H +#define PROPSHT_H +#pragma once + +#include +#include +#include + +class CPropertyPage +{ +public: + + CPropertyPage( UINT nIDTemplate, UINT nIDCaption = 0 ); + + const PROPSHEETPAGE* GetPropSheetPage(); + +protected: + + // + // Overridable callbacks + // + + virtual void OnActivate( UINT state, HWND hwndActDeact, BOOL fMinimized ) + { + } + + virtual BOOL OnInitDialog( HWND /* hwnd */ ) + { + return FALSE; + } + + virtual void OnDestroy( void ) + { + } + + virtual BOOL OnSetActive( LPPSHNOTIFY /* lppsn */ ) + { + return 0; + } + + virtual BOOL OnKillActive( LPPSHNOTIFY /* lppsn */ ) + { + return FALSE; + } + + virtual LONG OnApply( LPPSHNOTIFY /* lppsn */ ) + { + return PSNRET_NOERROR; + } + + virtual void OnReset( LPPSHNOTIFY /* lppsn */ ) + { + } + + virtual BOOL OnCommand( WORD /* wNotifyCode */, WORD /* wID */, HWND /* hwndCtl */ ) + { + return FALSE; + } + + virtual BOOL OnNotify( int /* idCtrl */, LPNMHDR /* pnmh */ ) + { + return FALSE; + } + +private: + + static UINT CALLBACK StaticCallback( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp ); + + static BOOL CALLBACK StaticDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ); + BOOL CALLBACK DlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ); + + PROPSHEETPAGE m_psp; + + CPropertyPage( const CPropertyPage& ); // no implementation + void operator=( const CPropertyPage& ); // no implementation +}; + +inline const PROPSHEETPAGE* CPropertyPage::GetPropSheetPage( + void + ) +{ + return &m_psp; +} + +class CPropertySheet +{ +public: + + CPropertySheet( LPCTSTR pszCaption, HWND hwndParent, UINT nStartPage = 0 ); + ~CPropertySheet( ); + + void AddPage( CPropertyPage* pPage ); + virtual int DoModal( void ); + +private: + + static int CALLBACK StaticCallback( HWND hwndDlg, UINT uMsg, LPARAM lParam ); + + PROPSHEETHEADER m_psh; + std::vector m_pages; + std::string m_strCaption; + + CPropertySheet( const CPropertySheet& ); // no implementation + void operator=( const CPropertySheet& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/RoundButton.cxx b/stella/src/ui/win32/RoundButton.cxx new file mode 100644 index 000000000..d3bdc45cb --- /dev/null +++ b/stella/src/ui/win32/RoundButton.cxx @@ -0,0 +1,625 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#include "pch.hxx" +#include "RoundButton.hxx" +#include +#include + +// prototypes +COLORREF GetColour(double dAngle, COLORREF crBright, COLORREF crDark); + +void DrawCircle( HDC hdc, const POINT& p, LONG lRadius, + COLORREF crColour, BOOL bDashed = FALSE); + +void DrawCircleLeft( HDC hdc, const POINT& p, LONG lRadius, + COLORREF crBright, COLORREF crDark); + +void DrawCircleRight( HDC hdc, const POINT& p, LONG lRadius, + COLORREF crBright, COLORREF crDark); + + +// Calculate colour for a point at the given angle by performing a linear +// interpolation between the colours crBright and crDark based on the cosine +// of the angle between the light source and the point. +// +// Angles are measured from the +ve x-axis (i.e. (1,0) = 0 degrees, (0,1) = 90 degrees ) +// But remember: +y points down! + +COLORREF GetColour( + double dAngle, + COLORREF crBright, + COLORREF crDark + ) +{ +#define Rad2Deg 180.0/3.1415 + +// For better light-continuity along the edge of a stretched button: +// LIGHT_SOURCE_ANGLE == -1.88 + +//#define LIGHT_SOURCE_ANGLE -2.356 // -2.356 radians = -135 degrees, i.e. From top left +#define LIGHT_SOURCE_ANGLE -1.88 + + ASSERT(dAngle > -3.1416 && dAngle < 3.1416); + double dAngleDifference = LIGHT_SOURCE_ANGLE - dAngle; + + if (dAngleDifference < -3.1415) dAngleDifference = 6.293 + dAngleDifference; + else if (dAngleDifference > 3.1415) dAngleDifference = 6.293 - dAngleDifference; + + double Weight = 0.5*(cos(dAngleDifference)+1.0); + + BYTE Red = (BYTE) (Weight*GetRValue(crBright) + (1.0-Weight)*GetRValue(crDark)); + BYTE Green = (BYTE) (Weight*GetGValue(crBright) + (1.0-Weight)*GetGValue(crDark)); + BYTE Blue = (BYTE) (Weight*GetBValue(crBright) + (1.0-Weight)*GetBValue(crDark)); + + //TRACE("LightAngle = %0.0f, Angle = %3.0f, Diff = %3.0f, Weight = %0.2f, RGB %3d,%3d,%3d\n", + // LIGHT_SOURCE_ANGLE*Rad2Deg, dAngle*Rad2Deg, dAngleDifference*Rad2Deg, Weight,Red,Green,Blue); + + return RGB(Red, Green, Blue); +} + +void DrawCircle( + HDC hdc, + const POINT& p, + LONG lRadius, + COLORREF crColour, + BOOL bDashed + ) +{ + const int nDashLength = 1; + LONG lError, lXoffset, lYoffset; + int nDash = 0; + BOOL bDashOn = TRUE; + + //Check to see that the coordinates are valid + ASSERT( (p.x + lRadius <= LONG_MAX) && (p.y + lRadius <= LONG_MAX) ); + ASSERT( (p.x - lRadius >= LONG_MIN) && (p.y - lRadius >= LONG_MIN) ); + + //Set starting values + lXoffset = lRadius; + lYoffset = 0; + lError = -lRadius; + + do + { + if (bDashOn) + { + ::SetPixelV(hdc, p.x + lXoffset, p.y + lYoffset, crColour); + ::SetPixelV(hdc, p.x + lXoffset, p.y - lYoffset, crColour); + ::SetPixelV(hdc, p.x + lYoffset, p.y + lXoffset, crColour); + ::SetPixelV(hdc, p.x + lYoffset, p.y - lXoffset, crColour); + ::SetPixelV(hdc, p.x - lYoffset, p.y + lXoffset, crColour); + ::SetPixelV(hdc, p.x - lYoffset, p.y - lXoffset, crColour); + ::SetPixelV(hdc, p.x - lXoffset, p.y + lYoffset, crColour); + ::SetPixelV(hdc, p.x - lXoffset, p.y - lYoffset, crColour); + } + + //Advance the error term and the constant X axis step + lError += lYoffset++; + + //Check to see if error term has overflowed + if ((lError += lYoffset) >= 0) + lError -= --lXoffset * 2; + + if (bDashed && (++nDash == nDashLength)) + { + nDash = 0; + bDashOn = !bDashOn; + } + + } while (lYoffset <= lXoffset); //Continue until halfway point +} + +// The original Drawcircle function is split up into DrawCircleRight and DrawCircleLeft +// to make stretched buttons + +void DrawCircleRight( + HDC hdc, + const POINT& p, + LONG lRadius, + COLORREF crBright, + COLORREF crDark + ) +{ + LONG lError, lXoffset, lYoffset; + + //Check to see that the coordinates are valid + ASSERT( (p.x + lRadius <= LONG_MAX) && (p.y + lRadius <= LONG_MAX) ); + ASSERT( (p.x - lRadius >= LONG_MIN) && (p.y - lRadius >= LONG_MIN) ); + + //Set starting values + lXoffset = lRadius; + lYoffset = 0; + lError = -lRadius; + + do + { + const double Pi = 3.141592654, + Pi_on_2 = Pi * 0.5; + COLORREF crColour; + double dAngle = atan2(lYoffset, lXoffset); + + //Draw the current pixel, reflected across all four arcs + + crColour = GetColour(dAngle, crBright, crDark); + ::SetPixelV(hdc, p.x + lXoffset, p.y + lYoffset, crColour); + + crColour = GetColour(Pi_on_2 - dAngle, crBright, crDark); + ::SetPixelV(hdc, p.x + lYoffset, p.y + lXoffset, crColour); + + crColour = GetColour(-Pi_on_2 + dAngle, crBright, crDark); + ::SetPixelV(hdc, p.x + lYoffset, p.y - lXoffset, crColour); + + crColour = GetColour(-dAngle, crBright, crDark); + ::SetPixelV(hdc, p.x + lXoffset, p.y - lYoffset, crColour); + + //Advance the error term and the constant X axis step + lError += lYoffset++; + + //Check to see if error term has overflowed + if ((lError += lYoffset) >= 0) + lError -= --lXoffset * 2; + + } while (lYoffset <= lXoffset); //Continue until halfway point +} + +// The original Drawcircle function is split up into DrawCircleRight and DrawCircleLeft +// to make stretched buttons + +void DrawCircleLeft( + HDC hdc, + const POINT& p, + LONG lRadius, + COLORREF crBright, + COLORREF crDark + ) +{ + LONG lError, lXoffset, lYoffset; + + //Check to see that the coordinates are valid + ASSERT( (p.x + lRadius <= LONG_MAX) && (p.y + lRadius <= LONG_MAX) ); + ASSERT( (p.x - lRadius >= LONG_MIN) && (p.y - lRadius >= LONG_MIN) ); + + //Set starting values + lXoffset = lRadius; + lYoffset = 0; + lError = -lRadius; + + do + { + const double Pi = 3.141592654, + Pi_on_2 = Pi * 0.5; + COLORREF crColour; + double dAngle = atan2(lYoffset, lXoffset); + + //Draw the current pixel, reflected across all eight arcs + + crColour = GetColour(Pi_on_2 + dAngle, crBright, crDark); + ::SetPixelV(hdc, p.x - lYoffset, p.y + lXoffset, crColour); + + crColour = GetColour(Pi - dAngle, crBright, crDark); + ::SetPixelV(hdc, p.x - lXoffset, p.y + lYoffset, crColour); + + crColour = GetColour(-Pi + dAngle, crBright, crDark); + ::SetPixelV(hdc, p.x - lXoffset, p.y - lYoffset, crColour); + + crColour = GetColour(-Pi_on_2 - dAngle, crBright, crDark); + ::SetPixelV(hdc, p.x - lYoffset, p.y - lXoffset, crColour); + + //Advance the error term and the constant X axis step + lError += lYoffset++; + + //Check to see if error term has overflowed + if ((lError += lYoffset) >= 0) + lError -= --lXoffset * 2; + + } while (lYoffset <= lXoffset); //Continue until halfway point +} + +///////////////////////////////////////////////////////////////////////////// + +static void ClientToScreen( + HWND hwnd, + LPRECT lpRect + ) +{ + ::ClientToScreen(hwnd, (LPPOINT)lpRect); + ::ClientToScreen(hwnd, ((LPPOINT)lpRect)+1); +} + +static void ScreenToClient( + HWND hwnd, + LPRECT lpRect + ) +{ + ::ScreenToClient(hwnd, (LPPOINT)lpRect); + ::ScreenToClient(hwnd, ((LPPOINT)lpRect)+1); +} + +static void FillSolidRect( + HDC hdc, + LPCRECT lpRect, + COLORREF clr + ) +{ + COLORREF crOld = ::SetBkColor(hdc, clr); + ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, lpRect, NULL, 0, NULL); + ::SetBkColor(hdc, crOld); +} + +////////////////////////////////////////////////////////////////////////////// + +CRoundButton::CRoundButton( + ) : \ + m_hrgn( NULL ), + m_fDrawDashedFocusCircle( TRUE ), + m_fStretch( FALSE ) +{ +} + +CRoundButton::~CRoundButton( + ) +{ + if ( m_hrgn ) + { + ::DeleteObject( m_hrgn ); + } +} + +void CRoundButton::PreSubclassWindow( + HWND hwnd + ) +{ + RECT rect; + ::GetClientRect( hwnd, &rect ); + + m_fStretch = (rect.right-rect.left) > (rect.bottom-rect.top); + + if ( ! m_fStretch ) + { + rect.bottom = rect.right = min ( rect.bottom, rect.right ); + } + + m_ptCenter.x = m_ptLeft.x = m_ptRight.x = ((rect.right-rect.left)/2); + m_ptCenter.y = m_ptLeft.y = m_ptRight.y = ((rect.bottom-rect.top)/2); + + m_nRadius = rect.bottom/2 - 1; + + m_ptLeft.x = m_nRadius; + m_ptRight.x = rect.right - m_nRadius - 1; + + ::SetWindowRgn( hwnd, NULL, FALSE ); + m_hrgn = ::CreateEllipticRgnIndirect( &rect ); + ::SetWindowRgn( hwnd, m_hrgn, TRUE ); + + ::ClientToScreen( hwnd, &rect ); + + HWND hwndParent = ::GetParent( hwnd ); + if ( hwndParent ) + { + ::ScreenToClient( hwndParent, &rect ); + } + + if ( ! m_fStretch ) + { + ::MoveWindow( hwnd, rect.left, rect.top, + rect.right-rect.left, rect.bottom-rect.top, TRUE ); + } +} + +LRESULT CRoundButton::WndProc( + HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam, + BOOL& rfHandled + ) +{ + switch (msg) + { + case WM_DRAWITEM: + rfHandled = TRUE; + OnDrawItem(hWnd, (UINT)wParam, (LPDRAWITEMSTRUCT)lParam); + return TRUE; + + case WM_ERASEBKGND: + // don't do erasing + return TRUE; + } + + return 0; +} + +void CRoundButton::OnDrawItem( + HWND hwnd, + UINT idCtl, + LPDRAWITEMSTRUCT lpdis + ) +{ + UNUSED_ALWAYS( idCtl ); + + HDC hdc = lpdis->hDC; + RECT& rect = lpdis->rcItem; + UINT state = lpdis->itemState; + UINT nStyle = GetWindowLong(hwnd, GWL_STYLE); + int nRadius = m_nRadius; + + int nSavedDC = ::SaveDC( hdc ); + + ::SelectObject( hdc, ::GetStockObject( NULL_BRUSH ) ); + ::FillSolidRect( hdc, &rect, ::GetSysColor(COLOR_BTNFACE) ); + + // Draw the focus circle around the button for non-stretched buttons + + if ( (state & ODS_FOCUS) && m_fDrawDashedFocusCircle && !m_fStretch ) + { + DrawCircle( hdc, m_ptCenter, nRadius--, RGB( 0, 0, 0) ); + } + + // Draw the raised/sunken edges of the button (unless flat) + + if ( nStyle & BS_FLAT ) + { + if ( m_fStretch ) + { + // for stretched buttons: draw left and right arcs and connect the with lines + + HPEN hpenOld; + + RECT rectLeftBound; + ::SetRect( &rectLeftBound, 0, 0, nRadius*2, nRadius*2 ); + + RECT rectRightBound; + ::SetRect( &rectRightBound, m_ptRight.x-nRadius, 0, m_ptRight.x+nRadius, nRadius*2 ); + + hpenOld = (HPEN)::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, ::GetSysColor(COLOR_3DDKSHADOW) ) ); + + ::Arc( hdc, + rectLeftBound.left, rectLeftBound.top, rectLeftBound.right, rectLeftBound.bottom, + m_ptLeft.x, 0, + m_ptLeft.x, nRadius*2 ); + + ::Arc( hdc, + rectRightBound.left, rectRightBound.top, rectRightBound.right, rectRightBound.bottom, + m_ptRight.x, nRadius*2, + m_ptRight.x, 0 ); + + ::MoveToEx( hdc, m_ptLeft.x, 0, NULL ); + ::LineTo( hdc, m_ptRight.x, 0 ); + + ::MoveToEx( hdc, m_ptLeft.x, nRadius*2-1, NULL ); + ::LineTo( hdc, m_ptRight.x, nRadius*2-1 ); + + nRadius--; + + ::InflateRect( &rectLeftBound, -1, -1 ); + ::InflateRect( &rectRightBound, -1, -1 ); + + ::DeleteObject( ::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, ::GetSysColor( COLOR_3DHIGHLIGHT ) ) ) ); + + ::Arc( hdc, + rectLeftBound.left, rectLeftBound.top, rectLeftBound.right, rectLeftBound.bottom, + m_ptLeft.x, 1, + m_ptLeft.x, nRadius*2 ); + + ::Arc( hdc, + rectRightBound.left, rectRightBound.top, rectRightBound.right, rectRightBound.bottom, + m_ptRight.x, nRadius*2, + m_ptRight.x, 0 ); + + ::MoveToEx( hdc, m_ptLeft.x, 1, NULL ); + ::LineTo( hdc, m_ptRight.x, 1 ); + + ::MoveToEx( hdc, m_ptLeft.x, nRadius*2, NULL ); + ::LineTo( hdc, m_ptRight.x, nRadius*2 ); + + ::DeleteObject( ::SelectObject( hdc, hpenOld ) ); + } + else + { + // for non-stretched buttons: draw two circles + + DrawCircle( hdc, m_ptCenter, nRadius--, ::GetSysColor(COLOR_3DDKSHADOW) ); + DrawCircle( hdc, m_ptCenter, nRadius--, ::GetSysColor(COLOR_3DHIGHLIGHT) ); + } + } + else + { + if (state & ODS_SELECTED) + { + // draw the circular segments for stretched AND non-stretched buttons + + DrawCircleLeft( hdc, m_ptLeft, nRadius, + ::GetSysColor(COLOR_3DDKSHADOW), ::GetSysColor(COLOR_3DHIGHLIGHT) ); + DrawCircleRight( hdc, m_ptRight, nRadius, + ::GetSysColor(COLOR_3DDKSHADOW), ::GetSysColor(COLOR_3DHIGHLIGHT) ); + DrawCircleLeft( hdc, m_ptLeft, nRadius-1, + ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DLIGHT) ); + DrawCircleRight( hdc, m_ptRight, nRadius-1, + ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DLIGHT) ); + + if ( m_fStretch ) + { + // draw connecting lines for stretched buttons only + + HPEN hpenOld; + + hpenOld = (HPEN)::SelectObject( hdc, + (HPEN)::CreatePen( PS_SOLID, 1, ::GetSysColor(COLOR_3DDKSHADOW) ) ); + + ::MoveToEx( hdc, m_ptLeft.x, 1, NULL ); + ::LineTo( hdc, m_ptRight.x, 1 ); + + ::DeleteObject( ::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW) ) ) ); + + ::MoveToEx( hdc, m_ptLeft.x, 2, NULL ); + ::LineTo( hdc, m_ptRight.x, 2 ); + + ::DeleteObject( ::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, ::GetSysColor( COLOR_3DLIGHT ) ) ) ); + + ::MoveToEx( hdc, m_ptLeft.x, m_ptLeft.y + nRadius-1, NULL ); + ::LineTo( hdc, m_ptRight.x, m_ptLeft.y + nRadius-1 ); + + ::DeleteObject( ::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, ::GetSysColor( COLOR_3DHIGHLIGHT ) ) ) ); + + ::MoveToEx( hdc, m_ptLeft.x, m_ptLeft.y + nRadius, NULL ); + ::LineTo( hdc, m_ptRight.x, m_ptLeft.y + nRadius ); + + ::DeleteObject( ::SelectObject( hdc, hpenOld ) ); + } + } + else + { + // draw the circular segments for stretched AND non-stretched buttons + + DrawCircleLeft( hdc, m_ptLeft, nRadius, + ::GetSysColor(COLOR_3DHIGHLIGHT), ::GetSysColor(COLOR_3DDKSHADOW) ); + + DrawCircleRight( hdc, m_ptRight, nRadius, + ::GetSysColor(COLOR_3DHIGHLIGHT), ::GetSysColor(COLOR_3DDKSHADOW) ); + + DrawCircleLeft( hdc, m_ptLeft, nRadius - 1, + ::GetSysColor(COLOR_3DLIGHT), ::GetSysColor(COLOR_3DSHADOW) ); + + DrawCircleRight( hdc, m_ptRight, nRadius - 1, + ::GetSysColor(COLOR_3DLIGHT), ::GetSysColor(COLOR_3DSHADOW) ); + + // draw connecting lines for stretch buttons + + if ( m_fStretch ) + { + HPEN hpenOld; + + hpenOld = (HPEN)::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, + ::GetPixel( hdc, m_ptLeft.x, 1 ) ) ); + + ::MoveToEx( hdc, m_ptLeft.x, 1, NULL ); + ::LineTo( hdc, m_ptRight.x , 1); + + ::DeleteObject( ::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, + ::GetPixel( hdc, m_ptLeft.x, 2 ) ) ) ); + + ::MoveToEx( hdc, m_ptLeft.x, 2, NULL ); + ::LineTo( hdc, m_ptRight.x, 2 ); + + ::DeleteObject( ::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, + ::GetPixel( hdc, m_ptLeft.x, m_ptLeft.y+nRadius ) ) ) ); + + ::MoveToEx( hdc, m_ptLeft.x, m_ptLeft.y + nRadius, NULL ); + ::LineTo( hdc, m_ptRight.x, m_ptLeft.y + nRadius ); + + ::DeleteObject( ::SelectObject( hdc, + ::CreatePen( PS_SOLID, 1, + ::GetPixel( hdc, m_ptLeft.x, m_ptLeft.y+nRadius-1 ) ) ) ); + + ::MoveToEx( hdc, m_ptLeft.x, m_ptLeft.y + nRadius - 1, NULL ); + ::LineTo( hdc, m_ptRight.x, m_ptLeft.y + nRadius - 1 ); + + ::DeleteObject( ::SelectObject( hdc, hpenOld ) ); + } + } + } + + // Draw the text if there is any + + TCHAR pszText[ 256 ]; + int cch = ::GetWindowText( hwnd, pszText, 255 ); + + if ( cch != 0 ) + { + HRGN hrgn; + + if ( m_fStretch ) + { + hrgn = ::CreateRectRgn( m_ptLeft.x - nRadius / 2, m_ptCenter.y - nRadius, + m_ptRight.x + nRadius / 2, m_ptCenter.y + nRadius ); + } + else + { + hrgn = CreateEllipticRgn( m_ptCenter.x - nRadius, m_ptCenter.y - nRadius, + m_ptCenter.x + nRadius, m_ptCenter.y + nRadius ); + } + + ::SelectClipRgn( hdc, hrgn ); + + SIZE size; + ::GetTextExtentPoint32( hdc, pszText, cch, &size ); + + POINT pt = { m_ptCenter.x - size.cx / 2, m_ptCenter.y - size.cy / 2 - 1 }; + POINT pt2 = { m_ptCenter.x + size.cx/2, m_ptCenter.y + size.cy/2 + 1 }; + + if ( state & ODS_SELECTED ) + { + ++( pt.x ); + ++( pt.y ); + } + + ::SetBkMode( hdc, TRANSPARENT ); + +#if 0 + if ( state & ODS_DISABLED ) + { + ::DrawState( hdc, NULL, NULL, (LPARAM)pszText, (WPARAM)cch, + pt.x, pt.y, size.cx, size.cy, DSS_DISABLED ); + } + else +#endif + { + // give text a 3d-look + + RECT pos; + ::SetRect( &pos, pt.x, pt.y, pt2.x, pt2.y ); + + COLORREF crOld = ::SetTextColor( hdc, + ( state & ODS_DISABLED ) ? + ::GetSysColor( COLOR_GRAYTEXT ) : + ::GetSysColor( COLOR_WINDOWTEXT ) ); + + if ( state & ODS_FOCUS ) + { + LOGFONT lf; + ::GetObject( (HFONT)::SendMessage( hwnd, WM_GETFONT, 0, 0 ), + sizeof(LOGFONT), &lf ); + + lf.lfWeight = FW_BOLD; + + HFONT hfontOld = (HFONT)::SelectObject( hdc, ::CreateFontIndirect( &lf ) ); + + ::DrawText( hdc, pszText, -1, &pos, DT_SINGLELINE | DT_CENTER ); + + ::DeleteObject( ::SelectObject( hdc, hfontOld) ); + + } + else + { + ::DrawText( hdc, pszText, -1, &pos, DT_SINGLELINE | DT_CENTER ); + } + + ::SetTextColor( hdc, crOld ); + } + + ::SelectClipRgn( hdc, NULL ); + + DeleteObject( hrgn ); + } + +#if 0 + // draw the focus circle on the inside + + if ( (state & ODS_FOCUS) && m_fDrawDashedFocusCircle && !m_fStretch ) + { + DrawCircle( hdc, m_ptCenter, nRadius-2, RGB(0, 0, 0), TRUE ); + } +#endif + + ::RestoreDC( hdc, nSavedDC ); +} diff --git a/stella/src/ui/win32/RoundButton.hxx b/stella/src/ui/win32/RoundButton.hxx new file mode 100644 index 000000000..6b22970e3 --- /dev/null +++ b/stella/src/ui/win32/RoundButton.hxx @@ -0,0 +1,39 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef RNDBUT_H +#define RNDBUT_H + +#include "Wnd.hxx" + +class CRoundButton : public CWnd +{ +public: + + CRoundButton(); + ~CRoundButton(); + +protected: + + LRESULT WndProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam, BOOL& fHandled ); + virtual void PreSubclassWindow(HWND hwnd); + +private: + + void OnDrawItem(HWND hwnd, UINT idCtl, LPDRAWITEMSTRUCT lpdis); + + HRGN m_hrgn; + POINT m_ptCenter; + POINT m_ptLeft; + POINT m_ptRight; + int m_nRadius; + BOOL m_fDrawDashedFocusCircle; + BOOL m_fStretch; + + CRoundButton( const CRoundButton& ); // no implementation + void operator=( const CRoundButton& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/SoundWin32.cxx b/stella/src/ui/win32/SoundWin32.cxx new file mode 100644 index 000000000..d101c7f84 --- /dev/null +++ b/stella/src/ui/win32/SoundWin32.cxx @@ -0,0 +1,173 @@ +// +// StellaX +// Jeff Miller 04/26/2000 +// +#include "pch.hxx" +#include "SoundWin32.hxx" +#include "resource.h" + +#include "AudioStream.hxx" + +SoundWin32::SoundWin32( + ) : + m_fInitialized( FALSE ), + m_pass( NULL ), + m_pasCurrent( NULL ) +{ + TRACE("SoundWin32::SoundWin32"); +} + + +HRESULT SoundWin32::Initialize( + HWND hwnd + ) +{ + TRACE( "SoundWin32::Initialize hwnd=%08X", hwnd ); + + if ( m_fInitialized ) + { + TRACE( "SoundWin32::Initialize - already initialized" ); + return S_OK; + } + + HRESULT hr = S_OK; + + m_pass = new AudioStreamServices; + if ( m_pass == NULL ) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + if ( ! m_pass->Initialize( hwnd ) ) + { + TRACE( "ASS Initialize failed" ); + MessageBox( (HINSTANCE)::GetWindowLong( hwnd, GWL_HINSTANCE ), + hwnd, + IDS_ASS_FAILED ); + hr = E_FAIL; + goto cleanup; + } + + m_pasCurrent = new AudioStream; + if ( m_pasCurrent == NULL ) + { + hr = E_OUTOFMEMORY; + goto cleanup; + } + + if ( ! m_pasCurrent->Create( m_pass ) ) + { + TRACE( "PAS Create failed" ); + MessageBox( (HINSTANCE)::GetWindowLong( hwnd, GWL_HINSTANCE ), + hwnd, + IDS_PAS_FAILED ); + hr = E_FAIL; + goto cleanup; + } + + m_pasCurrent->Play(); + + m_fInitialized = TRUE; +cleanup: + + if ( FAILED(hr) ) + { + if ( m_pasCurrent ) + { + m_pasCurrent->Destroy(); + delete m_pasCurrent; + m_pasCurrent = NULL; + } + + if ( m_pass ) + { + delete m_pass; + m_pass = NULL; + } + } + + return hr; +} + +SoundWin32::~SoundWin32( + ) +{ + TRACE("SoundWin32::~SoundWin32"); + + if ( m_pasCurrent ) + { + m_pasCurrent->Destroy(); + delete m_pasCurrent; + m_pasCurrent = NULL; + } + + delete m_pass; + m_pass = NULL; +} + + +void SoundWin32::set(Sound::Register reg, uInt8 value) +{ + if ( ! m_fInitialized ) + { + TRACE( "SoundWin32::set -- not initialized" ); + return; + } + + // + // Process TIA data + // + + switch( reg ) + { + case AUDC0: + Update_tia_sound( 0x15, value ); + break; + + case AUDC1: + Update_tia_sound( 0x16, value ); + break; + + case AUDF0: + Update_tia_sound( 0x17, value ); + break; + + case AUDF1: + Update_tia_sound( 0x18, value ); + break; + + case AUDV0: + Update_tia_sound( 0x19, value ); + break; + + case AUDV1: + Update_tia_sound( 0x1A, value ); + break; + } + +} + +void SoundWin32::mute( + bool mute + ) +{ + if ( ! m_fInitialized ) + { + TRACE( "SoundWin32::mute -- not initialized" ); + return; + } + + if ( m_pasCurrent ) + { + if ( mute ) + { + m_pasCurrent->Stop(); + } + else + { + m_pasCurrent->Play(); + } + } +} + diff --git a/stella/src/ui/win32/SoundWin32.hxx b/stella/src/ui/win32/SoundWin32.hxx new file mode 100644 index 000000000..916ac1c3b --- /dev/null +++ b/stella/src/ui/win32/SoundWin32.hxx @@ -0,0 +1,109 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef SNDWIN32_H +#define SNDWIN32_H + +#include "bspf.hxx" +#include "Sound.hxx" +#include "TIASound.h" + + +class AudioStreamServices; +class AudioStream; + +#define SAMPLES_PER_SEC 22050 +#define NUM_CHANNELS 1 +#define BITS_PER_SAMPLE 8 + +class WaveFile +{ +public: + + WaveFile( void ) + { + ZeroMemory( &wfx, sizeof(wfx) ); + wfx.cbSize = sizeof( wfx ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = NUM_CHANNELS; + wfx.nSamplesPerSec = SAMPLES_PER_SEC; + wfx.wBitsPerSample = BITS_PER_SAMPLE; + wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + + } + + ~WaveFile( void ) + { + } + + BOOL Open( void ) + { + Tia_sound_init( 31400, wfx.nSamplesPerSec ); + + return TRUE; + } + + BOOL Cue( void ) + { + return TRUE; + } + + void Read( BYTE* pbDest, UINT cbSize ) + { + Tia_process ( pbDest, cbSize ); + } + + UINT GetAvgDataRate (void) + { + return wfx.nAvgBytesPerSec; + } + + BYTE GetSilenceData (void) + { + return 0; + } + + WAVEFORMATEX* GetWaveFormatEx( void ) + { + return &wfx; + } + +private: + + WAVEFORMATEX wfx; + + WaveFile( const WaveFile& ); // no implementation + void operator=( const WaveFile& ); // no implementation +}; + +class SoundWin32 : public Sound +{ +public: + + SoundWin32(); + virtual ~SoundWin32(); + + HRESULT Initialize( HWND hwnd ); + + // + // base class virtuals + // + + virtual void set(Sound::Register reg, uInt8 value); + + virtual void mute( bool state ); + +private: + + BOOL m_fInitialized; + + AudioStreamServices* m_pass; + AudioStream* m_pasCurrent; + + SoundWin32( const SoundWin32& ); // no implementation + void operator=( const SoundWin32& ); // no implementation +}; +#endif + diff --git a/stella/src/ui/win32/Stella.dsp b/stella/src/ui/win32/Stella.dsp new file mode 100644 index 000000000..877375bd7 --- /dev/null +++ b/stella/src/ui/win32/Stella.dsp @@ -0,0 +1,675 @@ +# Microsoft Developer Studio Project File - Name="Stella" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=Stella - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Stella.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Stella.mak" CFG="Stella - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Stella - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "Stella - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Stella - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\..\build" /I "..\..\emucore" /I "..\..\emucore\m6502\src" /I "..\..\emucore\m6502\src\bspf\src" /I "..\sound" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "BSPF_WIN32" /Fr /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL" +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL" +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib /nologo /subsystem:windows /debug /machine:I386 + +!ELSEIF "$(CFG)" == "Stella - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\..\build" /I "..\..\emucore" /I "..\..\emucore\m6502\src" /I "..\..\emucore\m6502\src\bspf\src" /I "..\sound" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "BSPF_WIN32" /FR /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL" +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL" +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib /nologo /subsystem:windows /profile /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "Stella - Win32 Release" +# Name "Stella - Win32 Debug" +# Begin Group "M6502" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\D6502.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\D6502.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\Device.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\Device.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\M6502.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\M6502.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\M6502Hi.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\M6502Hi.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\M6502Low.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\M6502Low.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\NullDev.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\NullDev.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\System.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\System.hxx +# End Source File +# End Group +# Begin Group "Core" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\emucore\Booster.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Booster.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Cart.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Cart.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Cart2K.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Cart2K.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Cart3F.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Cart3F.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Cart4K.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Cart4K.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartAR.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartAR.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartDPC.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartDPC.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartE0.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartE0.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartE7.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartE7.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF4SC.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF4SC.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF6.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF6.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF6SC.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF6SC.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF8.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF8.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF8SC.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartF8SC.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartFASC.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartFASC.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartFE.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartFE.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartMC.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\CartMC.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Console.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Console.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Control.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Control.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\DefProps.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\DefProps.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Driving.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Driving.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Event.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Event.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Joystick.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Joystick.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Keyboard.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Keyboard.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\M6532.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\M6532.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\MD5.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\MD5.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\MediaSrc.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\MediaSrc.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Paddles.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Paddles.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Props.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Props.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\PropsSet.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\PropsSet.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Random.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Random.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Sound.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Sound.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Switches.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\Switches.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\TIA.cxx +# End Source File +# Begin Source File + +SOURCE=..\..\emucore\TIA.hxx +# End Source File +# End Group +# Begin Group "Win32" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\AboutPage.cxx +# End Source File +# Begin Source File + +SOURCE=.\AboutPage.hxx +# End Source File +# Begin Source File + +SOURCE=.\AudioStream.cxx +# End Source File +# Begin Source File + +SOURCE=.\AudioStream.hxx +# End Source File +# Begin Source File + +SOURCE=.\BrowseForFolder.cxx +# End Source File +# Begin Source File + +SOURCE=.\BrowseForFolder.hxx +# End Source File +# Begin Source File + +SOURCE=.\ConfigPage.cxx +# End Source File +# Begin Source File + +SOURCE=.\ConfigPage.hxx +# End Source File +# Begin Source File + +SOURCE=.\ControlHost.cxx +# End Source File +# Begin Source File + +SOURCE=.\ControlHost.hxx +# End Source File +# Begin Source File + +SOURCE=.\CoolCaption.cxx +# End Source File +# Begin Source File + +SOURCE=.\CoolCaption.hxx +# End Source File +# Begin Source File + +SOURCE=.\debug.cxx +# End Source File +# Begin Source File + +SOURCE=.\debug.hxx +# End Source File +# Begin Source File + +SOURCE=.\DirectDraw.cxx +# End Source File +# Begin Source File + +SOURCE=.\DirectDraw.hxx +# End Source File +# Begin Source File + +SOURCE=.\DirectInput.cxx +# End Source File +# Begin Source File + +SOURCE=.\DirectInput.hxx +# End Source File +# Begin Source File + +SOURCE=.\DirectXFullScreen.cxx +# End Source File +# Begin Source File + +SOURCE=.\DirectXFullScreen.hxx +# End Source File +# Begin Source File + +SOURCE=.\DirectXWindow.cxx +# End Source File +# Begin Source File + +SOURCE=.\DirectXWindow.hxx +# End Source File +# Begin Source File + +SOURCE=.\DocPage.cxx +# End Source File +# Begin Source File + +SOURCE=.\DocPage.hxx +# End Source File +# Begin Source File + +SOURCE=.\FileDialog.cxx +# End Source File +# Begin Source File + +SOURCE=.\FileDialog.hxx +# End Source File +# Begin Source File + +SOURCE=.\GlobalData.cxx +# End Source File +# Begin Source File + +SOURCE=.\GlobalData.hxx +# End Source File +# Begin Source File + +SOURCE=.\HeaderCtrl.cxx +# End Source File +# Begin Source File + +SOURCE=.\HeaderCtrl.hxx +# End Source File +# Begin Source File + +SOURCE=.\HyperLink.cxx +# End Source File +# Begin Source File + +SOURCE=.\HyperLink.hxx +# End Source File +# Begin Source File + +SOURCE=.\main.cxx +# End Source File +# Begin Source File + +SOURCE=.\MainDlg.cxx +# End Source File +# Begin Source File + +SOURCE=.\MainDlg.hxx +# End Source File +# Begin Source File + +SOURCE=.\pch.cxx +# End Source File +# Begin Source File + +SOURCE=.\pch.hxx +# End Source File +# Begin Source File + +SOURCE=.\PropertySheet.cxx +# End Source File +# Begin Source File + +SOURCE=.\PropertySheet.hxx +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# Begin Source File + +SOURCE=.\RoundButton.cxx +# End Source File +# Begin Source File + +SOURCE=.\RoundButton.hxx +# End Source File +# Begin Source File + +SOURCE=.\SoundWin32.cxx +# End Source File +# Begin Source File + +SOURCE=.\SoundWin32.hxx +# End Source File +# Begin Source File + +SOURCE=.\stella.rc +# End Source File +# Begin Source File + +SOURCE=.\StellaXMain.cxx +# End Source File +# Begin Source File + +SOURCE=.\StellaXMain.hxx +# End Source File +# Begin Source File + +SOURCE=.\TextButton3d.cxx +# End Source File +# Begin Source File + +SOURCE=.\TextButton3d.hxx +# End Source File +# Begin Source File + +SOURCE=.\Timer.cxx +# End Source File +# Begin Source File + +SOURCE=.\Timer.hxx +# End Source File +# Begin Source File + +SOURCE=.\Wnd.cxx +# End Source File +# Begin Source File + +SOURCE=.\Wnd.hxx +# End Source File +# End Group +# Begin Group "Sound" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\sound\TIASound.c +# End Source File +# Begin Source File + +SOURCE=..\sound\TIASound.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\emucore\m6502\src\bspf\src\bspf.hxx +# End Source File +# Begin Source File + +SOURCE=..\..\build\M6502Hi.ins +# End Source File +# Begin Source File + +SOURCE=..\..\build\M6502Low.ins +# End Source File +# Begin Source File + +SOURCE=.\STELLA.ICO +# End Source File +# Begin Source File + +SOURCE=.\tile.bmp +# End Source File +# End Target +# End Project diff --git a/stella/src/ui/win32/Stella.dsw b/stella/src/ui/win32/Stella.dsw new file mode 100644 index 000000000..c095bd228 --- /dev/null +++ b/stella/src/ui/win32/Stella.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Stella"=.\Stella.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/stella/src/ui/win32/StellaXMain.cxx b/stella/src/ui/win32/StellaXMain.cxx new file mode 100644 index 000000000..e17598738 --- /dev/null +++ b/stella/src/ui/win32/StellaXMain.cxx @@ -0,0 +1,397 @@ +// +// StellaX +// Jeff Miller 05/13/2000 +// +#include "pch.hxx" +#include "StellaXMain.hxx" + +#include +#include +#include +#include + +#include "bspf.hxx" +#include "Console.hxx" +#include "DefProps.hxx" +#include "Event.hxx" +#include "MediaSrc.hxx" +#include "PropsSet.hxx" + +#include "resource.h" +#include "GlobalData.hxx" +#include "SoundWin32.hxx" + +// +// Undefining USE_FS will use the (untested) windowed mode +// + +#define USE_FS + +#ifdef USE_FS +#include "DirectXFullScreen.hxx" +#else +#include "DirectXWindow.hxx" +#endif + +#include "DirectInput.hxx" +#include "DirectDraw.hxx" + +// +// CStellaXMain +// +// equivalent to main() in the DOS version of stella +// + +#define FORCED_VIDEO_CX 640 +#define FORCED_VIDEO_CY 480 + +#ifdef USE_MY_STELLAPRO +#include "misc\stellapro.h" +#endif + +CStellaXMain::CStellaXMain( + ) : \ + m_pPropertiesSet( NULL ) +{ + TRACE( "CStellaXMain::CStellaXMain" ); +} + +CStellaXMain::~CStellaXMain( + ) +{ + TRACE( "CStellaXMain::~CStellaXMain" ); + + delete m_pPropertiesSet; + m_pPropertiesSet = NULL; +} + +DWORD CStellaXMain::Initialize( + void + ) +{ + TRACE( "CStellaXMain::SetupProperties" ); + + // Create a properties set for us to use + + if ( m_pPropertiesSet ) + { + return ERROR_SUCCESS; + } + + m_pPropertiesSet = new PropertiesSet("Cartridge.Name"); + if ( m_pPropertiesSet == NULL ) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + + // Try to load the file stella.pro file + + string filename( "stella.pro" ); + + // See if we can open the file + ifstream stream(filename.c_str()); + if(stream) + { + // File was opened so load properties from it + m_pPropertiesSet->load(stream, &Console::defaultProperties()); + } + else + { +#ifdef USE_MY_STELLAPRO + int cPropSet = sizeof( g_propset ) / sizeof( g_propset[0] ); + int iPropSet; + for ( iPropSet = 0; iPropSet < cPropSet; ++iPropSet ) + { + Properties properties( &Console::defaultProperties() ); + + PROPSET::PROPS* pProps = g_propset[iPropSet].props; + + while ( pProps->key != NULL ) + { + properties.set( pProps->key, pProps->value ); + ++pProps; + } + + m_pPropertiesSet->insert( properties ); + } +#else + // @@ REVIEW: This is REALLY SLOW! + + // Couldn't open the file so use the builtin properties file + strstream builtin; + for(const char** p = defaultPropertiesFile(); *p != 0; ++p) + { + builtin << *p << endl; + } + +#ifdef _DEBUG + DWORD dwStartTick = ::GetTickCount(); +#endif + m_pPropertiesSet->load( builtin, &Console::defaultProperties() ); +#ifdef _DEBUG + TRACE( "Elapsed ticks for load = %ld", ::GetTickCount()-dwStartTick ); +#endif + +#endif + } + + + return ERROR_SUCCESS; +} + +HRESULT CStellaXMain::PlayROM( + HWND hwnd, + LPCTSTR pszPathName, + LPCTSTR pszFriendlyName, + CGlobalData& rGlobalData + ) +{ + UNUSED_ALWAYS( hwnd ); + + HRESULT hr = S_OK; + + TRACE("CStellaXMain::PlayROM"); + + // + // show wait cursor while loading + // + + HCURSOR hcur = ::SetCursor( ::LoadCursor( NULL, IDC_WAIT ) ); + + // + // setup objects used here + // + + BYTE* pImage = NULL; + LPCTSTR pszFileName = NULL; + +#ifdef USE_FS + CDirectXFullScreen* pwnd = NULL; +#else + CDirectXWindow* pwnd = NULL; +#endif + Console* pConsole = NULL; + Sound* pSound = NULL; + Event rEvent; + + // + // Load the rom file + // + + HANDLE hFile; + DWORD dwImageSize; + + hFile = ::CreateFile( pszPathName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if (hFile == INVALID_HANDLE_VALUE) + { + HINSTANCE hInstance = (HINSTANCE)::GetWindowLong( hwnd, GWL_HINSTANCE ); + + DWORD dwLastError = ::GetLastError(); + + TCHAR pszCurrentDirectory[ MAX_PATH + 1 ]; + ::GetCurrentDirectory( MAX_PATH, pszCurrentDirectory ); + + // ::MessageBoxFromGetLastError( pszPathName ); + TCHAR pszFormat[ 1024 ]; + LoadString( hInstance, + IDS_ROM_LOAD_FAILED, + pszFormat, 1023 ); + + LPTSTR pszLastError = NULL; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dwLastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&pszLastError, + 0, + NULL); + + TCHAR pszError[ 1024 ]; + wsprintf( pszError, + pszFormat, + pszCurrentDirectory, + pszPathName, + dwLastError, + pszLastError ); + + ::MessageBox( hwnd, + pszError, + _T("Error"), + MB_OK | MB_ICONEXCLAMATION ); + + ::LocalFree( pszLastError ); + + hr = HRESULT_FROM_WIN32( ::GetLastError() ); + goto exit; + } + + dwImageSize = ::GetFileSize( hFile, NULL ); + + pImage = new BYTE[dwImageSize + 1]; + if ( pImage == NULL ) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + DWORD dwActualSize; + if ( ! ::ReadFile( hFile, pImage, dwImageSize, &dwActualSize, NULL ) ) + { + VERIFY( ::CloseHandle( hFile ) ); + + MessageBoxFromGetLastError( pszPathName ); + + hr = HRESULT_FROM_WIN32( ::GetLastError() ); + goto exit; + } + + VERIFY( ::CloseHandle(hFile) ); + + // + // Create Sound driver object + // (Will be initialized once we have a window handle below) + // + + if (rGlobalData.NoSound()) + { + TRACE("Creating Sound driver"); + pSound = new Sound; + } + else + { + TRACE("Creating SoundWin32 driver"); + pSound = new SoundWin32; + } + if ( pSound == NULL ) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + // + // get just the filename + // + + pszFileName = _tcsrchr( pszPathName, _T('\\') ); + if ( pszFileName ) + { + ++pszFileName; + } + else + { + pszFileName = pszPathName; + } + + try + { + // If this throws an exception, then it's probably a bad cartridge + + pConsole = new Console( pImage, + dwActualSize, + pszFileName, + rEvent, + *m_pPropertiesSet, + *pSound ); + if ( pConsole == NULL ) + { + hr = E_OUTOFMEMORY; + goto exit; + } + } + catch (...) + { + MessageBox(rGlobalData.ModuleInstance(), + NULL, IDS_CANTSTARTCONSOLE); + + goto exit; + } + +#ifdef USE_FS + pwnd = new CDirectXFullScreen( rGlobalData, + pConsole, + rEvent ); +#else + pwnd = new CDirectXWindow( rGlobalData, + pConsole, + rEvent ); +#endif + if ( pwnd == NULL ) + { + hr = E_OUTOFMEMORY; + goto exit; + } + +#ifdef USE_FS + if ( rGlobalData.AutoSelectVideoMode() ) + { + hr = pwnd->Initialize( ); + } + else + { + // + // Initialize with 640 x 480 + // + + hr = pwnd->Initialize( FORCED_VIDEO_CX, FORCED_VIDEO_CY ); + } +#else + hr = pwnd->Initialize( hwnd, pszFriendlyName ); +#endif + if ( FAILED(hr) ) + { + TRACE( "CWindow::Initialize failed, err = %X", hr ); + goto exit; + } + + if (!rGlobalData.NoSound()) + { + // + // 060499: Pass pwnd->GetHWND() in instead of hwnd as some systems + // will not play sound if this isn't set to the active window + // + + SoundWin32* pSoundWin32 = static_cast( pSound ); + + hr = pSoundWin32->Initialize( *pwnd ); + if ( FAILED(hr) ) + { + TRACE( "Sndwin32 Initialize failed, err = %X", hr ); + goto exit; + } + } + + // restore cursor + + ::SetCursor( hcur ); + hcur = NULL; + + ::ShowWindow( hwnd, SW_HIDE ); + + (void)pwnd->Run(); + + ::ShowWindow( hwnd, SW_SHOW ); + +exit: + + if ( hcur ) + { + ::SetCursor( hcur ); + } + + delete pwnd; + + delete pConsole; + delete pSound; + delete pImage; + + return hr; +} diff --git a/stella/src/ui/win32/StellaXMain.hxx b/stella/src/ui/win32/StellaXMain.hxx new file mode 100644 index 000000000..403316a8a --- /dev/null +++ b/stella/src/ui/win32/StellaXMain.hxx @@ -0,0 +1,41 @@ +// +// StellaX +// Jeff Miller 05/12/2000 +// +#ifndef STELLAX_H +#define STELLAX_H +#pragma once + +class PropertiesSet; +class CGlobalData; + +class CStellaXMain +{ +public: + + CStellaXMain(); + ~CStellaXMain(); + + DWORD Initialize( void ); + + HRESULT PlayROM( HWND hwnd, LPCTSTR ctszPathName, + LPCTSTR pszFriendlyName, + CGlobalData& rGlobalData ); + PropertiesSet& GetPropertiesSet() const; + +private: + + PropertiesSet* m_pPropertiesSet; + + CStellaXMain( const CStellaXMain& ); // no implementation + void operator=( const CStellaXMain& ); // no implementation +}; + +inline PropertiesSet& CStellaXMain::GetPropertiesSet( + void + ) const +{ + return *m_pPropertiesSet; +} + +#endif \ No newline at end of file diff --git a/stella/src/ui/win32/TextButton3d.cxx b/stella/src/ui/win32/TextButton3d.cxx new file mode 100644 index 000000000..e2ccb5760 --- /dev/null +++ b/stella/src/ui/win32/TextButton3d.cxx @@ -0,0 +1,186 @@ +// +// StellaX +// Jeff Miller 10/12/1999 +// +#include "pch.hxx" +#include "TextButton3d.hxx" + +// button style should be VISIBLE / DISABLED / OWNERDRAW + +CTextButton3d::CTextButton3d +( +) +{ +} + +LRESULT CTextButton3d::WndProc +( + HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam, + BOOL& rfHandled +) +{ + switch (msg) + { +#if 0 + + // TODO: re do this now that I send this message here + case WM_DRAWITEM: + rfHandled = TRUE; + pThis->OnDrawItem(hWnd, (UINT)wParam, (LPDRAWITEMSTRUCT)lParam); + return TRUE; +#endif + + case WM_PAINT: + rfHandled = TRUE; + OnPaint(hWnd, (HDC)wParam); + return TRUE; + + case WM_ERASEBKGND: + rfHandled = TRUE; + return OnEraseBkgnd(hWnd, (HDC)wParam); + } + + return 0; +} + +void CTextButton3d::OnPaint +( + HWND hwnd, + HDC hdcPaint +) +{ + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + + int cxClient, cyClient; + + RECT rc; + GetClientRect(hwnd, &rc); + + RECT rcClient; + CopyRect(&rcClient, &rc); + + // lpdis->rcItem, lpdis->itemState + + int l = GetWindowTextLength(hwnd); + LPTSTR text = new TCHAR[l + 2]; + if (text == NULL) + { + EndPaint(hwnd, &ps); + return; + } + GetWindowText(hwnd, text, l+1); + + HFONT hfont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); + LOGFONT lf; + GetObject(hfont, sizeof(lf), &lf); + if (lf.lfHeight == 0) + lf.lfHeight = 20; + lf.lfWidth = 0; + lf.lfWeight = FW_BLACK; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + HFONT hfontTry = CreateFontIndirect(&lf); + + HFONT hfontOld = (HFONT)SelectObject(hdc, hfontTry); + + rcClient.left += 3; + rcClient.top += 3; + rcClient.right -= 3; + rcClient.bottom -= 3; + + cxClient = rcClient.right - rcClient.left; + cyClient = rcClient.bottom - rcClient.top; + + SIZE sizeTextClient; + GetTextExtentPoint(hdc, text, lstrlen(text), + &sizeTextClient); + if (cxClient*sizeTextClient.cy > cyClient*sizeTextClient.cx) + { + lf.lfHeight = MulDiv(lf.lfHeight, cyClient, sizeTextClient.cy); + } + else + { + lf.lfHeight = MulDiv(lf.lfHeight, cxClient, sizeTextClient.cx); + } + + lf.lfHeight--; + + rcClient.left -= 3; + rcClient.top -= 3; + rcClient.right += 3; + rcClient.bottom += 3; + + cxClient = rcClient.right - rcClient.left; + cyClient = rcClient.bottom - rcClient.top; + + hfont = CreateFontIndirect(&lf); + SelectObject(hdc, hfont); + GetTextExtentPoint(hdc, text, lstrlen(text), + &sizeTextClient); + + int minx = rcClient.left + (cxClient - sizeTextClient.cx) / 2; + int miny = rcClient.top + (cyClient - sizeTextClient.cy) / 2; + + int iOldBkMode = SetBkMode(hdc, TRANSPARENT); + COLORREF crText = GetSysColor(COLOR_BTNTEXT); + COLORREF crOldText = SetTextColor(hdc, crText); + + int cx = minx; + int cy = miny; + + int s = 1; + cx += 3; + cy += 3; + + // draw 3D highlights + + SetTextColor(hdc, GetSysColor(COLOR_3DDKSHADOW)); + TextOut(hdc, cx-s*2, cy+s*2, text, lstrlen(text)); + TextOut(hdc, cx+s*2, cy-s*2, text, lstrlen(text)); + TextOut(hdc, cx+s*2, cy+s*2, text, lstrlen(text)); + SetTextColor(hdc, ::GetSysColor(COLOR_3DHILIGHT)); + TextOut(hdc, cx+s*1, cy-s*2, text, lstrlen(text)); + TextOut(hdc, cx-s*2, cy+s*1, text, lstrlen(text)); + TextOut(hdc, cx-s*2, cy-s*2, text, lstrlen(text)); + SetTextColor(hdc, GetSysColor(COLOR_3DSHADOW)); + TextOut(hdc, cx-s*1, cy+s*1, text, lstrlen(text)); + TextOut(hdc, cx+s*1, cy-s*1, text, lstrlen(text)); + TextOut(hdc, cx+s*1, cy+s*1, text, lstrlen(text)); + SetTextColor(hdc, GetSysColor(COLOR_3DLIGHT)); + TextOut(hdc, cx, cy-s*1, text, lstrlen(text)); + TextOut(hdc, cx-s*1, cy, text, lstrlen(text)); + TextOut(hdc, cx-s*1, cy-s*1, text, lstrlen(text)); + SetTextColor(hdc, crText); + + // draw the text + + TextOut(hdc, cx, cy, text, lstrlen(text)); + + // restore dc + + SetTextColor(hdc, crOldText); + SetBkMode(hdc, iOldBkMode); + SelectObject(hdc, hfontOld); + + DeleteObject(hfont); + DeleteObject(hfontTry); + + delete[] text; + + EndPaint(hwnd, &ps); +} + +BOOL CTextButton3d::OnEraseBkgnd +( + HWND hwnd, + HDC hdc +) +{ + // dont do any erasing + + return TRUE; +} diff --git a/stella/src/ui/win32/TextButton3d.hxx b/stella/src/ui/win32/TextButton3d.hxx new file mode 100644 index 000000000..51a221368 --- /dev/null +++ b/stella/src/ui/win32/TextButton3d.hxx @@ -0,0 +1,33 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef TXTBTN_H +#define TXTBTN_H +#pragma once + +// derived from Roger Onslow's code + +#include "Wnd.hxx" + +class CTextButton3d : public CWnd +{ +public: + + CTextButton3d(); + +protected: + + LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, + BOOL& rfHandled); + +private: + + void OnPaint(HWND, HDC); + BOOL OnEraseBkgnd(HWND, HDC); + + CTextButton3d( const CTextButton3d& ); // no implementation + void operator=( const CTextButton3d& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/Timer.cxx b/stella/src/ui/win32/Timer.cxx new file mode 100644 index 000000000..5a4d18537 --- /dev/null +++ b/stella/src/ui/win32/Timer.cxx @@ -0,0 +1,89 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#include "pch.hxx" +#include "Timer.hxx" + +CMmTimer::CMmTimer( + void + ) : + m_mmrIDTimer( NULL ) +{ + TRACE( "CMmTimer::CMmTimer" ); +} + +CMmTimer::~CMmTimer( + void + ) +{ + TRACE( "CMmTimer::~CMmTimer" ); + + if ( m_mmrIDTimer != NULL ) + { + ::timeKillEvent( m_mmrIDTimer ); + m_mmrIDTimer = NULL; + } +} + + +BOOL CMmTimer::Create( + UINT nPeriod, + UINT nRes, + DWORD dwUser, + TIMERCALLBACK pfnCallback + ) +{ + BOOL fRet = TRUE; + + TRACE( "CMmTimer::Create" ); + + ASSERT( pfnCallback != NULL ); + ASSERT( nPeriod > 10 ); + ASSERT( nPeriod >= nRes ); + + m_nPeriod = nPeriod; + m_nRes = nRes; + m_dwUser = dwUser; + m_pfnCallback = pfnCallback; + + m_mmrIDTimer = ::timeSetEvent( m_nPeriod, + m_nRes, + TimeProc, + (DWORD)this, + TIME_PERIODIC ); + if ( m_mmrIDTimer == NULL ) + { + fRet = FALSE; + } + + return fRet; +} + + +// Timer proc for multimedia timer callback set with timeSetTime(). +// +// Calls procedure specified when Timer object was created. The +// dwUser parameter contains "this" pointer for associated Timer object. +// + +void CALLBACK CMmTimer::TimeProc( + UINT uID, + UINT uMsg, + DWORD dwUser, + DWORD dw1, + DWORD dw2 + ) +{ + // dwUser contains ptr to Timer object + + CMmTimer* pThis = (CMmTimer*)dwUser; + + ASSERT( pThis != NULL ); + + // Call user-specified callback and pass back user specified data + + ASSERT( pThis->m_pfnCallback != NULL ); + + pThis->m_pfnCallback( pThis->m_dwUser ); +} diff --git a/stella/src/ui/win32/Timer.hxx b/stella/src/ui/win32/Timer.hxx new file mode 100644 index 000000000..3bb8c0a33 --- /dev/null +++ b/stella/src/ui/win32/Timer.hxx @@ -0,0 +1,46 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef TIMER_H +#define TIMER_H + +// Constants + +typedef BOOL ( * TIMERCALLBACK )( DWORD ); + +// Classes + +// Timer +// +// Wrapper class for Windows multimedia timer services. Provides +// both periodic and one-shot events. User must supply callback +// for periodic events. +// + +class CMmTimer +{ +public: + + CMmTimer( void ); + ~CMmTimer( void ); + + BOOL Create( UINT nPeriod, UINT nRes, DWORD dwUser, TIMERCALLBACK pfnCallback ); + +protected: + + static void CALLBACK TimeProc( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2 ); + + TIMERCALLBACK m_pfnCallback; + DWORD m_dwUser; + UINT m_nPeriod; + UINT m_nRes; + MMRESULT m_mmrIDTimer; + +private: + + CMmTimer( const CMmTimer& ); // no implementation + void operator=( const CMmTimer& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/Wnd.cxx b/stella/src/ui/win32/Wnd.cxx new file mode 100644 index 000000000..b1e500d0e --- /dev/null +++ b/stella/src/ui/win32/Wnd.cxx @@ -0,0 +1,75 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#include "pch.hxx" +#include "Wnd.hxx" + +// REVIEW: Need to reset old wnd proc? + +CWnd::CWnd( + ) : \ + m_pOldWindowProc(NULL), + m_hwnd(NULL) +{ +} + +BOOL CWnd::SubclassDlgItem( + HWND hwndParent, + UINT nID + ) +{ + if ( nID == 0 || hwndParent == NULL ) + { + return FALSE; + } + + // can't subclass twice! + + if ( m_pOldWindowProc != NULL ) + { + return FALSE; + } + + m_hwnd = GetDlgItem( hwndParent, nID ); + if (m_hwnd == NULL) + { + ASSERT( FALSE ); + return FALSE; + } + + PreSubclassWindow( m_hwnd ); + + m_pOldWindowProc = (WNDPROC)GetWindowLong( m_hwnd, GWL_WNDPROC ); + SetWindowLong( m_hwnd, GWL_USERDATA, (LONG)this ); + SetWindowLong( m_hwnd, GWL_WNDPROC, (LONG)StaticWndProc ); + + return TRUE; +} + +void CWnd::PreSubclassWindow( + HWND hwnd + ) +{ + // no default behavior +} + +LRESULT CALLBACK CWnd::StaticWndProc( + HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam + ) +{ + CWnd* pThis = (CWnd*)GetWindowLong(hWnd, GWL_USERDATA); + + BOOL fHandled = FALSE; + LRESULT lRes = pThis->WndProc( hWnd, msg, wParam, lParam, fHandled ); + + if (fHandled) + { + return lRes; + } + + return CallWindowProc( pThis->m_pOldWindowProc, hWnd, msg, wParam, lParam ); +} diff --git a/stella/src/ui/win32/Wnd.hxx b/stella/src/ui/win32/Wnd.hxx new file mode 100644 index 000000000..ad14cf991 --- /dev/null +++ b/stella/src/ui/win32/Wnd.hxx @@ -0,0 +1,39 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef WND_H +#define WND_H + +class CWnd +{ +public: + + CWnd(); + + BOOL SubclassDlgItem( HWND hwndParent, UINT nID ); + + operator HWND() const + { + return m_hwnd; + } + +protected: + + virtual void PreSubclassWindow( HWND hwnd ); + virtual LRESULT WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, + BOOL& rfHandled ) = 0; + +private: + + static LRESULT CALLBACK StaticWndProc( HWND hWnd, UINT msg, + WPARAM wParam, LPARAM lParam ); + + WNDPROC m_pOldWindowProc; + HWND m_hwnd; + + CWnd( const CWnd& ); // no implementation + void operator=( const CWnd& ); // no implementation +}; + +#endif diff --git a/stella/src/ui/win32/debug.cxx b/stella/src/ui/win32/debug.cxx new file mode 100644 index 000000000..f4682d20b --- /dev/null +++ b/stella/src/ui/win32/debug.cxx @@ -0,0 +1,75 @@ +// +// StellaX +// Jeff Miller 04/27/2000 +// + +#include "debug.hxx" + +#ifdef _DEBUG // entire file + +// log file name (or NULL for no log) -- must be defined by client + +extern LPCTSTR g_ctszDebugLog; + +// --------------------------------------------------------------------------- +// DEBUG DEFINED + +void AFX_CDECL AfxTrace( + LPCTSTR lpszFormat, + ... + ) +{ + va_list args; + va_start(args, lpszFormat); + + int nBuf; + TCHAR szBuffer[512]; + + nBuf = _vsntprintf(szBuffer, _countof(szBuffer), lpszFormat, args); + + // was there an error? was the expanded string too long? + ASSERT(nBuf >= 0); + + if (nBuf < 511) + { + lstrcat(szBuffer, _T("\r\n")); + } + + _RPT0(_CRT_WARN, szBuffer); + + if (g_ctszDebugLog) + { + HANDLE hfile = CreateFile(g_ctszDebugLog, GENERIC_WRITE, 0, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hfile != INVALID_HANDLE_VALUE) + { + SetFilePointer(hfile, 0, NULL, FILE_END); + + DWORD dw; + WriteFile(hfile, szBuffer, (lstrlen(szBuffer)+1)*sizeof(TCHAR), + &dw, NULL); + + CloseHandle(hfile); + } + } + + va_end(args); +} + +BOOL AFXAPI AfxAssertFailedLine( + LPCSTR lpszFileName, + int nLine, + LPCTSTR lpszCondition + ) +{ + // we remove WM_QUIT because if it is in the queue then the message box + // won't display + MSG msg; + BOOL bQuit = PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); + BOOL bResult = _CrtDbgReport(_CRT_ASSERT, lpszFileName, nLine, NULL, lpszCondition); + if (bQuit) + PostQuitMessage(msg.wParam); + return bResult; +} + +#endif //_DEBUG diff --git a/stella/src/ui/win32/debug.hxx b/stella/src/ui/win32/debug.hxx new file mode 100644 index 000000000..9e198047c --- /dev/null +++ b/stella/src/ui/win32/debug.hxx @@ -0,0 +1,99 @@ +// +// StellaX +// Jeff Miller 05/27/1999 +// + +#ifndef _AFX +#define _AFX +#pragma once + +#include +#include + +// --------------------------------------------------------------------------- +// This file defines: +// _countof +// TRACE, TRACE0, TRACE1, TRACE2, TRACE3 +// ASSERT +// VERIFY +// DEBUG_ONLY + + + +// determine number of elements in an array (not bytes) +#define _countof(array) (sizeof(array)/sizeof(array[0])) + +// AFXAPI is used on global public functions +#ifndef AFXAPI + #define AFXAPI __stdcall +#endif + +// AFX_CDECL is used for rare functions taking variable arguments +#ifndef AFX_CDECL + #define AFX_CDECL __cdecl +#endif + + +#ifdef _DEBUG + +// --------------------------------------------------------------------------- +// DEBUG DEFINED + +#include + +#include +#include + +#include + +#ifndef AfxDebugBreak +#define AfxDebugBreak() _CrtDbgBreak() +#endif + +BOOL AFXAPI AfxAssertFailedLine(LPCSTR lpszFileName, int nLine, LPCTSTR lpszCondition); + +void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...); + +#define TRACE ::AfxTrace +#define THIS_FILE __FILE__ +#define ASSERT(f) \ + do \ + { \ + if (!(f) && AfxAssertFailedLine(THIS_FILE, __LINE__, #f)) \ + AfxDebugBreak(); \ + } while (0) \ + +#define VERIFY(f) ASSERT(f) +#define DEBUG_ONLY(f) (f) + +// The following trace macros are provided for backward compatiblity +// (they also take a fixed number of parameters which provides +// some amount of extra error checking) +#define TRACE0(sz) ::AfxTrace(_T("%s"), _T(sz)) +#define TRACE1(sz, p1) ::AfxTrace(_T(sz), p1) +#define TRACE2(sz, p1, p2) ::AfxTrace(_T(sz), p1, p2) +#define TRACE3(sz, p1, p2, p3) ::AfxTrace(_T(sz), p1, p2, p3) + +#else // _DEBUG + +// --------------------------------------------------------------------------- +// DEBUG NOT DEFINED + +#ifdef AfxDebugBreak +#undef AfxDebugBreak +#endif +#define AfxDebugBreak() + +#define ASSERT(f) ((void)0) +#define VERIFY(f) ((void)(f)) +#define DEBUG_ONLY(f) ((void)0) +inline void AFX_CDECL AfxTrace(LPCTSTR, ...) { } +#define TRACE 1 ? (void)0 : ::AfxTrace +#define TRACE0(sz) +#define TRACE1(sz, p1) +#define TRACE2(sz, p1, p2) +#define TRACE3(sz, p1, p2, p3) + +#endif // !_DEBUG + +#endif diff --git a/stella/src/ui/win32/main.cxx b/stella/src/ui/win32/main.cxx new file mode 100644 index 000000000..2505719b1 --- /dev/null +++ b/stella/src/ui/win32/main.cxx @@ -0,0 +1,185 @@ +/* + + StellaX + Win32 DirectX port of Stella + + Written by Jeff Miller (contact Bradford for current email address) + + Stella core developed by Bradford W. Mott + + This software is Copyright (c) 1995-2000, Jeff Miller + +REVISIONS: + + 14-Mar-99 1.1.0 new code base + + 19-Mar-99 1.1.1 took into account video width + fixed no sound card bug + -mute option + fixed mame32 www link + + 11-Jun-99 1.1.2 removed registerfiletypes call + fixed directsound + added directinput support for keyboard, mouse, joystick + fixed minimize button bug + created virtual list view to speed load time + add screen capture (f12 key) + added romdir read from STELLA.INI file + (defaults to ROMS directory) + I now use WM_SETICON so the stella icon shows on the task list + Works on NT4 (dont fail if DirectInput not available) + Added UI field for a cartridge.note + Changed stellax web site address + Added really cool help property sheet w/adobe doc + + 9-Sep-99 1.1.3 Improved video detection logic + Added DisableJoystick and ListSort options to .ini + Rewrote the sound driver code + Added multiple screen shot support - first writes to + stella00.bmp then stella01.bmp, etc. + Added cool round buttons on main screen + 18-Apr-00 Started removing all exceptions + Fixed sound code (now uses streaming properly) + Added configuration dialog + Fixed it so that Alt-F4 will close the dialog + Updated the master ROM list + + 02-May-00 1.1.3a Fixed joystick handling regression + Fixed repaint problem on doc page + Fixed problem where some machines would report path not found + Added force 640x480 video mode + Added browse button on config dialog + + 05-Jan-02 n/a Wow, it's been awhile...Released source code + +*/ + +#include "pch.hxx" +#include "resource.h" + +#include "GlobalData.hxx" +#include "MainDlg.hxx" + +class CSingleInstance +{ +public: + + CSingleInstance( LPCTSTR pszName ) + { + ::SetLastError( ERROR_SUCCESS ); + + m_hMutex = ::CreateMutex( NULL, TRUE, pszName ); + + m_dwError = ::GetLastError(); + } + + ~CSingleInstance() + { + if ( m_hMutex != INVALID_HANDLE_VALUE && + m_dwError != ERROR_ALREADY_EXISTS ) + { + VERIFY( ::ReleaseMutex( m_hMutex ) ); + VERIFY( ::CloseHandle( m_hMutex ) ); + } + } + + BOOL AlreadyExists( void ) const + { + return ( m_dwError == ERROR_ALREADY_EXISTS ); + } + +private: + + HANDLE m_hMutex; + DWORD m_dwError; + + CSingleInstance( const CSingleInstance& ); // no implementation + void operator=( const CSingleInstance& ); // no implementation +}; + +// see debug.cpp + +LPCTSTR g_ctszDebugLog = _T("stella.log"); + + +int WINAPI _tWinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow + ) +{ + UNUSED_ALWAYS( hPrevInstance ); + UNUSED_ALWAYS( lpCmdLine ); + UNUSED_ALWAYS( nCmdShow ); + + DWORD dwRet; + + (void)::DeleteFile(g_ctszDebugLog); + + CSingleInstance mutex( _T("StellaXMutex") ); + if ( mutex.AlreadyExists() ) + { + MessageBox( hInstance, NULL, IDS_ALREADYRUNNING ); + return 1; + } + + HRESULT hrCoInit = ::CoInitialize( NULL ); + if ( FAILED(hrCoInit) ) + { + MessageBox( hInstance, NULL, IDS_COINIT_FAILED ); + } + + ::InitCommonControls(); + + BOOL fOk = FALSE; + + CGlobalData globaldata( hInstance ); + + fOk = globaldata.ParseCommandLine( __argc, __argv ); + if (!fOk) + { + MessageBox( hInstance, NULL, IDS_BADARGUMENT ); + } + else + { + LPCTSTR ctszPathName = globaldata.PathName(); + if (ctszPathName != NULL) + { + // + // a filename was given on the commandline, skip the UI + // + + CStellaXMain stellax; + + dwRet = stellax.Initialize(); + if ( dwRet != ERROR_SUCCESS ) + { + MessageBoxFromWinError( dwRet, _T("CStellaX::Initialize") ); + } + else + { + dwRet = stellax.PlayROM( GetDesktopWindow(), + ctszPathName, + _T("StellaX"), // Dont knwo the friendly name + globaldata ); + } + } + else + { + // + // show the ui + // + + CMainDlg dlg( globaldata, hInstance ); + dlg.DoModal( NULL ); + } + } + + if ( hrCoInit == S_OK ) + { + ::CoUninitialize(); + } + + return 0; +} diff --git a/stella/src/ui/win32/misc/ConvPro.cxx b/stella/src/ui/win32/misc/ConvPro.cxx new file mode 100644 index 000000000..c331a980f --- /dev/null +++ b/stella/src/ui/win32/misc/ConvPro.cxx @@ -0,0 +1,185 @@ +#include "StdAfx.hxx" + +// +// Program to convert stella.pro into quick and easy format: +// +// struct PROPSET +// { +// struct PROPS { char* key; char* value; } props[25]; +// } g_propset[] = +// { +// { "key", "value", "key", "value", NULL, NULL, }, +// }; +// +// Jeff Miller 20-Apr-2000 + +void GetQuotedString( + char** ppszStart, + char** ppszEnd + ) +{ + char* pszStart = *ppszStart; + while ( *pszStart && *pszStart != '"' ) + { + ++pszStart; + } + if ( *pszStart == NULL ) + { + return; + } + ++pszStart; + + char* pszEnd = pszStart; + while ( *pszEnd && *pszEnd != '"' ) + { + ++pszEnd; + } + + if ( pszEnd ) + { + *pszEnd = '\0'; + ++pszEnd; + } + + *ppszStart = pszStart; + + if ( ppszEnd ) + { + *ppszEnd = pszEnd; + } +} + + +bool convert( + const char* pszIn, + const char* pszOut + ) +{ + bool fRet = false; + FILE* pfileIn = NULL; + FILE* pfileOut = NULL; + const int cchBuffer = 128; + char pszBuffer[cchBuffer+1]; + bool fNeedNew = true; + char* pszKey; + char* pszValue; + + + pfileIn = fopen( pszIn, "rt" ); + if ( pfileIn == NULL ) + { + goto cleanup; + } + + pfileOut = fopen( pszOut, "wt" ); + if ( pfileOut == NULL ) + { + goto cleanup; + } + + // + // write header to output file + // + + fprintf( pfileOut, "struct PROPSET\n{\n\tstruct PROPS { char* key; char* value; } props[25];\n} g_propset[] =\n{\n" ); + + for ( ; ; ) + { + if ( ! fgets( pszBuffer, cchBuffer, pfileIn ) ) + { + // + // Close up + // + + fprintf( pfileOut, "\n};\n" ); + break; + } + + // + // Remove the newline + // + int cch = strlen( pszBuffer ) - 1; + pszBuffer[ cch ] = '\0'; + + if ( cch == 0 ) + { + // + // ignore blank lines + // + + continue; + } + + if ( cch == 2 && pszBuffer[0] == '"' && pszBuffer[1] == '"' ) + { + // + // go to next item in prop set (terminate with two nulls) + // + + fprintf( pfileOut, " NULL, NULL, },\n" ); + fNeedNew = true; + continue; + } + + + // + // Handle comments + // + + if ( pszBuffer[0] == ';' ) + { + fprintf( pfileOut, "\t/* %s */\n", pszBuffer ); + continue; + } + + if ( fNeedNew ) + { + fprintf( pfileOut, "\t{ " ); + fNeedNew = false; + } + + // + // print data + // + + pszKey = pszBuffer; + GetQuotedString( &pszKey, &pszValue ); + + GetQuotedString( &pszValue, NULL ); + + fprintf( pfileOut, "\"%s\", \"%s\", ", pszKey, pszValue ); + } + + fRet = true; + +cleanup: + + if ( pfileIn ) + { + fclose( pfileIn ); + } + + if ( pfileOut ) + { + fclose( pfileOut ); + } + + return fRet; +} + +int main(int argc, char* argv[]) +{ + if ( argc != 3 ) + { + puts( "USAGE: convpro d:\\stella-1.1\\src\\emucore\\stella.pro stellapro.h" ); + return 1; + } + + if ( ! convert( argv[1], argv[2] ) ) + { + puts( "convert failed" ); + return 1; + } + + return 0; +} diff --git a/stella/src/ui/win32/misc/ConvPro.dsp b/stella/src/ui/win32/misc/ConvPro.dsp new file mode 100644 index 000000000..7e796dc8a --- /dev/null +++ b/stella/src/ui/win32/misc/ConvPro.dsp @@ -0,0 +1,105 @@ +# Microsoft Developer Studio Project File - Name="ConvPro" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=ConvPro - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ConvPro.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ConvPro.mak" CFG="ConvPro - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ConvPro - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "ConvPro - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ConvPro - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "ConvPro - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "ConvPro - Win32 Release" +# Name "ConvPro - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\ConvPro.cxx +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\StdAfx.hxx +# End Source File +# End Group +# End Target +# End Project diff --git a/stella/src/ui/win32/misc/ConvPro.dsw b/stella/src/ui/win32/misc/ConvPro.dsw new file mode 100644 index 000000000..5937c1d59 --- /dev/null +++ b/stella/src/ui/win32/misc/ConvPro.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "ConvPro"=".\ConvPro.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/stella/src/ui/win32/misc/StdAfx.cxx b/stella/src/ui/win32/misc/StdAfx.cxx new file mode 100644 index 000000000..1b967c470 --- /dev/null +++ b/stella/src/ui/win32/misc/StdAfx.cxx @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ConvPro.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "StdAfx.hxx" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/stella/src/ui/win32/misc/StdAfx.hxx b/stella/src/ui/win32/misc/StdAfx.hxx new file mode 100644 index 000000000..aaac26c3b --- /dev/null +++ b/stella/src/ui/win32/misc/StdAfx.hxx @@ -0,0 +1,21 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__EB8E67E2_17CD_11D4_A0C9_C677F56CA64D__INCLUDED_) +#define AFX_STDAFX_H__EB8E67E2_17CD_11D4_A0C9_C677F56CA64D__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include +#include + +// TODO: reference additional headers your program requires here + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__EB8E67E2_17CD_11D4_A0C9_C677F56CA64D__INCLUDED_) diff --git a/stella/src/ui/win32/misc/stellapro.h b/stella/src/ui/win32/misc/stellapro.h new file mode 100644 index 000000000..d9302861f --- /dev/null +++ b/stella/src/ui/win32/misc/stellapro.h @@ -0,0 +1,704 @@ +struct PROPSET +{ + struct PROPS { char* key; char* value; } props[25]; +} g_propset[] = +{ + /* ; */ + /* ; LAST MODIFIED: 08 Jun 1999 7:53AM ET */ + /* ; */ + { "Cartridge.MD5", "0db4f4150fecf77e4ce72ca4d04c052f", "Cartridge.Name", "3D Tic-Tac-Toe (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2618 / 4975123", "Cartridge.Rarity", "Uncommon", "Display.YStart", "37", NULL, NULL, }, + { "Cartridge.MD5", "f3213a8a702b0646d2eaf9ee0722b51c", "Cartridge.Name", "3D Tic-Tac-Toe (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2618 / 4975123", "Cartridge.Rarity", "Uncommon", "Display.YStart", "37", NULL, NULL, }, + { "Cartridge.MD5", "291bcdb05f2b37cdf9452d2bf08e0321", "Cartridge.Name", "32 in 1", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26163", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "c00734a2233ef683d9b6e622ac97a5c8", "Cartridge.Name", "A-Team, The", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26133", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Display.YStart", "44", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "17ee23e5da931be82f733917adcb6386", "Cartridge.Name", "Acid Drop", "Cartridge.Manufacturer", "Salu", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "57", "Cartridge.Type", "F6", "Display.YStart", "52", "Display.Height", "255", NULL, NULL, }, + { "Cartridge.MD5", "d573089534ca596e64efef474be7b6bc", "Cartridge.Name", "Action Man", "Cartridge.Note", "Uses paddle/joystick combination; variant to G.I. Joe-Cobra Strike by Parker Bros.", "Display.YStart", "57", "Controller.Left", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "157bddb7192754a45372be196797f284", "Cartridge.Name", "Adventure", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2613 / 4975154", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Cartridge.Note", "One player joystick only", "Display.YStart", "37", "Display.Height", "192", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "ca4f8c5b4d6fb9d608bb96bc7ebd26c7", "Cartridge.Name", "Adventures of Tron", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT4317", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.YStart", "46", "Display.Height", "180", NULL, NULL, }, + { "Cartridge.MD5", "c4bc8c2e130d76346ebf8eb544991b46", "Cartridge.Name", "Advertisement Cartridge", NULL, NULL, }, + { "Cartridge.MD5", "98e5e4d5c4dd9a986d30fd62bd2f75ae", "Cartridge.Name", "Air-Sea Battle (1)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2602", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Display.FrameRate", "30", "Display.YStart", "39", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "1d1d2603ec139867c1d1f5ddf83093f1", "Cartridge.Name", "Air-Sea Battle (2)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2602", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Display.FrameRate", "30", "Display.YStart", "39", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "16cb43492987d2f32b423817cdaaf7c4", "Cartridge.Name", "Air-Sea Battle (3)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2602", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Display.FrameRate", "30", "Display.YStart", "39", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "4d77f291dca1518d7d8e47838695f54b", "Cartridge.Name", "Airlock", "Cartridge.Manufacturer", "Data Age", "Cartridge.ModelNo", "DA 1004", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "a9cb638cd2cb2e8e0643d7a67db4281c", "Cartridge.Name", "Air Raiders (1)", "Cartridge.Manufacturer", "M-Network", "Cartridge.ModelNo", "MT5861", "Display.YStart", "37", "Display.Height", "239", NULL, NULL, }, + { "Cartridge.MD5", "35be55426c1fec32dfb503b4f0651572", "Cartridge.Name", "Air Raiders (2)", "Cartridge.Manufacturer", "CCE", "Cartridge.ModelNo", "C-817", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "37", "Display.Height", "239", NULL, NULL, }, + { "Cartridge.MD5", "f1a0a23e6464d954e3a9579c4ccd01c8", "Cartridge.Name", "Alien", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11006", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.FrameRate", "60", "Display.XStart", "16", "Display.Width", "128", "Display.YStart", "39", "Display.Height", "188", NULL, NULL, }, + { "Cartridge.MD5", "103f1756d9dc0dd2b16b53ad0f0f1859", "Cartridge.Name", "Alien's Return", "Cartridge.Manufacturer", "ITT Family Games", "Cartridge.ModelNo", "554-33391", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "67", "Display.Height", "214", NULL, NULL, }, + { "Cartridge.MD5", "9efb4e1a15a6cdd286e4bcd7cd94b7b8", "Cartridge.Name", "Alligator People", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.Rarity", "Unreleased Prototype", NULL, NULL, }, + { "Cartridge.MD5", "9e01f7f95cb8596765e03b9a36e8e33c", "Cartridge.Name", "Alpha Beam with Ernie", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26103", "Cartridge.Rarity", "Rare", "Controller.Left", "Keypad", "Controller.Right", "Keypad", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "acb7750b4d0c4bd34969802a7deb2990", "Cartridge.Name", "Amidar", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5310", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Cartridge.Note", "One player joystick only", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "40", "Display.Height", "182", "Console.LeftDifficulty", "A", "Console.RightDifficulty", "A", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "038e1e79c3d4410defde4bfe0b99cc32", "Cartridge.Name", "Aquaventure", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Cartridge.Type", "F8", "Emulation.HmoveBlanks", "No", "Display.FrameRate", "60", "Display.YStart", "35", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "a7b584937911d60c120677fe0d47f36f", "Cartridge.Name", "Armor Ambush", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5661", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.FrameRate", "60", "Display.YStart", "37", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "c77c35a6fc3c0f12bf9e8bae48cba54b", "Cartridge.Name", "Artillery Duel (1)", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "99004", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.Height", "186", NULL, NULL, }, + { "Cartridge.MD5", "3f039981255691d3859d04ef813a1264", "Cartridge.Name", "Artillery Duel (2)", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "99004", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.Height", "186", NULL, NULL, }, + { "Cartridge.MD5", "de78b3a064d374390ac0710f95edde92", "Cartridge.Name", "Assault", "Cartridge.Manufacturer", "Bomb", "Cartridge.ModelNo", "CA281", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "34", "Display.Height", "203", NULL, NULL, }, + { "Cartridge.MD5", "c5c7cc66febf2d4e743b4459de7ed868", "Cartridge.Name", "Asterix", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2696", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "67", "Display.Height", "187", NULL, NULL, }, + { "Cartridge.MD5", "b227175699e372b8fe10ce243ad6dda5", "Cartridge.Name", "Asteroids (Copyright Version)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2649 / 4975163", "Cartridge.Rarity", "Common", "Cartridge.Type", "F8", "Emulation.HmoveBlanks", "No", "Display.YStart", "37", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "ccbd36746ed4525821a8083b0d6d2c2c", "Cartridge.Name", "Asteroids", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2649", "Cartridge.Rarity", "Common", "Cartridge.Type", "F8", "Emulation.HmoveBlanks", "No", "Display.YStart", "37", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "75169c08b56e4e6c36681e599c4d8cc5", "Cartridge.Name", "Astroblast", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5666", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Uses left swapped paddles", "Controller.Left", "Joystick", "Controller.Right", "None", "Display.YStart", "27", "Display.Height", "202", NULL, NULL, }, + { "Cartridge.MD5", "8f53a3b925f0fd961d9b8c4d46ee6755", "Cartridge.Name", "Astro War", "Cartridge.Manufacturer", "Artic", "Cartridge.ModelNo", "SM8002", "Cartridge.Rarity", "Unbelievably Rare", "Display.Height", "202", NULL, NULL, }, + { "Cartridge.MD5", "3f540a30fdee0b20aed7288e4a5ea528", "Cartridge.Name", "Atari Video Cube", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2670", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.FrameRate", "60", "Display.YStart", "38", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "9ad36e699ef6f45d9eb6c4cf90475c9f", "Cartridge.Name", "Atlantis", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3203", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "20", "Display.YStart", "49", "Display.Height", "185", NULL, NULL, }, + { "Cartridge.MD5", "5b124850de9eea66781a50b2e9837000", "Cartridge.Name", "Bachelor Party", "Cartridge.Manufacturer", "Mystique", "Cartridge.ModelNo", "1002", "Cartridge.Rarity", "Extremely Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", "Display.YStart", "27", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "274d17ccd825ef9c728d68394b4569d2", "Cartridge.Name", "Bachelorette Party", "Cartridge.Manufacturer", "Mystique", "Cartridge.ModelNo", "1004", "Cartridge.Rarity", "Extremely Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", "Display.YStart", "27", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "8556b42aa05f94bc29ff39c39b11bff4", "Cartridge.Name", "Backgammon", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2617 / 6699848 / 4975183", "Cartridge.Rarity", "Extremely Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "00ce0bdd43aed84a983bef38fe7f5ee3", "Cartridge.Name", "Bank Heist", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11012", "Cartridge.Rarity", "Rare", "Display.Height", "187", NULL, NULL, }, + { "Cartridge.MD5", "73a710e621d44e97039d640071908aef", "Cartridge.Name", "Barber Pole Demo", NULL, NULL, }, + { "Cartridge.MD5", "f8240e62d8c0a64a61e19388414e3104", "Cartridge.Name", "Barnstorming", "Cartridge.Manufacturer", "Activision (Steve Cartwright)", "Cartridge.ModelNo", "AX-013", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "42", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "819aeeb9a2e11deb54e6de334f843894", "Cartridge.Name", "Basic Math / Fun With Numbers", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2661", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Released under two titles", "Display.YStart", "22", NULL, NULL, }, + { "Cartridge.MD5", "9f48eeb47836cf145a15771775f0767a", "Cartridge.Name", "Basic Programming", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX262", "Cartridge.Rarity", "Rare", "Controller.Left", "Keypad", "Controller.Right", "Keypad", "Display.Height", "220", NULL, NULL, }, + { "Cartridge.MD5", "d6dc9b4508da407e2437bfa4de53d1b2", "Cartridge.Name", "Base Attack", "Cartridge.Manufacturer", "Homevision", "Cartridge.ModelNo", "13", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "67", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "ab4ac994865fb16ebb85738316309457", "Cartridge.Name", "Basketball", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2624", "Cartridge.Rarity", "Common", "Display.YStart", "42", "Display.Height", "193", NULL, NULL, }, + { "Cartridge.MD5", "e13c7627b2e136b9c449d9e8925b4547", "Cartridge.Name", "Basketball (4K version)", "Cartridge.Note", "4K version of 2K ROM", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2624", "Cartridge.Rarity", "Common", "Display.YStart", "42", "Display.Height", "193", NULL, NULL, }, + { "Cartridge.MD5", "41f252a66c6301f1e8ab3612c19bc5d4", "Cartridge.Name", "Battlezone", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2681", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F8", "Emulation.HmoveBlanks", "No", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "32", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.Name", "Beamrider (1)", "Cartridge.MD5", "3604e725e81dd0abede07fd1c82eb058", "Cartridge.Manufacturer", "Activision (Dave Rolle)", "Cartridge.ModelNo", "AZ-037-04", "Cartridge.Rarity", "Rare", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "79ab4123a83dc11d468fb2108ea09e2e", "Cartridge.Name", "Beamrider (2)", "Cartridge.Manufacturer", "Activision (Dave Rolle)", "Cartridge.ModelNo", "AZ-037-04", "Cartridge.Rarity", "Rare", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "d0b9df57bfea66378c0418ec68cfe37f", "Cartridge.Name", "Beany Bopper", "Cartridge.Manufacturer", "20th Century Fox Video Games (Sirius)", "Cartridge.ModelNo", "11002", "Cartridge.Rarity", "Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "ee6665683ebdb539e89ba620981cb0f6", "Cartridge.Name", "Bearenstein Bears", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2658", "Cartridge.Rarity", "Unbelieveably Rare", "Cartridge.Type", "F8", NULL, NULL, }, + { "Cartridge.MD5", "59e96de9628e8373d1c685f5e57dcf10", "Cartridge.Name", "Beat 'Em and Eat 'Em", "Cartridge.Manufacturer", "Mystique", "Cartridge.ModelNo", "1003", "Cartridge.Rarity", "Extremely Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "073d7aff37b7601431e4f742c36c0dc1", "Cartridge.Name", "Bermuda", "Cartridge.Manufacturer", "Rainbow Vision", "Cartridge.ModelNo", "SS-009", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "30", "Display.Height", "199", NULL, NULL, }, + { "Cartridge.MD5", "b8ed78afdb1e6cfe44ef6e3428789d5f", "Cartridge.Name", "Bermuda Triangle", "Cartridge.Manufacturer", "Data Age", "Cartridge.ModelNo", "112-007", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "136f75c4dd02c29283752b7e5799f978", "Cartridge.Name", "Berzerk", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2650 / 4975168", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.FrameRate", "20", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "38", "Display.Height", "188", NULL, NULL, }, + { "Cartridge.MD5", "1278f74ca1dfaa9122df3eca3c5bcaad", "Cartridge.Name", "Bi! Bi!", "Cartridge.Manufacturer", "Rainbow Vision", "Cartridge.Note", "Variant of Sea Hunt by Panda", "Cartridge.ModelNo", "SS-013", "Display.YStart", "42", "Display.Height", "245", NULL, NULL, }, + { "Cartridge.MD5", "1802cc46b879b229272501998c5de04f", "Cartridge.Name", "Big Bird's Egg Catch", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26104", "Cartridge.Rarity", "Rare", "Cartridge.Note", "One player left keypad only", "Display.YStart", "27", "Controller.Left", "Keypad", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "f714a223954c28eccf459295517dcae6", "Cartridge.Name", "Big Move This Demo", NULL, NULL, }, + { "Cartridge.MD5", "16cc6d1b4ddce51c767a1ba8e5ff196c", "Cartridge.Name", "Big Move This Demo 2", NULL, NULL, }, + { "Cartridge.MD5", "0a981c03204ac2b278ba392674682560", "Cartridge.Name", "Blackjack (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2651 / 6699805 / 4975602", "Cartridge.Rarity", "Rare", "Display.YStart", "34", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "b2761efb8a11fc59b00a3b9d78022ad6", "Cartridge.Name", "Blackjack (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2651 / 6699805 / 4975602", "Cartridge.Rarity", "Rare", "Display.YStart", "34", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "33d68c3cd74e5bc4cf0df3716c5848bc", "Cartridge.Name", "Blueprint", "Cartridge.Manufacturer", "CBS Electronics", "Cartridge.ModelNo", "4L-2486", "Cartridge.Rarity", "Uncommon", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "968efc79d500dce52a906870a97358ab", "Cartridge.Name", "BMX Airmaster", "Cartridge.Manufacturer", "Atari / TNT Games", "Cartridge.ModelNo", "CX26190", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "2823364702595feea24a3fbee138a243", "Cartridge.Name", "Bobby Is Going Home", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG206", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "245", NULL, NULL, }, + { "Cartridge.MD5", "c59633dbebd926c150fb6d30b0576405", "Cartridge.Name", "Bogey Blaster", "Cartridge.Manufacturer", "Telegames", "Cartridge.ModelNo", "5861 A030", "Cartridge.Rarity", "Rare", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "14c2548712099c220964d7f044c59fd9", "Cartridge.Name", "Boing", "Cartridge.Manufacturer", "First Star Software", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "4K", "Display.FrameRate", "60", "Display.YStart", "40", "Display.Height", "184", NULL, NULL, }, + { "Cartridge.MD5", "a2aae759e4e76f85c8afec3b86529317", "Cartridge.Name", "Boom Bang", "Cartridge.Manufacturer", "Cooper Black", "Cartridge.Note", "Variant of Crackpots by Activision", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "42", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "c9b7afad3bfd922e006a6bfc1d4f3fe7", "Cartridge.Name", "Bowling", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2628 / 6699842 / 4975117", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "d4aa6d6095258ce46aaf6f144b09eea7", "Cartridge.Name", "Bowling (non-functional Taiwanese clone)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2628 / 6699842 / 4975117", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "a28d872fc50fa6b64eb35981d0f4bb8d", "Cartridge.Name", "Bowling (4K version)", "Cartridge.Note", "4K version of 2K ROM", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2628 / 6699842 / 4975117", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "c3ef5c4653212088eda54dc91d787870", "Cartridge.Name", "Boxing (1)", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-002", "Cartridge.Rarity", "Uncommon", "Display.XStart", "8", "Display.Width", "140", NULL, NULL, }, + { "Cartridge.MD5", "277cca62014fceebb46c549bac25a2e3", "Cartridge.Name", "Boxing (2)", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-002", "Cartridge.Rarity", "Uncommon", "Display.XStart", "8", "Display.Width", "140", NULL, NULL, }, + { "Cartridge.MD5", "1cca2197d95c5a41f2add49a13738055", "Cartridge.Name", "Brain Games (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2664 / 6699818", "Cartridge.Rarity", "Uncommon", "Controller.Left", "Keypad", "Controller.Right", "Keypad", NULL, NULL, }, + { "Cartridge.MD5", "cb9626517b440f099c0b6b27ca65142c", "Cartridge.Name", "Brain Games (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2664 / 6699818", "Cartridge.Rarity", "Uncommon", "Controller.Left", "Keypad", "Controller.Right", "Keypad", NULL, NULL, }, + { "Cartridge.MD5", "f34f08e5eb96e500e851a80be3277a56", "Cartridge.Name", "Breakout / Breakaway IV (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2622", "Cartridge.Rarity", "Common", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "9a25b3cfe2bbb847b66a97282200cca2", "Cartridge.Name", "Breakout / Breakaway IV (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2622", "Cartridge.Rarity", "Common", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "e80a4026d29777c3c7993fbfaee8920f", "Cartridge.Name", "Brick Kick", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "cfd6a8b23d12b0462baf6a05ef347cd8", "Cartridge.Name", "Bridge", "Cartridge.Manufacturer", "Activision (Larry Kaplan)", "Cartridge.ModelNo", "AX-006", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "1cf59fc7b11cdbcefe931e41641772f6", "Cartridge.Name", "Buck Rogers-Planet of Zoom", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "005-01", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "fa4404fabc094e3a31fcd7b559cdd029", "Cartridge.Name", "Bugs Bunny", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "68597264c8e57ada93be3a5be4565096", "Cartridge.Name", "Bugs", "Cartridge.Manufacturer", "Data Age", "Cartridge.ModelNo", "DA 1005", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "76f53abbbf39a0063f24036d6ee0968a", "Cartridge.Name", "Bump 'N' Jump", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT7045", "Cartridge.Rarity", "Rare", "Cartridge.Type", "E7", "Display.YStart", "41", "Display.Height", "188", NULL, NULL, }, + { "Cartridge.MD5", "0443cfa9872cdb49069186413275fa21", "Cartridge.Name", "Burgertime", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT4518", "Cartridge.Rarity", "Rare", "Cartridge.Type", "E7", "Display.YStart", "31", "Display.Height", "180", NULL, NULL, }, + { "Cartridge.MD5", "19d6956ff17a959c48fcd8f4706a848d", "Cartridge.Name", "Burning Desire", "Cartridge.Manufacturer", "Mystique", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "7f6533386644c7d6358f871666c86e79", "Cartridge.Name", "Cakewalk", "Cartridge.Manufacturer", "CommaVid", "Cartridge.ModelNo", "CM-008", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "9ab72d3fd2cc1a0c9adb504502579037", "Cartridge.Name", "California Games", "Cartridge.Manufacturer", "Epyx", "Cartridge.ModelNo", "8056100286", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6", "Display.FrameRate", "20", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "43", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "3051b6071cb26377cd428af155e1bfc4", "Cartridge.Name", "Canyon Bomber (4K version)", "Cartridge.Note", "4K version of 2K ROM", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2607 / 6699828 / 4975115", "Cartridge.Rarity", "Uncommon", "Display.YStart", "47", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "feedcc20bc3ca34851cd5d9e38aa2ca6", "Cartridge.Name", "Canyon Bomber", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2607 / 6699828 / 4975115", "Cartridge.Rarity", "Uncommon", "Display.YStart", "47", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "028024fb8e5e5f18ea586652f9799c96", "Cartridge.Name", "Carnival", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2468", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", "Display.Height", "212", NULL, NULL, }, + { "Cartridge.MD5", "b816296311019ab69a21cb9e9e235d12", "Cartridge.Name", "Casino / Poker Plus", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2652", "Cartridge.Rarity", "Uncommon", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "76f66ce3b83d7a104a899b4b3354a2f2", "Cartridge.Name", "Cat & Mouse", "Cartridge.Type", "4K", "Display.FrameRate", "60", "Display.YStart", "33", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "9e192601829f5f5c2d3b51f8ae25dbe5", "Cartridge.Name", "Cathouse Blues", "Cartridge.Manufacturer", "Mystique", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "91c2098e88a6b13f977af8c003e0bca5", "Cartridge.Name", "Centipede", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2676", "Cartridge.Rarity", "Common", "Cartridge.Type", "F8", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "39", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "4311a4115fb7bc68477c96cf44cebacf", "Cartridge.Name", "Challenge (1)", "Cartridge.Manufacturer", "Zellers", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "37", "Display.Height", "213", NULL, NULL, }, + { "Cartridge.MD5", "9905f9f4706223dadee84f6867ede8e3", "Cartridge.Name", "Challenge (2)", "Cartridge.Manufacturer", "Zellers", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "37", "Display.Height", "213", NULL, NULL, }, + { "Cartridge.MD5", "5d799bfa9e1e7b6224877162accada0d", "Cartridge.Name", "Challenge of...NEXAR", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-206", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "36", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "3e33ac10dcf2dff014bc1decf8a9aea4", "Cartridge.Name", "Chase The Chuckwagon", "Cartridge.Manufacturer", "Spectravision", "Cartridge.Rarity", "Unbelievably Rare", "Display.YStart", "22", NULL, NULL, }, + { "Cartridge.MD5", "3f5a43602f960ede330cd2f43a25139e", "Cartridge.Name", "Checkers (1)", "Cartridge.Manufacturer", "Activision (Alan Miller)", "Cartridge.ModelNo", "AG-003", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.XStart", "8", NULL, NULL, }, + { "Cartridge.MD5", "7edc8fcb319b3fb61cac87614afd4ffa", "Cartridge.Name", "Checkers (2)", "Cartridge.Manufacturer", "Activision (Alan Miller)", "Cartridge.ModelNo", "AG-003", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.XStart", "8", NULL, NULL, }, + { "Cartridge.MD5", "749fec9918160921576f850b2375b516", "Cartridge.Name", "China Syndrome", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-205", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.FrameRate", "60", "Display.YStart", "32", "Display.Height", "207", NULL, NULL, }, + { "Cartridge.MD5", "c1cb228470a87beb5f36e90ac745da26", "Cartridge.Name", "Chopper Command", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AX-015", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "49", "Display.Height", "183", NULL, NULL, }, + { "Cartridge.MD5", "3f58f972276d1e4e0e09582521ed7a5b", "Cartridge.Name", "Chuck Norris Superkicks / Kung Fu Superkicks / Superkicks", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "99003", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "40", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "a29df35557f31dfea2e2ae4609c6ebb7", "Cartridge.Name", "Circus Atari / Circus (Joystick Version)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2630 / 4975122", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "42", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "a7b96a8150600b3e800a4689c3ec60a2", "Cartridge.Name", "Circus Atari / Circus (Paddle Version)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2630 / 4975122", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Cartridge.Note", "Two player left paddles only", "Display.YStart", "42", "Display.Height", "190", "Controller.Left", "Paddles", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "93acd5020ae8eb5673601e2edecbc158", "Cartridge.Name", "Clock (version 3)", NULL, NULL, }, + { "Cartridge.MD5", "f6efa00ae99aaf33e427b674bcfd834d", "Cartridge.Name", "Clock (version 003)", NULL, NULL, }, + { "Cartridge.MD5", "75e8d8b9e9c5c67c2226dbfd77dcfa7d", "Cartridge.Name", "Clock (version B1)", NULL, NULL, }, + { "Cartridge.MD5", "5e99aa93d0acc741dcda8752c4e813ce", "Cartridge.Name", "Clock (version B2)", NULL, NULL, }, + { "Cartridge.MD5", "4faeb04b1b7fb0fa25db05753182a898", "Cartridge.Name", "Clock", NULL, NULL, }, + { "Cartridge.MD5", "7ff53f6922708119e7bf478d7d618c86", "Cartridge.Name", "Clown Down Town", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "1e587ca91518a47753a28217cd4fd586", "Cartridge.Name", "Coconuts", "Cartridge.Manufacturer", "Telesys", "Cartridge.ModelNo", "1001", "Cartridge.Rarity", "Rare", "Display.YStart", "37", NULL, NULL, }, + { "Cartridge.MD5", "5846b1d34c296bf7afc2fa05bbc16e98", "Cartridge.Name", "Code Breaker (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2643 / 99815", "Cartridge.Rarity", "Uncommon / Rare", "Controller.Left", "Keypad", "Controller.Right", "Keypad", "Display.YStart", "33", NULL, NULL, }, + { "Cartridge.MD5", "83f50fa0fbae545e4b88bb53b788c341", "Cartridge.Name", "Code Breaker (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2643 / 99815", "Cartridge.Rarity", "Uncommon / Rare", "Controller.Left", "Keypad", "Controller.Right", "Keypad", "Display.YStart", "33", NULL, NULL, }, + { "Cartridge.MD5", "5846b1d34c296bf7afc2fa05bbc16e98", "Cartridge.Name", "Code Breaker", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2643", "Cartridge.Rarity", "Uncommon", "Controller.Left", "Keypad", "Controller.Right", "Keypad", "Display.YStart", "33", NULL, NULL, }, + { "Cartridge.MD5", "212d0b200ed8b45d8795ad899734d7d7", "Cartridge.Name", "Coke Wins", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unbelievably Rare Prototype", NULL, NULL, }, + { "Cartridge.MD5", "4093382187f8387e6d011883e8ea519b", "Cartridge.Name", "Col 'N'", "Cartridge.Manufacturer", "ITT Family Games", "Cartridge.ModelNo", "554-33391", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "67", "Display.Height", "214", NULL, NULL, }, + { "Cartridge.MD5", "ac05c0e53a5e7009ddd75ed4b99949fc", "Cartridge.Name", "Combat / Tank Plus (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2601 / 6699801 / 4975124", "Cartridge.Rarity", "Common", "Cartridge.Type", "2K", "Display.YStart", "39", "Display.Height", "206", NULL, NULL, }, + { "Cartridge.MD5", "4c8832ed387bbafc055320c05205bc08", "Cartridge.Name", "Combat / Tank Plus (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2601 / 6699801 / 4975124", "Cartridge.Rarity", "Common", "Cartridge.Type", "2K", "Display.YStart", "39", "Display.Height", "206", NULL, NULL, }, + { "Cartridge.MD5", "be35d8b37bbc03848a5f020662a99909", "Cartridge.Name", "Combat / Tank Plus (4K version)", "Cartridge.Note", "4K version of 2K ROM", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2601 / 6699801 / 4975124", "Cartridge.Rarity", "Common", "Cartridge.Type", "2K", "Display.YStart", "39", "Display.Height", "206", NULL, NULL, }, + { "Cartridge.MD5", "76a9bf05a6de8418a3ebc7fc254b71b4", "Cartridge.Name", "Color Bar Generator", "Cartridge.Manufacturer", "VideoSoft", "Cartridge.ModelNo", "VS1008", "Cartridge.Rarity", "Unreleased", NULL, NULL, }, + { "Cartridge.MD5", "5d2cc33ca798783dee435eb29debf6d6", "Cartridge.Name", "Commando", "Cartridge.Manufacturer", "Activision (Mike Reidel)", "Cartridge.ModelNo", "AK-043", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "47", "Display.Height", "180", NULL, NULL, }, + { "Cartridge.MD5", "f457674cef449cfd85f21db2b4f631a7", "Cartridge.Name", "Commando Raid", "Cartridge.Manufacturer", "U.S. Games (Vidtec)", "Cartridge.ModelNo", "VC 1004", "Cartridge.Rarity", "Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "2c8835aed7f52a0da9ade5226ee5aa75", "Cartridge.Name", "Communist Mutants from Space", "Cartridge.ModelNo", "AR-4101", "Cartridge.Manufacturer", "Starpath (Steve Landrum)", "Cartridge.Rarity", "Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "196", NULL, NULL, }, + { "Cartridge.MD5", "1f21666b8f78b65051b7a609f1d48608", "Cartridge.Name", "Condor Attack", "Cartridge.Manufacturer", "CCE", "Cartridge.ModelNo", "C-851", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "00b7b4cbec81570642283e7fc1ef17af", "Cartridge.Name", "Congo Bongo", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "006-01", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "57c5b351d4de021785cf8ed8191a195c", "Cartridge.Name", "Cookie Monster Munch", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26102", "Cartridge.Rarity", "Rare", "Controller.Left", "Keypad", "Controller.Right", "Keypad", "Display.YStart", "36", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "ab5bf1ef5e463ad1cbb11b6a33797228", "Cartridge.Name", "Cosmic Ark", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3204", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "60", "Display.YStart", "42", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "133b56de011d562cbab665968bde352b", "Cartridge.Name", "Cosmic Commuter", "Cartridge.Manufacturer", "Activision (John van Ryzin)", "Cartridge.ModelNo", "AG-038", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.FrameRate", "60", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "37", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "f367e58667a30e7482175809e3cec4d4", "Cartridge.Name", "Cosmic Corridor", "Cartridge.Manufacturer", "Emag / Zimag", "Cartridge.ModelNo", "GN-040 / 708-111", "Cartridge.Rarity", "Rare", "Display.YStart", "35", "Display.Height", "212", NULL, NULL, }, + { "Cartridge.MD5", "3c853d864a1d5534ed0d4b325347f131", "Cartridge.Name", "Cosmic Creeps", "Cartridge.Manufacturer", "Telesys", "Cartridge.ModelNo", "1002", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "e5f17b3e62a21d0df1ca9aee1aa8c7c5", "Cartridge.Name", "Cosmic Swarm", "Cartridge.Manufacturer", "CommaVid", "Cartridge.ModelNo", "CM-003", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "9dec0be14d899e1aac4337acef5ab94a", "Cartridge.Name", "Cosmic Swarm (4K version)", "Cartridge.Note", "4K version of 2K ROM", "Cartridge.Manufacturer", "CommaVid", "Cartridge.ModelNo", "CM-003", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "a184846d8904396830951217b47d13d9", "Cartridge.Name", "Crackpots", "Cartridge.Manufacturer", "Activision (Dan Kitchen)", "Cartridge.ModelNo", "AX-029", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "fb88c400d602fe759ae74ef1716ee84e", "Cartridge.Name", "Crash Dive", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11031", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "55ef7b65066428367844342ed59f956c", "Cartridge.Name", "Crazy Climber", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2683", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "46", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "48f18d69799a5f5451a5f0d17876acef", "Cartridge.Name", "Criminal Pursuit", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "8cd26dcf249456fe4aeb8db42d49df74", "Cartridge.Name", "Crossbow", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26139", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "c17bdc7d14a36e10837d039f43ee5fa3", "Cartridge.Name", "Cross Force", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-203", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "74f623833429d35341b7a84bc09793c0", "Cartridge.Name", "Cruise Missile", "Cartridge.Manufacturer", "Froggo", "Cartridge.ModelNo", "FG 1007", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "384f5fbf57b5e92ed708935ebf8a8610", "Cartridge.Name", "Crypts of Chaos", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11009", "Cartridge.Rarity", "Rare", "Display.YStart", "52", "Display.Height", "185", NULL, NULL, }, + { "Cartridge.MD5", "1c6eb740d3c485766cade566abab8208", "Cartridge.Name", "Crystal Castles", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26110", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F6SC", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "50", "Display.Height", "174", NULL, NULL, }, + { "Cartridge.MD5", "6fa0ac6943e33637d8e77df14962fbfc", "Cartridge.Name", "Cubicolor", "Cartridge.Manufacturer", "Imagic", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "281ff9bd0470643853de5cbd6d9e17f5", "Cartridge.Name", "Cubis (EM)", "Cartridge.Manufacturer", "Eckhard Stolberg", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "6caff1b4b96ed098123febf51f9121dc", "Cartridge.Name", "Cubis (PAL version) (1)", "Cartridge.Manufacturer", "Eckhard Stolberg", "Cartridge.Rarity", "New Release", "Display.Format", "PAL", NULL, NULL, }, + { "Cartridge.MD5", "f74ad642552385c3daa203a2a6fc2291", "Cartridge.Name", "Cubis (PAL version) (2)", "Cartridge.Manufacturer", "Eckhard Stolberg", "Cartridge.Rarity", "New Release", "Display.Format", "PAL", NULL, NULL, }, + { "Cartridge.MD5", "58513bae774360b96866a07ca0e8fd8e", "Cartridge.Name", "Custer's Revenge", "Cartridge.Manufacturer", "Mystique", "Cartridge.ModelNo", "1001", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "929e8a84ed50601d9af8c49b0425c7ea", "Cartridge.Name", "Dancing Plate", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG205", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "230", NULL, NULL, }, + { "Cartridge.MD5", "3d8a2d6493123a53ade45e3e2c5cafa0", "Cartridge.Name", "Dare Diver", "Cartridge.ModelNo", "99843 / 75118", "Cartridge.Manufacturer", "Sears", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "a422194290c64ef9d444da9d6a207807", "Cartridge.Name", "Dark Cavern", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5667", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.YStart", "41", "Display.Height", "189", NULL, NULL, }, + { "Cartridge.MD5", "106855474c69d08c8ffa308d47337269", "Cartridge.Name", "Dark Chambers", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26151", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F6SC", "Emulation.HmoveBlanks", "No", "Display.YStart", "48", "Display.Height", "170", NULL, NULL, }, + { "Cartridge.MD5", "dba270850ae997969a18ee0001675821", "Cartridge.Name", "Dark Mage (4K version)", "Cartridge.Manufacturer", "Greg Troutman", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "6333ef5b5cbb77acd47f558c8b7a95d3", "Cartridge.Name", "Dark Mage (8K version)", "Cartridge.Manufacturer", "Greg Troutman", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "e4c00beb17fdc5881757855f2838c816", "Cartridge.Name", "Deadly Duck", "Cartridge.Manufacturer", "20th Century Fox Video Games (Sirius)", "Cartridge.ModelNo", "11004", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "4e15ddfd48bca4f0bf999240c47b49f5", "Cartridge.Name", "Deathtrap", "Cartridge.Manufacturer", "Avalon Hill", "Cartridge.ModelNo", "50010", "Cartridge.Rarity", "Unbelievably Rare", NULL, NULL, }, + { "Cartridge.MD5", "ac7c2260378975614192ca2bc3d20e0b", "Cartridge.Name", "Decathlon", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AZ-030", "Cartridge.Rarity", "Rare", "Cartridge.Type", "FE", NULL, NULL, }, + { "Cartridge.MD5", "0f643c34e40e3f1daafd9c524d3ffe64", "Cartridge.Name", "Defender", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2609 / 4975186", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "3a771876e4b61d42e3a3892ad885d889", "Cartridge.Name", "Defender II", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26120", "Cartridge.Rarity", "Rare", "Cartridge.Note", "Variant of Stargate by Atari", "Cartridge.Type", "F8SC", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "37", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "d09935802d6760ae58253685ff649268", "Cartridge.Name", "Demolition Herby", "Cartridge.Manufacturer", "Telesys", "Cartridge.ModelNo", "1006", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "37", "Display.Height", "187", NULL, NULL, }, + { "Cartridge.MD5", "f0e0addc07971561ab80d9abe1b8d333", "Cartridge.Name", "Demon Attack", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3200", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "39", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "698f569eab5a9906eec3bc7c6b3e0980", "Cartridge.Name", "Demons!", "Cartridge.Manufacturer", "SpkSoft", "Cartridge.Note", "Variant of Phoenix by Atari and Demon Attack by Imagic", NULL, NULL, }, + { "Cartridge.MD5", "f91fb8da3223b79f1c9a07b77ebfa0b2", "Cartridge.Name", "Demons to Diamonds", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2615 / 4975140", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Two player left paddles only (swapped)", "Display.YStart", "35", "Display.Height", "195", "Controller.Left", "Paddles", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "fd4f5536fd80f35c64d365df85873418", "Cartridge.Name", "Desert Falcon", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26140", "Cartridge.Rarity", "Common", "Display.YStart", "17", NULL, NULL, }, + { "Cartridge.MD5", "9222b25a0875022b412e8da37e7f6887", "Cartridge.Name", "Dice Puzzle", "Cartridge.Manufacturer", "Panda Inc.", "Cartridge.ModelNo", "106", "Cartridge.Rarity", "Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "6dda84fb8e442ecf34241ac0d1d91d69", "Cartridge.Name", "Dig Dug", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2677", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F6SC", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "33", "Display.Height", "193", NULL, NULL, }, + { "Cartridge.MD5", "939ce554f5c0e74cc6e4e62810ec2111", "Cartridge.Name", "Dishaster", "Cartridge.Manufacturer", "Cooper Black", "Cartridge.Note", "Variant of Dancing Plate by Emag/Zimag", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "83bdc819980db99bf89a7f2ed6a2de59", "Cartridge.Name", "Dodge 'Em / Dodger Cars", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2637 / 4975158", "Cartridge.Rarity", "Uncommon", "Display.YStart", "57", "Display.Height", "180", NULL, NULL, }, + { "Cartridge.MD5", "ca09fa7406b7d2aea10d969b6fc90195", "Cartridge.Name", "Dolphin", "Cartridge.Manufacturer", "Activision (Matthew Hubbard)", "Cartridge.ModelNo", "AX-024", "Cartridge.Rarity", "Rare", "Display.YStart", "40", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "84d1cf884f029e458db196548db9c2ad", "Cartridge.Name", "Domino (PAL version)", "Cartridge.Manufacturer", "Eckhard Stolberg", "Cartridge.Rarity", "New Release", "Display.Format", "PAL", NULL, NULL, }, + { "Cartridge.MD5", "937736d899337036de818391a87271e0", "Cartridge.Name", "Donald Duck's Speedboat", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26108", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "36b20c427975760cb9cf4a47e41369e4", "Cartridge.Name", "Donkey Kong", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2451", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "7fd52208fb6391bae0cd7e68c27bde6f", "Cartridge.Name", "Donkey Kong Jr. (1)", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2653", "Cartridge.Rarity", "Rare", "Display.YStart", "35", NULL, NULL, }, + { "Cartridge.MD5", "c8fa5d69d9e555eb16068ef87b1c9c45", "Cartridge.Name", "Donkey Kong Jr. (2)", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2653", "Cartridge.Rarity", "Rare", "Display.YStart", "35", NULL, NULL, }, + { "Cartridge.MD5", "47464694e9cce07fdbfd096605bf39d4", "Cartridge.Name", "Double Dragon", "Cartridge.Manufacturer", "Activision (Dan Kitchen)", "Cartridge.ModelNo", "AK-050-04", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "68", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "368d88a6c071caba60b4f778615aae94", "Cartridge.Name", "Double Dunk", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26159", "Cartridge.Rarity", "Rare", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "6a882fb1413912d2ce5cf5fa62cf3875", "Cartridge.Name", "Dragon Defender", "Cartridge.ModelNo", "TP-605", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "32", "Display.Height", "249", NULL, NULL, }, + { "Cartridge.MD5", "41810dd94bd0de1110bedc5092bef5b0", "Cartridge.Name", "Dragonfire (1)", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3611", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "58", "Display.Height", "211", NULL, NULL, }, + { "Cartridge.MD5", "6fc394dbf21cf541a60e3b3631b817f1", "Cartridge.Name", "Dragonfire (2)", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3611", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "58", "Display.Height", "211", NULL, NULL, }, + { "Cartridge.MD5", "90ccf4f30a5ad8c801090b388ddd5613", "Cartridge.Name", "Dragonstomper (1)", "Cartridge.ModelNo", "AR-4400", "Cartridge.Manufacturer", "Starpath (Steve Landrum)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "39", "Display.Height", "189", NULL, NULL, }, + { "Cartridge.MD5", "77057d9d14b99e465ea9e29783af0ae3", "Cartridge.Name", "Dragster (1)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AG-001", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "63a6eda1da30446569ac76211d0f861c", "Cartridge.Name", "Dragster (2)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AG-001", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "37f42ab50018497114f6b0f4f01aa9a1", "Cartridge.Name", "Droid Demo 2-M", NULL, NULL, }, + { "Cartridge.MD5", "2bc6c53b19e0097a242f22375a6a60ff", "Cartridge.Name", "Droid Demo 2", NULL, NULL, }, + { "Cartridge.MD5", "bff8f8f53a8aeb1ee804004ccbb08313", "Cartridge.Name", "Droid Demo 22", NULL, NULL, }, + { "Cartridge.MD5", "1bb91bae919ddbd655fa25c54ea6f532", "Cartridge.Name", "Duck Shoot", "Display.YStart", "67", "Display.Height", "187", NULL, NULL, }, + { "Cartridge.MD5", "1f773a94d919b2a3c647172bbb97f6b4", "Cartridge.Name", "Dumbo's Flying Circus", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2678", "Cartridge.Rarity", "Unbelievably Rare Prototype", NULL, NULL, }, + { "Cartridge.MD5", "51de328e79d919d7234cf19c1cd77fbc", "Cartridge.Name", "Dukes of Hazard", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2678", "Cartridge.Rarity", "Unbelievably Rare Prototype", NULL, NULL, }, + { "Cartridge.MD5", "033e21521e0bf4e54e8816873943406d", "Cartridge.Name", "Earth Dies Screaming, The", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11020", "Cartridge.Rarity", "Rare", "Display.YStart", "30", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "683dc64ef7316c13ba04ee4398e2b93a", "Cartridge.Name", "Edtris", "Cartridge.Manufacturer", "Ed Federmeyer", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "42b2c3b4545f1499a083cfbc4a3b7640", "Cartridge.Name", "Eggomania", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 2003", "Cartridge.Rarity", "Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "b6812eaf87127f043e78f91f2028f9f4", "Cartridge.Name", "Eli's Ladder", "Cartridge.Manufacturer", "Simage", "Cartridge.Rarity", "Unbelievably Rare", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "7eafc9827e8d5b1336905939e097aae7", "Cartridge.Name", "Elk Attack", "Cartridge.Manufacturer", "Mark R. Hahn", "Cartridge.Rarity", "Uncommon / New Release", "Cartridge.Type", "F8", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "37", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "dbc8829ef6f12db8f463e30f60af209f", "Cartridge.Name", "Encounter at L-5", "Cartridge.Manufacturer", "Data Age", "Cartridge.ModelNo", "DA 1001", "Cartridge.Rarity", "Uncommon", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "4279485e922b34f127a88904b31ce9fa", "Cartridge.Name", "Enduro (1)", "Cartridge.Manufacturer", "Activision (Larry Miller)", "Cartridge.ModelNo", "AX-026", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "20", "Display.XStart", "12", "Display.Width", "140", "Display.YStart", "64", "Display.Height", "175", NULL, NULL, }, + { "Cartridge.MD5", "94b92a882f6dbaa6993a46e2dcc58402", "Cartridge.Name", "Enduro (2)", "Cartridge.Manufacturer", "Activision (Larry Miller)", "Cartridge.ModelNo", "AX-026", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "20", "Display.XStart", "12", "Display.Width", "140", "Display.YStart", "64", "Display.Height", "175", NULL, NULL, }, + { "Cartridge.MD5", "6b683be69f92958abe0e2a9945157ad5", "Cartridge.Name", "Entombed / Name That Game", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 2007 / VC 1007", "Cartridge.Rarity", "Extremely Rare / Rare", "Cartridge.Note", "Released as Name That Game for a contest (winning name was Entombed)", NULL, NULL, }, + { "Cartridge.MD5", "8538c5e3ee83267774480649f83fa8d6", "Cartridge.Name", "Escape Demo", NULL, NULL, }, + { "Cartridge.MD5", "d1a1841b7f2007a24439ac248374630a", "Cartridge.Name", "Escape from the Mindmaster", "Cartridge.ModelNo", "AR-4200", "Cartridge.Manufacturer", "Starpath (Dennis Caswell)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "36", "Display.Height", "192", "Emulation.HmoveBlanks", "No", NULL, NULL, }, + { "Cartridge.MD5", "f1127ade54037236e75a133b1dfc389d", "Cartridge.Name", "Escape from the Mindmaster - Demonstration", "Cartridge.ModelNo", "AR-4200", "Cartridge.Manufacturer", "Starpath (Dennis Caswell)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "36", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "f7a138eed69665b5cd1bfa796a550b01", "Cartridge.Name", "Espial", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-012", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "3F", "Display.YStart", "76", "Display.Height", "196", NULL, NULL, }, + { "Cartridge.MD5", "615a3bf251a38eb6638cdc7ffbde5480", "Cartridge.Name", "E.T.-The Extra Terrestrial", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2674", "Cartridge.Rarity", "Common", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "6362396c8344eec3e86731a700b13abf", "Cartridge.Name", "Exocet", "Cartridge.Manufacturer", "Panda Inc.", "Cartridge.ModelNo", "109", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "2ac3a08cfbf1942ba169c3e9e6c47e09", "Cartridge.Name", "F14 Tomcat Flight Simulator", "Cartridge.Manufacturer", "Absolute Entertainment", "Cartridge.ModelNo", "AK-046", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "F6", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "56", "Display.Height", "196", NULL, NULL, }, + { "Cartridge.MD5", "b80d50ecee73919a507498d0a4d922ae", "Cartridge.Name", "Fantastic Voyage", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11008", "Cartridge.Rarity", "Rare", "Display.YStart", "27", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "f7e07080ed8396b68f2e5788a5c245e2", "Cartridge.Name", "Farmyard Fun", "Cartridge.ModelNo", "TP-617", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "37", "Display.Height", "212", NULL, NULL, }, + { "Cartridge.MD5", "9de0d45731f90a0a922ab09228510393", "Cartridge.Name", "Fast Eddie (1)", "Cartridge.Manufacturer", "20th Century Fox Video Games (Sirius)", "Cartridge.ModelNo", "11003", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.YStart", "38", "Display.Height", "198", NULL, NULL, }, + { "Cartridge.MD5", "a1f9159121142d42e63e6fb807d337aa", "Cartridge.Name", "Fast Eddie (2)", "Cartridge.Manufacturer", "20th Century Fox Video Games (Sirius)", "Cartridge.ModelNo", "11003", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.YStart", "58", "Display.Height", "198", NULL, NULL, }, + { "Cartridge.MD5", "665b8f8ead0eef220ed53886fbd61ec9", "Cartridge.Name", "Fast Food", "Cartridge.Manufacturer", "Telesys", "Cartridge.ModelNo", "1003", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "074ec425ec20579e64a7ded592155d48", "Cartridge.Name", "Fatal Run", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26162", "Cartridge.Rarity", "Unbelievably Rare", "Cartridge.Type", "F4SC", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "70", "Display.Height", "178", NULL, NULL, }, + { "Cartridge.MD5", "0b55399cf640a2a00ba72dd155a0c140", "Cartridge.Name", "Fathom", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "O3205", "Cartridge.Rarity", "Rare", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "211fbbdbbca1102dc5b43dc8157c09b3", "Cartridge.Name", "Final Approach", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2009", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "386ff28ac5e254ba1b1bac6916bcc93a", "Cartridge.Name", "Fireball", "Cartridge.ModelNo", "AR-4300", "Cartridge.Manufacturer", "Starpath (Scott Nelson)", "Cartridge.Rarity", "Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "136", "Display.YStart", "32", "Display.Height", "200", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "d09f1830fb316515b90694c45728d702", "Cartridge.Name", "Fire Fighter", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3400", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "20dca534b997bf607d658e77fbb3c0ee", "Cartridge.Name", "Fire Fly", "Cartridge.Manufacturer", "Mythicon", "Cartridge.ModelNo", "MA-1002", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "d3171407c3a8bb401a3a62eb578f48fb", "Cartridge.Name", "Fire Spinner", "Cartridge.Manufacturer", "Emag", "Cartridge.ModelNo", "GN-080", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "01e60a109a6a67c70d3c0528381d0187", "Cartridge.Name", "Fire Birds", "Cartridge.Manufacturer", "ITT Family Games", "Cartridge.ModelNo", "554-33383", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "67", NULL, NULL, }, + { "Cartridge.MD5", "b8865f05676e64f3bec72b9defdacfa7", "Cartridge.Name", "Fishing Derby (1)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AG-004", "Cartridge.Rarity", "Uncommon", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "85227160f37aaa29f5e3a6c7a3219f54", "Cartridge.Name", "Fishing Derby (2)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AG-004", "Cartridge.Rarity", "Uncommon", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "30512e0e83903fc05541d2f6a6a62654", "Cartridge.Name", "Flag Capture / Capture (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2644 / 99824 / 99824", "Cartridge.Rarity", "Uncommon / Rare", "Display.YStart", "39", NULL, NULL, }, + { "Cartridge.MD5", "4b143d7dcf6c96796c37090cba045f4f", "Cartridge.Name", "Flag Capture / Capture (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2644 / 99824 / 99824", "Cartridge.Rarity", "Uncommon / Rare", "Display.YStart", "39", NULL, NULL, }, + { "Cartridge.MD5", "8786c1e56ef221d946c64f6b65b697e9", "Cartridge.Name", "Flash Gordon", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11015", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "e549f1178e038fa88dc6d657dc441146", "Cartridge.Name", "Football (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2625 / 6699827 / 4975114", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "d86deb100c6abed1588aa84b2f7b3a98", "Cartridge.Name", "Football (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2625 / 6699827 / 4975114", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "213e5e82ecb42af237cfed8612c128ac", "Cartridge.Name", "Forest", "Cartridge.Manufacturer", "Panda Inc.", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "34", "Display.Height", "254", NULL, NULL, }, + { "Cartridge.MD5", "15dd21c2608e0d7d9f54c0d3f08cca1f", "Cartridge.Name", "Frankenstein's Monster", "Cartridge.Manufacturer", "Data Age", "Cartridge.ModelNo", "112-008", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "8e0ab801b1705a740b476b7f588c6d16", "Cartridge.Name", "Freeway (1)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AG-009", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "851cc1f3c64eaedd10361ea26345acea", "Cartridge.Name", "Freeway (2)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AG-009", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "94e3fbc19107a169909e274187247a9d", "Cartridge.Name", "Freeway (3)", "Cartridge.Manufacturer", "Activision (David Crane) / Zellers", "Cartridge.ModelNo", "AG-009", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "5f73e7175474c1c22fb8030c3158e9b3", "Cartridge.Name", "Frog Pond", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2665", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Display.YStart", "37", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "081e2c114c9c20b61acf25fc95c71bf4", "Cartridge.Name", "Frogger", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5300", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "34", "Display.Height", "191", NULL, NULL, }, + { "Cartridge.MD5", "27c6a2ca16ad7d814626ceea62fa8fb4", "Cartridge.Name", "Frogger II: Threedeep!", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5590", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "E0", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "40", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "c73ae5ba5a0a3f3ac77f0a9e14770e73", "Cartridge.Name", "Official Frogger by Sega, The", "Cartridge.ModelNo", "AR-4105", "Cartridge.Manufacturer", "Starpath (Steve Landrum)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "33", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "f5d103a9ae36d1d4ee7eef657b75d2b3", "Cartridge.Name", "Official Frogger by Sega, The - Demonstration", "Cartridge.ModelNo", "AR-4105", "Cartridge.Manufacturer", "Starpath (Steve Landrum)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "33", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "dcc2956c7a39fdbf1e861fc5c595da0d", "Cartridge.Name", "Frogs and Flies", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5664", "Cartridge.Rarity", "Uncommon", "Display.YStart", "42", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "e556e07cc06c803f2955986f53ef63ed", "Cartridge.Name", "Front Line", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2665", "Cartridge.Rarity", "Rare", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "4ca73eb959299471788f0b685c3ba0b5", "Cartridge.Name", "Frostbite", "Cartridge.Manufacturer", "Activision (Steve Cartwright)", "Cartridge.ModelNo", "AX-031", "Cartridge.Rarity", "Rare", "Display.Height", "197", NULL, NULL, }, + { "Cartridge.MD5", "01b09872dcd9556427761f0ed64aa42a", "Cartridge.Name", "Galaga", "Cartridge.Note", "Variant of River Raid by Activision (Carol Shaw)", "Cartridge.Type", "4K", "Display.YStart", "36", "Display.Height", "199", NULL, NULL, }, + { "Cartridge.MD5", "211774f4c5739042618be8ff67351177", "Cartridge.Name", "Galaxian", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2684", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F8", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "36", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "102672bbd7e25cd79f4384dd7214c32b", "Cartridge.Name", "Game of Concentration, A / Hunt and Score / Memory Match (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2642 / 6699814", "Cartridge.Rarity", "Rare (Uncommon) / Uncommon", "Cartridge.Note", "Released under three titles", "Controller.Left", "Keypad", "Controller.Right", "Keypad", NULL, NULL, }, + { "Cartridge.MD5", "31f4692ee2ca07a7ce1f7a6a1dab4ac9", "Cartridge.Name", "Game of Concentration, A / Hunt and Score / Memory Match (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2642 / 6699814", "Cartridge.Rarity", "Rare (Uncommon) / Uncommon", "Cartridge.Note", "Released under three titles", "Controller.Left", "Keypad", "Controller.Right", "Keypad", NULL, NULL, }, + { "Cartridge.MD5", "20edcc3aa6c189259fa7e2f044a99c49", "Cartridge.Name", "Gangster Alley", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-201", "Cartridge.Rarity", "Rare", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "728152f5ae6fdd0d3a9b88709bee6c7a", "Cartridge.Name", "Gas Hog", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-217", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "e64a8008812327853877a37befeb6465", "Cartridge.Name", "Gauntlet", "Cartridge.Manufacturer", "Answer Software", "Cartridge.ModelNo", "ASC1002", "Cartridge.Rarity", "Unbelievably Rare", "Display.YStart", "35", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "e314b42761cd13c03def744b4afc7b1b", "Cartridge.Name", "Ghostbusters", "Cartridge.Manufacturer", "Activision (David Crane and Dan Kitchen)", "Cartridge.ModelNo", "AZ-108-04", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "c2b5c50ccb59816867036d7cf730bf75", "Cartridge.Name", "Ghostbusters II", "Cartridge.Manufacturer", "Activision / Salu", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "42", "Display.Height", "242", NULL, NULL, }, + { "Cartridge.MD5", "0eecb5f58f55de9db4eedb3a0f6b74a8", "Cartridge.Name", "Ghost Manor (1)", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "99002", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "2bee7f226d506c217163bad4ab1768c0", "Cartridge.Name", "Ghost Manor (2)", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "99002", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "c1fdd44efda916414be3527a47752c75", "Cartridge.Name", "G.I. Joe-Cobra Strike", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5920", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Uses paddle/joystick combination", "Display.YStart", "27", "Controller.Left", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "1c8c42d1aee5010b30e7f1992d69216e", "Cartridge.Name", "Gigilo", "Cartridge.Manufacturer", "Mystique", "Cartridge.ModelNo", "1009", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "5e0c37f534ab5ccc4661768e2ddf0162", "Cartridge.Name", "Glacier Patrol", "Cartridge.Manufacturer", "Telegames (Sunrise)", "Cartridge.ModelNo", "5667 A106", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "2d9e5d8d083b6367eda880e80dfdfaeb", "Cartridge.Name", "Glib", "Cartridge.Manufacturer", "QDI", "Cartridge.ModelNo", "87", "Cartridge.Rarity", "Unbelievably Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "2e663eaa0d6b723b645e643750b942fd", "Cartridge.Name", "Golf (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2634 / 75121", "Cartridge.Rarity", "Common", "Display.YStart", "37", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "f542b5d0193a3959b54f3c4c803ba242", "Cartridge.Name", "Golf (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2634 / 75121", "Cartridge.Rarity", "Common", "Display.YStart", "37", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "c16c79aad6272baffb8aae9a7fff0864", "Cartridge.Name", "Gopher", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 2001", "Cartridge.Rarity", "Rare", "Display.Height", "197", NULL, NULL, }, + { "Cartridge.MD5", "98ba601a60172cb46c5bf9a962fd5b1f", "Cartridge.Name", "Gorilla Kong", "Cartridge.Note", "Variant of Donkey Kong by Coleco", NULL, NULL, }, + { "Cartridge.MD5", "81b3bf17cf01039d311b4cd738ae608e", "Cartridge.Name", "Gorf", "Cartridge.Manufacturer", "CBS Electronics", "Cartridge.ModelNo", "M8793", "Cartridge.Rarity", "Uncommon", "Display.YStart", "42", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "2903896d88a341511586d69fcfc20f7d", "Cartridge.Name", "Grand Prix", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AX-014", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "46", "Display.Height", "189", NULL, NULL, }, + { "Cartridge.MD5", "8ac18076d01a6b63acf6e2cab4968940", "Cartridge.Name", "Gravitar", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2685", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F8", "Display.YStart", "37", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "18f299edb5ba709a64c80c8c9cec24f2", "Cartridge.Name", "Great Escape", "Cartridge.Manufacturer", "Bomb", "Cartridge.ModelNo", "CA282", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.Height", "220", NULL, NULL, }, + { "Cartridge.MD5", "a3b9d2be822eab07e7f4b10593fb5eaa", "Cartridge.Name", "GREGXM Demo", NULL, NULL, }, + { "Cartridge.MD5", "01cb3e8dfab7203a9c62ba3b94b4e59f", "Cartridge.Name", "Gremlins", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26127", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "4ac9f40ddfcf194bd8732a75b3f2f214", "Cartridge.Name", "Grover's Music Maker", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26106", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Cartridge.Note", "One player left keypad only", "Display.YStart", "27", "Controller.Left", "Keypad", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "7ab2f190d4e59e8742e76a6e870b567e", "Cartridge.Name", "Guardian", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2008", "Cartridge.Rarity", "Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "b311ab95e85bc0162308390728a7361d", "Cartridge.Name", "Gyruss", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5080", "Cartridge.Rarity", "Rare", "Display.YStart", "47", "Display.Height", "180", NULL, NULL, }, + { "Cartridge.MD5", "30516cfbaa1bc3b5335ee53ad811f17a", "Cartridge.Name", "Halloween", "Cartridge.Manufacturer", "Wizard Video", "Cartridge.ModelNo", "007", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "b9232c1de494875efe1858fc8390616d", "Cartridge.Name", "Harbor Escape", "Cartridge.Manufacturer", "Panda Inc", "Cartridge.ModelNo", "110", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "f16c709df0a6c52f47ff52b9d95b7d8d", "Cartridge.Name", "Hangman", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2662", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "f0a6e99f5875891246c3dbecbf2d2cea", "Cartridge.Name", "Haunted House", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2654 / 4975141", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "38", "Display.Height", "184", NULL, NULL, }, + { "Cartridge.MD5", "fca4a5be1251927027f2c24774a02160", "Cartridge.Name", "H.E.R.O.", "Cartridge.Manufacturer", "Activision (John van Ryzin)", "Cartridge.ModelNo", "AZ-036-04", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F8", "Emulation.HmoveBlanks", "No", "Display.YStart", "49", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "3d48b8b586a09bdbf49f1a016bf4d29a", "Cartridge.Name", "Hole Hunter", "Cartridge.ModelNo", "TP-606", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.Height", "220", NULL, NULL, }, + { "Cartridge.MD5", "c52d9bbdc5530e1ef8e8ba7be692b01e", "Cartridge.Name", "Holy Moley", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Controller.Left", "Keypad", "Controller.Right", "Keypad", NULL, NULL, }, + { "Cartridge.MD5", "0bfabf1e98bdb180643f35f2165995d0", "Cartridge.Name", "Home Run / Baseball (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2623 / 99819 / 75125", "Cartridge.Rarity", "Common", "Display.YStart", "42", NULL, NULL, }, + { "Cartridge.MD5", "9f901509f0474bf9760e6ebd80e629cd", "Cartridge.Name", "Home Run / Baseball (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2623 / 99819 / 75125", "Cartridge.Rarity", "Common", "Display.YStart", "42", NULL, NULL, }, + { "Cartridge.MD5", "7dc03a1f56d0e6a8aae3e3e50d654a08", "Cartridge.Name", "Hozer Video Demo", NULL, NULL, }, + { "Cartridge.MD5", "7972e5101fa548b952d852db24ad6060", "Cartridge.Name", "Human Cannonball / Cannon Man (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2627 / 6699841", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "ffe51989ba6da2c6ae5a12d277862e16", "Cartridge.Name", "Human Cannonball / Cannon Man (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2627 / 6699841", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "f6a282374441012b01714e19699fc62a", "Cartridge.Name", "I Want My Mommy", "Cartridge.Manufacturer", "Emag / Zimag", "Cartridge.ModelNo", "GN-010 / 710-111", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "a4c08c4994eb9d24fb78be1793e82e26", "Cartridge.Name", "Ice Hockey", "Cartridge.Manufacturer", "Activision (Alan Miller)", "Cartridge.ModelNo", "AX-012", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "9813b9e4b8a6fd919c86a40c6bda8c93", "Cartridge.Name", "Ikari Warriors", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26177", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "9b21d8fc78cc4308990d99a4d906ec52", "Cartridge.Name", "Immies and Aggies", "Cartridge.Manufacturer", "CCE", "Cartridge.ModelNo", "C-838", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "3f6dbf448f25e2bd06dea44248eb122d", "Cartridge.Name", "International Soccer (1)", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5687", "Cartridge.Rarity", "Uncommon", "Display.YStart", "37", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "29630a20d356fb58685b150bfa8f00c3", "Cartridge.Name", "International Soccer (2)", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5687", "Cartridge.Rarity", "Uncommon", "Display.YStart", "37", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "b4030c38a720dd84b84178b6ce1fc749", "Cartridge.Name", "International Soccer (Pirate version) (1)", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5687", "Cartridge.Rarity", "Uncommon", "Display.YStart", "37", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "cd568d6acb2f14477ebf7e59fb382292", "Cartridge.Name", "International Soccer (Pirate version) (2)", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5687", "Cartridge.Rarity", "Uncommon", "Display.YStart", "62", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "c5301f549d0722049bb0add6b10d1e09", "Cartridge.Name", "Indy 500 / Race (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2611 / 6699821 / 4975149", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Uses two steering controllers", "Controller.Left", "Driving", "Controller.Right", "Driving", "Display.YStart", "30", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "dac762e4d01d445bdef20b7771f6570e", "Cartridge.Name", "Indy 500 / Race (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2611 / 6699821 / 4975149", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Uses two steering controllers", "Controller.Left", "Driving", "Controller.Right", "Driving", "Display.YStart", "30", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "afe88aae81d99e0947c0cfb687b16251", "Cartridge.Name", "Infiltrate", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2006", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "4868a81e1b6031ed66ecd60547e6ec85", "Cartridge.Name", "Inv (1-3-98 version)", "Cartridge.Manufacturer", "Eric Mooney and Piero Cavina", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "cd139ae6d09f3665ad09eb79da3f9e49", "Cartridge.Name", "Inv (4-24-97 version)", "Cartridge.Manufacturer", "Eric Mooney", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "2016726db38ad6a68b4c48ba6fe51557", "Cartridge.Name", "Inv 2", "Cartridge.Manufacturer", "Eric Mooney", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "4b9581c3100a1ef05eac1535d25385aa", "Cartridge.Name", "IQ 180", "Cartridge.Manufacturer", "Homevision", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.Height", "235", NULL, NULL, }, + { "Cartridge.MD5", "e51030251e440cffaab1ac63438b44ae", "Cartridge.Name", "James Bond 007", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5110", "Cartridge.Rarity", "Rare", "Cartridge.Type", "E0", NULL, NULL, }, + { "Cartridge.MD5", "b9c3bc1d77f8e9d814735188bf324e40", "Cartridge.Name", "James Bond 007 (non-working version)", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5110", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "58a82e1da64a692fd727c25faef2ecc9", "Cartridge.Name", "Jawbreaker", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-002", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "eb4252faff7a4f2ba5284a98b8f78d1a", "Cartridge.Name", "John K. Harvey's Equalizer", "Cartridge.Manufacturer", "John K. Harvey", NULL, NULL, }, + { "Cartridge.MD5", "30997031b668e37168d4d0e299ccc46f", "Cartridge.Name", "John K. Harvey's Equalizer (PAL version)", "Cartridge.Manufacturer", "John K. Harvey", "Display.Format", "PAL", NULL, NULL, }, + { "Cartridge.MD5", "718ae62c70af4e5fd8e932fee216948a", "Cartridge.Name", "Journey Escape", "Cartridge.Manufacturer", "Data Age", "Cartridge.ModelNo", "112-006", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "3276c777cbe97cdd2b4a63ffc16b7151", "Cartridge.Name", "Joust", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2691", "Cartridge.Rarity", "Common", "Cartridge.Type", "F8", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "39", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "36c29ceee2c151b23a1ad7aa04bd529d", "Cartridge.Name", "Jr. Pac Man", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26123", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F6SC", "Emulation.HmoveBlanks", "No", "Display.YStart", "49", "Display.Height", "170", NULL, NULL, }, + { "Cartridge.MD5", "2cccc079c15e9af94246f867ffc7e9bf", "Cartridge.Name", "Jungle Fever", "Cartridge.Manufacturer", "Mystique", "Cartridge.ModelNo", "1011", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "2bb9f4686f7e08c5fcc69ec1a1c66fe7", "Cartridge.Name", "Jungle Hunt", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2688", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F8", "Display.FrameRate", "20", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "40", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "5428cdfada281c569c74c7308c7f2c26", "Cartridge.Name", "Kaboom! (1)", "Cartridge.Manufacturer", "Activision (Larry Kaplan)", "Cartridge.ModelNo", "AG-010", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Cartridge.Note", "Two player left paddles only", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "41", "Display.Height", "192", "Controller.Left", "Paddles", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "af6ab88d3d7c7417db2b3b3c70b0da0a", "Cartridge.Name", "Kaboom! (2)", "Cartridge.Manufacturer", "Activision (Larry Kaplan)", "Cartridge.ModelNo", "AG-010", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Cartridge.Note", "Two player left paddles only", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "41", "Display.Height", "192", "Controller.Left", "Paddles", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "4326edb70ff20d0ee5ba58fa5cb09d60", "Cartridge.Name", "Kangaroo", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2689", "Cartridge.Rarity", "Uncommon", "Display.YStart", "37", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "cedbd67d1ff321c996051eec843f8716", "Cartridge.Name", "Karate", "Cartridge.Manufacturer", "Ultravision", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "be929419902e21bd7830a7a7d746195d", "Cartridge.Name", "Keystone Kapers", "Cartridge.Manufacturer", "Activision (Garry Kitchen)", "Cartridge.ModelNo", "AX-025", "Cartridge.Rarity", "Uncommon", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "7a7f6ab9215a3a6b5940b8737f116359", "Cartridge.Name", "Killer Satellites", "Cartridge.ModelNo", "AR-4103", "Cartridge.Manufacturer", "Starpath (Kevin Norman)", "Cartridge.Rarity", "Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "36", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "0b1056f1091cfdc5eb0e2301f47ac6c3", "Cartridge.Name", "King Kong", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-001", "Cartridge.Rarity", "Rare", "Display.YStart", "34", "Display.Height", "210", NULL, NULL, }, + { "Cartridge.MD5", "eed9eaf1a0b6a2b9bc4c8032cb43e3fb", "Cartridge.Name", "Klax (PAL version)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26192", "Cartridge.Rarity", "Extremely Rare", "Display.Format", "PAL", NULL, NULL, }, + { "Cartridge.MD5", "2c29182edf0965a7f56fe0897d2f84ba", "Cartridge.Name", "Klax", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26192", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "7fcd1766de75c614a3ccc31b25dd5b7a", "Cartridge.Name", "Knight on the Town / Lady In Wading", "Cartridge.Manufacturer", "Mystique", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "534e23210dd1993c828d944c6ac4d9fb", "Cartridge.Name", "Kool-Aid Man", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT4648", "Cartridge.Rarity", "Uncommon", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "4baada22435320d185c95b7dd2bcdb24", "Cartridge.Name", "Krull", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2682", "Cartridge.Rarity", "Rare", "Display.YStart", "35", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "5b92a93b23523ff16e2789b820e2a4c5", "Cartridge.Name", "Kung Fu Master", "Cartridge.Manufacturer", "Activision (Dan Kitchen)", "Cartridge.ModelNo", "AX-039", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F8", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "42", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "931b91a8ea2d39fe4dca1a23832b591a", "Cartridge.Name", "Laser Blast (1)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AG-008", "Cartridge.Rarity", "Common", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "d5e27051512c1e7445a9bf91501bda09", "Cartridge.Name", "Laser Blast (2)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AG-008", "Cartridge.Rarity", "Common", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "48287a9323a0ae6ab15e671ac2a87598", "Cartridge.Name", "Laser Gates (1)", "Cartridge.Manufacturer", "Imagic", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.YStart", "34", "Display.Height", "188", NULL, NULL, }, + { "Cartridge.MD5", "1fa58679d4a39052bd9db059e8cda4ad", "Cartridge.Name", "Laser Gates (2)", "Cartridge.Manufacturer", "Imagic", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.YStart", "34", "Display.Height", "188", NULL, NULL, }, + { "Cartridge.MD5", "4ab4af3adcdae8cdacc3d06084fc8d6a", "Cartridge.Name", "Led Zepplin Demonstration", "Cartridge.Manufacturer", "Nick Bensema", NULL, NULL, }, + { "Cartridge.MD5", "71464c54da46adae9447926fdbfc1abe", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.Name", "Lock 'n' Chase", "Cartridge.ModelNo", "MT5663", "Cartridge.Rarity", "Common", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "b4e2fd27d3180f0f4eb1065afc0d7fc9", "Cartridge.Name", "London Blitz", "Cartridge.Manufacturer", "Avalon Hill", "Cartridge.ModelNo", "50010", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "7c00e7a205d3fda98eb20da7c9c50a55", "Cartridge.Name", "Lost Luggage", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2004", "Cartridge.Rarity", "Uncommon", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "393e41ca8bdd35b52bf6256a968a9b89", "Cartridge.Name", "M.A.D.", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 1012", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.YStart", "44", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "ccb5fa954fb76f09caae9a8c66462190", "Cartridge.Name", "Malagai", "Cartridge.Manufacturer", "Answer Software", "Cartridge.ModelNo", "ASC1001", "Cartridge.Rarity", "Unbelievably Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "54a1c1255ed45eb8f71414dadb1cf669", "Cartridge.Name", "Mangia", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-212", "Cartridge.Rarity", "Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "13895ef15610af0d0f89d588f376b3fe", "Cartridge.Name", "Marauder", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-005", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "b00e8217633e870bf39d948662a52aac", "Cartridge.Name", "Marine Wars", "Cartridge.Manufacturer", "Konami", "Cartridge.ModelNo", "011", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "1b8d35d93697450ea26ebf7ff17bd4d1", "Cartridge.Name", "Marineflieger", NULL, NULL, }, + { "Cartridge.MD5", "e908611d99890733be31733a979c62d8", "Cartridge.Name", "Mario Bros.", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2697", "Cartridge.Rarity", "Uncommon", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "835759ff95c2cdc2324d7c1e7c5fa237", "Cartridge.Name", "M*A*S*H", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11011", "Cartridge.Rarity", "Uncommon", "Display.YStart", "37", "Display.Height", "197", NULL, NULL, }, + { "Cartridge.MD5", "ae4be3a36b285c1a1dff202157e2155d", "Cartridge.Name", "Master Builder", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-210", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "3b76242691730b2dd22ec0ceab351bc6", "Cartridge.Name", "Masters of the Universe: The Power of He-Man", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT4318", "Cartridge.Rarity", "Rare", "Cartridge.Type", "E7", "Display.FrameRate", "60", "Display.XStart", "0", "Display.Width", "160", "Display.YStart", "38", "Display.Height", "185", "TIA.PlayerDelay", "4", NULL, NULL, }, + { "Cartridge.MD5", "470878b9917ea0348d64b5750af149aa", "Cartridge.Name", "Math Gran Prix", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2658 / 4975128", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "244c6de27faff527886fc7699a41c3be", "Cartridge.Name", "Matt Demo", NULL, NULL, }, + { "Cartridge.MD5", "8108ad2679bd055afec0a35a1dca46a4", "Cartridge.Name", "Maze Craze / Maze Mania (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2635 / 4975157", "Cartridge.Rarity", "Uncommon", "Display.YStart", "52", "Display.Height", "175", NULL, NULL, }, + { "Cartridge.MD5", "f825c538481f9a7a46d1e9bc06200aaf", "Cartridge.Name", "Maze Craze / Maze Mania (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2635 / 4975157", "Cartridge.Rarity", "Uncommon", "Display.YStart", "52", "Display.Height", "175", NULL, NULL, }, + { "Cartridge.MD5", "daeb54957875c50198a7e616f9cc8144", "Cartridge.Name", "Mega Force", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11005", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "318a9d6dda791268df92d72679914ac3", "Cartridge.Name", "Megamania", "Cartridge.Manufacturer", "Activision (Steve Cartwright)", "Cartridge.ModelNo", "AX-017", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "43", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "6522717cfd75d1dba252cbde76992090", "Cartridge.Name", "Meteor Defense", "Cartridge.Manufacturer", "ITT Family Games", "Cartridge.ModelNo", "554-33391", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "67", NULL, NULL, }, + { "Cartridge.MD5", "f1554569321dc933c87981cf5c239c43", "Cartridge.Name", "Midnight Magic", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26129", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "37", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "3c57748c8286cf9e821ecd064f21aaa9", "Cartridge.Name", "Millipede", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26118", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6SC", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "39", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "f0541d2f7cda5ec7bab6d62b6128b823", "Cartridge.Name", "Bionic Breakthrough", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Cartridge.Note", "One player Mind Link controller only", "Controller.Left", "Mindlink", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "fa0570561aa80896f0ead05c46351389", "Cartridge.Name", "Miner 2049'er", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-008", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "3F", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "30", "Display.Height", "216", NULL, NULL, }, + { "Cartridge.MD5", "468f2dec984f3d4114ea84f05edf82b6", "Cartridge.Name", "Miner 2049'er Volume II", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-011", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "3F", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "60", "Display.Height", "212", NULL, NULL, }, + { "Cartridge.MD5", "4543b7691914dfd69c3755a5287a95e1", "Cartridge.Name", "Mines of Minos", "Cartridge.Manufacturer", "CommaVid", "Cartridge.ModelNo", "CM-005", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "35", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "635cc7a0db33773959d739d04eff96c2", "Cartridge.Name", "Minesweeper (1)", NULL, NULL, }, + { "Cartridge.MD5", "ac5f78bae0638cf3f2a0c8d07eb4df69", "Cartridge.Name", "Minesweeper (2)", NULL, NULL, }, + { "Cartridge.MD5", "df62a658496ac98a3aa4a6ee5719c251", "Cartridge.Name", "Miniature Golf / Arcade Golf (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2626 / 99829 / 75116", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "384db97670817103dd8c0bbdef132445", "Cartridge.Name", "Miniature Golf / Arcade Golf (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2626 / 99829 / 75116", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "3a2e2d0c6892aa14544083dfb7762782", "Cartridge.Name", "Missile Command", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2638 / 4975166", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "cb24210dc86d92df97b38cf2a51782da", "Cartridge.Name", "Missile Control", "Cartridge.Manufacturer", "Video Gems", "Cartridge.ModelNo", "VG-01", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "6efe876168e2d45d4719b6a61355e5fe", "Cartridge.Name", "Mission 3000 A.D.", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG207", "Cartridge.Rarity", "Rare", "Display.YStart", "60", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "7af40c1485ce9f29b1a7b069a2eb04a7", "Cartridge.Name", "Mogul Maniac", "Cartridge.Manufacturer", "Amiga", "Cartridge.ModelNo", "3120", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "37", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "1f60e48ad98b659a05ce0c1a8e999ad9", "Cartridge.Name", "Mondo Pong", "Cartridge.Manufacturer", "Piero Cavina", "Cartridge.Rarity", "New Release", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "6913c90002636c1487538d4004f7cac2", "Cartridge.Name", "Monstercise", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26131", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Controller.Left", "Keypad", "Controller.Right", "Keypad", NULL, NULL, }, + { "Cartridge.MD5", "3347a6dd59049b15a38394aa2dafa585", "Cartridge.Name", "Montezuma's Revenge: Starring Panama Joe", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5760", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "E0", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "515046e3061b7b18aa3a551c3ae12673", "Cartridge.Name", "Moon Patrol", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2692", "Cartridge.Rarity", "Uncommon", "Display.YStart", "47", "Display.Height", "175", NULL, NULL, }, + { "Cartridge.MD5", "7db7c5fd8d3f53127a4bb0092c91d983", "Cartridge.Name", "Moonsweeper (non-functional version)", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "O3207", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "203abb713c00b0884206dcc656caa48f", "Cartridge.Name", "Moonsweeper", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "O3207", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "f5a2f6efa33a3e5541bc680e9dc31d5b", "Cartridge.Name", "Motocross Racer (non-working version)", "Cartridge.Manufacturer", "Xonox", "Cartridge.Rarity", "Rare", "Display.YStart", "45", "Display.Height", "249", NULL, NULL, }, + { "Cartridge.MD5", "de0173ed6be9de6fd049803811e5f1a8", "Cartridge.Name", "Motocross Racer", "Cartridge.Manufacturer", "Xonox", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "b1e2d5dc1353af6d56cd2fe7cfe75254", "Cartridge.Name", "Motorodeo", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26171", "Cartridge.Rarity", "Unbelievably Rare", NULL, NULL, }, + { "Cartridge.MD5", "7e51a58de2c0db7d33715f518893b0db", "Cartridge.Name", "Mountain King", "Cartridge.Manufacturer", "CBS Electronics", "Cartridge.ModelNo", "4L-2738", "Cartridge.Rarity", "Rare", "Cartridge.Type", "FASC", "Display.XStart", "16", "Display.Width", "128", "Display.YStart", "47", "Display.Height", "186", NULL, NULL, }, + { "Cartridge.MD5", "5678ebaa09ca3b699516dba4671643ed", "Cartridge.Name", "Mouse Trap", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2459", "Cartridge.Rarity", "Uncommon", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "0164f26f6b38a34208cd4a2d0212afc3", "Cartridge.Name", "Mr. Do!", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2656", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "b7a7e34e304e4b7bc565ec01ba33ea27", "Cartridge.Name", "Mr. Do!'s Castle", "Cartridge.Manufacturer", "Coleco / Parker Bros.", "Cartridge.ModelNo", "2695 / PB5820", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "E0", NULL, NULL, }, + { "Cartridge.MD5", "f0daaa966199ef2b49403e9a29d12c50", "Cartridge.Name", "Mr. Postman", "Cartridge.Manufacturer", "Bit Corp. / CCE", "Cartridge.ModelNo", "PG209 / C-801", "Cartridge.Rarity", "Rare / Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "87e79cd41ce136fd4f72cc6e2c161bee", "Cartridge.Name", "Ms. Pac Man", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2675", "Cartridge.Rarity", "Common", "Cartridge.Type", "F8", "Emulation.HmoveBlanks", "No", "Display.YStart", "35", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "079fe9103515d15bc108577e234a484d", "Cartridge.Name", "Multi-Color Demo", "Cartridge.Note", "Variant of Sabotage by Ultravision", NULL, NULL, }, + { "Cartridge.MD5", "14163eb2a3ddd35576bd8527eae3b45e", "Cartridge.Name", "Multi-Color Demo (4K version)", "Cartridge.Note", "Variant of Sabotage by Ultravision", NULL, NULL, }, + { "Cartridge.MD5", "b1fd0b71de9f6eeb5143a97963674cb6", "Cartridge.Name", "Multi-Color Demo (8K version)", "Cartridge.Note", "Variant of Sabotage by Ultravision", NULL, NULL, }, + { "Cartridge.MD5", "65b106eba3e45f3dab72ea907f39f8b4", "Cartridge.Name", "Music Machine", "Cartridge.Manufacturer", "Sparrow", "Cartridge.ModelNo", "GCG 1001T", "Cartridge.Rarity", "Unbelievably Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "dfad86dd85a11c80259f3ddb6151f48f", "Cartridge.Name", "My Golf", "Cartridge.Manufacturer", "Absolute Entertainment / Home Entertainment Suppliers", "Cartridge.ModelNo", "535", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "53", NULL, NULL, }, + { "Cartridge.MD5", "392f00fd1a074a3c15bc96b0a57d52a1", "Cartridge.Name", "Night Driver", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2633 / 4975119", "Cartridge.Rarity", "Common", "Cartridge.Note", "One player paddles only", "Controller.Left", "Paddles", "Controller.Right", "None", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "f48022230bb774a7f22184b48a3385af", "Cartridge.Name", "Night Driver (4K version)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2633 / 4975119", "Cartridge.Rarity", "Common", "Cartridge.Note", "One player paddles only, 4K version of 2K ROM", "Controller.Left", "Paddles", "Controller.Right", "None", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "2783006ee6519f15cbc96adae031c9a9", "Cartridge.Name", "Night Stalker", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.YStart", "61", "Display.Height", "199", NULL, NULL, }, + { "Cartridge.MD5", "ed0ab909cf7b30aff6fc28c3a4660b8e", "Cartridge.Name", "Nightmare / Stuntman", "Cartridge.Manufacturer", "Sancho / Panda Inc.", "Cartridge.ModelNo", "TEC004 / 105", "Cartridge.Rarity", "Extremely Rare / Rare", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "b6d52a0cf53ad4216feb04147301f87d", "Cartridge.Name", "No Escape!", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3312", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.YStart", "44", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "de7a64108074098ba333cc0c70eef18a", "Cartridge.Name", "Nuts", "Cartridge.Manufacturer", "Technovision", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "669840b0411bfbab5c05b786947d55d4", "Cartridge.Name", "Obelix", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26117", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "64", "Display.Height", "193", NULL, NULL, }, + { "Cartridge.MD5", "4cabc895ea546022c2ecaa5129036634", "Cartridge.Name", "Ocean City Defender", "Cartridge.Manufacturer", "Zellers", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "36306070f0c90a72461551a7a4f3a209", "Cartridge.Name", "Octopus", "Cartridge.Manufacturer", "Zellers", "Cartridge.ModelNo", "VC 1007", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "131864e1d18d3406048700d3c0760417", "Cartridge.Name", "Off The Wall (1)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26168", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "98f63949e656ff309cefa672146dc1b8", "Cartridge.Name", "Off The Wall (2)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26168", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "b6166f15720fdf192932f1f76df5b65d", "Cartridge.Name", "Off Your Rocker", "Cartridge.Manufacturer", "Amiga", "Cartridge.ModelNo", "3130", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "c9c25fc536de9a7cdc5b9a916c459110", "Cartridge.Name", "Oink!", "Cartridge.Manufacturer", "Activision (Mike Lorenzen)", "Cartridge.ModelNo", "AX-023", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "42", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "ce4bbe11d682c15a490ae15a4a8716cf", "Cartridge.Name", "Okie Dokie", "Cartridge.Manufacturer", "RetroWare", "Cartridge.Rarity", "Uncommon / New Release", "Cartridge.Type", "2K", "Display.XStart", "16", "Display.Width", "128", "Display.YStart", "30", "Display.Height", "178", NULL, NULL, }, + { "Cartridge.MD5", "8101efafcf0af32fedda4579c941e6f4", "Cartridge.Name", "Okie Dokie (4K version)", "Cartridge.Manufacturer", "RetroWare", "Cartridge.Rarity", "Uncommon / New Release", "Display.XStart", "16", "Display.Width", "128", "Display.YStart", "90", "Display.Height", "178", NULL, NULL, }, + { "Cartridge.MD5", "9947f1ebabb56fd075a96c6d37351efa", "Cartridge.Name", "Omega Race", "Cartridge.Manufacturer", "CBS Electronics", "Cartridge.ModelNo", "4L-2737", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Right difficulty 'A' to use Booster-Grip in both ports", "Console.RightDifficulty", "A", "Controller.Left", "Booster-Grip", "Controller.Right", "Booster-Grip", NULL, NULL, }, + { "Cartridge.MD5", "28d5df3ed036ed63d33a31d0d8b85c47", "Cartridge.Name", "Open Sesame", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG204", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "fa1b060fd8e0bca0c2a097dcffce93d3", "Cartridge.Name", "Oscar's Trash Race", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26101", "Cartridge.Rarity", "Rare", "Cartridge.Note", "One player keypad only", "Controller.Left", "Keypad", "Controller.Right", "None", "Display.YStart", "37", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "55949cb7884f9db0f8dfcf8707c7e5cb", "Cartridge.Name", "Othello (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2639 / 4975162", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "2c3b9c171e214e9e46bbaa12bdf8977e", "Cartridge.Name", "Othello (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2639 / 4975162", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "890c13590e0d8d5d6149737d930e4d95", "Cartridge.Name", "Outlaw / Gunslinger (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2605 / 99822 / 75109", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "f060826626aac9e0d8cda0282f4b7fc3", "Cartridge.Name", "Outlaw / Gunslinger (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2605 / 99822 / 75109", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "17515a4d0b7ea5029ffff7dfa8456671", "Cartridge.Name", "Oystron Pre-Release Demo (1)", "Cartridge.Manufacturer", "Piero Cavina", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "4689081b7363721858756fe781cc7713", "Cartridge.Name", "Oystron Pre-Release Demo (2)", "Cartridge.Manufacturer", "Piero Cavina", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "91f0a708eeb93c133e9672ad2c8e0429", "Cartridge.Name", "Oystron", "Cartridge.Manufacturer", "Piero Cavina", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "f9da42f91a1c5cfa344d2ff440c6f8d4", "Cartridge.Name", "Pac Invaders", "Cartridge.Manufacturer", "ZUT", NULL, NULL, }, + { "Cartridge.MD5", "936ef1d6f8a57b9ff575dc195ee36b80", "Cartridge.Name", "Pac-Kong", "Cartridge.Manufacturer", "Funvision", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "6e372f076fb9586aff416144f5cfe1cb", "Cartridge.Name", "Pac-Man", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2646 / 4975185", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "36", "Display.Height", "202", NULL, NULL, }, + { "Cartridge.MD5", "f8582bc6ca7046adb8e18164e8cecdbc", "Cartridge.Name", "Panda Chase", "Cartridge.Manufacturer", "Homevision", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.Height", "249", "Timer.Adjustment", "1", NULL, NULL, }, + { "Cartridge.MD5", "714e13c08508ee9a7785ceac908ae831", "Cartridge.Name", "Parachute", "Cartridge.Manufacturer", "Homevision", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "47", "Display.Height", "220", NULL, NULL, }, + { "Cartridge.MD5", "012b8e6ef3b5fd5aabc94075c527709d", "Cartridge.Name", "Party Mix", "Cartridge.ModelNo", "AR-4302", "Cartridge.Manufacturer", "Starpath (Dennis Caswell)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Controller.Left", "Paddles", "Controller.Right", "Paddles", "Emulation.CPU", "High", "Display.XStart", "0", "Display.Width", "160", "Display.YStart", "34", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "4c0fb2544ae0f8b5f7ae8bce7bd7f134", "Cartridge.Name", "Party Mix - Demonstration", "Cartridge.ModelNo", "AR-4302", "Cartridge.Manufacturer", "Starpath (Dennis Caswell)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "0", "Display.Width", "160", "Display.YStart", "34", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "e40a818dac4dd851f3b4aafbe2f1e0c1", "Cartridge.Name", "Peek-A-Boo", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26137", "Cartridge.Rarity", "Unbelievably Rare Prototype", NULL, NULL, }, + { "Cartridge.MD5", "ace319dc4f76548659876741a6690d57", "Cartridge.Name", "Pele's Soccer / Pele's Championship Soccer", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2616", "Cartridge.Rarity", "Common", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "7a09299f473105ae1ef3ad6f9f2cd807", "Cartridge.Name", "Pele's Soccer / Pele's Championship Soccer (Pirate version)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2616", "Cartridge.Rarity", "Common", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "4bcc7f6ba501a26ee785b7efbfb0fdc8", "Cartridge.Name", "Pengo", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2690", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "F8", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "35", "Display.Height", "196", NULL, NULL, }, + { "Cartridge.MD5", "09388bf390cd9a86dc0849697b96c7dc", "Cartridge.Name", "Pete Rose Baseball", "Cartridge.Manufacturer", "Absolute Entertainment", "Cartridge.ModelNo", "AK-045", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "38", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "6b1fc959e28bd71aed7b89014574bdc2", "Cartridge.Name", "Phantom Tank", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG203", "Cartridge.Rarity", "Rare", "Display.Height", "219", "Display.YStart", "47", "Display.YStart", "42", "Display.Height", "245", NULL, NULL, }, + { "Cartridge.MD5", "62f74a2736841191135514422b20382d", "Cartridge.Name", "Pharoah's Curse", "Cartridge.Manufacturer", "Technovision", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "4K", "Display.YStart", "10", "Display.Height", "200", "Display.YStart", "22", "Display.Height", "225", NULL, NULL, }, + { "Cartridge.MD5", "7dcbfd2acc013e817f011309c7504daa", "Cartridge.Name", "Phasor Patrol", "Cartridge.ModelNo", "AR-4000", "Cartridge.Rarity", "Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "34", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "ca54de69f7cdf4d7996e86f347129892", "Cartridge.Name", "Philly Flasher", "Cartridge.Manufacturer", "Mystique", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "7e52a95074a66640fcfde124fffd491a", "Cartridge.Name", "Phoenix", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2673", "Cartridge.Rarity", "Common", "Cartridge.Type", "F8", "Display.YStart", "38", "Display.Height", "188", NULL, NULL, }, + { "Cartridge.MD5", "da79aad11572c80a96e261e4ac6392d0", "Cartridge.Name", "Pick 'N' Pile", "Cartridge.Manufacturer", "Salu", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "52", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "17c0a63f9a680e7a61beba81692d9297", "Cartridge.Name", "Picnic", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 2004", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "d3423d7600879174c038f53e5ebbf9d3", "Cartridge.Name", "Piece 'O Cake", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 2005", "Cartridge.Rarity", "Extremely Rare", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "9c7fa3cfcaaafb4e6daf1e2517d43d88", "Cartridge.Name", "PIEROXM Demo", NULL, NULL, }, + { "Cartridge.MD5", "8e4fa8c6ad8d8dce0db8c991c166cdaa", "Cartridge.Name", "Pigs In Space", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26114", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "3e90cf23106f2e08b2781e41299de556", "Cartridge.Name", "Pitfall!", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AX-018", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "41", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "f73d2d0eff548e8fc66996f27acf2b4b", "Cartridge.Name", "Pitfall! (CCE Version)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AX-018", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "41", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "f939780714db69dc69a80fbefe350e0d", "Cartridge.Name", "Pitfall II: Lost Caverns", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AB-035-04", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "48eb1fcde4caf6a2dce059c98bd2e375", "Cartridge.Name", "Pitfall II: Lost Caverns (10K version) (1)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AB-035-04", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "6d842c96d5a01967be9680080dd5be54", "Cartridge.Name", "Pitfall II: Lost Caverns (10K version) (2)", "Cartridge.Manufacturer", "Activision (David Crane)", "Cartridge.ModelNo", "AB-035-04", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "043f165f384fbea3ea89393597951512", "Cartridge.Name", "Planet Patrol", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-202", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "da4e3396aa2db3bd667f83a1cb9e4a36", "Cartridge.Name", "Plaque Attack", "Cartridge.Manufacturer", "Activision (Steve Cartwright)", "Cartridge.ModelNo", "AX-027", "Cartridge.Rarity", "Rare", "Display.YStart", "47", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "5f39353f7c6925779b0169a87ff86f1e", "Cartridge.Name", "Pole Position", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2694", "Cartridge.Rarity", "Common", "Display.YStart", "42", "Display.Height", "186", NULL, NULL, }, + { "Cartridge.MD5", "56606b4d9109e2db36dea47cf7c8cca6", "Cartridge.Name", "Pole Position (known glitch version)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2694", "Cartridge.Rarity", "Common", "Cartridge.Note", "Game crashes after car starts first turn (bad rom dump)", "Display.YStart", "42", "Display.Height", "186", NULL, NULL, }, + { "Cartridge.MD5", "203049f4d8290bb4521cc4402415e737", "Cartridge.Name", "Polaris", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-007", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "3F", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "62", "Display.Height", "210", NULL, NULL, }, + { "Cartridge.MD5", "ee28424af389a7f3672182009472500c", "Cartridge.Name", "Polo (1)", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unbelievably Rare Prototype", NULL, NULL, }, + { "Cartridge.MD5", "14b1e30982962c72f426e2e763eb4274", "Cartridge.Name", "Polo (2)", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unbelievably Rare Prototype", NULL, NULL, }, + { "Cartridge.MD5", "4799a40b6e889370b7ee55c17ba65141", "Cartridge.Name", "Pooyan", "Cartridge.Manufacturer", "Gakken", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "c7f13ef38f61ee2367ada94fdcc6d206", "Cartridge.Name", "Popeye", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5370", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "f93d7fee92717e161e6763a88a293ffa", "Cartridge.Name", "Porky's", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11013", "Cartridge.Rarity", "Rare", "Display.YStart", "42", NULL, NULL, }, + { "Cartridge.MD5", "97d079315c09796ff6d95a06e4b70171", "Cartridge.Name", "Pressure Cooker", "Cartridge.Manufacturer", "Activision (Garry Kitchen)", "Cartridge.ModelNo", "AZ-032", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "ef3a4f64b6494ba770862768caf04b86", "Cartridge.Name", "Private Eye", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-034-04", "Cartridge.Rarity", "Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "12123b534bdee79ed7563b9ad74f1cbd", "Cartridge.Name", "Pro Wrestling", "Cartridge.Manufacturer", "Activision / Absolute Entertainment", "Cartridge.ModelNo", "AG-041", "Cartridge.Rarity", "Extremely Rare / Rare", "Display.YStart", "47", "Display.Height", "184", NULL, NULL, }, + { "Cartridge.MD5", "679d30c7886b283cbe1db4e7dbe5f2a6", "Cartridge.Name", "Puzzle Demo", NULL, NULL, }, + { "Cartridge.MD5", "37fd7fa52d358f66984948999f1213c5", "Cartridge.Name", "Pyramid War", "Cartridge.Manufacturer", "Suntek", "Cartridge.ModelNo", "4", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "67", "Display.Height", "184", NULL, NULL, }, + { "Cartridge.MD5", "484b0076816a104875e00467d431c2d2", "Cartridge.Name", "Q*Bert", "Cartridge.Manufacturer", "Parker Bros. / Atari", "Cartridge.ModelNo", "PB5360 / CX26150", "Cartridge.Rarity", "Common / Rare", "Cartridge.Type", "4K", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "45", "Display.Height", "202", NULL, NULL, }, + { "Cartridge.MD5", "72b8dc752befbfb3ffda120eb98b2dd0", "Cartridge.Name", "Q*Bert's Qubes (1)", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5550", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "E0", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "32", "Display.Height", "210", NULL, NULL, }, + { "Cartridge.MD5", "517592e6e0c71731019c0cebc2ce044f", "Cartridge.Name", "Q*Bert's Qubes (2)", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5550", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "E0", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "32", "Display.Height", "210", NULL, NULL, }, + { "Cartridge.MD5", "392d34c0498075dd58df0ce7cd491ea2", "Cartridge.Name", "Quadrun", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2686", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "a0675883f9b09a3595ddd66a6f5d3498", "Cartridge.Name", "Quest for Quinta Roo", "Cartridge.Manufacturer", "Telegames (Sunrise)", "Cartridge.ModelNo", "6057 A227", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "7eba20c2291a982214cc7cbe8d0b47cd", "Cartridge.Name", "Quick Step", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "O3211", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "fb4ca865abc02d66e39651bd9ade140a", "Cartridge.Name", "Rabbit Transit", "Cartridge.ModelNo", "AR-4104", "Cartridge.Manufacturer", "Starpath (Brian McGhie)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "34", "Display.Height", "198", NULL, NULL, }, + { "Cartridge.MD5", "cd399bc422992a361ba932cc50f48b65", "Cartridge.Name", "Rabbit Transit - Demonstration", "Cartridge.ModelNo", "AR-4104", "Cartridge.Manufacturer", "Starpath (Brian McGhie)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "34", "Display.Height", "198", NULL, NULL, }, + { "Cartridge.MD5", "a20d931a8fddcd6f6116ed21ff5c4832", "Cartridge.Name", "Racquetball", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2003", "Cartridge.Rarity", "Uncommon", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "baf4ce885aa281fd31711da9b9795485", "Cartridge.Name", "Radar Lock", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26176", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6SC", "Display.YStart", "42", "Display.Height", "185", NULL, NULL, }, + { "Cartridge.MD5", "92a1a605b7ad56d863a56373a866761b", "Cartridge.Name", "Raft Rider", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 2006", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "f724d3dd2471ed4cf5f191dbb724b69f", "Cartridge.Name", "Raiders of the Lost Ark", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2659", "Cartridge.Rarity", "Common", "Cartridge.Type", "F8", "Display.YStart", "37", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "7096a198531d3f16a99d518ac0d7519a", "Cartridge.Name", "Ram It", "Cartridge.Manufacturer", "Telesys", "Cartridge.ModelNo", "1004", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "5e1b4629426f4992cf3b2905a696e1a7", "Cartridge.Name", "Rampage!", "Cartridge.Manufacturer", "Activision (Bobco)", "Cartridge.ModelNo", "AK-049", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6", "Display.XStart", "12", "Display.Width", "136", "Display.YStart", "38", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "9f8fad4badcd7be61bbd2bcaeef3c58f", "Cartridge.Name", "Reactor", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5330", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "eb634650c3912132092b7aee540bbce3", "Cartridge.Name", "RealSports Baseball", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2640", "Cartridge.Rarity", "Uncommon", "Display.YStart", "32", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "3177cc5c04c1a4080a927dfa4099482b", "Cartridge.Name", "RealSports Boxing", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26135", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F6", "Display.Height", "185", NULL, NULL, }, + { "Cartridge.MD5", "7ad257833190bc60277c1ca475057051", "Cartridge.Name", "RealSports Football", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2668", "Cartridge.Rarity", "Common", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "08f853e8e01e711919e734d85349220d", "Cartridge.Name", "RealSports Soccer", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2667", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "c7eab66576696e11e3c11ffff92e13cc", "Cartridge.Name", "RealSports Tennis (1)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2680", "Cartridge.Rarity", "Uncommon", "Display.YStart", "57", NULL, NULL, }, + { "Cartridge.MD5", "dac5c0fe74531f077c105b396874a9f1", "Cartridge.Name", "RealSports Tennis (2)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2680", "Cartridge.Rarity", "Uncommon", "Display.YStart", "57", NULL, NULL, }, + { "Cartridge.MD5", "aed0b7bd64cc384f85fdea33e28daf3b", "Cartridge.Name", "RealSports Volleyball", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2666", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "41", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "e56da674188ba2f02c7a0a343a01236f", "Cartridge.Name", "Rescue (prototype)", "Cartridge.Rarity", "New Release", "Cartridge.Note", "Variant of This Planet Sucks by What?Where", NULL, NULL, }, + { "Cartridge.MD5", "dfe6aa7443bb813cefa35a4cf4887422", "Cartridge.Name", "Rescue", "Cartridge.Rarity", "New Release", "Cartridge.Note", "Variant of This Planet Sucks by What?Where", NULL, NULL, }, + { "Cartridge.MD5", "8a9d874a38608964f33ec0c35cab618d", "Cartridge.Name", "Rescue Bira Bira", "Cartridge.Manufacturer", "Chris Cracknell / Mystique", "Cartridge.Note", "Less adult-oriented version of Mystique's Jungle Fever", NULL, NULL, }, + { "Cartridge.MD5", "60a61da9b2f43dd7e13a5093ec41a53d", "Cartridge.Name", "Rescue Terra I", "Cartridge.Manufacturer", "Venture Vision", "Cartridge.ModelNo", "VV2001", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "4f64d6d0694d9b7a1ed7b0cb0b83e759", "Cartridge.Name", "Revenge of the Beefsteak Tomatoes", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11016", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "a995b6cbdb1f0433abc74050808590e6", "Cartridge.Name", "Riddle of the Sphinx", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3600", "Cartridge.Rarity", "Uncommon", "Display.YStart", "40", "Display.Height", "185", NULL, NULL, }, + { "Cartridge.MD5", "31512cdfadfd82bfb6f196e3b0fd83cd", "Cartridge.Name", "River Patrol", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-004", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "3F", "Display.YStart", "40", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "393948436d1f4cc3192410bb918f9724", "Cartridge.Name", "River Raid (1)", "Cartridge.Manufacturer", "Activision (Carol Shaw)", "Cartridge.ModelNo", "AX-020", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.YStart", "36", "Display.Height", "199", NULL, NULL, }, + { "Cartridge.MD5", "291cc37604bc899e8e065c30153fc4b9", "Cartridge.Name", "River Raid (2)", "Cartridge.Manufacturer", "Activision (Carol Shaw)", "Cartridge.ModelNo", "AX-020", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.YStart", "36", "Display.Height", "199", NULL, NULL, }, + { "Cartridge.MD5", "6ce2110ac5dd89ab398d9452891752ab", "Cartridge.Name", "River Raid (Polyvox version)", "Cartridge.Manufacturer", "Polyvox", "Cartridge.Type", "4K", "Display.YStart", "36", "Display.Height", "199", NULL, NULL, }, + { "Cartridge.MD5", "dd92d6ad50976f881d86b52d38616118", "Cartridge.Name", "River Raid (SpkSoft version)", "Cartridge.Manufacturer", "SpkSoft", "Cartridge.Note", "Variant of River Raid by Activision (Carol Shaw)", NULL, NULL, }, + { "Cartridge.MD5", "ab56f1b2542a05bebc4fbccfc4803a38", "Cartridge.Name", "River Raid II", "Cartridge.Manufacturer", "Activision (Dan Kitchen / Imagineering)", "Cartridge.ModelNo", "AK-048-04", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "42", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "2bd00beefdb424fa39931a75e890695d", "Cartridge.Name", "Road Runner", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2663", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "72a46e0c21f825518b7261c267ab886e", "Cartridge.Name", "Robin Hood (1)", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "99005", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "25", "Display.Height", "225", NULL, NULL, }, + { "Cartridge.MD5", "db76f7a0819659d9e585f2cdde9175c7", "Cartridge.Name", "Robin Hood (2)", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "99005", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "25", "Display.Height", "225", NULL, NULL, }, + { "Cartridge.MD5", "1bef389e3dd2d4ca4f2f60d42c932509", "Cartridge.Name", "Robot Fight", "Cartridge.Manufacturer", "Homevision", "Cartridge.ModelNo", "1", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "4f618c2429138e0280969193ed6c107e", "Cartridge.Name", "Robot Tank", "Cartridge.Manufacturer", "Activision (Alan Miller)", "Cartridge.ModelNo", "AZ-028", "Cartridge.Rarity", "Rare", "Cartridge.Type", "FE", NULL, NULL, }, + { "Cartridge.MD5", "67931b0d37dc99af250dd06f1c095e8d", "Cartridge.Name", "Room of Doom", "Cartridge.Manufacturer", "CommaVid", "Cartridge.ModelNo", "CM-004", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "65bd29e8ab1b847309775b0de6b2e4fe", "Cartridge.Name", "Roc 'n' Rope", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2667", "Cartridge.Rarity", "Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "88d8a1accab58cf1abb043613cf185e9", "Cartridge.Name", "Sabotage", "Cartridge.Manufacturer", "Ultravision", NULL, NULL, }, + { "Cartridge.MD5", "1ec57bbd27bdbd08b60c391c4895c1cf", "Cartridge.Name", "Saboteur", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26119", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Display.YStart", "44", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "ed1a784875538c7871d035b7a98c2433", "Cartridge.Name", "Save Our Ship", "Cartridge.Manufacturer", "Technovision", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "19e761e53e5ec8e9f2fceea62715ca06", "Cartridge.Name", "Scuba Diver", "Cartridge.Manufacturer", "Panda Inc.", "Cartridge.ModelNo", "104", "Cartridge.Rarity", "Rare", "Display.YStart", "44", "Display.Height", "197", NULL, NULL, }, + { "Cartridge.MD5", "07f42847a79e4f5ae55cc03304b18c25", "Cartridge.Name", "Sea Hawk", "Cartridge.Manufacturer", "Froggo / Panda Inc. / Sancho", "Cartridge.ModelNo", "FG 1008 / 108 / TEC002", "Cartridge.Rarity", "Rare / Rare / Extremely Rare", "Display.YStart", "37", "Display.Height", "202", NULL, NULL, }, + { "Cartridge.MD5", "624e0a77f9ec67d628211aaf24d8aea6", "Cartridge.Name", "Sea Hawk (Pirate version)", "Cartridge.Manufacturer", "Froggo / Panda Inc. / Sancho", "Cartridge.ModelNo", "FG 1008 / 108 / TEC002", "Cartridge.Rarity", "Rare / Rare / Extremely Rare", "Display.YStart", "37", "Display.Height", "202", NULL, NULL, }, + { "Cartridge.MD5", "5dccf215fdb9bbf5d4a6d0139e5e8bcb", "Cartridge.Name", "Sea Hunt", "Cartridge.Manufacturer", "Froggo", "Cartridge.ModelNo", "FG 1009", "Cartridge.Rarity", "Rare", "Display.YStart", "44", "Display.Height", "197", NULL, NULL, }, + { "Cartridge.MD5", "68489e60268a5e6e052bad9c62681635", "Cartridge.Name", "Sea Monster", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG201", "Cartridge.Rarity", "Rare", "Display.YStart", "37", "Display.Height", "259", NULL, NULL, }, + { "Cartridge.MD5", "240bfbac5163af4df5ae713985386f92", "Cartridge.Name", "Seaquest", "Cartridge.Manufacturer", "Activision (Steve Cartwright)", "Cartridge.ModelNo", "AX-022", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "fc24a94d4371c69bc58f5245ada43c44", "Cartridge.Name", "Secret Quest", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26170", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6SC", "Display.XStart", "16", "Display.Width", "128", "Display.YStart", "42", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "efffafc17b7cb01b9ca35324aa767364", "Cartridge.Name", "Seesaw", "Cartridge.Manufacturer", "Cooper Black", "Cartridge.Note", "Variant of Circus Atari (Joystick Version) by Atari", "Cartridge.ModelNo", "CX2630", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "42", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "8da51e0c4b6b46f7619425119c7d018e", "Cartridge.Name", "Sentinel", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26183", "Cartridge.Rarity", "Rare", "Cartridge.Note", "One player light gun only", "Controller.Left", "Lightgun", "Controller.Right", "None", "Cartridge.Type", "F6", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "46", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "54f7efa6428f14b9f610ad0ca757e26c", "Cartridge.Name", "Shark Attack", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2005", "Cartridge.Rarity", "Uncommon", "Display.YStart", "22", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "15c11ab6e4502b2010b18366133fc322", "Cartridge.Name", "Shooting Arcade", "Cartridge.Manufacturer", "Atari / Axlon", "Cartridge.ModelNo", "CX26169", "Cartridge.Rarity", "Unreleased Prototype", NULL, NULL, }, + { "Cartridge.MD5", "b5a1a189601a785bdb2f02a424080412", "Cartridge.Name", "Shootin' Gallery", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3410", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "90b1799dddb8bf748ee286d22e609480", "Cartridge.Name", "Ship Demo", NULL, NULL, }, + { "Cartridge.MD5", "1db3bc4601f22cf43be7ce015d74f59a", "Cartridge.Name", "Ship Demo (version 10)", NULL, NULL, }, + { "Cartridge.MD5", "85e48d68c8d802e3ba9d494a47d6e016", "Cartridge.Name", "Ship Demo (version 15)", NULL, NULL, }, + { "Cartridge.MD5", "a0563dd6d8215c38c488fbbd61435626", "Cartridge.Name", "Ship Demo (version 1501)", NULL, NULL, }, + { "Cartridge.MD5", "1b1daaa9aa5cded3d633bfcbeb06479c", "Cartridge.Name", "Ship Demo (version 1502)", NULL, NULL, }, + { "Cartridge.MD5", "25b6dc012cdba63704ea9535c6987beb", "Cartridge.Name", "Shuttle Orbiter", "Cartridge.Manufacturer", "Avalon Hill", "Cartridge.ModelNo", "50040", "Cartridge.Rarity", "Unbelievably Rare", NULL, NULL, }, + { "Cartridge.MD5", "ea38fcfc06ad87a0aed1a3d1588744e4", "Cartridge.Name", "Sinistar", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26122", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Display.YStart", "37", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "7ead257e8b5a44cac538f5f54c7a0023", "Cartridge.Name", "Sir Lancelot (1)", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "7ead257e8b5a44cac538f5f54c7a0023", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "4c8970f6c294a0a54c9c45e5e8445f93", "Cartridge.Name", "Sir Lancelot (2)", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "7ead257e8b5a44cac538f5f54c7a0023", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "f847fb8dba6c6d66d13724dbe5d95c4d", "Cartridge.Name", "Skateboardin'", "Cartridge.Manufacturer", "Activision (David Crane) / Absolute Entertainment", "Cartridge.ModelNo", "AZ-042 / AG-042", "Cartridge.Rarity", "Extremely Rare / Rare", "Display.YStart", "34", "Display.Height", "210", NULL, NULL, }, + { "Cartridge.MD5", "39c78d682516d79130b379fa9deb8d1c", "Cartridge.Name", "Skeet Shoot (1)", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 1001", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "5f2b4c155949f01c06507fb32369d42a", "Cartridge.Name", "Skeet Shoot (2)", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 1001", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "8654d7f0fb351960016e06646f639b02", "Cartridge.Name", "Ski Hunt", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "37", "Display.Height", "235", "Timer.Adjustment", "2", NULL, NULL, }, + { "Cartridge.MD5", "f10e3f45fb01416c87e5835ab270b53a", "Cartridge.Name", "Ski Run", "Cartridge.Rarity", "Extremely Rare", "Cartridge.ModelNo", "TP-607", "Display.YStart", "52", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "b76fbadc8ffb1f83e2ca08b6fb4d6c9f", "Cartridge.Name", "Skiing (1)", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-005", "Cartridge.Rarity", "Uncommon", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "60bbd425cb7214ddb9f9a31948e91ecb", "Cartridge.Name", "Skiing (2)", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-005", "Cartridge.Rarity", "Uncommon", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "46c021a3e9e2fd00919ca3dd1a6b76d8", "Cartridge.Name", "Sky Diver", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2629", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "8bd8f65377023bdb7c5fcf46ddda5d31", "Cartridge.Name", "Sky Jinks (4K version)", "Cartridge.Note", "4K version of 2K ROM", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-019", "Cartridge.Rarity", "Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "2a0ba55e56e7a596146fa729acf0e109", "Cartridge.Name", "Sky Jinks", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-019", "Cartridge.Rarity", "Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "3b91c347d8e6427edbe942a7a405290d", "Cartridge.Name", "Sky Skipper", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5350", "Cartridge.Rarity", "Rare", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "f90b5da189f24d7e1a2117d8c8abc952", "Cartridge.Name", "Slot Machine / Slots (1)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2653 / 99823 / 75111", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "705fe719179e65b0af328644f3a04900", "Cartridge.Name", "Slot Machine / Slots (2)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2653 / 99823 / 75111", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "aed82052f7589df05a3f417bb4e45f0c", "Cartridge.Name", "Slot Racers / Maze (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2606 / 99825 /75112", "Cartridge.Rarity", "Uncommon / Common", "Display.YStart", "34", NULL, NULL, }, + { "Cartridge.MD5", "5f708ca39627697e859d1c53f8d8d7d2", "Cartridge.Name", "Slot Racers / Maze (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2606 / 99825 /75112", "Cartridge.Rarity", "Uncommon / Common", "Display.YStart", "34", NULL, NULL, }, + { "Cartridge.MD5", "24aff972d58990f9b88a6d787c796f1e", "Cartridge.Name", "Smurf Rescue in Gargamel's Castle (PAL version)", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2465", "Cartridge.Rarity", "Uncommon", "Display.Format", "PAL", NULL, NULL, }, + { "Cartridge.MD5", "3d1e83afdb4265fa2fb84819c9cfd39c", "Cartridge.Name", "Smurf Rescue in Gargamel's Castle", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2465", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "a204cd4fb1944c86e800120706512a64", "Cartridge.Name", "Smurfs Save The Day", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2511", "Cartridge.Rarity", "Unbelievably Rare", NULL, NULL, }, + { "Cartridge.MD5", "898b5467551d32af48a604802407b6e8", "Cartridge.Name", "Snail Against Squirrel", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG208", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "245", NULL, NULL, }, + { "Cartridge.MD5", "9c6faa4ff7f2ae549bbcb14f582b70e4", "Cartridge.Name", "Sneak 'N' Peak", "Cartridge.Manufacturer", "U.S. Games (Vidtec)", "Cartridge.ModelNo", "VC 1002", "Cartridge.Rarity", "Rare", "Display.YStart", "27", "Display.Height", "229", NULL, NULL, }, + { "Cartridge.MD5", "57939b326df86b74ca6404f64f89fce9", "Cartridge.Name", "Snoopy and the Red Baron", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26111", "Cartridge.Rarity", "Rare", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "947317a89af38a49c4864d6bdd6a91fb", "Cartridge.Name", "Solar Fox", "Cartridge.Manufacturer", "CBS Electronics", "Cartridge.ModelNo", "4L-2487", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "97842fe847e8eb71263d6f92f7e122bd", "Cartridge.Name", "Solar Storm", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "O3206", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "42", "Display.Height", "190", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "e72eb8d4410152bdcb69e7fba327b420", "Cartridge.Name", "Solaris", "Cartridge.Manufacturer", "Atari (Douglas Neubauer)", "Cartridge.ModelNo", "CX26136", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F6", "Display.YStart", "38", "Display.Height", "193", NULL, NULL, }, + { "Cartridge.MD5", "d2c4f8a4a98a905a9deef3ba7380ed64", "Cartridge.Name", "Sorcerer", "Cartridge.Manufacturer", "Mythicon", "Cartridge.ModelNo", "MA-1001", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "5f7ae9a7f8d79a3b37e8fc841f65643a", "Cartridge.Name", "Sorcerer's Apprentice", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26109", "Cartridge.Rarity", "Rare", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "f78c125b5da483c41e51522947d6c4ce", "Cartridge.Name", "Sound Paddle (Version 1)", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "eee7695ae3eea7818321df0b790b31f3", "Cartridge.Name", "Sound Paddle (Version 2)", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "310ba30e25ea8957e58180b663503c0c", "Cartridge.Name", "Sound X6", NULL, NULL, }, + { "Cartridge.MD5", "32f4e47a71601ab06cfb59e1c6a0b846", "Cartridge.Name", "SoundX", "Cartridge.Manufacturer", "Ed Federmeyer", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "17badbb3f54d1fc01ee68726882f26a6", "Cartridge.Name", "Space Attack", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5659", "Cartridge.Rarity", "Common", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "559317712f989f097ea464517f1a8318", "Cartridge.Name", "Space Canyon", "Cartridge.Manufacturer", "Panda Inc.", "Cartridge.ModelNo", "100", "Cartridge.Rarity", "Rare", "Display.YStart", "35", "Display.Height", "184", NULL, NULL, }, + { "Cartridge.MD5", "df6a28a89600affe36d94394ef597214", "Cartridge.Name", "Space Cavern", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2002", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "72ffbef6504b75e69ee1045af9075f66", "Cartridge.Name", "Space Invaders", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2632 / 4975153", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "42", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "012020625a3227815e47b37fd025e480", "Cartridge.Name", "Space Invaders (alternate version)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2632", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "42", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "6f2aaffaaf53d23a28bf6677b86ac0e3", "Cartridge.Name", "Space Jockey (1)", "Cartridge.Manufacturer", "U.S. Games (Vidtec)", "Cartridge.ModelNo", "VC 1001", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "d1a9478b99d6a55e13a9fd4262da7cd4", "Cartridge.Name", "Space Jockey (2)", "Cartridge.Manufacturer", "U.S. Games (Vidtec)", "Cartridge.ModelNo", "VC 1001", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "45040679d72b101189c298a864a5b5ba", "Cartridge.Name", "SpaceMaster X-7", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11022", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "1a624e236526c4c8f31175e9c89b2a22", "Cartridge.Name", "Space Raid", "Cartridge.Manufacturer", "Rainbow Vision", "Cartridge.ModelNo", "SS-007", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "3dfb7c1803f937fadc652a3e95ff7dc6", "Cartridge.Name", "Space Robot", "Cartridge.Manufacturer", "Artic", "Cartridge.ModelNo", "SM8001", "Cartridge.Rarity", "Unbelievably Rare", NULL, NULL, }, + { "Cartridge.MD5", "5894c9c0c1e7e29f3ab86c6d3f673361", "Cartridge.Name", "Space Shuttle", "Cartridge.Manufacturer", "Activision (Steve Kitchen)", "Cartridge.ModelNo", "AZ-033", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "df2745d585238780101df812d00b49f4", "Cartridge.Name", "Space Tunnel (1)", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG202", "Cartridge.Rarity", "Rare", "Display.YStart", "37", "Display.Height", "259", NULL, NULL, }, + { "Cartridge.MD5", "8917f7c1ac5eb05b82331cf01c495af2", "Cartridge.Name", "Space Tunnel (2)", "Cartridge.Manufacturer", "Bit Corp.", "Cartridge.ModelNo", "PG202", "Cartridge.Rarity", "Rare", "Display.YStart", "37", "Display.Height", "259", NULL, NULL, }, + { "Cartridge.MD5", "7e9da5cb84d5bc869854938fe3e85ffa", "Cartridge.Name", "Space War (4K version)", "Cartridge.Note", "4K version of 2K ROM", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2604", "Cartridge.Rarity", "Uncommon", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "a7ef44ccb5b9000caf02df3e6da71a92", "Cartridge.Name", "Space War", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2604", "Cartridge.Rarity", "Uncommon", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "ec5c861b487a5075876ab01155e74c6c", "Cartridge.Name", "Spacechase", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2001", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "24d018c4a6de7e5bd19a36f2b879b335", "Cartridge.Name", "Spider Fighter", "Cartridge.Manufacturer", "Activision (Larry Miller)", "Cartridge.ModelNo", "AX-021", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.YStart", "35", "Display.Height", "202", NULL, NULL, }, + { "Cartridge.MD5", "199eb0b8dce1408f3f7d46411b715ca9", "Cartridge.Name", "Spider-Man", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5900", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "33", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "8454ed9787c9d8211748ccddb673e920", "Cartridge.Name", "Spiderdroid", "Cartridge.Manufacturer", "Froggo", "Cartridge.Rarity", "Rare", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "40", "Display.Height", "182", NULL, NULL, }, + { "Cartridge.MD5", "a4e885726af9d97b12bb5a36792eab63", "Cartridge.Name", "Spike's Peak", "Cartridge.Manufacturer", "Xonox", "Cartridge.ModelNo", "99001", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "cef2287d5fd80216b2200fb2ef1adfa8", "Cartridge.Name", "Spitfire Attack", "Cartridge.Manufacturer", "Milton Bradley", "Cartridge.ModelNo", "4363", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "4cd796b5911ed3f1062e805a3df33d98", "Cartridge.Name", "Springer", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-006", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "3F", "Display.YStart", "36", "Display.Height", "202", NULL, NULL, }, + { "Cartridge.MD5", "5a8afe5422abbfb0a342fb15afd7415f", "Cartridge.Name", "Sprintmaster", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26155", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6SC", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "27", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "3105967f7222cc36a5ac6e5f6e89a0b4", "Cartridge.Name", "Spy Hunter", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "011-02", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "F8", "Display.YStart", "32", "Display.Height", "200", "TIA.PlayerDelay", "0", NULL, NULL, }, + { "Cartridge.MD5", "ba257438f8a78862a9e014d831143690", "Cartridge.Name", "Squeeze Box", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 2002", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "21a96301bb0df27fde2e7eefa49e0397", "Cartridge.Name", "Sssnake", "Cartridge.Manufacturer", "Data Age / Gameworld", "Cartridge.ModelNo", "DA 1003 / DA 1002", "Cartridge.Rarity", "Common / Extremely Rare", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "21d7334e406c2407e69dbddd7cec3583", "Cartridge.Name", "Stampede (1)", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-011", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "e66e5af5dea661d58420088368e4ef0d", "Cartridge.Name", "Stampede (2)", "Cartridge.Manufacturer", "Activision (Bob Whitehead)", "Cartridge.ModelNo", "AG-011", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "2K", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "38bd172da8b2a3a176e517c213fcd5a6", "Cartridge.Name", "Standalone Test Tape", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "MAO17600", NULL, NULL, }, + { "Cartridge.MD5", "f526d0c519f5001adb1fc7948bfbb3ce", "Cartridge.Name", "Star Fox", "Cartridge.Manufacturer", "Mythicon", "Cartridge.ModelNo", "MA-1003", "Cartridge.Rarity", "Uncommon", "Display.YStart", "54", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "57fa2d09c9e361de7bd2aa3a9575a760", "Cartridge.Name", "Stargate (1)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26120", "Cartridge.Rarity", "Rare", "Cartridge.Note", "Variant of Defender II by Atari", "Cartridge.Type", "F8SC", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "37", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "0c48e820301251fbb6bcdc89bd3555d9", "Cartridge.Name", "Stargate (2)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26120", "Cartridge.Rarity", "Rare", "Cartridge.Note", "Variant of Defender II by Atari", "Cartridge.Type", "F8SC", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "37", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "a3c1c70024d7aabb41381adbfb6d3b25", "Cartridge.Name", "Star Gunner", "Cartridge.Manufacturer", "Telesys", "Cartridge.ModelNo", "1005", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "cbd981a23c592fb9ab979223bb368cd5", "Cartridge.Name", "Star Raiders", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2660 / 4975187", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Uses Joystick/Keypad combination", "Controller.Right", "Keypad", NULL, NULL, }, + { "Cartridge.MD5", "e363e467f605537f3777ad33e74e113a", "Cartridge.Name", "Star Ship / Outer Space (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2603 / 6699803 / 4975601", "Cartridge.Rarity", "Rare", "Display.YStart", "32", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "7b938c7ddf18e8362949b62c7eaa660a", "Cartridge.Name", "Star Ship / Outer Space (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2603 / 6699803 / 4975601", "Cartridge.Rarity", "Rare", "Display.YStart", "32", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "79e5338dbfa6b64008bb0d72a3179d3c", "Cartridge.Name", "Star Strike", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT4313", "Cartridge.Rarity", "Rare", "Display.YStart", "43", "Display.Height", "193", NULL, NULL, }, + { "Cartridge.MD5", "03c3f7ba4585e349dd12bfa7b34b7729", "Cartridge.Name", "Star Trek: Strategic Operations Simulator", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "004-01", "Cartridge.Rarity", "Rare", "Display.YStart", "35", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "813985a940aa739cc28df19e0edd4722", "Cartridge.Name", "Star Voyager", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3201", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "6339d28c9a7f92054e70029eb0375837", "Cartridge.Name", "Star Wars: The Arcade Game", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5540", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "E0", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "39", "Display.Height", "184", NULL, NULL, }, + { "Cartridge.MD5", "5336f86f6b982cc925532f2e80aa1e17", "Cartridge.Name", "Star Wars: Return of the Jedi, Death Star Battle", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5060", "Cartridge.Rarity", "Rare", "Cartridge.Type", "E0", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "35", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "3c8e57a246742fa5d59e517134c0b4e6", "Cartridge.Name", "Star Wars: The Empire Strikes Back", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5050", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "41", "Display.Height", "188", NULL, NULL, }, + { "Cartridge.MD5", "c9f6e521a49a2d15dac56b6ddb3fb4c7", "Cartridge.Name", "Star Wars: Jedi Arena", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5000", "Cartridge.Rarity", "Rare", "Cartridge.Type", "4K", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "31", "Display.Height", "199", "Cartridge.Note", "Uses swapped paddles", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "d69559f9c9dc6ef528d841bf9d91b275", "Cartridge.Name", "Starmaster", "Cartridge.Manufacturer", "Activision (Alan Miller)", "Cartridge.ModelNo", "AX-016", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "656dc247db2871766dffd978c71da80c", "Cartridge.Name", "Steeplechase (1)", "Cartridge.Manufacturer", "Atari (available with Sears label only)", "Cartridge.ModelNo", "CX2614 / 4975126", "Cartridge.Rarity", "Rare", "Display.Height", "198", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "a174cece06b3abc0aec3516913cdf9cc", "Cartridge.Name", "Steeplechase (2)", "Cartridge.Manufacturer", "Atari (available with Sears label only)", "Cartridge.ModelNo", "CX2614 / 4975126", "Cartridge.Rarity", "Rare", "Display.Height", "198", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "47aef18509051bab493589cb2619170b", "Cartridge.Name", "Stell-A-Sketch", "Cartridge.Manufacturer", "Retroware", "Cartridge.Rarity", "New Release", "Cartridge.Note", "Uses two steering controllers", "Controller.Left", "Driving", "Controller.Right", "Driving", NULL, NULL, }, + { "Cartridge.MD5", "0b8d3002d8f744a753ba434a4d39249a", "Cartridge.Name", "Stellar Track", "Cartridge.Manufacturer", "Atari (available with Sears label only)", "Cartridge.ModelNo", "CX2619 / 4975159", "Cartridge.Rarity", "Rare", "Display.XStart", "8", "Display.Width", "136", NULL, NULL, }, + { "Cartridge.MD5", "9333172e3c4992ecf548d3ac1f2553eb", "Cartridge.Name", "Strategy X", "Cartridge.Manufacturer", "Konami / Gakken", "Cartridge.ModelNo", "010", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "e10d2c785aadb42c06390fae0d92f282", "Cartridge.Name", "Strawberry Shortcake Musical Match-Ups", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5910", "Cartridge.Rarity", "Uncommon", "Display.YStart", "27", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "396f7bc90ab4fa4975f8c74abe4e81f0", "Cartridge.Name", "Street Racer / Speedway II (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2612 / 6699804 / 4975103", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Uses swapped paddles", "Controller.Left", "Paddles", "Controller.Right", "Paddles", "Display.YStart", "34", NULL, NULL, }, + { "Cartridge.MD5", "6ff4156d10b357f61f09820d03c0f852", "Cartridge.Name", "Street Racer / Speedway II (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2612 / 6699804 / 4975103", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Uses swapped paddles", "Controller.Left", "Paddles", "Controller.Right", "Paddles", "Display.YStart", "34", NULL, NULL, }, + { "Cartridge.MD5", "7b3cf0256e1fa0fdc538caf3d5d86337", "Cartridge.Name", "Stronghold", "Cartridge.Manufacturer", "CommaVid", "Cartridge.ModelNo", "CM-009", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "93c52141d3c4e1b5574d072f1afde6cd", "Cartridge.Name", "Subterranea", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "O3213", "Cartridge.Rarity", "Rare", "Display.YStart", "29", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "5af9cd346266a1f2515e1fbc86f5186a", "Cartridge.Name", "Sub Scan", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "002-01", "Cartridge.Rarity", "Uncommon", "Display.YStart", "46", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "f3f5f72bfdd67f3d0e45d097e11b8091", "Cartridge.Name", "Submarine Commander", "Cartridge.Manufacturer", "Atari (available with Sears label only)", "Cartridge.ModelNo", "CX2647 / 4975412", "Cartridge.Rarity", "Extremely Rare Prototype / Extremely Rare", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "e4c666ca0c36928b95b13d33474dbb44", "Cartridge.Name", "Suicide Mission", "Cartridge.ModelNo", "AR-4102", "Cartridge.Manufacturer", "Starpath (Steve Hales and Steve Landrum)", "Cartridge.Rarity", "Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "172", NULL, NULL, }, + { "Cartridge.MD5", "712924a2c7b692f6e7b009285c2169a7", "Cartridge.Name", "Suicide Mission (no title screen)", "Cartridge.ModelNo", "AR-4102", "Cartridge.Manufacturer", "Starpath (Steve Hales and Steve Landrum)", "Cartridge.Rarity", "Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "38", "Display.Height", "172", NULL, NULL, }, + { "Cartridge.MD5", "45027dde2be5bdd0cab522b80632717d", "Cartridge.Name", "Summer Games", "Cartridge.Manufacturer", "Epyx", "Cartridge.ModelNo", "8056100250", "Cartridge.Rarity", "Rare", "Cartridge.Type", "F6", "Display.FrameRate", "30", "Display.YStart", "42", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "7adbcf78399b19596671edbffc3d34aa", "Cartridge.Name", "Super Baseball", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26152", "Cartridge.Rarity", "Uncommon", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "0ad9a358e361256b94f3fb4f2fa5a3b1", "Cartridge.Name", "Super Breakout (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2608 / 4975165", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Cartridge.Note", "One player paddles only", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "136", "Display.YStart", "35", "Display.Height", "180", "Controller.Left", "Paddles", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "8885d0ce11c5b40c3a8a8d9ed28cefef", "Cartridge.Name", "Super Breakout (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2608 / 4975165", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Cartridge.Note", "One player paddles only", "Display.FrameRate", "30", "Display.XStart", "8", "Display.Width", "136", "Display.YStart", "35", "Display.Height", "180", "Controller.Left", "Paddles", "Controller.Right", "None", NULL, NULL, }, + { "Cartridge.MD5", "9d37a1be4a6e898026414b8fee2fc826", "Cartridge.Name", "Super Challenge Baseball", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5658", "Cartridge.Rarity", "Common", "Display.YStart", "38", "Display.Height", "197", NULL, NULL, }, + { "Cartridge.MD5", "e275cbe7d4e11e62c3bfcfb38fca3d49", "Cartridge.Name", "Super Challenge Football", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5658", "Cartridge.Rarity", "Common", "Display.YStart", "38", "Display.Height", "197", NULL, NULL, }, + { "Cartridge.MD5", "c29f8db680990cb45ef7fef6ab57a2c2", "Cartridge.Name", "Super Cobra", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5320", "Cartridge.Rarity", "Uncommon", "Display.Height", "206", NULL, NULL, }, + { "Cartridge.MD5", "724613effaf7743cbcd695fab469c2a8", "Cartridge.Name", "Super Ferrari (1)", "Cartridge.Manufacturer", "Rainbow Vision", "Cartridge.ModelNo", "SS-011", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "2b27eb194e13f3b38d23c879cc1e3abf", "Cartridge.Name", "Super Ferrari (2)", "Cartridge.Manufacturer", "Rainbow Vision", "Cartridge.ModelNo", "SS-011", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "09abfe9a312ce7c9f661582fdf12eab6", "Cartridge.Name", "Super Football", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26154", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "F6SC", "Display.XStart", "4", "Display.Width", "152", "Display.YStart", "42", "Display.Height", "182", NULL, NULL, }, + { "Cartridge.MD5", "5de8803a59c36725888346fdc6e7429d", "Cartridge.Name", "Superman (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2631 / 6699845 / 4975152", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "a9531c763077464307086ec9a1fd057d", "Cartridge.Name", "Superman (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2631 / 6699845 / 4975152", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "149b543c917c180a1b02d33c12415206", "Cartridge.Name", "Superman (CCE Version)", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2631", "Cartridge.Rarity", "Common", "Cartridge.Type", "4K", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "aec9b885d0e8b24e871925630884095c", "Cartridge.Name", "Surf's Up", "Cartridge.Manufacturer", "Amiga", "Cartridge.ModelNo", "3125", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "17", "Display.Height", "244", NULL, NULL, }, + { "Cartridge.MD5", "c20f15282a1aa8724d70c117e5c9709e", "Cartridge.Name", "Surfer's Paradise: But Danger Below!", "Cartridge.Manufacturer", "Video Gems", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "4d7517ae69f95cfbc053be01312b7dba", "Cartridge.Name", "Surround / Chase (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2641 / 99807 / 75105", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "52a0003efb3b1c49fcde4dbc2c685d8f", "Cartridge.Name", "Surround / Chase (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2641 / 99807 / 75105", "Cartridge.Rarity", "Common", NULL, NULL, }, + { "Cartridge.MD5", "045035f995272eb2deb8820111745a07", "Cartridge.ModelNo", "AR-4401", "Cartridge.Name", "Survival Island", "Cartridge.Manufacturer", "Starpath (Scott Nelson)", "Cartridge.Rarity", "Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Emulation.HmoveBlanks", "No", "Display.YStart", "38", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "85e564dae5687e431955056fbda10978", "Cartridge.Name", "Survival Run", "Cartridge.Manufacturer", "Milton Bradley", "Cartridge.ModelNo", "4362", "Cartridge.Rarity", "Rare", "Display.YStart", "27", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "5ec73ac7d2ac95ac9530c6d33e713d14", "Cartridge.Name", "Sweat!: The Decathlon Game", "Cartridge.Manufacturer", "Starpath (Scott Nelson)", "Cartridge.Rarity", "Unreleased Prototype", "Cartridge.Type", "AR", "Controller.Left", "Paddles", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "35", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "528400fad9a77fd5ad7fc5fdc2b7d69d", "Cartridge.Name", "Sword of Saros", "Cartridge.ModelNo", "AR-4201", "Cartridge.Manufacturer", "Starpath (John Leupp)", "Cartridge.Rarity", "Extremely Rare", "Cartridge.Type", "AR", "Emulation.CPU", "High", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "35", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "05ebd183ea854c0a1b56c218246fbbae", "Cartridge.Name", "SwordQuest EarthWorld", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2656", "Cartridge.Rarity", "Common", "Display.YStart", "37", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "f9d51a4e5f8b48f68770c89ffd495ed1", "Cartridge.Name", "SwordQuest FireWorld", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2656", "Cartridge.Rarity", "Common", "Display.YStart", "37", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "bc5389839857612cfabeb810ba7effdc", "Cartridge.Name", "SwordQuest WaterWorld", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2671", "Cartridge.Rarity", "Unbelievably Rare", "Display.YStart", "37", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "294762000e853b4319f9991c1ced5dfc", "Cartridge.Name", "T.F. Space Invaders", "Cartridge.Type", "4K", "Cartridge.Note", "Variant of Space Invaders by Atari/Sears", "Display.YStart", "42", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "d45ebf130ed9070ea8ebd56176e48a38", "Cartridge.Name", "Tac-Scan", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "001-01", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Cartridge.Note", "One player right paddles only", "Display.FrameRate", "60", "Display.XStart", "8", "Display.Width", "152", "Display.YStart", "44", "Display.Height", "202", "Controller.Left", "None", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "fa6fe97a10efb9e74c0b5a816e6e1958", "Cartridge.Name", "Tanks But No Tanks", "Cartridge.Manufacturer", "Emag / Zimag", "Cartridge.ModelNo", "GN-030 / 707-111", "Cartridge.Rarity", "Extremely Rare / Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "c77d3b47f2293e69419b92522c6f6647", "Cartridge.Name", "Tank Brigade", "Cartridge.Manufacturer", "Panda", "Cartridge.ModelNo", "101", "Cartridge.Rarity", "Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "de3d0e37729d85afcb25a8d052a6e236", "Cartridge.Name", "Tape Worm", "Cartridge.Manufacturer", "Spectravision", "Cartridge.ModelNo", "SA-204", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "c0d2434348de72fa6edcc6d8e40f28d7", "Cartridge.Name", "Tapper", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "010-01", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "32", NULL, NULL, }, + { "Cartridge.MD5", "0c35806ff0019a270a7acae68de89d28", "Cartridge.Name", "Task Force", "Cartridge.Manufacturer", "Froggo", "Cartridge.ModelNo", "FG 1003", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "a1ead9c181d67859aa93c44e40f1709c", "Cartridge.Name", "Tax Avoiders", "Cartridge.Manufacturer", "American Videogame", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "4702d8d9b48a332724af198aeac9e469", "Cartridge.Name", "Taz", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2699", "Cartridge.Rarity", "Rare", "Display.YStart", "40", "Display.Height", "187", NULL, NULL, }, + { "Cartridge.MD5", "42cdd6a9e42a3639e190722b8ea3fc51", "Cartridge.Name", "Tennis (1)", "Cartridge.Manufacturer", "Activision (Alan Miller)", "Cartridge.ModelNo", "AG-007", "Cartridge.Rarity", "Common", "Cartridge.Type", "2K", "Display.FrameRate", "30", "Display.YStart", "39", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "aca09ffea77174b148b96b205109db4d", "Cartridge.Name", "Tennis (2)", "Cartridge.Manufacturer", "Activision (Alan Miller)", "Cartridge.ModelNo", "AG-007", "Cartridge.Rarity", "Common", "Cartridge.Type", "2K", "Display.FrameRate", "30", "Display.YStart", "39", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "b0e1ee07fbc73493eac5651a52f90f00", "Cartridge.Name", "Tetris26 (1)", "Cartridge.Manufacturer", "Colin Hughes", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "cae8f83c06831ec7bb6a3c07e98e9342", "Cartridge.Name", "Tetris26 (2)", "Cartridge.Manufacturer", "Colin Hughes", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "c6d48c6ae6461e0e82753540a985ac9e", "Cartridge.Name", "Tetris", NULL, NULL, }, + { "Cartridge.MD5", "5c73693a89b06e5a09f1721a13176f95", "Cartridge.Name", "Test Cartridge", NULL, NULL, }, + { "Cartridge.MD5", "5eeb81292992e057b290a5cd196f155d", "Cartridge.Name", "Texas Chainsaw Massacre", "Cartridge.Manufacturer", "Wizard Video", "Cartridge.ModelNo", "008", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "a98b649912b6ca19eaf5c2d2faf38562", "Cartridge.Name", "This Planet Sucks (1)", "Cartridge.Manufacturer", "What?Where", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "0acaf71e60b89f6b6eab63db6ab84510", "Cartridge.Name", "This Planet Sucks (2)", "Cartridge.Manufacturer", "What?Where", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "3b64a00ce147c3c29f7f8f8e531d08d8", "Cartridge.Name", "This Planet Sucks (16K Version)", "Cartridge.Manufacturer", "What?Where", "Cartridge.Rarity", "New Release", NULL, NULL, }, + { "Cartridge.MD5", "e63a87c231ee9a506f9599aa4ef7dfb9", "Cartridge.Name", "Threshold", "Cartridge.Manufacturer", "Tigervision", "Cartridge.ModelNo", "7-003", "Cartridge.Rarity", "Rare", "Display.YStart", "22", NULL, NULL, }, + { "Cartridge.MD5", "cf507910d6e74568a68ac949537bccf9", "Cartridge.Name", "Thunderground", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "003-01", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "4e99ebd65a967cabf350db54405d577c", "Cartridge.Name", "Time Pilot (1)", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2663", "Cartridge.Rarity", "Rare", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "fc2104dd2dadf9a6176c1c1c8f87ced9", "Cartridge.Name", "Time Pilot (2)", "Cartridge.Manufacturer", "Coleco", "Cartridge.ModelNo", "2663", "Cartridge.Rarity", "Rare", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "332f01fd18e99c6584f61aa45ee7791e", "Cartridge.Name", "Time Warp", "Cartridge.Manufacturer", "CCE", "Cartridge.ModelNo", "C-845", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", "Display.Height", "229", NULL, NULL, }, + { "Cartridge.MD5", "da6465a34d2e44d26aa9a2a0cd1bce4d", "Cartridge.Name", "Title Match Pro Wrestling", "Cartridge.Manufacturer", "Activision / Absolute Entertainment", "Cartridge.ModelNo", "AG-041", "Cartridge.Rarity", "Extremely Rare / Rare", "Display.YStart", "47", "Display.Height", "184", NULL, NULL, }, + { "Cartridge.MD5", "ece908d77ab944f7bac84322b9973549", "Cartridge.Name", "Tom Boy", "Cartridge.Manufacturer", "Rainbow Vision", "Cartridge.Note", "Variant of Pitfall by Activision", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "fa2be8125c3c60ab83e1c0fe56922fcb", "Cartridge.Name", "Tooth Protectors", "Cartridge.Manufacturer", "DSD / Camelot", "Cartridge.Rarity", "Unbelievably Rare", "Cartridge.Type", "E0", NULL, NULL, }, + { "Cartridge.MD5", "0aa208060d7c140f20571e3341f5a3f8", "Cartridge.Name", "Towering Inferno", "Cartridge.Manufacturer", "U.S. Games", "Cartridge.ModelNo", "VC 1009", "Cartridge.Rarity", "Uncommon", "Cartridge.Note", "Uses swapped joysticks", "Display.YStart", "32", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "6ae4dc6d7351dacd1012749ca82f9a56", "Cartridge.Name", "Track and Field", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26125 (label says CX26127 on some)", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "4df9d7352a56a458abb7961bf10aba4e", "Cartridge.Name", "Traffic", "Display.YStart", "35", "Display.Height", "208", NULL, NULL, }, + { "Cartridge.MD5", "24df052902aa9de21c2b2525eb84a255", "Cartridge.Name", "Trick Shot", "Cartridge.Manufacturer", "Imagic", "Cartridge.ModelNo", "IA3000", "Cartridge.Rarity", "Rare", "Display.YStart", "45", "Display.Height", "177", NULL, NULL, }, + { "Cartridge.MD5", "fb27afe896e7c928089307b32e5642ee", "Cartridge.Name", "Tron Deadly Discs", "Cartridge.Manufacturer", "M-Network (Mattel)", "Cartridge.ModelNo", "MT5662", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.XStart", "8", "Display.Width", "144", "Display.YStart", "53", "Display.Height", "162", NULL, NULL, }, + { "Cartridge.MD5", "e17699a54c90f3a56ae4820f779f72c4", "Cartridge.Name", "Tuby Bird", "Cartridge.Manufacturer", "Rainbow Vision", "Cartridge.Note", "Variant of Dolphin by Activision", "Cartridge.ModelNo", "SS-020", "Display.YStart", "44", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "b2737034f974535f5c0c6431ab8caf73", "Cartridge.Name", "Tunnel Runner (1)", "Cartridge.Manufacturer", "CBS Electronics", "Cartridge.ModelNo", "4L-2520", "Cartridge.Rarity", "Rare", "Cartridge.Type", "FASC", "Display.FrameRate", "20", "Display.YStart", "67", "Display.Height", "153", NULL, NULL, }, + { "Cartridge.MD5", "efefc02bbc5258815457f7a5b8d8750a", "Cartridge.Name", "Tunnel Runner (2)", "Cartridge.Manufacturer", "CBS Electronics", "Cartridge.ModelNo", "4L-2520", "Cartridge.Rarity", "Rare", "Cartridge.Type", "FASC", "Display.FrameRate", "20", "Display.YStart", "67", "Display.Height", "153", NULL, NULL, }, + { "Cartridge.MD5", "7a5463545dfb2dcfdafa6074b2f2c15e", "Cartridge.Name", "Turmoil", "Cartridge.Manufacturer", "20th Century Fox Video Games", "Cartridge.ModelNo", "11007", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.FrameRate", "30", "Display.YStart", "52", "Display.Height", "186", NULL, NULL, }, + { "Cartridge.MD5", "085322bae40d904f53bdcc56df0593fc", "Cartridge.Name", "Tutankham", "Cartridge.Manufacturer", "Parker Bros.", "Cartridge.ModelNo", "PB5340", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "619de46281eb2e0adbb98255732483b4", "Cartridge.Name", "UFO Patrol", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "22", NULL, NULL, }, + { "Cartridge.MD5", "81a010abdba1a640f7adf7f84e13d307", "Cartridge.Name", "Universal Chaos", "Cartridge.Manufacturer", "Telegames", "Cartridge.ModelNo", "7062 A305", "Cartridge.Rarity", "Rare", "Display.YStart", "42", "Display.Height", "190", NULL, NULL, }, + { "Cartridge.MD5", "a499d720e7ee35c62424de882a3351b6", "Cartridge.Name", "Up 'n' Down", "Cartridge.Manufacturer", "Sega", "Cartridge.ModelNo", "009-01", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "35", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "c6556e082aac04260596b4045bc122de", "Cartridge.Name", "Vanguard", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2669", "Cartridge.Rarity", "Common", "Cartridge.Type", "F8", "Display.YStart", "37", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "3e899eba0ca8cd2972da1ae5479b4f0d", "Cartridge.Name", "Venture", "Cartridge.Manufacturer", "Coleco / Atari", "Cartridge.ModelNo", "2457 / CX26145", "Cartridge.Rarity", "Common / Rare", "Display.YStart", "40", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "539d26b6e9df0da8e7465f0f5ad863b7", "Cartridge.Name", "Video Checkers / Checkers", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2636 / 4975156", "Cartridge.Rarity", "Rare", NULL, NULL, }, + { "Cartridge.MD5", "f0b7db930ca0e548c41a97160b9f6275", "Cartridge.Name", "Video Chess", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2645 / 6699817 / 4975181", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.XStart", "16", "Display.Width", "128", "Display.YStart", "37", "Display.Height", "194", NULL, NULL, }, + { "Cartridge.MD5", "4191b671bcd8237fc8e297b4947f2990", "Cartridge.Name", "Video Jogger", "Cartridge.Manufacturer", "Exus", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "35", "Display.Height", "200", NULL, NULL, }, + { "Cartridge.MD5", "60e0ea3cbe0913d39803477945e9e5ec", "Cartridge.Name", "Video Olympics / Pong Sports (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2621", "Cartridge.Rarity", "Common", "Cartridge.Type", "2K", "Display.YStart", "30", "Display.Height", "205", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "c00b65d1bae0aef6a1b5652c9c2156a1", "Cartridge.Name", "Video Olympics / Pong Sports (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2621", "Cartridge.Rarity", "Common", "Cartridge.Type", "2K", "Display.YStart", "30", "Display.Height", "205", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "107cc025334211e6d29da0b6be46aec7", "Cartridge.Name", "Video Pinball / Arcade Pinball", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2648 / 4975161", "Cartridge.Rarity", "Uncommon", "Display.YStart", "35", "Display.Height", "191", NULL, NULL, }, + { "Cartridge.MD5", "ee659ae50e9df886ac4f8d7ad10d046a", "Cartridge.Name", "Video Reflex", "Cartridge.Manufacturer", "Exus", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "1f349dd41c3f93c4214e5e308dccb056", "Cartridge.Name", "V-Pet (version 006)", NULL, NULL, }, + { "Cartridge.MD5", "6041f400b45511aa3a69fab4b8fc8f41", "Cartridge.Name", "Wabbit", "Cartridge.Manufacturer", "Apollo", "Cartridge.ModelNo", "AP 2010", "Cartridge.Rarity", "Rare", "Display.YStart", "38", "Display.Height", "192", NULL, NULL, }, + { "Cartridge.MD5", "d175258b2973b917a05b46df4e1cf15d", "Cartridge.Name", "Walker", "Cartridge.Note", "Variant of Clown Down Town", "Display.Height", "219", NULL, NULL, }, + { "Cartridge.MD5", "d3456b4cf1bd1a7b8fb907af1a80ee15", "Cartridge.Name", "Wall Ball", "Cartridge.Manufacturer", "Avalon Hill", "Cartridge.ModelNo", "50030", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "c16fbfdbfdf5590cc8179e4b0f5f5aeb", "Cartridge.Name", "Wall Defender", "Cartridge.Manufacturer", "Home Entertainment Suppliers / Bomb", "Cartridge.ModelNo", "CA285", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "219", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "cbe5a166550a8129a5e6d374901dffad", "Cartridge.Name", "Warlords", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2610 / 4975127", "Cartridge.Rarity", "Uncommon", "Cartridge.Type", "4K", "Display.YStart", "37", "Display.Height", "194", "Controller.Left", "Paddles", "Controller.Right", "Paddles", NULL, NULL, }, + { "Cartridge.MD5", "679e910b27406c6a2072f9569ae35fc8", "Cartridge.Name", "Warplock", "Cartridge.Manufacturer", "Data Age", "Cartridge.ModelNo", "DA 1002", "Cartridge.Rarity", "Common", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "9d2938eb2b17bb73e9a79bbc06053506", "Cartridge.Name", "Wing War", "Cartridge.Manufacturer", "Activision (Michael Greene)/ Imagic", "Cartridge.ModelNo", "EIZ-002-04", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "200", "Display.YStart", "52", NULL, NULL, }, + { "Cartridge.MD5", "83fafd7bd12e3335166c6314b3bde528", "Cartridge.Name", "Winter Games", "Cartridge.Manufacturer", "Epyx", "Cartridge.ModelNo", "8056100251", "Cartridge.Rarity", "Rare", "Display.YStart", "37", NULL, NULL, }, + { "Cartridge.MD5", "7b24bfe1b61864e758ada1fe9adaa098", "Cartridge.Name", "Wizard (1)", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unreleased Prototype", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "3b86a27132fb74d9b35d4783605a1bcb", "Cartridge.Name", "Wizard (2)", "Cartridge.Manufacturer", "Atari", "Cartridge.Rarity", "Unreleased Prototype", "Display.Height", "195", NULL, NULL, }, + { "Cartridge.MD5", "7e8aa18bc9502eb57daaf5e7c1e94da7", "Cartridge.Name", "Wizard of Wor", "Cartridge.Manufacturer", "CBS Electronics", "Cartridge.ModelNo", "M8774", "Cartridge.Rarity", "Rare", "Cartridge.Note", "Uses swapped joysticks", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "ec3beb6d8b5689e867bafb5d5f507491", "Cartridge.Name", "Word Zapper", "Cartridge.Manufacturer", "U.S. Games (Vidtec)", "Cartridge.ModelNo", "VC 1003", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "a556b9b2312407d785e1dcad28f04023", "Cartridge.Name", "XX Demo", NULL, NULL, }, + { "Cartridge.MD5", "87f020daa98d0132e98e43db7d8fea7e", "Cartridge.Name", "Worm War I", "Cartridge.Manufacturer", "20th Century Fox Video Games (Sirius)", "Cartridge.ModelNo", "11001", "Cartridge.Rarity", "Uncommon", NULL, NULL, }, + { "Cartridge.MD5", "5961d259115e99c30b64fe7058256bcf", "Cartridge.Name", "X-Man", "Cartridge.Manufacturer", "Universal Gamex", "Cartridge.ModelNo", "GX-001", "Cartridge.Rarity", "Unbelievably Rare", "Display.YStart", "27", NULL, NULL, }, + { "Cartridge.MD5", "eaf744185d5e8def899950ba7c6e7bb5", "Cartridge.Name", "Xenophobe", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX26172", "Cartridge.Rarity", "Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "c6688781f4ab844852f4e3352772289b", "Cartridge.Name", "Xevious", "Cartridge.Manufacturer", "Atari", "Cartridge.ModelNo", "CX2695", "Cartridge.Rarity", "Unbelievably Rare Prototype", "Display.Height", "210", "Display.YStart", "26", NULL, NULL, }, + { "Cartridge.MD5", "c5930d0e8cdae3e037349bfa08e871be", "Cartridge.Name", "Yars' Revenge (1)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2655 / 4975167", "Cartridge.Rarity", "Common", "Display.Height", "193", "Display.YStart", "36", NULL, NULL, }, + { "Cartridge.MD5", "5f681403b1051a0822344f467b05a94d", "Cartridge.Name", "Yars' Revenge (2)", "Cartridge.Manufacturer", "Atari / Sears", "Cartridge.ModelNo", "CX2655 / 4975167", "Cartridge.Rarity", "Common", "Display.Height", "193", "Display.YStart", "36", NULL, NULL, }, + { "Cartridge.MD5", "c469151655e333793472777052013f4f", "Cartridge.Name", "Z-Tack", "Cartridge.Manufacturer", "Bomb", "Cartridge.ModelNo", "13", "Cartridge.Rarity", "Extremely Rare", "Display.YStart", "30", "Display.Height", "205", NULL, NULL, }, + { "Cartridge.MD5", "eea0da9b987d661264cce69a7c13c3bd", "Cartridge.Name", "Zaxxon", "Cartridge.Manufacturer", "Coleco / CCE / CBS Electronics", "Cartridge.ModelNo", "2454 / C-1001 / 4L-2277", "Cartridge.Rarity", "Uncommon / Extremely Rare / Extremely Rare", NULL, NULL, }, + { "Cartridge.MD5", "fb833ed50c865a9a505a125fc9d79a7e", "Cartridge.Name", "Zoo Fun", "Cartridge.Manufacturer", "Homevision", "Cartridge.Rarity", "Extremely Rare", "Display.Height", "249", "Display.YStart", "27", "Timer.Adjustment", "1", NULL, NULL, }, + { "Cartridge.MD5", "4565c1a7abce773e53c75b35414adefd", "Cartridge.Name", "Non-functional STARPATH.BIN", NULL, NULL, }, + { "Cartridge.MD5", "130c5742cd6cbe4877704d733d5b08ca", "Cartridge.Name", "Non-functional WORLDEND.BIN", NULL, NULL, }, + { "Cartridge.MD5", "a8916734ff8c64ec3342f4c73fd5b57d", "Cartridge.Name", "Non-functional SALTDIAG.BIN", NULL, NULL, }, + { "Cartridge.MD5", "6a45e8570cc213ff9f056c5033b4aef5", "Cartridge.Name", "!Unknown ALFCHLNG.BIN", "Display.Height", "239", NULL, NULL, }, + { "Cartridge.MD5", "ce89529d6e98a13ddf3d84827bbdfe68", "Cartridge.Name", "!Unknown BEU05.BIN", NULL, NULL, }, + { "Cartridge.MD5", "a8101cb667e50a46165c6fb48c608b6b", "Cartridge.Name", "!Unknown BEU08S.BIN", NULL, NULL, }, + { "Cartridge.MD5", "7ffc2d80fd49a124808315306d19868e", "Cartridge.Name", "!Unknown DOMINO.BIN", NULL, NULL, }, + { "Cartridge.MD5", "1e060a8025512ad2127e3da11e212ccc", "Cartridge.Name", "!Unknown SWEAT2.BIN", NULL, NULL, }, + { "Cartridge.MD5", "2b71a59a53be5883399917bf582b7772", "Cartridge.Name", "!Unknown Z26-2.BIN", NULL, NULL, }, + +}; diff --git a/stella/src/ui/win32/pch.cxx b/stella/src/ui/win32/pch.cxx new file mode 100644 index 000000000..882b9e45f --- /dev/null +++ b/stella/src/ui/win32/pch.cxx @@ -0,0 +1,88 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#include "pch.hxx" +#include +#include + +#include "resource.h" + +// This will force in the DirectX library + +#pragma comment( lib, "dxguid" ) + +// Bring in DirectInput library (for c_dfDIMouse, etc) + +#pragma comment( lib, "dinput" ) + +// Bring in the common control library + +#pragma comment( lib, "comctl32" ) + +// Bring in multimedia timers + +#pragma comment( lib, "winmm" ) + +void MessageBox( + HINSTANCE hInstance, + HWND hwndParent, + UINT uIDText + ) +{ + const int nMaxStrLen = 1024; + TCHAR tszCaption[nMaxStrLen + 1] = { 0 }; + TCHAR tszText[nMaxStrLen + 1] = { 0 }; + + // Caption is always "StellaX" + + LoadString(hInstance, IDS_STELLA, tszCaption, nMaxStrLen); + + LoadString(hInstance, uIDText, tszText, nMaxStrLen); + + if (hwndParent == NULL) + { + hwndParent = ::GetForegroundWindow(); + } + + ::MessageBox(hwndParent, tszText, tszCaption, MB_ICONWARNING | MB_OK); +} + +void MessageBoxFromWinError( + DWORD dwError, + LPCTSTR pszCaption /* = NULL */ + ) +{ + const int nMaxStrLen = 1024; + TCHAR pszCaptionStellaX[nMaxStrLen + 1]; + + if ( pszCaption == NULL ) + { + // LoadString(hInstance, IDS_STELLA, tszCaption, nMaxStrLen); + lstrcpy( pszCaptionStellaX, _T("StellaX") ); + } + + LPTSTR pszText = NULL; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dwError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&pszText, + 0, + NULL); + + ::MessageBox(::GetForegroundWindow(), pszText, + pszCaption ? pszCaption : pszCaptionStellaX, MB_ICONWARNING | MB_OK); + + ::LocalFree( pszText ); +} + +void MessageBoxFromGetLastError( + LPCTSTR pszCaption /* = NULL */ + ) +{ + MessageBoxFromWinError( GetLastError(), pszCaption ); +} + diff --git a/stella/src/ui/win32/pch.hxx b/stella/src/ui/win32/pch.hxx new file mode 100644 index 000000000..83aa43725 --- /dev/null +++ b/stella/src/ui/win32/pch.hxx @@ -0,0 +1,139 @@ +// +// StellaX +// Jeff Miller 05/01/2000 +// +#ifndef PCH_H +#define PCH_H +#pragma once + +#ifndef _WIN32 +#error This file can only be compiled for a Win32 platform +#endif + +#define WIN32_LEAN_AND_MEAN +#define STRICT + +#include +#include +#include + +// warning C4201: nonstandard extension used : nameless struct/union + +#pragma warning ( once: 4201 ) + +#include +#include +#include + +#include +#include +#include + +#include "debug.hxx" + +// --------------------------------------------------------------------------- +// Conditional defines + +#define DOUBLE_WIDTH + +// +// Macros +// + +#ifdef _DEBUG +#define UNUSED(x) +#else +#define UNUSED(x) x +#endif +#define UNUSED_ALWAYS(x) x + +// +// Simple string class +// + +class CSimpleString +{ +public: + + CSimpleString() : + m_psz( NULL ), + m_cch( -1 ) + { + } + + ~CSimpleString() + { + delete[] m_psz; + m_psz = NULL; + + m_cch = -1; + } + + BOOL Set( LPCTSTR psz ) + { + int cch = lstrlen( psz ); + if ( cch > m_cch ) + { + delete[] m_psz; + m_psz = NULL; + m_cch = -1; + + m_psz = new TCHAR[ cch + 1 ]; + if ( m_psz == NULL ) + { + return FALSE; + } + + m_cch = cch; + } + + memcpy( m_psz, psz, ( cch + 1 ) * sizeof( TCHAR ) ); + + return TRUE; + } + + LPCTSTR Get( void ) const + { + ASSERT( m_psz != NULL ); + return m_psz; + } + + int Length( void ) const + { + return m_cch; + } + +private: + + // + // The string and its size (-1 means not initialized) + // + + LPTSTR m_psz; + int m_cch; + + CSimpleString( const CSimpleString& ); // no implementation + void operator=( const CSimpleString& ); // no implementation +}; + +// +// Utility methods +// + +void MessageBox( + HINSTANCE hInstance, + HWND hwndParent, + UINT uIDText + ); + +void MessageBoxFromWinError( + DWORD dwError, + LPCTSTR pszCaption /* = NULL */ + ); + +void MessageBoxFromGetLastError( + LPCTSTR pszCaption /* = NULL */ + ); + + +#endif diff --git a/stella/src/ui/win32/resource.h b/stella/src/ui/win32/resource.h new file mode 100644 index 000000000..18deef75a --- /dev/null +++ b/stella/src/ui/win32/resource.h @@ -0,0 +1,85 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Stella.rc +// +#define IDB_TILE 101 +#define IDI_APP 102 +#define IDD_ABOUT 103 +#define IDD_MAIN 104 +#define IDD_ABOUT_PAGE 106 +#define IDD_DOC_PAGE 107 +#define IDD_CONFIG_PAGE 108 +#define IDS_ALREADYRUNNING 200 +#define IDS_BADARGUMENT 201 +#define IDS_BADFRAMERATE 202 +#define IDS_BADPADDLEMODE 203 +#define IDS_CANTOPEN 204 +#define IDS_CW_FAILED 208 +#define IDS_DDCP_FAILED 224 +#define IDS_DDCS_FAILED 240 +#define IDS_DDSCL_FAILED 256 +#define IDS_DDSDM_FAILED 272 +#define IDS_DSCSBFAILED 273 +#define IDS_DSSCLFAILED 274 +#define IDS_FATALERROR 275 +#define IDS_FILEFILTER 276 +#define IDS_FILENAME 277 +#define IDS_MANUFACTURER 278 +#define IDS_MIKE 279 +#define IDS_NAME 280 +#define IDS_NODIRECTDRAW 281 +#define IDS_NOGOODVIDEOMODE 282 +#define IDS_OUTOFMEMORY 283 +#define IDS_RARITY 284 +#define IDS_RC_FAILED 288 +#define IDS_STATUSTEXT 289 +#define IDS_STELLA 290 +#define IDS_UNKNOWNERROR 291 +#define IDS_DEBUGBUILD 292 +#define IDS_NODIRECTINPUT 293 +#define IDS_FATALSOUNDERROR 294 +#define IDS_CANTSTARTCONSOLE 295 +#define IDS_COINIT_FAILED 296 +#define IDS_ASS_FAILED 297 +#define IDS_PAS_FAILED 298 +#define IDS_DD_FAILED 299 +#define IDS_DD_INIT_FAILED 300 +#define IDS_DD_ENUMMODES_FAILED 301 +#define IDS_NO_VID_MODE 302 +#define IDS_DI_INIT_FAILED 303 +#define IDS_ROM_LOAD_FAILED 304 +#define IDS_NO_ITEM_SELECTED 305 +#define IDC_ABOUT 1001 +#define IDC_EMAIL_JEFFMILL 1002 +#define IDC_EMAIL_STELLA 1003 +#define IDC_EXIT 1004 +#define IDC_PLAY 1005 +#define IDC_ROMCOUNT 1006 +#define IDC_ROMLIST 1007 +#define IDC_STATIC 1008 +#define IDC_TITLE 1009 +#define IDC_WEB_JEFFMILL 1010 +#define IDC_WEB_STELLA 1011 +#define IDC_WWW_MAME 1012 +#define IDC_ROMPATH 1013 +#define IDC_ROMNOTE 1014 +#define IDC_CONFIG 1015 +#define IDC_PADDLE 1017 +#define IDC_SOUND 1018 +#define IDC_JOYSTICK 1019 +#define IDC_AUTO_SELECT_VIDEOMODE 1020 +#define IDC_BROWSE 1021 +#define IDC_INSTRUCTIONS 2000 +#define IDC_ADOBE 2001 +#define ID_FILE_EXIT 32771 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 296 +#define _APS_NEXT_COMMAND_VALUE 32772 +#define _APS_NEXT_CONTROL_VALUE 1022 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/stella/src/ui/win32/stella.ico b/stella/src/ui/win32/stella.ico new file mode 100644 index 0000000000000000000000000000000000000000..f5d8e9433a6d6a772c94293d2379f1c1756db23a GIT binary patch literal 1078 zcmd5*yKcfj5S$}OX;983Rd5yY8Of*QFX-4$^QTazbeSrfnOy=LMj}#(#M-m3xjjEL z2NcZE_Z?&(GvI}>>#|-K;OPNav)2=^M8TXMFs{99Mv{zK270C#hJjIp#|?L71TVGZ z{@bdLd_)bWbXrF@nB?0!XEh&t?DuMF4zSLR#h=w`)bT*af~MxQuH&D$xBB>IH_xbn z#%%Lk0?TEwQ&5IRd;+Ued;wd*t@y9dJk4D??}(l_=$!dwGisa+H64MT>7R5i;pjwE t-g5HlNE2~b;5DUDtJ);4#8@Tut#a_YF#d+zJ2y literal 0 HcmV?d00001 diff --git a/stella/src/ui/win32/stella.ini b/stella/src/ui/win32/stella.ini new file mode 100644 index 000000000..1ce79b463 --- /dev/null +++ b/stella/src/ui/win32/stella.ini @@ -0,0 +1,49 @@ +[Options] + +; ---------------------------------------------------------------------------- +; Set this to the path where your .bin rom files are located +; If this isn't specified, ROMS will be used + +RomPath=ROMS + +; ---------------------------------------------------------------------------- +; Set this to specify which paddle the mouse should emulate. This can be any +; number from 0 to 3 inclusive. +; If this isn't specified, 0 will be used (the mouse will emulate paddle 0) + +Paddle=0 + +; ---------------------------------------------------------------------------- +; Set this to 1 if you don't want any sound effects. +; If this isn't specified, 0 will be used + +Mute=0 + +; ---------------------------------------------------------------------------- +; Set this to 1 to have StellaX not use any joysticks. This may speed up +; StellaX, but you won't be able to play using joystick! + +DisableJoystick=0 + +; ============================================================================ +; ADVANCED OPTIONS FOLLOW +; ============================================================================ + +; ---------------------------------------------------------------------------- +; Set this to 1 if you want to see the FPS count after a game is played +; If this isn't specified, 0 will be used + +ShowFps=0 + +; ---------------------------------------------------------------------------- +; Set this to the desired frame rate. This normally should not be changed. +; Any value from 1 to 300 is allowed. +; If this isn't specified, 60 will be used + +FrameRate=60 + +; ---------------------------------------------------------------------------- +; Set this to 0 if you want stella to always use 640x480 as the video mode. +; If this isn't specified, 1 will be used + +AutoSelectVideoMode=1 diff --git a/stella/src/ui/win32/stella.rc b/stella/src/ui/win32/stella.rc new file mode 100644 index 000000000..634de86dc --- /dev/null +++ b/stella/src/ui/win32/stella.rc @@ -0,0 +1,353 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "winresrc.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""winresrc.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""resource.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,1,3,1 + PRODUCTVERSION 1,1,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x29L +#else + FILEFLAGS 0x28L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "This version of StellaX is not to be sold without written permission of the author\0" + VALUE "CompanyName", "Jeff Miller (miller@zipcon.net)\0" + VALUE "FileDescription", "StellaX\0" + VALUE "FileVersion", "1, 1, 3, 1\0" + VALUE "InternalName", "StellaX\0" + VALUE "LegalCopyright", "Copyright (C) 1995-2000 Jeff Miller\0" + VALUE "LegalTrademarks", "n/a\0" + VALUE "OriginalFilename", "Stella.exe\0" + VALUE "PrivateBuild", "n/a\0" + VALUE "ProductName", "StellaX\0" + VALUE "ProductVersion", "1, 1, 0, 0\0" + VALUE "SpecialBuild", "n/a\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOG DISCARDABLE 0, 0, 409, 249 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "StellaX" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "&Files found in:",-1,"Static",SS_LEFTNOWORDWRAP | + WS_GROUP,7,45,44,8 + CONTROL "List1",IDC_ROMLIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_AUTOARRANGE | + WS_BORDER | WS_TABSTOP,7,57,395,143,WS_EX_STATICEDGE + CONTROL "&Play",IDC_PLAY,"Button",BS_OWNERDRAW | WS_TABSTOP,7, + 226,200,16 + CONTROL "&Options",IDC_CONFIG,"Button",BS_OWNERDRAW | WS_TABSTOP, + 230,226,54,16 + CONTROL "&Help",IDC_ABOUT,"Button",BS_OWNERDRAW | WS_TABSTOP,289, + 226,54,16 + CONTROL "E&xit",IDC_EXIT,"Button",BS_OWNERDRAW | WS_TABSTOP,348, + 226,54,16 + CTEXT "Written by Jeff Miller (miller@zipcon.net)\nStella core by Bradford W. Mott (bwmott@acm.org)", + IDC_STATIC,175,13,227,19,SS_NOPREFIX + RTEXT "",IDC_ROMCOUNT,322,45,80,8,SS_NOPREFIX + CONTROL "",-1,"Static",SS_ETCHEDHORZ,0,39,411,1 + CONTROL "",-1,"Static",SS_ETCHEDHORZ,0,218,411,1 + CONTROL "StellaX v1.1.3a",IDC_TITLE,"Button",BS_OWNERDRAW | + WS_DISABLED | WS_TABSTOP,0,0,167,34 + CONTROL "Static",IDC_ROMPATH,"Static",SS_LEFTNOWORDWRAP | + SS_NOPREFIX | WS_GROUP,57,45,254,8 + CONTROL "",IDC_ROMNOTE,"Static",SS_LEFTNOWORDWRAP | SS_NOPREFIX | + WS_GROUP,93,205,309,8 + LTEXT "Game notes (if available):",-1,7,205,80,8 +END + +IDD_DOC_PAGE DIALOG DISCARDABLE 0, 0, 390, 196 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Documentation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "To view the online documentation, you need to first install Adobe Acrobat Reader 4.0 software. Once you have done this, return to this page and you will be able to read the documentation.", + IDC_INSTRUCTIONS,7,7,376,21,NOT WS_VISIBLE + LTEXT "Click here to download the Adobe Acrobat Reader software", + IDC_ADOBE,7,35,376,10,SS_NOTIFY | NOT WS_VISIBLE +END + +IDD_ABOUT_PAGE DIALOG DISCARDABLE 0, 0, 390, 196 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Information" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "You must own legal copies of all ROM images you are using. Neither Jeff Miller nor the rest of the Stella team can tell you where to find ROM images so DON'T ASK. All requests will either be deleted or sent to the appropriate authorities.", + -1,7,7,376,18,SS_NOPREFIX + LTEXT "If you have a question or a problem, please try one of these contacts:", + -1,7,93,376,8,SS_NOPREFIX + LTEXT "Jeff Miller:",-1,7,109,32,8,SS_NOPREFIX + LTEXT "miller@zipcon.net",IDC_EMAIL_JEFFMILL,73,109,56,8, + SS_NOPREFIX | SS_NOTIFY + LTEXT "http://www.emuunlim.com/stellax/",IDC_WEB_JEFFMILL,178, + 109,110,8,SS_NOPREFIX | SS_NOTIFY + LTEXT "Stella dev team:",-1,7,121,52,8,SS_NOPREFIX + LTEXT "stella@csc.ncsu.edu",IDC_EMAIL_STELLA,73,121,67,8, + SS_NOPREFIX | SS_NOTIFY + LTEXT "http://stella.atari.org",IDC_WEB_STELLA,178,121,65,8, + SS_NOPREFIX | SS_NOTIFY + LTEXT "God, Suzi and Noah",-1,60,155,69,8,SS_NOPREFIX + LTEXT "Bradford Mott, Mike Balfour and Roger Onslow.",-1,60, + 168,287,8,SS_NOPREFIX + LTEXT "StellaX is free software. It may not be sold or offered as part of a collection for sale without permission from Jeff Miller. When distributing StellaX, the original distribution (the .zip file) must not be altered.", + -1,7,32,376,19,SS_NOPREFIX + CONTROL "",-1,"Static",SS_ETCHEDHORZ,7,143,376,1 + LTEXT "Beta Tester:",-1,7,181,44,8,SS_NOPREFIX + LTEXT "John Hardy IV",IDC_WWW_MAME,60,181,46,8,SS_NOPREFIX | + SS_NOTIFY + CONTROL "",-1,"Static",SS_ETCHEDHORZ,7,81,376,1 + LTEXT "TIPS: F12 will create a screen shot of the current game screen. Open up stella.ini in notepad to see configurable options.", + -1,7,56,376,18 + LTEXT "Thanks To:",-1,7,168,38,8 + LTEXT "Dedicated To:",-1,7,155,46,8 +END + +IDD_CONFIG_PAGE DIALOG DISCARDABLE 0, 0, 390, 196 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Options" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&ROM (.bin) file path:",-1,7,10,64,8 + EDITTEXT IDC_ROMPATH,82,7,246,14,ES_AUTOHSCROLL + PUSHBUTTON "&Browse...",IDC_BROWSE,333,7,50,14 + LTEXT "Set this to the path where your .bin rom files are located.", + -1,7,25,376,8 + CONTROL "",-1,"Static",SS_ETCHEDHORZ,7,39,376,1 + LTEXT "Mouse should emulate &paddle:",-1,7,49,97,8 + COMBOBOX IDC_PADDLE,109,46,48,82,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Set this to specify which paddle the mouse should emulate. This can be any number from 0 to 3 inclusive.", + -1,7,65,333,8 + CONTROL "",-1,"Static",SS_ETCHEDHORZ,8,82,375,1 + CONTROL "&Auto select video mode",IDC_AUTO_SELECT_VIDEOMODE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,90,90,10 + LTEXT "If this is not checked, then a standard video mode (640x480) will be used. Otherwise, StellaX will use the video mode which it believes will work best. You should only uncheck this if you are having video problems while running StellaX.", + -1,7,105,376,18 + CONTROL "",-1,"Static",SS_ETCHEDHORZ,7,129,374,1 + CONTROL "Disable &Sound",IDC_SOUND,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,141,62,10 + CONTROL "Disable &Joysticks",IDC_JOYSTICK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,155,70,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 402 + TOPMARGIN, 7 + BOTTOMMARGIN, 242 + END + + IDD_DOC_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 383 + TOPMARGIN, 7 + BOTTOMMARGIN, 189 + END + + IDD_ABOUT_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 383 + TOPMARGIN, 7 + BOTTOMMARGIN, 189 + END + + IDD_CONFIG_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 383 + TOPMARGIN, 7 + BOTTOMMARGIN, 189 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP ICON DISCARDABLE "STELLA.ICO" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TILE BITMAP DISCARDABLE "tile.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_STATUSTEXT "%d files found" + IDS_STELLA "StellaX" + IDS_NODIRECTINPUT "DirectInput could not be initialized. Be sure you have DirectX 5.0 or later installed." + IDS_CANTSTARTCONSOLE "Error starting console. You may have chosen an invalid ROM file." + IDS_COINIT_FAILED "Unable to initialize COM subsystem" + IDS_ASS_FAILED "Unable to initialize audio subsystem" + IDS_PAS_FAILED "Unable to create audio stream" + IDS_DD_INIT_FAILED "Unable to initialize DirectDraw object" + IDS_DD_ENUMMODES_FAILED "Unable to enumerate display modes" + IDS_NO_VID_MODE "No compatible video modes were found" + IDS_DI_INIT_FAILED "Unable to initialize DirectInput object" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_ALREADYRUNNING "StellaX is already running!" + IDS_BADARGUMENT "Unknown argument given on command line" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DDSDM_FAILED "Unable to set video mode. Your video adapter might be incompatible with stella." + IDS_DSCSBFAILED "IDirectSound::CreateSoundBuffer failed" + IDS_DSSCLFAILED "IDirectSound::SetCooperativeLevel failed" + IDS_FILEFILTER "Stella Files (*.bin)|*.bin|All Files (*.*)|*.*||" + IDS_FILENAME "Filename" + IDS_MANUFACTURER "Manufacturer" + IDS_MIKE """I've heard some folks discussing the possibility of writing an 2600 emulator in a high level language - forget it, not possible."" -- Mike Livesay on rec.games.video.classic" + IDS_NAME "Name" + IDS_NODIRECTDRAW "DirectDraw does not appear to be installed on this system!" + IDS_RARITY "Rarity" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CW_FAILED "CreateWindow failed" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DDSCL_FAILED "IDirectDraw::SetCooperativeLevel failed" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DDCS_FAILED "IDirectDraw::CreateSurface failed" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DDCP_FAILED "IDirectDraw::CreatePalette failed" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_ROM_LOAD_FAILED "Unable to load ROM image\n\nCurrent Directory: %s\nPath: %s\nError code: %d - %s" + IDS_NO_ITEM_SELECTED "Before pressing play you must first select a game from the list!" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/stella/src/ui/win32/tile.bmp b/stella/src/ui/win32/tile.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ec6faadf9c04d772a30d2a834cd336ec1b9cd7ba GIT binary patch literal 27318 zcmaK!eXy=qS>Bg3Rgvj*WTtZrbI$f4shh)$e;5TyFu;(OjHPgtkD*O0NOBruNHh_t zkq{uFNI%quj~GBHK>`&hgJ}h+Oe6t=HMBut1|(`421tOmAjJ^rho{%?y6$^D>)q$j z_3rn1*0a|AaoyK_uk}3dyZ1TgKJC5FA8wEN8)F<(?~Qujr1v+C2YLM3AM{z{p5cd zZ}_VE_un`E`hky*cm3s;$A|vgm&fbB^p)}1FMWLc+<*UHAa`KfQZ=`cM97y!=yN z8n=An?(u)`y=mO>vAf3wfAaP5vmg22s!XdKl%0Xu21~IxbBYo z#*6Rx{P-`QylMQ_hwmJ}@w@kpXWw@Jc&qw<@tGHmSAXP=@yT01INtT)4~&=p?q|mp z|M83CYoGp+@y7Rmcs%>v4~)O_J6{{Gc;D^g*Z<(x#^-K%&G?D;-8LTg_OFcV-t@U~ z{yXm(XT1HZRSG@M~<5{n}cf8?^w~qZ^{pz^v zO}{s8{JqP^7jOFcanE~Cj5odY*TxfG{psz0ZkN(^T$M?PRL*uUB{+{uW7k_r_Uvt+u@7mkPdDp#py#5uh z8$bPuw~j}>`4`6PZ@7H?*;W5~{Q5i28F#(*hsXE7^Eu$IOF)o$Ah1D^SJQ2uNsfN==$-!S6?!| z>%w0h54!LM&An&*!t;K7{Lw{E9>0Z^Mdi9CtN>%;Ji!6cc1r@8vCVj_ERnxXPouw@y+L5 zF;1NSh;hyj{ouIj`!5|&d-!$Zp-=sxasA)_p7HQUUopP*(U*@izVp@NoI|e~Pk-3u z4}aV9#}oISH2&tdUp^l4P@Vtw(edry z{D|?4vmZHL`s~LG*SX`uXFqv7<;g!d9&_$f#?M@Ed|Y$!@p0)D&m8~wr_LWQJpZ}l z`7gU@{L2@g7_Yqa;_)LVE*V$8=d2;-~@#l_n&i`?(za8s*#ia(W0iOH|u+hzkA-(bv4G}BZrP0K78o#5x2d){k^>-d+r}P zbm;KmBfO66Y4C`ycUm;+C<}*=9Nl+2bN@`8HNkspM~@uQj}H6$NB8$E6R|@_4ix}9 z-m{+k9zG&`05~ka0Ng*aCk;Wg6>AxuW*FK*cj%BtW;?7VDYe2#cYp7g9ZsphLk=l| zz%C4}#^j&C4vRPNF)x4`5q01*!2LZL(>Uy6|0pW|Uw{^Bf-s{dM|ol5$T7VW#oiW$ z#&+RpAOO_~f3Pe5Y?}o0(ht@MA^>%@1JKv=unJ`m8@3dX8a%3({fNFU1qig?X~~^9 zH$E%_&nz(+vr_Eb%o$Bi25rWNKx?5s&v}PCE0K&G5j-{1tAjHWOO-2OP_`<+0ygW< zQjR*g@%8SZ0BR6AAlKYSh)8$Ebrt@UTc%v+AxtjZ_CV-lb)Hoclx5b3`NL})&JvO8 zT*y7+voI{efwzIG-Z59WupnV8@)`53SDf105#l4kb zv~(0e9Z3>8fHMfaiM^XGoL*)25)*abW0@UxnBJlhQU4I=3rmMzzto1mk8Aa@Px! zjHxjor&CMRk`diyeTzGC6ya%r;`V-L-PB*;B2rZNJ}l4Roto+pBGJ&MKLu|dJs=hQ zLy3GgK54kVogMj*RlRZtw$oUYdA0+;tXlk~kA2MRg?;i|@Cu_=EQ=QYRCpmq*A){G zQc{{-;9(s19DtUF?Rv@)f6Pl_0TxI*_-sWGoiS+}prM%_8Q7nuG*aRW$G+=F-sKJq z_^XC=el#Ml(^es@o(G#PDt=6#V@7q~RR6Y<{=U#Legv=VG@Yekoa z3StG76FRu@K|^x2+}U(VWLRUZ9Uyy!r#at$D;>JrGN$YrAU@P|$W1bFYc6+R0faq; zijhbM)>{bb^(FD-f+#(*Ar|W19ikBfWErF?a#=IKi_u=mP~wjFS~0Xk`=D5db;C zE+S44{xt7XLGY5Lk1Row-(r^QG{K;JWyHr&p-XOV`}=t$2cY7|ppR2q19>r0Zls0f z9f0ddp{uxjW!bD>GYhzWJAqIYn5~Tq04CA3xe7~pmc=bc8ZLO&k}b|O#MSD$uFAz` zz=3gqKXPE2S#Ht@GSCrs!?T3uFs=j(pS{`=)NJ6IfT~1^O?Z;3oECouJ6+5U2AQJ% zY}j=`W~?CU3;;BMR>K*MuPDIxg$OXO*EHN6avUTsTdUeunQ@##i&=@faVo(n4}WeN zS*BS5WRNH|9D1Xa{05zi@O1FJI*Re!#G>3$@(_0laNkLinD0oHfMXRF7_52B5=CO4QYdd{J7>KKv1G zsZqI61D@HDt=gk_C{qjSJWpr^1t1g7aU5|qdn-QVW-A7{PEC^SymlF}MsO|QgrxLh zQ8x-Usr(<*nM4URt+xO2=r$38{*3}8z5E! z3wW52vqC4SG8Fk7!$FOZM5!cH_kl|@yt!U<9f%5)KL=(!Y1t{27^(f>^rTWvqg&_jX;4Fq75i#@7^8X6?o{ikl*PQ2AwZ2g7Wf3v$UA;4#A*PhKaZL{wp)S3 zzBatuCxe?_dORW^Kow6jQ^-bz(_L2Ik+${hP^JL;ip*MeVHj`2SxoyO75VW&`Q-lG@&G4X?9fj?sw5M9&CP(vE zugJATmUZCqX`lMbs;~>J##LEDRm$ia)3l=w8f?YAxn;#fUxvu?4ts`W1CGbvk;pfu zbKk(G)ty;x73R-?5Ta8|sE3|M0#NXBZ4W+pT5<-c5621AQcWqkX)xXmsjyxus~asOewkBSf>xLZtILU&ZCa6rwiVUxgL}<4Zy#HK^Ua?mEag|>v;;GE%WQJV^zeoJCOAPT3~ zD_YFu9s z{tOIc{7oo4qLPOV!r=<-bjT7l*|-1s%hsd%)lw>IKCZW0l-Zoa_*REbRdiS+#sNT~ z3#Q3mv8Qoo7(}K7cui;Ur3Sr(klKn|;CS&15&y3ZbYdu&zHgC}R>vj|<;& z*FYyBVrJqI74FYG&zZTsD`0@B6@3a$kQf6l1?m{>bgJ7|@c}*%izJsAa|ig79?yUN zG>@m*ScZggDnROUin+7hzmm`_764+!B`CSD=_?QcOZWr~{u~5@=E!DTZrEpdJ=1p) zLY0#w`3dP%cnT4f+4S8817z5tWQ$4R#2e3}cE&K_Nb zD({AA>3NXL2%@3BC)y73o+ZJW@$^uS5(`<8;7gTyfi$l z=YEHD(&e!RK$W7H273h5`21)gylQh(SojmYZjBQ$y|Y+5+zu9xw(h*ioCE|&m@)0V ziFsUkBmk4+Lg1;uYkIX_P}ggwA=Op&#{x)}sXrTcP666MC#Uh3%CVf`>26R|0+6k2 zhE=72Y}JAcN>~7^X+~eA0#2_Skai0`wmn%5e_8vQ4bBN~+LKw*IVU74zOt1ye>um; z7NL_D=E9(K2i$uwK2dPnnT3I1!RObC)!uzBy@z*A0@(oG?fPImXE-Jlrh z32D^dPi{Md#uRhB4iN>dHCaxdef_orLW42Pn7qpeZw=r6}`=vxN-863;PM8&8s1ImONDnnI& zK$f)AA%n$5v1E8W8meK1FpL)j0cd#Y`uWIK{Amb{+4%H?0g6O}b~z`j1Bk7N;4$@= zp7kU-W5;<2G&rn^J(mjr8AL*f<3v-{RhWi~F}-O>!IMbOm>;uLAkfD^fJ=$oD0OdX47}#al@X%vGx~;KhWZ|$;>3>OV6Pd z)Ivvc-N?!s#bVe1%dvbTE^P=X|6OSyqX-(G4g0WJv)@`6nzA)1PzGHj>XQFj-`sU^ zMP0WU=<5FF1ap;%jd=vE3jdl8!0=bXI~H~?5+JP8YBO+O2t&0#`Kpp6fERZxdJ4St zUy@PfJkb%qUsg2%K2(00a^#plePf~%2So|cvITHHS9mZ1bf>zb4o_$#tC19~SCdpphUx~?WX%hrcJR)E?>M2_=0O{2xIH*U#UGPzJ0c^S61 za!>dWQu)oSc_FH`Xb4hcK1qk!@aHPjT-6^t4Xt*I>{r6Y!$UWT&dsEP1u|dxL^+&d z#yI==z+jQUb*DfaB*XYW^9wr4b$!<*7d8MG<{?nd>siSV zd<$g)O+tF^ZL~+As`e7oc|%fjGwBehM2mHlRt$sc3+yF@3vLSxSHs!;4$ut0K{~ zU`iHzc#L2JBm{`}-Bq6Ee&*Zg%3xMO$AO+2)oPHTU1oqM0*l_szGRt*D~<(^g&Kgy zm%ak9b=hYCMN(#eHIb6UcePptNovt+1w?i!#GHuEFC(HZNr9@brP7Hb!vkRW>ttsV z=jsKXjx`D`8uZnKx7;bD6yA_f544iwgsEz-(ah+mX2w?P7=%5;Gd>k7gIjBOT5p)u zM0sXEV}o$|%J3opK8yxg1>q~jxmV9l z8WzW{QoKnP7Ev>i7<98+MunIVO}z8pSIB5hqP2Ca za>DCUQ$SdFk227(>whliLggTclBUU+`Z1|XMNCt#J_x$6OAj-nkUPIUr|F#r2j6EOAqkf}*!6bbkZrVeoOADO-GVp>YB0k!G-;pK%MoQYc|xyTJX_flC4gBCgp( zbWu7Ij#Vp%I(L;|;Bj-URUjJ8UwdnK8%3w$6SSI6EZ_)Wf;AhY$?%#(yEhA(F>>q% zl#jh=o!EZNW`_PoMzJUVjd+$|KR*|Ln{gGd>>%$9>*OjT!J8Fa(F|PSz~JUqFuGww zMOOD=VOxq)<>%+s1)j3q49@zKg25_EnlGNu<$UMLSXPmQ~oCKD&Y>9Ersr4s( z<7J?>JGz=h`#@9740~A@KHwM{D0h?u&(FN&P3UCRLO$XH^9rxBE0DINf!mVOKdu7z(=yk1MuZYKjp_Qw|&)rmJIm zIt8Aq2Yh8;4Dt}*q-jX$fVRm&ieeS! zxT0~dArbsQVByP9IcR`HUu9?ALgW7K0od{~Gb}|vdi&0%3-jkk8S^JY#Uo}a6=f0U zhLlcVu8@z0H_}T(lIPa+t>~+UyxxFNIY3( z;PdA15s$Oeg3JH~rQl6~^5eP;023kW`pF{+2#G81F|@5qYPx3@1}nlOOA6u70EKVD z19K2!)gQOvA>U2Z-z&%ze5i$>#WZDP<~2FX0zstRkN{vBF5HM*8vK}S(NjSB`|;wf zGaPGKeCDryH7o-*1q=|BeVpXkGn*)DvS$TCL@8_XtzAIy)br|hG@_pn(@Wf`z7lb9!aP`{EzTvML+cPro zq`HS# zF3~JaNZeA%dJep$XeL1TlNmeG(FSzXHH@R-%Ah|1YdFs>h^ZyK^mq7U0@E>%fbGkC z_JpV~w($qDB8zDm;6X2=x&}vDmRb6~eZymAI(tM*Lfs5_;g8tSpL6Pyf{nYHVX)X> zJ0Yq`nYwKOYx=_k<1bUwy30aWRRU(hmj@3UJ{Cuq|ucojk(PsSyOTmjnJ4LQ^0^N#uNNLJniZ;uE*_MMh`hgdG zn3Cj-0C~YywHO>p;U%D317BU0=?$O9BiwE^LYE|@`3pA_&S!A$ zv6`~^2H49RkeTq&f1k5lgQjdoKg2H*a0Z6ti+Y3p6vM<8T}^G-m%LgGS)*(Um;&Tdd;!6w& z6~M*J(v=@w5lIyahzWOo*&=kl?<@W~$(hY|zad{}xO zAaKhR!>&Qk&jfGE^oYoRV-zpi6n%&Dvn4f^w(wUv*qrlaI|8RKNyBAE@KHhdE5eC?jJVJxWCdWn%ik6v-hZv?Rpjpe_8HrVsjE% zdF`Q48GwQ3Z4WsQD*UE|@8sy`{}022Tf+-|rV-B6*c{#QgNS%2OY*sGM~;XClbR_g z*4qw@5@mK3Xmijp-(uxE2k$ZYMYs~96b5o?2(Znf@Q?zrJ!&dPpC4tt~Wd5>#3ZG9rY6-YdQ(Y@S z16t^-)G%EC{#>dngd)t2WmEDA5-B%ZXjvOehHDu+T=ETr|qGjr5S#gkRg=Mzikm_-4_#~u&Z4LKvbRuo9B{( zCmMkVK5D@x;%WntV9Ma`-xYt57a3+_j+Qt_a;lkX+j2nL8age-kcT&x0d!fLx_T&gY0D)F^}jEn;ng0T4GTKq4(2lyc(A(>>5-;uvi7um#u+o*#}gYzVb;Xaso4wbSm+oBMJW zASz44g})!iH0kW7y+}IX6Fd_r=-j}8vo!QfAa=~P9rZe}xz1iLl71HL){OoDbCw2P z@u#z01YtcVR4$p4I)%GnOP2u377@A*@SG2fv9=zwFKUFeHbh;@K3G;!2*R`Wm;VPS CAM#HC literal 0 HcmV?d00001