From 92eb6b857de33c728c3490564ed41f531a0f0f29 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Fri, 11 Nov 2011 07:41:31 -0800 Subject: [PATCH 01/11] allow null info ptr so getInfo can be used solely to run sanity check on dict. --- xwords4/android/XWords4/jni/xwjni.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xwords4/android/XWords4/jni/xwjni.c b/xwords4/android/XWords4/jni/xwjni.c index fe43f6276..70be75e4c 100644 --- a/xwords4/android/XWords4/jni/xwjni.c +++ b/xwords4/android/XWords4/jni/xwjni.c @@ -280,8 +280,10 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jniutil, NULL, jDictBytes, jpath, NULL, check ); if ( NULL != dict ) { - setInt( env, jinfo, "langCode", dict_getLangCode( dict ) ); - setInt( env, jinfo, "wordCount", dict_getWordCount( dict ) ); + if ( NULL != jinfo ) { + setInt( env, jinfo, "langCode", dict_getLangCode( dict ) ); + setInt( env, jinfo, "wordCount", dict_getWordCount( dict ) ); + } dict_destroy( dict ); result = true; } From 9ff45966671db7dea1a92e38a00676e96fa46ebf Mon Sep 17 00:00:00 2001 From: Andy2 Date: Fri, 11 Nov 2011 07:42:00 -0800 Subject: [PATCH 02/11] fix compile warning by passing ptr to embedded super --- xwords4/android/XWords4/jni/anddict.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/jni/anddict.c b/xwords4/android/XWords4/jni/anddict.c index c04c90a5a..0e1e1a4ad 100644 --- a/xwords4/android/XWords4/jni/anddict.c +++ b/xwords4/android/XWords4/jni/anddict.c @@ -564,7 +564,7 @@ makeDict( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, jstring jname, XP_U32 numEdges; XP_Bool parses = parseDict( anddict, (XP_U8*)anddict->bytes, len, &numEdges ); - if ( !parses || (check && !checkSanity( anddict, numEdges ) ) ) { + if ( !parses || (check && !checkSanity( &anddict->super, numEdges ) ) ) { and_dictionary_destroy( (DictionaryCtxt*)anddict ); anddict = NULL; } From b334e688bb8f9e1a6c24ef7d727eb8b33284a4fc Mon Sep 17 00:00:00 2001 From: Andy2 Date: Fri, 11 Nov 2011 07:45:25 -0800 Subject: [PATCH 03/11] use getInfo to run sanity check on .xwd files in download directory. This prevents deliberately corrupted files from making it into dict lists. To the extent that checkSanity() works, it should make use of that dir safe. --- .../src/org/eehouse/android/xw4/DictUtils.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java index d087abcf9..556aa4379 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictUtils.java @@ -121,7 +121,7 @@ public class DictUtils { String[] list = dir.list(); if ( null != list ) { for ( String file : list ) { - if ( isDict( file, strict ) ) { + if ( isDict( file, strict? dir : null ) ) { al.add( new DictAndLoc( removeDictExtn( file ), loc ) ); } } @@ -135,14 +135,14 @@ public class DictUtils { ArrayList al = new ArrayList(); for ( String file : getAssets( context ) ) { - if ( isDict( file, false ) ) { + if ( isDict( file, null ) ) { al.add( new DictAndLoc( removeDictExtn( file ), DictLoc.BUILT_IN ) ); } } for ( String file : context.fileList() ) { - if ( isDict( file, false ) ) { + if ( isDict( file, null ) ) { al.add( new DictAndLoc( removeDictExtn( file ), DictLoc.INTERNAL ) ); } @@ -464,11 +464,13 @@ public class DictUtils { return file.endsWith( XWConstants.GAME_EXTN ); } - private static boolean isDict( String file, boolean strictTest ) + private static boolean isDict( String file, File dir ) { boolean ok = file.endsWith( XWConstants.DICT_EXTN ); - if ( ok && strictTest ) { - Utils.logf( "isDict: not really checking %s yet", file ); + if ( ok && null != dir ) { + String fullPath = new File( dir, file ).getPath(); + ok = XwJNI.dict_getInfo( null, fullPath, JNIUtilsImpl.get(), + true, null ); } return ok; } From 44c5500ab0e62aa21fc18052dde223afb3c3702f Mon Sep 17 00:00:00 2001 From: Andy2 Date: Fri, 11 Nov 2011 18:29:26 -0800 Subject: [PATCH 04/11] don't crash when empty or corrupt dict file passed in. --- xwords4/android/XWords4/jni/anddict.c | 69 ++++++++++++++------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/xwords4/android/XWords4/jni/anddict.c b/xwords4/android/XWords4/jni/anddict.c index 0e1e1a4ad..8cd53e6a3 100644 --- a/xwords4/android/XWords4/jni/anddict.c +++ b/xwords4/android/XWords4/jni/anddict.c @@ -439,9 +439,9 @@ and_dictionary_destroy( DictionaryCtxt* dict ) XP_FREE( ctxt->super.mpool, ctxt->super.bitmaps ); } - XP_FREE( ctxt->super.mpool, ctxt->super.faces ); - XP_FREE( ctxt->super.mpool, ctxt->super.facePtrs ); - XP_FREE( ctxt->super.mpool, ctxt->super.countsAndValues ); + XP_FREEP( ctxt->super.mpool, &ctxt->super.faces ); + XP_FREEP( ctxt->super.mpool, &ctxt->super.facePtrs ); + XP_FREEP( ctxt->super.mpool, &ctxt->super.countsAndValues ); XP_FREEP( ctxt->super.mpool, &ctxt->super.name ); XP_FREEP( ctxt->super.mpool, &ctxt->super.langName ); @@ -456,7 +456,7 @@ and_dictionary_destroy( DictionaryCtxt* dict ) (*env)->DeleteGlobalRef( env, ctxt->byteArray ); } XP_FREE( ctxt->super.mpool, ctxt ); -} +} /* and_dictionary_destroy */ jobject and_dictionary_getChars( JNIEnv* env, DictionaryCtxt* dict ) @@ -524,49 +524,54 @@ DictionaryCtxt* makeDict( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, jstring jname, jbyteArray jbytes, jstring jpath, jstring jlangname, jboolean check ) { - AndDictionaryCtxt* anddict = (AndDictionaryCtxt*) - and_dictionary_make_empty( MPPARM(mpool) env, jniutil ); - - jsize len = 0; + jbyte* bytes = NULL; + jbyteArray byteArray = NULL; + off_t bytesSize = 0; if ( NULL == jpath ) { - len = (*env)->GetArrayLength( env, jbytes ); - anddict->byteArray = (*env)->NewGlobalRef( env, jbytes ); - anddict->bytes = - (*env)->GetByteArrayElements( env, anddict->byteArray, NULL ); + bytesSize = (*env)->GetArrayLength( env, jbytes ); + byteArray = (*env)->NewGlobalRef( env, jbytes ); + bytes = (*env)->GetByteArrayElements( env, byteArray, NULL ); } else { - XP_ASSERT( NULL == anddict->byteArray ); const char* path = (*env)->GetStringUTFChars( env, jpath, NULL ); struct stat statbuf; - if ( 0 == stat( path, &statbuf ) ) { - int fd = open( path, O_RDONLY ); + if ( 0 == stat( path, &statbuf ) && 0 < statbuf.st_size ) { + int fd = open( path, O_RDONLY ); if ( fd >= 0 ) { - anddict->bytes = mmap( NULL, statbuf.st_size, - PROT_READ, MAP_PRIVATE, - fd, 0 ); + void* ptr = mmap( NULL, statbuf.st_size, PROT_READ, + MAP_PRIVATE, fd, 0 ); close( fd ); - - anddict->bytesSize = statbuf.st_size; - len = statbuf.st_size; - XP_ASSERT( MAP_FAILED != anddict->bytes ); + if ( MAP_FAILED != ptr ) { + bytes = ptr; + bytesSize = statbuf.st_size; + } } } (*env)->ReleaseStringUTFChars( env, jpath, path ); } - anddict->super.destructor = and_dictionary_destroy; + AndDictionaryCtxt* anddict = NULL; + if ( NULL != bytes ) { + anddict = (AndDictionaryCtxt*) + and_dictionary_make_empty( MPPARM(mpool) env, jniutil ); + anddict->bytes = bytes; + anddict->byteArray = byteArray; + anddict->bytesSize = bytesSize; - /* copy the name */ - anddict->super.name = getStringCopy( MPPARM(mpool) env, jname ); - anddict->super.langName = getStringCopy( MPPARM(mpool) env, jlangname ); + anddict->super.destructor = and_dictionary_destroy; - XP_U32 numEdges; - XP_Bool parses = parseDict( anddict, (XP_U8*)anddict->bytes, - len, &numEdges ); - if ( !parses || (check && !checkSanity( &anddict->super, numEdges ) ) ) { - and_dictionary_destroy( (DictionaryCtxt*)anddict ); - anddict = NULL; + /* copy the name */ + anddict->super.name = getStringCopy( MPPARM(mpool) env, jname ); + anddict->super.langName = getStringCopy( MPPARM(mpool) env, jlangname ); + + XP_U32 numEdges; + XP_Bool parses = parseDict( anddict, (XP_U8*)anddict->bytes, + bytesSize, &numEdges ); + if ( !parses || (check && !checkSanity( &anddict->super, numEdges ) ) ) { + and_dictionary_destroy( (DictionaryCtxt*)anddict ); + anddict = NULL; + } } return (DictionaryCtxt*)anddict; From 4edca4ee4cdab5c2191c5d0e4c9345f25c5656e9 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Fri, 11 Nov 2011 18:30:53 -0800 Subject: [PATCH 05/11] Check for null return from getInfo(), the result when it tries to open a corrupt dict. Such dicts should never get this far, but it doesn't hurt to be safe. --- .../eehouse/android/xw4/DictLangCache.java | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java index 90ae05659..3589e1b7b 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictLangCache.java @@ -64,18 +64,19 @@ public class DictLangCache { public static String annotatedDictName( Context context, DictAndLoc dal ) { + String result = null; DictInfo info = getInfo( context, dal ); - int wordCount = info.wordCount; + if ( null != info ) { + int wordCount = info.wordCount; - String langName = getLangName( context, dal.name ); - String result; - if ( 0 == wordCount ) { - result = String.format( "%s (%s)", dal.name, langName ); - } else { - result = String.format( "%s (%s/%d)", dal.name, langName, - wordCount ); + String langName = getLangName( context, dal.name ); + if ( 0 == wordCount ) { + result = String.format( "%s (%s)", dal.name, langName ); + } else { + result = String.format( "%s (%s/%d)", dal.name, langName, + wordCount ); + } } - return result; } @@ -114,7 +115,7 @@ public class DictLangCache { DictAndLoc[] dals = DictUtils.dictList( context ); for ( DictAndLoc dal : dals ) { DictInfo info = getInfo( context, dal ); - if ( code == info.langCode ) { + if ( null != info && code == info.langCode ) { al.add( info ); } } @@ -159,7 +160,7 @@ public class DictLangCache { DictAndLoc[] dals = DictUtils.dictList( context ); for ( DictAndLoc dal : dals ) { DictInfo info = getInfo( context, dal ); - if ( code == info.langCode ) { + if ( null != info && code == info.langCode ) { al.add( dal ); } } @@ -373,6 +374,12 @@ public class DictLangCache { info = new DictInfo(); + // It should not be possible for dict_getInfo to fail + // unless DictUtils.dictList() isn't doing its job and + // puts unchecked dicts on the list. Should probably + // assert that this returns true. Open question: do I + // always trust dicts in the BUILTIN and INTERNAL + // locations? Files can get damaged.... if ( XwJNI.dict_getInfo( pairs.m_bytes[0], pairs.m_paths[0], JNIUtilsImpl.get(), DictUtils.DictLoc.DOWNLOAD == dal.loc, From d6fecaa3d3ed44a0b2203b9700ced775094f36a8 Mon Sep 17 00:00:00 2001 From: Eric House Date: Sun, 13 Nov 2011 17:06:57 -0800 Subject: [PATCH 06/11] don't crash when dict is empty file --- xwords4/linux/linuxdict.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xwords4/linux/linuxdict.c b/xwords4/linux/linuxdict.c index 3b9e48a76..4d065b4a6 100644 --- a/xwords4/linux/linuxdict.c +++ b/xwords4/linux/linuxdict.c @@ -1,6 +1,6 @@ -/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */ +/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */ /* - * Copyright 1997-2009 by Eric House (xwords@eehouse.org). All rights + * Copyright 1997 - 2011 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -219,7 +219,7 @@ initFromDictFile( LinuxDictionaryCtxt* dctx, const char* fileName ) const XP_U8* ptr; struct stat statbuf; - if ( 0 != stat( fileName, &statbuf ) ) { + if ( 0 != stat( fileName, &statbuf ) || 0 == statbuf.st_size ) { goto closeAndExit; } dctx->dictLength = statbuf.st_size; From 86d5778ec37fb90e3b7480f8745aea6f92decb3d Mon Sep 17 00:00:00 2001 From: Andy2 Date: Mon, 14 Nov 2011 18:21:41 -0800 Subject: [PATCH 07/11] remove 16x16 limitation on board size, replacing with 32x32. Change is enabled by a compile-time flag so Android needn't follow yet. --- xwords4/common/board.c | 4 ++-- xwords4/common/boarddrw.c | 6 +++--- xwords4/common/boardp.h | 2 +- xwords4/common/comtypes.h | 12 +++++++++++- xwords4/common/game.c | 7 +++++-- xwords4/common/game.h | 7 ++++++- xwords4/common/model.c | 21 +++++++++++++++------ xwords4/common/model.h | 6 +++++- xwords4/linux/Makefile | 1 + xwords4/linux/linuxutl.c | 5 +++-- 10 files changed, 52 insertions(+), 19 deletions(-) diff --git a/xwords4/common/board.c b/xwords4/common/board.c index f877b0a03..1615beb3d 100644 --- a/xwords4/common/board.c +++ b/xwords4/common/board.c @@ -1198,7 +1198,7 @@ invalPerimeter( BoardCtxt* board ) ScrollData* hsd = &board->sd[SCROLL_H]; XP_U16 firstCol = hsd->offset; XP_U16 lastCol = hsd->lastVisible; - XP_U16 firstAndLast = (1 << lastCol) | (1 << firstCol); + RowFlags firstAndLast = (1 << lastCol) | (1 << firstCol); ScrollData* vsd = &board->sd[SCROLL_V]; XP_U16 firstRow = vsd->offset; XP_U16 lastRow = vsd->lastVisible; @@ -1588,7 +1588,7 @@ invalReflection( BoardCtxt* board ) while ( nRows-- ) { XP_U16 nCols; - XP_U16 redrawFlag = board->redrawFlags[nRows]; + RowFlags redrawFlag = board->redrawFlags[nRows]; if ( !redrawFlag ) { continue; /* nothing set this row */ } diff --git a/xwords4/common/boarddrw.c b/xwords4/common/boarddrw.c index 8b29038fe..123c5616b 100644 --- a/xwords4/common/boarddrw.c +++ b/xwords4/common/boarddrw.c @@ -299,11 +299,11 @@ drawBoard( BoardCtxt* board ) nVisCols = model_numCols( model ) - board->zoomCount; for ( row = vsd->offset; row <= vsd->lastVisible; ++row ) { - XP_U16 rowFlags = board->redrawFlags[row]; + RowFlags rowFlags = board->redrawFlags[row]; if ( rowFlags != 0 ) { - XP_U16 failedBits = 0; + RowFlags failedBits = 0; for ( col = 0; col < nVisCols; ++col ) { - XP_U16 colMask = 1 << (col + hsd->offset); + RowFlags colMask = 1 << (col + hsd->offset); if ( 0 != (rowFlags & colMask) ) { if ( !drawCell( board, col + hsd->offset, row, XP_TRUE )) { diff --git a/xwords4/common/boardp.h b/xwords4/common/boardp.h index d9a50015b..10b4a9f0f 100644 --- a/xwords4/common/boardp.h +++ b/xwords4/common/boardp.h @@ -157,7 +157,7 @@ struct BoardCtxt { XP_S16 timerStoppedTurn; #endif - XP_U16 redrawFlags[MAX_ROWS]; + RowFlags redrawFlags[MAX_ROWS]; XP_Rect boardBounds; XP_U16 heightAsSet; diff --git a/xwords4/common/comtypes.h b/xwords4/common/comtypes.h index 10e38a518..c95cecdba 100644 --- a/xwords4/common/comtypes.h +++ b/xwords4/common/comtypes.h @@ -115,7 +115,9 @@ typedef enum { } XWTimerReason; #define MAX_NUM_PLAYERS 4 -#define MAX_ROWS 16 +#ifndef MAX_ROWS +# define MAX_ROWS 16 +#endif #define MAX_COLS MAX_ROWS #ifdef EIGHT_TILES # define MAX_TRAY_TILES 8 @@ -127,6 +129,14 @@ typedef enum { #define NPLAYERS_NBITS 3 #define EMPTIED_TRAY_BONUS 50 +#if MAX_ROWS <= 16 +typedef XP_U16 RowFlags; +#elif MAX_ROWS <= 32 +typedef XP_U32 RowFlags; +#else + error +#endif + /* I need a way to communiate prefs to common/ code. For now, though, I'll * leave storage of these values up to the platforms. First, because I don't * want to deal with versioning in the common code. Second, becuase they diff --git a/xwords4/common/game.c b/xwords4/common/game.c index b02d58ee0..1e0473727 100644 --- a/xwords4/common/game.c +++ b/xwords4/common/game.c @@ -432,7 +432,10 @@ gi_readFromStream( MPFORMAL XWStreamCtxt* stream, CurGameInfo* gi ) XP_FREEP( mpool, &str ); gi->nPlayers = (XP_U8)stream_getBits( stream, NPLAYERS_NBITS ); - gi->boardSize = (XP_U8)stream_getBits( stream, 4 ); + gi->boardSize = + (XP_U8)stream_getBits( stream, + strVersion < STREAM_VERS_BIGBOARD? 4 : + NUMCOLS_NBITS ); gi->serverRole = (DeviceRole)stream_getBits( stream, 2 ); gi->hintsNotAllowed = stream_getBits( stream, 1 ); if ( strVersion < STREAM_VERS_ROBOTIQ ) { @@ -494,7 +497,7 @@ gi_writeToStream( XWStreamCtxt* stream, const CurGameInfo* gi ) stringToStream( stream, gi->dictName ); stream_putBits( stream, NPLAYERS_NBITS, gi->nPlayers ); - stream_putBits( stream, 4, gi->boardSize ); + stream_putBits( stream, NUMCOLS_NBITS, gi->boardSize ); stream_putBits( stream, 2, gi->serverRole ); stream_putBits( stream, 1, gi->hintsNotAllowed ); stream_putBits( stream, 2, gi->phoniesAction ); diff --git a/xwords4/common/game.h b/xwords4/common/game.h index 139ac8878..61f4de9f3 100644 --- a/xwords4/common/game.h +++ b/xwords4/common/game.h @@ -31,6 +31,11 @@ extern "C" { #endif +#if MAX_COLS > 16 +# define STREAM_VERS_BIGBOARD 0x12 +#else +# define STREAM_VERS_BIGBOARD 0x11 +#endif #define STREAM_SAVE_PREVWORDS 0x11 #define STREAM_VERS_SERVER_SAVES_TOSHOW 0x10 #define STREAM_VERS_PLAYERDICTS 0x0F @@ -53,7 +58,7 @@ extern "C" { #define STREAM_VERS_41B4 0x02 #define STREAM_VERS_405 0x01 -#define CUR_STREAM_VERS STREAM_SAVE_PREVWORDS +#define CUR_STREAM_VERS STREAM_VERS_BIGBOARD typedef struct LocalPlayer { XP_UCHAR* name; diff --git a/xwords4/common/model.c b/xwords4/common/model.c index c40976c8c..70dd08f25 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -119,11 +119,14 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict, XP_Bool hasDict; XP_U16 nPlayers; XP_U16 version = stream_getVersion( stream ); + XP_U16 nColsNBits = + STREAM_VERS_BIGBOARD > version ? 4 : + NUMCOLS_NBITS; XP_ASSERT( !!dict || !!dicts ); - nCols = (XP_U16)stream_getBits( stream, NUMCOLS_NBITS ); - nRows = (XP_U16)stream_getBits( stream, NUMCOLS_NBITS ); + nCols = (XP_U16)stream_getBits( stream, nColsNBits ); + nRows = (XP_U16)stream_getBits( stream, nColsNBits ); hasDict = (version >= STREAM_VERS_MODEL_NO_DICT) ? XP_FALSE : stream_getBits( stream, 1 ); @@ -849,6 +852,9 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, { XP_U16 numTiles; Tile blank = dict_getBlankTile( model_getDictionary(model) ); + XP_U16 nColsNBits = + STREAM_VERS_BIGBOARD > stream_getVersion( stream ) ? 4 : + NUMCOLS_NBITS; model_resetCurrentTurn( model, playerNum ); @@ -860,8 +866,8 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, XP_S16 foundAt; Tile moveTile; Tile tileFace = (Tile)stream_getBits( stream, TILE_NBITS ); - XP_U16 col = (XP_U16)stream_getBits( stream, NUMCOLS_NBITS ); - XP_U16 row = (XP_U16)stream_getBits( stream, NUMCOLS_NBITS ); + XP_U16 col = (XP_U16)stream_getBits( stream, nColsNBits ); + XP_U16 row = (XP_U16)stream_getBits( stream, nColsNBits ); XP_Bool isBlank = stream_getBits( stream, 1 ); /* This code gets called both for the server, which has all the @@ -2215,6 +2221,9 @@ loadPlayerCtxt( XWStreamCtxt* stream, XP_U16 version, PlayerCtxt* pc ) { PendingTile* pt; XP_U16 nTiles; + XP_U16 nColsNBits = + STREAM_VERS_BIGBOARD > stream_getVersion( stream ) ? 4 : + NUMCOLS_NBITS; pc->curMoveValid = stream_getBits( stream, 1 ); @@ -2230,8 +2239,8 @@ loadPlayerCtxt( XWStreamCtxt* stream, XP_U16 version, PlayerCtxt* pc ) nTiles = pc->nPending + pc->nUndone; for ( pt = pc->pendingTiles; nTiles-- > 0; ++pt ) { XP_U16 nBits; - pt->col = (XP_U8)stream_getBits( stream, NUMCOLS_NBITS ); - pt->row = (XP_U8)stream_getBits( stream, NUMCOLS_NBITS ); + pt->col = (XP_U8)stream_getBits( stream, nColsNBits ); + pt->row = (XP_U8)stream_getBits( stream, nColsNBits ); nBits = (version <= STREAM_VERS_RELAY) ? 6 : 7; pt->tile = (Tile)stream_getBits( stream, nBits ); diff --git a/xwords4/common/model.h b/xwords4/common/model.h index 614829a0a..5bcf20685 100644 --- a/xwords4/common/model.h +++ b/xwords4/common/model.h @@ -29,7 +29,11 @@ extern "C" { #endif -#define NUMCOLS_NBITS 4 +#if MAX_COLS <= 16 +# define NUMCOLS_NBITS 4 +#elif MAX_COLS <= 32 +# define NUMCOLS_NBITS 5 +#endif #ifdef EIGHT_TILES # define NTILES_NBITS 4 diff --git a/xwords4/linux/Makefile b/xwords4/linux/Makefile index f7e34d4d1..861a1a3eb 100644 --- a/xwords4/linux/Makefile +++ b/xwords4/linux/Makefile @@ -93,6 +93,7 @@ DEFINES += -DSET_GAMESEED DEFINES += -DTEXT_MODEL DEFINES += -DXWFEATURE_WALKDICT DEFINES += -DXWFEATURE_DICTSANITY +#DEFINES += -DMAX_ROWS=32 ifdef CURSES_CELL_HT DEFINES += -DCURSES_CELL_HT=$(CURSES_CELL_HT) diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c index 40a435489..b1df9cd12 100644 --- a/xwords4/linux/linuxutl.c +++ b/xwords4/linux/linuxutl.c @@ -179,8 +179,9 @@ linux_util_getSquareBonus( XW_UtilCtxt* uc, const ModelCtxt* model, TW,EM,EM,DL,EM,EM,EM,DW, }; /* scrabbleBoard */ - if ( col > 7 ) col = 14 - col; - if ( row > 7 ) row = 14 - row; + XP_U16 nCols = model_numCols( model ); + if ( col > (nCols/2) ) col = nCols - 1 - col; + if ( row > (nCols/2) ) row = nCols - 1 - row; if ( col > row ) { XP_U16 tmp = col; col = row; From 379e5f1d96837e00bf08ae92e04f240ecbb95807 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Mon, 14 Nov 2011 20:18:09 -0800 Subject: [PATCH 08/11] use actual rather than max possible board dimensions to apportion space, fixing crashes when max upped to 32. Will still fail as actual size approaches that number. --- xwords4/linux/cursesmain.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 798236efe..11af9250b 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1569,15 +1569,16 @@ positionSizeStuff( CursesAppGlobals* globals, int width, int height ) XP_U16 cellWidth, cellHt, scoreLeft, scoreWidth; BoardCtxt* board = globals->cGlobals.game.board; int remWidth = width; + int nRows = globals->cGlobals.params->gi.boardSize; cellWidth = CURSES_CELL_WIDTH; cellHt = CURSES_CELL_HT; board_setPos( board, BOARD_OFFSET, BOARD_OFFSET, - cellWidth * MAX_COLS, cellHt * MAX_ROWS, + cellWidth * nRows, cellHt * nRows, cellWidth, XP_FALSE ); /* board_setScale( board, cellWidth, cellHt ); */ - scoreLeft = (cellWidth * MAX_COLS);// + BOARD_SCORE_PADDING; - remWidth -= cellWidth * MAX_COLS; + scoreLeft = (cellWidth * nRows);// + BOARD_SCORE_PADDING; + remWidth -= cellWidth * nRows; /* If the scoreboard will right of the board, put it there. Otherwise try to fit it below the boards. */ @@ -1591,8 +1592,9 @@ positionSizeStuff( CursesAppGlobals* globals, int width, int height ) trayTop = 8; } else { trayLeft = BOARD_OFFSET; - trayTop = BOARD_OFFSET + (cellHt * MAX_ROWS); + trayTop = BOARD_OFFSET + (cellHt * nRows); if ( trayTop + trayHt > height ) { + XP_ASSERT( height > trayTop ); trayHt = height - trayTop; } } From bdb8e6f03cc7cee5abac5f3cf73d3ffa81c6d176 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Tue, 15 Nov 2011 06:13:25 -0800 Subject: [PATCH 09/11] fix several crashers that prevented network games from working when MAX_COLS was larger than 16. In order that old-style messages on relay be readable by new-style code on device, modified server.c's protocol to include stream version. But: unless I come up with a better way of doing this all devices will have to be upgraded at the same time: old won't be able to read the new format as it's done here. --- xwords4/common/model.c | 9 ++++---- xwords4/common/server.c | 45 ++++++++++++++++++++++++++++------------ xwords4/common/xwproto.h | 2 ++ xwords4/linux/gtkmain.c | 35 +++++++++++++++++-------------- xwords4/linux/gtkmain.h | 4 +--- 5 files changed, 58 insertions(+), 37 deletions(-) diff --git a/xwords4/common/model.c b/xwords4/common/model.c index 70dd08f25..f1d6ade68 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -850,11 +850,10 @@ void model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, XWStreamCtxt* stream ) { - XP_U16 numTiles; + XP_U16 numTiles, ii; Tile blank = dict_getBlankTile( model_getDictionary(model) ); - XP_U16 nColsNBits = - STREAM_VERS_BIGBOARD > stream_getVersion( stream ) ? 4 : - NUMCOLS_NBITS; + XP_U16 version = stream_getVersion( stream ); + XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; model_resetCurrentTurn( model, playerNum ); @@ -862,7 +861,7 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, XP_LOGF( "%s: numTiles=%d", __func__, numTiles ); - while ( numTiles-- ) { + for ( ii = 0; ii < numTiles; ++ii ) { XP_S16 foundAt; Tile moveTile; Tile tileFace = (Tile)stream_getBits( stream, TILE_NBITS ); diff --git a/xwords4/common/server.c b/xwords4/common/server.c index 2e911ec63..a40960c3b 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -145,6 +145,7 @@ static void sendBadWordMsgs( ServerCtxt* server ); static XP_Bool handleIllegalWord( ServerCtxt* server, XWStreamCtxt* incoming ); static void tellMoveWasLegal( ServerCtxt* server ); +static void writeProto( XWStreamCtxt* stream, XW_Proto proto ); #endif #define PICK_NEXT -1 @@ -515,7 +516,7 @@ server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream ) if ( server->nv.gameState == XWSTATE_NONE ) { stream_open( stream ); - stream_putBits( stream, XWPROTO_NBITS, XWPROTO_DEVICE_REGISTRATION ); + writeProto( stream, XWPROTO_DEVICE_REGISTRATION ); nPlayers = gi->nPlayers; XP_ASSERT( nPlayers > 0 ); @@ -1095,8 +1096,10 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream ) PoolContext* pool; /* version; any dependencies here? */ - XP_U8 streamVersion = stream_getU8( stream ); - stream_setVersion( stream, streamVersion ); + if ( STREAM_SAVE_PREVWORDS >= stream_getVersion( stream ) ) { + XP_U8 streamVersion = stream_getU8( stream ); + stream_setVersion( stream, streamVersion ); + } gameID = stream_getU32( stream ); XP_LOGF( "read gameID of %lx; calling comms_setConnID", gameID ); @@ -1234,11 +1237,7 @@ server_sendInitialMessage( ServerCtxt* server ) DictionaryCtxt* dict = model_getDictionary(model); XP_ASSERT( !!stream ); stream_open( stream ); - stream_putBits( stream, XWPROTO_NBITS, XWPROTO_CLIENT_SETUP ); - - /* write version for server's benefit; use old version until format - changes */ - stream_putU8( stream, CUR_STREAM_VERS ); + writeProto( stream, XWPROTO_CLIENT_SETUP ); XP_LOGF( "putting gameID %lx into msg", gameID ); stream_putU32( stream, gameID ); @@ -1323,6 +1322,7 @@ printCode(char* intro, XW_Proto code) caseStr( str, XWPROTO_MOVE_CONFIRM ); caseStr( str, XWPROTO_CLIENT_REQ_END_GAME ); caseStr( str, XWPROTO_END_GAME ); + caseStr( str, XWPROTO_NEW_PROTO ); } XP_STATUSF( "\t%s for %s", intro, str ); @@ -1343,7 +1343,7 @@ messageStreamWithHeader( ServerCtxt* server, XP_U16 devIndex, XW_Proto code ) stream = util_makeStreamFromAddr( server->vol.util, channelNo ); stream_open( stream ); - stream_putBits( stream, XWPROTO_NBITS, code ); + writeProto( stream, code ); return stream; } /* messageStreamWithHeader */ @@ -2440,15 +2440,34 @@ server_handleUndo( ServerCtxt* server ) } /* server_handleUndo */ #ifndef XWFEATURE_STANDALONE_ONLY +static void +writeProto( XWStreamCtxt* stream, XW_Proto proto ) +{ + stream_putBits( stream, XWPROTO_NBITS, XWPROTO_NEW_PROTO ); + stream_putBits( stream, XWPROTO_NBITS, proto ); + stream_putU8( stream, CUR_STREAM_VERS ); +} + +static XW_Proto +readProto( XWStreamCtxt* stream ) +{ + XP_U8 version = STREAM_SAVE_PREVWORDS; /* version prior to fmt change */ + XW_Proto proto = (XW_Proto)stream_getBits( stream, XWPROTO_NBITS ); + if ( XWPROTO_NEW_PROTO == proto ) { + proto = (XW_Proto)stream_getBits( stream, XWPROTO_NBITS ); + version = stream_getU8( stream ); + } + stream_setVersion( stream, version ); + return proto; +} + XP_Bool server_receiveMessage( ServerCtxt* server, XWStreamCtxt* incoming ) { - XW_Proto code; XP_Bool accepted = XP_FALSE; + XW_Proto code = readProto( incoming ); - code = (XW_Proto)stream_getBits( incoming, XWPROTO_NBITS ); - - printCode("Receiving", code); + printCode( "Receiving", code ); if ( code == XWPROTO_DEVICE_REGISTRATION ) { /* This message is special: doesn't have the header that's possible diff --git a/xwords4/common/xwproto.h b/xwords4/common/xwproto.h index 011fe7f73..6361cd157 100644 --- a/xwords4/common/xwproto.h +++ b/xwords4/common/xwproto.h @@ -44,6 +44,8 @@ typedef enum { //XWPROTO_MOVEMADE_INFO, /* info about tiles placed and received */ ,XWPROTO_CLIENT_REQ_END_GAME /* non-server wants to end the game */ ,XWPROTO_END_GAME /* server says to end game */ + + ,XWPROTO_NEW_PROTO } XW_Proto; #define XWPROTO_NBITS 4 diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index dcb995a4c..ebaccce8e 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -528,6 +528,8 @@ configure_event( GtkWidget* widget, GdkEventConfigure* XP_UNUSED(event), gint trayTop; gint boardTop = 0; XP_U16 netStatWidth = 0; + gint nCols = globals->cGlobals.params->gi.boardSize; + gint nRows = nCols; if ( globals->draw == NULL ) { createOrLoadObjects( globals ); @@ -541,25 +543,23 @@ configure_event( GtkWidget* widget, GdkEventConfigure* XP_UNUSED(event), bdHeight = widget->allocation.height - (GTK_TOP_MARGIN + GTK_BOTTOM_MARGIN) - GTK_MIN_TRAY_SCALEV - GTK_BOTTOM_MARGIN; - hscale = bdWidth / GTK_NUM_COLS; + hscale = bdWidth / nCols; if ( 0 != globals->cGlobals.params->nHidden ) { vscale = hscale; } else { - vscale = (bdHeight / (GTK_NUM_ROWS + GTK_TRAY_HT_ROWS)); /* makd tray - height 3x cell - height */ + vscale = (bdHeight / (nCols + GTK_TRAY_HT_ROWS)); /* makd tray height + 3x cell height */ } if ( !globals->cGlobals.params->verticalScore ) { boardTop += GTK_HOR_SCORE_HEIGHT; } - trayTop = boardTop + (vscale * GTK_NUM_ROWS); + trayTop = boardTop + (vscale * nRows); /* move tray up if part of board's meant to be hidden */ trayTop -= vscale * globals->cGlobals.params->nHidden; board_setPos( globals->cGlobals.game.board, GTK_BOARD_LEFT, boardTop, - hscale*GTK_NUM_COLS, vscale * GTK_NUM_ROWS, - hscale * 2, XP_FALSE ); + hscale * nCols, vscale * nRows, hscale * 2, XP_FALSE ); /* board_setScale( globals->cGlobals.game.board, hscale, vscale ); */ globals->gridOn = XP_TRUE; @@ -569,16 +569,16 @@ configure_event( GtkWidget* widget, GdkEventConfigure* XP_UNUSED(event), timerTop = GTK_TIMER_TOP; if ( globals->cGlobals.params->verticalScore ) { - timerLeft = GTK_BOARD_LEFT + (hscale*GTK_NUM_COLS) + 1; + timerLeft = GTK_BOARD_LEFT + (hscale*nCols) + 1; board_setScoreboardLoc( globals->cGlobals.game.board, timerLeft, GTK_VERT_SCORE_TOP, GTK_VERT_SCORE_WIDTH, - vscale*GTK_NUM_COLS, + vscale*nCols, XP_FALSE ); } else { - timerLeft = GTK_BOARD_LEFT + (hscale*GTK_NUM_COLS) + timerLeft = GTK_BOARD_LEFT + (hscale*nCols) - GTK_TIMER_WIDTH - netStatWidth; board_setScoreboardLoc( globals->cGlobals.game.board, GTK_BOARD_LEFT, GTK_HOR_SCORE_TOP, @@ -598,7 +598,7 @@ configure_event( GtkWidget* widget, GdkEventConfigure* XP_UNUSED(event), GTK_TIMER_WIDTH, GTK_HOR_SCORE_HEIGHT ); board_setTrayLoc( globals->cGlobals.game.board, GTK_TRAY_LEFT, trayTop, - hscale * GTK_NUM_COLS, vscale * GTK_TRAY_HT_ROWS + 10, + hscale * nCols, vscale * GTK_TRAY_HT_ROWS + 10, GTK_DIVIDER_WIDTH ); setCtrlsForTray( globals ); @@ -1251,7 +1251,8 @@ handle_hide_button( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* globals ) XP_Bool draw = XP_FALSE; if ( globals->cGlobals.params->nHidden > 0 ) { - globals->adjustment->page_size = GTK_NUM_ROWS; + gint nRows = globals->cGlobals.params->gi.boardSize; + globals->adjustment->page_size = nRows; globals->adjustment->value = 0.0; gtk_signal_emit_by_name( GTK_OBJECT(globals->adjustment), "changed" ); @@ -1341,7 +1342,7 @@ setCtrlsForTray( GtkAppGlobals* XP_UNUSED(globals) ) XP_S16 nHidden = globals->cGlobals.params->nHidden; if ( nHidden != 0 ) { - XP_U16 pageSize = GTK_NUM_ROWS; + XP_U16 pageSize = nRows; if ( state == TRAY_HIDDEN ) { /* we recover what tray covers */ nHidden -= GTK_TRAY_HT_ROWS; @@ -1373,7 +1374,8 @@ gtk_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 maxOffset, { GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure; if ( !!globals->adjustment ) { - globals->adjustment->page_size = GTK_NUM_ROWS - maxOffset; + gint nRows = globals->cGlobals.params->gi.boardSize; + globals->adjustment->page_size = nRows - maxOffset; globals->adjustment->value = newOffset; gtk_adjustment_value_changed( GTK_ADJUSTMENT(globals->adjustment) ); } @@ -2317,9 +2319,10 @@ gtkmain( LaunchParams* params, int argc, char *argv[] ) /* install scrollbar even if not needed -- since zooming can make it needed later */ GtkWidget* vscrollbar; + gint nRows = globals.cGlobals.params->gi.boardSize; globals.adjustment = (GtkAdjustment*) - gtk_adjustment_new( 0, 0, GTK_NUM_ROWS, 1, 2, - GTK_NUM_ROWS-globals.cGlobals.params->nHidden ); + gtk_adjustment_new( 0, 0, nRows, 1, 2, + nRows - globals.cGlobals.params->nHidden ); vscrollbar = gtk_vscrollbar_new( globals.adjustment ); g_signal_connect( GTK_OBJECT(globals.adjustment), "value_changed", G_CALLBACK(scroll_value_changed), &globals ); diff --git a/xwords4/linux/gtkmain.h b/xwords4/linux/gtkmain.h index f99713213..ca06ea8b3 100644 --- a/xwords4/linux/gtkmain.h +++ b/xwords4/linux/gtkmain.h @@ -132,8 +132,6 @@ typedef struct GtkAppGlobals { /* DictionaryCtxt* gtk_dictionary_make(); */ int gtkmain( LaunchParams* params, int argc, char *argv[] ); -#define GTK_NUM_COLS 15 -#define GTK_NUM_ROWS 15 #define GTK_MIN_SCALE 12 /* was 14 */ #define GTK_MIN_TRAY_SCALEH 24 @@ -156,7 +154,7 @@ int gtkmain( LaunchParams* params, int argc, char *argv[] ); #define GTK_TIMER_WIDTH 40 #define GTK_NETSTAT_WIDTH 20 #define GTK_TIMER_TOP GTK_HOR_SCORE_TOP -#define GTK_HOR_SCORE_WIDTH ((GTK_MIN_SCALE*MAX_COLS)-GTK_TIMER_PAD) +#define GTK_HOR_SCORE_WIDTH ((GTK_MIN_SCALE*20)-GTK_TIMER_PAD) #define GTK_VERT_SCORE_WIDTH 40 #define GTK_BOARD_TOP (GTK_SCORE_TOP + GTK_SCORE_HEIGHT \ From fab39a5f87c78eb9dcc09c11a1fec3dd601c36e5 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Wed, 16 Nov 2011 06:47:55 -0800 Subject: [PATCH 10/11] Trying to deal with old- and new-proto devices interacting. Fix clients to append their stream version to their inital connect message. (The format can't change, so detecting additional length was the only option. comm.c on existing clients won't allow more than one connect message per channel, so adding a new to be used in addition didn't work.) New servers detect this; old will ignore. Track the version (implicit or not) of all clients, and use the lowest any supports, so that new server and all new clients will use newer proto. --- xwords4/common/game.c | 10 +-- xwords4/common/game.h | 8 ++- xwords4/common/model.c | 32 ++++++--- xwords4/common/server.c | 148 ++++++++++++++++++++++++++++++++-------- 4 files changed, 155 insertions(+), 43 deletions(-) diff --git a/xwords4/common/game.c b/xwords4/common/game.c index 1e0473727..62e3e2178 100644 --- a/xwords4/common/game.c +++ b/xwords4/common/game.c @@ -426,16 +426,18 @@ gi_readFromStream( MPFORMAL XWStreamCtxt* stream, CurGameInfo* gi ) XP_U16 ii; XP_UCHAR* str; XP_U16 strVersion = stream_getVersion( stream ); +#ifdef STREAM_VERS_BIGBOARD + XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > strVersion ? 4 : NUMCOLS_NBITS; +#else + XP_U16 nColsNBits = NUMCOLS_NBITS; +#endif str = stringFromStream( mpool, stream ); replaceStringIfDifferent( mpool, &gi->dictName, str ); XP_FREEP( mpool, &str ); gi->nPlayers = (XP_U8)stream_getBits( stream, NPLAYERS_NBITS ); - gi->boardSize = - (XP_U8)stream_getBits( stream, - strVersion < STREAM_VERS_BIGBOARD? 4 : - NUMCOLS_NBITS ); + gi->boardSize = (XP_U8)stream_getBits( stream, nColsNBits ); gi->serverRole = (DeviceRole)stream_getBits( stream, 2 ); gi->hintsNotAllowed = stream_getBits( stream, 1 ); if ( strVersion < STREAM_VERS_ROBOTIQ ) { diff --git a/xwords4/common/game.h b/xwords4/common/game.h index 61f4de9f3..15601a24a 100644 --- a/xwords4/common/game.h +++ b/xwords4/common/game.h @@ -33,8 +33,6 @@ extern "C" { #if MAX_COLS > 16 # define STREAM_VERS_BIGBOARD 0x12 -#else -# define STREAM_VERS_BIGBOARD 0x11 #endif #define STREAM_SAVE_PREVWORDS 0x11 #define STREAM_VERS_SERVER_SAVES_TOSHOW 0x10 @@ -58,7 +56,11 @@ extern "C" { #define STREAM_VERS_41B4 0x02 #define STREAM_VERS_405 0x01 -#define CUR_STREAM_VERS STREAM_VERS_BIGBOARD +#if MAX_COLS > 16 +# define CUR_STREAM_VERS STREAM_VERS_BIGBOARD +#else +# define CUR_STREAM_VERS STREAM_SAVE_PREVWORDS +#endif typedef struct LocalPlayer { XP_UCHAR* name; diff --git a/xwords4/common/model.c b/xwords4/common/model.c index f1d6ade68..13bebf0ab 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -118,10 +118,13 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict, short i; XP_Bool hasDict; XP_U16 nPlayers; + XP_U16 nColsNBits; XP_U16 version = stream_getVersion( stream ); - XP_U16 nColsNBits = - STREAM_VERS_BIGBOARD > version ? 4 : - NUMCOLS_NBITS; +#ifdef STREAM_VERS_BIGBOARD + nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + nColsNBits = NUMCOLS_NBITS; +#endif XP_ASSERT( !!dict || !!dicts ); @@ -818,6 +821,12 @@ model_currentMoveToStream( ModelCtxt* model, XP_S16 turn, { PlayerCtxt* player; XP_S16 numTiles; +#ifdef STREAM_VERS_BIGBOARD + XP_U16 version = stream_getVersion( stream ); + XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + XP_U16 nColsNBits = NUMCOLS_NBITS; +#endif XP_ASSERT( turn >= 0 ); player = &model->players[turn]; @@ -834,8 +843,8 @@ model_currentMoveToStream( ModelCtxt* model, XP_S16 turn, &col, &row, &isBlank ); XP_ASSERT( numTiles >= 0 ); stream_putBits( stream, TILE_NBITS, tile ); - stream_putBits( stream, NUMCOLS_NBITS, col ); - stream_putBits( stream, NUMCOLS_NBITS, row ); + stream_putBits( stream, nColsNBits, col ); + stream_putBits( stream, nColsNBits, row ); stream_putBits( stream, 1, isBlank ); } } /* model_currentMoveToStream */ @@ -852,8 +861,13 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, { XP_U16 numTiles, ii; Tile blank = dict_getBlankTile( model_getDictionary(model) ); +#ifdef STREAM_VERS_BIGBOARD XP_U16 version = stream_getVersion( stream ); XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + XP_U16 nColsNBits = NUMCOLS_NBITS; +#endif + model_resetCurrentTurn( model, playerNum ); @@ -2220,9 +2234,11 @@ loadPlayerCtxt( XWStreamCtxt* stream, XP_U16 version, PlayerCtxt* pc ) { PendingTile* pt; XP_U16 nTiles; - XP_U16 nColsNBits = - STREAM_VERS_BIGBOARD > stream_getVersion( stream ) ? 4 : - NUMCOLS_NBITS; +#ifdef STREAM_VERS_BIGBOARD + XP_U16 nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + XP_U16 nColsNBits = NUMCOLS_NBITS; +#endif pc->curMoveValid = stream_getBits( stream, 1 ); diff --git a/xwords4/common/server.c b/xwords4/common/server.c index a40960c3b..d8a38bfe7 100644 --- a/xwords4/common/server.c +++ b/xwords4/common/server.c @@ -60,6 +60,9 @@ typedef struct ServerPlayer { typedef struct RemoteAddress { XP_PlayerAddr channelNo; +#ifdef STREAM_VERS_BIGBOARD + XP_U8 streamVersion; +#endif } RemoteAddress; /* These are the parts of the server's state that needs to be preserved @@ -84,6 +87,9 @@ typedef struct ServerNonvolatiles { XP_U8 pendingRegistrations; XP_Bool showRobotScores; XP_Bool sortNewTiles; +#ifdef STREAM_VERS_BIGBOARD + XP_U8 streamVersion; +#endif #ifdef XWFEATURE_SLOW_ROBOT XP_U16 robotThinkMin, robotThinkMax; /* not saved (yet) */ #endif @@ -139,13 +145,14 @@ static XWStreamCtxt* messageStreamWithHeader( ServerCtxt* server, XP_U16 devIndex, XW_Proto code ); static XP_Bool handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ); -static void registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ); +static XP_S8 registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ); static void server_sendInitialMessage( ServerCtxt* server ); static void sendBadWordMsgs( ServerCtxt* server ); static XP_Bool handleIllegalWord( ServerCtxt* server, XWStreamCtxt* incoming ); static void tellMoveWasLegal( ServerCtxt* server ); -static void writeProto( XWStreamCtxt* stream, XW_Proto proto ); +static void writeProto( const ServerCtxt* server, XWStreamCtxt* stream, + XW_Proto proto ); #endif #define PICK_NEXT -1 @@ -231,6 +238,9 @@ initServer( ServerCtxt* server ) syncPlayers( server ); server->nv.nDevices = 1; /* local device (0) is always there */ +#ifdef STREAM_VERS_BIGBOARD + server->nv.streamVersion = STREAM_SAVE_PREVWORDS; /* default to old */ +#endif } /* initServer */ ServerCtxt* @@ -281,7 +291,18 @@ getNV( XWStreamCtxt* stream, ServerNonvolatiles* nv, XP_U16 nPlayers ) for ( ii = 0; ii < nPlayers; ++ii ) { nv->addresses[ii].channelNo = (XP_PlayerAddr)stream_getBits( stream, 16 ); +#ifdef STREAM_VERS_BIGBOARD + if ( STREAM_VERS_BIGBOARD <= version ) { + nv->addresses[ii].streamVersion = stream_getBits( stream, 8 ); + } +#endif } +#ifdef STREAM_VERS_BIGBOARD + if ( STREAM_SAVE_PREVWORDS < version ) { + nv->streamVersion = stream_getU8 ( stream ); + } + XP_LOGF( "%s: read streamVersion: 0x%x", __func__, nv->streamVersion ); +#endif } /* getNV */ static void @@ -302,7 +323,14 @@ putNV( XWStreamCtxt* stream, ServerNonvolatiles* nv, XP_U16 nPlayers ) for ( ii = 0; ii < nPlayers; ++ii ) { stream_putBits( stream, 16, nv->addresses[ii].channelNo ); +#ifdef STREAM_VERS_BIGBOARD + stream_putBits( stream, 8, nv->addresses[ii].streamVersion ); +#endif } +#ifdef STREAM_VERS_BIGBOARD + stream_putU8( stream, nv->streamVersion ); + XP_LOGF( "%s: wrote streamVersion: 0x%x", __func__, nv->streamVersion ); +#endif } /* putNV */ static XWStreamCtxt* @@ -516,7 +544,7 @@ server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream ) if ( server->nv.gameState == XWSTATE_NONE ) { stream_open( stream ); - writeProto( stream, XWPROTO_DEVICE_REGISTRATION ); + writeProto( server, stream, XWPROTO_DEVICE_REGISTRATION ); nPlayers = gi->nPlayers; XP_ASSERT( nPlayers > 0 ); @@ -532,8 +560,8 @@ server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream ) continue; } - stream_putBits( stream, 1, LP_IS_ROBOT(lp) ); /* better not to send this */ - + stream_putBits( stream, 1, LP_IS_ROBOT(lp) ); /* better not to + send this */ /* The first nPlayers players are the ones we'll use. The local flag doesn't matter when for SERVER_ISCLIENT. */ name = emptyStringIfNull(lp->name); @@ -544,6 +572,10 @@ server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream ) stream_putBits( stream, NAME_LEN_NBITS, len ); stream_putBytes( stream, name, len ); } +#ifdef STREAM_VERS_BIGBOARD + stream_putU8( stream, CUR_STREAM_VERS ); +#endif + } else { XP_LOGF( "%s: wierd state %s; dropping message", __func__, getStateStr(server->nv.gameState) ); @@ -594,11 +626,32 @@ callTurnChangeListener( ServerCtxt* server ) } /* callTurnChangeListener */ #ifndef XWFEATURE_STANDALONE_ONLY +# ifdef STREAM_VERS_BIGBOARD +static void +setStreamVersion( ServerCtxt* server ) +{ + XP_U16 devIndex; + XP_U8 streamVersion = CUR_STREAM_VERS; + for ( devIndex = 1; devIndex < server->nv.nDevices; ++devIndex ) { + XP_U8 devVersion = server->nv.addresses[devIndex].streamVersion; + if ( devVersion < streamVersion ) { + streamVersion = devVersion; + } + } + XP_LOGF( "%s: setting streamVersion: %d", __func__, streamVersion ); + server->nv.streamVersion = streamVersion; +} +# else +# define setStreamVersion(s) +# endif + static XP_Bool handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ) { XP_Bool success = XP_TRUE; - XP_U16 playersInMsg, i; + XP_U16 playersInMsg; + XP_S8 clientIndex; + XP_U16 ii = 0; LOG_FUNC(); /* code will have already been consumed */ @@ -609,21 +662,41 @@ handleRegistrationMsg( ServerCtxt* server, XWStreamCtxt* stream ) util_userError( server->vol.util, ERR_REG_UNEXPECTED_USER ); success = XP_FALSE; } else { - for ( i = 0; i < playersInMsg; ++i ) { - registerRemotePlayer( server, stream ); + XP_S8 prevIndex = -1; + for ( ; ii < playersInMsg; ++ii ) { + clientIndex = registerRemotePlayer( server, stream ); /* This is abusing the semantics of turn change -- at least in the case where there is another device yet to register -- but we need to let the board know to redraw the scoreboard with more players there. */ callTurnChangeListener( server ); + XP_ASSERT( ii == 0 || prevIndex == clientIndex ); + prevIndex = clientIndex; } - if ( server->nv.pendingRegistrations == 0 ) { - assignTilesToAll( server ); - SETSTATE( server, XWSTATE_RECEIVED_ALL_REG ); + } + +#ifdef STREAM_VERS_BIGBOARD + if ( 0 < stream_getSize(stream) ) { + XP_U8 streamVersion = stream_getU8( stream ); + if ( streamVersion >= STREAM_VERS_BIGBOARD ) { + XP_LOGF( "%s: upping device %d streamVersion to %d", + __func__, clientIndex, streamVersion ); + server->nv.addresses[clientIndex].streamVersion = streamVersion; } } +#endif + + if ( server->nv.pendingRegistrations == 0 ) { + XP_ASSERT( ii == playersInMsg ); /* otherwise malformed */ + setStreamVersion( server ); + assignTilesToAll( server ); + SETSTATE( server, XWSTATE_RECEIVED_ALL_REG ); + } +/* now set server's streamVersion if all remote players have the higher one. */ +/* But first need to pass it in the strramx */ + return success; } /* handleRegistrationMsg */ #endif @@ -1000,7 +1073,7 @@ findFirstPending( ServerCtxt* server, ServerPlayer** playerP ) return lp; } /* findFirstPending */ -static void +static XP_S8 registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) { XP_S8 deviceIndex; @@ -1040,10 +1113,13 @@ registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) XP_ASSERT( channelNo != 0 ); addr->channelNo = channelNo; +#ifdef STREAM_VERS_BIGBOARD + addr->streamVersion = STREAM_SAVE_PREVWORDS; +#endif } player->deviceIndex = deviceIndex; - + return deviceIndex; } /* registerRemotePlayer */ static void @@ -1096,10 +1172,9 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream ) PoolContext* pool; /* version; any dependencies here? */ - if ( STREAM_SAVE_PREVWORDS >= stream_getVersion( stream ) ) { - XP_U8 streamVersion = stream_getU8( stream ); - stream_setVersion( stream, streamVersion ); - } + XP_U8 streamVersion = stream_getU8( stream ); + XP_LOGF( "%s: set streamVersion to %d", __func__, streamVersion ); + stream_setVersion( stream, streamVersion ); gameID = stream_getU32( stream ); XP_LOGF( "read gameID of %lx; calling comms_setConnID", gameID ); @@ -1237,7 +1312,9 @@ server_sendInitialMessage( ServerCtxt* server ) DictionaryCtxt* dict = model_getDictionary(model); XP_ASSERT( !!stream ); stream_open( stream ); - writeProto( stream, XWPROTO_CLIENT_SETUP ); + writeProto( server, stream, XWPROTO_CLIENT_SETUP ); + + stream_putU8( stream, CUR_STREAM_VERS ); XP_LOGF( "putting gameID %lx into msg", gameID ); stream_putU32( stream, gameID ); @@ -1341,9 +1418,11 @@ messageStreamWithHeader( ServerCtxt* server, XP_U16 devIndex, XW_Proto code ) printCode("making", code); stream = util_makeStreamFromAddr( server->vol.util, channelNo ); - +#ifdef STREAM_VERS_BIGBOARD + stream_setVersion( stream, server->nv.streamVersion ); +#endif stream_open( stream ); - writeProto( stream, code ); + writeProto( server, stream, code ); return stream; } /* messageStreamWithHeader */ @@ -2441,22 +2520,34 @@ server_handleUndo( ServerCtxt* server ) #ifndef XWFEATURE_STANDALONE_ONLY static void -writeProto( XWStreamCtxt* stream, XW_Proto proto ) +writeProto( const ServerCtxt* server, XWStreamCtxt* stream, XW_Proto proto ) { - stream_putBits( stream, XWPROTO_NBITS, XWPROTO_NEW_PROTO ); +#ifdef STREAM_VERS_BIGBOARD + XP_ASSERT( server->nv.streamVersion > 0 ); + if ( STREAM_SAVE_PREVWORDS < server->nv.streamVersion ) { + stream_putBits( stream, XWPROTO_NBITS, XWPROTO_NEW_PROTO ); + stream_putBits( stream, 8, CUR_STREAM_VERS ); + } +#else + XP_USE(server); +#endif stream_putBits( stream, XWPROTO_NBITS, proto ); - stream_putU8( stream, CUR_STREAM_VERS ); } static XW_Proto -readProto( XWStreamCtxt* stream ) +readProto( ServerCtxt* server, XWStreamCtxt* stream ) { - XP_U8 version = STREAM_SAVE_PREVWORDS; /* version prior to fmt change */ XW_Proto proto = (XW_Proto)stream_getBits( stream, XWPROTO_NBITS ); + XP_U8 version = STREAM_SAVE_PREVWORDS; /* version prior to fmt change */ +#ifdef STREAM_VERS_BIGBOARD if ( XWPROTO_NEW_PROTO == proto ) { + version = stream_getBits( stream, 8 ); proto = (XW_Proto)stream_getBits( stream, XWPROTO_NBITS ); - version = stream_getU8( stream ); } + server->nv.streamVersion = version; +#else + XP_USE(server); +#endif stream_setVersion( stream, version ); return proto; } @@ -2465,7 +2556,7 @@ XP_Bool server_receiveMessage( ServerCtxt* server, XWStreamCtxt* incoming ) { XP_Bool accepted = XP_FALSE; - XW_Proto code = readProto( incoming ); + XW_Proto code = readProto( server, incoming ); printCode( "Receiving", code ); @@ -2554,7 +2645,8 @@ server_receiveMessage( ServerCtxt* server, XWStreamCtxt* incoming ) accepted = XP_TRUE; break; default: - XP_WARNF( "Unknown code on incoming message: %d\n", code ); + XP_WARNF( "%s: Unknown code on incoming message: %d\n", + __func__, code ); break; } /* switch */ } From 210c59ef9bdaf1487aece09c8461fe744e31e64a Mon Sep 17 00:00:00 2001 From: Andy2 Date: Wed, 16 Nov 2011 19:01:11 -0800 Subject: [PATCH 11/11] move bonus square values into model: add API to set an array, and API to query model for values. Now everybody else queries new model API rather than client via util_getSquareBonus(), model uses its internal values if present otherwise falls back to util_getSquareBonus(), and internalizes the array as part of the game. Now it should be easier to have different bonus patterns and to have them exchanged as part of network game init. --- xwords4/common/board.c | 3 +- xwords4/common/boarddrw.c | 8 +-- xwords4/common/comtypes.h | 10 ++++ xwords4/common/dictnry.h | 10 ---- xwords4/common/model.c | 120 ++++++++++++++++++++++++++++++++++--- xwords4/common/model.h | 5 ++ xwords4/common/modelp.h | 2 + xwords4/common/mscore.c | 5 +- xwords4/common/util.h | 7 +-- xwords4/linux/cursesmain.c | 2 +- xwords4/linux/gtkmain.c | 1 + xwords4/linux/linuxutl.c | 84 +++++++++++++++++++------- xwords4/linux/linuxutl.h | 11 +++- 13 files changed, 213 insertions(+), 55 deletions(-) diff --git a/xwords4/common/board.c b/xwords4/common/board.c index 1615beb3d..0d381cab6 100644 --- a/xwords4/common/board.c +++ b/xwords4/common/board.c @@ -1002,8 +1002,7 @@ timerFiredForPen( BoardCtxt* board ) #endif if ( !listWords ) { XWBonusType bonus; - bonus = util_getSquareBonus( board->util, board->model, - col, row ); + bonus = model_getSquareBonus( board->model, col, row ); if ( bonus != BONUS_NONE ) { #ifdef XWFEATURE_MINIWIN text = draw_getMiniWText( board->draw, diff --git a/xwords4/common/boarddrw.c b/xwords4/common/boarddrw.c index 123c5616b..88570fa13 100644 --- a/xwords4/common/boarddrw.c +++ b/xwords4/common/boarddrw.c @@ -1,6 +1,6 @@ -/* -*-mode: C; fill-column: 78; compile-command: "cd ../linux && make MEMDEBUG=TRUE"; -*- */ +/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */ /* - * Copyright 1997 - 2010 by Eric House (xwords@eehouse.org). All rights + * Copyright 1997 - 2011 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -331,7 +331,7 @@ drawBoard( BoardCtxt* board ) XWBonusType bonus; HintAtts hintAtts; CellFlags flags = CELL_NONE; - bonus = util_getSquareBonus( board->util, model, col, row ); + bonus = model_getSquareBonus( model, col, row ); hintAtts = figureHintAtts( board, col, row ); #ifdef KEYBOARD_NAV if ( cellFocused( board, col, row ) ) { @@ -436,7 +436,7 @@ drawCell( BoardCtxt* board, XP_U16 col, XP_U16 row, XP_Bool skipBlanks ) textP = dict_getTileString( dict, tile ); } } - bonus = util_getSquareBonus( board->util, model, col, row ); + bonus = model_getSquareBonus( model, col, row ); hintAtts = figureHintAtts( board, col, row ); if ( (col==board->star_row) && (row==board->star_row) ) { diff --git a/xwords4/common/comtypes.h b/xwords4/common/comtypes.h index c95cecdba..dd8313f22 100644 --- a/xwords4/common/comtypes.h +++ b/xwords4/common/comtypes.h @@ -137,6 +137,16 @@ typedef XP_U32 RowFlags; error #endif +typedef enum { + BONUS_NONE, + BONUS_DOUBLE_LETTER, + BONUS_DOUBLE_WORD, + BONUS_TRIPLE_LETTER, + BONUS_TRIPLE_WORD, + + BONUS_LAST +} XWBonusType; + /* I need a way to communiate prefs to common/ code. For now, though, I'll * leave storage of these values up to the platforms. First, because I don't * want to deal with versioning in the common code. Second, becuase they diff --git a/xwords4/common/dictnry.h b/xwords4/common/dictnry.h index 16efe36e6..53788e77a 100644 --- a/xwords4/common/dictnry.h +++ b/xwords4/common/dictnry.h @@ -39,16 +39,6 @@ extern "C" { typedef XP_U8 XP_LangCode; -typedef enum { - BONUS_NONE, - BONUS_DOUBLE_LETTER, - BONUS_DOUBLE_WORD, - BONUS_TRIPLE_LETTER, - BONUS_TRIPLE_WORD, - - BONUS_LAST -} XWBonusType; - typedef enum { INTRADE_MW_TEXT = BONUS_LAST } XWMiniTextType; diff --git a/xwords4/common/model.c b/xwords4/common/model.c index 13bebf0ab..4745f414d 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -115,7 +115,7 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict, { ModelCtxt* model; XP_U16 nCols, nRows; - short i; + XP_U16 ii; XP_Bool hasDict; XP_U16 nPlayers; XP_U16 nColsNBits; @@ -144,6 +144,20 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict, model = model_make( MPPARM(mpool) dict, dicts, util, nCols, nRows ); model->nPlayers = nPlayers; +#ifdef STREAM_VERS_BIGBOARD + if ( STREAM_VERS_BIGBOARD <= version ) { + model->nBonuses = stream_getBits( stream, 7 ); + if ( 0 < model->nBonuses ) { + model->bonuses = + XP_MALLOC( model->vol.mpool, + model->nBonuses * sizeof( model->bonuses[0] ) ); + for ( ii = 0; ii < model->nBonuses; ++ii ) { + model->bonuses[ii] = stream_getBits( stream, 4 ); + } + } + } +#endif + stack_loadFromStream( model->vol.stack, stream ); buildModelFromStack( model, model->vol.stack, XP_FALSE, 0, @@ -151,10 +165,10 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict, (MovePrintFuncPre)NULL, (MovePrintFuncPost)NULL, NULL ); - for ( i = 0; i < model->nPlayers; ++i ) { - loadPlayerCtxt( stream, version, &model->players[i] ); - setPendingCounts( model, i ); - invalidateScore( model, i ); + for ( ii = 0; ii < model->nPlayers; ++ii ) { + loadPlayerCtxt( stream, version, &model->players[ii] ); + setPendingCounts( model, ii ); + invalidateScore( model, ii ); } return model; @@ -163,7 +177,7 @@ model_makeFromStream( MPFORMAL XWStreamCtxt* stream, DictionaryCtxt* dict, void model_writeToStream( ModelCtxt* model, XWStreamCtxt* stream ) { - short i; + XP_U16 ii; stream_putBits( stream, NUMCOLS_NBITS, model->nCols ); stream_putBits( stream, NUMCOLS_NBITS, model->nRows ); @@ -171,10 +185,17 @@ model_writeToStream( ModelCtxt* model, XWStreamCtxt* stream ) /* we have two bits for nPlayers, so range must be 0..3, not 1..4 */ stream_putBits( stream, NPLAYERS_NBITS, model->nPlayers ); +#ifdef STREAM_VERS_BIGBOARD + stream_putBits( stream, 7, model->nBonuses ); + for ( ii = 0; ii < model->nBonuses; ++ii ) { + stream_putBits( stream, 4, model->bonuses[ii] ); + } +#endif + stack_writeToStream( model->vol.stack, stream ); - for ( i = 0; i < model->nPlayers; ++i ) { - writePlayerCtxt( stream, &model->players[i] ); + for ( ii = 0; ii < model->nPlayers; ++ii ) { + writePlayerCtxt( stream, &model->players[ii] ); } } /* model_writeToStream */ @@ -246,9 +267,90 @@ model_destroy( ModelCtxt* model ) { stack_destroy( model->vol.stack ); /* is this it!? */ + if ( !!model->bonuses ) { + XP_FREE( model->vol.mpool, model->bonuses ); + } XP_FREE( model->vol.mpool, model ); } /* model_destroy */ +#ifdef STREAM_VERS_BIGBOARD +void +model_setSquareBonuses( ModelCtxt* model, XWBonusType* bonuses, XP_U16 nBonuses ) +{ +#ifdef DEBUG + XP_U16 nCols = (1 + model_numCols( model )) / 2; + XP_ASSERT( 0 < nCols ); + XP_U16 wantLen = 0; + while ( nCols > 0 ) { + wantLen += nCols--; + } + XP_ASSERT( wantLen == nBonuses ); +#endif + + if ( !!model->bonuses ) { + XP_FREE( model->vol.mpool, model->bonuses ); + } + model->bonuses = XP_MALLOC( model->vol.mpool, + nBonuses * sizeof(model->bonuses[0]) ); + XP_MEMCPY( model->bonuses, bonuses, nBonuses * sizeof(model->bonuses[0]) ); + model->nBonuses = nBonuses; +} + +static void +borrowSquareBonuses( ModelCtxt* dest, const ModelCtxt* src ) +{ + XP_ASSERT( !dest->bonuses ); + dest->bonuses = src->bonuses; + dest->nBonuses = src->nBonuses; +} + +static void +returnSquareBonuses( ModelCtxt* dest ) +{ + dest->bonuses = NULL; + dest->nBonuses = 0; +} +#else +# define borrowSquareBonuses(d,s) +# define returnSquareBonuses(d) +#endif + +XWBonusType +model_getSquareBonus( const ModelCtxt* model, XP_U16 col, XP_U16 row ) +{ + XWBonusType result = BONUS_NONE; + + if ( 0 ) { +#ifdef STREAM_VERS_BIGBOARD + } else if ( !!model->bonuses ) { + XP_U16 nCols = model_numCols( model ); + XP_U16 ii; + if ( col > (nCols/2) ) { + col = nCols - 1 - col; + } + if ( row > (nCols/2) ) { + row = nCols - 1 - row; + } + if ( col > row ) { + XP_U16 tmp = col; + col = row; + row = tmp; + } + for ( ii = 1; ii <= row; ++ii ) { + col += ii; + } + + if ( col < model->nBonuses ) { + result = model->bonuses[col]; + } +#endif + } else { + result = util_getSquareBonus( model->vol.util, model_numRows(model), + col, row ); + } + return result; +} + static void modelAddEntry( ModelCtxt* model, XP_U16 indx, const StackEntry* entry, XP_Bool useStack, XWStreamCtxt* stream, @@ -1950,6 +2052,7 @@ makeTmpModel( ModelCtxt* model, XWStreamCtxt* stream, model->vol.util, model_numCols(model), model_numRows(model)); model_setNPlayers( tmpModel, model->nPlayers ); + borrowSquareBonuses( tmpModel, model ); buildModelFromStack( tmpModel, model->vol.stack, XP_FALSE, 0, stream, (WordNotifierInfo*)NULL, mpf_pre, mpf_post, closure ); @@ -1971,6 +2074,7 @@ model_writeGameHistory( ModelCtxt* model, XWStreamCtxt* stream, tmpModel = makeTmpModel( model, stream, printMovePre, printMovePost, &closure ); + returnSquareBonuses( tmpModel ); model_destroy( tmpModel ); if ( gameOver ) { diff --git a/xwords4/common/model.h b/xwords4/common/model.h index 5bcf20685..ab01af9fd 100644 --- a/xwords4/common/model.h +++ b/xwords4/common/model.h @@ -269,6 +269,11 @@ void model_listWordsThrough( ModelCtxt* model, XP_U16 col, XP_U16 row, /* Have there been too many passes (so game should end)? */ XP_Bool model_recentPassCountOk( ModelCtxt* model ); +XWBonusType model_getSquareBonus( const ModelCtxt* model, + XP_U16 col, XP_U16 row ); +void model_setSquareBonuses( ModelCtxt* model, XWBonusType* bonuses, + XP_U16 nBonuses ); + XP_Bool model_checkMoveLegal( ModelCtxt* model, XP_S16 player, XWStreamCtxt* stream, WordNotifierInfo* notifyInfo ); diff --git a/xwords4/common/modelp.h b/xwords4/common/modelp.h index f9b15db7d..2d52f4c11 100644 --- a/xwords4/common/modelp.h +++ b/xwords4/common/modelp.h @@ -76,6 +76,8 @@ struct ModelCtxt { XP_U16 nPlayers; XP_U16 nCols; XP_U16 nRows; + XP_U16 nBonuses; + XWBonusType* bonuses; }; void invalidateScore( ModelCtxt* model, XP_S16 player ); diff --git a/xwords4/common/mscore.c b/xwords4/common/mscore.c index 80426c21d..4077f613c 100644 --- a/xwords4/common/mscore.c +++ b/xwords4/common/mscore.c @@ -526,7 +526,7 @@ figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* moveInfo, static XP_U16 word_multiplier( const ModelCtxt* model, XP_U16 col, XP_U16 row ) { - XWBonusType bonus = util_getSquareBonus( model->vol.util, model, col, row ); + XWBonusType bonus = model_getSquareBonus( model, col, row ); switch ( bonus ) { case BONUS_DOUBLE_WORD: return 2; @@ -540,8 +540,7 @@ word_multiplier( const ModelCtxt* model, XP_U16 col, XP_U16 row ) static XP_U16 tile_multiplier( const ModelCtxt* model, XP_U16 col, XP_U16 row ) { - XWBonusType bonus = util_getSquareBonus( model->vol.util, model, - col, row ); + XWBonusType bonus = model_getSquareBonus( model, col, row ); switch ( bonus ) { case BONUS_DOUBLE_LETTER: return 2; diff --git a/xwords4/common/util.h b/xwords4/common/util.h index dc25d28da..6815ebb46 100644 --- a/xwords4/common/util.h +++ b/xwords4/common/util.h @@ -99,8 +99,7 @@ typedef struct UtilVtable { XP_PlayerAddr channelNo ); #endif - XWBonusType (*m_util_getSquareBonus)( XW_UtilCtxt* uc, - const ModelCtxt* model, + XWBonusType (*m_util_getSquareBonus)( XW_UtilCtxt* uc, XP_U16 boardSize, XP_U16 col, XP_U16 row ); void (*m_util_userError)( XW_UtilCtxt* uc, UtilErrID id ); @@ -201,8 +200,8 @@ struct XW_UtilCtxt { #define util_makeStreamFromAddr(uc,a) \ (uc)->vtable->m_util_makeStreamFromAddr((uc),(a)) -#define util_getSquareBonus(uc,m,c,r) \ - (uc)->vtable->m_util_getSquareBonus((uc),(m),(c),(r)) +#define util_getSquareBonus(uc,b,c,r) \ + (uc)->vtable->m_util_getSquareBonus((uc),(b),(c),(r)) #define util_userError(uc,err) \ (uc)->vtable->m_util_userError((uc),(err)) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 11af9250b..7ef2e211d 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1807,7 +1807,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) #endif model_setDictionary( g_globals.cGlobals.game.model, params->dict ); - + setSquareBonuses( &g_globals.cGlobals ); positionSizeStuff( &g_globals, width, height ); #ifndef XWFEATURE_STANDALONE_ONLY diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index ebaccce8e..092d5740c 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -486,6 +486,7 @@ createOrLoadObjects( GtkAppGlobals* globals ) } #endif model_setDictionary( globals->cGlobals.game.model, params->dict ); + setSquareBonuses( &globals->cGlobals ); model_setPlayerDicts( globals->cGlobals.game.model, ¶ms->dicts ); #ifdef XWFEATURE_SEARCHLIMIT diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c index b1df9cd12..886231376 100644 --- a/xwords4/linux/linuxutl.c +++ b/xwords4/linux/linuxutl.c @@ -80,6 +80,63 @@ linux_util_makeEmptyDict( XW_UtilCtxt* XP_UNUSED_DBG(uctx) ) #define TL BONUS_TRIPLE_LETTER #define TW BONUS_TRIPLE_WORD +static XWBonusType* +bonusesFor( XP_U16 boardSize, XP_U16* len ) +{ + static XWBonusType scrabbleBoard[] = { + TW,//EM,EM,DL,EM,EM,EM,TW, + EM,DW,//EM,EM,EM,TL,EM,EM, + + EM,EM,DW,//EM,EM,EM,DL,EM, + DL,EM,EM,DW,//EM,EM,EM,DL, + + EM,EM,EM,EM,DW,//EM,EM,EM, + EM,TL,EM,EM,EM,TL,//EM,EM, + + EM,EM,DL,EM,EM,EM,DL,//EM, + TW,EM,EM,DL,EM,EM,EM,DW, + }; /* scrabbleBoard */ + + static XWBonusType seventeen[] = { + TW,//EM,EM,DL,EM,EM,EM,TW, + EM,DW,//EM,EM,EM,TL,EM,EM, + + EM,EM,DW,//EM,EM,EM,DL,EM, + DL,EM,EM,DW,//EM,EM,EM,DL, + + EM,EM,EM,EM,DW,//EM,EM,EM, + EM,TL,EM,EM,EM,TL,//EM,EM, + + EM,EM,DL,EM,EM,EM,DL,//EM, + TW,EM,EM,DL,EM,EM,EM,DW, + TW,EM,EM,DL,EM,EM,EM,DW,DW, + }; /* scrabbleBoard */ + + XWBonusType* result = NULL; + if ( boardSize == 15 ) { + result = scrabbleBoard; + *len = VSIZE(scrabbleBoard); + } else if ( boardSize == 17 ) { + result = seventeen; + *len = VSIZE(seventeen); + } + + return result; +} + +#ifdef STREAM_VERS_BIGBOARD +void +setSquareBonuses( const CommonGlobals* cGlobals ) +{ + XP_U16 nBonuses; + XWBonusType* bonuses = + bonusesFor( cGlobals->params->gi.boardSize, &nBonuses ); + if ( !!bonuses ) { + model_setSquareBonuses( cGlobals->game.model, bonuses, nBonuses ); + } +} +#endif + static XWBonusType* parseBonusFile( XP_U16 nCols, const char* bonusFile ) { @@ -146,14 +203,13 @@ parseBonusFile( XP_U16 nCols, const char* bonusFile ) } static XWBonusType -linux_util_getSquareBonus( XW_UtilCtxt* uc, const ModelCtxt* model, +linux_util_getSquareBonus( XW_UtilCtxt* uc, XP_U16 nCols, XP_U16 col, XP_U16 row ) { static XWBonusType* parsedFile = NULL; XWBonusType result = EM; CommonGlobals* cGlobals = (CommonGlobals*)uc->closure; - XP_U16 nCols = model_numCols( model ); if ( NULL == parsedFile && NULL != cGlobals->params->bonusFile ) { if ( !parsedFile ) { parsedFile = parseBonusFile( nCols, cGlobals->params->bonusFile ); @@ -162,24 +218,10 @@ linux_util_getSquareBonus( XW_UtilCtxt* uc, const ModelCtxt* model, if ( NULL != parsedFile ) { result = parsedFile[(row*nCols) + col]; } else { + XP_U16 nEntries; + XWBonusType* scrabbleBoard = bonusesFor( 15, &nEntries ); + XP_U16 index, ii; - /* This must be static or won't compile under multilink (for Palm). - Fix! */ - static char scrabbleBoard[8*8] = { - TW,//EM,EM,DL,EM,EM,EM,TW, - EM,DW,//EM,EM,EM,TL,EM,EM, - - EM,EM,DW,//EM,EM,EM,DL,EM, - DL,EM,EM,DW,//EM,EM,EM,DL, - - EM,EM,EM,EM,DW,//EM,EM,EM, - EM,TL,EM,EM,EM,TL,//EM,EM, - - EM,EM,DL,EM,EM,EM,DL,//EM, - TW,EM,EM,DL,EM,EM,EM,DW, - }; /* scrabbleBoard */ - - XP_U16 nCols = model_numCols( model ); if ( col > (nCols/2) ) col = nCols - 1 - col; if ( row > (nCols/2) ) row = nCols - 1 - row; if ( col > row ) { @@ -192,8 +234,8 @@ linux_util_getSquareBonus( XW_UtilCtxt* uc, const ModelCtxt* model, index += ii; } - if ( index < VSIZE(scrabbleBoard) ) { - result = (XWBonusType)scrabbleBoard[index]; + if ( index < nEntries) { + result = scrabbleBoard[index]; } } return result; diff --git a/xwords4/linux/linuxutl.h b/xwords4/linux/linuxutl.h index 198d466fc..59e61d83b 100644 --- a/xwords4/linux/linuxutl.h +++ b/xwords4/linux/linuxutl.h @@ -1,6 +1,6 @@ -/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */ +/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */ /* - * Copyright 2000-2008 by Eric House (xwords@eehouse.org). All rights + * Copyright 2000 - 2011 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -25,6 +25,7 @@ #include "xptypes.h" #include "dictnry.h" #include "util.h" +#include "main.h" #ifdef DEBUG void linux_debugf(const char*, ...) @@ -41,4 +42,10 @@ const XP_UCHAR* linux_getErrString( UtilErrID id, XP_Bool* silent ); void formatConfirmTrade( const XP_UCHAR** tiles, XP_U16 nTiles, char* buf, XP_U16 buflen ); +#ifdef STREAM_VERS_BIGBOARD +void setSquareBonuses( const CommonGlobals* cGlobals ); +#else +# define setSquareBonuses( cg ) +#endif + #endif