/* -*- compile-command: "make -j3 TARGET_OS=wince DEBUG=TRUE"; -*- */ /* * Copyright 2009 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. */ #include "ceresstr.h" #include "ceutil.h" #include "cedebug.h" #include "strutils.h" static XP_U16 getDLLVersion( HINSTANCE hinst ); HINSTANCE ceLoadResFile( const XP_UCHAR* file ) { HINSTANCE hinst = NULL; wchar_t widebuf[256]; (void)MultiByteToWideChar( CP_ACP, 0, file, -1, widebuf, VSIZE(widebuf) ); hinst = LoadLibrary( widebuf ); if ( CUR_DLL_VERSION != getDLLVersion( hinst ) ) { FreeLibrary( hinst ); hinst = NULL; } return hinst; } /* ceLoadResFile */ void ceCloseResFile( HINSTANCE inst ) { XP_ASSERT( !!inst ); FreeLibrary( inst ); } #ifdef LOADSTRING_BROKEN typedef struct _ResStrEntry { union { XP_UCHAR nstr[1]; wchar_t wstr[1]; } u; } ResStrEntry; typedef struct _ResStrStorage { ResStrEntry* entries[CE_LAST_RES_ID - CE_FIRST_RES_ID + 1]; #ifdef DEBUG XP_U16 nUsed; #endif } ResStrStorage; static const ResStrEntry* getEntry( CEAppGlobals* globals, XP_U16 resID, XP_Bool isWide ) { ResStrStorage* storage = (ResStrStorage*)globals->resStrStorage; ResStrEntry* entry; XP_U16 index; XP_ASSERT( resID >= CE_FIRST_RES_ID && resID <= CE_LAST_RES_ID ); index = CE_LAST_RES_ID - resID; XP_ASSERT( index < VSIZE(storage->entries) ); if ( !storage ) { XP_ASSERT( !globals->exiting ); storage = XP_MALLOC( globals->mpool, sizeof( *storage ) ); XP_MEMSET( storage, 0, sizeof(*storage) ); globals->resStrStorage = storage; } entry = storage->entries[index]; if ( !entry ) { wchar_t wbuf[265]; XP_U16 len; LoadString( globals->locInst, resID, wbuf, VSIZE(wbuf) ); if ( isWide ) { len = wcslen( wbuf ); entry = (ResStrEntry*)XP_MALLOC( globals->mpool, (len*sizeof(wchar_t)) + sizeof(*entry) ); wcscpy( entry->u.wstr, wbuf ); } else { XP_UCHAR nbuf[265]; (void)WideCharToMultiByte( CP_UTF8, 0, wbuf, -1, nbuf, VSIZE(nbuf), NULL, NULL ); len = XP_STRLEN( nbuf ); entry = (ResStrEntry*)XP_MALLOC( globals->mpool, len + 1 + sizeof(*entry) ); XP_STRNCPY( entry->u.nstr, nbuf, len + 1 ); } storage->entries[index] = entry; #ifdef DEBUG ++storage->nUsed; #endif } return entry; } /* getEntry */ #endif const XP_UCHAR* ceGetResString( CEAppGlobals* globals, XP_U16 resID ) { #ifdef LOADSTRING_BROKEN const ResStrEntry* entry = getEntry( globals, resID, XP_FALSE ); return entry->u.nstr; #else /* Docs say that you can call LoadString with 0 as the length and it'll return a read-only ptr to the text within the resource, but I'm getting a ptr to wide chars back the resource text being multibyte. I swear I've seen it work, though, so might be a res file formatting thing or a param to the res compiler. Need to investigate. Until I do, the above caches local multibyte copies of the resources so the API can stay the same. */ const XP_UCHAR* str = NULL; LoadString( globals->locInst, resID, (LPSTR)&str, 0 ); return str; #endif } const wchar_t* ceGetResStringL( CEAppGlobals* globals, XP_U16 resID ) { #ifdef LOADSTRING_BROKEN const ResStrEntry* entry = getEntry( globals, resID, XP_TRUE ); return entry->u.wstr; #else /* Docs say that you can call LoadString with 0 as the length and it'll return a read-only ptr to the text within the resource, but I'm getting a ptr to wide chars back the resource text being multibyte. I swear I've seen it work, though, so might be a res file formatting thing or a param to the res compiler. Need to investigate. Until I do, the above caches local multibyte copies of the resources so the API can stay the same. */ const XP_UCHAR* str = NULL; LoadString( globals->locInst, resID, (LPSTR)&str, 0 ); return str; #endif } #ifdef LOADSTRING_BROKEN void ceFreeResStrings( CEAppGlobals* globals ) { #ifdef DEBUG XP_U16 nUsed = 0; #endif ResStrStorage* storage = (ResStrStorage*)globals->resStrStorage; if ( !!storage ) { XP_U16 ii; for ( ii = 0; ii < VSIZE(storage->entries); ++ii ) { ResStrEntry* entry = storage->entries[ii]; if ( !!entry ) { XP_FREE( globals->mpool, entry ); #ifdef DEBUG ++nUsed; #endif } } XP_ASSERT( nUsed == storage->nUsed ); XP_FREE( globals->mpool, storage ); globals->resStrStorage = NULL; } #ifdef DEBUG XP_LOGF( "%s: %d of %d strings loaded and used", __func__, nUsed, VSIZE(storage->entries) ); #endif } #endif typedef struct _DllSelState { CeDlgHdr dlgHdr; wchar_t wbuf[MAX_PATH]; const wchar_t* curFile; wchar_t* names[8]; wchar_t* files[8]; XP_U16 nItems; XP_U16 initialSel; XP_U16 dllListID; XP_Bool inited; XP_Bool cancelled; } DllSelState; static void copyWideStr( CEAppGlobals* XP_UNUSED_DBG(globals), wchar_t** dest, const wchar_t* src ) { XP_U16 len = 1 + wcslen( src ); *dest = XP_MALLOC( globals->mpool, len * sizeof(**dest) ); wcscpy( *dest, src ); } static XP_U16 getDLLVersion( HINSTANCE hinst ) { XP_U16 version = 0; /* illegal value */ HRSRC rsrcH = FindResource( hinst, MAKEINTRESOURCE(ID_DLLVERS_RES), TEXT("DLLV") ); if ( !!rsrcH ) { HGLOBAL globH = LoadResource( hinst, rsrcH ); version = *(XP_U16*)globH; DeleteObject( globH ); } return version; } typedef XP_Bool (*DllCbk)( const WIN32_FIND_DATA* data, const wchar_t* name, void* closure ); static void for_each_dll( DllCbk cbk, void* closure ) { wchar_t name[64]; HANDLE fileH; WIN32_FIND_DATA data; XP_MEMSET( &data, 0, sizeof(data) ); wchar_t path[MAX_PATH]; ceGetExeDir( path, VSIZE(path) ); wcscat( path, L"\\xwords4*.dll" ); fileH = FindFirstFile( path, &data ); while ( fileH != INVALID_HANDLE_VALUE ) { HINSTANCE hinst = LoadLibrary( data.cFileName ); if ( !!hinst ) { if ( CUR_DLL_VERSION != getDLLVersion( hinst ) ) { /* do nothing; wrong version (or just not our .dll) */ } else if ( LoadString( hinst, IDS_LANGUAGE_NAME, name, VSIZE(name) ) ) { if ( !(*cbk)( &data, name, closure ) ) { break; } } else { XP_LOGF( "IDS_LANGUAGE_NAME not found in %ls", data.cFileName ); } FreeLibrary( hinst ); } else { logLastError("LoadLibrary"); XP_LOGF( "Unable to open" ); } if ( !FindNextFile( fileH, &data ) ) { XP_ASSERT( GetLastError() == ERROR_NO_MORE_FILES ); break; } } } /* for_each_dll */ typedef struct _ListingData { DllSelState* state; CEAppGlobals* globals; XP_U16 nItems; XP_S16 selIndex; } ListingData; static XP_Bool addToListCB( const WIN32_FIND_DATA* data, const wchar_t* name, void* closure ) { ListingData* ld = (ListingData*)closure; (void)SendDlgItemMessage( ld->state->dlgHdr.hDlg, ld->state->dllListID, ADDSTRING(ld->globals), 0, (LPARAM)name ); copyWideStr( ld->globals, &ld->state->names[ld->nItems], name ); copyWideStr( ld->globals, &ld->state->files[ld->nItems], data->cFileName ); if ( !!ld->state->curFile ) { if ( !wcscmp( data->cFileName, ld->state->curFile ) ) { ld->selIndex = ld->nItems; } } ++ld->nItems; XP_Bool cont = ld->nItems < VSIZE(ld->state->names); return cont; } /* Iterate through .dll files listing the name of any that has one. Pair with * file from which it came since that's what we'll return. */ static void listDlls( DllSelState* state ) { HWND hDlg = state->dlgHdr.hDlg; CEAppGlobals* globals = state->dlgHdr.globals; wchar_t name[64]; ListingData ld = { .globals = globals, .state = state, .nItems = 0, .selIndex = 0 }; LoadString( globals->hInst, IDS_LANGUAGE_NAME, name, VSIZE(name) ); copyWideStr( globals, &state->names[ld.nItems++], name ); (void)SendDlgItemMessage( hDlg, state->dllListID, ADDSTRING(globals), 0, (LPARAM)name ); for_each_dll( addToListCB, &ld ); SendDlgItemMessage( hDlg, state->dllListID, SETCURSEL(globals), ld.selIndex, 0L ); state->nItems = ld.nItems; state->initialSel = ld.selIndex; } /* listDlls */ static void unlistDlls( DllSelState* state ) { XP_U16 ii; #ifdef DEBUG CEAppGlobals* globals = state->dlgHdr.globals; #endif for ( ii = 0; ii < state->nItems; ++ii ) { XP_ASSERT( ii == 0 || !!state->files[ii] ); if ( ii > 0 ) { XP_FREE( globals->mpool, state->files[ii] ); } XP_FREE( globals->mpool, state->names[ii] ); } } static XP_Bool getSelText( DllSelState* state ) { XP_Bool gotIt = XP_FALSE; HWND hDlg = state->dlgHdr.hDlg; CEAppGlobals* globals = state->dlgHdr.globals; XP_S16 sel = SendDlgItemMessage( hDlg, state->dllListID, GETCURSEL(globals), 0, 0 ); if ( sel >= 0 && sel != state->initialSel ) { gotIt = XP_TRUE; if ( sel > 0 ) { wcscpy( state->wbuf, state->files[sel] ); } } return gotIt; } /* getSelText */ LRESULT CALLBACK DllSelDlg( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { DllSelState* state; BOOL result = FALSE; if ( message == WM_INITDIALOG ) { SetWindowLongPtr( hDlg, GWL_USERDATA, lParam ); state = (DllSelState*)lParam; state->cancelled = XP_TRUE; state->dllListID = LB_IF_PPC( state->dlgHdr.globals, LOCALES_COMBO ); ceDlgSetup( &state->dlgHdr, hDlg, DLG_STATE_NONE ); ceDlgComboShowHide( &state->dlgHdr, LOCALES_COMBO ); result = TRUE; } else { state = (DllSelState*)GetWindowLongPtr( hDlg, GWL_USERDATA ); if ( !!state ) { if ( !state->inited ) { state->inited = XP_TRUE; listDlls( state ); } if ( ceDoDlgHandle( &state->dlgHdr, message, wParam, lParam) ) { result = TRUE; } else if ( (WM_COMMAND == message) && (BN_CLICKED == HIWORD(wParam)) ) { switch( LOWORD(wParam) ) { case IDOK: state->cancelled = !getSelText( state ); /* fallthrough */ case IDCANCEL: unlistDlls( state ); EndDialog( hDlg, LOWORD(wParam) ); result = TRUE; break; } } } } return result; } /* DllSelDlg */ typedef struct _FindOneData { XP_UCHAR* buf; XP_U16 bufLen; XP_Bool called; } FindOneData; static XP_Bool findFirstCB( const WIN32_FIND_DATA* data, const wchar_t* XP_UNUSED(name), void* closure ) { FindOneData* fd = (FindOneData*)closure; (void)WideCharToMultiByte( CP_ACP, 0, data->cFileName, -1, fd->buf, fd->bufLen, NULL, NULL ); fd->called = XP_TRUE; return XP_FALSE; /* don't continue */ } /* Pick the first .dll found, if any */ XP_Bool ceGetOneResFile( XP_UCHAR* buf, XP_U16 bufLen ) { FindOneData fod = { .buf = buf, .bufLen = bufLen, .called = XP_FALSE, }; for_each_dll( findFirstCB, &fod ); return fod.called; } /* ceChooseResFile: List all the available .rc files and return if user * chooses one different from the one passed in. */ XP_Bool ceChooseResFile( HWND hwnd, CEAppGlobals* globals, const XP_UCHAR* curFileName, XP_UCHAR* buf, XP_U16 bufLen ) { DllSelState state; wchar_t wCurFile[MAX_PATH]; XP_MEMSET( &state, 0, sizeof(state) ); state.dlgHdr.globals = globals; if ( !!curFileName ) { (void)MultiByteToWideChar( CP_ACP, 0, curFileName, -1, wCurFile, VSIZE(wCurFile) ); state.curFile = wCurFile; } (void)DialogBoxParam( globals->locInst, (LPCTSTR)IDD_LOCALESDLG, hwnd, (DLGPROC)DllSelDlg, (long)&state ); if ( !state.cancelled ) { (void)WideCharToMultiByte( CP_ACP, 0, state.wbuf, -1, buf, bufLen, NULL, NULL ); } LOG_RETURNF( "%s", buf ); return !state.cancelled; } /* ceChooseResFile */