mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-11-16 07:48:07 +01:00
2108 lines
63 KiB
C
Executable file
2108 lines
63 KiB
C
Executable file
/* -*-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 <commctrl.h>
|
|
#include <commdlg.h>
|
|
#include <stdio.h>
|
|
#include <winuser.h>
|
|
#include <aygshell.h>
|
|
#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
|