xwords/wince/cedict.c
ehouse 8f08667f5d Lots more changes, and some reversions. Win32, gtk and ncurses all
work now including saving/opening games.  Network play probably
doesn't as writing/reading a dict from stream has to change.  It's
doubtful patches from this branch will be merged.  Rather, I'll take
the concepts and crib some code when re-implementing.  Concepts:
XP_UCHAR becomes UTF-8 on both platforms, and on wince we translate to
wchar_t just as now but from the utf-8 codepage.  That keeps the work
and risk to a minimum.
2009-03-29 15:53:15 +00:00

889 lines
26 KiB
C
Executable file

/* -*- fill-column: 77; compile-command: "make TARGET_OS=wince DEBUG=TRUE" -*- */
/*
* Copyright 1997-2008 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.
*/
#ifndef STUBBED_DICT
#include <stdio.h> /* _snwprintf */
#include <string.h> /* _snwprintf */
#include "stdafx.h"
/* #include <accctrl.h> */
#include <commdlg.h>
#include "dictnryp.h"
#include "strutils.h"
#include "cedict.h"
#include "debhacks.h"
#include "cedebug.h"
#include "ceutil.h"
typedef struct CEDictionaryCtxt {
DictionaryCtxt super;
HANDLE mappedFile;
void* mappedBase;
} CEDictionaryCtxt;
static void ce_dict_destroy( DictionaryCtxt* dict );
static const XP_UCHAR* ce_dict_getShortName( const DictionaryCtxt* dict );
static void ceLoadSpecialData( CEDictionaryCtxt* ctxt, XP_U8** ptrp );
static XP_U16 ceCountSpecials( CEDictionaryCtxt* ctxt );
static XP_Bitmap ceMakeBitmap( CEDictionaryCtxt* ctxt, XP_U8** ptrp );
static XP_U32 n_ptr_tohl( XP_U8** in );
static XP_U16 n_ptr_tohs( XP_U8** in );
static XP_U8* openMappedFile( MPFORMAL const wchar_t* name,
HANDLE* mappedFileP, HANDLE* hFileP,
XP_U32* sizep );
static void closeMappedFile( MPFORMAL XP_U8* base, HANDLE mappedFile );
static XP_Bool checkIfDictAndLegal( MPFORMAL wchar_t* path, XP_U16 pathLen,
wchar_t* name );
static XP_Bool findAlternateDict( CEAppGlobals* globals, wchar_t* dictName );
#define ALIGN_COUNT 2
DictionaryCtxt*
ce_dictionary_make( CEAppGlobals* globals, const char* dictName )
{
XP_LOGF( "%s(%s)", __func__, dictName );
CEDictionaryCtxt* ctxt = (CEDictionaryCtxt*)NULL;
HANDLE mappedFile = NULL;
wchar_t nameBuf[MAX_PATH+1];
HANDLE hFile;
XP_U8* ptr;
XP_U32 dictLength;
XP_UCHAR buf[CE_MAX_PATH_LEN+1]; /* in case we have to look */
XP_ASSERT( !!dictName );
#ifdef UCHAR_IS_WIDE
XP_SNPRINTF( nameBuf, VSIZE(nameBuf), XP_L("%ls"), dictName );
#else
MultiByteToWideChar( CP_UTF8, MB_PRECOMPOSED, dictName, -1,
nameBuf, VSIZE(nameBuf) );
#endif
XP_LOGF( "calling openMappedFile 1" );
ptr = openMappedFile( MPPARM(globals->mpool) nameBuf, &mappedFile,
&hFile, &dictLength );
if ( !ptr ) {
if ( findAlternateDict( globals, nameBuf ) ) {
#ifdef UCHAR_IS_WIDE
XP_SNPRINTF( buf, VSIZE(buf), XP_L("%ls"), nameBuf );
#else
(void)WideCharToMultiByte( CP_UTF8, 0, nameBuf, -1,
buf, sizeof(buf), NULL, NULL );
#endif
XP_LOGF( "calling openMappedFile 2" );
ptr = openMappedFile( MPPARM(globals->mpool) nameBuf, &mappedFile,
&hFile, &dictLength );
if ( !!ptr ) {
dictName = buf;
}
}
}
XP_LOGF( "opened file %ls: %p", nameBuf, ptr );
while( !!ptr ) { /* lets us break.... */
XP_U32 offset;
XP_U16 numFaces, numFaceBytes;
XP_U16 i;
XP_U16 flags;
void* mappedBase = (void*)ptr;
XP_U8 nodeSize;
XP_Bool isUTF8 = XP_FALSE;
flags = n_ptr_tohs( &ptr );
#ifdef NODE_CAN_4
if ( flags == 0x0002 ) {
nodeSize = 3;
} else if ( flags == 0x0003 ) {
nodeSize = 4;
} else if ( flags == 0x0004 ) {
isUTF8 = XP_TRUE;
nodeSize = 3;
} else if ( flags == 0x0005 ) {
isUTF8 = XP_TRUE;
nodeSize = 4;
} else {
break; /* we want to return NULL */
}
#else
if( flags != 0x0001 ) {
break;
}
#endif
if ( isUTF8 ) {
numFaceBytes = (XP_U16)(*ptr++);
}
numFaces = (XP_U16)(*ptr++);
if ( numFaces > 64 ) {
break;
}
ctxt = (CEDictionaryCtxt*)ce_dictionary_make_empty( globals );
ctxt->mappedFile = mappedFile;
ctxt->mappedBase = mappedBase;
ctxt->super.nodeSize = nodeSize;
ctxt->super.destructor = ce_dict_destroy;
ctxt->super.func_dict_getShortName = ce_dict_getShortName;
if ( !isUTF8 ) {
numFaceBytes = numFaces * 2;
}
ctxt->super.nFaces = (XP_U8)numFaces;
ctxt->super.isUTF8 = isUTF8;
if ( isUTF8 ) {
dict_splitFaces( &ctxt->super, ptr, numFaceBytes, numFaces );
ptr += numFaceBytes;
} else {
XP_U8 tmp[numFaces*4]; /* should be enough... */
XP_U16 nBytes = 0;
XP_U16 ii;
/* Need to translate from iso-8859-n to utf8 */
for ( ii = 0; ii < numFaces; ++ii ) {
XP_UCHAR ch = ptr[1];
wchar_t wch;
ptr += 2;
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, &ch, 1, &wch, 1 );
XP_UCHAR utfs[8];
int len = WideCharToMultiByte( CP_UTF8, 0, &wch, 1,
utfs, VSIZE(utfs), NULL, NULL );
XP_MEMCPY( &tmp[nBytes], utfs, len );
nBytes += len;
}
dict_splitFaces( &ctxt->super, tmp, nBytes, numFaces );
}
XP_LOGF( "%s: dict_splitFaces done", __func__ );
ctxt->super.is_4_byte = (ctxt->super.nodeSize == 4);
ctxt->super.countsAndValues =
(XP_U8*)XP_MALLOC(globals->mpool, numFaces*2);
ptr += 2; /* skip xloc header */
for ( i = 0; i < numFaces*2; i += 2 ) {
ctxt->super.countsAndValues[i] = *ptr++;
ctxt->super.countsAndValues[i+1] = *ptr++;
}
ceLoadSpecialData( ctxt, &ptr );
XP_LOGF( "ceLoadSpecialData done" );
dictLength -= ptr - (XP_U8*)ctxt->mappedBase;
if ( dictLength > sizeof(XP_U32) ) {
offset = n_ptr_tohl( &ptr );
dictLength -= sizeof(offset);
#ifdef NODE_CAN_4
XP_ASSERT( dictLength % ctxt->super.nodeSize == 0 );
# ifdef DEBUG
ctxt->super.numEdges = dictLength / ctxt->super.nodeSize;
# endif
#else
XP_ASSERT( dictLength % 3 == 0 );
# ifdef DEBUG
ctxt->super.numEdges = dictLength / 3;
# endif
#endif
} else {
offset = 0;
}
if ( dictLength > 0 ) {
ctxt->super.base = (array_edge*)ptr;
#ifdef NODE_CAN_4
ctxt->super.topEdge = ctxt->super.base
+ (offset * ctxt->super.nodeSize);
#else
ctxt->super.topEdge = ctxt->super.base + (offset * 3);
#endif
} else {
ctxt->super.topEdge = (array_edge*)NULL;
ctxt->super.base = (array_edge*)NULL;
}
setBlankTile( &ctxt->super );
XP_LOGF( "copying %s", dictName );
ctxt->super.name = copyString(globals->mpool, dictName);
XP_LOGF( "got %s", ctxt->super.name );
break; /* exit phony while loop */
}
LOG_RETURNF( "%p", ctxt );
return (DictionaryCtxt*)ctxt;
} /* ce_dictionary_make */
DictionaryCtxt*
ce_dictionary_make_empty( CEAppGlobals* XP_UNUSED_DBG(globals) )
{
CEDictionaryCtxt* ctxt = (CEDictionaryCtxt*)XP_MALLOC(globals->mpool,
sizeof(*ctxt));
XP_MEMSET( ctxt, 0, sizeof(*ctxt) );
dict_super_init( (DictionaryCtxt*)ctxt );
MPASSIGN( ctxt->super.mpool, globals->mpool );
return (DictionaryCtxt*)ctxt;
} /* ce_dictionary_make_empty */
void
dict_splitFaces( DictionaryCtxt* dict, const XP_U8* utf8,
XP_U16 nBytes, XP_U16 nFaces )
{
XP_UCHAR* faces = XP_MALLOC( dict->mpool, nBytes + nFaces );
XP_U16* indices = XP_MALLOC( dict->mpool, nFaces * sizeof(indices[0]));
XP_U16 ii;
/* XP_Bool isUTF8 = dict->isUTF8; */
XP_UCHAR* next = faces;
/* const XP_U8* bytes = (const XP_U8*)utf8; */
/* if ( isUTF8 ) { */
wchar_t widebuf[nFaces];
int nRead = MultiByteToWideChar( CP_UTF8, MB_PRECOMPOSED, (char*)utf8, nBytes,
widebuf, VSIZE(widebuf) );
XP_ASSERT( nRead == nFaces );
/* now split */
for ( ii = 0; ii < nFaces; ++ii ) {
indices[ii] = next - faces;
int nWritten = WideCharToMultiByte( CP_UTF8, 0, &widebuf[ii], 1,
next, 100, NULL, NULL );
next += nWritten;
*next++ = 0;
XP_LOGF( "%s: read char %d: %s", __func__, ii, &faces[indices[ii]] );
}
/* } else { */
/* for ( ii = 0; ii < nFaces; ++ii ) { */
/* indices[ii] = next - faces; */
/* XP_ASSERT( 0 == *bytes ); */
/* ++bytes; /\* skip empty *\/ */
/* *next++ = *bytes++; */
/* *next++ = '\0'; */
/* } */
/* } */
XP_ASSERT( next == faces + nFaces + nBytes );
XP_ASSERT( !dict->faces );
dict->faces = faces;
XP_ASSERT( !dict->faceIndices );
dict->faceIndices = indices;
} /* dict_splitFaces */
static void
ceLoadSpecialData( CEDictionaryCtxt* ctxt, XP_U8** ptrp )
{
XP_U16 nSpecials = ceCountSpecials( ctxt );
XP_U8* ptr = *ptrp;
Tile ii;
XP_UCHAR** texts;
SpecialBitmaps* bitmaps;
texts = (XP_UCHAR**)XP_MALLOC( ctxt->super.mpool,
nSpecials * sizeof(*texts) );
bitmaps = (SpecialBitmaps*)
XP_MALLOC( ctxt->super.mpool, nSpecials * sizeof(*bitmaps) );
for ( ii = 0; ii < ctxt->super.nFaces; ++ii ) {
XP_UCHAR* facep = &ctxt->super.faces[ctxt->super.faceIndices[(short)ii]];
if ( IS_SPECIAL(*facep) ) {
XP_LOGF( "face %d is special", ii );
/* get the string */
XP_U8 txtlen = *ptr++;
XP_UCHAR* text = (XP_UCHAR*)XP_MALLOC(ctxt->super.mpool, txtlen+1);
XP_MEMCPY( text, ptr, txtlen );
ptr += txtlen;
text[txtlen] = '\0';
XP_ASSERT( *facep < nSpecials );
texts[(int)*facep] = text;
bitmaps[(int)*facep].largeBM = ceMakeBitmap( ctxt, &ptr );
bitmaps[(int)*facep].smallBM = ceMakeBitmap( ctxt, &ptr );
}
}
ctxt->super.chars = texts;
ctxt->super.bitmaps = bitmaps;
*ptrp = ptr;
} /* ceLoadSpecialData */
static XP_U16
ceCountSpecials( CEDictionaryCtxt* ctxt )
{
XP_U16 result = 0;
XP_U16 ii;
for ( ii = 0; ii < ctxt->super.nFaces; ++ii ) {
XP_U16 index = ctxt->super.faceIndices[ii];
if ( IS_SPECIAL( ctxt->super.faces[index] ) ) {
++result;
}
}
return result;
} /* ceCountSpecials */
#if 0
static void
printBitmapData1( XP_U16 nCols, XP_U16 nRows, XP_U8* data )
{
char strs[20];
XP_U16 rowBytes;
XP_U16 row, col;
rowBytes = (nCols + 7) / 8;
while ( (rowBytes % 2) != 0 ) {
++rowBytes;
}
for ( row = 0; row < nRows; ++row ) {
for ( col = 0; col < nCols; ++col ) {
XP_UCHAR byt = data[col / 8];
XP_Bool set = (byt & (0x80 >> (col % 8))) != 0;
strs[col] = set? '#': '.';
}
data += rowBytes;
strs[nCols] = '\0';
XP_DEBUGF( strs );
}
} /* printBitmapData1 */
static void
printBitmapData2( XP_U16 nCols, XP_U16 nRows, XP_U8* data )
{
while ( nRows-- ) {
XP_UCHAR buf[100];
XP_UCHAR* ptr = buf;
XP_U16 rowBytes = (nCols + 7) / 8;
while ( (rowBytes % ALIGN_COUNT) != 0 ) {
++rowBytes;
}
while ( rowBytes-- ) {
ptr += XP_SNPRINTF( ptr, sizeof(buf), "0x%.2x ", *data++ );
}
XP_DEBUGF( buf );
}
} /* printBitmapData2 */
static void
longSwapData( XP_U8* destBase, XP_U16 nRows, XP_U16 rowBytes )
{
XP_U32* longBase = (XP_U32*)destBase;
rowBytes /= 4;
while ( nRows-- ) {
XP_U16 i;
for ( i = 0; i < rowBytes; ++i ) {
XP_U32 n = *longBase;
XP_U32 tmp = 0;
tmp |= (n >> 24) & 0x000000FF;
tmp |= (n >> 16) & 0x0000FF00;
tmp |= (n >> 8 ) & 0x00FF0000;
tmp |= (n >> 0 ) & 0xFF000000;
*longBase = tmp;
++longBase;
}
}
} /* longSwapData */
#endif
static XP_Bitmap
ceMakeBitmap( CEDictionaryCtxt* XP_UNUSED_DBG(ctxt), XP_U8** ptrp )
{
XP_U8* ptr = *ptrp;
XP_U8 nCols = *ptr++;
CEBitmapInfo* bitmap = (CEBitmapInfo*)NULL;
if ( nCols > 0 ) {
XP_U8* dest;
XP_U8* savedDest;
XP_U8 nRows = *ptr++;
XP_U16 rowBytes = (nCols+7) / 8;
XP_U8 srcByte = 0;
XP_U8 destByte = 0;
XP_U8 nBits;
XP_U16 i;
bitmap = (CEBitmapInfo*)XP_MALLOC( ctxt->super.mpool,
sizeof(bitmap) );
bitmap->nCols = nCols;
bitmap->nRows = nRows;
dest = XP_MALLOC( ctxt->super.mpool, rowBytes * nRows );
bitmap->bits = savedDest = dest;
nBits = nRows * nCols;
for ( i = 0; i < nBits; ++i ) {
XP_U8 srcBitIndex = i % 8;
XP_U8 destBitIndex = (i % nCols) % 8;
XP_U8 srcMask, bit;
if ( srcBitIndex == 0 ) {
srcByte = *ptr++;
}
srcMask = 1 << (7 - srcBitIndex);
bit = (srcByte & srcMask) != 0;
destByte |= bit << (7 - destBitIndex);
/* we need to put the byte if we've filled it or if we're done
with the row */
if ( (destBitIndex==7) || ((i%nCols) == (nCols-1)) ) {
*dest++ = destByte;
destByte = 0;
}
}
/* printBitmapData1( nCols, nRows, savedDest ); */
/* printBitmapData2( nCols, nRows, savedDest ); */
}
*ptrp = ptr;
return (XP_Bitmap)bitmap;
} /* ceMakeBitmap */
static void
ceDeleteBitmap( const CEDictionaryCtxt* XP_UNUSED_DBG(ctxt),
XP_Bitmap* bitmap )
{
if ( !!bitmap ) {
CEBitmapInfo* bmi = (CEBitmapInfo*)bitmap;
XP_FREE( ctxt->super.mpool, bmi->bits );
XP_FREE( ctxt->super.mpool, bmi );
}
}
static void
ce_dict_destroy( DictionaryCtxt* dict )
{
CEDictionaryCtxt* ctxt = (CEDictionaryCtxt*)dict;
XP_U16 nSpecials = ceCountSpecials( ctxt );
XP_U16 i;
if ( !!ctxt->super.chars ) {
for ( i = 0; i < nSpecials; ++i ) {
XP_UCHAR* text = ctxt->super.chars[i];
if ( !!text ) {
XP_FREE( ctxt->super.mpool, text );
}
}
XP_FREE( ctxt->super.mpool, ctxt->super.chars );
}
if ( !!ctxt->super.bitmaps ) {
for ( i = 0; i < nSpecials; ++i ) {
ceDeleteBitmap( ctxt, ctxt->super.bitmaps[i].largeBM );
ceDeleteBitmap( ctxt, ctxt->super.bitmaps[i].smallBM );
}
XP_FREE( ctxt->super.mpool, ctxt->super.bitmaps );
}
XP_FREE( ctxt->super.mpool, ctxt->super.faces );
XP_FREE( ctxt->super.mpool, ctxt->super.faceIndices );
XP_FREE( ctxt->super.mpool, ctxt->super.countsAndValues );
XP_FREE( ctxt->super.mpool, ctxt->super.name );
closeMappedFile( MPPARM(ctxt->super.mpool) ctxt->mappedBase,
ctxt->mappedFile );
XP_FREE( ctxt->super.mpool, ctxt );
} // ce_dict_destroy
static const XP_UCHAR*
ce_dict_getShortName( const DictionaryCtxt* dict )
{
const XP_UCHAR* name = dict_getName( dict );
return bname( name );
} /* ce_dict_getShortName */
static XP_U8*
openMappedFile( MPFORMAL const wchar_t* name, HANDLE* mappedFileP,
HANDLE* hFileP, XP_U32* sizep )
{
XP_U8* ptr = NULL;
HANDLE hFile;
#if defined _WIN32_WCE
hFile = CreateFileForMapping( name,
GENERIC_READ,
FILE_SHARE_READ, /* (was 0: no sharing) */
NULL, /* security */
OPEN_EXISTING,
FILE_FLAG_RANDOM_ACCESS,
NULL );
if ( hFile == INVALID_HANDLE_VALUE ) {
logLastError( "CreateFileForMapping" );
} else {
HANDLE mappedFile;
mappedFile = CreateFileMapping( hFile,
NULL,
PAGE_READONLY,
0,
0,
NULL );
if ( mappedFile != INVALID_HANDLE_VALUE ) {
void* mappedBase = MapViewOfFile( mappedFile,
FILE_MAP_READ,
0, 0, 0 );
ptr = (XP_U8*)mappedBase;
*mappedFileP = mappedFile;
*hFileP = hFile;
if ( sizep != NULL ) {
*sizep = GetFileSize( hFile, NULL );
}
}
}
#else
hFile = CreateFile( name,
GENERIC_READ,
FILE_SHARE_READ,
NULL, /* security */
OPEN_EXISTING,
FILE_FLAG_RANDOM_ACCESS,
NULL );
if ( hFile != INVALID_HANDLE_VALUE ) {
DWORD size = GetFileSize( hFile, NULL );
ptr = XP_MALLOC( mpool, size );
if ( ptr != NULL ) {
DWORD nRead;
if ( ReadFile( hFile, ptr, size, &nRead, NULL ) ) {
XP_ASSERT( nRead == size );
} else {
XP_FREE( mpool, ptr );
ptr = NULL;
}
}
CloseHandle( hFile );
*hFileP = NULL; /* nothing to close later */
if ( sizep != NULL ) {
*sizep = size;
}
*mappedFileP = (HANDLE)ptr;
}
#endif
return ptr;
} /* openMappedFile */
static void
closeMappedFile( MPFORMAL XP_U8* base,
HANDLE XP_UNUSED_32(mappedFile) )
{
#ifdef _WIN32_WCE
UnmapViewOfFile( base );
CloseHandle( mappedFile );
#else
XP_FREE( mpool, base );
#endif
}
static XP_Bool
checkIfDictAndLegal( MPFORMAL wchar_t* path, XP_U16 pathLen,
wchar_t* name )
{
XP_Bool result = XP_FALSE;
XP_U16 len;
len = wcslen(name);
/* are the last four bytes ".xwd"? */
if ( 0 == lstrcmp( name + len - 4, L".xwd" ) ) {
XP_U16 flags;
HANDLE mappedFile, hFile;
XP_U8* base;
wchar_t pathBuf[CE_MAX_PATH_LEN+1];
wcscpy( pathBuf, path );
pathBuf[pathLen] = 0;
wcscat( pathBuf, name );
#ifdef DEBUG
{
char narrowName[CE_MAX_PATH_LEN+1];
int len = wcslen( pathBuf );
len = WideCharToMultiByte( CP_UTF8, 0, pathBuf, len + 1,
narrowName, len + 1, NULL, NULL );
}
#endif
base = openMappedFile( MPPARM(mpool) pathBuf, &mappedFile,
&hFile, NULL );
if ( !!base ) {
XP_U8* ptr = base;
flags = n_ptr_tohs( &ptr );
closeMappedFile( MPPARM(mpool) base, mappedFile );
#ifdef NODE_CAN_4
/* are the flags what we expect */
result = flags >= 0x0002 && flags <= 0x0005;
#else
result = flags == 0x0001;
#endif
}
}
return result;
} /* checkIfDictAndLegal */
static XP_Bool
locateOneDir( MPFORMAL wchar_t* path, OnePathCB cb, void* ctxt, XP_U16 nSought,
XP_U16* nFoundP )
{
WIN32_FIND_DATA data;
HANDLE fileH;
XP_Bool done = XP_FALSE;
XP_U16 startLen;
lstrcat( path, L"\\" );
startLen = wcslen(path); /* record where we were so can back up */
lstrcat( path, L"*" );
XP_MEMSET( &data, 0, sizeof(data) );
/* Looks like I need to look at every file. If it's a directory I search
it recursively. If it's an .xwd file I check whether it's got the
right flags and if so I return its name. */
fileH = FindFirstFile( path, &data );
if ( fileH != INVALID_HANDLE_VALUE ) {
for ( ; ; ) {
if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0){
if ( ( data.cFileName[0] == '.' )
&& ( (data.cFileName[1] == '.')
|| (data.cFileName[1] == '\0' ) ) ) {
/* skip . and .. */
} else {
lstrcpy( path+startLen, data.cFileName );
done = locateOneDir( MPPARM(mpool) path, cb, ctxt,
nSought, nFoundP );
XP_ASSERT( done || *nFoundP < nSought );
if ( done ) {
break;
}
}
} else if ( checkIfDictAndLegal( MPPARM(mpool) path, startLen,
data.cFileName ) ) {
XP_ASSERT( *nFoundP < nSought );
lstrcpy( path+startLen, data.cFileName );
done = (*cb)( path, (*nFoundP)++, ctxt )
|| *nFoundP == nSought;
if ( done ) {
break;
}
}
if ( !FindNextFile( fileH, &data ) ) {
XP_ASSERT( GetLastError() == ERROR_NO_MORE_FILES );
break;
}
path[startLen] = 0;
}
(void)FindClose( fileH );
}
return done;
} /* locateOneDir */
static XP_Bool
getDictDir( wchar_t* buf, XP_U16 bufLen )
{
/* I wanted to use SHGetKnownFolderPath to search in \\Program
Files\\Crosswords, but perhaps it's better to search in the directory
in which the app is located. If I get CAB files working for
Smartphone, then that directory will be \\Program Files\\Crosswords.
But if users have to install files using the File Explorer it'll be
easier for them if all that's required is that the app and dictionaries
be in the same place. GetModuleFileName() supports both.
*/
DWORD nChars = GetModuleFileName( NULL, buf, bufLen );
XP_Bool success = nChars < bufLen;
if ( success ) {
wchar_t* lastSlash = wcsrchr( buf, '\\' );
if ( !!lastSlash ) {
*lastSlash = 0;
}
}
/* SHGetSpecialFolderPath(NULL,NULL,0,FALSE); */
return success;
} /* getDictDir */
XP_U16
ceLocateNDicts( CEAppGlobals* globals, XP_U16 nSought, OnePathCB cb,
void* ctxt )
{
XP_U16 nFound = 0;
wchar_t path[CE_MAX_PATH_LEN+1];
if ( getDictDir( path, VSIZE(path) ) ) {
locateOneDir( MPPARM(globals->mpool) path, cb, ctxt, nSought, &nFound );
}
if ( nFound < nSought ) {
ceGetPath( globals, PROGFILES_PATH, path, VSIZE(path) );
locateOneDir( MPPARM(globals->mpool) path, cb, ctxt, nSought, &nFound );
}
if ( nFound < nSought ) {
WIN32_FIND_DATA data;
HANDLE fileH;
XP_MEMSET( &data, 0, sizeof(data) );
fileH = FindFirstFile( L"\\*", &data );
while ( fileH != INVALID_HANDLE_VALUE ) {
if ( ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
&& (((data.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) != 0) ) ) {
wsprintf( path, L"\\%s\\Crosswords", data.cFileName );
XP_LOGW( "looking in:", path );
locateOneDir( MPPARM(globals->mpool) path, cb, ctxt,
nSought, &nFound );
}
if ( nFound >= nSought ) {
break;
}
if ( !FindNextFile( fileH, &data ) ) {
break;
}
}
}
return nFound;
} /* ceLocateNDicts */
typedef struct FindOneData {
wchar_t* result;
const wchar_t* sought;
XP_Bool found;
} FindOneData;
static XP_Bool
matchShortName( const wchar_t* wPath, XP_U16 XP_UNUSED(index), void* ctxt )
{
FindOneData* datap = (FindOneData*)ctxt;
wchar_t buf[CE_MAX_PATH_LEN+1];
wchar_t* name;
XP_ASSERT( !datap->found );
name = wbname( buf, VSIZE(buf), wPath );
if ( 0 == wcscmp( name, datap->sought ) ) {
wcscpy( datap->result, wPath );
datap->found = XP_TRUE;
}
return datap->found;
} /* matchShortName */
/* Users sometimes move dicts. Given a path to a dict that doesn't exist, See
* if another with the same short name exists somewhere else we're willing to
* look.
*/
static XP_Bool
findAlternateDict( CEAppGlobals* globals, wchar_t* path )
{
wchar_t shortPath[CE_MAX_PATH_LEN+1];
FindOneData data;
XP_MEMSET( &data, 0, sizeof(data) );
data.sought = wbname( shortPath, VSIZE(shortPath), path );
data.result = path;
(void)ceLocateNDicts( globals, CE_MAXDICTS, matchShortName,
&data );
return data.found;
} /* findAlternateDict */
static XP_U32
n_ptr_tohl( XP_U8** inp )
{
XP_U32 t;
XP_MEMCPY( &t, *inp, sizeof(t) );
*inp += sizeof(t);
return XP_NTOHL(t);
} /* n_ptr_tohl */
static XP_U16
n_ptr_tohs( XP_U8** inp )
{
XP_U16 t;
XP_MEMCPY( &t, *inp, sizeof(t) );
*inp += sizeof(t);
return XP_NTOHS(t);
} /* n_ptr_tohs */
const XP_UCHAR*
bname( const XP_UCHAR* in )
{
XP_U16 len = (XP_U16)XP_STRLEN(in);
const XP_UCHAR* out = in + len - 1;
while ( *out != '\\' && out >= in ) {
--out;
}
return out + 1;
} /* bname */
/* WFT does this do, and why not using strrchr? */
wchar_t*
wbname( wchar_t* buf, XP_U16 buflen, const wchar_t* in )
{
wchar_t* result;
_snwprintf( buf, buflen, L"%s", in );
result = buf + wcslen( buf ) - 1;
/* wipe out extension */
while ( *result != L'.' ) {
--result;
XP_ASSERT( result > buf ); /* firing */
}
*result = 0;
while ( result >= buf && *result != L'\\' ) {
--result;
}
return result + 1;
} /* wbname */
#endif /* ifndef STUBBED_DICT */