mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-28 09:58:30 +01:00
578 lines
16 KiB
C
578 lines
16 KiB
C
/* -*-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 <stdio.h>
|
|
# include <stdlib.h>
|
|
#endif
|
|
|
|
#include "comtypes.h"
|
|
#include "dictnryp.h"
|
|
#include "xwstream.h"
|
|
#include "strutils.h"
|
|
#include "game.h"
|
|
|
|
#ifdef CPLUS
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
****************************************************************************/
|
|
void
|
|
setBlankTile( DictionaryCtxt* dict )
|
|
{
|
|
XP_U16 ii;
|
|
|
|
dict->blankTile = -1; /* no known blank */
|
|
|
|
for ( ii = 0; ii < dict->nFaces; ++ii ) {
|
|
if ( dict->facePtrs[ii][0] == 0 ) {
|
|
XP_ASSERT( dict->blankTile == -1 ); /* only one passes test? */
|
|
dict->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 */
|
|
|
|
static const XP_UCHAR*
|
|
dict_getTileStringRaw( const DictionaryCtxt* dict, Tile tile )
|
|
{
|
|
XP_ASSERT( tile < dict->nFaces );
|
|
return dict->facePtrs[tile];
|
|
}
|
|
|
|
const XP_UCHAR*
|
|
dict_getTileString( const DictionaryCtxt* dict, Tile tile )
|
|
{
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
if ( IS_SPECIAL(*facep) ) {
|
|
facep = dict->chars[(XP_U16)*facep];
|
|
}
|
|
return facep;
|
|
}
|
|
|
|
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* dict, const Tile* tiles,
|
|
XP_U16 nTiles, XP_UCHAR* buf, XP_U16 bufSize )
|
|
{
|
|
XP_UCHAR* bufp = buf;
|
|
XP_UCHAR* end = bufp + bufSize;
|
|
XP_U16 result = 0;
|
|
|
|
while ( nTiles-- ) {
|
|
Tile tile = *tiles++;
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
|
|
if ( IS_SPECIAL(*facep) ) {
|
|
XP_UCHAR* chars = dict->chars[(XP_U16)*facep];
|
|
XP_U16 len = XP_STRLEN( chars );
|
|
if ( bufp + len >= end ) {
|
|
bufp = NULL;
|
|
break;
|
|
}
|
|
XP_MEMCPY( bufp, chars, len );
|
|
bufp += len;
|
|
} else {
|
|
XP_ASSERT ( tile != dict->blankTile ); /* printing blank should be
|
|
handled by specials
|
|
mechanism */
|
|
if ( bufp + 1 >= end ) {
|
|
bufp = NULL;
|
|
break;
|
|
}
|
|
bufp += XP_SNPRINTF( bufp, end - bufp, XP_S, facep );
|
|
}
|
|
}
|
|
|
|
if ( bufp != NULL && 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;
|
|
}
|
|
face1 = dict_getTileStringRaw( dict1, ii );
|
|
face2 = dict_getTileStringRaw( dict2, 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;
|
|
}
|
|
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
|
|
ucharsToNarrow( 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_getTileStringRaw( dict, ii );
|
|
if ( IS_SPECIAL(*facep) ) {
|
|
buf[nUsed++] = *facep;
|
|
} else {
|
|
nUsed += XP_SNPRINTF( &buf[nUsed], bufsize - nUsed, "%s", facep );
|
|
}
|
|
XP_ASSERT( nUsed < bufsize );
|
|
}
|
|
buf[nUsed] = 0;
|
|
*bufsizep = nUsed;
|
|
}
|
|
|
|
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 nBytes;
|
|
|
|
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] );
|
|
}
|
|
|
|
/* Stream format of the faces is unchanged: chars run together, which
|
|
* happens to equal utf-8 for ascii. But now there may be more than one
|
|
* byte per face. Old code assumes that, but compatibility is ensured by
|
|
* the caller which will not accept an incoming message if the version's
|
|
* too new. And utf-8 dicts are flagged as newer by the sender.
|
|
*/
|
|
|
|
nBytes = sizeof(buf);
|
|
ucharsToNarrow( dict, buf, &nBytes );
|
|
stream_putU8( stream, nBytes );
|
|
stream_putBytes( stream, buf, nBytes );
|
|
|
|
for ( nSpecials = ii = 0; ii < dict->nFaces; ++ii ) {
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, (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_getTileStringRaw( dict, 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->facePtrs );
|
|
|
|
XP_FREE( dict->mpool, dict );
|
|
} /* common_destructor */
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
void
|
|
dict_loadFromStream( DictionaryCtxt* dict, XWStreamCtxt* stream )
|
|
{
|
|
XP_U8 nFaces, nFaceBytes;
|
|
XP_U16 maxCountBits, maxValueBits;
|
|
XP_U16 ii, nSpecials;
|
|
XP_UCHAR* localTexts[32];
|
|
XP_U8 utf8[MAX_UNIQUE_TILES];
|
|
|
|
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 );
|
|
}
|
|
|
|
nFaceBytes = (XP_U8)stream_getU8( stream );
|
|
XP_ASSERT( nFaceBytes < VSIZE(utf8) );
|
|
stream_getBytes( stream, utf8, nFaceBytes );
|
|
dict->isUTF8 = XP_TRUE; /* need to communicate this in stream */
|
|
dict_splitFaces( dict, utf8, nFaceBytes, nFaces );
|
|
|
|
for ( nSpecials = ii = 0; ii < nFaces; ++ii ) {
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, (Tile)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_getTileStringRaw( dict, 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_getTileStringRaw( dict, 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* dict )
|
|
{
|
|
/* subclass may change these later.... */
|
|
dict->func_edge_for_index = dict_super_edge_for_index;
|
|
dict->func_dict_getTopEdge = dict_super_getTopEdge;
|
|
dict->func_dict_getShortName = dict_getName;
|
|
} /* dict_super_init */
|
|
|
|
#ifdef CPLUS
|
|
}
|
|
#endif
|