2011-10-25 03:27:16 +02:00
|
|
|
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
|
2003-11-01 06:35:29 +01:00
|
|
|
/*
|
2022-03-15 23:03:03 +01:00
|
|
|
* Copyright 1997 - 2022 by Eric House (xwords@eehouse.org). All rights
|
2011-10-25 03:27:16 +02:00
|
|
|
* reserved.
|
2003-11-01 06:35:29 +01:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef USE_STDIO
|
|
|
|
# include <stdio.h>
|
|
|
|
# include <stdlib.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "comtypes.h"
|
|
|
|
#include "dictnryp.h"
|
2011-11-01 04:56:48 +01:00
|
|
|
#include "dictiter.h"
|
2003-11-01 06:35:29 +01:00
|
|
|
#include "xwstream.h"
|
|
|
|
#include "strutils.h"
|
2011-11-01 02:30:55 +01:00
|
|
|
#include "dictiter.h"
|
2009-04-05 21:02:21 +02:00
|
|
|
#include "game.h"
|
2020-05-01 18:18:27 +02:00
|
|
|
#include "dbgutil.h"
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
#ifdef CPLUS
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
2014-03-09 05:57:27 +01:00
|
|
|
|
2020-05-01 18:18:27 +02:00
|
|
|
static XP_Bool makeBitmap( XP_U8 const** ptrp, const XP_U8* end );
|
2022-03-15 23:03:03 +01:00
|
|
|
static XP_U8* getCountsFor( const DictionaryCtxt* dict, XP_U16 nCols );
|
2020-05-01 18:18:27 +02:00
|
|
|
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
const DictionaryCtxt*
|
|
|
|
p_dict_ref( const DictionaryCtxt* dict, XWEnv XP_UNUSED(xwe)
|
2014-03-11 02:59:10 +01:00
|
|
|
#ifdef DEBUG_REF
|
|
|
|
,const char* func, const char* file, int line
|
|
|
|
#endif
|
|
|
|
)
|
2014-03-09 05:57:27 +01:00
|
|
|
{
|
|
|
|
if ( !!dict ) {
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
DictionaryCtxt* _dict = (DictionaryCtxt*)dict;
|
|
|
|
pthread_mutex_lock( &_dict->mutex );
|
|
|
|
++_dict->refCount;
|
2014-08-21 16:15:20 +02:00
|
|
|
#ifdef DEBUG_REF
|
2021-02-08 01:31:32 +01:00
|
|
|
XP_LOGFF( "(dict=%p): refCount now %d (from line %d of %s() in %s)",
|
|
|
|
dict, dict->refCount, line, func, file );
|
2014-08-21 16:15:20 +02:00
|
|
|
#endif
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
pthread_mutex_unlock( &_dict->mutex );
|
2014-03-09 05:57:27 +01:00
|
|
|
}
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
p_dict_unref( const DictionaryCtxt* dict, XWEnv xwe
|
2014-03-11 02:59:10 +01:00
|
|
|
#ifdef DEBUG_REF
|
|
|
|
,const char* func, const char* file, int line
|
|
|
|
#endif
|
|
|
|
)
|
2014-03-09 05:57:27 +01:00
|
|
|
{
|
|
|
|
if ( !!dict ) {
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
DictionaryCtxt* _dict = (DictionaryCtxt*)dict;
|
|
|
|
pthread_mutex_lock( &_dict->mutex );
|
2021-03-12 19:37:53 +01:00
|
|
|
XP_ASSERT( 0 != _dict->refCount );
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
--_dict->refCount;
|
|
|
|
XP_ASSERT( 0 <= _dict->refCount );
|
2014-08-21 16:15:20 +02:00
|
|
|
#ifdef DEBUG_REF
|
2021-05-14 20:32:08 +02:00
|
|
|
XP_LOGFF( "(dict=%p): refCount now %d (from line %d of %s() in %s)",
|
|
|
|
dict, dict->refCount, line, func, file );
|
2014-08-21 16:15:20 +02:00
|
|
|
#endif
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
pthread_mutex_unlock( &_dict->mutex );
|
|
|
|
if ( 0 == _dict->refCount ) {
|
2020-08-17 04:29:08 +02:00
|
|
|
/* There's a race here. If another thread locks the mutex we'll
|
|
|
|
still destroy the dict (and the locked mutex!!!) PENDING */
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
pthread_mutex_destroy( &_dict->mutex );
|
|
|
|
(*dict->destructor)( _dict, xwe );
|
2014-03-09 05:57:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-04-25 20:34:58 +02:00
|
|
|
dict_unref_all( PlayerDicts* pd, XWEnv xwe )
|
2014-03-09 05:57:27 +01:00
|
|
|
{
|
|
|
|
XP_U16 ii;
|
|
|
|
for ( ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {
|
2020-04-25 20:34:58 +02:00
|
|
|
dict_unref( pd->dicts[ii], xwe );
|
2014-03-09 05:57:27 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-01 18:18:27 +02:00
|
|
|
|
2022-04-21 06:00:51 +02:00
|
|
|
static XP_Bool
|
|
|
|
getNullTermParam( DictionaryCtxt* XP_UNUSED_DBG(dctx), XP_UCHAR** out,
|
|
|
|
const XP_U8** ptrp, const XP_U8* end )
|
2020-05-01 18:18:27 +02:00
|
|
|
{
|
2022-04-21 06:00:51 +02:00
|
|
|
const XP_U8* ptr = *ptrp;
|
|
|
|
XP_Bool success = ptr < end;
|
|
|
|
if ( success ) {
|
|
|
|
XP_U16 len = 1 + XP_STRLEN( (XP_UCHAR*)ptr );
|
|
|
|
success = ptr + len <= end;
|
|
|
|
if ( success ) {
|
|
|
|
*out = XP_MALLOC( dctx->mpool, len );
|
|
|
|
XP_MEMCPY( *out, ptr, len );
|
|
|
|
*ptrp += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
XP_ASSERT( success || NULL == *out );
|
|
|
|
return success;
|
2020-05-01 18:18:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
loadSpecialData( DictionaryCtxt* ctxt, XP_U8 const** ptrp,
|
|
|
|
const XP_U8* end )
|
|
|
|
{
|
|
|
|
LOG_FUNC();
|
|
|
|
XP_Bool success = XP_TRUE;
|
|
|
|
XP_U16 nSpecials = countSpecials( ctxt );
|
|
|
|
XP_U8 const* ptr = *ptrp;
|
|
|
|
XP_UCHAR** texts;
|
|
|
|
XP_UCHAR** textEnds;
|
|
|
|
SpecialBitmaps* bitmaps;
|
|
|
|
|
|
|
|
texts = (XP_UCHAR**)XP_MALLOC( ctxt->mpool,
|
|
|
|
nSpecials * sizeof(*texts) );
|
|
|
|
textEnds = (XP_UCHAR**)XP_MALLOC( ctxt->mpool,
|
|
|
|
nSpecials * sizeof(*textEnds) );
|
|
|
|
|
|
|
|
bitmaps = (SpecialBitmaps*)
|
|
|
|
XP_CALLOC( ctxt->mpool, nSpecials * sizeof(*bitmaps) );
|
|
|
|
|
|
|
|
for ( Tile ii = 0; ii < ctxt->nFaces; ++ii ) {
|
|
|
|
const XP_UCHAR* facep = ctxt->facePtrs[(short)ii];
|
|
|
|
if ( IS_SPECIAL(*facep) ) {
|
|
|
|
/* get the string */
|
|
|
|
CHECK_PTR( ptr, 1, end, error );
|
|
|
|
XP_U8 txtlen = *ptr++;
|
|
|
|
CHECK_PTR( ptr, txtlen, end, error );
|
|
|
|
XP_UCHAR* text = (XP_UCHAR*)XP_MALLOC(ctxt->mpool, txtlen+1);
|
|
|
|
texts[(int)*facep] = text;
|
|
|
|
textEnds[(int)*facep] = text + txtlen + 1;
|
|
|
|
XP_MEMCPY( text, ptr, txtlen );
|
|
|
|
ptr += txtlen;
|
|
|
|
text[txtlen] = '\0';
|
|
|
|
XP_ASSERT( *facep < nSpecials ); /* firing */
|
|
|
|
|
|
|
|
/* This little hack is safe because all bytes but the first in a
|
|
|
|
multi-byte utf-8 char have the high bit set. SYNONYM_DELIM
|
|
|
|
does not have its high bit set */
|
|
|
|
XP_ASSERT( 0 == (SYNONYM_DELIM & 0x80) );
|
|
|
|
for ( ; '\0' != *text; ++text ) {
|
|
|
|
if ( *text == SYNONYM_DELIM ) {
|
|
|
|
*text = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !makeBitmap( &ptr, end ) ) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if ( !makeBitmap( &ptr, end ) ) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
error:
|
|
|
|
success = XP_FALSE;
|
|
|
|
done:
|
|
|
|
ctxt->chars = texts;
|
|
|
|
ctxt->charEnds = textEnds;
|
|
|
|
ctxt->bitmaps = bitmaps;
|
|
|
|
|
|
|
|
*ptrp = ptr;
|
|
|
|
return success;
|
|
|
|
} /* loadSpecialData */
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* end )
|
|
|
|
{
|
|
|
|
const XP_U8* ptr = *ptrp;
|
|
|
|
XP_Bool hasHeader = XP_FALSE;
|
|
|
|
XP_Bool isUTF8 = XP_FALSE;
|
|
|
|
XP_U16 charSize;
|
|
|
|
|
|
|
|
XP_U16 flags;
|
|
|
|
XP_Bool formatOk = sizeof(flags) <= end - ptr;
|
|
|
|
if ( formatOk ) {
|
|
|
|
XP_MEMCPY( &flags, ptr, sizeof(flags) );
|
|
|
|
ptr += sizeof( flags );
|
|
|
|
flags = XP_NTOHS(flags);
|
|
|
|
|
|
|
|
XP_LOGFF( "flags=0X%X", flags );
|
|
|
|
hasHeader = 0 != (DICT_HEADER_MASK & flags);
|
|
|
|
/* if ( hasHeader ) { */
|
|
|
|
/* flags &= ~DICT_HEADER_MASK; */
|
|
|
|
/* } */
|
|
|
|
|
|
|
|
XP_U8 nodeSize = 4;
|
|
|
|
switch ( flags & 0x0007 ) {
|
|
|
|
case 0x0001:
|
|
|
|
nodeSize = 3;
|
|
|
|
charSize = 1;
|
|
|
|
dctx->is_4_byte = XP_FALSE;
|
|
|
|
break;
|
|
|
|
case 0x0002:
|
|
|
|
nodeSize = 3;
|
|
|
|
charSize = 2;
|
|
|
|
dctx->is_4_byte = XP_FALSE;
|
|
|
|
break;
|
|
|
|
case 0x0003:
|
|
|
|
charSize = 2;
|
|
|
|
dctx->is_4_byte = XP_TRUE;
|
|
|
|
break;
|
|
|
|
case 0x0004:
|
|
|
|
nodeSize = 3;
|
|
|
|
isUTF8 = XP_TRUE;
|
|
|
|
dctx->is_4_byte = XP_FALSE;
|
|
|
|
break;
|
|
|
|
case 0x0005:
|
|
|
|
isUTF8 = XP_TRUE;
|
|
|
|
dctx->is_4_byte = XP_TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
formatOk = XP_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dctx->isUTF8 = isUTF8;
|
|
|
|
dctx->nodeSize = nodeSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( formatOk ) {
|
|
|
|
XP_U8 numFaceBytes, numFaces;
|
|
|
|
|
|
|
|
if ( hasHeader ) {
|
2022-03-15 23:03:03 +01:00
|
|
|
const XP_U8* headerEnd;
|
|
|
|
{
|
|
|
|
XP_U16 headerLen;
|
|
|
|
XP_MEMCPY( &headerLen, ptr, sizeof(headerLen) );
|
|
|
|
ptr += sizeof(headerLen);
|
|
|
|
headerLen = XP_NTOHS( headerLen );
|
|
|
|
headerEnd = ptr + headerLen;
|
|
|
|
}
|
2020-05-01 18:18:27 +02:00
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_U32 wordCount;
|
2020-05-04 17:33:15 +02:00
|
|
|
XP_MEMCPY( &wordCount, ptr, sizeof(wordCount) );
|
2020-05-01 18:18:27 +02:00
|
|
|
ptr += sizeof(wordCount);
|
|
|
|
dctx->nWords = XP_NTOHL( wordCount );
|
|
|
|
XP_DEBUGF( "dict contains %d words", dctx->nWords );
|
|
|
|
|
2022-04-21 22:26:26 +02:00
|
|
|
XP_ASSERT( ptr <= headerEnd );
|
|
|
|
if ( !getNullTermParam( dctx, &dctx->desc, &ptr, headerEnd ) ) {
|
2022-03-14 18:28:08 +01:00
|
|
|
XP_LOGFF( "no note" );
|
2022-03-15 23:03:03 +01:00
|
|
|
goto done;
|
2020-05-01 18:18:27 +02:00
|
|
|
}
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_ASSERT( ptr < headerEnd );
|
2020-05-04 17:33:15 +02:00
|
|
|
|
2022-04-21 06:00:51 +02:00
|
|
|
if ( !getNullTermParam( dctx, &dctx->md5Sum, &ptr, headerEnd ) ) {
|
2022-04-21 22:26:26 +02:00
|
|
|
XP_LOGFF( "no md5Sum" );
|
2022-04-21 06:00:51 +02:00
|
|
|
goto done;
|
|
|
|
}
|
2020-05-04 17:33:15 +02:00
|
|
|
XP_U16 headerFlags = 0;
|
2022-03-15 23:03:03 +01:00
|
|
|
if ( ptr + sizeof(headerFlags) > headerEnd ) {
|
|
|
|
goto done;
|
2020-05-04 17:33:15 +02:00
|
|
|
}
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_MEMCPY( &headerFlags, ptr, sizeof(headerFlags) );
|
|
|
|
headerFlags = XP_NTOHS( headerFlags );
|
|
|
|
ptr += sizeof(headerFlags);
|
|
|
|
|
2020-05-04 17:33:15 +02:00
|
|
|
XP_LOGFF( "setting headerFlags: 0x%x", headerFlags );
|
|
|
|
dctx->headerFlags = headerFlags;
|
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
if ( ptr == headerEnd ) {
|
|
|
|
goto done;
|
|
|
|
}
|
2022-05-28 18:00:20 +02:00
|
|
|
if ( getNullTermParam( dctx, &dctx->isoCode, &ptr, headerEnd )
|
|
|
|
&& getNullTermParam( dctx, &dctx->langName, &ptr, headerEnd ) ) {
|
|
|
|
XP_LOGFF( "got langName: %s; isoCode: %s", dctx->langName,
|
|
|
|
dctx->isoCode );
|
2022-04-21 06:00:51 +02:00
|
|
|
} else {
|
2022-03-15 23:03:03 +01:00
|
|
|
goto done;
|
|
|
|
}
|
2022-04-21 06:00:51 +02:00
|
|
|
XP_U8 othersLen = *ptr++;
|
|
|
|
if ( 0 < othersLen ) {
|
|
|
|
XP_ASSERT( ptr + othersLen <= headerEnd );
|
|
|
|
if ( ptr + othersLen <= headerEnd ) {
|
|
|
|
dctx->otherCounts = XP_MALLOC( dctx->mpool, othersLen );
|
|
|
|
dctx->otherCountsEnd = dctx->otherCounts + othersLen;
|
|
|
|
XP_MEMCPY( dctx->otherCounts, ptr, othersLen );
|
|
|
|
ptr += othersLen;
|
2022-03-15 23:03:03 +01:00
|
|
|
}
|
2020-05-04 17:33:15 +02:00
|
|
|
}
|
2020-05-01 18:18:27 +02:00
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
done:
|
|
|
|
if ( ptr < headerEnd ) {
|
|
|
|
XP_LOGFF( "skipping %zu bytes of header", headerEnd - ptr );
|
|
|
|
}
|
|
|
|
ptr = headerEnd;
|
2020-05-01 18:18:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( isUTF8 ) {
|
|
|
|
numFaceBytes = *ptr++;
|
|
|
|
}
|
|
|
|
numFaces = *ptr++;
|
|
|
|
if ( !isUTF8 ) {
|
|
|
|
numFaceBytes = numFaces * charSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( NULL == dctx->md5Sum
|
|
|
|
#ifdef DEBUG
|
|
|
|
|| XP_TRUE
|
|
|
|
#endif
|
|
|
|
) {
|
|
|
|
XP_UCHAR checksum[256];
|
|
|
|
// XP_LOGFF( "figuring checksum with len: %uz", end - ptr );
|
|
|
|
computeChecksum( dctx, xwe, ptr, end - ptr, checksum );
|
|
|
|
if ( NULL == dctx->md5Sum ) {
|
|
|
|
dctx->md5Sum = copyString( dctx->mpool, checksum );
|
|
|
|
} else {
|
2021-02-03 17:47:23 +01:00
|
|
|
#ifndef PLATFORM_WASM
|
2020-05-01 18:18:27 +02:00
|
|
|
XP_ASSERT( 0 == XP_STRCMP( dctx->md5Sum, checksum ) );
|
2021-02-03 17:47:23 +01:00
|
|
|
#endif
|
2020-05-01 18:18:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dctx->nFaces = numFaces;
|
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
dctx->values = XP_MALLOC( dctx->mpool, numFaces);
|
2022-04-22 16:59:50 +02:00
|
|
|
XP_U8* counts15 = XP_MALLOC( dctx->mpool, numFaces);
|
2022-03-15 23:03:03 +01:00
|
|
|
dctx->counts[15>>1] = counts15;
|
|
|
|
|
2020-05-01 18:18:27 +02:00
|
|
|
XP_U16 facesSize = numFaceBytes;
|
|
|
|
if ( !isUTF8 ) {
|
|
|
|
facesSize /= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
XP_U8 tmp[numFaceBytes];
|
|
|
|
XP_MEMCPY( tmp, ptr, numFaceBytes );
|
|
|
|
ptr += numFaceBytes;
|
|
|
|
|
|
|
|
dict_splitFaces( dctx, xwe, tmp, numFaceBytes, numFaces );
|
|
|
|
|
|
|
|
unsigned short xloc;
|
|
|
|
XP_MEMCPY( &xloc, ptr, sizeof(xloc) );
|
|
|
|
ptr += sizeof(xloc);
|
2022-03-15 23:03:03 +01:00
|
|
|
|
|
|
|
for ( int ii = 0; ii < numFaces; ++ii ) {
|
|
|
|
counts15[ii] = *ptr++;
|
|
|
|
dctx->values[ii] = *ptr++;
|
|
|
|
}
|
2020-05-01 18:18:27 +02:00
|
|
|
|
2022-05-28 18:00:20 +02:00
|
|
|
if ( NULL == dctx->isoCode ) {
|
2022-06-04 23:03:03 +02:00
|
|
|
XP_LangCode langCode = xloc & 0x7F;
|
|
|
|
const XP_UCHAR* isoCode = lcToLocale( langCode );
|
2022-05-28 18:00:20 +02:00
|
|
|
XP_ASSERT( !!isoCode );
|
|
|
|
dctx->isoCode = copyString( dctx->mpool, isoCode );
|
2022-05-29 18:15:03 +02:00
|
|
|
XP_LOGFF( "looked up isoCode %s for langCode %d", isoCode,
|
2022-06-04 23:03:03 +02:00
|
|
|
langCode );
|
2022-05-28 18:00:20 +02:00
|
|
|
}
|
2022-05-29 18:15:03 +02:00
|
|
|
#ifdef DEBUG
|
2022-06-04 23:03:03 +02:00
|
|
|
const XP_UCHAR* locale = lcToLocale( xloc & 0x7F );
|
2022-05-29 18:15:03 +02:00
|
|
|
XP_ASSERT( !locale || !XP_STRCMP( locale, dctx->isoCode ) );
|
|
|
|
#endif
|
2020-05-01 18:18:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( formatOk ) {
|
|
|
|
formatOk = loadSpecialData( dctx, &ptr, end );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( formatOk ) {
|
|
|
|
XP_ASSERT( ptr < end );
|
|
|
|
*ptrp = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_RETURNF( "%s", boolToStr(formatOk) );
|
|
|
|
return formatOk;
|
2022-04-21 06:00:51 +02:00
|
|
|
} /* parseCommon */
|
2020-05-01 18:18:27 +02:00
|
|
|
|
|
|
|
static XP_Bool
|
|
|
|
makeBitmap( XP_U8 const** ptrp, const XP_U8* end )
|
|
|
|
{
|
|
|
|
XP_Bool success = XP_TRUE;
|
|
|
|
XP_U8 const* ptr = *ptrp;
|
|
|
|
CHECK_PTR( ptr, 1, end, error );
|
|
|
|
XP_U8 nCols = *ptr++;
|
|
|
|
if ( nCols > 0 ) {
|
|
|
|
CHECK_PTR( ptr, 1, end, error );
|
|
|
|
XP_U8 nRows = *ptr++;
|
|
|
|
CHECK_PTR( ptr, ((nRows*nCols)+7) / 8, end, error );
|
|
|
|
#ifdef DROP_BITMAPS
|
|
|
|
ptr += ((nRows*nCols)+7) / 8;
|
|
|
|
#else
|
|
|
|
do not compile
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
error:
|
|
|
|
success = XP_FALSE;
|
|
|
|
done:
|
|
|
|
*ptrp = ptr;
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
XP_U16
|
|
|
|
countSpecials( DictionaryCtxt* ctxt )
|
|
|
|
{
|
|
|
|
XP_U16 result = 0;
|
|
|
|
|
|
|
|
for ( int ii = 0; ii < ctxt->nFaces; ++ii ) {
|
|
|
|
if ( IS_SPECIAL( ctxt->facePtrs[ii][0] ) ) {
|
|
|
|
++result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} /* countSpecials */
|
2014-03-09 05:57:27 +01:00
|
|
|
|
2003-11-01 06:35:29 +01:00
|
|
|
void
|
2009-09-13 07:28:12 +02:00
|
|
|
setBlankTile( DictionaryCtxt* dict )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2009-09-13 07:28:12 +02:00
|
|
|
dict->blankTile = -1; /* no known blank */
|
2020-12-27 20:53:36 +01:00
|
|
|
XP_U16 maxLen = 0;
|
|
|
|
for ( int tile = 0; tile < dict->nFaces; ++tile ) {
|
|
|
|
const XP_UCHAR* facePtr = dict->facePtrs[tile];
|
|
|
|
if ( facePtr[0] == 0 ) {
|
2009-09-13 07:28:12 +02:00
|
|
|
XP_ASSERT( dict->blankTile == -1 ); /* only one passes test? */
|
2020-12-27 20:53:36 +01:00
|
|
|
dict->blankTile = (XP_S8)tile;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( IS_SPECIAL( *facePtr ) ) {
|
|
|
|
facePtr = dict_getTileString( dict, tile );
|
2003-11-01 06:35:29 +01:00
|
|
|
}
|
2020-12-27 20:53:36 +01:00
|
|
|
XP_U16 thisLen = XP_STRLEN( facePtr );
|
|
|
|
if ( thisLen > maxLen ) {
|
|
|
|
maxLen = thisLen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dict->maxChars = maxLen;
|
2003-11-01 06:35:29 +01:00
|
|
|
} /* setBlankTile */
|
|
|
|
|
2008-09-09 14:20:09 +02:00
|
|
|
/* #if defined BLANKS_FIRST || defined DEBUG */
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_Bool
|
2006-02-18 07:39:40 +01:00
|
|
|
dict_hasBlankTile( const DictionaryCtxt* dict )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
|
|
|
return dict->blankTile >= 0;
|
|
|
|
} /* dict_hasBlankTile */
|
2008-09-09 14:20:09 +02:00
|
|
|
/* #endif */
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
Tile
|
2006-02-18 07:39:40 +01:00
|
|
|
dict_getBlankTile( const DictionaryCtxt* dict )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
|
|
|
XP_ASSERT( dict_hasBlankTile(dict) );
|
|
|
|
return (Tile)dict->blankTile;
|
|
|
|
} /* dict_getBlankTile */
|
|
|
|
|
|
|
|
XP_U16
|
2022-03-14 18:28:08 +01:00
|
|
|
dict_getTileValue( const DictionaryCtxt* dict, const Tile tile )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2013-11-29 19:15:22 +01:00
|
|
|
XP_ASSERT( !!dict );
|
2004-07-20 17:08:45 +02:00
|
|
|
if ( (tile & TILE_VALUE_MASK) != tile ) {
|
|
|
|
XP_ASSERT( tile == 32 &&
|
|
|
|
tile == dict_getBlankTile( dict ) );
|
|
|
|
}
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_ASSERT( tile < dict->nFaces );
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_ASSERT( !!dict->values );
|
|
|
|
return dict->values[tile];
|
2003-11-01 06:35:29 +01:00
|
|
|
} /* dict_getTileValue */
|
|
|
|
|
2009-09-13 06:57:44 +02:00
|
|
|
static const XP_UCHAR*
|
|
|
|
dict_getTileStringRaw( const DictionaryCtxt* dict, Tile tile )
|
|
|
|
{
|
|
|
|
XP_ASSERT( tile < dict->nFaces );
|
2009-09-13 07:28:12 +02:00
|
|
|
return dict->facePtrs[tile];
|
2009-09-13 06:57:44 +02:00
|
|
|
}
|
|
|
|
|
2009-04-05 21:02:21 +02:00
|
|
|
const XP_UCHAR*
|
|
|
|
dict_getTileString( const DictionaryCtxt* dict, Tile tile )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2009-09-13 06:57:44 +02:00
|
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
|
|
if ( IS_SPECIAL(*facep) ) {
|
|
|
|
facep = dict->chars[(XP_U16)*facep];
|
|
|
|
}
|
|
|
|
return facep;
|
2009-04-05 21:02:21 +02:00
|
|
|
}
|
2003-11-01 06:35:29 +01:00
|
|
|
|
2013-04-06 20:43:57 +02:00
|
|
|
const XP_UCHAR*
|
2013-04-09 16:43:04 +02:00
|
|
|
dict_getNextTileString( const DictionaryCtxt* dict, Tile tile,
|
2013-04-06 20:43:57 +02:00
|
|
|
const XP_UCHAR* cur )
|
|
|
|
{
|
|
|
|
const XP_UCHAR* result = NULL;
|
|
|
|
if ( NULL == cur ) {
|
2013-04-09 16:43:04 +02:00
|
|
|
result = dict_getTileString( dict, tile );
|
2013-04-06 20:43:57 +02:00
|
|
|
} else {
|
|
|
|
cur += XP_STRLEN( cur ) + 1;
|
2013-04-09 16:43:04 +02:00
|
|
|
XP_Bool isSpecial = dict_faceIsBitmap( dict, tile );
|
2013-05-15 07:00:22 +02:00
|
|
|
if ( isSpecial || tile == dict->blankTile ) {
|
2013-04-09 16:43:04 +02:00
|
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
|
|
|
if ( cur < dict->charEnds[(XP_U16)*facep] ) {
|
2013-04-06 20:43:57 +02:00
|
|
|
result = cur;
|
|
|
|
}
|
|
|
|
} else {
|
2013-04-09 16:43:04 +02:00
|
|
|
/* use cur only if it is is not now off the end or pointing to to the
|
|
|
|
next tile */
|
|
|
|
if ( ++tile == dict->nFaces ) {
|
|
|
|
if ( cur < dict->facesEnd ) {
|
|
|
|
result = cur;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const XP_UCHAR* nxt = dict_getTileStringRaw( dict, tile );
|
|
|
|
if ( nxt != cur ) {
|
|
|
|
result = cur;
|
|
|
|
}
|
2013-04-06 20:43:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_U16
|
2022-03-11 07:15:49 +01:00
|
|
|
dict_numTilesForSize( const DictionaryCtxt* dict, Tile tile, XP_U16 nCols )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_U8* counts = getCountsFor( dict, nCols );
|
|
|
|
return counts[tile];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Older wordlists are built assuming 15x15 boards. Different sized boards
|
|
|
|
need different numbers of tiles. The wordlist might provide for the size we
|
|
|
|
have, in which case we use it. Otherwise we extrapolate.
|
|
|
|
*/
|
|
|
|
static XP_U8*
|
|
|
|
getCountsFor( const DictionaryCtxt* dict, XP_U16 nCols )
|
|
|
|
{
|
|
|
|
int offset = nCols >> 1;
|
|
|
|
XP_U8* counts = dict->counts[offset];
|
|
|
|
if ( !counts ) {
|
|
|
|
counts = XP_MALLOC( dict->mpool, dict->nFaces );
|
|
|
|
((DictionaryCtxt*)dict)->counts[offset] = counts;
|
|
|
|
|
|
|
|
XP_U8* ptr = dict->otherCounts;
|
|
|
|
XP_Bool found = XP_FALSE;
|
|
|
|
while ( !found && !!ptr && ptr < dict->otherCountsEnd ) {
|
|
|
|
XP_U8 siz = *ptr++;
|
|
|
|
found = siz == nCols;
|
|
|
|
if ( found ) {
|
|
|
|
XP_MEMCPY( counts, ptr, dict->nFaces );
|
|
|
|
} else {
|
|
|
|
ptr += dict->nFaces;
|
|
|
|
}
|
2022-03-14 18:28:08 +01:00
|
|
|
}
|
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
/* don't have it in the wordlist? Extrapolate */
|
|
|
|
if ( !found ) {
|
2022-04-04 17:28:10 +02:00
|
|
|
const Tile blank = dict_getBlankTile( dict );
|
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_U16 pct = (nCols * nCols * 100) / (15 * 15);
|
|
|
|
XP_U8* src15 = dict->counts[15>>1];
|
|
|
|
XP_ASSERT( !!src15 );
|
|
|
|
|
2022-04-04 17:28:10 +02:00
|
|
|
for ( Tile tile = 0; tile < dict->nFaces; ++tile ) {
|
|
|
|
XP_U16 count = src15[tile];
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_U16 newCount = count * pct / 100;
|
|
|
|
if ( 50 < (count * pct) % 100 ) {
|
|
|
|
++newCount;
|
|
|
|
}
|
2022-04-04 17:28:10 +02:00
|
|
|
if ( tile == blank && newCount > MAX_NUM_BLANKS ) {
|
|
|
|
newCount = MAX_NUM_BLANKS;
|
|
|
|
}
|
2022-06-05 23:38:33 +02:00
|
|
|
XP_ASSERT( tile != blank || newCount <= MAX_NUM_BLANKS );
|
2022-04-04 17:28:10 +02:00
|
|
|
counts[tile] = newCount;
|
2022-03-15 23:03:03 +01:00
|
|
|
}
|
2022-03-14 18:28:08 +01:00
|
|
|
}
|
2022-03-11 07:15:49 +01:00
|
|
|
}
|
|
|
|
|
2022-03-14 18:28:08 +01:00
|
|
|
// XP_LOGFF( "(tile=%d, ncols=%d) => %d", tile, nCols, count );
|
2022-03-15 23:03:03 +01:00
|
|
|
return counts;
|
|
|
|
} /* getCountsFor */
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
XP_U16
|
2006-02-18 07:39:40 +01:00
|
|
|
dict_numTileFaces( const DictionaryCtxt* dict )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
|
|
|
return dict->nFaces;
|
|
|
|
} /* dict_numTileFaces */
|
|
|
|
|
2020-12-27 20:53:36 +01:00
|
|
|
XP_U16
|
|
|
|
dict_getMaxTileChars( const DictionaryCtxt* ctxt )
|
|
|
|
{
|
|
|
|
XP_ASSERT( 0 != ctxt->maxChars );
|
|
|
|
return ctxt->maxChars;
|
|
|
|
}
|
|
|
|
|
2020-05-04 17:33:15 +02:00
|
|
|
static void
|
|
|
|
appendIfSpace( XP_UCHAR** bufp, const XP_UCHAR* end, const XP_UCHAR* newtxt )
|
|
|
|
{
|
|
|
|
XP_U16 len = XP_STRLEN( newtxt );
|
|
|
|
if ( *bufp + len < end ) {
|
|
|
|
XP_MEMCPY( *bufp, newtxt, len );
|
|
|
|
*bufp += len;
|
|
|
|
} else {
|
|
|
|
*bufp = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_U16
|
2009-09-13 06:57:44 +02:00
|
|
|
dict_tilesToString( const DictionaryCtxt* dict, const Tile* tiles,
|
2020-05-04 17:33:15 +02:00
|
|
|
XP_U16 nTiles, XP_UCHAR* buf, XP_U16 bufSize,
|
|
|
|
const XP_UCHAR* delim )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2005-07-08 05:06:08 +02:00
|
|
|
XP_UCHAR* bufp = buf;
|
2020-05-04 17:33:15 +02:00
|
|
|
const XP_UCHAR* end = bufp + bufSize;
|
|
|
|
XP_U16 delimLen = NULL == delim ? 0 : XP_STRLEN(delim);
|
2009-09-04 14:30:10 +02:00
|
|
|
|
2020-05-04 17:33:15 +02:00
|
|
|
for ( int ii = 0; ii < nTiles && !!bufp; ++ii ) {
|
2020-04-29 19:15:58 +02:00
|
|
|
|
2020-05-04 17:33:15 +02:00
|
|
|
if ( 0 < delimLen && 0 < ii ) {
|
|
|
|
appendIfSpace( &bufp, end, delim );
|
|
|
|
}
|
|
|
|
|
|
|
|
Tile tile = tiles[ii];
|
|
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
2009-09-04 14:30:10 +02:00
|
|
|
if ( IS_SPECIAL(*facep) ) {
|
2009-09-13 06:57:44 +02:00
|
|
|
XP_UCHAR* chars = dict->chars[(XP_U16)*facep];
|
2020-05-04 17:33:15 +02:00
|
|
|
appendIfSpace( &bufp, end, chars );
|
2009-09-04 14:30:10 +02:00
|
|
|
} else {
|
2009-09-13 06:57:44 +02:00
|
|
|
XP_ASSERT ( tile != dict->blankTile ); /* printing blank should be
|
2009-09-04 14:30:10 +02:00
|
|
|
handled by specials
|
|
|
|
mechanism */
|
2020-05-04 17:33:15 +02:00
|
|
|
appendIfSpace( &bufp, end, facep );
|
2003-11-01 06:35:29 +01:00
|
|
|
}
|
2009-09-04 14:30:10 +02:00
|
|
|
}
|
2005-07-08 05:06:08 +02:00
|
|
|
|
2020-05-04 17:33:15 +02:00
|
|
|
XP_U16 result = 0;
|
|
|
|
if ( !!bufp && bufp < end ) {
|
2009-09-04 14:30:10 +02:00
|
|
|
*bufp = '\0';
|
|
|
|
result = bufp - buf;
|
2005-07-08 05:06:08 +02:00
|
|
|
}
|
|
|
|
return result;
|
2003-11-01 06:35:29 +01:00
|
|
|
} /* dict_tilesToString */
|
|
|
|
|
2011-11-01 04:56:48 +01:00
|
|
|
/* Convert str to an array of tiles, continuing until we fail to match or we
|
|
|
|
* run out of room in which to return tiles. Failure to match means return of
|
|
|
|
* XP_FALSE, but if we run out of room before failing we return XP_TRUE.
|
2009-04-05 21:02:21 +02:00
|
|
|
*/
|
2020-05-04 17:33:15 +02:00
|
|
|
|
|
|
|
static XP_Bool
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
tilesForStringImpl( const DictionaryCtxt* dict,
|
|
|
|
const XP_UCHAR* str, XP_U16 strLen,
|
2020-05-04 17:33:15 +02:00
|
|
|
Tile* tiles, XP_U16 nTiles, XP_U16 nFound,
|
|
|
|
OnFoundTiles proc, void* closure )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2020-05-04 17:33:15 +02:00
|
|
|
XP_Bool goOn;
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
if ( nFound == nTiles || 0 == strLen ) {
|
2020-05-04 17:33:15 +02:00
|
|
|
/* We've recursed to the end and have found a tile! */
|
|
|
|
goOn = (*proc)( closure, tiles, nFound );
|
2012-08-27 16:10:40 +02:00
|
|
|
} else {
|
2020-05-04 17:33:15 +02:00
|
|
|
goOn = XP_TRUE;
|
|
|
|
|
2012-08-27 16:10:40 +02:00
|
|
|
XP_U16 nFaces = dict_numTileFaces( dict );
|
2020-05-04 17:33:15 +02:00
|
|
|
for ( Tile tile = 0; goOn && tile < nFaces; ++tile ) {
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
for ( const XP_UCHAR* facep = NULL; ; ) {
|
|
|
|
facep = dict_getNextTileString( dict, tile, facep );
|
|
|
|
if ( !facep ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
XP_U16 faceLen = XP_STRLEN( facep );
|
|
|
|
if ( 0 == XP_STRNCMP( facep, str, faceLen ) ) {
|
|
|
|
tiles[nFound] = tile;
|
|
|
|
goOn = tilesForStringImpl( dict, str + faceLen,
|
|
|
|
strLen - faceLen,
|
|
|
|
tiles, nTiles, nFound + 1,
|
|
|
|
proc, closure );
|
|
|
|
break; /* impossible to have than one match per tile */
|
2011-11-01 04:56:48 +01:00
|
|
|
}
|
2003-11-01 06:35:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-04 17:33:15 +02:00
|
|
|
return goOn;
|
2012-08-27 16:10:40 +02:00
|
|
|
} /* tilesForStringImpl */
|
|
|
|
|
2020-05-04 17:33:15 +02:00
|
|
|
void
|
2012-08-27 16:10:40 +02:00
|
|
|
dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* str,
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
XP_U16 strLen, OnFoundTiles proc, void* closure )
|
2012-08-27 16:10:40 +02:00
|
|
|
{
|
2020-05-04 17:33:15 +02:00
|
|
|
Tile tiles[32];
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
if ( 0 == strLen ) {
|
|
|
|
strLen = XP_STRLEN( str );
|
|
|
|
}
|
|
|
|
tilesForStringImpl( dict, str, strLen, tiles, VSIZE(tiles), 0, proc, closure );
|
2011-11-01 04:56:48 +01:00
|
|
|
} /* dict_tilesForString */
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
XP_Bool
|
2006-09-02 07:30:51 +02:00
|
|
|
dict_tilesAreSame( const DictionaryCtxt* dict1, const DictionaryCtxt* dict2 )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
|
|
|
XP_Bool result = XP_FALSE;
|
|
|
|
|
2011-04-26 05:17:54 +02:00
|
|
|
XP_ASSERT( !!dict1 );
|
|
|
|
XP_ASSERT( !!dict2 );
|
|
|
|
|
2009-04-05 21:02:21 +02:00
|
|
|
Tile ii;
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_U16 nTileFaces = dict_numTileFaces( dict1 );
|
|
|
|
|
|
|
|
if ( nTileFaces == dict_numTileFaces( dict2 ) ) {
|
2009-04-05 21:02:21 +02:00
|
|
|
for ( ii = 0; ii < nTileFaces; ++ii ) {
|
2003-11-01 06:35:29 +01:00
|
|
|
|
2009-04-05 21:02:21 +02:00
|
|
|
const XP_UCHAR* face1;
|
|
|
|
const XP_UCHAR* face2;
|
2003-11-01 06:35:29 +01:00
|
|
|
|
2009-04-05 21:02:21 +02:00
|
|
|
if ( dict_getTileValue( dict1, ii )
|
|
|
|
!= dict_getTileValue( dict2, ii ) ){
|
2003-11-01 06:35:29 +01:00
|
|
|
break;
|
|
|
|
}
|
2009-09-13 06:57:44 +02:00
|
|
|
face1 = dict_getTileStringRaw( dict1, ii );
|
|
|
|
face2 = dict_getTileStringRaw( dict2, ii );
|
2009-04-05 21:02:21 +02:00
|
|
|
if ( IS_SPECIAL(*face1) != IS_SPECIAL(*face2) ) {
|
2003-11-01 06:35:29 +01:00
|
|
|
break;
|
|
|
|
}
|
2009-04-05 21:02:21 +02:00
|
|
|
if ( IS_SPECIAL(*face1) ) {
|
|
|
|
XP_UCHAR* chars1 = dict1->chars[(int)*face1];
|
|
|
|
XP_UCHAR* chars2 = dict2->chars[(int)*face2];
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_U16 len = XP_STRLEN(chars1);
|
|
|
|
if ( 0 != XP_STRNCMP( chars1, chars2, len ) ) {
|
|
|
|
break;
|
|
|
|
}
|
2009-04-05 21:02:21 +02:00
|
|
|
} else if ( 0 != XP_STRCMP( face1, face2 ) ) {
|
2003-11-01 06:35:29 +01:00
|
|
|
break;
|
|
|
|
}
|
2022-03-11 07:15:49 +01:00
|
|
|
if ( dict_numTilesForSize( dict1, ii, 15 ) != dict_numTilesForSize( dict2, ii, 15 ) ) {
|
2003-11-01 06:35:29 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-04-05 21:02:21 +02:00
|
|
|
result = ii == nTileFaces; /* did we get that far */
|
2003-11-01 06:35:29 +01:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
} /* dict_tilesAreSame */
|
|
|
|
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
/* Summarize tile info in a way it can be presented to users */
|
|
|
|
void
|
2022-03-11 07:15:49 +01:00
|
|
|
dict_writeTilesInfo( const DictionaryCtxt* dict, XP_U16 boardSize, XWStreamCtxt* stream )
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
{
|
|
|
|
XP_U16 nFaces = dict_numTileFaces( dict );
|
|
|
|
for ( Tile tile = 0; tile < nFaces; ++tile ) {
|
|
|
|
XP_U16 val = dict_getTileValue( dict, tile );
|
2022-03-11 07:15:49 +01:00
|
|
|
XP_U16 count = dict_numTilesForSize( dict, tile, boardSize );
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
const XP_UCHAR* face = dict_getTileString( dict, tile );
|
|
|
|
XP_UCHAR buf[32];
|
|
|
|
XP_SNPRINTF( buf, VSIZE(buf), "%s\t%d\t%d\n", face, count, val );
|
|
|
|
stream_catString( stream, buf );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-01 06:35:29 +01:00
|
|
|
void
|
2022-03-15 23:03:03 +01:00
|
|
|
dict_writeToStream( const DictionaryCtxt* XP_UNUSED(dict),
|
|
|
|
XWStreamCtxt* XP_UNUSED(stream) )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_ASSERT(0);
|
|
|
|
/* 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 ); */
|
|
|
|
|
|
|
|
/* XP_ASSERT(0); /\* if this fires, need to fix for per-boardSize counts *\/ */
|
|
|
|
/* 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; ++ii ) { */
|
|
|
|
/* stream_putBits( stream, maxCountBits, counts[ii] ); */
|
|
|
|
/* stream_putBits( stream, maxValueBits, dict->values[ii] ); */
|
|
|
|
/* } */
|
|
|
|
|
|
|
|
/* /\* 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++] ); */
|
|
|
|
/* } */
|
|
|
|
/* } */
|
2003-11-01 06:35:29 +01:00
|
|
|
} /* dict_writeToStream */
|
|
|
|
|
|
|
|
static void
|
|
|
|
freeSpecials( DictionaryCtxt* dict )
|
|
|
|
{
|
2009-04-05 21:02:21 +02:00
|
|
|
Tile tt;
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_U16 nSpecials;
|
|
|
|
|
2009-04-05 21:02:21 +02:00
|
|
|
for ( nSpecials = tt = 0; tt < dict->nFaces; ++tt ) {
|
2009-09-13 06:57:44 +02:00
|
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tt );
|
2009-04-05 21:02:21 +02:00
|
|
|
if ( IS_SPECIAL( *facep ) ) {
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
XP_ASSERT( !!dict->chars[nSpecials] );
|
|
|
|
XP_FREE( dict->mpool, dict->chars[nSpecials] );
|
|
|
|
|
2011-04-08 03:07:45 +02:00
|
|
|
XP_FREEP( dict->mpool, &dict->bitmaps[nSpecials].largeBM );
|
|
|
|
XP_FREEP( dict->mpool, &dict->bitmaps[nSpecials].smallBM );
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
++nSpecials;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( nSpecials > 0 ) {
|
|
|
|
XP_FREE( dict->mpool, dict->chars );
|
|
|
|
XP_FREE( dict->mpool, dict->bitmaps );
|
|
|
|
}
|
|
|
|
} /* freeSpecials */
|
|
|
|
|
|
|
|
static void
|
2020-04-25 20:34:58 +02:00
|
|
|
common_destructor( DictionaryCtxt* dict, XWEnv XP_UNUSED(xwe) )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
|
|
|
freeSpecials( dict );
|
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
XP_FREE( dict->mpool, dict->values );
|
|
|
|
for ( int ii = 0; ii < VSIZE(dict->counts); ++ii ) {
|
|
|
|
XP_FREEP( dict->mpool, &dict->counts[ii] );
|
|
|
|
}
|
|
|
|
XP_FREE( dict->mpool, dict->otherCounts );
|
2009-04-05 21:02:21 +02:00
|
|
|
XP_FREE( dict->mpool, dict->faces );
|
2009-09-13 07:28:12 +02:00
|
|
|
XP_FREE( dict->mpool, dict->facePtrs );
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
XP_FREE( dict->mpool, dict );
|
2009-04-05 21:02:21 +02:00
|
|
|
} /* common_destructor */
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
void
|
2020-04-25 20:34:58 +02:00
|
|
|
dict_loadFromStream( DictionaryCtxt* dict, XWEnv xwe, XWStreamCtxt* stream )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2022-03-14 18:28:08 +01:00
|
|
|
XP_ASSERT(0); /* if this fires, need to fix for per-boardSize counts */
|
2009-09-04 14:30:10 +02:00
|
|
|
XP_U8 nFaces, nFaceBytes;
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_U16 maxCountBits, maxValueBits;
|
2009-04-05 21:02:21 +02:00
|
|
|
XP_U16 ii, nSpecials;
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_UCHAR* localTexts[32];
|
2009-09-12 17:35:03 +02:00
|
|
|
XP_U8 utf8[MAX_UNIQUE_TILES];
|
2003-11-01 06:35:29 +01:00
|
|
|
|
2020-01-29 19:59:57 +01:00
|
|
|
if ( !!dict->destructor ) {
|
|
|
|
XP_LOGF( "%s(): replacing destructor!!", __func__ );
|
|
|
|
}
|
2003-11-01 06:35:29 +01:00
|
|
|
dict->destructor = common_destructor;
|
2004-12-18 02:04:57 +01:00
|
|
|
dict->func_dict_getShortName = dict_getName; /* default */
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
nFaces = (XP_U8)stream_getBits( stream, 6 );
|
|
|
|
maxCountBits = (XP_U16)stream_getBits( stream, 3 );
|
|
|
|
maxValueBits = (XP_U16)stream_getBits( stream, 3 );
|
|
|
|
|
|
|
|
dict->nFaces = nFaces;
|
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
dict->values = (XP_U8*)XP_MALLOC( dict->mpool,
|
|
|
|
sizeof(dict->values[0]) * nFaces );
|
|
|
|
XP_U8* counts = dict->counts[15>>1]
|
|
|
|
= (XP_U8*)XP_MALLOC( dict->mpool, sizeof(dict->values[0]) * nFaces );
|
2003-11-01 06:35:29 +01:00
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
for ( ii = 0; ii < dict->nFaces; ++ii ) {
|
|
|
|
counts[ii] = (XP_U8)stream_getBits( stream, maxCountBits );
|
|
|
|
dict->values[ii] = (XP_U8)stream_getBits( stream, maxValueBits );
|
2003-11-01 06:35:29 +01:00
|
|
|
}
|
2009-04-07 06:33:47 +02:00
|
|
|
|
2009-09-04 14:30:10 +02:00
|
|
|
nFaceBytes = (XP_U8)stream_getU8( stream );
|
2009-09-12 17:35:03 +02:00
|
|
|
XP_ASSERT( nFaceBytes < VSIZE(utf8) );
|
2009-04-05 21:02:21 +02:00
|
|
|
stream_getBytes( stream, utf8, nFaceBytes );
|
2009-09-04 14:30:10 +02:00
|
|
|
dict->isUTF8 = XP_TRUE; /* need to communicate this in stream */
|
2020-04-25 20:34:58 +02:00
|
|
|
dict_splitFaces( dict, xwe, utf8, nFaceBytes, nFaces );
|
2009-04-05 21:02:21 +02:00
|
|
|
|
|
|
|
for ( nSpecials = ii = 0; ii < nFaces; ++ii ) {
|
2009-09-13 06:57:44 +02:00
|
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, (Tile)ii );
|
2009-04-05 21:02:21 +02:00
|
|
|
if ( IS_SPECIAL( *facep ) ) {
|
2007-03-19 00:31:51 +01:00
|
|
|
XP_UCHAR* txt = stringFromStream( dict->mpool, stream );
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_ASSERT( !!txt );
|
|
|
|
localTexts[nSpecials] = txt;
|
|
|
|
|
|
|
|
++nSpecials;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( nSpecials > 0 ) {
|
2022-04-22 16:59:50 +02:00
|
|
|
dict->bitmaps =
|
2003-11-01 06:35:29 +01:00
|
|
|
(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 */
|
2021-03-12 19:37:53 +01:00
|
|
|
|
2011-04-13 15:45:22 +02:00
|
|
|
#ifdef TEXT_MODEL
|
|
|
|
/* Return the strlen of the longest face, e.g. 1 for English and Italian;
|
|
|
|
2 for Spanish; 3 for Catalan */
|
|
|
|
XP_U16
|
|
|
|
dict_getMaxWidth( const DictionaryCtxt* dict )
|
|
|
|
{
|
|
|
|
XP_U16 result = 0;
|
|
|
|
Tile tile;
|
|
|
|
XP_U16 nFaces = dict_numTileFaces( dict );
|
|
|
|
|
|
|
|
for ( tile = 0; tile < nFaces; ++tile ) {
|
|
|
|
const XP_UCHAR* face = dict_getTileString( dict, tile );
|
|
|
|
XP_U16 len = XP_STRLEN( face );
|
|
|
|
if ( len > result ) {
|
|
|
|
result = len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2006-09-24 17:28:15 +02:00
|
|
|
const XP_UCHAR*
|
2006-09-02 07:30:51 +02:00
|
|
|
dict_getName( const DictionaryCtxt* dict )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
|
|
|
XP_ASSERT( !!dict );
|
2006-04-30 15:58:24 +02:00
|
|
|
XP_ASSERT( !!dict->name );
|
2003-11-01 06:35:29 +01:00
|
|
|
return dict->name;
|
|
|
|
} /* dict_getName */
|
|
|
|
|
2009-04-05 21:02:21 +02:00
|
|
|
XP_Bool
|
|
|
|
dict_isUTF8( const DictionaryCtxt* dict )
|
|
|
|
{
|
|
|
|
XP_ASSERT( !!dict );
|
|
|
|
return dict->isUTF8;
|
|
|
|
}
|
|
|
|
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_Bool
|
2006-09-02 07:30:51 +02:00
|
|
|
dict_faceIsBitmap( const DictionaryCtxt* dict, Tile tile )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
2009-09-13 06:57:44 +02:00
|
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
2010-03-08 07:11:42 +01:00
|
|
|
return IS_SPECIAL(*facep) && (tile != dict->blankTile);
|
2003-11-01 06:35:29 +01:00
|
|
|
} /* dict_faceIsBitmap */
|
|
|
|
|
2009-01-13 13:57:56 +01:00
|
|
|
void
|
|
|
|
dict_getFaceBitmaps( const DictionaryCtxt* dict, Tile tile, XP_Bitmaps* bmps )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
|
|
|
SpecialBitmaps* bitmaps;
|
2009-09-13 06:57:44 +02:00
|
|
|
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
XP_ASSERT( dict_faceIsBitmap( dict, tile ) );
|
|
|
|
XP_ASSERT( !!dict->bitmaps );
|
|
|
|
|
2009-04-05 21:02:21 +02:00
|
|
|
bitmaps = &dict->bitmaps[(XP_U16)*facep];
|
2009-01-13 13:57:56 +01:00
|
|
|
bmps->nBitmaps = 2;
|
|
|
|
bmps->bmps[0] = bitmaps->smallBM;
|
|
|
|
bmps->bmps[1] = bitmaps->largeBM;
|
2009-01-25 21:31:13 +01:00
|
|
|
} /* dict_getFaceBitmaps */
|
2003-11-01 06:35:29 +01:00
|
|
|
|
2010-12-07 03:24:31 +01:00
|
|
|
XP_U32
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
dict_getWordCount( const DictionaryCtxt* dict, XWEnv xwe )
|
2010-12-07 03:24:31 +01:00
|
|
|
{
|
2011-10-25 03:27:16 +02:00
|
|
|
XP_U32 nWords = dict->nWords;
|
|
|
|
#ifdef XWFEATURE_WALKDICT
|
2011-10-29 05:27:16 +02:00
|
|
|
if ( 0 == nWords ) {
|
add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)
Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 18:25:33 +02:00
|
|
|
DictIter* iter = di_makeIter( dict, xwe, NULL, NULL, 0, NULL, 0 );
|
|
|
|
nWords = di_countWords( iter, NULL );
|
|
|
|
di_freeIter( iter, xwe );
|
2011-10-29 05:27:16 +02:00
|
|
|
}
|
2011-10-25 03:27:16 +02:00
|
|
|
#endif
|
|
|
|
return nWords;
|
2010-12-07 03:24:31 +01:00
|
|
|
}
|
|
|
|
|
2012-08-27 05:58:28 +02:00
|
|
|
const XP_UCHAR*
|
|
|
|
dict_getDesc( const DictionaryCtxt* dict )
|
|
|
|
{
|
|
|
|
return dict->desc;
|
|
|
|
}
|
|
|
|
|
2022-06-04 23:02:28 +02:00
|
|
|
const XP_UCHAR*
|
|
|
|
dict_getISOCode( const DictionaryCtxt* dict )
|
|
|
|
{
|
|
|
|
return dict->isoCode;
|
|
|
|
}
|
|
|
|
|
2012-09-08 19:09:31 +02:00
|
|
|
const XP_UCHAR*
|
|
|
|
dict_getMd5Sum( const DictionaryCtxt* dict )
|
|
|
|
{
|
|
|
|
return dict->md5Sum;
|
|
|
|
}
|
|
|
|
|
2020-05-04 17:33:15 +02:00
|
|
|
XP_Bool
|
|
|
|
dict_hasDuplicates( const DictionaryCtxt* dict )
|
|
|
|
{
|
|
|
|
return 0 != (dict->headerFlags & HEADERFLAGS_DUPS_SUPPORTED_BIT);
|
|
|
|
}
|
|
|
|
|
2003-11-01 06:35:29 +01:00
|
|
|
#ifdef STUBBED_DICT
|
|
|
|
|
|
|
|
#define BLANK_FACE '\0'
|
|
|
|
|
|
|
|
static XP_U8 stub_english_data[] = {
|
|
|
|
/* count value face */
|
2008-05-31 05:26:16 +02:00
|
|
|
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 */
|
2003-11-01 06:35:29 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
setStubbedSpecials( DictionaryCtxt* dict )
|
|
|
|
{
|
2005-01-04 05:06:37 +01:00
|
|
|
dict->chars = (XP_UCHAR**)XP_MALLOC( dict->mpool, sizeof(char*) );
|
2003-11-01 06:35:29 +01:00
|
|
|
dict->chars[0] = "_";
|
|
|
|
|
|
|
|
} /* setStubbedSpecials */
|
|
|
|
|
|
|
|
void
|
|
|
|
destroy_stubbed_dict( DictionaryCtxt* dict )
|
|
|
|
{
|
|
|
|
XP_FREE( dict->mpool, dict->countsAndValues );
|
2010-01-02 02:40:25 +01:00
|
|
|
XP_FREE( dict->mpool, dict->faces );
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_FREE( dict->mpool, dict->chars );
|
|
|
|
XP_FREE( dict->mpool, dict->name );
|
2011-04-12 03:55:42 +02:00
|
|
|
XP_FREE( dict->mpool, dict->langName );
|
2022-05-28 18:00:20 +02:00
|
|
|
XP_FREE( dict->mpool, dict->isoCode );
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_FREE( dict->mpool, dict->bitmaps );
|
|
|
|
XP_FREE( dict->mpool, dict );
|
|
|
|
} /* destroy_stubbed_dict */
|
|
|
|
|
|
|
|
DictionaryCtxt*
|
|
|
|
make_stubbed_dict( MPFORMAL_NOCOMMA )
|
|
|
|
{
|
2005-01-04 05:06:37 +01:00
|
|
|
DictionaryCtxt* dict = (DictionaryCtxt*)XP_MALLOC( mpool, sizeof(*dict) );
|
2003-11-01 06:35:29 +01:00
|
|
|
XP_U8* data = stub_english_data;
|
|
|
|
XP_U16 datasize = sizeof(stub_english_data);
|
2010-01-02 02:40:25 +01:00
|
|
|
XP_U16 ii;
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
XP_MEMSET( dict, 0, sizeof(*dict) );
|
|
|
|
|
|
|
|
MPASSIGN( dict->mpool, mpool );
|
2006-09-15 09:32:39 +02:00
|
|
|
dict->name = copyString( mpool, "Stub dictionary" );
|
2003-11-01 06:35:29 +01:00
|
|
|
dict->nFaces = datasize/3;
|
|
|
|
|
|
|
|
dict->destructor = destroy_stubbed_dict;
|
|
|
|
|
2010-01-02 02:40:25 +01:00
|
|
|
dict->faces = (XP_UCHAR*)
|
|
|
|
XP_MALLOC( mpool, 2 * dict->nFaces * sizeof(dict->faces[0]) );
|
|
|
|
dict->facePtrs = (XP_UCHAR**)
|
|
|
|
XP_MALLOC( mpool, dict->nFaces * sizeof(dict->facePtrs[0]) );
|
|
|
|
|
|
|
|
XP_UCHAR* nextChar = dict->faces;
|
|
|
|
XP_UCHAR** nextPtr = dict->facePtrs;
|
|
|
|
for ( ii = 0; ii < datasize/3; ++ii ) {
|
|
|
|
*nextPtr++ = nextChar;
|
|
|
|
*nextChar++ = (XP_UCHAR)data[(ii*3)+2];
|
|
|
|
*nextChar++ = '\0';
|
2003-11-01 06:35:29 +01:00
|
|
|
}
|
|
|
|
|
2005-01-04 05:06:37 +01:00
|
|
|
dict->countsAndValues = (XP_U8*)XP_MALLOC( mpool, dict->nFaces*2 );
|
2010-01-02 02:40:25 +01:00
|
|
|
for ( ii = 0; ii < datasize/3; ++ii ) {
|
|
|
|
dict->countsAndValues[ii*2] = data[(ii*3)];
|
|
|
|
dict->countsAndValues[(ii*2)+1] = data[(ii*3)+1];
|
2003-11-01 06:35:29 +01:00
|
|
|
}
|
|
|
|
|
2005-01-04 05:06:37 +01:00
|
|
|
dict->bitmaps = (SpecialBitmaps*)XP_MALLOC( mpool, sizeof(SpecialBitmaps) );
|
2003-11-01 06:35:29 +01:00
|
|
|
dict->bitmaps->largeBM = dict->bitmaps->largeBM = NULL;
|
|
|
|
|
|
|
|
setStubbedSpecials( dict );
|
|
|
|
|
|
|
|
setBlankTile( dict );
|
|
|
|
|
|
|
|
return dict;
|
2022-03-14 18:28:08 +01:00
|
|
|
} /* make_stubbed_dict */
|
2003-11-01 06:35:29 +01:00
|
|
|
|
|
|
|
#endif /* STUBBED_DICT */
|
|
|
|
|
2004-10-07 15:23:20 +02:00
|
|
|
static array_edge*
|
2006-09-02 07:30:51 +02:00
|
|
|
dict_super_edge_for_index( const DictionaryCtxt* dict, XP_U32 index )
|
2003-11-01 06:35:29 +01:00
|
|
|
{
|
|
|
|
array_edge* result;
|
|
|
|
|
|
|
|
if ( index == 0 ) {
|
|
|
|
result = NULL;
|
|
|
|
} else {
|
|
|
|
XP_ASSERT( index < dict->numEdges );
|
2004-10-07 15:23:20 +02:00
|
|
|
/* avoid long-multiplication lib call on Palm... */
|
|
|
|
if ( dict->nodeSize == 3 ) {
|
|
|
|
index += (index << 1);
|
|
|
|
} else {
|
|
|
|
XP_ASSERT( dict->nodeSize == 4 );
|
|
|
|
index <<= 2;
|
|
|
|
}
|
2003-11-01 06:35:29 +01:00
|
|
|
result = &dict->base[index];
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
} /* dict_edge_for_index */
|
2004-10-07 15:23:20 +02:00
|
|
|
|
|
|
|
static array_edge*
|
2006-09-02 07:30:51 +02:00
|
|
|
dict_super_getTopEdge( const DictionaryCtxt* dict )
|
2004-10-07 15:23:20 +02:00
|
|
|
{
|
|
|
|
return dict->topEdge;
|
|
|
|
} /* dict_super_getTopEdge */
|
|
|
|
|
2014-03-13 03:24:00 +01:00
|
|
|
static XP_U32
|
2011-10-22 03:51:33 +02:00
|
|
|
dict_super_index_from( const DictionaryCtxt* dict, array_edge* p_edge )
|
|
|
|
{
|
|
|
|
unsigned long result;
|
|
|
|
|
|
|
|
array_edge_new* edge = (array_edge_new*)p_edge;
|
|
|
|
result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF;
|
|
|
|
|
|
|
|
if ( dict->is_4_byte ) {
|
|
|
|
result |= ((XP_U32)edge->moreBits) << 16;
|
|
|
|
} else {
|
|
|
|
XP_ASSERT( dict->nodeSize == 3 );
|
|
|
|
if ( (edge->bits & EXTRABITMASK_NEW) != 0 ) {
|
|
|
|
result |= 0x00010000; /* using | instead of + saves 4 bytes */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
} /* dict_super_index_from */
|
|
|
|
|
|
|
|
static array_edge*
|
|
|
|
dict_super_follow( const DictionaryCtxt* dict, array_edge* in )
|
|
|
|
{
|
|
|
|
XP_U32 index = dict_index_from( dict, in );
|
|
|
|
array_edge* result = index > 0?
|
|
|
|
dict_edge_for_index( dict, index ): (array_edge*)NULL;
|
|
|
|
return result;
|
|
|
|
} /* dict_super_follow */
|
|
|
|
|
2011-10-29 05:27:16 +02:00
|
|
|
static array_edge*
|
|
|
|
dict_super_edge_with_tile( const DictionaryCtxt* dict, array_edge* from,
|
|
|
|
Tile tile )
|
|
|
|
{
|
|
|
|
for ( ; ; ) {
|
|
|
|
Tile candidate = EDGETILE(dict,from);
|
|
|
|
if ( candidate == tile ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( IS_LAST_EDGE(dict, from ) ) {
|
|
|
|
from = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
from += dict->nodeSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
return from;
|
|
|
|
} /* edge_with_tile */
|
|
|
|
|
2004-10-07 15:23:20 +02:00
|
|
|
void
|
2021-02-02 05:13:25 +01:00
|
|
|
dict_super_init( MPFORMAL DictionaryCtxt* dict )
|
2004-10-07 15:23:20 +02:00
|
|
|
{
|
2021-02-11 01:46:15 +01:00
|
|
|
MPASSIGN(dict->mpool, mpool);
|
|
|
|
|
2004-10-07 15:23:20 +02:00
|
|
|
/* subclass may change these later.... */
|
2009-09-13 06:57:44 +02:00
|
|
|
dict->func_edge_for_index = dict_super_edge_for_index;
|
|
|
|
dict->func_dict_getTopEdge = dict_super_getTopEdge;
|
2011-10-22 03:51:33 +02:00
|
|
|
dict->func_dict_index_from = dict_super_index_from;
|
|
|
|
dict->func_dict_follow = dict_super_follow;
|
2011-10-29 05:27:16 +02:00
|
|
|
dict->func_dict_edge_with_tile = dict_super_edge_with_tile;
|
2009-09-13 06:57:44 +02:00
|
|
|
dict->func_dict_getShortName = dict_getName;
|
2014-03-12 06:01:11 +01:00
|
|
|
|
|
|
|
pthread_mutex_init( &dict->mutex, NULL );
|
2004-10-07 15:23:20 +02:00
|
|
|
} /* dict_super_init */
|
2003-11-01 06:35:29 +01:00
|
|
|
|
2022-03-15 23:03:03 +01:00
|
|
|
void
|
|
|
|
dict_super_destroy( DictionaryCtxt* dict )
|
|
|
|
{
|
|
|
|
XP_FREEP( dict->mpool, &dict->desc );
|
|
|
|
XP_FREEP( dict->mpool, &dict->md5Sum );
|
|
|
|
XP_FREEP( dict->mpool, &dict->values );
|
|
|
|
for ( int ii = 0; ii < VSIZE(dict->counts); ++ii ) {
|
|
|
|
XP_FREEP( dict->mpool, &dict->counts[ii] );
|
|
|
|
}
|
|
|
|
XP_FREEP( dict->mpool, &dict->otherCounts );
|
|
|
|
XP_FREEP( dict->mpool, &dict->faces );
|
|
|
|
XP_FREEP( dict->mpool, &dict->facePtrs );
|
|
|
|
XP_FREEP( dict->mpool, &dict->name );
|
2022-05-28 18:00:20 +02:00
|
|
|
XP_FREEP( dict->mpool, &dict->isoCode );
|
|
|
|
XP_FREEP( dict->mpool, &dict->langName );
|
2022-03-15 23:03:03 +01:00
|
|
|
}
|
|
|
|
|
2011-04-12 03:55:42 +02:00
|
|
|
const XP_UCHAR*
|
|
|
|
dict_getLangName( const DictionaryCtxt* ctxt )
|
|
|
|
{
|
|
|
|
return ctxt->langName;
|
|
|
|
}
|
|
|
|
|
2011-11-09 15:51:12 +01:00
|
|
|
#ifdef XWFEATURE_DICTSANITY
|
|
|
|
XP_Bool
|
|
|
|
checkSanity( DictionaryCtxt* dict, const XP_U32 numEdges )
|
|
|
|
{
|
|
|
|
XP_U32 ii;
|
|
|
|
XP_Bool passed = XP_TRUE;
|
2011-11-11 03:24:45 +01:00
|
|
|
|
2011-11-09 15:51:12 +01:00
|
|
|
array_edge* edge = dict->base;
|
2011-11-22 03:06:46 +01:00
|
|
|
if ( NULL != edge ) { /* not empty dict */
|
|
|
|
XP_U16 nFaces = dict_numTileFaces( dict );
|
|
|
|
Tile prevTile = 0;
|
|
|
|
for ( ii = 0; ii < numEdges && passed; ++ii ) {
|
|
|
|
Tile tile = EDGETILE( dict, edge );
|
|
|
|
if ( tile < prevTile || tile >= nFaces ) {
|
2014-01-07 15:58:20 +01:00
|
|
|
XP_LOGF( "%s: node %d (out of %d) has too-large or "
|
2011-11-22 03:06:46 +01:00
|
|
|
"out-of-order tile", __func__, ii, numEdges );
|
|
|
|
passed = XP_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prevTile = tile;
|
2011-11-09 15:51:12 +01:00
|
|
|
|
2014-03-13 03:24:00 +01:00
|
|
|
XP_U32 index = dict_index_from( dict, edge );
|
2011-11-22 03:06:46 +01:00
|
|
|
if ( index >= numEdges ) {
|
2014-03-13 03:24:00 +01:00
|
|
|
XP_LOGF( "%s: node %d (out of %d) has too-high index %d",
|
2011-11-22 03:06:46 +01:00
|
|
|
__func__, ii, numEdges, index );
|
|
|
|
passed = XP_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
2011-11-09 15:51:12 +01:00
|
|
|
|
2011-11-22 03:06:46 +01:00
|
|
|
if ( IS_LAST_EDGE( dict, edge ) ) {
|
|
|
|
prevTile = 0;
|
|
|
|
}
|
|
|
|
edge += dict->nodeSize;
|
2011-11-09 15:51:12 +01:00
|
|
|
}
|
|
|
|
|
2011-11-22 03:06:46 +01:00
|
|
|
if ( passed ) {
|
|
|
|
passed = 0 == prevTile; /* last edge seen was a LAST_EDGE */
|
|
|
|
}
|
2011-11-09 15:51:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return passed;
|
|
|
|
} /* checkSanity */
|
|
|
|
#endif
|
|
|
|
|
2003-11-01 06:35:29 +01:00
|
|
|
#ifdef CPLUS
|
|
|
|
}
|
|
|
|
#endif
|