xwords/xwords4/android/XWords4/jni/xwjni.c
Eric House c4df31892e Fix occasional infinite loop of devices sending and receiving the same
message over and over when getting updated by GCM.  The problem
occurred when one device had an un-ACKable initial message still in
its queue.  I call resendAll a lot, which caused that message to get
resent to the other game which then replied without being able to ACK
it so it remained to be sent again.  This would continue until users
moved forward in the game.  The fix is to add a backoff timer to
resendAll() so that it can't loop.  The timer is reset when an ackable
and new message is received, meaning there's been a change in what's
available to resend.  And since users calling resendAll manually
expect it to do something, add a force param that ignores the backoff.
seems to fix the problem (but needs a lot of testing.)
2012-11-13 21:25:03 -08:00

1660 lines
50 KiB
C

/* -*-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 <string.h>
#include <sys/time.h>
#include <jni.h>
#include <android/log.h>
#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 = getCurSeconds( env );
}
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;
deleteLocalRef( env, jlp );
}
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 );
deleteLocalRef( env, jlp );
}
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 )
{
int len = (*env)->GetArrayLength( env, jstream );
XWStreamCtxt* stream = mem_stream_make_sized( MPPARM(mpool) vtMgr,
len, NULL, 0, NULL );
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, 0 );
destroyGI( MPPARM(mpool) &gi );
result = streamToBArray( env, stream );
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 );
#endif
return jstr;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
( JNIEnv* env, jclass C, jbyteArray jDictBytes, jstring jname, 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, jname,
jDictBytes, jpath, NULL, check );
if ( NULL != dict ) {
if ( NULL != jinfo ) {
XP_LangCode code = dict_getLangCode( dict );
XP_ASSERT( 0 < code );
setInt( env, jinfo, "langCode", code );
setInt( env, jinfo, "wordCount", dict_getWordCount( dict ) );
setString( env, jinfo, "md5Sum", dict_getMd5Sum( 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 );
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;
XP_U16 curSaveCount;
XP_U16 lastSavedSize;
#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 = getCurSeconds( env );
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_sized( MPPARM(mpool) globals->vtMgr,
state->lastSavedSize,
NULL, 0, NULL );
game_saveToStream( &state->game, gi, stream, ++state->curSaveCount );
if ( NULL != jgi ) {
destroyGI( MPPARM(mpool) &gi );
}
state->lastSavedSize = stream_getSize( stream );
result = streamToBArray( env, stream );
stream_destroy( stream );
XWJNI_END();
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1saveSucceeded
( JNIEnv* env, jclass C, jint gamePtr )
{
XWJNI_START();
game_saveSucceeded( &state->game, state->curSaveCount );
XWJNI_END();
}
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, 0 );
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 );
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 );
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 );
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] = {0};
XP_U16 buflen = sizeof(buf);
if ( !model_getPlayersLastScore( state->game.model, player, buf,
&buflen ) ) {
buf[0] = '\0';
}
result = (*env)->NewStringUTF( env, buf );
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 );
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 );
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, "<init>", "()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 );
deleteLocalRef( env, jaddr );
}
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 ) );
setInt( env, jsummary, "lastMoveTime",
server_getLastMoveTime(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 );
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 );
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 );
setInt( env, jgsi, "trayVisState", info.trayVisState );
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;
}
#ifdef XWFEATURE_CHANGEDICT
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1changeDict
( JNIEnv* env, jclass C, jint gamePtr, jobject jgi, jstring jname,
jbyteArray jDictBytes, jstring jpath )
{
XWJNI_START_GLOBALS();
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, globals->jniutil,
jname, jDictBytes, jpath, NULL, false );
game_changeDict( MPPARM(mpool) &state->game, globals->gi, dict );
setJGI( env, jgi, globals->gi );
XWJNI_END();
return XP_FALSE; /* no need to redraw */
}
#endif
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1resendAll
( JNIEnv* env, jclass C, jint gamePtr, jboolean force, jboolean thenAck )
{
XWJNI_START();
CommsCtxt* comms = state->game.comms;
XP_ASSERT( !!comms );
(void)comms_resendAll( comms, force );
if ( thenAck ) {
#ifdef XWFEATURE_COMMSACK
comms_ackAny( comms );
#endif
}
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 jname,
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, jname,
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 );
}
}
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 );
deleteLocalRef( env, jstr );
}
}
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 );
}
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 );
}
}
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 ) {
const char* prefix = (*env)->GetStringUTFChars( env, jprefix, NULL );
if ( 0 <= dict_findStartsWith( &data->iter, prefix ) ) {
result = dict_getPosition( &data->iter );
}
(*env)->ReleaseStringUTFChars( env, jprefix, prefix );
}
return result;
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getDesc
( JNIEnv* env, jclass C, jint closure )
{
jstring result = NULL;
DictIterData* data = (DictIterData*)closure;
if ( NULL != data ) {
const XP_UCHAR* disc = dict_getDesc( data->dict );
if ( NULL != disc && '\0' != disc[0] ) {
result = (*env)->NewStringUTF( env, disc );
}
}
return result;
}
#ifdef XWFEATURE_BASE64
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 = makeByteArray( env, outlen, (jbyte*)out );
} else {
XP_ASSERT(0);
}
(*env)->ReleaseStringUTFChars( env, jstr, instr );
return result;
}
#endif
#endif /* XWFEATURE_BOARDWORDS */