/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */ /* * Copyright 2002-2004 by Eric House (fixin@peak.org). All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Derived from code generated by M$'s eVC++. */ #include "stdafx.h" #include "xwords4.h" #include #include #include #include #include #include "strutils.h" #include "memstream.h" #include "cemain.h" #include "cedefines.h" #include "ceginfo.h" #include "cestrbx.h" #include "cedict.h" #include "ceblank.h" #include "ceprefs.h" #include "ceaskpwd.h" #include "ceutil.h" #include "ceir.h" #include "ceclrsel.h" #include "cehntlim.h" #include "LocalizedStrIncludes.h" #define MAX_LOADSTRING 100 /* #define PREFSFILENAME L"\\My Documents\\Personal\\.xwprefs" */ /* #define UNSAVEDGAMEFILENAME "\\My Documents\\Personal\\_newgame" */ #define DEFAULT_DIR_NAME L"\\My Documents\\Crosswords" /* #define PREFSFILENAME L"\\My Documents\\Crosswords\\.xwprefs" */ #define PREFSFILENAME L"\\My Documents\\Crosswords\\xwprefs" #define UNSAVEDGAMEFILENAME "\\My Documents\\Crosswords\\_newgame" #ifdef MEM_DEBUG # define MEMPOOL globals->mpool, #else # define MEMPOOL #endif typedef struct FileWriteState { CEAppGlobals* globals; XP_UCHAR* path; } FileWriteState; /* forward util function decls */ static VTableMgr* ce_util_getVTManager( XW_UtilCtxt* uc ); static void ce_util_userError( XW_UtilCtxt* uc, UtilErrID id ); static XP_Bool ce_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream ); static XWBonusType ce_util_getSquareBonus( XW_UtilCtxt* uc, ModelCtxt* model, XP_U16 col, XP_U16 row ); static XP_S16 ce_util_userPickTile( XW_UtilCtxt* uc, PickInfo* pi, XP_U16 playerNum, XP_UCHAR4* texts, XP_U16 nTiles ); static XP_Bool ce_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name, XP_UCHAR* buf, XP_U16* len ); static void ce_util_trayHiddenChange( XW_UtilCtxt* uc, XP_Bool nowHidden ); static void ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset, XP_U16 newOffset ); static void ce_util_notifyGameOver( XW_UtilCtxt* uc ); static XP_Bool ce_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row ); static XP_Bool ce_util_engineProgressCallback( XW_UtilCtxt* uc ); static void ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why ); static void ce_util_requestTime( XW_UtilCtxt* uc ); static XP_U32 ce_util_getCurSeconds( XW_UtilCtxt* uc ); static DictionaryCtxt* ce_util_makeEmptyDict( XW_UtilCtxt* uc ); #ifndef XWFEATURE_STANDALONE_ONLY static XWStreamCtxt* ce_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_U16 channelNo ); #endif static XP_UCHAR* ce_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode ); static XP_Bool ce_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 turn, XP_Bool turnLost ); #ifdef XWFEATURE_SEARCHLIMIT static XP_Bool ce_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min, XP_U16* max ); #endif #ifdef SHOW_PROGRESS static void ce_util_engineStarting( XW_UtilCtxt* uc ); static void ce_util_engineStopping( XW_UtilCtxt* uc ); #endif static void notImpl( CEAppGlobals* globals ); static void messageBoxChar( CEAppGlobals* globals, XP_UCHAR* str, wchar_t* title ); static XP_Bool queryBoxChar( CEAppGlobals* globals, XP_UCHAR* msg ); static XP_Bool ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream, wchar_t* title, XP_Bool isQuery, XP_Bool destroy ); static void RECTtoXPR( XP_Rect* dest, RECT* src ); static XP_Bool doNewGame( CEAppGlobals* globals, XP_Bool silent ); static XP_Bool ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave ); static void updateForColors( CEAppGlobals* globals ); // Forward declarations of functions included in this code module: ATOM MyRegisterClass (HINSTANCE, LPTSTR); BOOL InitInstance (HINSTANCE, int); LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About (HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable; // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_XWORDS4); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } // // FUNCTION: MyRegisterClass() // // PURPOSE: Registers the window class. // // COMMENTS: // // It is important to call this function so that the application // will get 'well formed' small icons associated with it. // ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(CEAppGlobals*); wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_XWORDS4)); wc.hCursor = 0; wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = szWindowClass; return RegisterClass(&wc); } #define N_TOOLBAR_BUTTONS 4 static void addButtonsToCmdBar( CEAppGlobals* globals ) { XP_Bool success; XP_U16 i; int index; int cmds[N_TOOLBAR_BUTTONS] = { FLIP_BUTTON_ID, VALUE_BUTTON_ID, HINT_BUTTON_ID, JUGGLE_BUTTON_ID }; int resIDs[N_TOOLBAR_BUTTONS] = { IDB_FLIPBUTTON, IDB_VALUESBUTTON, IDB_HINTBUTTON, IDB_JUGGLEBUTTON }; TBBUTTON buttData = { 0, /*iBitmap; */ 0, //FLIP_BUTTON_ID, TBSTATE_ENABLED, /* state */ TBSTYLE_BUTTON, 0, -1 }; for ( i = 0; i < N_TOOLBAR_BUTTONS; ++i ) { index = CommandBar_AddBitmap(globals->hwndCB, globals->hInst, resIDs[i], 1, 16, 16 ); buttData.iBitmap = index; buttData.idCommand = cmds[i]; success = CommandBar_InsertButton( globals->hwndCB, -1, &buttData ); } } /* addButtonsToCmdBar */ static void ceInitUtilFuncs( CEAppGlobals* globals ) { UtilVtable* vtable = globals->util.vtable = XP_MALLOC( globals->mpool, sizeof( UtilVtable ) ); globals->util.closure = (void*)globals; globals->util.gameInfo = &globals->gameInfo; MPASSIGN( globals->util.mpool, globals->mpool ); vtable->m_util_getVTManager = ce_util_getVTManager; vtable->m_util_userError = ce_util_userError; vtable->m_util_getSquareBonus = ce_util_getSquareBonus; vtable->m_util_userQuery = ce_util_userQuery; vtable->m_util_userPickTile = ce_util_userPickTile; vtable->m_util_askPassword = ce_util_askPassword; vtable->m_util_trayHiddenChange = ce_util_trayHiddenChange; vtable->m_util_yOffsetChange = ce_util_yOffsetChange; vtable->m_util_notifyGameOver = ce_util_notifyGameOver; vtable->m_util_hiliteCell = ce_util_hiliteCell; vtable->m_util_engineProgressCallback = ce_util_engineProgressCallback; vtable->m_util_setTimer = ce_util_setTimer; vtable->m_util_requestTime = ce_util_requestTime; vtable->m_util_getCurSeconds = ce_util_getCurSeconds; vtable->m_util_makeEmptyDict = ce_util_makeEmptyDict; vtable->m_util_getUserString = ce_util_getUserString; vtable->m_util_warnIllegalWord = ce_util_warnIllegalWord; #ifdef XWFEATURE_SEARCHLIMIT vtable->m_util_getTraySearchLimits = ce_util_getTraySearchLimits; #endif #ifdef SHOW_PROGRESS vtable->m_util_engineStarting = ce_util_engineStarting; vtable->m_util_engineStopping = ce_util_engineStopping; #endif } /* ceInitUtilFuncs */ static XP_Bool cePositionBoard( CEAppGlobals* globals ) { XP_Bool erase = XP_FALSE; XP_U16 nCols, leftEdge; XP_U16 boardHeight, trayTop, scoreWidth; XP_ASSERT( !!globals->game.model ); nCols = model_numCols( globals->game.model ); XP_ASSERT( nCols <= CE_MAX_ROWS ); leftEdge = CE_BOARD_LEFT_RH; leftEdge += ((CE_MAX_ROWS-nCols)/2) * CE_BOARD_SCALEH; board_setTimerLoc( globals->game.board, CE_TIMER_LEFT, CE_TIMER_TOP, CE_TIMER_WIDTH, CE_TIMER_HEIGHT ); /* If there's no timer, set the scoreboard to include the timer's rect so that the entire screen will be owned and erased as appropriate. Otherwise that region never gets redrawn. */ scoreWidth = CE_TIMER_LEFT - CE_SCORE_LEFT; if ( !globals->gameInfo.timerEnabled ) { scoreWidth += CE_TIMER_WIDTH; } board_setPos( globals->game.board, leftEdge, CE_BOARD_TOP, XP_FALSE ); board_setScale( globals->game.board, CE_BOARD_SCALEH, CE_BOARD_SCALEV ); board_setScoreboardLoc( globals->game.board, CE_SCORE_LEFT, CE_SCORE_TOP, scoreWidth, CE_SCORE_HEIGHT, XP_TRUE ); board_setShowColors( globals->game.board, globals->appPrefs.showColors ); board_setYOffset( globals->game.board, 0, XP_FALSE /* why bother */ ); board_prefsChanged( globals->game.board, &globals->appPrefs.cp ); /* figure location for the tray. If possible, make it smaller than the ideal to avoid using a scrollbar. Also, note at this point whether a scrollbar will be required. */ boardHeight = CE_BOARD_SCALEV * nCols; trayTop = boardHeight + CE_BOARD_TOP + 1; if ( trayTop < CE_TRAY_TOP ) { trayTop = CE_TRAY_TOP;/* we want it this low even if not necessary */ } else { /* while ( trayTop > CE_TRAY_TOP_MAX ) { */ /* XP_DEBUGF( "dropping trayTop from %d\n", trayTop ); */ /* trayTop -= scale; */ /* } */ } XP_DEBUGF( "set trayTop to %d", trayTop ); board_setTrayLoc( globals->game.board, CE_TRAY_LEFT_RH, trayTop, CE_TRAY_SCALEH, CE_TRAY_SCALEV, CE_DIVIDER_WIDTH ); /* setCtrlsForTray( globals ); */ /* drawFormButtons( globals ); */ server_prefsChanged( globals->game.server, &globals->appPrefs.cp ); return erase; } /* cePositionBoard */ /* Set the title to be app-name COLON file-name. If there's no colon there * now append colon and game name. Else if there is a color replace what * follows it with the new name. */ static void ceSetTitleFromName( CEAppGlobals* globals ) { wchar_t widebuf[256]; wchar_t* colonPos; XP_UCHAR* baseStart; XP_UCHAR* gameName; XP_U16 len = (XP_U16)SendMessage( globals->hWnd, WM_GETTEXT, sizeof(widebuf), (long)widebuf ); colonPos = wcsstr( widebuf, L":" ); if ( colonPos == NULL ) { wcscat( widebuf, L":" ); colonPos = widebuf + len; /* we'll write at the end */ } ++colonPos; /* skip the colon */ gameName = globals->curGameName; baseStart = strrchr( gameName, '\\' ); ++baseStart; len = (XP_U16)XP_STRLEN( baseStart ); MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, baseStart, len + 1, colonPos, len + 1 ); /* now get rid of the ".xwg" */ colonPos = wcsrchr( widebuf, '.' ); if ( colonPos != NULL ) { *colonPos = 0; } SendMessage( globals->hWnd, WM_SETTEXT, 0, (long)widebuf ); } /* ceSetTitleFromName */ static void ceInitAndStartBoard( CEAppGlobals* globals, XP_Bool newGame, CeGamePrefs* gp ) { DictionaryCtxt* dict; XP_UCHAR* newDictName = globals->gameInfo.dictName; /* This needs to happen even when !newGame because it's how the dict slots in PlayerInfo get loaded. That really ought to happen earlier, though. */ XP_ASSERT( !!globals->game.model ); dict = model_getDictionary( globals->game.model ); if ( !!dict ) { XP_UCHAR* curDictName = dict_getName( dict ); if ( !!newDictName && ( !curDictName || 0 != strcmp( curDictName, newDictName ) ) ) { dict_destroy( dict ); dict = NULL; } else { replaceStringIfDifferent( MEMPOOL &globals->gameInfo.dictName, curDictName ); } } if ( !dict ) { #ifdef STUBBED_DICT dict = make_stubbed_dict( MPPARM_NOCOMMA(globals->mpool) ); #else XP_ASSERT( !!newDictName ); XP_DEBUGF( "calling ce_dictionary_make" ); dict = ce_dictionary_make( globals, copyString( MEMPOOL newDictName )); #endif XP_ASSERT( !!dict ); model_setDictionary( globals->game.model, dict ); } if ( newGame ) { XP_U16 newGameID = 0; game_reset( MEMPOOL &globals->game, &globals->gameInfo, newGameID, &globals->appPrefs.cp, (TransportSend)NULL, globals ); if ( !!gp ) { globals->gameInfo.hintsNotAllowed = gp->hintsNotAllowed; globals->gameInfo.robotSmartness = gp->robotSmartness; } } XP_ASSERT( !!globals->game.board ); (void)cePositionBoard( globals ); board_invalAll( globals->game.board ); InvalidateRect( globals->hWnd, NULL, TRUE /* erase */ ); server_do( globals->game.server ); globals->isNewGame = FALSE; } /* ceInitAndStartBoard */ #ifdef DEBUG void logLastError( XP_UCHAR* comment ) { LPVOID lpMsgBuf; DWORD lastErr = GetLastError(); XP_UCHAR msg[256]; XP_U16 len; XP_U16 lenSoFar; strcpy( msg, comment ); strcat( msg, ": " ); lenSoFar = strlen( msg ); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastErr, 0, // Default language (LPTSTR) &lpMsgBuf, 0, NULL ); len = wcslen( lpMsgBuf ); if ( len >= sizeof(msg)-lenSoFar ) { len = sizeof(msg) - lenSoFar - 1; } WideCharToMultiByte( CP_ACP, 0, lpMsgBuf, len + 1, msg + lenSoFar, len + 1, NULL, NULL ); LocalFree( lpMsgBuf ); XP_LOGF( "system error: %s", msg ); } /* logLastError */ #endif static void ceSavePrefs( CEAppGlobals* globals ) { HANDLE fileH; fileH = CreateFile( PREFSFILENAME, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL ); if ( fileH != INVALID_HANDLE_VALUE ) { XP_U32 nWritten; XP_U16 nameLen = 0; XP_UCHAR* name = globals->curGameName; if ( name != NULL ) { nameLen = (XP_U16)XP_STRLEN( name ); } SetFilePointer( fileH, 0, 0, FILE_BEGIN ); /* write prefs, including version num */ WriteFile( fileH, &globals->appPrefs, sizeof(globals->appPrefs), &nWritten, NULL ); XP_DEBUGF( "sizeof(appPrefs) => %ld", sizeof( globals->appPrefs ) ); WriteFile( fileH, &nameLen, sizeof(nameLen), &nWritten, NULL ); WriteFile( fileH, name, nameLen, &nWritten, NULL ); SetEndOfFile( fileH ); /* truncate anything previously there */ XP_DEBUGF( "ceSavePrefs: prefs file written" ); } else { XP_LOGF( "failed to create prefs file" ); } } /* ceSavePrefs */ static XP_Bool ceLoadPrefs( CEAppGlobals* globals ) { XP_Bool result = XP_FALSE; HANDLE fileH; fileH = CreateFile( PREFSFILENAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( fileH != INVALID_HANDLE_VALUE ) { XP_U32 fileSize = GetFileSize( fileH, NULL ); XP_DEBUGF( "ceLoadPrefs: prefs file found" ); if ( fileSize >= sizeof( CEAppPrefs ) ) { CEAppPrefs tmpPrefs; XP_U32 bytesRead; XP_U16 nameLen; XP_UCHAR* name; if ( ReadFile( fileH, &tmpPrefs, sizeof(tmpPrefs), &bytesRead, NULL ) ) { if ( tmpPrefs.versionFlags == CUR_CE_PREFS_FLAGS ) { XP_MEMCPY( &globals->appPrefs, &tmpPrefs, sizeof(globals->appPrefs) ); result = XP_TRUE; ReadFile( fileH, &nameLen, sizeof(nameLen), &bytesRead, NULL ); name = XP_MALLOC( globals->mpool, nameLen + 1 ); ReadFile( fileH, name, nameLen, &bytesRead, NULL ); name[nameLen] = '\0'; globals->curGameName = name; XP_DEBUGF( "loaded saved name: %s", name ); } } } CloseHandle( fileH ); } else { XP_LOGF( "ceLoadPrefs: prefs file not found" ); } return result; /* none found */ } /* ceLoadPrefs */ static XWStreamCtxt* make_generic_stream( CEAppGlobals* globals ) { return mem_stream_make( MPPARM(globals->mpool) globals->vtMgr, globals, 0, NULL ); } /* make_generic_stream */ static XWStreamCtxt* fileToStream( CEAppGlobals* globals, XP_UCHAR* path ) { XWStreamCtxt* stream = NULL; HANDLE fileH; wchar_t widebuf[257]; XP_U16 len; XP_DEBUGF( "fileToStream" ); len = (XP_U16)XP_STRLEN( path ); MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path, len + 1, widebuf, len + 1 ); fileH = CreateFile( widebuf, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( fileH != INVALID_HANDLE_VALUE ) { XP_U32 len = GetFileSize( fileH, NULL ); XP_U32 nRead; XP_UCHAR* buf = XP_MALLOC( globals->mpool, len ); XP_DEBUGF( "fileSize = %ld", len ); ReadFile( fileH, buf, len, &nRead, NULL ); CloseHandle( fileH ); stream = make_generic_stream( globals ); stream_open( stream ); stream_putBytes( stream, buf, (XP_U16)nRead ); XP_FREE( globals->mpool, buf ); } XP_DEBUGF( "fileToStream => 0x%lx", (unsigned long)stream ); return stream; } /* fileToStream */ static void ceSaveGamePrefs( CEAppGlobals* globals, XWStreamCtxt* stream ) { } /* ceSaveGamePrefs */ static void ceLoadGamePrefs( CEAppGlobals* globals, XWStreamCtxt* stream ) { XP_DEBUGF( "ceLoadGamePrefs" ); } /* ceLoadGamePrefs */ static XP_Bool ceLoadSavedGame( CEAppGlobals* globals ) { XP_Bool success = XP_FALSE; XWStreamCtxt* stream; XP_DEBUGF( "ceLoadSavedGame" ); stream = fileToStream( globals, globals->curGameName ); success = stream != NULL; if ( success ) { DictionaryCtxt* dict; XP_Bool hasDict; ceLoadGamePrefs( globals, stream ); hasDict = stream_getU8( stream ); if ( hasDict ) { #ifdef STUBBED_DICT XP_ASSERT(0); /* just don't do this!!!! */ #else XP_UCHAR* name = stringFromStream( MPPARM(globals->mpool) stream ); dict = ce_dictionary_make( globals, name ); #endif } else { dict = NULL; } XP_DEBUGF( "calling game_makeFromStream" ); game_makeFromStream( MEMPOOL stream, &globals->game, &globals->gameInfo, dict, &globals->util, globals->draw, &globals->appPrefs.cp, ce_ir_send, globals ); stream_destroy( stream ); } return success; } /* ceLoadSavedGame */ #ifndef XWFEATURE_STANDALONE_ONLY XP_S16 ce_ir_send( XP_U8* buf, XP_U16 len, CommsAddrRec* addr, void* closure ) { XP_DEBUGF( "ce_ir_send called" ); return -1; } /* ce_ir_send */ #endif static void colorsFromRsrc( CEAppGlobals* globals ) { XP_U16 i; HGLOBAL globH; HRSRC rsrcH; XP_U16* ptr; rsrcH = FindResource( globals->hInst, MAKEINTRESOURCE(ID_COLORS_RES), TEXT("CLRS") ); globH = LoadResource( globals->hInst, rsrcH ); ptr = (XP_U16*)globH; XP_LOGF( "setting colors in globals" ); for ( i = 0; i < NUM_COLORS; ++i ) { XP_U8 r = (XP_U8)*ptr++; XP_U8 g = (XP_U8)*ptr++; XP_U8 b = (XP_U8)*ptr++; globals->appPrefs.colors[i] = RGB( r, g, b ); } DeleteObject( globH ); } /* colorsFromRsrc */ static void ceInitPrefs( CEAppGlobals* globals ) { globals->appPrefs.versionFlags = CUR_CE_PREFS_FLAGS; globals->appPrefs.showColors = XP_TRUE; globals->appPrefs.cp.showBoardArrow = XP_TRUE; globals->appPrefs.cp.showRobotScores = XP_FALSE; colorsFromRsrc( globals ); } /* ceInitPrefs */ // // FUNCTION: InitInstance(HANDLE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // The window class name CEAppGlobals* globals; BOOL result = TRUE; XP_Bool oldGameLoaded; XP_Bool prevStateExists; XP_Bool newDone = XP_FALSE; XP_U16 len; MPSLOT; LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_XWORDS4, szWindowClass, MAX_LOADSTRING); //If it is already running, then focus on the window hWnd = FindWindow( szWindowClass, szTitle); if ( hWnd ) { SetForegroundWindow( (HWND)((ULONG) hWnd | 0x00000001) ); return FALSE; } (void)CreateDirectory( DEFAULT_DIR_NAME, 0 ); #ifdef MEM_DEBUG mpool = mpool_make(); #endif globals = (CEAppGlobals*)XP_MALLOC( mpool, sizeof(*globals) ); XP_DEBUGF( "" ); XP_DEBUGF( "globals created: 0x%lx", globals ); XP_MEMSET( globals, 0, sizeof(*globals) ); MPASSIGN( globals->mpool, mpool ); globals->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) ); len = wcslen( DEFAULT_DIR_NAME ); len = (len + 1) * sizeof(globals->lastDefaultDir[0]); globals->lastDefaultDir = XP_MALLOC( mpool, len ); XP_MEMCPY( globals->lastDefaultDir, DEFAULT_DIR_NAME, len ); globals->hInst = hInstance; // Initialize global strings MyRegisterClass(hInstance, szWindowClass); hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, globals); globals->hWnd = hWnd; if (!hWnd) { return FALSE; } if ( globals->hwndCB ) { RECT rc, rcmb; GetWindowRect( hWnd, &rc ); GetWindowRect( globals->hwndCB, &rcmb ); rc.bottom -= (rcmb.bottom - rcmb.top); MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, FALSE); } ceInitUtilFuncs( globals ); /* choose one. If none found it's an error. */ #ifndef STUBBED_DICT globals->gameInfo.dictName = ceLocateNthDict( MPPARM(mpool) 0 ); result = globals->gameInfo.dictName != NULL; if ( !result ) { messageBoxChar( globals, "Please install at least one Crosswords " "dictionary.", L"Fatal error" ); return FALSE; } #endif gi_initPlayerInfo( MPPARM(mpool) &globals->gameInfo, "Player %d" ); /* here's where we want to behave differently if there's saved state. But that's a long ways off. */ prevStateExists = ceLoadPrefs( globals ); if ( !prevStateExists ) { ceInitPrefs( globals ); } /* must load prefs before creating draw ctxt */ globals->draw = ce_drawctxt_make( MPPARM(globals->mpool) hWnd, globals ); oldGameLoaded = prevStateExists && ceLoadSavedGame( globals ); if ( !oldGameLoaded ) { game_makeNewGame( MPPARM(mpool) &globals->game, &globals->gameInfo, &globals->util, globals->draw, &globals->appPrefs.cp, (TransportSend)NULL, globals ); newDone = doNewGame( globals, XP_TRUE ); /* calls ceInitAndStartBoard */ if ( !newDone ) { result = FALSE; } } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); if (globals->hwndCB) { CommandBar_Show(globals->hwndCB, TRUE); } if ( result && !newDone ) { ceInitAndStartBoard( globals, !oldGameLoaded, NULL ); } return result; } /* InitInstance */ static XP_Bool ceHandleHintRequest( CEAppGlobals* globals ) { XP_Bool notDone; XP_Bool draw; XP_ASSERT( !!globals->game.board ); draw = board_requestHint( globals->game.board, #ifdef XWFEATURE_SEARCHLIMIT globals->askTrayLimits, #endif ¬Done ); globals->hintPending = notDone; return draw; } /* ceHandleHintRequest */ static XP_Bool handleTradeCmd( CEAppGlobals* globals ) { return board_beginTrade( globals->game.board ); } /* handleTradeCmd */ static XP_Bool handleJuggleCmd( CEAppGlobals* globals ) { return board_juggleTray( globals->game.board ); } /* handleJuggleCmd */ static XP_Bool handleHidetrayCmd( CEAppGlobals* globals ) { XW_TrayVisState curState = board_getTrayVisState( globals->game.board ); if ( curState == TRAY_REVEALED ) { return board_hideTray( globals->game.board ); } else { return board_showTray( globals->game.board ); } } /* handleHidetrayCmd */ static XP_Bool handleDoneCmd( CEAppGlobals* globals) { return board_commitTurn( globals->game.board ); } /* handleDoneCmd */ static void ceCountsAndValues( CEAppGlobals* globals ) { if ( !!globals->game.server ) { XWStreamCtxt* stream = make_generic_stream( globals ); server_formatDictCounts( globals->game.server, stream, 3 ); (void)ceMsgFromStream( globals, stream, L"Tile Counts and Values", XP_FALSE, XP_TRUE ); } } /* ceCountsAndValues */ static void ceTilesLeft( CEAppGlobals* globals ) { if ( !!globals->game.board ) { XWStreamCtxt* stream = make_generic_stream( globals ); board_formatRemainingTiles( globals->game.board, stream ); (void)ceMsgFromStream( globals, stream, L"Remaining tiles", XP_FALSE, XP_TRUE ); } } /* ceTilesLeft */ static void ceDoHistory( CEAppGlobals* globals ) { XP_Bool gameOver = server_getGameIsOver(globals->game.server); XWStreamCtxt* stream; stream = make_generic_stream( globals ); model_writeGameHistory( globals->game.model, stream, globals->game.server, gameOver ); (void)ceMsgFromStream( globals, stream, L"Game history", XP_FALSE, XP_TRUE ); } /* ceDoHistory */ static void drawInsidePaint( HWND hWnd, CEAppGlobals* globals ) { HDC hdc; hdc = GetDC( hWnd ); if ( !hdc ) { logLastError( "drawInsidePaint" ); } else { HDC prevHDC = globals->hdc; globals->hdc = hdc; board_draw( globals->game.board ); globals->hdc = prevHDC; } } /* drawInsidePaint */ static void ceDisplayFinalScores( CEAppGlobals* globals ) { XWStreamCtxt* stream; stream = make_generic_stream( globals ); server_writeFinalScores( globals->game.server, stream ); stream_putU8( stream, '\0' ); (void)ceMsgFromStream( globals, stream, L"Final scores", XP_FALSE, XP_TRUE ); } /* ceDisplayFinalScores */ static XP_Bool doNewGame( CEAppGlobals* globals, XP_Bool silent ) { GameInfoState giState; XP_Bool changed = XP_FALSE; /* What happens if user cancels below? I'm hosed without a name, no? PENDING */ if ( globals->curGameName != NULL ) { XP_FREE( globals->mpool, globals->curGameName ); globals->curGameName = NULL; } XP_MEMSET( &giState, 0, sizeof(giState) ); giState.globals = globals; giState.isNewGame = XP_TRUE; DialogBoxParam( globals->hInst, (LPCTSTR)IDD_GAMEINFO, globals->hWnd, (DLGPROC)GameInfo, (long)&giState ); if ( !giState.userCancelled #ifndef STUBBED_DICT && ((XP_U16)XP_STRLEN(giState.newDictName) > 0) #endif ) { if ( giState.prefsChanged ) { loadCurPrefsFromState( &globals->appPrefs, &globals->gameInfo, &giState.prefsPrefs ); if ( giState.colorsChanged ) { updateForColors( globals ); } } ceInitAndStartBoard( globals, XP_TRUE, NULL ); changed = XP_TRUE; } return changed; } /* doNewGame */ static void ceChooseAndOpen( CEAppGlobals* globals ) { wchar_t path[256]; OPENFILENAME openFileStruct; XP_MEMSET( &openFileStruct, 0, sizeof(openFileStruct) ); XP_MEMSET( path, 0, sizeof(path) ); openFileStruct.lStructSize = sizeof(openFileStruct); openFileStruct.hwndOwner = globals->hWnd; openFileStruct.lpstrFilter = L"Crosswords games" L"\0" L"*.xwg" L"\0\0"; openFileStruct.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST; openFileStruct.lpstrFile = path; openFileStruct.nMaxFile = sizeof(path)/sizeof(path[0]); if ( GetOpenFileName( &openFileStruct ) ) { XP_UCHAR* name; XP_U16 len; len = wcslen(path); name = XP_MALLOC( globals->mpool, len + 1 ); WideCharToMultiByte( CP_ACP, 0, path, len + 1, name, len + 1, NULL, NULL ); if ( globals->curGameName != NULL && 0 == XP_STRCMP( name, globals->curGameName ) ){ /*already open*/ XP_FREE( globals->mpool, name ); } else if ( ceSaveCurGame( globals, XP_FALSE ) || queryBoxChar( globals, "Do you really want to " "overwrite the current game?" ) ) { if ( globals->curGameName != NULL ) { XP_FREE( globals->mpool, globals->curGameName ); } globals->curGameName = name; ceLoadSavedGame( globals ); ceInitAndStartBoard( globals, XP_FALSE, NULL ); ceSetTitleFromName( globals ); } } } /* ceChooseAndOpen */ static void updateForColors( CEAppGlobals* globals ) { ce_drawctxt_update( globals->draw, globals ); if ( !!globals->game.board ) { board_invalAll( globals->game.board ); } } /* updateForColors */ static void ceDoPrefsDlg( CEAppGlobals* globals ) { CePrefsDlgState state; CePrefsPrefs prefsPrefs; XP_MEMSET( &state, 0, sizeof(state) ); loadStateFromCurPrefs( &globals->appPrefs, &globals->gameInfo, &prefsPrefs ); (void)WrapPrefsDialog( globals->hWnd, globals, &state, &prefsPrefs, XP_FALSE ); if ( !state.userCancelled ) { loadCurPrefsFromState( &globals->appPrefs, &globals->gameInfo, &prefsPrefs ); (void)cePositionBoard( globals ); if ( state.colorsChanged ) { updateForColors( globals ); } /* need to reflect vars set in state into globals, and update/inval as appropriate. */ } } /* ceDoPrefsDlg */ static void ceWriteToFile( XWStreamCtxt* stream, void* closure ) { FileWriteState* fwState = (FileWriteState*)closure; CEAppGlobals* globals = fwState->globals; XP_UCHAR* path = fwState->path; wchar_t widebuf[257]; XP_U16 len = (XP_U16)XP_STRLEN( path ); HANDLE fileH; XP_DEBUGF( "ceWriteToFile called for %s", path ); len = (XP_U16)XP_STRLEN( path ); MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path, len + 1, widebuf, len + 1 ); fileH = CreateFile( widebuf, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if ( fileH != INVALID_HANDLE_VALUE ) { XP_U16 len = stream_getSize( stream ); XP_UCHAR* buf = XP_MALLOC( globals->mpool, len ); XP_U32 nWritten; stream_getBytes( stream, buf, len ); WriteFile( fileH, buf, len, &nWritten, NULL ); SetEndOfFile( fileH ); CloseHandle( fileH ); XP_FREE( globals->mpool, buf ); } } /* ceWriteToFile */ static XP_Bool isDefaultName( XP_UCHAR* name ) { return 0 == XP_STRCMP( UNSAVEDGAMEFILENAME, name, ); } /* isDefaultName */ static void makeUniqueName( wchar_t* buf, XP_U16 bufLen ) { XP_U16 i; DWORD attributes; for ( i = 1; i < 100; ++i ) { swprintf( buf, DEFAULT_DIR_NAME L"\\Untitled%d.xwg", i ); XP_ASSERT( wcslen(buf) < bufLen ); attributes = GetFileAttributes( buf ); if ( attributes == 0xFFFFFFFF ) { break; } } /* If we fall out of the loop, the user will be asked to confirm delete of Untitled99 or somesuch. That's ok.... */ } /* makeUniqueName */ static XP_Bool ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave ) { XP_Bool confirmed = XP_FALSE; /* If it doesn't yet have a name, get a path at which to save it. User has a chance to cancel this. But if there is a name go ahead and save using it and don't bother giving cancel chance -- since there's no harm in making 'em restart. Not sure how this changes when IR's involved. */ XP_UCHAR* name = globals->curGameName; if ( name == NULL || isDefaultName(name) ) { wchar_t nameBuf[256]; XP_UCHAR* newName = NULL; if ( autoSave ) { XP_U16 len = XP_STRLEN(UNSAVEDGAMEFILENAME) + 1; newName = XP_MALLOC( globals->mpool, len ); XP_MEMCPY( newName, UNSAVEDGAMEFILENAME, len ); confirmed = XP_TRUE; } else { OPENFILENAME sfs; XP_MEMSET( &sfs, 0, sizeof(sfs) ); XP_MEMSET( nameBuf, 0, sizeof(nameBuf) ); makeUniqueName( nameBuf, sizeof(nameBuf)/sizeof(nameBuf[0]) ); sfs.lStructSize = sizeof(sfs); sfs.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; sfs.hwndOwner = globals->hWnd; sfs.lpstrFile = nameBuf; sfs.nMaxFile = sizeof(nameBuf)/sizeof(nameBuf[0]); sfs.lpstrDefExt = L"xwg"; // sfs.lpstrTitle: doesn't work // sfs.lpstrInitialDir: doesn't either confirmed = GetSaveFileName( &sfs ); if ( confirmed ) { XP_U16 len = wcslen(nameBuf); XP_DEBUGF( "len(nameBuf) = %d", len ); newName = XP_MALLOC( globals->mpool, len + 1 ); WideCharToMultiByte( CP_ACP, 0, nameBuf, len + 1, newName, len + 1, NULL, NULL ); /* If user picked from a different directory, remember it as the new starting point. */ nameBuf[sfs.nFileOffset] = 0; if ( wcscmp( nameBuf, globals->lastDefaultDir ) != 0 ) { XP_FREE( globals->mpool, globals->lastDefaultDir ); globals->lastDefaultDir = XP_MALLOC( globals->mpool, (sfs.nFileOffset + 1) * 2 ); wcscpy( globals->lastDefaultDir, nameBuf ); } } } if ( confirmed ) { XP_ASSERT( !!newName ); if ( !!globals->curGameName ) { XP_FREE( globals->mpool, globals->curGameName ); } globals->curGameName = newName; } } else { confirmed = XP_TRUE; } if ( confirmed ) { if ( !!globals->game.server ) { XWStreamCtxt* memStream; DictionaryCtxt* dict; FileWriteState fwState; char* dictName; fwState.path = globals->curGameName; fwState.globals = globals; board_hideTray( globals->game.board ); /* so won't be visible when next opened */ memStream = mem_stream_make( MEMPOOL globals->vtMgr, &fwState, 0, ceWriteToFile ); stream_open( memStream ); ceSaveGamePrefs( globals, memStream ); /* the dictionary */ dict = model_getDictionary( globals->game.model ); #ifdef STUBBED_DICT /* don't check this in!!! */ dictName = NULL; #else dictName = !!dict? dict_getName( dict ) : NULL; #endif stream_putU8( memStream, (XP_U8)!!dictName ); if ( !!dictName ) { stringToStream( memStream, dictName ); } game_saveToStream( &globals->game, &globals->gameInfo, memStream ); stream_destroy( memStream ); } } return confirmed; } /* ceSaveCurGame */ static XP_Bool ceConfirmAndSave( CEAppGlobals* globals ) { XP_Bool confirmed = ceSaveCurGame( globals, XP_TRUE ); if ( confirmed ) { ceSavePrefs( globals ); } return confirmed; } /* ceConfirmAndSave */ static HWND makeCommandBar( HWND hwnd, HINSTANCE hInst ) { SHMENUBARINFO mbi; XP_MEMSET( &mbi, 0, sizeof(SHMENUBARINFO) ); mbi.cbSize = sizeof(SHMENUBARINFO); mbi.hwndParent = hwnd; mbi.nToolBarId = IDM_MENU; mbi.hInstRes = hInst; mbi.nBmpId = 0; mbi.cBmpImages = 0; mbi.dwFlags = SHCMBF_HIDESIPBUTTON; /* eeh added */ if (!SHCreateMenuBar(&mbi)) { XP_LOGF( "SHCreateMenuBar failed" ); return NULL; } return mbi.hwndMB; } /* makeCommandBar */ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; RECT rt; XP_Bool draw = XP_FALSE; XWTimerReason why; CEAppGlobals* globals; XP_Bool handled; if ( message == WM_CREATE ) { globals = ((CREATESTRUCT*)lParam)->lpCreateParams; SetWindowLong( hWnd, GWL_USERDATA, (long)globals ); globals->hwndCB = makeCommandBar( hWnd, globals->hInst ); addButtonsToCmdBar( globals ); } else { globals = (CEAppGlobals*)GetWindowLong( hWnd, GWL_USERDATA ); switch (message) { case WM_ACTIVATE: // Notify shell of our activate message SHHandleWMActivate( hWnd, wParam, lParam, &globals->sai, FALSE ); break; case WM_SETTINGCHANGE: SHHandleWMSettingChange( hWnd, wParam, lParam, &globals->sai ); break; case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case ID_FILE_ABOUT: DialogBox(globals->hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case ID_GAME_GAMEINFO: { GameInfoState state; XP_MEMSET( &state, 0, sizeof(state) ); state.globals = globals; state.isNewGame = XP_FALSE; DialogBoxParam(globals->hInst, (LPCTSTR)IDD_GAMEINFO, hWnd, (DLGPROC)GameInfo, (long)&state ); if ( !state.userCancelled && state.prefsChanged ) { /* need to update some prefs? */ /* if ( state.colorsChanged ) { */ updateForColors( globals ); /* } */ } } break; case ID_FILE_NEWGAME: XP_LOGF( "ID_FILE_NEWGAME" ); if ( ceSaveCurGame( globals, XP_FALSE ) || queryBoxChar( globals, "Do you really want to " "overwrite the current game?" ) ) { draw = doNewGame( globals, XP_FALSE ); } break; case ID_FILE_SAVEDGAMES: ceChooseAndOpen( globals ); break; case ID_FILE_PREFERENCES: ceDoPrefsDlg( globals ); break; case ID_GAME_FINALSCORES: if ( server_getGameIsOver( globals->game.server ) ) { ceDisplayFinalScores( globals ); } else if ( queryBoxChar( globals, "Are you sure you want to end " "the game now?" ) ) { server_endGame( globals->game.server ); draw = TRUE; } break; case ID_GAME_TILECOUNTSANDVALUES: ceCountsAndValues( globals ); break; case ID_GAME_TILESLEFT: ceTilesLeft( globals ); break; case ID_GAME_HISTORY: ceDoHistory( globals ); break; case ID_MOVE_TRADE: draw = handleTradeCmd( globals ); break; case ID_MOVE_JUGGLE: case JUGGLE_BUTTON_ID: draw = handleJuggleCmd( globals ); break; case ID_MOVE_HIDETRAY: draw = handleHidetrayCmd( globals ); break; case ID_MOVE_TURNDONE: draw = handleDoneCmd( globals); break; case FLIP_BUTTON_ID: draw = board_flip( globals->game.board ); break; case VALUE_BUTTON_ID: draw = board_toggle_showValues( globals->game.board ); break; case ID_MOVE_HINT: #ifdef XWFEATURE_SEARCHLIMIT case ID_MOVE_LIMITEDHINT: globals->askTrayLimits = wmId == ID_MOVE_LIMITEDHINT; #endif board_resetEngine( globals->game.board ); /* fallthru */ case ID_MOVE_NEXTHINT: case HINT_BUTTON_ID: draw = ceHandleHintRequest( globals ); break; case IDM_FILE_EXIT: if ( ceConfirmAndSave( globals ) ) { /* user may cancel... */ DestroyWindow(hWnd); } break; case ID_MOVE_UNDOCURRENT: draw = board_replaceTiles( globals->game.board ); break; case ID_MOVE_UNDOLAST: draw = server_handleUndo( globals->game.server ); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: if ( !!globals && GetUpdateRect( hWnd, &rt, FALSE ) ) { XP_Rect rect; if ( !!globals->game.board ) { /* When an obscuring window goes away, the update region needs to be redrawn. This allows invalidating it. */ RECTtoXPR( &rect, &rt ); board_invalRect( globals->game.board, &rect ); drawInsidePaint( hWnd, globals ); } if ( !ValidateRect( hWnd, &rt ) ) { logLastError( "WM_PAINT:ValidateRect" ); } } break; case WM_LBUTTONDOWN: globals->penDown = XP_TRUE; draw = board_handlePenDown( globals->game.board, LOWORD(lParam), HIWORD(lParam), 0, &handled ); break; case WM_MOUSEMOVE: if ( globals->penDown ) { draw = board_handlePenMove( globals->game.board, LOWORD(lParam), HIWORD(lParam) ); } break; case WM_LBUTTONUP: if ( globals->penDown ) { draw = board_handlePenUp( globals->game.board, LOWORD(lParam), HIWORD(lParam), 0 ); globals->penDown = XP_FALSE; } break; case WM_CHAR: if ( wParam == 0x08 ) { wParam = XP_CURSOR_KEY_DEL; } draw = board_handleKey( globals->game.board, wParam ) || board_handleKey( globals->game.board, wParam - ('a'-'A') ); break; case WM_TIMER: why = (XWTimerReason)wParam; if ( why == TIMER_PENDOWN || why == TIMER_TIMERTICK ) { board_timerFired( globals->game.board, why ); /* they otherwise repeat.... */ XP_ASSERT( why <= N_TIMER_TYPES ); (void)KillTimer( hWnd, globals->timerIDs[why-1] ); } break; case WM_DESTROY: CommandBar_Destroy(globals->hwndCB); /* supposedly not needed */ PostQuitMessage(0); break; case XWWM_TIME_RQST: draw = server_do( globals->game.server ); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } if ( draw ) { /* This is stupid. We can't just say "draw" because windoze clips drawing to the inval rect, and the board isn't set up to tell us what its inval rect is. So we inval everything, and then when the WM_PAINT message comes we inval the whole board because there's a huge inval rect. Dumb. Need to figure out how to have the methods in cedraw.c set the clip region to encompass the object being drawn -- taking board's word for it -- or the intersection of that with the actual clip rgn in the case where some window's gone away and revealed a large rect board didn't know about. That was the source of some trouble on Palm, and CE's so fast this works. But it's stupid. */ RECT r = { 100, 100, 102, 102 }; InvalidateRect( globals->hWnd, &r, FALSE /* erase */ ); } return 0; } /* WndProc */ // Mesage handler for the About box. LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; } /* About */ static void notImpl( CEAppGlobals* globals ) { messageBoxChar( globals, "feature not implemented", NULL ); } /* notImpl */ static XP_Bool ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream, wchar_t* title, XP_Bool isQuery, XP_Bool destroy ) { StrBoxInit init; init.title = title; init.stream = stream; init.isQuery = isQuery; init.globals = globals; DialogBoxParam( globals->hInst, (LPCTSTR)IDD_STRBOX, globals->hWnd, (DLGPROC)StrBox, (long)&init ); if ( destroy ) { stream_destroy( stream ); } return init.result == IDOK; } /* ceMsgFromStream */ static void messageBoxChar( CEAppGlobals* globals, XP_UCHAR* str, wchar_t* title ) { wchar_t* widebuf; XP_U32 len, wsize; /* first get the length required, then alloc and go */ len = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, str, strlen(str), NULL, 0 ); wsize = (len+1) * sizeof( wchar_t ); widebuf = XP_MALLOC( globals->mpool, wsize ); XP_MEMSET( widebuf, 0, wsize ); MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, str, strlen(str), widebuf, len ); MessageBox( globals->hWnd, widebuf, title, MB_OK ); XP_FREE( globals->mpool, widebuf ); } /* messageBoxChar */ static XP_UCHAR* ceStreamToStrBuf( MPFORMAL XWStreamCtxt* stream ) { XP_U16 len = stream_getSize( stream ); XP_UCHAR* buf = XP_MALLOC( mpool, len + 1 ); stream_getBytes( stream, buf, len ); buf[len] = '\0'; return buf; } /* ceStreamToStrBuf */ static void messageBoxStream( CEAppGlobals* globals, XWStreamCtxt* stream, wchar_t* title ) { XP_UCHAR* buf = ceStreamToStrBuf( MPPARM(globals->mpool) stream ); messageBoxChar( globals, buf, title ); XP_FREE( globals->mpool, buf ); } /* messageBoxStream */ static XP_Bool queryBoxChar( CEAppGlobals* globals, XP_UCHAR* msg ) { wchar_t widebuf[128]; XP_U16 answer; XP_U16 len = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, msg, strlen(msg), widebuf, sizeof(widebuf)/sizeof(widebuf[0]) ); widebuf[len] = 0; answer = MessageBox( globals->hWnd, widebuf, L"Question", MB_YESNO ); return answer == IDOK || answer == IDYES; } /* queryBoxChar */ static XP_Bool queryBoxStream( CEAppGlobals* globals, XWStreamCtxt* stream ) { XP_UCHAR* buf = ceStreamToStrBuf( MPPARM(globals->mpool) stream ); XP_Bool result = queryBoxChar( globals, buf ); XP_FREE( globals->mpool, buf ); return result; } /* queryBoxStream */ static XP_Bool ceQueryFromStream( CEAppGlobals* globals, XWStreamCtxt* stream ) { return ceMsgFromStream( globals, stream, L"Question", XP_TRUE, XP_FALSE ); } /* ceQueryFromStream */ static void RECTtoXPR( XP_Rect* dest, RECT* src ) { dest->top = (short)src->top; dest->left = (short)src->left; dest->width = (short)(src->right - src->left); dest->height = (short)(src->bottom - src->top); } /* RECTtoXPR */ void wince_assert( XP_UCHAR* s, int line, char* fileName ) { XP_WARNF( "ASSERTION FAILED %s: file %s, line %d\n", s, fileName, line ); } /* wince_assert */ static void makeTimeStamp( XP_UCHAR* timeStamp, XP_U16 size ) { SYSTEMTIME st; GetLocalTime( &st ); sprintf( timeStamp, "%d:%.2d:%.2d ", st.wHour, st.wMinute, st.wSecond ); XP_ASSERT( size > strlen(timeStamp) ); } /* makeTimeStamp */ void wince_debugf(XP_UCHAR* format, ...) { XP_UCHAR buf[256]; /* wchar_t widebuf[256]; */ va_list ap; va_start( ap, format ); vsprintf( buf, format, ap ); va_end(ap); #if 0 messageBoxChar( globals, buf, NULL ); /* #elif defined _WIN32_WCE_EMULATION */ /* XP_MEMSET( widebuf, 0, sizeof(widebuf) ); */ /* MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, buf, strlen(buf), */ /* widebuf, sizeof(widebuf)/sizeof(widebuf[0]) ); */ /* OutputDebugString( widebuf ); */ #else { /* Create logfile if necessary and write to it in ascii. */ XP_U16 nBytes; XP_U32 nWritten; HANDLE fileH; XP_UCHAR timeStamp[32]; makeTimeStamp(timeStamp, sizeof(timeStamp)); fileH = CreateFile( L"\\My Documents\\Crosswords\\xwDbgLog.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); #ifdef _WIN32_WCE_EMULATION strcat( buf, "\n" ); #else strcat( buf, XP_CR ); #endif SetFilePointer( fileH, 0, 0, FILE_END ); nBytes = strlen( timeStamp ); WriteFile( fileH, timeStamp, nBytes, &nWritten, NULL ); XP_ASSERT( nWritten == nBytes ); nBytes = strlen( buf ); WriteFile( fileH, buf, nBytes, &nWritten, NULL ); XP_ASSERT( nWritten == nBytes ); CloseHandle( fileH ); } #endif } /* wince_debugf */ XP_U16 wince_snprintf( XP_UCHAR* buf, XP_U16 len, XP_UCHAR* format, ... ) { va_list ap; va_start( ap, format ); _vsnprintf( buf, len, format, ap ); /* FormatMessage( */ /* FORMAT_MESSAGE_FROM_STRING, */ /* format, */ /* 0, */ /* 0, // Default language */ /* (LPTSTR)buf, */ /* len, &ap ); */ va_end(ap); return strlen(buf); } /* wince_snprintf */ /* I can't believe the stupid compiler's making me implement this */ void p_ignore(XP_UCHAR* c, ...){} static VTableMgr* ce_util_getVTManager( XW_UtilCtxt* uc ) { CEAppGlobals* globals = (CEAppGlobals*)uc->closure; return globals->vtMgr; } /* ce_util_getVTManager */ static void ce_util_userError( XW_UtilCtxt* uc, UtilErrID id ) { XP_UCHAR* message; CEAppGlobals* globals = (CEAppGlobals*)uc->closure; switch( id ) { case ERR_TILES_NOT_IN_LINE: message = "All tiles played must be in a line."; break; case ERR_NO_EMPTIES_IN_TURN: message = "Empty squares cannot separate tiles played."; break; case ERR_TWO_TILES_FIRST_MOVE: message = "Must play two or more pieces on the first move."; break; case ERR_TILES_MUST_CONTACT: message = "New pieces must contact others already in place (or " "the middle square on the first move)."; break; case ERR_NOT_YOUR_TURN: message = "You can't do that; it's not your turn!"; break; case ERR_NO_PEEK_ROBOT_TILES: message = "No peeking at the robot's tiles!"; break; case ERR_CANT_TRADE_MID_MOVE: message = "Remove played tiles before trading."; break; case ERR_TOO_FEW_TILES_LEFT_TO_TRADE: message = "Too few tiles left to trade."; break; case ERR_CANT_UNDO_TILEASSIGN: message = "Tile assignment can't be undone."; break; default: message = "unknown errorcode ID!!!"; break; } messageBoxChar( globals, message, L"Oops!" ); } /* ce_util_userError */ static XP_Bool ce_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream ) { char* query = NULL; char* info = NULL; CEAppGlobals* globals = (CEAppGlobals*)uc->closure; XP_Bool answer = XP_FALSE; XP_Bool queryWithStream = XP_FALSE; switch( id ) { case QUERY_COMMIT_TURN: return ceQueryFromStream( globals, stream ); case QUERY_COMMIT_TRADE: query = "Are you sure you want to spend this move trading tiles?"; return queryBoxChar( globals, query ); case QUERY_ROBOT_MOVE: return ceMsgFromStream( globals, stream, L"FYI", XP_FALSE, XP_FALSE ); case QUERY_ROBOT_TRADE: messageBoxStream( globals, stream, L"FYI" ); break; default: XP_ASSERT(0); } return XP_FALSE; } /* ce_util_userQuery */ static XWBonusType ce_util_getSquareBonus( XW_UtilCtxt* uc, ModelCtxt* model, XP_U16 col, XP_U16 row ) { XP_U16 index; CEAppGlobals* globals = (CEAppGlobals*)uc->closure; if ( !globals->bonusInfo ) { HRSRC rsrcH; HGLOBAL globH; rsrcH = FindResource( globals->hInst, MAKEINTRESOURCE(ID_BONUS_RES), TEXT("BONS") ); if ( !!rsrcH ) { globH = LoadResource( globals->hInst, rsrcH ); if ( !!globH ) { globals->bonusInfo = (XP_U16*)globH; /* We don't want to call DeleteObject here, but should when the app closes. Or does Wince free up all memory associated with a process when it closes? PENDING(eeh) */ // DeleteObject( globH ); } } } if ( col > 7 ) col = 14 - col; if ( row > 7 ) row = 14 - row; index = (row*8) + col; if ( !globals->bonusInfo || (index >= 8*8) ) { XP_ASSERT( 0 ); return (XWBonusType)BONUS_NONE; } else { /* This is probably a bit slow. Consider caching the resource in memory with one bonus value per byte. */ XP_U16 value = globals->bonusInfo[index/4]; value >>= ((3 - (index % 4)) * 4); return value & 0x0F; } } /* ce_util_getSquareBonus */ static XP_S16 ce_util_userPickTile( XW_UtilCtxt* uc, PickInfo* pi, XP_U16 playerNum, XP_UCHAR4* texts, XP_U16 nTiles ) { BlankDialogState state; CEAppGlobals* globals = (CEAppGlobals*)uc->closure; XP_MEMSET( &state, 0, sizeof(state) ); state.globals = globals; state.texts = texts; state.nTiles = nTiles; state.playerNum = playerNum; state.pi = pi; DialogBoxParam( globals->hInst, (LPCTSTR)IDD_ASKBLANK, globals->hWnd, (DLGPROC)BlankDlg, (long)&state ); return state.result; } /* ce_util_userPickTile */ static XP_Bool ce_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name, XP_UCHAR* buf, XP_U16* len ) { PasswdDialogState state; CEAppGlobals* globals = (CEAppGlobals*)uc->closure; XP_MEMSET( &state, 0, sizeof(state) ); state.name = name; state.buf = buf; state.lenp = len; DialogBoxParam( globals->hInst, (LPCTSTR)IDD_ASKPASS, globals->hWnd, (DLGPROC)PasswdDlg, (long)&state ); return !state.userCancelled; } /* ce_util_askPassword */ static void ce_util_trayHiddenChange( XW_UtilCtxt* uc, XP_Bool nowHidden ) { CEAppGlobals* globals = (CEAppGlobals*)uc->closure; drawInsidePaint( globals->hWnd, globals ); } /* ce_util_trayHiddenChange */ static void ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset, XP_U16 newOffset ) { XP_ASSERT( XP_FALSE ); /* no scrolling on CE */ } /* ce_util_yOffsetChange */ static void ce_util_notifyGameOver( XW_UtilCtxt* uc ) { CEAppGlobals* globals = (CEAppGlobals*)uc->closure; drawInsidePaint( globals->hWnd, globals ); ceDisplayFinalScores( globals ); } /* ce_util_notifyGameOver */ static XP_Bool ce_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row ) { return XP_TRUE; } /* ce_util_hiliteCell */ static XP_Bool ce_util_engineProgressCallback( XW_UtilCtxt* uc ) { return XP_TRUE; } /* ce_util_engineProgressCallback */ static void ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why ) { CEAppGlobals* globals = (CEAppGlobals*)uc->closure; XP_U32 timerID; XP_U32 howLong; if ( why == TIMER_PENDOWN ) { howLong = 400; } else if ( why == TIMER_TIMERTICK ) { howLong = 1000; /* 1 second */ } else { XP_ASSERT(0); return; } timerID = SetTimer( globals->hWnd, why, howLong, NULL); XP_ASSERT( why <= N_TIMER_TYPES ); globals->timerIDs[why-1] = timerID; } /* ce_util_setTimer */ static void ce_util_requestTime( XW_UtilCtxt* uc ) { CEAppGlobals* globals = (CEAppGlobals*)uc->closure; PostMessage( globals->hWnd, XWWM_TIME_RQST, 0, 0 ); } /* palm_util_requestTime */ static XP_U32 ce_util_getCurSeconds( XW_UtilCtxt* uc ) { return 0L; } /* ce_util_getCurSeconds */ static DictionaryCtxt* ce_util_makeEmptyDict( XW_UtilCtxt* uc ) { CEAppGlobals* globals = (CEAppGlobals*)uc->closure; #ifdef STUBBED_DICT return make_stubbed_dict( MPPARM_NOCOMMA(globals->mpool) ); #else return ce_dictionary_make( globals, NULL ); #endif } /* ce_util_makeEmptyDict */ #ifndef XWFEATURE_STANDALONE_ONLY static XWStreamCtxt* ce_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_U16 channelNo ) { } /* ce_util_makeStreamFromAddr */ #endif static XP_UCHAR* ce_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode ) { switch( stringCode ) { case STRD_REMAINING_TILES_ADD: return (XP_UCHAR*)"+ %d [all remaining tiles]"; case STRD_UNUSED_TILES_SUB: return (XP_UCHAR*)"- %d [unused tiles]"; case STR_BONUS_ALL: return (XP_UCHAR*)"Bonus for using all tiles: 50" XP_CR; case STRD_TURN_SCORE: return (XP_UCHAR*)"Score for turn: %d" XP_CR; case STR_COMMIT_CONFIRM: return (XP_UCHAR*)"Commit the current move?" XP_CR; case STR_LOCAL_NAME: return (XP_UCHAR*)"%s"; case STR_NONLOCAL_NAME: return (XP_UCHAR*)"%s (remote)"; case STRD_TIME_PENALTY_SUB: return (XP_UCHAR*)" - %d [time]"; case STRD_CUMULATIVE_SCORE: return (XP_UCHAR*)"Cumulative score: %d" XP_CR; case STRS_MOVE_ACROSS: return (XP_UCHAR*)"move (from %s across)" XP_CR; case STRS_MOVE_DOWN: return (XP_UCHAR*)"move (from %s down)" XP_CR; case STRS_TRAY_AT_START: return (XP_UCHAR*)"Tray at start: %s" XP_CR; case STRS_NEW_TILES: return (XP_UCHAR*)"New tiles: %s" XP_CR; case STRSS_TRADED_FOR: return (XP_UCHAR*)"Traded %s for %s."; case STR_PASS: return (XP_UCHAR*)"pass" XP_CR; case STR_PHONY_REJECTED: return (XP_UCHAR*)"Illegal word in move; turn lost!" XP_CR; case STRD_ROBOT_TRADED: return (XP_UCHAR*)"Robot traded tiles %d this turn."; case STR_ROBOT_MOVED: return (XP_UCHAR*)"The robot made this move:" XP_CR; case STR_REMOTE_MOVED: return (XP_UCHAR*)"Remote player made this move:" XP_CR; case STR_PASSED: return (XP_UCHAR*)"Passed"; case STRSD_SUMMARYSCORED: return (XP_UCHAR*)"%s:%d"; case STRD_TRADED: return (XP_UCHAR*)"Traded %d"; case STR_LOSTTURN: return (XP_UCHAR*)"Lost turn"; case STRS_VALUES_HEADER: return (XP_UCHAR*)"%s counts/values:" XP_CR; default: XP_LOGF( "stringCode=%d", stringCode ); return (XP_UCHAR*)"unknown code"; } } /* ce_util_getUserString */ static void ce_formatBadWords( BadWordInfo* bwi, XP_UCHAR buf[], XP_U16 bufsiz ) { XP_U16 i; for ( i = 0, buf[0] = '\0'; ; ) { XP_UCHAR wordBuf[18]; sprintf( wordBuf, "\"%s\"", bwi->words[i] ); XP_ASSERT( strlen(wordBuf) < sizeof(wordBuf)-1 ); strncat( buf, wordBuf, bufsiz - 1 ); if ( ++i == bwi->nWords ) { break; } bufsiz -= strlen( wordBuf ); strncat( buf, ", ", bufsiz - 1 ); bufsiz -= 2; } } /* ce_formatBadWords */ static XP_Bool ce_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 turn, XP_Bool turnLost ) { CEAppGlobals* globals = (CEAppGlobals*)uc->closure; XP_UCHAR wordsBuf[256]; XP_UCHAR msgBuf[256]; XP_Bool isOk; ce_formatBadWords( bwi, wordsBuf, sizeof(wordsBuf) ); sprintf( msgBuf, "Word[s] %s not found in dictionary.", wordsBuf ); if ( turnLost ) { messageBoxChar( globals, msgBuf, L"Illegal word" ); isOk = XP_TRUE; } else { strcat( msgBuf, " Use it anyway?" ); isOk = queryBoxChar( globals, msgBuf ); } return isOk; } /* ce_util_warnIllegalWord */ #ifdef XWFEATURE_SEARCHLIMIT static XP_Bool ce_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min, XP_U16* max ) { CEAppGlobals* globals = (CEAppGlobals*)uc->closure; HintLimitsState hls; XP_MEMSET( &hls, 0, sizeof(hls) ); hls.globals = globals; hls.min = *min; hls.max = *max; DialogBoxParam( globals->hInst, (LPCTSTR)IDD_ASKHINTLIMTS, globals->hWnd, (DLGPROC)HintLimitsDlg, (long)&hls ); if ( !hls.cancelled ) { *min = hls.min; *max = hls.max; } return !hls.cancelled; } /* ce_util_getTraySearchLimits */ #endif #ifdef SHOW_PROGRESS static void ce_util_engineStarting( XW_UtilCtxt* uc ) { } /* ce_util_engineStarting */ static void ce_util_engineStopping( XW_UtilCtxt* uc ) { } /* ce_util_engineStopping */ #endif