From 6b644925cca20843c6cd74956e95400b5130f3ad Mon Sep 17 00:00:00 2001 From: stephena Date: Sat, 10 Jul 2004 22:25:58 +0000 Subject: [PATCH] StellaX now loads metadata from the stella.pro file (Filename, Manufacturer, Rarity, Note, etc). Since the loading of the metadata is slow, I added a cachefile for the gamelist (named 'stellax.cache'). If this file is present, the gamelist is created based on the contents of the cachefile. This is quite fast, since no stella.pro parsing or ROM loading is done. The cache file is recreated each time a full reload is done. Pressing the 'Reload' button always forces a full reload (not from cache), so this is useful if you change the ROM directory (or add ROMs). Still TODO is add a check to see if the ROM dir has had any files added to it since last time you ran StellaX, and do a full reload. Got rid of the filename field in the game listview. Now only show 'Cartridge Name', 'Cartridge Manufacturer' and 'Cartridge Rarity'. If some game doesn't have an entry in the stella.pro file, its filename will be shown as cartridge name. Still TODO are add sorting/permanent resizing options to the column headers in the listview, and add options to the Config Page. It's getting close, folks. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@300 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- stella/src/build/makefile | 4 +- stella/src/win32/StellaX/Game.cxx | 35 ++ stella/src/win32/StellaX/Game.hxx | 76 +++ stella/src/win32/StellaX/GlobalData.cxx | 7 +- stella/src/win32/StellaX/GlobalData.hxx | 13 +- stella/src/win32/StellaX/MainDlg.cxx | 571 ++++++++++------------- stella/src/win32/StellaX/MainDlg.hxx | 227 ++------- stella/src/win32/StellaX/StellaX.aps | Bin 39668 -> 39668 bytes stella/src/win32/StellaX/StellaX.rc | 2 +- stella/src/win32/StellaX/StellaX.vcproj | 28 +- stella/src/win32/StellaX/StellaXMain.cxx | 11 +- stella/src/win32/StellaX/StellaXMain.hxx | 6 +- stella/src/win32/StellaX/main.cxx | 4 +- stella/src/win32/StellaX/pch.hxx | 35 +- stella/src/win32/Stella_Emulator.ncb | Bin 510976 -> 510976 bytes stella/src/win32/Stella_Emulator.suo | Bin 11776 -> 11776 bytes 16 files changed, 453 insertions(+), 566 deletions(-) create mode 100755 stella/src/win32/StellaX/Game.cxx create mode 100755 stella/src/win32/StellaX/Game.hxx diff --git a/stella/src/build/makefile b/stella/src/build/makefile index 660b29ea7..c84b3aa50 100644 --- a/stella/src/build/makefile +++ b/stella/src/build/makefile @@ -13,7 +13,7 @@ ## See the file "license" for information on usage and redistribution of ## this file, and for a DISCLAIMER OF ALL WARRANTIES. ## -## $Id: makefile,v 1.54 2004-06-28 02:06:21 stephena Exp $ +## $Id: makefile,v 1.55 2004-07-10 22:25:57 stephena Exp $ ##============================================================================ ##============================================================================ @@ -25,7 +25,7 @@ ### add your own compiler optimizations here ### if none are provided, the defaults will be used -OPTIMIZATIONS = +OPTIMIZATIONS = -O2 -march=i586 ### to include joystick support JOYSTICK_SUPPORT = 1 diff --git a/stella/src/win32/StellaX/Game.cxx b/stella/src/win32/StellaX/Game.cxx new file mode 100755 index 000000000..f6b553a86 --- /dev/null +++ b/stella/src/win32/StellaX/Game.cxx @@ -0,0 +1,35 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 2004 by Stephen Anthony +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Game.cxx,v 1.1 2004-07-10 22:25:58 stephena Exp $ +//============================================================================ + +#include "Game.hxx" + +Game::Game( void ) +{ + _available = false; + _rom = " "; + _md5 = " "; + _name = " "; + _rarity = " "; + _manufacturer = " "; + _note = " "; +} + + +Game::~Game( void ) +{ +} diff --git a/stella/src/win32/StellaX/Game.hxx b/stella/src/win32/StellaX/Game.hxx new file mode 100755 index 000000000..bdeddaa07 --- /dev/null +++ b/stella/src/win32/StellaX/Game.hxx @@ -0,0 +1,76 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 2004 by Stephen Anthony +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Game.hxx,v 1.1 2004-07-10 22:25:58 stephena Exp $ +//============================================================================ + +#ifndef GAME_H +#define GAME_H + +#include "bspf.hxx" + +/** Hold game info for an Atari2600 game. */ +class Game +{ + public: + Game( void ); + ~Game( void ); + + /** sets the rom of a game */ + void setAvailable( bool available ) { _available = available; } + /** returns the rom of a game */ + bool isAvailable( void ) { return _available; } + + /** sets the rom of a game */ + void setRom( const string& rom ) { _rom = rom; }; + /** returns the rom of a game */ + string rom( void ) const { return _rom; } + + /** sets the md5 sum of a game */ + void setMd5( const string& md5 ) { _md5 = md5; }; + /** returns the md5 sum of a game */ + string md5( void ) const { return _md5; } + + /** sets the name of a game */ + void setName( const string& name ) { _name = name; } + /** returns the name of a game */ + string name( void ) const { return _name; } + + /** sets the rarity of a game */ + void setRarity( const string& rarity ) { _rarity = rarity; } + /** returns the rarity of a game */ + string rarity( void ) const { return _rarity; } + + /** sets the manufacturer of a game */ + void setManufacturer( const string& manufacturer) { _manufacturer = manufacturer; } + /** returns the manufacturer of a game */ + string manufacturer( void ) const { return _manufacturer; } + + /** sets the note of a game */ + void setNote( const string& note ) { _note = note; } + /** returns the note of a game */ + string note( void ) const { return _note; } + + protected: + bool _available; + string _rom; + string _md5; + string _name; + string _rarity; + string _manufacturer; + string _note; +}; + +#endif diff --git a/stella/src/win32/StellaX/GlobalData.cxx b/stella/src/win32/StellaX/GlobalData.cxx index a559706e0..71b9a9181 100644 --- a/stella/src/win32/StellaX/GlobalData.cxx +++ b/stella/src/win32/StellaX/GlobalData.cxx @@ -14,21 +14,22 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: GlobalData.cxx,v 1.2 2004-07-04 20:16:03 stephena Exp $ +// $Id: GlobalData.cxx,v 1.3 2004-07-10 22:25:58 stephena Exp $ //============================================================================ #include "pch.hxx" #include "resource.h" +#include "bspf.hxx" #include "Settings.hxx" #include "SettingsWin32.hxx" #include "GlobalData.hxx" CGlobalData::CGlobalData( HINSTANCE hInstance ) - : myInstance(hInstance) + : mySettings(0), + myInstance(hInstance) { - myPathName[0] = _T('\0'); mySettings = new SettingsWin32(); mySettings->loadConfig(); } diff --git a/stella/src/win32/StellaX/GlobalData.hxx b/stella/src/win32/StellaX/GlobalData.hxx index 2ea3cc832..5803d7ba1 100644 --- a/stella/src/win32/StellaX/GlobalData.hxx +++ b/stella/src/win32/StellaX/GlobalData.hxx @@ -14,7 +14,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: GlobalData.hxx,v 1.1 2004-06-28 23:13:54 stephena Exp $ +// $Id: GlobalData.hxx,v 1.2 2004-07-10 22:25:58 stephena Exp $ //============================================================================ #ifndef GLOBAL_DATA_HXX @@ -34,14 +34,6 @@ class CGlobalData CGlobalData( HINSTANCE hInstance ); ~CGlobalData( void ); - LPCTSTR PathName( void ) const - { - if ( myPathName[0] == _T('\0') ) - return NULL; - - return myPathName; - } - HINSTANCE ModuleInstance( void ) const { return myInstance; @@ -55,10 +47,7 @@ class CGlobalData private: Settings* mySettings; - string myArgumentList; - HINSTANCE myInstance; - TCHAR myPathName[ MAX_PATH ]; CGlobalData( const CGlobalData& ); // no implementation void operator=( const CGlobalData& ); // no implementation diff --git a/stella/src/win32/StellaX/MainDlg.cxx b/stella/src/win32/StellaX/MainDlg.cxx index 7ebf3356b..20fe8b95b 100644 --- a/stella/src/win32/StellaX/MainDlg.cxx +++ b/stella/src/win32/StellaX/MainDlg.cxx @@ -14,54 +14,33 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: MainDlg.cxx,v 1.3 2004-07-06 22:51:58 stephena Exp $ +// $Id: MainDlg.cxx,v 1.4 2004-07-10 22:25:58 stephena Exp $ //============================================================================ #include "pch.hxx" #include "MainDlg.hxx" +#include "Game.hxx" #include "GlobalData.hxx" #include "PropertySheet.hxx" #include "AboutPage.hxx" #include "ConfigPage.hxx" #include "resource.h" #include "Settings.hxx" +#include "PropsSet.hxx" +#include "MD5.hxx" #define BKGND_BITMAP_TOP 64 #define BKGND_BITMAP_BOTTOM 355 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -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 ) +MainDlg::MainDlg( CGlobalData& rGlobalData, HINSTANCE hInstance ) : myGlobalData(rGlobalData), m_hInstance(hInstance) { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CMainDlg::ClearList( void ) -{ - int nCount = ListView_GetItemCount( myHwndList ); - - for (int i = 0; i < nCount; ++i) - delete (CListData*)ListView_GetItemData( myHwndList, i ); - - ListView_DeleteAllItems( myHwndList ); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int CMainDlg::DoModal( HWND hwndParent ) +int MainDlg::DoModal( HWND hwndParent ) { return DialogBoxParam( m_hInstance, MAKEINTRESOURCE(IDD), @@ -72,20 +51,20 @@ int CMainDlg::DoModal( HWND hwndParent ) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BOOL CALLBACK -CMainDlg::StaticDialogFunc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +MainDlg::StaticDialogFunc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { - CMainDlg* pDlg; + MainDlg* pDlg; switch ( uMsg ) { case WM_INITDIALOG: - pDlg = reinterpret_cast( lParam ); + pDlg = reinterpret_cast( lParam ); pDlg->myHwnd = hDlg; (void)::SetWindowLong( hDlg, DWL_USER, reinterpret_cast( pDlg ) ); break; default: - pDlg = reinterpret_cast( ::GetWindowLong( hDlg, DWL_USER ) ); + pDlg = reinterpret_cast( ::GetWindowLong( hDlg, DWL_USER ) ); if ( pDlg == NULL ) return FALSE; break; @@ -96,7 +75,7 @@ CMainDlg::StaticDialogFunc( HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BOOL CALLBACK -CMainDlg::DialogFunc( UINT uMsg, WPARAM wParam, LPARAM lParam ) +MainDlg::DialogFunc( UINT uMsg, WPARAM wParam, LPARAM lParam ) { BOOL b; @@ -168,7 +147,7 @@ CMainDlg::DialogFunc( UINT uMsg, WPARAM wParam, LPARAM lParam ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BOOL CMainDlg::OnInitDialog( void ) +BOOL MainDlg::OnInitDialog( void ) { DWORD dwRet; @@ -181,9 +160,7 @@ BOOL CMainDlg::OnInitDialog( void ) // 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; @@ -218,18 +195,25 @@ BOOL CMainDlg::OnInitDialog( void ) RECT rc; ::GetClientRect( myHwndList, &rc ); - LONG lTotalWidth = rc.right-rc.left - GetSystemMetrics(SM_CXVSCROLL); - int cx = lTotalWidth / CListData::GetColumnCount(); + // Declare the column headings + int columnHeader[] = { IDS_NAME, IDS_MANUFACTURER, IDS_RARITY }; - for (int i = 0; i < CListData::GetColumnCount(); ++i) + // Set the column widths + LONG lTotalWidth = rc.right-rc.left - GetSystemMetrics(SM_CXVSCROLL); + int columnWidth[3]; + columnWidth[0] = (int) (0.58 * lTotalWidth); + columnWidth[1] = (int) (0.25 * lTotalWidth); + columnWidth[2] = lTotalWidth - columnWidth[0] - columnWidth[1]; + + // Set up the column headings + for (int i = 0; i < sizeof(columnHeader)/sizeof(int); ++i) { - ::LoadString( m_hInstance, CListData::GetColumnNameIdForColumn( i ), - psz, nMaxString ); + LoadString( m_hInstance, columnHeader[i], psz, nMaxString ); LV_COLUMN lvc; lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; lvc.fmt = LVCFMT_LEFT; - lvc.cx = cx; + lvc.cx = columnWidth[i]; lvc.pszText = psz; ListView_InsertColumn( myHwndList, i, &lvc ); } @@ -245,14 +229,14 @@ BOOL CMainDlg::OnInitDialog( void ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BOOL CMainDlg::OnCommand( int id, HWND hwndCtl, UINT codeNotify ) +BOOL MainDlg::OnCommand( int id, HWND hwndCtl, UINT codeNotify ) { UNUSED_ALWAYS( hwndCtl ); UNUSED_ALWAYS( codeNotify ); HWND hwnd = *this; - CListData* pListData; - + Game* g; + string romfile; int nItem; switch (id) @@ -263,28 +247,23 @@ BOOL CMainDlg::OnCommand( int id, HWND hwndCtl, UINT codeNotify ) ASSERT( nItem != -1 ); if ( nItem == -1 ) { - ::MessageBox( m_hInstance, hwnd, IDS_NO_ITEM_SELECTED ); + MessageBox( m_hInstance, hwnd, IDS_NO_ITEM_SELECTED ); return TRUE; } - pListData = (CListData*)ListView_GetItemData( myHwndList, nItem ); - - TCHAR pszPathName[ MAX_PATH + 1 ]; - lstrcpy( pszPathName, myGlobalData.settings().getString("romdir").c_str() ); - lstrcat( pszPathName, _T("\\") ); - lstrcat( pszPathName, pListData->GetTextForColumn( CListData::FILENAME_COLUMN ) ); - - (void)m_stella.PlayROM( pszPathName, myGlobalData ); + g = (Game*) ListView_GetItemData( myHwndList, nItem ); + romfile = myGlobalData.settings().getString("romdir") + "\\" + g->rom(); + (void)m_stella.PlayROM( romfile, myGlobalData ); // Set focus back to the rom list - ::SetFocus( myHwndList ); + SetFocus( myHwndList ); return TRUE; break; // case IDC_PLAY case IDC_EXIT: - ClearList(); - ::EndDialog( hwnd, IDCANCEL ); + ListView_Clear(); + EndDialog( hwnd, IDCANCEL ); return TRUE; break; // case IDC_EXIT @@ -315,7 +294,7 @@ BOOL CMainDlg::OnCommand( int id, HWND hwndCtl, UINT codeNotify ) case IDC_RELOAD: { - UpdateRomList(); + LoadRomListFromDisk(); return TRUE; break; // case IDC_RELOAD @@ -326,24 +305,20 @@ BOOL CMainDlg::OnCommand( int id, HWND hwndCtl, UINT codeNotify ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BOOL CMainDlg::OnNotify( int idCtrl, LPNMHDR pnmh ) +BOOL MainDlg::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 LVN_COLUMNCLICK: +// OnColumnClick( (LPNMLISTVIEW)pnmh ); +// return TRUE; + case NM_DBLCLK: // send out an ok click to play ::SendDlgItemMessage( *this, IDC_PLAY, BM_CLICK, 0, 0 ); @@ -354,6 +329,33 @@ BOOL CMainDlg::OnNotify( int idCtrl, LPNMHDR pnmh ) return FALSE; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void MainDlg::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; + } + + Game* g = (Game*)ListView_GetItemData( pnmv->hdr.hwndFrom, pnmv->iItem ); + SetWindowText( hwndNote, g->note().c_str() ); + InvalidateRect( hwnd, &rc, TRUE ); + EnableWindow( GetDlgItem( hwnd, IDC_PLAY ), TRUE ); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static void ScreenToClient( HWND hwnd, LPRECT lpRect ) { @@ -370,7 +372,7 @@ static void FillSolidRect( HDC hdc, LPCRECT lpRect, COLORREF clr ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -BOOL CMainDlg::OnEraseBkgnd( HDC hdc ) +BOOL MainDlg::OnEraseBkgnd( HDC hdc ) { // don't do this in 256 color @@ -419,7 +421,7 @@ BOOL CMainDlg::OnEraseBkgnd( HDC hdc ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -HBRUSH CMainDlg::OnCtlColorStatic( HDC hdcStatic, HWND hwndStatic ) +HBRUSH MainDlg::OnCtlColorStatic( HDC hdcStatic, HWND hwndStatic ) { // don't do this in 256 color @@ -435,21 +437,20 @@ HBRUSH CMainDlg::OnCtlColorStatic( HDC hdcStatic, HWND hwndStatic ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CMainDlg::UpdateRomList( void ) +void MainDlg::UpdateRomList( void ) { HWND hwndText; RECT rc; - DWORD dwError = PopulateRomList(); - if ( dwError != ERROR_SUCCESS ) - MessageBoxFromWinError( dwError, _T("PopulateRomList") ); + if ( !PopulateRomList() ) + MessageBoxFromWinError( 0, _T("PopulateRomList") ); // if items added, select first item and enable play button int nCount = ListView_GetItemCount( myHwndList ); if (nCount != 0) { myHeader.SetSortCol( 0, TRUE ); - ListView_SortItems( myHwndList, ListViewCompareFunc, (LPARAM)this ); +// ListView_SortItems( myHwndList, ListViewCompareFunc, (LPARAM)this ); ListView_SetItemState( myHwndList, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED ); } @@ -480,287 +481,200 @@ void CMainDlg::UpdateRomList( void ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -DWORD CMainDlg::PopulateRomList( void ) +bool MainDlg::PopulateRomList( void ) { - DWORD dwRet; - ClearList(); + bool result = false; + bool cacheFileExists = myGlobalData.settings().fileExists("stellax.cache"); + bool cacheIsStale = false; // FIXME - TCHAR pszPath[ MAX_PATH ]; + if(cacheFileExists && !cacheIsStale) + result = LoadRomListFromCache(); + else + result = LoadRomListFromDisk(); - lstrcpy( pszPath, myGlobalData.settings().getString("romdir").c_str() ); - lstrcat( pszPath, _T("\\*.bin") ); + return result; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool MainDlg::LoadRomListFromDisk() +{ + ListView_Clear(); + + // Get the location of the romfiles (*.bin) + string romdir = myGlobalData.settings().getString("romdir"); + romdir += "\\"; + string romfiles = romdir + "*.bin"; WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFile( pszPath, &ffd ); + HANDLE hFind = FindFirstFile( romfiles.c_str(), &ffd ); ListView_SetItemCount( myHwndList, 100 ); int iItem = 0; - BOOL fDone = (hFind == INVALID_HANDLE_VALUE); - while(!fDone) + bool done = (hFind == INVALID_HANDLE_VALUE); + while(!done) { - // 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; + Game* g = new Game(); + if( g == NULL ) + 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; + lvi.pszText = ""; + lvi.lParam = (LPARAM) g; int nItem = ListView_InsertItem( myHwndList, &lvi ); ASSERT( nItem != -1 ); - ListView_SetItemText( myHwndList, nItem, - CListData::FILENAME_COLUMN, LPSTR_TEXTCALLBACK ); - ListView_SetItemText(myHwndList, nItem, - CListData::MANUFACTURER_COLUMN, LPSTR_TEXTCALLBACK); - ListView_SetItemText( myHwndList, nItem, - CListData::RARITY_COLUMN, LPSTR_TEXTCALLBACK ); + g->setAvailable( true ); + g->setRom( ffd.cFileName ); + + // Set the name (shown onscreen) to be the rom + // It will be overwritten later if a name is found in the properties set + g->setName( ffd.cFileName ); // go to the next rom file - fDone = !FindNextFile(hFind, &ffd); + done = !FindNextFile(hFind, &ffd); } if( hFind != INVALID_HANDLE_VALUE ) VERIFY( ::FindClose( hFind ) ); - return ERROR_SUCCESS; -} + // Create a propertiesset object and load the properties + // We don't create the propsSet until we need it, since it's + // a time-consuming process + PropertiesSet propsSet; + string theUserProFile = myGlobalData.settings().userPropertiesFilename(); + string theSystemProFile = myGlobalData.settings().systemPropertiesFilename(); + if(theUserProFile != "") + propsSet.load(theUserProFile, true); + else if(theSystemProFile != "") + propsSet.load(theSystemProFile, true); + else + propsSet.load("", false); -DWORD CMainDlg::ReadRomData(CListData* pListData) const -{ -/* - // TODO: Move this method to ListData class (?) - if( pListData == NULL ) + // Now, rescan the items in the listview and update the onscreen + // text and game properties + // Also generate a cache file so the program will start faster next time + ofstream cache("stellax.cache"); + int count = ListView_GetItemCount( myHwndList ); + cache << count << endl; + for (int i = 0; i < count; ++i) { - ASSERT( FALSE ); - return ERROR_BAD_ARGUMENTS; + Game* g = (Game*) ListView_GetItemData(myHwndList, i); + + // Get the MD5sum for this rom + // Use it to lookup the rom in the properties set + string rom = romdir + g->rom(); + ifstream in(rom.c_str(), ios_base::binary); + if(!in) + return false; + uInt8* image = new uInt8[512 * 1024]; + in.read((char*)image, 512 * 1024); + uInt32 size = in.gcount(); + in.close(); + string md5 = MD5( image, size ); + delete[] image; + + // Get the properties for this rom + Properties props; + propsSet.getMD5( md5, props ); + + // For some braindead reason, the ListView_SetItemText won't + // allow std::string::c_str(), so we create C-strings instead + char name[256], manufacturer[256], rarity[256]; + strncpy(name, props.get("Cartridge.Name").c_str(), 255); + strncpy(manufacturer, props.get("Cartridge.Manufacturer").c_str(), 255); + strncpy(rarity, props.get("Cartridge.Rarity").c_str(), 255); + + // Make sure the onscreen 'Name' field isn't blank + if(props.get("Cartridge.Name") == "") + strncpy(name, g->name().c_str(), 255); + + // Update the current game + // To save memory, 'Note' is the only item that needs to be saved + g->setNote( props.get("Cartridge.Note") ); + + // Update the cachefile with this game + cache << g->rom() << endl + << md5 << endl + << name << endl + << rarity << endl + << manufacturer << endl + << g->note() << endl; + + // Finally, update the listview itself + ListView_SetItemText( myHwndList, i, 0, name ); + ListView_SetItemText( myHwndList, i, 1, manufacturer ); + ListView_SetItemText( myHwndList, i, 2, rarity ); } + cache.close(); + SetFocus( myHwndList ); - // attempt to read the rom file - TCHAR pszPath[MAX_PATH + 1]; - lstrcpy( pszPath, myGlobalData.settings().getString("romdir").c_str() ); - 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; + return true; } -void CMainDlg::OnColumnClick( LPNMLISTVIEW pnmv ) +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool MainDlg::LoadRomListFromCache() { - HCURSOR hcur = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); + ListView_Clear(); + char count[10], rom[256], md5[256], name[256], rarity[256], + manufacturer[256], note[256]; - myHeader.SetSortCol(pnmv->iSubItem, TRUE); - ListView_SortItems(pnmv->hdr.hwndFrom, ListViewCompareFunc, (LPARAM)this); + // We don't scan the roms at all; just load directly from the cache file + ifstream in("stellax.cache"); + if (!in) + return false; - // ensure the selected item is visible + in.getline(count, 9); + int numRoms = atoi(count); + ListView_SetItemCount( myHwndList, 100 ); - int nItem = ListView_GetNextItem( myHwndList, -1, LVNI_SELECTED ); - if (nItem != -1) - { - ListView_EnsureVisible( myHwndList, 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) + int iItem = 0; + for(int i = 0; i < numRoms; i++) { - SetWindowText( hwndNote, _T("") ); - EnableWindow( ::GetDlgItem( hwnd, IDC_PLAY ), FALSE ); - InvalidateRect( hwnd, &rc, TRUE ); - return; + string line; + Game* g = new Game(); + if( g == NULL ) + return false; + + // Get the data from the cache file + in.getline(rom, 255); + in.getline(md5, 255); + in.getline(name, 255); + in.getline(rarity, 255); + in.getline(manufacturer, 255); + in.getline(note, 255); + + // These are the only things we really need to save + g->setAvailable( true ); + g->setRom( rom ); + g->setNote( note ); + + LV_ITEM lvi; + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iItem = iItem++; + lvi.iSubItem = 0; + lvi.pszText = ""; + lvi.lParam = (LPARAM) g; + int nItem = ListView_InsertItem( myHwndList, &lvi ); + + ASSERT( nItem != -1 ); + + ListView_SetItemText( myHwndList, nItem, 0, name ); + ListView_SetItemText( myHwndList, nItem, 1, manufacturer ); + ListView_SetItemText( myHwndList, nItem, 2, rarity ); } + SetFocus( myHwndList ); - CListData* pListData = (CListData*)ListView_GetItemData( - pnmv->hdr.hwndFrom, - pnmv->iItem ); - - SetWindowText( hwndNote, pListData->GetNote() ); - InvalidateRect( hwnd, &rc, TRUE ); - EnableWindow( ::GetDlgItem( hwnd, IDC_PLAY ), TRUE ); + return 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) ); -} - -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->myHeader.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 MainDlg::OnDestroy( void ) { @@ -773,24 +687,51 @@ void CMainDlg::OnDestroy( } } -void CMainDlg::OnNcPaint( +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void MainDlg::OnNcPaint( HRGN hrgn ) { m_CoolCaption.OnNcPaint( hrgn ); } -void CMainDlg::OnNcActivate( +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void MainDlg::OnNcActivate( BOOL fActive ) { m_CoolCaption.OnNcActivate( fActive ); } -BOOL CMainDlg::OnNcLButtonDown( +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +BOOL MainDlg::OnNcLButtonDown( INT nHitTest, POINTS pts ) { return m_CoolCaption.OnNCLButtonDown( nHitTest, pts ); } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +LPARAM MainDlg::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; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void MainDlg::ListView_Clear( void ) +{ + int nCount = ListView_GetItemCount( myHwndList ); + + for (int i = 0; i < nCount; ++i) + delete (Game*) ListView_GetItemData( myHwndList, i ); + + ListView_DeleteAllItems( myHwndList ); +} diff --git a/stella/src/win32/StellaX/MainDlg.hxx b/stella/src/win32/StellaX/MainDlg.hxx index 00aa9fe6f..cf937d09c 100644 --- a/stella/src/win32/StellaX/MainDlg.hxx +++ b/stella/src/win32/StellaX/MainDlg.hxx @@ -1,10 +1,24 @@ -// -// StellaX -// Jeff Miller 05/10/2000 -// -#ifndef MAINDLG_H -#define MAINDLG_H -#pragma once +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2000 by Jeff Miller +// Copyright (c) 2004 by Stephen Anthony +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: MainDlg.hxx,v 1.3 2004-07-10 22:25:58 stephena Exp $ +//============================================================================ + +#ifndef __MAINDLG_H_ +#define __MAINDLG_H_ #include "resource.h" @@ -16,175 +30,19 @@ class CGlobalData; #include "HeaderCtrl.hxx" #include "RoundButton.hxx" -class CMainDlg; -class CListData +class MainDlg { - 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: - + public: enum { IDD = IDD_MAIN }; - CMainDlg( CGlobalData& rGlobalData, HINSTANCE hInstance ); + MainDlg( CGlobalData& rGlobalData, HINSTANCE hInstance ); virtual int DoModal( HWND hwndParent ); - operator HWND( void ) const - { - return myHwnd; - } - -private: + operator HWND( void ) const { return myHwnd; } + private: HWND myHwnd; CCoolCaption m_CoolCaption; @@ -196,66 +54,45 @@ private: CRoundButton myConfigButton; CRoundButton myExitButton; - // // 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 ); +// void OnColumnClick( 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(); void UpdateRomList(); - DWORD ReadRomData( CListData* ) const; + bool PopulateRomList(); + bool LoadRomListFromDisk(); + bool LoadRomListFromCache(); HINSTANCE m_hInstance; // stuff in list - HWND myHwndList; - void ClearList(); + LPARAM ListView_GetItemData( HWND hwndList, int iItem ); + void ListView_Clear(); HFONT m_hfontRomNote; // Stella stuff - CGlobalData& myGlobalData; CStellaXMain m_stella; - -CMainDlg( const CMainDlg& ); // no implementation -void operator=( const CMainDlg& ); // no implementation - }; #endif diff --git a/stella/src/win32/StellaX/StellaX.aps b/stella/src/win32/StellaX/StellaX.aps index 7c80c61ae06cc0e3cc1b87e984bfceae1344c2e2..3487aa81e9fb42c8205c354499f11526ad3d47f7 100644 GIT binary patch delta 28 kcmeyemFde?rVaDNnNBinULYRA%$mt?mO*p#PpRMa0JBO9>;M1& delta 28 kcmeyemFde?rVaDNnci@2ULYRA%o@vZmcexMPpRMa0JbR%7ytkO diff --git a/stella/src/win32/StellaX/StellaX.rc b/stella/src/win32/StellaX/StellaX.rc index 828bc592d..fe5655c1b 100644 --- a/stella/src/win32/StellaX/StellaX.rc +++ b/stella/src/win32/StellaX/StellaX.rc @@ -130,7 +130,7 @@ BEGIN 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 + WS_GROUP,105,205,297,8 LTEXT "Game notes (if available):",-1,7,205,98,8 CONTROL "&Reload",IDC_RELOAD,"Button",BS_OWNERDRAW | WS_TABSTOP, 172,226,54,16 diff --git a/stella/src/win32/StellaX/StellaX.vcproj b/stella/src/win32/StellaX/StellaX.vcproj index 58e76a808..7214549f6 100644 --- a/stella/src/win32/StellaX/StellaX.vcproj +++ b/stella/src/win32/StellaX/StellaX.vcproj @@ -20,7 +20,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="..\..\emucore;..\..\emucore\m6502\src\bspf\src;..\..\win32" - PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;BSPF_WIN32;DISPLAY_OPENGL" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;BSPF_WIN32;JOYSTICK_SUPPORT;SNAPSHOT_SUPPORT;DISPLAY_OPENGL" MinimalRebuild="TRUE" BasicRuntimeChecks="3" RuntimeLibrary="5" @@ -68,7 +68,7 @@ + + @@ -144,12 +147,21 @@ + + + + + + @@ -188,6 +200,9 @@ + + @@ -200,12 +215,21 @@ + + + + + + diff --git a/stella/src/win32/StellaX/StellaXMain.cxx b/stella/src/win32/StellaX/StellaXMain.cxx index 04b63b486..c7b3c24b2 100644 --- a/stella/src/win32/StellaX/StellaXMain.cxx +++ b/stella/src/win32/StellaX/StellaXMain.cxx @@ -14,7 +14,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: StellaXMain.cxx,v 1.3 2004-07-06 22:51:58 stephena Exp $ +// $Id: StellaXMain.cxx,v 1.4 2004-07-10 22:25:58 stephena Exp $ //============================================================================ #include @@ -37,15 +37,14 @@ CStellaXMain::~CStellaXMain() { } -void CStellaXMain::PlayROM( LPCTSTR filename, CGlobalData& globaldata ) +void CStellaXMain::PlayROM( string& romfile, CGlobalData& globaldata ) { - string rom = filename; ostringstream buf; // Make sure the specfied ROM exists - if(!globaldata.settings().fileExists(filename)) + if(!globaldata.settings().fileExists(romfile)) { - buf << "\"" << rom << "\" doesn't exist"; + buf << "\"" << romfile << "\" doesn't exist"; MessageBox( NULL, buf.str().c_str(), "Unknown ROM", MB_ICONEXCLAMATION|MB_OK); return; @@ -54,6 +53,6 @@ void CStellaXMain::PlayROM( LPCTSTR filename, CGlobalData& globaldata ) // Assume that the ROM file does exist, attempt to run external Stella // Since all settings are saved to the stella.ini file, we don't need // to pass any arguments here ... - buf << "\"" << rom << "\""; + buf << "\"" << romfile << "\""; ShellExecute(NULL, "open", "stella.exe", buf.str().c_str(), NULL, 0); } diff --git a/stella/src/win32/StellaX/StellaXMain.hxx b/stella/src/win32/StellaX/StellaXMain.hxx index f14a5857a..a5f7f4029 100644 --- a/stella/src/win32/StellaX/StellaXMain.hxx +++ b/stella/src/win32/StellaX/StellaXMain.hxx @@ -14,12 +14,14 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: StellaXMain.hxx,v 1.2 2004-07-04 20:16:03 stephena Exp $ +// $Id: StellaXMain.hxx,v 1.3 2004-07-10 22:25:58 stephena Exp $ //============================================================================ #ifndef STELLAX_H #define STELLAX_H +#include "bspf.hxx" + class CGlobalData; class CStellaXMain @@ -28,7 +30,7 @@ class CStellaXMain CStellaXMain(); ~CStellaXMain(); - void PlayROM( LPCTSTR filename, CGlobalData& globaldata ); + void PlayROM( string& romfile, CGlobalData& globaldata ); private: CStellaXMain( const CStellaXMain& ); // no implementation diff --git a/stella/src/win32/StellaX/main.cxx b/stella/src/win32/StellaX/main.cxx index ad2a9944d..2b3fb07b2 100644 --- a/stella/src/win32/StellaX/main.cxx +++ b/stella/src/win32/StellaX/main.cxx @@ -14,7 +14,7 @@ // See the file "license" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. // -// $Id: main.cxx,v 1.1 2004-06-28 23:13:54 stephena Exp $ +// $Id: main.cxx,v 1.2 2004-07-10 22:25:58 stephena Exp $ //============================================================================ #include "pch.hxx" @@ -84,7 +84,7 @@ int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, CGlobalData globaldata( hInstance ); // show the ui - CMainDlg dlg( globaldata, hInstance ); + MainDlg dlg( globaldata, hInstance ); dlg.DoModal( NULL ); if ( hrCoInit == S_OK ) diff --git a/stella/src/win32/StellaX/pch.hxx b/stella/src/win32/StellaX/pch.hxx index e16009fc1..da406cea2 100644 --- a/stella/src/win32/StellaX/pch.hxx +++ b/stella/src/win32/StellaX/pch.hxx @@ -4,29 +4,18 @@ // #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 #define DIRECTINPUT_VERSION 5 + #include #include #include -// warning C4201: nonstandard extension used : nameless struct/union - -#pragma warning ( once: 4201 ) - #include #include #include -#include -#include #include #include "debug.hxx" @@ -34,9 +23,6 @@ // --------------------------------------------------------------------------- // Conditional defines -#define DOUBLE_WIDTH - -// // Macros // @@ -53,21 +39,19 @@ class CSimpleString { -public: - + public: CSimpleString() : m_psz( NULL ), m_cch( -1 ) - { - } + { + } ~CSimpleString() - { - delete[] m_psz; - m_psz = NULL; - - m_cch = -1; - } + { + delete[] m_psz; + m_psz = NULL; + m_cch = -1; + } BOOL Set( LPCTSTR psz ) { @@ -135,5 +119,4 @@ void MessageBoxFromGetLastError( LPCTSTR pszCaption /* = NULL */ ); - #endif diff --git a/stella/src/win32/Stella_Emulator.ncb b/stella/src/win32/Stella_Emulator.ncb index d96d08cf17b5271750bc31814183ad57d7149483..f2ba98d6631bdaf4087a414aa7eb9b5a40708638 100644 GIT binary patch delta 38952 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 delta 31178 zcmeHw2Vhji_V>)(yLZ#G=@qge6+$4PLjo+)g+LHceDtF9ZWql;ktUF^$Y7&`h$u~1 zeNuveqN1Rn@)QLXpD6f%hYA9c?|0_jT@nQUzVdJ1_hq>3mD2ogarVM$L`)WK1fN zEBn};f&1)eCB2gG^{_Yg>V54;xaY*9h5UJ$+S~Ycxm#b@%N{LHY_liJ=w9|h&$vHj z%O{2b^lTe@9&amm4YNndqiyUV^29KEw#P9oJ&>Wl9FLIQ?zA_~)sb~lt#}gfw8Ik( zSPY&(Jel}C7f)?Gv2x=;d*i@*$QIyfA>Z0?heD2otbCfW z2pjsY@M77MK~DxXe+sa7Aa<0v>1y+S_T@G zVew$$!BoJAjfy^)h;$z80-hWh!mBxB(Z}snFnLL!4=Ir)vH?~Fo^7utmn48faRba2 ze~p|~Ln&FXWEiw2;BUVxg=wN%qPvS3C3P-ZXM46aW1s2OLu(teMm5LMCD!z#iL!%vcjhkRL zCXrndHqAp7`#YD+?%~V%9)CyV)A_uy=YtQTVg=is-E4Mj^b>M^e zT7OaMOl|fRPZX`qzRAY*cxDT>&Z5zPZQ;lGHZ+r?dUF;uVWaqX{tcSR6)#-)zW}YR z#a=RDo1a;DH}~)jwR6;a`p?EjvU}LrnH5hG|&L z+E>S`rHcbS3pOnB)c&PawY#sI_hp#(2YvJ2GwYY6z_FO&Lovheq8Z)~qg+mGOkj1G zfh2YbCfajT5IW-bXrLyLfcS((V`3nPj@3a1LUdOg6Ez`FIS`&&A$TB2A{ek}l=F=E z`2CW67Q$LX7Ks%$)g3Y##EPt$Y~rgTh#QRY4kl`E#gAlp#OZA{v4GHj7Xz;c`m?gn0<9wvI#3;CeUE%m`w8lj3`|f2x$bOoi#<;La?yVViOjC zX;;ABBN#PM;RQl;3)xr&!&;zVL98*-1;8Bl{>;%BPTX#Z)P-R6e&R0Gm^(wE3mUM= z)RS_(*z8FdhOY)Y4VXcVQ9TX^4a|U2%#t54W0cCpY+8<)1Hz_?B~^=AH4w9FRIm(( zUgJS(^vp(z01~8>Q5)4oOO_{*hJbe@Aa$RqGXL)v~y>{Ie zXKB(6RNaHXVC)}chkMYZo8*U)#^-kcc6yno?!^K=+%xiG7n^%JmO;;seQA80=c|j) zH2ek)9!ibHGPn?pyB!j|&nMZ``0JNJG=8C6{Hv(tIr?_q=8(I8{iy#oZG0_TpAlQH=lOh(%1|#Wi5@MXW}(y5AsL& z5YUtB41U&Awld%IT6p{A`}LCNYG9iC*$S-bF&2)Mm}96pqG5F_Aq&Fn{4V_6fVp zdJ@df{sgvko#TThHIuhb0^oXgR99OpO6qf@cE0$6EU$kx#Vpt&uN} zGjReT;3J{;2)_pM6QI>79|N3st)=f48u2IFbLvqDh))rqzfn!0RcR5QXMc z*s81z6}Eblh62-L%?8y){%fldvA`*?@Ealjy;X4{@?}i{!H6oy!jgl+0)fO^wxpInB|IW~xfVI`_!DjbFsgprM`WA5V# z7JxJ#4Rp|QmghoaI3{Z~KaBPuO|U;%f%F~l0#U~0WY{cd5aeQ((u_fd@S|Wl7Ou|h z0MP^J7SJnXgKexW7&8Kh*MN3*8xBuv35MR${-qENnaj%9Tno>H1+j7L3HF*rkQ!Ks z)j0)wknNT6WP;;KCp^@@RbAB%} z<(_*k6~%%J0zEEd+3Y(cbsz!qCoOn0PsHVDnbDFz;n{dOTI0RBXW!)}%a4bc<7=^$ zdU*Z#Vi+*K+#Fx4Sp>U&d-A#+QKp@kO4-gJAygMV{E#xbpY{M&sM~ z2IE_BgYhlEcINu=EnrcV#ur?mK`)RI7>|;I8w_ayW{ZDFd1tkSEV#jt7NCnBwvaIz zH%oX5DvvKljQUtmd3>oq3Kq_~vabDDCs+?6h~ro>3c$xDNYw~NqorUG>^Y?GPzh9n z1&d_QARQ){A1{hMjns(B`tT%g!Ksfi>{+D2ctwK`e)0nB^;aUPGc!OgyeNQFZ9wwD zi0=hhEUqH?&(R82js1kA&e0}4;H9#^BKgkIwCyZlX>1eQ)Q=s;DEkDk0K34Kk?w`O z#Q2$J!hG9dn%)JKr?8;Dz81#5~W@e`(YDgHVN*fDl2jHzvkkD#aQa@3y+n2UXZ z%L_o8N@cuawgahvVCHNsfd=Zv zb|Vd`f_NL-gH*E$!S2{dTxOdIkNDewv{w(d6KUDy%9ZqFyO7?jLhp9=I?^XqV0W+| zS;>!Vb`@YRNLx8OUWMSD>-7qZ^00_i;FB*CD=c_Hh=USVFg9574?C7^|@ zFScShcFecVMvZXfi1r-;aRjkJ5yITIa7#)_=OiX3)Mp~M8WT~;K*Lchx;5Umz|#Ov z6Fh}DF}miBGgrvOi5Jh$Mf!9*}w4{>yFgrjYA6chJs9YyKUgjTP~6;-fZz z>EGBq?jsp3{rF)_=fV6t;PV!~y>SGD@dTR(d`>jfkiiG%`g5XgL_Wi z*2HuCWP(f$7PrZX722HD4z4e3!VPU250V=l+~a-L#fNAM3wdB!Yp1uSn^%b7Z=lgS z5k=OitW{4VV6wwi^D#NMH4>awO-zXS7>F$ zHHpk8$aJIk=Y^;nD%;iN3q@q8{Ng-csBPh=y!Wrv8|rf1S#5^5K|{V@loeNdvYL24 zuHNe7F2>tt2S9R2ousOEG+&q_(#why!b2>}zJju1nc7m+%mD1j+Vq!07WQOpN5UzT zf1q7slqIoli|IfU+#$zB@fbO)DIaDFXJ_J1c`tAth-wX4Tu;;jZP`&5D>T&aJ)?`h zB4$=FQ5rtvy$;xxbYw6haAuPO6UAV8ytPOOa?Es7E2i9a()+C)=1m?ie+UxOV+KEe z66F}1(3P=4w=r0TFuUaknrI+hf)~p2_98HJ>lZ&jxC~PS)lsJbY)r=23q4zR{VH2bA-b4)LoiYa_vnLOJYYA zg}eROWI*1mu&=?$&|Ewc6z=LgoH?l;vj69@H?PC1jTIAfG3qV6+goNSpo3is+epKrw6q{6`%svMV@k3(lm zt>aW7R1U`^BODD&VYlXHJ{(+{XjNcG1;iYp8Q^p-)ZIz z>`>I9l;#wjN@M($Wmhpl5429DQP_dbvU(PPo~2log`qM!%uog4s9=j#S$*VrI}>h4 z$H*c9_-+d^9-W zyK-=X6G8P%cns!8WfS*9HGO51SbehtB{=Ij*#lN(F{o^c)w(dWZh+M|I~unP+M6oV zQFTL$TXqc1^A=ZZ;L2310tWR!$3DUj11HGNVwR0%aaN;4(CEXB!GC&@3Z%+bh=K7Y zoQlt|8W)Xxsa1g;6>PK`WS~J;ohH5=yr^vDAX_)l4+6KcEUrYMHab@lJmPj#{FK$> z2=qAH8nXb5S!H8Gk$--qGFwL<(K_sDt8orAuH0%6>`2~_28d)) z9#+<>p)N?hU&s-kM|cl6)oY8b$s!ztBfY6Eu`?)`NYE}Pfz^};T8mWO&6tbUI#t{$ z&Zd;LiZY3eU16IRDC>6=kwRxOHC4pb=EY2RaL$=Vvg^Vl7zmjEz%{5LdO_OAEnB6D z=p+|*7H;MQfPP$fXg$rQyxditccUJJ$tQwDq|-5#iEPDtyj4Fg#-1oNIjXZv`OI*U zTEDz}HgEKIvyNdqX7hc!d@P^Me99>l%4wlh&_Kbjhl^Z&#)A1=Mvo9qQQA>98X@X% zo4jj;sGAwTZ8op}ptU>QJ%`u%t4T42Eo19Ig?i)KF8@qL>d5a#h%|Bb&oXQzeoc_g zM~eC}JFL1AF-?4PY=xH(5Iect(@j4rOZ#ChK6snn#H$Y$$y|KXKvo|jYH3hj-cCcr zyV;?u0)NL1s*I`oOF zb$3qR`Qzr2Z%p1$j_=dvi-_|cSV4}OvV zQ_knp?dP+0K9f>3>iPVyTKrt`a?2}M#f#re30auE>Fl}QqGI{TNq;Ikb~OCiGiR>m zU9Bj+{N#!+cmDYCh5Sif-~RhOi~c^OqT<9)?HjDQFy-xvkAAxJ)|-P{*7)`Z`D=qE z-CB=1@4WKmjH@$Oe6u6vodfr5OR0JMSVhYRuC^@MbK%j6@3cHE$L_uRg@MP`UjF&Y zrD2CYnth>S#g2+EE55!kY0@|Cc7**{5q@s{$B*2%D$YbkDaaf;KECX|MvZ-E57^iol^^UE+`DR z7}lg-!@NsTu@%SK=U(}?&%VKu4-;`wYVxU>sJ6xURmjvkM7lgUnA<{W0%KM?X=b?P zz+ob##D#3CxrfHv$%J6ed6$|#m_Y&bmNS>Ye{S=wnSn?-9K4khDHj9;*Q^2vZpQIH zSH&?MKwpU&1BNMM_)tG2V3}^0lc@^13pJq$4oH~8u&Uf5?**y4`TfRG7Bm`eCh#CuDE~7?>RBcV+ zW3urGk!uNJmV2+;FNhc4oqR(*?P2StGTzkfkmd*Hn$8PgDP_5z%e3C&wJg?<$^Jy zPg202-Fz*$LPHtBZf9i{?M%?F7$bV74zWlaf7Q+ZZjlZI>H5}!7bPDXE4pj1dfoDq zu_9kaj}uO=vx2wLZ(ijy4Snq{S!n}?Ntox<_RJ-wDe^+ z_piwE%L!s=fQAkZb3Mjo@kEHHd!lf1hg`PXHds2`BHGg`I^MH0KE#{eSsb;?bhkJt z4-V0r$R>}8A#&7RdLOAz7A_fZx86h>_7d1V89i{{t&ftOr-%+Rd#GM2^QMYEnrofg zJ8!BO&Vzq?$eaxEFM|1xi-B^|<6^(;F4R@khnPW}lvoE;i z#2M&I_LfKG`WYfdi+|oN-!Uw<%<22W&bgHN4avgsI5J) z+%3Nzqqo(Hm$_xmSUpvDkK7n2`;67^(c*uZEDt;(G8WDeC*;{V;GuIaa*lc8S?{Zd zY|&gEc~-2EPdtbJT#Ln2d2lga_qtE7El)2IL*mBoHQSnxnLqmjGateh$)QUzfiJdv zOs-#wCQiK{Jnyhf3=0#Rq*;qom+Zb=Jcnyb z`KJfz2P&une9Y&1OU6)`RNxQh-WjGaAJBD+4U=gX6?0MY$0#<0g0 zjK^8jvU7{b5B6OD}@( zV&Zo3LMoX{2@nN7EL?8pH8z&n51IKGnZHA%@i^IYhZr18mQ2D0>Dl(#S(>RP`H05=5L(*a;GTZQLQ;>j*<;`L&cH_oG1qq zqjp0G_kC+C<{>h{sbvMzjWoSXz{{3SEtkj2(N3*T$2moyhHPNXOV>sRlMRWg zOhlFT474^}Hq6lK%X>1k+GyRf3@t68$^OawtHE?`q})W>=G>)wK}sT-`i*19?{E4j16J~>l0se7s6CML=YCg6-G>8uXF58r9wG&E_d%m@ap~3jK!lIreXO?Pd zS}*i?eW{i&PnUuvh72v!vfBHO<82_#pIN<%1)FwQW#ictR_9BTW*uC|e6h{U>ugr# zT~>)tX7ub;FSvJbW0GR%B!;BvFSMKuCc>6V3x(P=cK*v!{t4ds;i+Nz`k z5NY_f3Dj|k>2aGJJ^}`H^=Qq5$sSInVWW_Ye_nH)Hv7X9n&7@XM_Wn+LDl`EIbXFx`kmTQM{k4-|=iu+by#P7wd#!uI(iLv5 z?AJgE$Bc;RW}>IabKh$%gXwyPN;d=_>YmaXMz_Q-&dr+w$@7g~1mff`A2_A;(Tev? zmY-a<JI$+GMx?crd$2O@Z5z=MC*9)lX2^RqTjTZ(?AT+o_oXMe?l zbU|wy9*64a4v(;F0DHv+tt^<_2nc|6Qr5qyA`eqPw)`Hb1ngH$U84-Ez{@{ ziQvsK^DCRJ$Qgj(LBL;e3B8PwpI-tUy09g9B;a?X7`b}(_QEhZBgMEUIUa5MGSyd2 zj4~hx0X6R`G)}zJNRK0L0xBGg%ILC*@~uFqz-hGA23(!|Y9$))fDY|Ce7$5^E^g$=muc* zm}g=cvYW@~A9)-g^3o$@hU?(m`@L6p3(0Xa0y3qV38Ad`JU33#59_7ddh59@U*avKSYSIHBap-vyA&lL}+JPJ)%m6|~ zhS4@@T;XH9!G04Vj+L^xQ%r`IECEe#!J{B+E$}*bDkN`bT zKM3m5gn~3 z;q<9MeKW+QC`gav8S<_m{r>RdZ%*bfpa7kqkE6|}g7j4FA~NB@dTMCE=TrFor6#!m z**sYHtj^Yk$%Em>Fr0##I`q0){O42TNC)jH9r`d{TmItE@7J;coEU+sbG@%cAZ&rx zl)EDJXL!2o8)aSj9$AVqS&td1Sy?#5uGnH$8jYjFV2o27 z=2HOpX29i0z!RV2{5SG3qpRHUn9{DvP!xm>`$ao=YsWs1 zu~pcuHpAYw2+(FA(O;gaqqnW8p@4u2rB^KwvuQX_|EDoo)+^!ROND#qUn6?%AGF#8xXAMH&^b>;puY@NnN@7 z@w&2$NR_YLo5M@#SER~U?#*$#9_7DtN5y6(w|V3%^)y_;d-lJQ0Vg5P`BxrKJ&Zf* zN8y4;chsWN9d$=sQQr#J*v_7(x1^+=!&P*ZE9y324sMM*;`aDfTt|2Ie2UBLKwQni z#`zCjMCZ~)G%mn#8LjT0b6xnPyJ)(D#>Ka~0H+Ia497#bi_W#~qMf*lz9p5>EfPJ? zLtk%xVCyIj|LNrU`)n=gmKf}&AG*!k?j)Vf%6%|$P9KB46kvWlzZ3g0&g7^DJR{dn zMBtE;YWjfhSgicLNm-@gc7Q5>61%Q8+_UgvTJyQ{tstC0*d=af5$9l`Ama^28Qy7Z z0#Yl24gZ##uweE8Qf-25_56JC{KI7!PKBl`x($7_8xN%B}+k zY$#K)PWdm(qt$p5Z*)I?n*U?|)aRF&{?u}0GXKoe?w5l6R5TzJ4M>Cc0)*gNXv9q% zsHgiKsO2ad5AwurN~u-JZ`LM;nSQf=?pN{&7c(pSgnO1A>fk+iM$6MvA6!p6A#=P) za%;nFRbm;73|*^+Bu%FF(W9EvE4uOAJ~BYn7mOkzHmlacex7zHcx9D3iaZrB*9hH;fxjIy@OJo;+$QVf@VL~0sZ0!pG(G?~l7W~HjiFTs zW76FPx04BQN2%kvdwotqP&^aw#4#AF3zw?FZ0Ix)J z>1;J#)B_SnHHKVxl1ei@zbp%u&mQE_B{XQKi@=}>zPojf-gh)%6Yt3~Wn5!Va>?oF z>41ouFdLi2=krZn0J~}w2RNx3I~&O-@qtxffouRD#v4|F1+$*KFJIh+0=tHf8DbU= zWhHz%w^1b^XjbBYh5Ti9)+%hmB3O5nyOr=(m0LZ8(;Z*JJ6FMrh98{H_f&z!Gdr)t zS5Svl9XOGb&7?K&$Q@M>tFva@#UH8yOJ#@xrK&LGQx zkxLTXIPeTX{we6$JF(i{3At^+>~h&OBPBQp#W=jN;3g&Coo3|o>hf36Z%%GbHmvOA zBtr2*f(=0N0f5!V0-Oo8Np4WIkPih;O^{8;`}+8-&jMuM$BpxKHPsS$W2u%5#p{MZ ze-?_TA7G2{JGE#f9=d!klJlk;)l#{l2{8oSARuy8Pc-T!WmKJM@`LF{OboW43NZ$s z)DQ>|C=A?z;L@NOhQrnmREuQF3?r#Kv11h41*XH7nC3Ufsgz-rw4=!;MK{Z42y`s~A!*M@-nDD=G_I8X#i)P!Lux)}LUC{8$c0X7=) z+Yh5#gc|$ecN$$N1jX{KGxWqeQW*&WtrZk~`sen@M{H5cjDh}<3i=GN2C!$ifI~2HdxgbYZVy$nh>cYJ>qfX^12gI!Mqfu(vJJWj3KoP}T9a{IW1^i7 zg0%31a0OIyh21ZkB$ua9j^fDLfadLZ5^00n-dnx17OAZc(e;urv@_DLrAUP&ncE_N zya>(CWYgK6v2s%h(Dbct#+pJi-Q1$X+Pp{L-y=gPH zfUqi2qqw=_U}I$%ZP3VO7YReAuO+l1JOXOhqFFd(D1+4}*h9i|;bcQk?T=c;qN5lY zOz;f2LFTZhGO#9c=9%(Qmz)&MdQ}Tzl*+z0Usp*$fN;)$NZI#Qj3-eCep3g zP-1)=?6BHcZk><`0g3%hY$)co8CZ=)F%Vnm&#`4i56#$6*(6HrTOF-O*X7hqHlI`P zLXk_r5+-09!?>#+6O6dfu^QT=&y;od==ZhMXIepg5!f6TGKqmlTP;`=P7=-8GrwHh z0)(PonpByW0fYanJh4abnEyQ*eWO^uPkbFir+*X6H%ON5)$?xOiW;M_=^!iKB|P+* z?ld>yH04&KwKscz^LO&mYIG(W=l>&5^15MN|pBqG0r8SaA)DL=H2R$Z?RM{hBL}NC*{{;kZq~%1xNY zP9a^Wf~T|Zk<=cD%3=oOKwFXro&_tXuH`_>Tw<3>*vMjYmbmg<00t;Gag?}G7tM;vc7lpi*r!PE zRe?FxLEx(@uvEAk|AmdJQb!Fnvr6(zpjAm4JBrk;3Sv6En?F|;-Ux8PtT=;xhNQNP z1cS992ggl1>Z>gyaZqt98|Y1#+ANn*A>J0TSJkpqN!jE4CiV+N)Yz zxCI*;Y^kO~gC%2)>;(YgYc(f$;qh+#E*$D7YEuEl)vgGMVx+|RB zd0k-&y~2XQ9Oxa5AAPAwQvz5pQX+v^;mB%1zMBqILMPHdwc#A3TpF&yY}gqNRUWa| z=-KgI1SNgg%P1T{EQI0>gaR6eM6M3kV1c+L9?N!HmC$St#Ljgh68UVPnN|ZSLcgu@ zV1XvJI1>uB6s9~=uEEIpGM()pqbKx_q7aA?dx$;C!oUUYhefk#Y&!ecvW`+s^bHhl zNXo0gGyH6ghk~^+^0~QINdngGQ1;g?#G!NL>usx{dx*B(3MPrF^c~+FEi?zoN99?pccQq?72Y7Ra60*bk06iK#Ja zzd=6F23AbZx}t$`e5h`|OTy3xurO>y4gQkuN$*mm{kUBB+=EZPOZV*Q()6}b*q=}YGI@!*=~ZxRr7T1ni=bVl?zix9kv$3 zY;@1^%J`dotRXJD6Jsf4oYGrE+ z9Kc*?VmJnorglyOB!^(=G{FLq?gSpFmoulgUt4{-DCrIhXt*_9(LO~^ZI%HeVm8Lr zmfr`X#@Zm~;47KH_5DgM;;Bgy?7U_#OV2Jea1SX#Vf$Rbo_o7^53A5(~+-IskdoQ@s?QK zS$BxA(enNNdOPu4v5Xv`*YixeSSudN5z{#gRkcHU`z&|>iq@A*JcKWqxSt!IKmC#w zct0A2i6;_0v%bs1Wt;CyB;?Z3@LxJSS~wGL=InL^F1KUEZI-WF_=eYJ^LB5TdMbp? zmM@MG`2|hUZtxGRti!%6GxG+#+V1FSo^ofrI{WwB;yU`>;s$tU-`p*(vGMsix^?v! zN1O9r;bxB!ZZGT4gAdINKw}cd`FK)%E#_UmcgW_IrO$#wW4Y>Ckrg~P$wUia5#A*e z^?{CsPM)j%_4z=V=j0if~9?=>&|s=6f{2> zJ0v_?lRbaiP+eZUs`ZGbxgAD_Jruz|=H5An%M)*i+LotLrzn_i4jnUlISq~zXHS{= zLAbuJ9C>gVdn=km6g;=jKQ*^5q_M682PxycaH zY0oWXsTZGGsjPBt0`ug&oiyrhOWLEbM{U8pB<+wf zQv<}P0Q|uOYk$JJ=&`vBgXJdggc zbJSVPhN&COaY(%89My%D&$*#y&v$R5*YQbC%!VATvXE*j5 z!HAxMg{$K#d2U7kM!U~wj3&ju=U0MB#}8U)Ek8meth|fZazbEn>Xwea;$$(>lSCXh z%I$y9GFza5bY&9_oizqY=>vboi7>?WGW7u(Z8oMFq^u)**KD+W@dvF&{!vVtUNGy; zCA%nAHjYgg^n$sOBdZZMfqk%`48~@y4<^(&dG!Y^&K84twpu3sO?zCrQ+da-;wJbW zL+(+rmR_lM)JuLw>%(C_C1E7RmBn6Sbw9)y)r1bh7-9XwQGASPt%zEq#)x2aX4!-? zkycrP(QZcB^(w(=ERVZ@NA!SCcx0D6O6;zW(b>ih#CW~Vj?AgD`X!b zf7J+@tO}N(zDQwHi9!TV6flv!fHZ_)#4ZI(QeQb@S9n-U6)c%aq>rnVt%fh%TgU2E zDO+6~ygLxgUvUasieyGm^7emjmcABK0uRHicsU8B&5lP<*O zEe~g#2{_v{!ql!qz8G{?!PKTb6BZ zrjf_}Om^T{7~S^c(PeQj5nEQ=oWfV~@*MS*c2cv_juA+JBZ2vSlBUKY7|qo4^)P!^ zVMWS2xL3n>czG@!OpV!!UhIKzMWKPCAe(Qa@`{Ef`h9oNmP^N4S9%? zwgU}$z9_NbLI<;6)kz9dN9q&-STCuY%OM@fl2kb3wUdjc4u#9W#cO4CM(2&zRbV!C z61w#=@chRZ0~bKGR3ISAqIM_MZpZCVKI=&p<2TQO{TZ=`ORNS2sXcwUN!W8>f3u-+ z)vRJ+tRB)bD&{|Z(U)U7l@s)}wmH;Mp&pU&w{U@$2>crd}}lsE3Z zrY#BVr2k@-@T=2dn|@Z);*{xzzP!i?^ja|T)=FozS21{Z-E;E242{lTj`uT2o0+t# zL1PB0Z&fnbU7k)|@;#H@Z<~VyrTLoN?L!zj-(TxOh8lYMCMlzS7W15bzbMv!IIV@* zn5aTv>15dTKxPYnFLTkSNEH=%jYD-XL8I7NBGn_yYEfM%m1qWCKpgUHK9HI1XG%SF zRPGX58NI$zQuK9ER;d5#b$Y-b2=`BnyJ@(8gUaFlqkg5unSNS80xSK%5Hr>HYd{4; zA_pZoS@-NyE@lP#Q^|Iazvj4!yB6p_Lc6v4Z^Qh(2=mvp|KjNWEumIi=@ETMz`r4! zG|;j2iq4mMd7(JOt;kZBIQ~xrlB6!555!190b`+}A1Bb=l(F;#NA6mTqL(sMO$b%by#}L$Atl1zx9xLjp@JQiaFB^>$b#4$HDSEvu7mgFR zn!%CIb+d0494RiYm5+}X_az*BVfOFDMv66S@43i+*7NsNWX@ zd3?E?FhdlGy>nCqqzG6l-<%=ph;e)5&ojjBD*iF?AkNn}ihn%5Sk9j*`iWPclYgHn znu%WJvd%0Nx<2@k;vL7u&b=1>D26>FzZwI3rR!-~Yph;F-*9!Q75g{h_kkHQ@* zUtWp^-Z=De!;U%seCXq=O`cGpk3n?442FE2JR-Y1tk=6f^6>~NsS^3CjeG8Ok&oi!dAVsDJRNTo_=vF9ALsn%L_TK!IOm3uk2Amw75TWR$rJy2Tv z>*E{4D#tgTY$E5TYh(UUc;iKk#aouG?%x&OIN;O`EqD!z0l!gr<2b;tx8P|I7B}D& z-k2MY%uS;k)5R&Mshb5iw#CuwUkq-nQHbjQqu54-@UhnSn2YGY7Tb6bwcRqzr1Woz zZ9ESCREceDgRq(3#x^3f4_Kto!kYyw_6(-)jimh;!mVx?m`I^el>-y= z(0mn`=tAaS2u!?bP+~FY{!2lLhzR@Vf)Z!E_)2PL}qLQmW@DDf7sih>fwhP_YD z)nl!gL~(qt{Mc#KQz41s>|WWg)M%$I{cvujh{RX%8$~3B?fY=9KO*tShw}R}qn~yX zFI7O|MPw=mBxWC)>kmlmh5U^I5)nFBDIjqeu&M?mYKsodmA%uAj2QY-QJM(!eX;*R zu;ChzQNf0y{E$q{Flsms0`i+!!?3e}`eF?O4$CnaMm>M1Aw_UfsA24lLJggV<@+|h zMq=LKxi^e7bRCvgGmLggu6O3%Fwn60Fm`*t7ic&DgnfaA6vS!<8a{wxHw`pITw&Ef zLl?^3IM8qgsQ`Xt)S%MxbFr99f@e{OG&w=-V3UW@-aos^G(N zw5U?>VYqRbTQP?Rf%2P}!{Y${qcMlQKAQXA9dqc!l}OciLy9#tV-91li#g1dN2eGU zs|H-i>}f{&e_psn%Ks0y_+JoiLE#5va@W{@PPj#QD$K(^0WJR663_u3R}s))eTiJ! zR>TM2_Khjk8e1YO!gcz>Bo!(=4h4TBwr&7#Cbl{(IP$wFBkRTy!}~7I`L{_gMF~^X zFhvT}Efz%%-y~wVpe@2QaHGRjhz>%2V0ynke%JsRiXkRB_Qeqg;k(>}@sJ$}HZ?i6 zuHMlXO`MCrkg<|oJ#+O8slz9KW!~UL#!uSTa$^~n)0Y?>XvZ%H&xYD4=2Ij4? z(g@}1!lUKi%38(BPOFS;xxj`$$1`OW0?-Nn<5dO%rIqCiku$GS#dzv-aaN0{kglAd#U@ce`Poo$`@Z5 z&r!zv=-0+;+$%=gn+Q4fTjN#rw$@2wm3(@fy@|AcXB5jC91HZ^o$w^_a|M7x*J1=V=|KBUPHb_vioU+YWK;oLP-Keg{ z{l8L>B?S4Px@%T{o~IqO{yBK%(Spk4#y5>-|Knu5I@E)mzUvJ?-hZd4ONjbWwWP3H zNl`?7YV_E8*wFN{Hr2e-KQ$G*_y0LH5T-*7(9b`kLhtl!te)geYGRB|@Xjc*@3(n+ zbXuqWjO`EIs{R?7MP=Qsn-D_T<0}py3-%*e~i2*~*YVfw1qd*uratX&`ZN%7_s8hvAE(mY{Ap6*h0vYa00 b$(+ia&HRD%G$4|MJ5F;l~YXniQ!@d_^JIoFHtNU>EBx(hZuh9>Pgd2{YwVw(j; zcfIuXh^5BPfF9_L{0N5nSgto3;@LhOgsnI z;Wk_$*|U(PU4#-8(J>p?9?Rh6^z;9PE0a{}BH09uVVH})Da|YVV2+O^U~Qlzb&5<< z5ekX5fB%Gis+};bq<%&hNnHyXq_9bK>ZG`i`E+waG}Wc|_`>hr3&0dZYq*TBQ;zZs z9>OwQg=H+H?_qW58n(tmV&B$fT!??(m%kR%u7`~1iH=MG+ldY Gll=n$jL2sI delta 781 zcmZWmO=uHQ5T3WbT{m%)YIVeO>@Z6IZ53QFR3W7Rsw;K!2^3BeCGw;pMtCefzwF(c` z9xN}sxzS|1fFzFdd?LVNWOfn$kYHF%;}&meY@2Y%uiU^!fa8{I@(UEeqLO15@v$<@ zF5xF-E?FYdBuo;{6Q&5i>8OWkEbM+&S4Wiqi&97YmG}Q7h=30KywvUqn39@VfPQwVClqro-4-OCo%$rL-+!^f2ZtC=Zvd9uVf=XKdi2cHq!r= zxHc1(K9<9hDBvy;U`#9#^ZiS>)PD_+CC0_w$q}f+9NdR{H1Qd-s*bL}EcsP93Qk>o z+gp$hfY`U<))nSJmh5rYceK}x_ZKhaibz*8^qNBiY@`OH)pbnr7=}{`d}r*(&4hxt lyW_ZF@Ok+=BWSx