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; diff --git a/xwords4/android/XWords4/jni/xwjni.c b/xwords4/android/XWords4/jni/xwjni.c index d62c7a267..329f2eac1 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; } 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, 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; } diff --git a/xwords4/common/board.c b/xwords4/common/board.c index f877b0a03..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, @@ -1198,7 +1197,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 +1587,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..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 @@ -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 )) { @@ -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/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..dd8313f22 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,24 @@ 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 + +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/game.c b/xwords4/common/game.c index b02d58ee0..62e3e2178 100644 --- a/xwords4/common/game.c +++ b/xwords4/common/game.c @@ -426,13 +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, 4 ); + 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 ) { @@ -494,7 +499,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..15601a24a 100644 --- a/xwords4/common/game.h +++ b/xwords4/common/game.h @@ -31,6 +31,9 @@ extern "C" { #endif +#if MAX_COLS > 16 +# define STREAM_VERS_BIGBOARD 0x12 +#endif #define STREAM_SAVE_PREVWORDS 0x11 #define STREAM_VERS_SERVER_SAVES_TOSHOW 0x10 #define STREAM_VERS_PLAYERDICTS 0x0F @@ -53,7 +56,11 @@ extern "C" { #define STREAM_VERS_41B4 0x02 #define STREAM_VERS_405 0x01 -#define CUR_STREAM_VERS STREAM_SAVE_PREVWORDS +#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 c40976c8c..4745f414d 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -115,15 +115,21 @@ 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; XP_U16 version = stream_getVersion( stream ); +#ifdef STREAM_VERS_BIGBOARD + nColsNBits = STREAM_VERS_BIGBOARD > version ? 4 : NUMCOLS_NBITS; +#else + nColsNBits = NUMCOLS_NBITS; +#endif 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 ); @@ -138,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, @@ -145,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; @@ -157,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 ); @@ -165,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 */ @@ -240,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, @@ -815,6 +923,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]; @@ -831,8 +945,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 */ @@ -847,8 +961,15 @@ void model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum, XWStreamCtxt* stream ) { - XP_U16 numTiles; + 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 ); @@ -856,12 +977,12 @@ 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 ); - 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 @@ -1931,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 ); @@ -1952,6 +2074,7 @@ model_writeGameHistory( ModelCtxt* model, XWStreamCtxt* stream, tmpModel = makeTmpModel( model, stream, printMovePre, printMovePost, &closure ); + returnSquareBonuses( tmpModel ); model_destroy( tmpModel ); if ( gameOver ) { @@ -2215,6 +2338,11 @@ loadPlayerCtxt( XWStreamCtxt* stream, XP_U16 version, PlayerCtxt* pc ) { PendingTile* pt; XP_U16 nTiles; +#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 ); @@ -2230,8 +2358,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..ab01af9fd 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 @@ -265,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/server.c b/xwords4/common/server.c index 2e911ec63..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,12 +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( const ServerCtxt* server, XWStreamCtxt* stream, + XW_Proto proto ); #endif #define PICK_NEXT -1 @@ -230,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* @@ -280,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 @@ -301,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* @@ -515,7 +544,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( server, stream, XWPROTO_DEVICE_REGISTRATION ); nPlayers = gi->nPlayers; XP_ASSERT( nPlayers > 0 ); @@ -531,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); @@ -543,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) ); @@ -593,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 */ @@ -608,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 @@ -999,7 +1073,7 @@ findFirstPending( ServerCtxt* server, ServerPlayer** playerP ) return lp; } /* findFirstPending */ -static void +static XP_S8 registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream ) { XP_S8 deviceIndex; @@ -1039,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,6 +1173,7 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream ) /* version; any dependencies here? */ XP_U8 streamVersion = stream_getU8( stream ); + XP_LOGF( "%s: set streamVersion to %d", __func__, streamVersion ); stream_setVersion( stream, streamVersion ); gameID = stream_getU32( stream ); @@ -1234,10 +1312,8 @@ server_sendInitialMessage( ServerCtxt* server ) DictionaryCtxt* dict = model_getDictionary(model); XP_ASSERT( !!stream ); stream_open( stream ); - stream_putBits( stream, XWPROTO_NBITS, XWPROTO_CLIENT_SETUP ); + writeProto( server, stream, XWPROTO_CLIENT_SETUP ); - /* write version for server's benefit; use old version until format - changes */ stream_putU8( stream, CUR_STREAM_VERS ); XP_LOGF( "putting gameID %lx into msg", gameID ); @@ -1323,6 +1399,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 ); @@ -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 ); - stream_putBits( stream, XWPROTO_NBITS, code ); + writeProto( server, stream, code ); return stream; } /* messageStreamWithHeader */ @@ -2440,15 +2519,46 @@ server_handleUndo( ServerCtxt* server ) } /* server_handleUndo */ #ifndef XWFEATURE_STANDALONE_ONLY +static void +writeProto( const ServerCtxt* server, XWStreamCtxt* stream, XW_Proto 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 ); +} + +static XW_Proto +readProto( ServerCtxt* server, XWStreamCtxt* stream ) +{ + 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 ); + } + server->nv.streamVersion = version; +#else + XP_USE(server); +#endif + 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( server, 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 @@ -2535,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 */ } 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/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/Makefile b/xwords4/linux/Makefile index 80b8d072a..2f90dc32e 100644 --- a/xwords4/linux/Makefile +++ b/xwords4/linux/Makefile @@ -94,6 +94,7 @@ DEFINES += -DTEXT_MODEL DEFINES += -DXWFEATURE_WALKDICT DEFINES += -DXWFEATURE_WALKDICT_FILTER DEFINES += -DXWFEATURE_DICTSANITY +#DEFINES += -DMAX_ROWS=32 ifdef CURSES_CELL_HT DEFINES += -DCURSES_CELL_HT=$(CURSES_CELL_HT) diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 798236efe..7ef2e211d 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; } } @@ -1805,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 dcb995a4c..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 @@ -528,6 +529,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 +544,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 +570,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 +599,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 +1252,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 +1343,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 +1375,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 +2320,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 \ 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; diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c index 40a435489..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,25 +218,12 @@ 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 */ - - if ( col > 7 ) col = 14 - col; - if ( row > 7 ) row = 14 - row; + 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; @@ -191,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