/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ /* * Copyright 1997-2000 by Eric House (xwords@eehouse.org). All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef USE_STDIO # include # include #endif #include "comtypes.h" #include "dictnryp.h" #include "xwstream.h" #include "strutils.h" #include "game.h" #ifdef CPLUS extern "C" { #endif /***************************************************************************** * ****************************************************************************/ void setBlankTile( DictionaryCtxt* dctx ) { XP_U16 ii; dctx->blankTile = -1; /* no known blank */ for ( ii = 0; ii < dctx->nFaces; ++ii ) { const XP_UCHAR* facep = dctx->faceStarts[ii]; if ( *facep == 0 ) { XP_ASSERT( dctx->blankTile == -1 ); /* only one passes test? */ dctx->blankTile = (XP_S8)ii; #ifndef DEBUG break; #endif } } } /* setBlankTile */ /* #if defined BLANKS_FIRST || defined DEBUG */ XP_Bool dict_hasBlankTile( const DictionaryCtxt* dict ) { return dict->blankTile >= 0; } /* dict_hasBlankTile */ /* #endif */ Tile dict_getBlankTile( const DictionaryCtxt* dict ) { XP_ASSERT( dict_hasBlankTile(dict) ); return (Tile)dict->blankTile; } /* dict_getBlankTile */ XP_U16 dict_getTileValue( const DictionaryCtxt* dict, Tile tile ) { if ( (tile & TILE_VALUE_MASK) != tile ) { XP_ASSERT( tile == 32 && tile == dict_getBlankTile( dict ) ); } XP_ASSERT( tile < dict->nFaces ); tile *= 2; return dict->countsAndValues[tile+1]; } /* dict_getTileValue */ const XP_UCHAR* dict_getTileString( const DictionaryCtxt* dict, Tile tile ) { const XP_UCHAR* start; XP_ASSERT( tile < dict->nFaces ); start = dict->faceStarts[tile]; if ( IS_SPECIAL(*start) ) { start = dict->chars[(int)*start]; } return start; } XP_U16 dict_numTiles( const DictionaryCtxt* dict, Tile tile ) { tile *= 2; return dict->countsAndValues[tile]; } /* dict_numTiles */ XP_U16 dict_numTileFaces( const DictionaryCtxt* dict ) { return dict->nFaces; } /* dict_numTileFaces */ XP_U16 dict_tilesToString( const DictionaryCtxt* ctxt, const Tile* tiles, XP_U16 nTiles, XP_UCHAR* buf, XP_U16 bufSize ) { XP_UCHAR* bufp = buf; XP_U16 result = 0; if ( bufp != NULL ) { XP_UCHAR* end = bufp + bufSize; while ( nTiles-- ) { Tile tile = *tiles++; const XP_UCHAR* facep = dict_getTileString( ctxt, tile ); bufp += XP_SNPRINTF( bufp, end - bufp, XP_S, facep ); } if ( bufp < end ) { *bufp = '\0'; result = bufp - buf; } } return result; } /* dict_tilesToString */ /* dict_tileForString: used to map user keys to tiles in the tray. Returns * EMPTY_TILE if no match found. */ Tile dict_tileForString( const DictionaryCtxt* dict, const XP_UCHAR* key ) { XP_U16 nFaces = dict_numTileFaces( dict ); Tile tile = EMPTY_TILE; XP_U16 ii; for ( ii = 0; ii < nFaces; ++ii ) { if ( ii != dict->blankTile ) { const XP_UCHAR* facep = dict_getTileString( dict, ii ); if ( 0 == XP_STRCMP( facep, key ) ) { tile = (Tile)ii; break; } } } return tile; } /* dict_tileForChar */ XP_Bool dict_tilesAreSame( const DictionaryCtxt* dict1, const DictionaryCtxt* dict2 ) { XP_Bool result = XP_FALSE; Tile ii; XP_U16 nTileFaces = dict_numTileFaces( dict1 ); if ( nTileFaces == dict_numTileFaces( dict2 ) ) { for ( ii = 0; ii < nTileFaces; ++ii ) { const XP_UCHAR* face1; const XP_UCHAR* face2; if ( dict_getTileValue( dict1, ii ) != dict_getTileValue( dict2, ii ) ){ break; } #if 0 face1 = dict_getTileString( dict1, ii ); face2 = dict_getTileString( dict2, ii ); if ( 0 != XP_STRCMP( face1, face2 ) ) { break; } #else face1 = dict1->faceStarts[ii]; face2 = dict2->faceStarts[ii]; if ( IS_SPECIAL(*face1) != IS_SPECIAL(*face2) ) { break; } if ( IS_SPECIAL(*face1) ) { XP_UCHAR* chars1 = dict1->chars[(int)*face1]; XP_UCHAR* chars2 = dict2->chars[(int)*face2]; XP_U16 len = XP_STRLEN(chars1); if ( 0 != XP_STRNCMP( chars1, chars2, len ) ) { break; } } else if ( 0 != XP_STRCMP( face1, face2 ) ) { break; } #endif if ( dict_numTiles( dict1, ii ) != dict_numTiles( dict2, ii ) ) { break; } } result = ii == nTileFaces; /* did we get that far */ } return result; } /* dict_tilesAreSame */ #ifndef XWFEATURE_STANDALONE_ONLY static void unsplitFaces( const DictionaryCtxt* dict, XP_UCHAR* buf, XP_U16* bufsizep ) { XP_U16 ii; XP_U16 nUsed = 0; XP_U16 bufsize = *bufsizep; for ( ii = 0; ii < dict->nFaces; ++ii ) { const XP_UCHAR* facep = dict->faceStarts[ii]; if ( IS_SPECIAL(*facep) ) { buf[nUsed++] = *facep; } else { nUsed += XP_SNPRINTF( &buf[nUsed], bufsize - nUsed, "%s", facep ); } XP_ASSERT( nUsed < bufsize ); } *bufsizep = nUsed; } /* unsplitFaces */ void dict_writeToStream( const DictionaryCtxt* dict, XWStreamCtxt* stream ) { XP_U16 maxCount = 0; XP_U16 maxValue = 0; XP_U16 ii, nSpecials; XP_U16 maxCountBits, maxValueBits; XP_UCHAR buf[64]; XP_U16 nFaceBytes; /* Need to keep format identical for non-utf so new versions can play against old using not UTF8 dicts. The old ones won't even recognize UTF8 dicts as dicts, so there shouldn't be any attempts to connect with them having one open. */ stream_putBits( stream, 6, dict->nFaces ); for ( ii = 0; ii < dict->nFaces*2; ii+=2 ) { XP_U16 count, value; count = dict->countsAndValues[ii]; if ( maxCount < count ) { maxCount = count; } value = dict->countsAndValues[ii+1]; if ( maxValue < value ) { maxValue = value; } } maxCountBits = bitsForMax( maxCount ); maxValueBits = bitsForMax( maxValue ); stream_putBits( stream, 3, maxCountBits ); /* won't be bigger than 5 */ stream_putBits( stream, 3, maxValueBits ); for ( ii = 0; ii < dict->nFaces*2; ii+=2 ) { stream_putBits( stream, maxCountBits, dict->countsAndValues[ii] ); stream_putBits( stream, maxValueBits, dict->countsAndValues[ii+1] ); } nFaceBytes = sizeof(buf); unsplitFaces( dict, buf, &nFaceBytes ); if ( dict_isUTF8( dict ) ) { /* nBytes == nFaces for non-UTF8 dicts */ stream_putU8( stream, nFaceBytes ); } stream_putBytes( stream, buf, nFaceBytes ); for ( nSpecials = ii = 0; ii < dict->nFaces; ++ii ) { const XP_UCHAR* facep = dict->faceStarts[(Tile)ii]; if ( IS_SPECIAL( *facep ) ) { stringToStream( stream, dict->chars[nSpecials++] ); } } } /* dict_writeToStream */ #endif static void freeSpecials( DictionaryCtxt* dict ) { Tile tt; XP_U16 nSpecials; for ( nSpecials = tt = 0; tt < dict->nFaces; ++tt ) { const XP_UCHAR* facep = dict->faceStarts[tt]; if ( IS_SPECIAL( *facep ) ) { XP_ASSERT( !!dict->chars[nSpecials] ); XP_FREE( dict->mpool, dict->chars[nSpecials] ); if ( !!dict->bitmaps[nSpecials].largeBM ) { XP_FREE( dict->mpool, dict->bitmaps[nSpecials].largeBM ); } if ( !!dict->bitmaps[nSpecials].smallBM ) { XP_FREE( dict->mpool, dict->bitmaps[nSpecials].smallBM ); } ++nSpecials; } } if ( nSpecials > 0 ) { XP_FREE( dict->mpool, dict->chars ); XP_FREE( dict->mpool, dict->bitmaps ); } } /* freeSpecials */ static void common_destructor( DictionaryCtxt* dict ) { freeSpecials( dict ); XP_FREE( dict->mpool, dict->countsAndValues ); XP_FREE( dict->mpool, dict->faces ); XP_FREE( dict->mpool, dict->faceStarts ); XP_FREE( dict->mpool, dict ); } /* common_destructor */ #ifndef XWFEATURE_STANDALONE_ONLY void dict_loadFromStream( DictionaryCtxt* dict, XWStreamCtxt* stream ) { XP_U16 nFaceBytes, nFaces; XP_U16 maxCountBits, maxValueBits; XP_U16 ii, nSpecials; XP_UCHAR* localTexts[32]; XP_U16 streamVersion = stream_getVersion( stream ); XP_Bool isUTF8 = streamVersion >= STREAM_VERS_UTF8; XP_U8 utf8[96]; XP_ASSERT( !dict->destructor ); dict->destructor = common_destructor; dict->func_dict_getShortName = dict_getName; /* default */ nFaces = (XP_U8)stream_getBits( stream, 6 ); maxCountBits = (XP_U16)stream_getBits( stream, 3 ); maxValueBits = (XP_U16)stream_getBits( stream, 3 ); dict->nFaces = nFaces; dict->countsAndValues = (XP_U8*)XP_MALLOC( dict->mpool, sizeof(dict->countsAndValues[0]) * nFaces * 2 ); for ( ii = 0; ii < dict->nFaces*2; ii+=2 ) { dict->countsAndValues[ii] = (XP_U8)stream_getBits( stream, maxCountBits ); dict->countsAndValues[ii+1] = (XP_U8)stream_getBits( stream, maxValueBits ); } if ( isUTF8 ) { nFaceBytes = stream_getU8( stream ); } else { nFaceBytes = nFaces; } XP_ASSERT( nFaceBytes < VSIZE(utf8) ); stream_getBytes( stream, utf8, nFaceBytes ); dict->isUTF8 = isUTF8; dict_splitFaces( dict, utf8, nFaceBytes, nFaces ); for ( nSpecials = ii = 0; ii < nFaces; ++ii ) { const XP_UCHAR* facep = dict->faceStarts[ii]; if ( IS_SPECIAL( *facep ) ) { XP_UCHAR* txt = stringFromStream( dict->mpool, stream ); XP_ASSERT( !!txt ); localTexts[nSpecials] = txt; ++nSpecials; } } if ( nSpecials > 0 ) { dict->bitmaps = (SpecialBitmaps*)XP_MALLOC( dict->mpool, nSpecials * sizeof(*dict->bitmaps) ); XP_MEMSET( dict->bitmaps, 0, nSpecials * sizeof(*dict->bitmaps) ); dict->chars = (XP_UCHAR**)XP_MALLOC( dict->mpool, nSpecials * sizeof(*dict->chars) ); XP_MEMCPY(dict->chars, localTexts, nSpecials * sizeof(*dict->chars)); } setBlankTile( dict ); } /* dict_loadFromStream */ #endif const XP_UCHAR* dict_getName( const DictionaryCtxt* dict ) { XP_ASSERT( !!dict ); XP_ASSERT( !!dict->name ); return dict->name; } /* dict_getName */ XP_Bool dict_isUTF8( const DictionaryCtxt* dict ) { XP_ASSERT( !!dict ); return dict->isUTF8; } XP_Bool dict_faceIsBitmap( const DictionaryCtxt* dict, Tile tile ) { const XP_UCHAR* facep = dict->faceStarts[tile]; return IS_SPECIAL(*facep); } /* dict_faceIsBitmap */ void dict_getFaceBitmaps( const DictionaryCtxt* dict, Tile tile, XP_Bitmaps* bmps ) { SpecialBitmaps* bitmaps; const XP_UCHAR* facep = dict->faceStarts[tile]; XP_ASSERT( dict_faceIsBitmap( dict, tile ) ); XP_ASSERT( !!dict->bitmaps ); bitmaps = &dict->bitmaps[(XP_U16)*facep]; bmps->nBitmaps = 2; bmps->bmps[0] = bitmaps->smallBM; bmps->bmps[1] = bitmaps->largeBM; } /* dict_getFaceBitmaps */ #ifdef TALL_FONTS XP_LangCode dict_getLangCode( const DictionaryCtxt* dict ) { return dict->langCode; } #endif #ifdef STUBBED_DICT #define BLANK_FACE '\0' static XP_U8 stub_english_data[] = { /* count value face */ 9, 1, 'A', 2, 3, 'B', 2, 3, 'C', 4, 2, 'D', 12, 1, 'E', 2, 4, 'F', 3, 2, 'G', 2, 4, 'H', 9, 1, 'I', 1, 8, 'J', 1, 5, 'K', 4, 1, 'L', 2, 3, 'M', 6, 1, 'N', 8, 1, 'O', 2, 3, 'P', 1, 10, 'Q', 6, 1, 'R', 4, 1, 'S', 6, 1, 'T', 4, 1, 'U', 2, 4, 'V', 2, 4, 'W', 1, 8, 'X', 2, 4, 'Y', 1, 10, 'Z', 2, 0, BLANK_FACE, /* BLANK1 */ }; void setStubbedSpecials( DictionaryCtxt* dict ) { dict->chars = (XP_UCHAR**)XP_MALLOC( dict->mpool, sizeof(char*) ); dict->chars[0] = "_"; } /* setStubbedSpecials */ void destroy_stubbed_dict( DictionaryCtxt* dict ) { XP_FREE( dict->mpool, dict->countsAndValues ); XP_FREE( dict->mpool, dict->faces16 ); XP_FREE( dict->mpool, dict->chars ); XP_FREE( dict->mpool, dict->name ); XP_FREE( dict->mpool, dict->bitmaps ); XP_FREE( dict->mpool, dict ); } /* destroy_stubbed_dict */ DictionaryCtxt* make_stubbed_dict( MPFORMAL_NOCOMMA ) { DictionaryCtxt* dict = (DictionaryCtxt*)XP_MALLOC( mpool, sizeof(*dict) ); XP_U8* data = stub_english_data; XP_U16 datasize = sizeof(stub_english_data); XP_U16 i; XP_MEMSET( dict, 0, sizeof(*dict) ); MPASSIGN( dict->mpool, mpool ); dict->name = copyString( mpool, "Stub dictionary" ); dict->nFaces = datasize/3; dict->destructor = destroy_stubbed_dict; dict->faces16 = (XP_CHAR16*) XP_MALLOC( mpool, dict->nFaces * sizeof(dict->faces16[0]) ); for ( i = 0; i < datasize/3; ++i ) { dict->faces16[i] = (XP_CHAR16)data[(i*3)+2]; } dict->countsAndValues = (XP_U8*)XP_MALLOC( mpool, dict->nFaces*2 ); for ( i = 0; i < datasize/3; ++i ) { dict->countsAndValues[i*2] = data[(i*3)]; dict->countsAndValues[(i*2)+1] = data[(i*3)+1]; } dict->bitmaps = (SpecialBitmaps*)XP_MALLOC( mpool, sizeof(SpecialBitmaps) ); dict->bitmaps->largeBM = dict->bitmaps->largeBM = NULL; setStubbedSpecials( dict ); setBlankTile( dict ); return dict; } /* make_subbed_dict */ #endif /* STUBBED_DICT */ static array_edge* dict_super_edge_for_index( const DictionaryCtxt* dict, XP_U32 index ) { array_edge* result; if ( index == 0 ) { result = NULL; } else { XP_ASSERT( index < dict->numEdges ); #ifdef NODE_CAN_4 /* avoid long-multiplication lib call on Palm... */ if ( dict->nodeSize == 3 ) { index += (index << 1); } else { XP_ASSERT( dict->nodeSize == 4 ); index <<= 2; } #else index += (index << 1); #endif result = &dict->base[index]; } return result; } /* dict_edge_for_index */ static array_edge* dict_super_getTopEdge( const DictionaryCtxt* dict ) { return dict->topEdge; } /* dict_super_getTopEdge */ void dict_super_init( DictionaryCtxt* ctxt ) { /* subclass may change these later.... */ ctxt->func_edge_for_index = dict_super_edge_for_index; ctxt->func_dict_getTopEdge = dict_super_getTopEdge; ctxt->func_dict_getShortName = dict_getName; } /* dict_super_init */ #ifdef CPLUS } #endif