xwords/xwords4/wince/cemain.c

2573 lines
76 KiB
C
Raw Normal View History

2003-11-20 17:26:35 +01:00
/* -*-mode: C; fill-column: 77; c-basic-offset: 4; -*- */
/*
* Copyright 2002-2004 by Eric House (xwords@eehouse.org). All rights reserved.
2003-11-20 17:26:35 +01:00
*
* 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++.
2003-11-20 17:26:35 +01:00
*/
#include "stdafx.h"
#include "xwords4.h"
#include <commctrl.h>
#include <commdlg.h>
#include <stdio.h>
#include <winuser.h>
#ifndef CANT_DO_SHELL_THING
# include <aygshell.h>
#endif
2003-11-20 17:26:35 +01:00
#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"
2004-03-28 03:14:34 +02:00
#include "ceclrsel.h"
2004-07-10 19:46:36 +02:00
#include "cehntlim.h"
2003-11-20 17:26:35 +01:00
#include "LocalizedStrIncludes.h"
#include "debhacks.h"
2003-11-20 17:26:35 +01:00
#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"
#define SCROLLBAR_WIDTH 12
#define SCROLLBARID 0x4321 /* needs to be unique! */
2003-11-20 17:26:35 +01:00
/* CE_DEBUG_SAVEOFTEN: Temporary hack to attempt to get a reproducible case
* of the lockup folks are seeing on WM 2003 SE devices.
*/
#define CE_DEBUG_SAVEOFTEN
2003-11-20 17:26:35 +01:00
#ifdef MEM_DEBUG
# define MEMPOOL globals->mpool,
#else
# define MEMPOOL
#endif
typedef struct FileWriteState {
CEAppGlobals* globals;
XP_UCHAR* path;
} FileWriteState;
/* forward util function decls */
#ifndef XWFEATURE_STANDALONE_ONLY
static XP_S16 ce_send_proc( XP_U8* buf, XP_U16 len,
const CommsAddrRec* addr,
void* closure );
#define CE_SEND_PROC ce_send_proc
#else
#define CE_SEND_PROC NULL
#endif
2003-11-20 17:26:35 +01:00
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 );
2003-11-20 17:26:35 +01:00
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, const PickInfo* pi,
2003-11-20 17:26:35 +01:00
XP_U16 playerNum,
const XP_UCHAR4* texts, XP_U16 nTiles );
2003-11-20 17:26:35 +01:00
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,
XW_TrayVisState newState );
2003-11-20 17:26:35 +01:00
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 );
2005-06-23 16:09:20 +02:00
static void ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when,
TimerProc proc, void* closure);
2003-11-20 17:26:35 +01:00
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 BEYOND_IR
static void ce_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr,
const CommsAddrRec* newAddr );
#endif
2004-07-10 19:46:36 +02:00
#ifdef XWFEATURE_SEARCHLIMIT
static XP_Bool ce_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min,
XP_U16* max );
#endif
2003-11-20 17:26:35 +01:00
#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 );
2003-11-20 17:26:35 +01:00
static void RECTtoXPR( XP_Rect* dest, RECT* src );
static XP_Bool ceDoNewGame( CEAppGlobals* globals, XP_Bool silent );
static XP_Bool ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave );
static void updateForColors( CEAppGlobals* globals );
static XWStreamCtxt* make_generic_stream( CEAppGlobals* globals );
static void ce_send_on_close( XWStreamCtxt* stream, void* closure );
2003-11-20 17:26:35 +01:00
// 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,
#if defined TARGET_OS_WINCE
LPWSTR lpCmdLine,
#elif defined TARGET_OS_WIN32
LPSTR lpCmdLine,
#endif
2003-11-20 17:26:35 +01:00
int nCmdShow)
{
MSG msg;
HACCEL hAccelTable;
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow)) {
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_XWORDS4);
// Main message loop. Return of 0 indicates quit message. Return of -1
// indicates major error (so we just bail.)
while ( 0 < GetMessage(&msg, NULL, 0, 0) ) {
2003-11-20 17:26:35 +01:00
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/* This would be a good place to free up memory, close sockets, etc. */
2003-11-20 17:26:35 +01:00
return msg.wParam;
}
#ifdef __GNUC__
int
main()
{
XP_LOGF( "" );
LOG_FUNC();
return WinMain( GetModuleHandle(NULL), 0,
#if defined TARGET_OS_WINCE
GetCommandLineW(),
#elif defined TARGET_OS_WIN32
GetCommandLineA(),
#endif
SW_SHOWDEFAULT );
}
#endif
2003-11-20 17:26:35 +01:00
//
// 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;
XP_MEMSET( &wc, 0, sizeof(wc) );
2003-11-20 17:26:35 +01:00
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbWndExtra = sizeof(CEAppGlobals*);
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_XWORDS4));
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
#if defined TARGET_OS_WIN32
wc.lpszMenuName = (LPCTSTR)IDM_MENU;
#endif
2003-11-20 17:26:35 +01:00
wc.lpszClassName = szWindowClass;
return RegisterClass(&wc);
}
#define N_TOOLBAR_BUTTONS 4
2003-11-20 17:26:35 +01:00
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 };
2003-11-20 17:26:35 +01:00
int resIDs[N_TOOLBAR_BUTTONS] = { IDB_FLIPBUTTON, IDB_VALUESBUTTON,
IDB_HINTBUTTON, IDB_JUGGLEBUTTON };
2003-11-20 17:26:35 +01:00
TBBUTTON buttData = {
0, /*iBitmap; */
0, //FLIP_BUTTON_ID,
TBSTATE_ENABLED, /* state */
TBSTYLE_BUTTON,
0,
-1
};
#ifndef CANT_DO_CMDBAR
#ifdef TARGET_OS_WINCE
2003-11-20 17:26:35 +01:00
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 );
}
#endif
#endif
2003-11-20 17:26:35 +01:00
} /* 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 BEYOND_IR
vtable->m_util_addrChange = ce_util_addrChange;
vtable->m_util_makeStreamFromAddr = ce_util_makeStreamFromAddr;
#endif
2004-07-10 19:46:36 +02:00
#ifdef XWFEATURE_SEARCHLIMIT
vtable->m_util_getTraySearchLimits = ce_util_getTraySearchLimits;
#endif
2003-11-20 17:26:35 +01:00
#ifdef SHOW_PROGRESS
vtable->m_util_engineStarting = ce_util_engineStarting;
vtable->m_util_engineStopping = ce_util_engineStopping;
#endif
} /* ceInitUtilFuncs */
#ifdef CEFEATURE_CANSCROLL
# define SCROLL_SHRINK 1
static void
showScroller( CEAppGlobals* globals, XP_U16 nHidden, XP_U16 x, XP_U16 y,
XP_U16 width, XP_U16 height )
{
if ( !globals->scrollHandle ) {
HWND hwndSB;
SCROLLINFO sinfo;
int ret;
hwndSB = CreateWindow( TEXT("SCROLLBAR"), // Class name
NULL, // Window text
// Window style
SBS_VERT|WS_VISIBLE|WS_CHILD,
x + SCROLL_SHRINK, y,
width - SCROLL_SHRINK, height + 1,
globals->hWnd,
(HMENU)SCROLLBARID,// The control identifier
globals->hInst, // The instance handle
NULL ); // s'pposed to be NULL
XP_MEMSET( &sinfo, 0, sizeof(sinfo) );
sinfo.cbSize = sizeof(sinfo);
sinfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
sinfo.nPos = 0;
sinfo.nMin = 0;
sinfo.nMax = nHidden;
sinfo.nPage = 1;
ret = SetScrollInfo( hwndSB, SB_CTL, &sinfo, TRUE );
EnableWindow( hwndSB, TRUE ); /* tray hidden */
globals->scrollHandle = hwndSB;
}
ShowWindow( globals->scrollHandle, SW_SHOW );
} /* showScroller */
static void
hideScroller( CEAppGlobals* globals )
{
if ( !!globals->scrollHandle ) {
ShowWindow( globals->scrollHandle, SW_HIDE );
}
/* else there's nothing to do */
}
#endif
#define MIN_CELL_WIDTH 12
#define MIN_CELL_HEIGHT 12
#if defined TARGET_OS_WINCE
# define MIN_TRAY_HEIGHT 20
#elif defined TARGET_OS_WIN32
# define MIN_TRAY_HEIGHT 40
#endif
#define TRAY_PADDING 1
typedef struct CEBoardParms {
XP_U16 boardHScale;
XP_U16 boardVScale;
XP_U8 trayVScale;
XP_U8 trayHScale;
XP_U16 trayTop;
XP_U16 leftEdge;
XP_U16 scoreWidth;
XP_U16 scoreHeight;
XP_Bool needsScroller;
} CEBoardParms;
static void
figureBoardParms( CEAppGlobals* globals, XP_U16 nCols, CEBoardParms* bparms )
{
RECT rc;
XP_U16 width, height;
XP_U16 trayVScale, leftEdge, scoreWidth;
XP_U16 boardHt, boardWidth, visBoardHt, hScale, vScale, nHiddenRows;
XP_U16 boardHtLimit, trayTop;
XP_Bool needsScroller;
GetClientRect( globals->hWnd, &rc );
width = (XP_U16)(rc.right - rc.left);
height = (XP_U16)(rc.bottom - rc.top);
boardHt = height - CE_SCORE_HEIGHT - MIN_TRAY_HEIGHT;
/* Try to make it fit without scrolling. But if necessary, reduce the
width for a scrollbar. */
vScale = boardHt / nCols;
needsScroller = vScale < MIN_CELL_HEIGHT;
if ( needsScroller ) {
vScale = MIN_CELL_HEIGHT;
}
boardWidth = width;
if ( needsScroller ) {
boardWidth -= SCROLLBAR_WIDTH;
}
hScale = boardWidth / nCols;
/* Figure tray top. May overlap board. The tray's height must be at
least the minimum, plus whatever fraction of a row is left when
visible board height is determined. */
visBoardHt = vScale * nCols;
nHiddenRows = 0;
boardHtLimit = height - CE_SCORE_HEIGHT - MIN_TRAY_HEIGHT;
while ( visBoardHt > boardHtLimit ) {
visBoardHt -= vScale;
++nHiddenRows;
}
trayTop = CE_SCORE_HEIGHT + visBoardHt + TRAY_PADDING;
trayVScale = height - trayTop;
/* Center the board */
boardWidth = nCols * hScale;
if ( needsScroller ) {
boardWidth += SCROLLBAR_WIDTH;
}
leftEdge = (width - boardWidth) / 2; /* center it all */
scoreWidth = width;
if ( globals->gameInfo.timerEnabled ) {
scoreWidth -= CE_TIMER_WIDTH;
}
bparms->boardHScale = hScale;
bparms->boardVScale = vScale;
bparms->trayTop = trayTop;
bparms->trayVScale = (XP_U8)trayVScale;
bparms->trayHScale = CE_TRAY_SCALEH; /* unchanged so far... */
bparms->leftEdge = leftEdge;
bparms->scoreWidth = scoreWidth;
bparms->scoreHeight = CE_SCORE_HEIGHT;
#ifdef CEFEATURE_CANSCROLL
bparms->needsScroller = needsScroller;
if ( needsScroller ) {
XP_U16 boardRight = leftEdge + (nCols * hScale);
showScroller( globals, nHiddenRows,
boardRight,
CE_SCORE_HEIGHT,
rc.right - boardRight, visBoardHt );
XP_LOGF( "NEEDING SCROLLBAR!!!!" );
XP_LOGF( "%d rows hidden", nHiddenRows );
} else {
hideScroller( globals );
}
globals->nHiddenRows = nHiddenRows;
#endif
} /* figureBoardParms */
2003-11-20 17:26:35 +01:00
static XP_Bool
cePositionBoard( CEAppGlobals* globals )
{
XP_Bool erase = XP_FALSE;
XP_U16 nCols;
CEBoardParms bparms;
2003-11-20 17:26:35 +01:00
XP_ASSERT( !!globals->game.model );
nCols = model_numCols( globals->game.model );
XP_ASSERT( nCols <= CE_MAX_ROWS );
figureBoardParms( globals, nCols, &bparms );
2003-11-20 17:26:35 +01:00
board_setTimerLoc( globals->game.board, CE_TIMER_LEFT,
CE_TIMER_TOP, CE_TIMER_WIDTH, CE_TIMER_HEIGHT );
board_setPos( globals->game.board, bparms.leftEdge,
bparms.scoreHeight, XP_FALSE );
board_setScale( globals->game.board, bparms.boardHScale, bparms.boardVScale );
2003-11-20 17:26:35 +01:00
board_setScoreboardLoc( globals->game.board, CE_SCORE_LEFT,
CE_SCORE_TOP, bparms.scoreWidth,
bparms.scoreHeight, XP_TRUE );
2003-11-20 17:26:35 +01:00
board_setShowColors( globals->game.board, globals->appPrefs.showColors );
2005-01-23 09:15:29 +01:00
board_setYOffset( globals->game.board, 0 );
2003-11-20 17:26:35 +01:00
board_prefsChanged( globals->game.board, &globals->appPrefs.cp );
board_setTrayLoc( globals->game.board,
CE_TRAY_LEFT_RH,
bparms.trayTop,
bparms.trayHScale, bparms.trayVScale,
2003-11-20 17:26:35 +01:00
CE_DIVIDER_WIDTH );
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,
const CommsAddrRec* addr )
2003-11-20 17:26:35 +01:00
{
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
2004-01-17 23:10:23 +01:00
XP_ASSERT( !!newDictName );
2003-11-20 17:26:35 +01:00
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, &globals->util,
newGameID, &globals->appPrefs.cp, CE_SEND_PROC,
2004-01-17 23:10:23 +01:00
globals );
2003-11-20 17:26:35 +01:00
if ( !!gp ) {
globals->gameInfo.hintsNotAllowed = gp->hintsNotAllowed;
globals->gameInfo.robotSmartness = gp->robotSmartness;
}
#ifndef XWFEATURE_STANDALONE_ONLY
if ( !!addr ) {
2005-08-21 16:07:29 +02:00
XP_ASSERT( globals->game.comms != NULL );
comms_setAddr( globals->game.comms, addr );
}
#endif
2003-11-20 17:26:35 +01:00
}
XP_ASSERT( !!globals->game.board );
(void)cePositionBoard( globals );
board_invalAll( globals->game.board );
InvalidateRect( globals->hWnd, NULL, TRUE /* erase */ );
#ifdef BEYOND_IR
if ( newGame && globals->gameInfo.serverRole == SERVER_ISCLIENT ) {
XWStreamCtxt* stream;
XP_ASSERT( !!globals->game.comms );
stream = make_generic_stream( globals );
stream_setOnCloseProc( stream, ce_send_on_close );
server_initClientConnection( globals->game.server, stream );
}
#endif
2003-11-20 17:26:35 +01:00
server_do( globals->game.server );
globals->isNewGame = FALSE;
} /* ceInitAndStartBoard */
#ifdef DEBUG
void
logLastError( const char* comment )
{
LPVOID lpMsgBuf;
DWORD lastErr = GetLastError();
XP_UCHAR msg[256];
XP_U16 len;
XP_U16 lenSoFar;
sprintf( msg, "%s (err: %d): ", comment, lastErr );
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
2003-11-20 17:26:35 +01:00
static void
ceSavePrefs( CEAppGlobals* globals )
{
HANDLE fileH;
fileH = CreateFile( PREFSFILENAME, GENERIC_WRITE, 0, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL );
2003-11-20 17:26:35 +01:00
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 {
logLastError( "failed to create prefs file" );
2003-11-20 17:26:35 +01:00
}
} /* 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 );
2003-11-20 17:26:35 +01:00
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" );
2003-11-20 17:26:35 +01:00
}
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 */
2003-11-20 17:26:35 +01:00
static XWStreamCtxt*
fileToStream( CEAppGlobals* globals, XP_UCHAR* path )
{
XWStreamCtxt* stream = NULL;
HANDLE fileH;
wchar_t widebuf[257];
XP_U16 len;
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 );
2003-11-20 17:26:35 +01:00
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 */
2003-11-20 17:26:35 +01:00
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 ) {
2004-01-17 23:10:23 +01:00
#ifdef STUBBED_DICT
XP_ASSERT(0); /* just don't do this!!!! */
#else
2003-11-20 17:26:35 +01:00
XP_UCHAR* name = stringFromStream( MPPARM(globals->mpool)
stream );
dict = ce_dictionary_make( globals, name );
success = dict != NULL;
2004-01-17 23:10:23 +01:00
#endif
2003-11-20 17:26:35 +01:00
} else {
dict = NULL;
}
if ( success ) {
XP_DEBUGF( "calling game_makeFromStream" );
game_makeFromStream( MEMPOOL stream, &globals->game,
&globals->gameInfo,
dict, &globals->util, globals->draw,
&globals->appPrefs.cp, CE_SEND_PROC, globals );
}
2003-11-20 17:26:35 +01:00
stream_destroy( stream );
}
return success;
} /* ceLoadSavedGame */
2004-03-28 03:14:34 +02:00
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 */
2003-11-20 17:26:35 +01:00
static void
ceInitPrefs( CEAppGlobals* globals )
{
globals->appPrefs.versionFlags = CUR_CE_PREFS_FLAGS;
2004-05-28 04:45:49 +02:00
globals->appPrefs.showColors = XP_TRUE;
2003-11-20 17:26:35 +01:00
globals->appPrefs.cp.showBoardArrow = XP_TRUE;
globals->appPrefs.cp.showRobotScores = XP_FALSE;
2004-03-28 03:14:34 +02:00
colorsFromRsrc( globals );
2003-11-20 17:26:35 +01:00
} /* 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;
2003-12-09 06:18:47 +01:00
XP_Bool oldGameLoaded;
2003-11-20 17:26:35 +01:00
XP_Bool prevStateExists;
2003-12-09 06:18:47 +01:00
XP_Bool newDone = XP_FALSE;
XP_U16 len;
2003-11-20 17:26:35 +01:00
MPSLOT;
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
XP_LOGF( "unable to laod winsock" );
}
}
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 );
2003-11-20 17:26:35 +01:00
#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 );
2003-11-20 17:26:35 +01:00
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;
}
#ifdef TARGET_OS_WINCE
2004-12-17 08:45:30 +01:00
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);
}
#endif
2005-11-28 00:11:05 +01:00
#ifdef TARGET_OS_WIN32
srand( time(NULL) );
#endif
2003-11-20 17:26:35 +01:00
ceInitUtilFuncs( globals );
/* choose one. If none found it's an error. */
2004-01-17 23:10:23 +01:00
#ifndef STUBBED_DICT
2003-11-20 17:26:35 +01:00
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;
}
2004-01-17 23:10:23 +01:00
#endif
gi_initPlayerInfo( MPPARM(mpool) &globals->gameInfo, "Player %d" );
2003-11-20 17:26:35 +01:00
/* 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 ) {
2003-11-20 17:26:35 +01:00
ceInitPrefs( globals );
2004-03-28 03:14:34 +02:00
}
/* must load prefs before creating draw ctxt */
2004-03-28 03:14:34 +02:00
globals->draw = ce_drawctxt_make( MPPARM(globals->mpool)
hWnd, globals );
2003-11-20 17:26:35 +01:00
oldGameLoaded = prevStateExists && ceLoadSavedGame( globals );
2004-03-28 03:14:34 +02:00
if ( !oldGameLoaded ) {
2005-01-21 09:26:32 +01:00
XP_U16 gameID = 0; /* good enough until I get networking going */
2003-11-20 17:26:35 +01:00
game_makeNewGame( MPPARM(mpool) &globals->game, &globals->gameInfo,
2005-01-21 09:26:32 +01:00
&globals->util, globals->draw, gameID,
&globals->appPrefs.cp,
CE_SEND_PROC, globals );
2003-11-20 17:26:35 +01:00
newDone = ceDoNewGame( globals, XP_TRUE ); /* calls ceInitAndStartBoard */
if ( !newDone ) {
result = FALSE;
2003-11-20 17:26:35 +01:00
}
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
#ifndef CANT_DO_CMDBAR
#ifdef TARGET_OS_WINCE
2003-11-20 17:26:35 +01:00
if (globals->hwndCB) {
CommandBar_Show(globals->hwndCB, TRUE);
}
#endif
#endif
2003-12-09 06:18:47 +01:00
if ( result && !newDone ) {
ceInitAndStartBoard( globals, !oldGameLoaded, NULL, NULL );
2003-11-20 17:26:35 +01:00
}
return result;
} /* InitInstance */
static XP_Bool
ceHandleHintRequest( CEAppGlobals* globals )
{
XP_Bool notDone;
XP_Bool draw;
XP_ASSERT( !!globals->game.board );
2004-12-11 05:25:45 +01:00
draw = board_requestHint( globals->game.board,
#ifdef XWFEATURE_SEARCHLIMIT
globals->askTrayLimits,
#endif
2004-07-10 19:46:36 +02:00
&notDone );
2003-11-20 17:26:35 +01:00
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 );
2003-11-20 17:26:35 +01:00
2004-12-18 02:03:26 +01:00
server_formatDictCounts( globals->game.server, stream, 3 );
2003-11-20 17:26:35 +01:00
(void)ceMsgFromStream( globals, stream, L"Tile Counts and Values",
XP_FALSE, XP_TRUE );
2003-11-20 17:26:35 +01:00
}
} /* 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 */
2003-11-20 17:26:35 +01:00
static void
ceDoHistory( CEAppGlobals* globals )
{
XP_Bool gameOver = server_getGameIsOver(globals->game.server);
XWStreamCtxt* stream;
stream = make_generic_stream( globals );
2003-11-20 17:26:35 +01:00
model_writeGameHistory( globals->game.model, stream,
globals->game.server, gameOver );
(void)ceMsgFromStream( globals, stream, L"Game history",
XP_FALSE, XP_TRUE );
2003-11-20 17:26:35 +01:00
} /* ceDoHistory */
static void
drawInsidePaint( HWND hWnd, CEAppGlobals* globals )
{
HDC hdc;
hdc = GetDC( hWnd );
if ( !hdc ) {
logLastError( __FUNCTION__ );
} else {
HDC prevHDC = globals->hdc;
globals->hdc = hdc;
2003-11-20 17:26:35 +01:00
board_draw( globals->game.board );
2003-11-20 17:26:35 +01:00
globals->hdc = prevHDC;
}
2003-11-20 17:26:35 +01:00
} /* drawInsidePaint */
static void
ceDisplayFinalScores( CEAppGlobals* globals )
{
XWStreamCtxt* stream;
stream = make_generic_stream( globals );
2003-11-20 17:26:35 +01:00
server_writeFinalScores( globals->game.server, stream );
stream_putU8( stream, '\0' );
(void)ceMsgFromStream( globals, stream, L"Final scores",
XP_FALSE, XP_TRUE );
2003-11-20 17:26:35 +01:00
} /* ceDisplayFinalScores */
static XP_Bool
ceDoNewGame( CEAppGlobals* globals, XP_Bool silent )
2003-11-20 17:26:35 +01:00
{
GameInfoState giState;
CommsAddrRec* addr = NULL;
2003-11-20 17:26:35 +01:00
XP_Bool changed = XP_FALSE;
/* What happens if user cancels below? I'm hosed without a name, no?
PENDING */
2003-11-20 17:26:35 +01:00
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;
DH(DialogBoxParam)( globals->hInst, (LPCTSTR)IDD_GAMEINFO, globals->hWnd,
(DLGPROC)GameInfo, (long)&giState );
2003-11-20 17:26:35 +01:00
2004-01-17 23:10:23 +01:00
if ( !giState.userCancelled
#ifndef STUBBED_DICT
&& ((XP_U16)XP_STRLEN(giState.newDictName) > 0)
#endif
) {
2003-11-20 17:26:35 +01:00
if ( giState.prefsChanged ) {
loadCurPrefsFromState( globals, &globals->appPrefs,
&globals->gameInfo, &giState.prefsPrefs );
if ( giState.colorsChanged ) {
updateForColors( globals );
}
2003-11-20 17:26:35 +01:00
}
#ifndef XWFEATURE_STANDALONE_ONLY
if ( giState.addrChanged ) {
addr = &giState.prefsPrefs.addrRec;
}
#endif
ceInitAndStartBoard( globals, XP_TRUE, NULL, addr );
2003-11-20 17:26:35 +01:00
changed = XP_TRUE;
}
return changed;
} /* ceDoNewGame */
2003-11-20 17:26:35 +01:00
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?" ) ) {
2003-11-20 17:26:35 +01:00
if ( globals->curGameName != NULL ) {
XP_FREE( globals->mpool, globals->curGameName );
}
globals->curGameName = name;
ceLoadSavedGame( globals );
ceInitAndStartBoard( globals, XP_FALSE, NULL, NULL );
2003-11-20 17:26:35 +01:00
ceSetTitleFromName( globals );
}
}
} /* ceChooseAndOpen */
static void
updateForColors( CEAppGlobals* globals )
{
ce_drawctxt_update( globals->draw, globals );
if ( !!globals->game.board ) {
board_invalAll( globals->game.board );
}
} /* updateForColors */
2003-11-20 17:26:35 +01:00
static void
ceDoPrefsDlg( CEAppGlobals* globals )
{
CePrefsDlgState state;
CePrefsPrefs prefsPrefs;
2003-11-20 17:26:35 +01:00
XP_MEMSET( &state, 0, sizeof(state) );
loadStateFromCurPrefs( globals, &globals->appPrefs, &globals->gameInfo,
2003-11-20 17:26:35 +01:00
&prefsPrefs );
(void)WrapPrefsDialog( globals->hWnd, globals, &state, &prefsPrefs,
XP_FALSE );
if ( !state.userCancelled ) {
loadCurPrefsFromState( globals, &globals->appPrefs, &globals->gameInfo,
2003-11-20 17:26:35 +01:00
&prefsPrefs );
(void)cePositionBoard( globals );
if ( state.colorsChanged ) {
updateForColors( globals );
}
2003-11-20 17:26:35 +01:00
/* 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 )
2003-11-20 17:26:35 +01:00
{
return 0 == XP_STRCMP( UNSAVEDGAMEFILENAME, name );
} /* isDefaultName */
2003-11-20 17:26:35 +01:00
2004-02-18 05:25:29 +01:00
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 */
2003-11-20 17:26:35 +01:00
static XP_Bool
ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave )
2003-11-20 17:26:35 +01:00
{
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) ) {
2003-11-20 17:26:35 +01:00
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 {
2003-11-20 17:26:35 +01:00
OPENFILENAME sfs;
2003-11-20 17:26:35 +01:00
XP_MEMSET( &sfs, 0, sizeof(sfs) );
XP_MEMSET( nameBuf, 0, sizeof(nameBuf) );
2003-11-20 17:26:35 +01:00
2004-02-18 05:25:29 +01:00
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 );
}
}
}
2003-11-20 17:26:35 +01:00
if ( confirmed ) {
XP_ASSERT( !!newName );
if ( !!globals->curGameName ) {
XP_FREE( globals->mpool, globals->curGameName );
}
globals->curGameName = newName;
2003-11-20 17:26:35 +01:00
}
} 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;
#ifndef CE_DEBUG_SAVEOFTEN
2003-11-20 17:26:35 +01:00
board_hideTray( globals->game.board ); /* so won't be visible when
next opened */
#endif
2003-11-20 17:26:35 +01:00
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
2003-11-20 17:26:35 +01:00
dictName = !!dict? dict_getName( dict ) : NULL;
#endif
2003-11-20 17:26:35 +01:00
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 );
2003-11-20 17:26:35 +01:00
if ( confirmed ) {
ceSavePrefs( globals );
}
return confirmed;
} /* ceConfirmAndSave */
2004-12-17 08:45:30 +01:00
static HWND
makeCommandBar( HWND hwnd, HINSTANCE hInst )
{
#ifndef CANT_DO_SHELL_THING
2004-12-17 08:45:30 +01:00
SHMENUBARINFO mbi;
XP_MEMSET( &mbi, 0, sizeof(SHMENUBARINFO) );
mbi.cbSize = sizeof(SHMENUBARINFO);
mbi.hwndParent = hwnd;
mbi.nToolBarId = IDM_MENU;
mbi.hInstRes = hInst;
//mbi.dwFlags = SHCMBF_HIDESIPBUTTON; /* eeh added. Why??? */
2004-12-17 08:45:30 +01:00
if (!SHCreateMenuBar(&mbi)) {
XP_LOGF( "SHCreateMenuBar failed" );
return NULL;
}
return mbi.hwndMB;
#else
return NULL;
#endif
2004-12-17 08:45:30 +01:00
} /* makeCommandBar */
#ifdef CEFEATURE_CANSCROLL
static XP_Bool
handleScroll( CEAppGlobals* globals, XP_S16 pos, /* only valid for THUMB* */
XP_S16 code, HWND wnd )
{
XP_Bool result = XP_FALSE;
if ( wnd == globals->scrollHandle ) {
XP_S16 newOffset;
XP_U16 curYOffset = board_getYOffset( globals->game.board );
XP_ASSERT( !!globals->game.board );
switch ( code ) {
/* case SB_BOTTOM: // Scrolls to the lower right */
/* case SB_ENDSCROLL: // Ends scroll */
case SB_LINEUP: // Scrolls one line up
case SB_PAGEUP: //
newOffset = curYOffset - 1;
break;
case SB_LINEDOWN: // Scrolls one line down
case SB_PAGEDOWN: // Scrolls one page down
newOffset = curYOffset + 1;
break;
case SB_THUMBTRACK: /* still dragging; don't redraw */
case SB_THUMBPOSITION:
XP_LOGF( "Got scroll message: %d", pos );
newOffset = pos;
break;
default:
newOffset = -1;
/* do nothing */
}
if ( newOffset >= 0 && newOffset <= globals->nHiddenRows ) {
result = curYOffset != newOffset;
if ( result ) {
result = board_setYOffset( globals->game.board, newOffset );
}
}
}
return result;
} /* handleScroll */
#endif
#ifdef CE_DEBUG_SAVEOFTEN
static void
debug_saveCurState( CEAppGlobals* globals )
{
ceSaveCurGame( globals, XP_TRUE );
ceSavePrefs( globals );
}
#else
# define debug_saveCurState(g)
#endif
2005-06-23 16:09:20 +02:00
static void
ceFireTimer( CEAppGlobals* globals, XWTimerReason why )
{
2005-08-21 16:07:29 +02:00
TimerProc proc;
void* closure;
proc = globals->timerProcs[why];
closure = globals->timerClosures[why];
2005-06-23 16:09:20 +02:00
(*proc)( closure, why );
2005-08-21 16:07:29 +02:00
} /* ceFireTimer */
2005-06-23 16:09:20 +02:00
#ifndef XWFEATURE_STANDALONE_ONLY
static XP_Bool
processPacket( CEAppGlobals* globals, XWStreamCtxt* instream )
{
XP_Bool draw = XP_FALSE;
XP_ASSERT( globals->game.comms != NULL );
if ( comms_checkIncomingStream( globals->game.comms,
instream, NULL ) ) {
draw = server_receiveMessage( globals->game.server, instream );
}
stream_destroy( instream );
ce_util_requestTime( &globals->util );
return draw;
} /* processPacket */
#endif
2003-11-20 17:26:35 +01:00
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;
2003-11-20 17:26:35 +01:00
if ( message == WM_CREATE ) {
globals = ((CREATESTRUCT*)lParam)->lpCreateParams;
SetWindowLong( hWnd, GWL_USERDATA, (long)globals );
#ifdef TARGET_OS_WINCE
2004-12-17 08:45:30 +01:00
globals->hwndCB = makeCommandBar( hWnd, globals->hInst );
2003-11-20 17:26:35 +01:00
addButtonsToCmdBar( globals );
#endif
#ifndef CANT_DO_SHELL_THING
globals->sai.cbSize = sizeof(globals->sai);
#endif
2003-11-20 17:26:35 +01:00
} else {
globals = (CEAppGlobals*)GetWindowLong( hWnd, GWL_USERDATA );
switch (message) {
2004-12-17 08:45:30 +01:00
#ifndef CANT_DO_SHELL_THING
2004-12-17 08:45:30 +01:00
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 );
cePositionBoard( globals );
board_invalAll( globals->game.board );
draw = XP_TRUE;
2004-12-17 08:45:30 +01:00
break;
#endif
2004-12-17 08:45:30 +01:00
#ifdef CEFEATURE_CANSCROLL
case WM_VSCROLL:
draw = handleScroll( globals, (short int)HIWORD(wParam),
(short int)LOWORD(wParam),
(HWND)lParam );
break;
#endif
2003-11-20 17:26:35 +01:00
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case ID_FILE_ABOUT:
DH(DialogBoxParam)(globals->hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd,
(DLGPROC)About, 0L );
2003-11-20 17:26:35 +01:00
break;
case ID_GAME_GAMEINFO: {
GameInfoState state;
XP_MEMSET( &state, 0, sizeof(state) );
state.globals = globals;
state.isNewGame = XP_FALSE;
DH(DialogBoxParam)(globals->hInst, (LPCTSTR)IDD_GAMEINFO, hWnd,
(DLGPROC)GameInfo, (long)&state );
2003-11-20 17:26:35 +01:00
if ( !state.userCancelled && state.prefsChanged ) {
/* need to update some prefs? */
/* if ( state.colorsChanged ) { */
updateForColors( globals );
/* } */
2003-11-20 17:26:35 +01:00
}
}
2003-11-20 17:26:35 +01:00
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 = ceDoNewGame( globals, XP_FALSE );
}
2003-11-20 17:26:35 +01:00
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;
2003-11-20 17:26:35 +01:00
case ID_GAME_HISTORY:
ceDoHistory( globals );
break;
case ID_MOVE_TRADE:
draw = handleTradeCmd( globals );
break;
case ID_MOVE_JUGGLE:
case JUGGLE_BUTTON_ID:
2003-11-20 17:26:35 +01:00
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:
2004-07-10 19:46:36 +02:00
#ifdef XWFEATURE_SEARCHLIMIT
case ID_MOVE_LIMITEDHINT:
globals->askTrayLimits = wmId == ID_MOVE_LIMITEDHINT;
#endif
2003-11-20 17:26:35 +01:00
board_resetEngine( globals->game.board );
2004-07-10 19:46:36 +02:00
/* fallthru */
2003-11-20 17:26:35 +01:00
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" );
}
2003-11-20 17:26:35 +01:00
}
break;
case WM_LBUTTONDOWN:
globals->penDown = XP_TRUE;
draw = board_handlePenDown( globals->game.board, LOWORD(lParam),
2005-07-14 04:54:47 +02:00
HIWORD(lParam), &handled );
debug_saveCurState( globals );
2003-11-20 17:26:35 +01:00
break;
case WM_MOUSEMOVE:
if ( globals->penDown ) {
draw = board_handlePenMove( globals->game.board,
LOWORD(lParam),
HIWORD(lParam) );
debug_saveCurState( globals );
2003-11-20 17:26:35 +01:00
}
break;
case WM_LBUTTONUP:
if ( globals->penDown ) {
draw = board_handlePenUp( globals->game.board, LOWORD(lParam),
HIWORD(lParam), 0 );
globals->penDown = XP_FALSE;
debug_saveCurState( globals );
2003-11-20 17:26:35 +01:00
}
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') );
debug_saveCurState( globals );
2003-11-20 17:26:35 +01:00
break;
case WM_TIMER:
why = (XWTimerReason)wParam;
2005-07-13 14:35:38 +02:00
if ( why == TIMER_PENDOWN || why == TIMER_TIMERTICK
#ifdef BEYOND_IR
|| why == TIMER_HEARTBEAT
#endif
) {
2005-08-21 16:07:29 +02:00
XP_ASSERT( why < NUM_TIMERS_PLUS_ONE );
2005-06-23 16:09:20 +02:00
/* Kill since they otherwise repeat, but kill before firing
as fired proc may set another. */
2005-08-21 16:07:29 +02:00
(void)KillTimer( hWnd, globals->timerIDs[why] );
2005-06-23 16:09:20 +02:00
ceFireTimer( globals, why );
2003-11-20 17:26:35 +01:00
}
break;
case WM_DESTROY:
#ifndef CANT_DO_CMDBAR
#ifdef TARGET_OS_WINCE
CommandBar_Destroy(globals->hwndCB); /* supposedly not needed */
#endif
#endif
2003-11-20 17:26:35 +01:00
PostQuitMessage(0);
break;
case XWWM_TIME_RQST:
draw = server_do( globals->game.server );
2003-11-20 17:26:35 +01:00
break;
#ifndef XWFEATURE_STANDALONE_ONLY
case XWWM_PACKET_ARRIVED:
draw = processPacket( globals, (XWStreamCtxt*)lParam );
break;
#endif
2003-11-20 17:26:35 +01:00
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 */ );
2003-11-20 17:26:35 +01:00
}
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
2003-11-20 17:26:35 +01:00
ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream,
wchar_t* title, XP_Bool isQuery, XP_Bool destroy )
2003-11-20 17:26:35 +01:00
{
StrBoxInit init;
init.title = title;
init.stream = stream;
init.isQuery = isQuery;
2003-11-20 17:26:35 +01:00
init.globals = globals;
DH(DialogBoxParam)( globals->hInst, (LPCTSTR)IDD_STRBOX, globals->hWnd,
(DLGPROC)StrBox, (long)&init );
2003-11-20 17:26:35 +01:00
if ( destroy ) {
stream_destroy( stream );
}
return init.result == IDOK;
2003-11-20 17:26:35 +01:00
} /* 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 */
2003-11-20 17:26:35 +01:00
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 */
#ifdef DEBUG
2003-11-20 17:26:35 +01:00
static void
makeTimeStamp( XP_UCHAR* timeStamp, XP_U16 size )
{
SYSTEMTIME st;
DWORD tid;
tid = DH(GetCurrentThreadId)();
2003-11-20 17:26:35 +01:00
GetLocalTime( &st );
sprintf( timeStamp, "<%lx>%d:%.2d:%.2d ", tid, st.wHour, st.wMinute,
st.wSecond );
2003-11-20 17:26:35 +01:00
XP_ASSERT( size > strlen(timeStamp) );
} /* makeTimeStamp */
void
wince_debugf(XP_UCHAR* format, ...)
{
#ifdef BEYOND_IR
static HANDLE s_logMutex = NULL;
DWORD wres;
#endif
2003-11-20 17:26:35 +01:00
XP_UCHAR buf[256];
XP_UCHAR timeStamp[32];
XP_U16 nBytes;
XP_U32 nWritten;
HANDLE fileH;
2003-11-20 17:26:35 +01:00
va_list ap;
wchar_t* logFileName;
2003-11-20 17:26:35 +01:00
va_start( ap, format );
vsprintf( buf, format, ap );
va_end(ap);
/* Create logfile if necessary and write to it in ascii. If there are
multiple threads, protect with mutex. */
2003-11-20 17:26:35 +01:00
#ifdef BEYOND_IR
if ( s_logMutex == NULL ) {
s_logMutex = CreateMutex( NULL, FALSE, NULL );
}
if ( WaitForSingleObject( s_logMutex, 200L ) == WAIT_OBJECT_0 ) {
#endif
2003-11-20 17:26:35 +01:00
makeTimeStamp(timeStamp, sizeof(timeStamp));
#if defined TARGET_OS_WINCE
logFileName = L"\\My Documents\\Crosswords\\xwDbgLog.txt";
#elif defined TARGET_OS_WIN32
logFileName = L"xwDbgLog.txt";
#endif
fileH = CreateFile( logFileName,
GENERIC_WRITE, 0, NULL,
2003-11-20 17:26:35 +01:00
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 );
#ifdef DEBUG_TS
2003-11-20 17:26:35 +01:00
nBytes = strlen( timeStamp );
WriteFile( fileH, timeStamp, nBytes, &nWritten, NULL );
#endif
2003-11-20 17:26:35 +01:00
nBytes = strlen( buf );
WriteFile( fileH, buf, nBytes, &nWritten, NULL );
CloseHandle( fileH );
#ifdef BEYOND_IR
ReleaseMutex( s_logMutex );
2003-11-20 17:26:35 +01:00
}
#endif
} /* wince_debugf */
#endif
2003-11-20 17:26:35 +01:00
XP_U16
wince_snprintf( XP_UCHAR* buf, XP_U16 len, XP_UCHAR* format, ... )
{
va_list ap;
va_start( ap, format );
2004-12-18 02:03:26 +01:00
_vsnprintf( buf, len, format, ap );
2003-11-20 17:26:35 +01:00
/* FormatMessage( */
/* FORMAT_MESSAGE_FROM_STRING, */
/* format, */
/* 0, */
/* 0, // Default language */
/* (LPTSTR)buf, */
/* len, &ap ); */
va_end(ap);
return strlen(buf);
} /* wince_snprintf */
#ifndef XWFEATURE_STANDALONE_ONLY
static void
got_data_proc( XP_U8* data, XP_U16 len, void* closure )
{
/* Remember that this gets called by the reader thread, not by the one
running the window loop. */
CEAppGlobals* globals = (CEAppGlobals*)closure;
BOOL posted;
XWStreamCtxt* stream;
stream = make_generic_stream( globals );
stream_putBytes( stream, data, len );
posted = PostMessage( globals->hWnd, XWWM_PACKET_ARRIVED,
0, (DWORD)stream );
XP_ASSERT( posted );
} /* got_data_proc */
static XP_S16
ce_send_proc( XP_U8* buf, XP_U16 len, const CommsAddrRec* addr, void* closure )
{
CEAppGlobals* globals = (CEAppGlobals*)closure;
XP_LOGF( "ce_send_proc called" );
if ( !globals->socketWrap ) {
globals->socketWrap = ce_sockwrap_new( MPPARM(globals->mpool)
got_data_proc, globals );
}
return ce_sockwrap_send( globals->socketWrap, buf, len, addr );
} /* ce_send_proc */
static void
ce_send_on_close( XWStreamCtxt* stream, void* closure )
{
CEAppGlobals* globals = (CEAppGlobals*)closure;
XP_ASSERT( !!globals->game.comms );
comms_send( globals->game.comms, stream );
}
#endif
2003-11-20 17:26:35 +01:00
/* 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;
2004-05-15 03:07:34 +02:00
case ERR_CANT_UNDO_TILEASSIGN:
message = "Tile assignment can't be undone.";
break;
2005-09-07 14:31:32 +02:00
case ERR_RELAY_BASE + XWRELAY_ERROR_TIMEOUT:
message = "The relay timed you out; usually that means "
"the other players didn't show.";
break;
case ERR_RELAY_BASE + XWRELAY_ERROR_HEART_YOU:
message = "You were disconnected from relay because it didn't "
"hear from you in too long.";
break;
case ERR_RELAY_BASE + XWRELAY_ERROR_HEART_OTHER:
case ERR_RELAY_BASE + XWRELAY_ERROR_LOST_OTHER:
message = "The relay has lost contact with a device in this game.";
break;
2003-11-20 17:26:35 +01:00
default:
message = "unknown errorcode ID!!!";
break;
}
messageBoxChar( globals, message, L"Oops!" );
} /* ce_util_userError */
static XP_Bool
2003-11-20 17:26:35 +01:00
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;
2003-11-20 17:26:35 +01:00
XP_Bool queryWithStream = XP_FALSE;
switch( id ) {
case QUERY_COMMIT_TURN:
return ceQueryFromStream( globals, stream );
2003-11-20 17:26:35 +01:00
case QUERY_COMMIT_TRADE:
query = "Are you sure you want to spend this move trading tiles?";
return queryBoxChar( globals, query );
2003-11-20 17:26:35 +01:00
case QUERY_ROBOT_MOVE:
return ceMsgFromStream( globals, stream, L"FYI", XP_FALSE,
XP_FALSE );
case QUERY_ROBOT_TRADE:
messageBoxStream( globals, stream, L"FYI" );
2003-11-20 17:26:35 +01:00
break;
default:
XP_ASSERT(0);
}
return XP_FALSE;
2003-11-20 17:26:35 +01:00
} /* 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 );
}
}
}
2003-11-20 17:26:35 +01:00
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;
2003-11-20 17:26:35 +01:00
} 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;
2003-11-20 17:26:35 +01:00
}
} /* ce_util_getSquareBonus */
static XP_S16
ce_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
2003-11-20 17:26:35 +01:00
XP_U16 playerNum,
const XP_UCHAR4* texts, XP_U16 nTiles )
2003-11-20 17:26:35 +01:00
{
BlankDialogState state;
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
XP_MEMSET( &state, 0, sizeof(state) );
2003-12-09 06:18:47 +01:00
state.globals = globals;
2003-11-20 17:26:35 +01:00
state.texts = texts;
state.nTiles = nTiles;
state.playerNum = playerNum;
state.pi = pi;
DH(DialogBoxParam)( globals->hInst, (LPCTSTR)IDD_ASKBLANK, globals->hWnd,
(DLGPROC)BlankDlg, (long)&state );
2003-11-20 17:26:35 +01:00
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;
DH(DialogBoxParam)( globals->hInst, (LPCTSTR)IDD_ASKPASS, globals->hWnd,
(DLGPROC)PasswdDlg, (long)&state );
2003-11-20 17:26:35 +01:00
return !state.userCancelled;
} /* ce_util_askPassword */
static void
ce_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState newState )
2003-11-20 17:26:35 +01:00
{
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
#ifdef CEFEATURE_CANSCROLL
/* If there's a scrollbar, hide/show it. It wants to be
active/visible only when the tray is NOT hidden */
if ( !!globals->scrollHandle ) {
EnableWindow( globals->scrollHandle, TRAY_HIDDEN != newState );
}
#endif
drawInsidePaint( globals->hWnd, globals );
2003-11-20 17:26:35 +01:00
} /* ce_util_trayHiddenChange */
static void
ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset,
XP_U16 newOffset )
{
#ifdef CEFEATURE_CANSCROLL
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
(void)SetScrollPos( globals->scrollHandle, SB_CTL, newOffset, XP_TRUE );
#endif
2003-11-20 17:26:35 +01:00
} /* 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
2005-06-23 16:09:20 +02:00
ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when,
TimerProc proc, void* closure)
2003-11-20 17:26:35 +01:00
{
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
2004-06-16 06:16:22 +02:00
XP_U32 timerID;
XP_U32 howLong;
2005-08-21 16:07:29 +02:00
XP_ASSERT( why < NUM_TIMERS_PLUS_ONE );
globals->timerProcs[why] = proc;
globals->timerClosures[why] = closure;
2005-06-23 16:09:20 +02:00
switch ( why ) {
case TIMER_PENDOWN:
howLong = 400;
2005-06-23 16:09:20 +02:00
break;
case TIMER_TIMERTICK:
howLong = 1000; /* 1 second */
2005-06-23 16:09:20 +02:00
break;
2005-07-13 14:35:38 +02:00
#ifdef BEYOND_IR
2005-06-23 16:09:20 +02:00
case TIMER_HEARTBEAT:
howLong = when * 1000;
break;
#endif
default:
2004-06-16 06:16:22 +02:00
XP_ASSERT(0);
return;
2003-11-20 17:26:35 +01:00
}
timerID = SetTimer( globals->hWnd, why, howLong, NULL);
2005-08-21 16:07:29 +02:00
globals->timerIDs[why] = timerID;
2003-11-20 17:26:35 +01:00
} /* 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;
2004-01-17 23:10:23 +01:00
#ifdef STUBBED_DICT
return make_stubbed_dict( MPPARM_NOCOMMA(globals->mpool) );
#else
return ce_dictionary_make_empty( globals );
2004-01-17 23:10:23 +01:00
#endif
2003-11-20 17:26:35 +01:00
} /* ce_util_makeEmptyDict */
#ifdef BEYOND_IR
2003-11-20 17:26:35 +01:00
static XWStreamCtxt*
ce_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_U16 channelNo )
{
XWStreamCtxt* stream;
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
stream = make_generic_stream( globals );
stream_setOnCloseProc( stream, ce_send_on_close );
stream_setAddress( stream, channelNo );
return stream;
2003-11-20 17:26:35 +01:00
} /* 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;
2004-05-01 16:30:34 +02:00
case STR_LOCAL_NAME:
return (XP_UCHAR*)"%s";
2003-11-20 17:26:35 +01:00
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;
2004-05-13 04:13:48 +02:00
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;
2003-11-20 17:26:35 +01:00
default:
2004-05-01 16:30:34 +02:00
XP_LOGF( "stringCode=%d", stringCode );
2003-11-20 17:26:35 +01:00
return (XP_UCHAR*)"unknown code";
}
} /* ce_util_getUserString */
static void
ce_formatBadWords( BadWordInfo* bwi, XP_UCHAR buf[], XP_U16 bufsiz )
2003-11-20 17:26:35 +01:00
{
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 );
2003-11-20 17:26:35 +01:00
if ( ++i == bwi->nWords ) {
break;
}
bufsiz -= strlen( wordBuf );
strncat( buf, ", ", bufsiz - 1 );
bufsiz -= 2;
2003-11-20 17:26:35 +01:00
}
} /* 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) );
2003-11-20 17:26:35 +01:00
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 BEYOND_IR
static void
ce_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr,
const CommsAddrRec* newAddr )
{
XP_LOGF( "ce_util_addrChange called; DO SOMETHING." );
} /* ce_util_addrChange */
#endif
2004-07-10 19:46:36 +02:00
#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;
DH(DialogBoxParam)( globals->hInst, (LPCTSTR)IDD_ASKHINTLIMTS, globals->hWnd,
(DLGPROC)HintLimitsDlg, (long)&hls );
2004-07-10 19:46:36 +02:00
if ( !hls.cancelled ) {
*min = hls.min;
*max = hls.max;
}
return !hls.cancelled;
} /* ce_util_getTraySearchLimits */
#endif
2003-11-20 17:26:35 +01:00
#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