/* -*-mode: C; compile-command: "cd ../; ../scripts/ndkbuild.sh -j3"; -*- */ /* * Copyright © 2009 - 2011 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 #include #include #include #include "comtypes.h" #include "game.h" #include "board.h" #include "mempool.h" #include "strutils.h" #include "dictnry.h" #include "dictiter.h" #include "utilwrapper.h" #include "drawwrapper.h" #include "xportwrapper.h" #include "anddict.h" #include "andutils.h" #include "jniutlswrapper.h" #include "paths.h" static CurGameInfo* makeGI( MPFORMAL JNIEnv* env, jobject j_gi ) { CurGameInfo* gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*gi) ); XP_UCHAR buf[256]; /* in case needs whole path */ gi->nPlayers = getInt( env, j_gi, "nPlayers"); gi->gameSeconds = getInt( env, j_gi, "gameSeconds"); gi->boardSize = getInt( env, j_gi, "boardSize" ); /* Unlike on other platforms, gi is created without a call to game_makeNewGame, which sets gameID. So check here if it's still unset and if necessary set it -- including back in the java world. */ gi->gameID = getInt( env, j_gi, "gameID" ); if ( 0 == gi->gameID ) { while ( 0 == gi->gameID ) { gi->gameID = and_util_getCurSeconds( NULL ); } setInt( env, j_gi, "gameID", gi->gameID ); } gi->dictLang = getInt( env, j_gi, "dictLang" ); gi->hintsNotAllowed = getBool( env, j_gi, "hintsNotAllowed" ); gi->timerEnabled = getBool( env, j_gi, "timerEnabled" ); gi->allowPickTiles = getBool( env, j_gi, "allowPickTiles" ); gi->allowHintRect = getBool( env, j_gi, "allowHintRect" ); gi->phoniesAction = jenumFieldToInt( env, j_gi, "phoniesAction", PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") ); gi->serverRole = jenumFieldToInt( env, j_gi, "serverRole", PKG_PATH("jni/CurGameInfo$DeviceRole")); getString( env, j_gi, "dictName", buf, VSIZE(buf) ); gi->dictName = copyString( mpool, buf ); XP_ASSERT( gi->nPlayers <= MAX_NUM_PLAYERS ); jobject jplayers; if ( getObject( env, j_gi, "players", "[L" PKG_PATH("jni/LocalPlayer") ";", &jplayers ) ) { int ii; for ( ii = 0; ii < gi->nPlayers; ++ii ) { LocalPlayer* lp = &gi->players[ii]; jobject jlp = (*env)->GetObjectArrayElement( env, jplayers, ii ); XP_ASSERT( !!jlp ); lp->robotIQ = getInt( env, jlp, "robotIQ" ); lp->isLocal = getBool( env, jlp, "isLocal" ); getString( env, jlp, "name", buf, VSIZE(buf) ); lp->name = copyString( mpool, buf ); getString( env, jlp, "password", buf, VSIZE(buf) ); lp->password = copyString( mpool, buf ); getString( env, jlp, "dictName", buf, VSIZE(buf) ); lp->dictName = copyString( mpool, buf ); lp->secondsUsed = 0; (*env)->DeleteLocalRef( env, jlp ); } (*env)->DeleteLocalRef( env, jplayers ); } else { XP_ASSERT(0); } return gi; } /* makeGI */ static void setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi ) { // set fields setInt( env, jgi, "nPlayers", gi->nPlayers ); setInt( env, jgi, "gameSeconds", gi->gameSeconds ); setInt( env, jgi, "boardSize", gi->boardSize ); setInt( env, jgi, "gameID", gi->gameID ); setInt( env, jgi, "dictLang", gi->dictLang ); setBool( env, jgi, "hintsNotAllowed", gi->hintsNotAllowed ); setBool( env, jgi, "timerEnabled", gi->timerEnabled ); setBool( env, jgi, "allowPickTiles", gi->allowPickTiles ); setString( env, jgi, "dictName", gi->dictName ); intToJenumField( env, jgi, gi->phoniesAction, "phoniesAction", PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") ); intToJenumField( env, jgi, gi->serverRole, "serverRole", PKG_PATH("jni/CurGameInfo$DeviceRole") ); jobject jplayers; if ( getObject( env, jgi, "players", "[L" PKG_PATH("jni/LocalPlayer") ";", &jplayers ) ) { int ii; for ( ii = 0; ii < gi->nPlayers; ++ii ) { const LocalPlayer* lp = &gi->players[ii]; jobject jlp = (*env)->GetObjectArrayElement( env, jplayers, ii ); XP_ASSERT( !!jlp ); setInt( env, jlp, "robotIQ", lp->robotIQ ); setBool( env, jlp, "isLocal", lp->isLocal ); setString( env, jlp, "name", lp->name ); setString( env, jlp, "password", lp->password ); setString( env, jlp, "dictName", lp->dictName ); setInt( env, jlp, "secondsUsed", lp->secondsUsed ); (*env)->DeleteLocalRef( env, jlp ); } (*env)->DeleteLocalRef( env, jplayers ); } else { XP_ASSERT(0); } } /* setJGI */ static void destroyGI( MPFORMAL CurGameInfo** gip ) { CurGameInfo* gi = *gip; if ( !!gi ) { gi_disposePlayerInfo( MPPARM(mpool) gi ); XP_FREE( mpool, gi ); *gip = NULL; } } static void loadCommonPrefs( JNIEnv* env, CommonPrefs* cp, jobject j_cp ) { XP_ASSERT( !!j_cp ); cp->showBoardArrow = getBool( env, j_cp, "showBoardArrow" ); cp->showRobotScores = getBool( env, j_cp, "showRobotScores" ); cp->hideTileValues = getBool( env, j_cp, "hideTileValues" ); cp->skipCommitConfirm = getBool( env, j_cp, "skipCommitConfirm" ); cp->showColors = getBool( env, j_cp, "showColors" ); cp->sortNewTiles = getBool( env, j_cp, "sortNewTiles" ); cp->allowPeek = getBool( env, j_cp, "allowPeek" ); #ifdef XWFEATURE_CROSSHAIRS cp->hideCrosshairs = getBool( env, j_cp, "hideCrosshairs" ); #endif } static XWStreamCtxt* streamFromJStream( MPFORMAL JNIEnv* env, VTableMgr* vtMgr, jbyteArray jstream ) { XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) vtMgr, NULL, 0, NULL ); int len = (*env)->GetArrayLength( env, jstream ); jbyte* jelems = (*env)->GetByteArrayElements( env, jstream, NULL ); stream_putBytes( stream, jelems, len ); (*env)->ReleaseByteArrayElements( env, jstream, jelems, 0 ); return stream; } /* streamFromJStream */ /**************************************************** * These three methods are stateless: no gamePtr ****************************************************/ JNIEXPORT jbyteArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_gi_1to_1stream (JNIEnv* env, jclass C, jobject jgi ) { LOG_FUNC(); jbyteArray result; #ifdef MEM_DEBUG MemPoolCtx* mpool = mpool_make(); #endif CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi ); VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) ); XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) vtMgr, NULL, 0, NULL ); game_saveToStream( NULL, gi, stream ); destroyGI( MPPARM(mpool) &gi ); int nBytes = stream_getSize( stream ); result = (*env)->NewByteArray( env, nBytes ); jbyte* jelems = (*env)->GetByteArrayElements( env, result, NULL ); stream_getBytes( stream, jelems, nBytes ); (*env)->ReleaseByteArrayElements( env, result, jelems, 0 ); stream_destroy( stream ); vtmgr_destroy( MPPARM(mpool) vtMgr ); #ifdef MEM_DEBUG mpool_destroy( mpool ); #endif LOG_RETURN_VOID(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_gi_1from_1stream ( JNIEnv* env, jclass C, jobject jgi, jbyteArray jstream ) { #ifdef MEM_DEBUG MemPoolCtx* mpool = mpool_make(); #endif VTableMgr* vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) ); XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, vtMgr, jstream ); CurGameInfo gi; XP_MEMSET( &gi, 0, sizeof(gi) ); if ( game_makeFromStream( MPPARM(mpool) stream, NULL, &gi, NULL, NULL, NULL, NULL, NULL, NULL ) ) { setJGI( env, jgi, &gi ); } else { XP_LOGF( "%s: game_makeFromStream failed", __func__ ); } gi_disposePlayerInfo( MPPARM(mpool) &gi ); stream_destroy( stream ); vtmgr_destroy( MPPARM(mpool) vtMgr ); #ifdef MEM_DEBUG mpool_destroy( mpool ); #endif } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1getInitialAddr ( JNIEnv* env, jclass C, jobject jaddr, jstring jname, jint port ) { CommsAddrRec addr; const char* chars = (*env)->GetStringUTFChars( env, jname, NULL ); comms_getInitialAddr( &addr, chars, port ); (*env)->ReleaseStringUTFChars( env, jname, chars ); setJAddrRec( env, jaddr, &addr ); } JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1getUUID ( JNIEnv* env, jclass C ) { jstring jstr = NULL; #ifdef XWFEATURE_BLUETOOTH const char* uuid = XW_BT_UUID; XP_LOGF( "uuid: %s", uuid ); jstr = (*env)->NewStringUTF( env, uuid ); // (*env)->DeleteLocalRef( env, jstr ); #endif return jstr; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo ( JNIEnv* env, jclass C, jbyteArray jDictBytes, jstring jpath, jobject jniu, jboolean check, jobject jinfo ) { jboolean result = false; #ifdef MEM_DEBUG MemPoolCtx* mpool = mpool_make(); #endif JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(mpool) &env, jniu ); DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jniutil, NULL, jDictBytes, jpath, NULL, check ); if ( NULL != dict ) { if ( NULL != jinfo ) { setInt( env, jinfo, "langCode", dict_getLangCode( dict ) ); setInt( env, jinfo, "wordCount", dict_getWordCount( dict ) ); } dict_destroy( dict ); result = true; } destroyJNIUtil( &jniutil ); #ifdef MEM_DEBUG mpool_destroy( mpool ); #endif return result; } /* Dictionary methods: don't use gamePtr */ JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1tilesAreSame ( JNIEnv* env, jclass C, jint dictPtr1, jint dictPtr2 ) { jboolean result; const DictionaryCtxt* dict1 = (DictionaryCtxt*)dictPtr1; const DictionaryCtxt* dict2 = (DictionaryCtxt*)dictPtr2; result = dict_tilesAreSame( dict1, dict2 ); return result; } JNIEXPORT jobjectArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getChars ( JNIEnv* env, jclass C, jint dictPtr ) { jobject result = NULL; result = and_dictionary_getChars( env, (DictionaryCtxt*)dictPtr ); // (*env)->DeleteLocalRef( env, result ); return result; } JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getTileValue ( JNIEnv* env, jclass C, jint dictPtr, jint tile ) { return dict_getTileValue( (DictionaryCtxt*)dictPtr, tile ); } typedef struct _JNIState { XWGame game; JNIEnv* env; AndGlobals globals; #ifdef DEBUG const char* envSetterFunc; #endif MPSLOT } JNIState; #ifdef DEBUG # define CHECK_ENV() \ if ( state->env != 0 && state->env != env ) { \ XP_LOGF( "ERROR: %s trying to set env when %s still has it", \ __func__, state->envSetterFunc ); \ XP_ASSERT( state->env == 0 || state->env == env ); \ } \ state->envSetterFunc = __func__; \ #else # define CHECK_ENV() #endif #define XWJNI_START() { \ XP_ASSERT( 0 != gamePtr ); \ JNIState* state = (JNIState*)gamePtr; \ MPSLOT; \ MPASSIGN( mpool, state->mpool); \ /* if reentrant must be from same thread */ \ CHECK_ENV(); \ JNIEnv* _oldEnv = state->env; \ state->env = env; #define XWJNI_START_GLOBALS() \ XWJNI_START() \ AndGlobals* globals = &state->globals; \ #define XWJNI_END() \ state->env = _oldEnv; \ } JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_initJNI ( JNIEnv* env, jclass C ) { struct timeval tv; gettimeofday( &tv, NULL ); srandom( tv.tv_sec ); #ifdef MEM_DEBUG MemPoolCtx* mpool = mpool_make(); #endif JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) ); AndGlobals* globals = &state->globals; globals->state = (struct JNIState*)state; MPASSIGN( state->mpool, mpool ); globals->vtMgr = make_vtablemgr(MPPARM_NOCOMMA(mpool)); XP_U32 secs = and_util_getCurSeconds( NULL ); XP_LOGF( "initing srand with %ld", secs ); srandom( secs ); return (jint) state; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame ( JNIEnv* env, jclass C, jint gamePtr, jobject j_gi, jobject j_util, jobject jniu, jobject j_draw, jobject j_cp, jobject j_procs, jobjectArray j_names, jobjectArray j_dicts, jobjectArray j_paths, jstring j_lang ) { XWJNI_START_GLOBALS(); CurGameInfo* gi = makeGI( MPPARM(mpool) env, j_gi ); globals->gi = gi; globals->util = makeUtil( MPPARM(mpool) &state->env, j_util, gi, globals ); globals->jniutil = makeJNIUtil( MPPARM(mpool) &state->env, jniu ); DrawCtx* dctx = makeDraw( MPPARM(mpool) &state->env, j_draw ); globals->dctx = dctx; globals->xportProcs = makeXportProcs( MPPARM(mpool) &state->env, j_procs ); CommonPrefs cp; loadCommonPrefs( env, &cp, j_cp ); XP_LOGF( "calling game_makeNewGame" ); game_makeNewGame( MPPARM(mpool) &state->game, gi, globals->util, dctx, &cp, globals->xportProcs ); DictionaryCtxt* dict; PlayerDicts dicts; makeDicts( MPPARM(mpool) env, globals->jniutil, &dict, &dicts, j_names, j_dicts, j_paths, j_lang ); #ifdef STUBBED_DICT if ( !dict ) { XP_LOGF( "falling back to stubbed dict" ); dict = make_stubbed_dict( MPPARM_NOCOMMA(mpool) ); } #endif model_setDictionary( state->game.model, dict ); model_setPlayerDicts( state->game.model, &dicts ); XWJNI_END(); } /* makeNewGame */ JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose ( JNIEnv * env, jclass claz, jint gamePtr ) { JNIState* state = (JNIState*)gamePtr; #ifdef MEM_DEBUG MemPoolCtx* mpool = state->mpool; #endif AndGlobals* globals = &state->globals; destroyGI( MPPARM(mpool) &globals->gi ); JNIEnv* oldEnv = state->env; state->env = env; game_dispose( &state->game ); destroyDraw( &globals->dctx ); destroyXportProcs( &globals->xportProcs ); destroyUtil( &globals->util ); destroyJNIUtil( &globals->jniutil ); vtmgr_destroy( MPPARM(mpool) globals->vtMgr ); state->env = oldEnv; XP_FREE( mpool, state ); mpool_destroy( mpool ); } /* game_dispose */ JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream ( JNIEnv* env, jclass C, jint gamePtr, jbyteArray jstream, jobject /*out*/jgi, jobjectArray jdictNames, jobjectArray jdicts, jobjectArray jpaths, jstring jlang, jobject jutil, jobject jniu, jobject jdraw, jobject jcp, jobject jprocs ) { jboolean result; DictionaryCtxt* dict; PlayerDicts dicts; XWJNI_START_GLOBALS(); globals->gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*globals->gi) ); globals->util = makeUtil( MPPARM(mpool) &state->env, jutil, globals->gi, globals ); globals->jniutil = makeJNIUtil( MPPARM(mpool) &state->env, jniu ); makeDicts( MPPARM(mpool) env, globals->jniutil, &dict, &dicts, jdictNames, jdicts, jpaths, jlang ); globals->dctx = makeDraw( MPPARM(mpool) &state->env, jdraw ); globals->xportProcs = makeXportProcs( MPPARM(mpool) &state->env, jprocs ); XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, globals->vtMgr, jstream ); CommonPrefs cp; loadCommonPrefs( env, &cp, jcp ); result = game_makeFromStream( MPPARM(mpool) stream, &state->game, globals->gi, dict, &dicts, globals->util, globals->dctx, &cp, globals->xportProcs ); stream_destroy( stream ); if ( result ) { XP_ASSERT( 0 != globals->gi->gameID ); if ( !!jgi ) { setJGI( env, jgi, globals->gi ); } } else { destroyDraw( &globals->dctx ); destroyXportProcs( &globals->xportProcs ); destroyDicts( &dicts ); if ( NULL != dict ) { dict_destroy( dict ); } destroyUtil( &globals->util ); destroyJNIUtil( &globals->jniutil ); destroyGI( MPPARM(mpool) &globals->gi ); } XWJNI_END(); return result; } /* makeFromStream */ JNIEXPORT jbyteArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1saveToStream ( JNIEnv* env, jclass C, jint gamePtr, jobject jgi ) { jbyteArray result; XWJNI_START_GLOBALS(); /* Use our copy of gi if none's provided. That's because only the caller knows if its gi should win -- user has changed game config -- or if ours should -- changes like remote players being added. */ CurGameInfo* gi = (NULL == jgi) ? globals->gi : makeGI( MPPARM(mpool) env, jgi ); XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr, NULL, 0, NULL ); game_saveToStream( &state->game, gi, stream ); if ( NULL != jgi ) { destroyGI( MPPARM(mpool) &gi ); } int nBytes = stream_getSize( stream ); result = (*env)->NewByteArray( env, nBytes ); jbyte* jelems = (*env)->GetByteArrayElements( env, result, NULL ); stream_getBytes( stream, jelems, nBytes ); (*env)->ReleaseByteArrayElements( env, result, jelems, 0 ); stream_destroy( stream ); // (*env)->DeleteLocalRef( env, result ); XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1invalAll ( JNIEnv *env, jclass C, jint gamePtr ) { XWJNI_START(); board_invalAll( state->game.board ); XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1draw ( JNIEnv *env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = board_draw( state->game.board ); XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1setPos (JNIEnv *env, jclass C, jint gamePtr, jint left, jint top, jint width, jint height, jint maxCellSize, jboolean lefty ) { XWJNI_START(); board_setPos( state->game.board, left, top, width, height, maxCellSize, lefty ); XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1zoom ( JNIEnv* env, jclass C, jint gamePtr, jint zoomBy, jbooleanArray jCanZoom ) { jboolean result; XWJNI_START(); XP_Bool canInOut[2]; result = board_zoom( state->game.board, zoomBy, canInOut ); jboolean canZoom[2] = { canInOut[0], canInOut[1] }; setBoolArray( env, jCanZoom, VSIZE(canZoom), canZoom ); XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1setScoreboardLoc ( JNIEnv *env, jclass C, jint gamePtr, jint left, jint top, jint width, jint height, jboolean divideHorizontally ) { XWJNI_START(); board_setScoreboardLoc( state->game.board, left, top, width, height, divideHorizontally ); XWJNI_END(); } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1setTimerLoc ( JNIEnv* env, jclass C, jint gamePtr, jint timerLeft, jint timerTop, jint timerWidth, jint timerHeight ) { XWJNI_START(); XP_LOGF( "%s(%d,%d,%d,%d)", __func__, timerLeft, timerTop, timerWidth, timerHeight ); board_setTimerLoc( state->game.board, timerLeft, timerTop, timerWidth, timerHeight ); XWJNI_END(); } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1setTrayLoc ( JNIEnv *env, jclass C, jint gamePtr, jint left, jint top, jint width, jint height, jint minDividerWidth ) { XWJNI_START(); board_setTrayLoc( state->game.board, left, top, width, height, minDividerWidth ); XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1handlePenDown (JNIEnv *env, jclass C, jint gamePtr, jint xx, jint yy, jbooleanArray barray ) { jboolean result; XWJNI_START(); XP_Bool bb; /* drop this for now */ result = board_handlePenDown( state->game.board, xx, yy, &bb ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1handlePenMove ( JNIEnv *env, jclass C, jint gamePtr, jint xx, jint yy ) { jboolean result; XWJNI_START(); result = board_handlePenMove( state->game.board, xx, yy ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1handlePenUp ( JNIEnv *env, jclass C, jint gamePtr, jint xx, jint yy ) { jboolean result; XWJNI_START(); result = board_handlePenUp( state->game.board, xx, yy ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1juggleTray (JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = board_juggleTray( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1getTrayVisState (JNIEnv* env, jclass C, jint gamePtr) { jboolean result; XWJNI_START(); result = board_getTrayVisState( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1hideTray (JNIEnv* env, jclass C, jint gamePtr) { jboolean result; XWJNI_START(); result = board_hideTray( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1showTray (JNIEnv* env, jclass C, jint gamePtr) { jboolean result; XWJNI_START(); result = board_showTray( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1beginTrade ( JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = board_beginTrade( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1endTrade ( JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = board_endTrade( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1toggle_1showValues ( JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = board_toggle_showValues( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1commitTurn (JNIEnv* env, jclass C, jint gamePtr) { jboolean result; XWJNI_START(); result = board_commitTurn( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1flip (JNIEnv* env, jclass C, jint gamePtr) { jboolean result; XWJNI_START(); result = board_flip( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1replaceTiles (JNIEnv* env, jclass C, jint gamePtr) { jboolean result; XWJNI_START(); result = board_replaceTiles( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1redoReplacedTiles ( JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = board_redoReplacedTiles( state->game.board ); XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1reset (JNIEnv* env, jclass C, jint gamePtr ) { XWJNI_START(); server_reset( state->game.server, state->game.comms ); XWJNI_END(); } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1handleUndo (JNIEnv* env, jclass C, jint gamePtr) { XWJNI_START(); server_handleUndo( state->game.server ); XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1do (JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = server_do( state->game.server ); XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1resetEngine (JNIEnv* env, jclass C, jint gamePtr ) { XWJNI_START(); board_resetEngine( state->game.board ); XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1requestHint ( JNIEnv* env, jclass C, jint gamePtr, jboolean useLimits, jboolean goBack, jbooleanArray workRemains ) { jboolean result; XWJNI_START(); XP_Bool tmpbool; result = board_requestHint( state->game.board, #ifdef XWFEATURE_SEARCHLIMIT useLimits, #endif goBack, &tmpbool ); /* If passed need to do workRemains[0] = tmpbool */ if ( workRemains ) { jboolean jbool = tmpbool; setBoolArray( env, workRemains, 1, &jbool ); } XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_timerFired ( JNIEnv* env, jclass C, jint gamePtr, jint why, jint when, jint handle ) { jboolean result; XWJNI_START_GLOBALS(); XW_UtilCtxt* util = globals->util; result = utilTimerFired( util, why, handle ); XWJNI_END(); return result; } JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1formatRemainingTiles (JNIEnv* env, jclass C, jint gamePtr ) { jstring result; XWJNI_START_GLOBALS(); XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr, NULL, 0, NULL ); board_formatRemainingTiles( state->game.board, stream ); result = streamToJString( env, stream ); stream_destroy( stream ); // (*env)->DeleteLocalRef( env, result ); XWJNI_END(); return result; } JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1formatDictCounts ( JNIEnv* env, jclass C, jint gamePtr, jint nCols ) { jstring result; XWJNI_START_GLOBALS(); XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals ); server_formatDictCounts( state->game.server, stream, nCols ); result = streamToJString( env, stream ); stream_destroy( stream ); // (*env)->DeleteLocalRef( env, result ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1getGameIsOver ( JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = server_getGameIsOver( state->game.server ); XWJNI_END(); return result; } JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_model_1writeGameHistory ( JNIEnv* env, jclass C, jint gamePtr, jboolean gameOver ) { jstring result; XWJNI_START_GLOBALS(); XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals ); model_writeGameHistory( state->game.model, stream, state->game.server, gameOver ); result = streamToJString( env, stream ); // (*env)->DeleteLocalRef( env, result ); stream_destroy( stream ); XWJNI_END(); return result; } JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_model_1getNMoves ( JNIEnv* env, jclass C, jint gamePtr ) { jint result; XWJNI_START(); XP_ASSERT( !!state->game.model ); result = model_getNMoves( state->game.model ); XWJNI_END(); return result; } JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_model_1getPlayersLastScore (JNIEnv* env, jclass C, jint gamePtr, jint player ) { jstring result = NULL; XWJNI_START(); XP_ASSERT( !!state->game.model ); XP_UCHAR buf[64]; XP_U16 buflen = sizeof(buf); if ( !model_getPlayersLastScore( state->game.model, player, buf, &buflen ) ) { buf[0] = '\0'; } result = (*env)->NewStringUTF( env, buf ); // (*env)->DeleteLocalRef( env, result ); XWJNI_END(); return result; } JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1writeFinalScores ( JNIEnv* env, jclass C, jint gamePtr ) { jstring result; XWJNI_START_GLOBALS(); XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals ); server_writeFinalScores( state->game.server, stream ); result = streamToJString( env, stream ); // (*env)->DeleteLocalRef( env, result ); stream_destroy( stream ); XWJNI_END(); return result; } void and_send_on_close( XWStreamCtxt* stream, void* closure ) { AndGlobals* globals = (AndGlobals*)closure; JNIState* state = (JNIState*)globals->state; XP_ASSERT( !!state->game.comms ); if ( stream_getSize( stream ) > 0 ) { comms_send( state->game.comms, stream ); } } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1initClientConnection ( JNIEnv* env, jclass C, jint gamePtr ) { LOG_FUNC(); XWJNI_START_GLOBALS(); XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals ); stream_setOnCloseProc( stream, and_send_on_close ); server_initClientConnection( state->game.server, stream ); XWJNI_END(); LOG_RETURN_VOID(); } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1start ( JNIEnv* env, jclass C, jint gamePtr ) { XWJNI_START(); if ( !!state->game.comms ) { comms_start( state->game.comms ); } XWJNI_END(); } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1resetSame ( JNIEnv* env, jclass C, jint gamePtr ) { XWJNI_START(); if ( !!state->game.comms ) { comms_resetSame( state->game.comms ); } XWJNI_END(); } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1getAddr (JNIEnv* env, jclass C, jint gamePtr, jobject jaddr ) { XWJNI_START(); XP_ASSERT( state->game.comms ); CommsAddrRec addr; comms_getAddr( state->game.comms, &addr ); setJAddrRec( env, jaddr, &addr ); XWJNI_END(); } JNIEXPORT jobjectArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1getAddrs ( JNIEnv* env, jclass C, jint gamePtr ) { jobjectArray result = NULL; XP_U16 ii; XWJNI_START(); XP_ASSERT( state->game.comms ); CommsAddrRec addrs[MAX_NUM_PLAYERS]; XP_U16 count = VSIZE(addrs); comms_getAddrs( state->game.comms, addrs, &count ); jclass clas = (*env)->FindClass( env, PKG_PATH("jni/CommsAddrRec") ); result = (*env)->NewObjectArray( env, count, clas, NULL ); jmethodID initId = (*env)->GetMethodID( env, clas, "", "()V" ); for ( ii = 0; ii < count; ++ii ) { jobject jaddr = (*env)->NewObject( env, clas, initId ); setJAddrRec( env, jaddr, &addrs[ii] ); (*env)->SetObjectArrayElement( env, result, ii, jaddr ); (*env)->DeleteLocalRef( env, jaddr ); } (*env)->DeleteLocalRef( env, clas ); XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1setAddr ( JNIEnv* env, jclass C, jint gamePtr, jobject jaddr ) { XWJNI_START(); if ( state->game.comms ) { CommsAddrRec addr; getJAddrRec( env, &addr, jaddr ); comms_setAddr( state->game.comms, &addr ); } else { XP_LOGF( "%s: no comms this game", __func__ ); } XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1receiveMessage ( JNIEnv* env, jclass C, jint gamePtr, jbyteArray jstream, jobject jaddr ) { jboolean result; XWJNI_START_GLOBALS(); XP_ASSERT( state->game.comms ); XP_ASSERT( state->game.server ); XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, globals->vtMgr, jstream ); CommsAddrRec* addrp = NULL; CommsAddrRec addr; if ( NULL != jaddr ) { getJAddrRec( env, &addr, jaddr ); addrp = &addr; } result = comms_checkIncomingStream( state->game.comms, stream, addrp ); if ( result ) { ServerCtxt* server = state->game.server; (void)server_do( server ); (void)server_receiveMessage( server, stream ); /* in case MORE work's pending. Multiple calls are required in at least one case, where I'm a host handling client registration *AND* I'm a robot. Only one server_do and I'll never make that first robot move. That's because comms can't detect a duplicate initial packet (in validateInitialMessage()). */ int ii; for ( ii = 0; ii < 5; ++ii ) { (void)server_do( server ); } } stream_destroy( stream ); XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1summarize ( JNIEnv* env, jclass C, jint gamePtr, jobject jsummary ) { XWJNI_START(); ModelCtxt* model = state->game.model; XP_S16 nMoves = model_getNMoves( model ); setInt( env, jsummary, "nMoves", nMoves ); XP_Bool gameOver = server_getGameIsOver( state->game.server ); setBool( env, jsummary, "gameOver", gameOver ); setInt( env, jsummary, "turn", server_getCurrentTurn( state->game.server ) ); if ( !!state->game.comms ) { CommsAddrRec addr; CommsCtxt* comms = state->game.comms; comms_getAddr( comms, &addr ); intToJenumField( env, jsummary, addr.conType, "conType", PKG_PATH("jni/CommsAddrRec$CommsConnType") ); setInt( env, jsummary, "seed", comms_getChannelSeed( comms ) ); setInt( env, jsummary, "missingPlayers", server_getMissingPlayers( state->game.server ) ); if ( COMMS_CONN_RELAY == addr.conType ) { XP_UCHAR buf[128]; XP_U16 len = VSIZE(buf); if ( comms_getRelayID( comms, buf, &len ) ) { XP_ASSERT( '\0' == buf[len-1] ); /* failed! */ setString( env, jsummary, "relayID", buf ); } setString( env, jsummary, "roomName", addr.u.ip_relay.invite ); #if defined XWFEATURE_BLUETOOTH || defined XWFEATURE_SMS } else if ( COMMS_CONN_BT == addr.conType || COMMS_CONN_SMS == addr.conType ) { XP_Bool isBT = COMMS_CONN_BT == addr.conType; CommsAddrRec addrs[MAX_NUM_PLAYERS]; XP_U16 count = VSIZE(addrs); comms_getAddrs( comms, addrs, &count ); int ii; const XP_UCHAR* addrps[count]; for ( ii = 0; ii < count; ++ii ) { addrps[ii] = isBT ? (XP_UCHAR*)&addrs[ii].u.bt.btAddr : (XP_UCHAR*)&addrs[ii].u.sms.phone; XP_LOGF( "%s: adding btaddr/phone %s", __func__, addrps[ii] ); } jobjectArray jaddrs = makeStringArray( env, count, addrps ); setObject( env, jsummary, "remoteDevs", "[Ljava/lang/String;", jaddrs ); (*env)->DeleteLocalRef( env, jaddrs ); #endif } } XP_U16 nPlayers = model_getNPlayers( model ); jint jvals[nPlayers]; int ii; if ( gameOver ) { ScoresArray scores; model_figureFinalScores( model, &scores, NULL ); for ( ii = 0; ii < nPlayers; ++ii ) { jvals[ii] = scores.arr[ii]; } } else { for ( ii = 0; ii < nPlayers; ++ii ) { jvals[ii] = model_getPlayerScore( model, ii ); } } jintArray jarr = makeIntArray( env, nPlayers, jvals ); setObject( env, jsummary, "scores", "[I", jarr ); (*env)->DeleteLocalRef( env, jarr ); XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1server_1prefsChanged ( JNIEnv* env, jclass C, jint gamePtr, jobject jcp ) { jboolean result; XWJNI_START(); CommonPrefs cp; loadCommonPrefs( env, &cp, jcp ); result = board_prefsChanged( state->game.board, &cp ); server_prefsChanged( state->game.server, &cp ); XWJNI_END(); return result; } #ifdef KEYBOARD_NAV JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1getFocusOwner ( JNIEnv* env, jclass C, jint gamePtr ) { jint result; XWJNI_START(); result = board_getFocusOwner( state->game.board ); XWJNI_END(); return result; } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1focusChanged ( JNIEnv* env, jclass C, jint gamePtr, jint typ ) { jboolean result; XWJNI_START(); result = board_focusChanged( state->game.board, typ, XP_TRUE ); XWJNI_END(); return result; } #endif #ifdef KEYBOARD_NAV JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_board_1handleKey ( JNIEnv* env, jclass C, jint gamePtr, jobject jkey, jboolean jup, jbooleanArray jhandled ) { jboolean result; XWJNI_START(); XP_Bool tmpbool; XP_Key key = jEnumToInt( env, jkey ); if ( jup ) { result = board_handleKeyUp( state->game.board, key, &tmpbool ); } else { result = board_handleKeyDown( state->game.board, key, &tmpbool ); } jboolean jbool = tmpbool; setBoolArray( env, jhandled, 1, &jbool ); XWJNI_END(); return result; } #endif JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1getGi ( JNIEnv* env, jclass C, jint gamePtr, jobject jgi ) { XWJNI_START_GLOBALS(); setJGI( env, jgi, globals->gi ); XWJNI_END(); } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1getState ( JNIEnv* env, jclass C, jint gamePtr, jobject jgsi ) { XWJNI_START(); GameStateInfo info; game_getState( &state->game, &info ); setInt( env, jgsi, "visTileCount", info.visTileCount ); setBool( env, jgsi, "canHint", info.canHint ); setBool( env, jgsi, "canRedo", info.canRedo); setBool( env, jgsi, "inTrade", info.inTrade ); setBool( env, jgsi, "tradeTilesSelected", info.tradeTilesSelected ); setBool( env, jgsi, "gameIsConnected", info.gameIsConnected ); setBool( env, jgsi, "canShuffle", info.canShuffle ); setBool( env, jgsi, "curTurnSelected", info.curTurnSelected ); XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1hasComms ( JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = NULL != state->game.comms; XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1resendAll ( JNIEnv* env, jclass C, jint gamePtr, jboolean thenAck ) { XWJNI_START(); CommsCtxt* comms = state->game.comms; XP_ASSERT( !!comms ); (void)comms_resendAll( comms ); if ( thenAck ) { comms_ackAny( comms ); } XWJNI_END(); } #ifdef XWFEATURE_COMMSACK JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1ackAny ( JNIEnv* env, jclass C, jint gamePtr ) { XWJNI_START(); XP_ASSERT( !!state->game.comms ); (void)comms_ackAny( state->game.comms ); XWJNI_END(); } #endif JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1transportFailed ( JNIEnv* env, jclass C, jint gamePtr ) { XWJNI_START(); XP_ASSERT( !!state->game.comms ); (void)comms_transportFailed( state->game.comms ); XWJNI_END(); } JNIEXPORT jboolean JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_comms_1isConnected ( JNIEnv* env, jclass C, jint gamePtr ) { jboolean result; XWJNI_START(); result = NULL != state->game.comms && comms_isConnected( state->game.comms ); XWJNI_END(); return result; } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1endGame ( JNIEnv* env, jclass C, jint gamePtr ) { XWJNI_START(); XP_ASSERT( !!state->game.server ); server_endGame( state->game.server ); XWJNI_END(); } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_server_1sendChat ( JNIEnv* env, jclass C, jint gamePtr, jstring jmsg ) { XWJNI_START(); XP_ASSERT( !!state->game.server ); const char* msg = (*env)->GetStringUTFChars( env, jmsg, NULL ); server_sendChat( state->game.server, msg ); (*env)->ReleaseStringUTFChars( env, jmsg, msg ); XWJNI_END(); } #ifdef XWFEATURE_WALKDICT //////////////////////////////////////////////////////////// // Dict iterator //////////////////////////////////////////////////////////// typedef struct _DictIterData { JNIEnv* env; JNIUtilCtxt* jniutil; VTableMgr* vtMgr; DictionaryCtxt* dict; DictIter iter; IndexData idata; XP_U16 depth; #ifdef MEM_DEBUG MemPoolCtx* mpool; #endif } DictIterData; JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1init (JNIEnv* env, jclass C, jbyteArray jDictBytes, jstring jpath, jobject jniu ) { jint closure = 0; #ifdef MEM_DEBUG MemPoolCtx* mpool = mpool_make(); #endif DictIterData* data = XP_CALLOC( mpool, sizeof(*data) ); data->env = env; JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(mpool) &data->env, jniu ); DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jniutil, NULL, jDictBytes, jpath, NULL, false ); if ( !!dict ) { data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) ); data->jniutil = jniutil; data->dict = dict; data->depth = 2; #ifdef MEM_DEBUG data->mpool = mpool; #endif closure = (int)data; } else { destroyJNIUtil( &jniutil ); XP_FREE( mpool, data ); #ifdef MEM_DEBUG mpool_destroy( mpool ); #endif } return closure; } static void freeIndices( DictIterData* data ) { IndexData* idata = &data->idata; if ( !!idata->prefixes ) { XP_FREE( data->mpool, idata->prefixes ); idata->prefixes = NULL; } if( !!idata->indices ) { XP_FREE( data->mpool, idata->indices ); idata->indices = NULL; } } static void makeIndex( DictIterData* data ) { XP_U16 nFaces = dict_numTileFaces( data->dict ); XP_U16 ii; XP_U16 count; for ( count = 1, ii = 0; ii < data->depth; ++ii ) { count *= nFaces; } freeIndices( data ); IndexData* idata = &data->idata; idata->prefixes = XP_MALLOC( data->mpool, count * data->depth * sizeof(*idata->prefixes) ); idata->indices = XP_MALLOC( data->mpool, count * sizeof(*idata->indices) ); idata->count = count; dict_makeIndex( &data->iter, data->depth, idata ); if ( 0 < idata->count ) { idata->prefixes = XP_REALLOC( data->mpool, idata->prefixes, idata->count * data->depth * sizeof(*idata->prefixes) ); idata->indices = XP_REALLOC( data->mpool, idata->indices, idata->count * sizeof(*idata->indices) ); } else { freeIndices( data ); } } /* makeIndex */ JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1setMinMax ( JNIEnv* env, jclass C, jint closure, jint min, jint max ) { DictIterData* data = (DictIterData*)closure; if ( NULL != data ) { dict_initIter( &data->iter, data->dict, min, max ); makeIndex( data ); (void)dict_firstWord( &data->iter ); } } JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1destroy ( JNIEnv* env, jclass C, jint closure ) { DictIterData* data = (DictIterData*)closure; if ( NULL != data ) { #ifdef MEM_DEBUG MemPoolCtx* mpool = data->mpool; #endif dict_destroy( data->dict ); destroyJNIUtil( &data->jniutil ); freeIndices( data ); vtmgr_destroy( MPPARM(mpool) data->vtMgr ); XP_FREE( mpool, data ); #ifdef MEM_DEBUG mpool_destroy( mpool ); #endif } } JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1wordCount (JNIEnv* env, jclass C, jint closure ) { jint result = 0; DictIterData* data = (DictIterData*)closure; if ( NULL != data ) { result = data->iter.nWords; } return result; } JNIEXPORT jintArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getCounts (JNIEnv* env, jclass C, jint closure ) { jintArray result = NULL; DictIterData* data = (DictIterData*)closure; if ( NULL != data ) { DictIter iter; dict_initIter( &iter, data->dict, 0, MAX_COLS_DICT ); LengthsArray lens; if ( 0 < dict_countWords( &iter, &lens ) ) { XP_ASSERT( sizeof(jint) == sizeof(lens.lens[0]) ); result = makeIntArray( env, VSIZE(lens.lens), (jint*)&lens.lens ); // (*env)->DeleteLocalRef( env, result ); } } return result; } JNIEXPORT jobjectArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getPrefixes ( JNIEnv* env, jclass C, jint closure ) { jobjectArray result = NULL; DictIterData* data = (DictIterData*)closure; if ( NULL != data && NULL != data->idata.prefixes ) { result = makeStringArray( env, data->idata.count, NULL ); int ii; XP_U16 depth = data->depth; for ( ii = 0; ii < data->idata.count; ++ii ) { XP_UCHAR buf[16]; (void)dict_tilesToString( data->dict, &data->idata.prefixes[depth*ii], depth, buf, VSIZE(buf) ); jstring jstr = (*env)->NewStringUTF( env, buf ); (*env)->SetObjectArrayElement( env, result, ii, jstr ); (*env)->DeleteLocalRef( env, jstr ); } // (*env)->DeleteLocalRef( env, result ); } return result; } JNIEXPORT jintArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getIndices ( JNIEnv* env, jclass C , jint closure ) { jintArray jindices = NULL; DictIterData* data = (DictIterData*)closure; if ( NULL != data ) { XP_ASSERT( !!data->idata.indices ); XP_ASSERT( sizeof(jint) == sizeof(data->idata.indices[0]) ); jindices = makeIntArray( env, data->idata.count, (jint*)data->idata.indices ); // (*env)->DeleteLocalRef( env, jindices ); } return jindices; } JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1nthWord ( JNIEnv* env, jclass C, jint closure, jint nn) { jstring result = NULL; DictIterData* data = (DictIterData*)closure; if ( NULL != data ) { if ( dict_getNthWord( &data->iter, nn, data->depth, &data->idata ) ) { XP_UCHAR buf[64]; dict_wordToString( &data->iter, buf, VSIZE(buf) ); result = (*env)->NewStringUTF( env, buf ); // (*env)->DeleteLocalRef( env, result ); } } return result; } JNIEXPORT jint JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getStartsWith ( JNIEnv* env, jclass C, jint closure, jstring jprefix ) { jint result = -1; DictIterData* data = (DictIterData*)closure; if ( NULL != data ) { Tile tiles[MAX_COLS]; XP_U16 nTiles = VSIZE(tiles); const char* prefix = (*env)->GetStringUTFChars( env, jprefix, NULL ); if ( dict_tilesForString( data->dict, prefix, tiles, &nTiles ) ) { if ( dict_findStartsWith( &data->iter, NULL, tiles, nTiles ) ) { result = dict_getPosition( &data->iter ); } } (*env)->ReleaseStringUTFChars( env, jprefix, prefix ); } return result; } #ifdef XWFEATURE_SMS JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_base64Encode ( JNIEnv* env, jclass C, jbyteArray jbytes ) { int inlen = (*env)->GetArrayLength( env, jbytes ); jbyte* elems = (*env)->GetByteArrayElements( env, jbytes, NULL ); XP_ASSERT( !!elems ); XP_UCHAR out[4+(inlen*4/3)]; XP_U16 outlen = VSIZE( out ); binToSms( out, &outlen, (const XP_U8*)elems, inlen ); (*env)->ReleaseByteArrayElements( env, jbytes, elems, 0 ); jstring result = (*env)->NewStringUTF( env, out ); return result; } JNIEXPORT jbyteArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_base64Decode ( JNIEnv* env, jclass C, jstring jstr ) { jbyteArray result = NULL; const char* instr = (*env)->GetStringUTFChars( env, jstr, NULL ); XP_U16 inlen = (*env)->GetStringUTFLength( env, jstr ); XP_U8 out[inlen]; XP_U16 outlen = VSIZE(out); if ( smsToBin( out, &outlen, instr, inlen ) ) { result = (*env)->NewByteArray( env, outlen ); jbyte* jelems = (*env)->GetByteArrayElements( env, result, NULL ); XP_MEMCPY( jelems, out, outlen ); (*env)->ReleaseByteArrayElements( env, result, jelems, 0 ); } else { XP_ASSERT(0); } (*env)->ReleaseStringUTFChars( env, jstr, instr ); return result; } #endif #endif /* XWFEATURE_BOARDWORDS */