xwords/xwords4/android/jni/xwjni.c
Eric House ed90c9c16d implement util->dutil split for Android
So now all jni code uses a single dutil context, but also a single
mempool and jniutil instance instead of new instances of the latter two
per game and dict-iteration.
2018-07-05 08:32:19 -07:00

2337 lines
69 KiB
C

/* -*- compile-command: "find-and-gradle.sh inXw4Deb"; -*- */
/*
* Copyright © 2009 - 2018 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 <pthread.h>
#include <jni.h>
#include <android/log.h>
#include "comtypes.h"
#include "game.h"
#include "board.h"
#include "mempool.h"
#include "strutils.h"
#include "dbgutil.h"
#include "dictnry.h"
#include "dictiter.h"
#include "dictmgr.h"
#include "nli.h"
#include "utilwrapper.h"
#include "drawwrapper.h"
#include "xportwrapper.h"
#include "anddict.h"
#include "andutils.h"
#include "andglobals.h"
#include "jniutlswrapper.h"
#include "paths.h"
typedef struct _EnvThreadEntry {
JNIEnv* env;
pthread_t owner;
} EnvThreadEntry;
struct _EnvThreadInfo {
pthread_mutex_t mtxThreads;
int nEntries;
EnvThreadEntry* entries;
MPSLOT
};
/* Globals for the whole game */
typedef struct _JNIGlobalState {
EnvThreadInfo ti;
DictMgrCtxt* dictMgr;
VTableMgr* vtMgr;
XW_DUtilCtxt* dutil;
JNIUtilCtxt* jniutil;
XP_Bool mpoolInUse;
MPSLOT
} JNIGlobalState;
#ifdef MEM_DEBUG
static MemPoolCtx*
getMPool( JNIGlobalState* state )
{
XP_ASSERT( !state->mpoolInUse );
state->mpoolInUse = XP_TRUE;
return state->mpool;
}
static void
releaseMPool( JNIGlobalState* state )
{
XP_ASSERT( state->mpoolInUse );
state->mpoolInUse = XP_FALSE;
}
#else
# define releaseMPool(s)
#endif
#define LOG_MAPPING
// #define LOG_MAPPING_ALL
#define GAMEPTR_IS_OBJECT
#ifdef GAMEPTR_IS_OBJECT
typedef jobject GamePtrType;
#else
typedef int GamePtrType;
#endif
#ifdef LOG_MAPPING
# ifdef DEBUG
static int
countUsed(const EnvThreadInfo* ti)
{
int count = 0;
for ( int ii = 0; ii < ti->nEntries; ++ii ) {
EnvThreadEntry* entry = &ti->entries[ii];
if ( 0 != entry->owner ) {
# ifdef LOG_MAPPING_ALL
XP_LOGF( "%s(): ii=%d; owner: %x", __func__, ii, (unsigned int)entry->owner );
# endif
++count;
}
}
return count;
}
# endif
#endif
static void
map_thread( EnvThreadInfo* ti, JNIEnv* env )
{
pthread_t self = pthread_self();
pthread_mutex_lock( &ti->mtxThreads );
XP_Bool found = false;
int nEntries = ti->nEntries;
EnvThreadEntry* firstEmpty = NULL;
for ( int ii = 0; !found && ii < nEntries; ++ii ) {
EnvThreadEntry* entry = &ti->entries[ii];
if ( 0 == entry->owner ) {
if ( NULL == firstEmpty ) {
firstEmpty = entry;
}
} else if ( self == entry->owner ) {
found = true;
if ( env != entry->env ) {
/* this DOES happen!!! */
XP_LOGF( "%s (ti=%p): replacing env %p with env %p for thread %x",
__func__, ti, entry->env, env, (int)self );
entry->env = env;
}
}
}
if ( !found ) {
if ( !firstEmpty ) { /* out of slots */
if ( 0 == nEntries ) { /* first time */
nEntries = 2;
XP_ASSERT( !ti->entries );
} else {
nEntries *= 2;
}
EnvThreadEntry* entries = XP_CALLOC( ti->mpool, nEntries * sizeof(*entries) );
if ( !!ti->entries ) {
XP_MEMCPY( entries, ti->entries, ti->nEntries * sizeof(*ti->entries) );
}
firstEmpty = &entries[ti->nEntries]; /* first new entry */
ti->entries = entries;
ti->nEntries = nEntries;
#ifdef LOG_MAPPING
XP_LOGF( "%s: num env entries now %d", __func__, nEntries );
#endif
}
XP_ASSERT( !!firstEmpty );
firstEmpty->owner = self;
firstEmpty->env = env;
#ifdef LOG_MAPPING
XP_LOGF( "%s: entry %d: mapped env %p to thread %x", __func__,
firstEmpty - ti->entries, env, (int)self );
XP_LOGF( "%s: num entries USED now %d", __func__, countUsed(ti) );
#endif
}
pthread_mutex_unlock( &ti->mtxThreads );
} /* map_thread */
static void
map_init( MPFORMAL EnvThreadInfo* ti, JNIEnv* env )
{
pthread_mutex_init( &ti->mtxThreads, NULL );
MPASSIGN( ti->mpool, mpool );
map_thread( ti, env );
}
#define MAP_REMOVE( ti, env ) map_remove((ti), (env), __func__)
static void
map_remove( EnvThreadInfo* ti, JNIEnv* env, const char* func )
{
XP_Bool found = false;
pthread_mutex_lock( &ti->mtxThreads );
for ( int ii = 0; !found && ii < ti->nEntries; ++ii ) {
found = env == ti->entries[ii].env;
if ( found ) {
XP_ASSERT( pthread_self() == ti->entries[ii].owner );
#ifdef LOG_MAPPING
XP_LOGF( "%s: UNMAPPED env %p to thread %x (from %s)", __func__,
ti->entries[ii].env,
(int)ti->entries[ii].owner, func );
#endif
ti->entries[ii].env = NULL;
ti->entries[ii].owner = 0;
#ifdef LOG_MAPPING
XP_LOGF( "%s: %d entries left", __func__, countUsed( ti ) );
#endif
}
}
pthread_mutex_unlock( &ti->mtxThreads );
XP_ASSERT( found );
}
static void
map_destroy( EnvThreadInfo* ti )
{
pthread_mutex_destroy( &ti->mtxThreads );
}
static JNIEnv*
prvEnvForMe( EnvThreadInfo* ti )
{
JNIEnv* result = NULL;
pthread_t self = pthread_self();
pthread_mutex_lock( &ti->mtxThreads );
for ( int ii = 0; ii < ti->nEntries; ++ii ) {
if ( self == ti->entries[ii].owner ) {
result = ti->entries[ii].env;
break;
}
}
pthread_mutex_unlock( &ti->mtxThreads );
return result;
}
JNIEnv*
envForMe( EnvThreadInfo* ti, const char* caller )
{
JNIEnv* result = prvEnvForMe( ti );
#ifdef DEBUG
if( !result ) {
pthread_t self = pthread_self();
XP_LOGF( "no env for %s (thread %x)", caller, (int)self );
XP_ASSERT(0);
}
#endif
return result;
}
static void
tilesArrayToTileSet( JNIEnv* env, jintArray jtiles, TrayTileSet* tset )
{
if ( jtiles != NULL ) {
jsize nTiles = (*env)->GetArrayLength( env, jtiles );
int tmp[MAX_TRAY_TILES];
getIntsFromArray( env, tmp, jtiles, nTiles, XP_FALSE );
tset->nTiles = nTiles;
for ( int ii = 0; ii < nTiles; ++ii ) {
tset->tiles[ii] = tmp[ii];
}
}
}
#ifdef GAMEPTR_IS_OBJECT
static JNIState*
getState( JNIEnv* env, GamePtrType gamePtr )
{
XP_ASSERT( NULL != gamePtr );
jmethodID mid = getMethodID( env, gamePtr, "ptr", "()I" );
XP_ASSERT( !!mid );
return (JNIState*)(*env)->CallIntMethod( env, gamePtr, mid );
}
#else
# define getState( env, gamePtr ) ((JNIState*)(gamePtr))
#endif
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_initGlobals
( JNIEnv* env, jclass C, jobject jdutil, jobject jniu )
{
#ifdef MEM_DEBUG
MemPoolCtx* mpool = mpool_make( NULL );
#endif
JNIGlobalState* state = (JNIGlobalState*)XP_CALLOC( mpool, sizeof(*state) );
map_init( MPPARM(mpool) &state->ti, env );
state->jniutil = makeJNIUtil( MPPARM(mpool) env, &state->ti, jniu );
state->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
state->dutil = makeDUtil( MPPARM(mpool) &state->ti, jdutil, state->vtMgr,
state->jniutil, NULL );
state->dictMgr = dmgr_make( MPPARM_NOCOMMA( mpool ) );
MPASSIGN( state->mpool, mpool );
// LOG_RETURNF( "%p", state );
return (jint)state;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_cleanGlobals
( JNIEnv* env, jclass C, jint jniGlobalPtr )
{
// LOG_FUNC();
if ( 0 != jniGlobalPtr ) {
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
#ifdef MEM_DEBUG
MemPoolCtx* mpool = getMPool( state );
#endif
XP_ASSERT( ENVFORME(&state->ti) == env );
vtmgr_destroy( MPPARM(mpool) state->vtMgr );
dmgr_destroy( state->dictMgr );
destroyDUtil( &state->dutil );
destroyJNIUtil( env, &state->jniutil );
map_destroy( &state->ti );
XP_FREE( mpool, state );
mpool_destroy( mpool );
}
}
static const SetInfo gi_ints[] = {
ARR_MEMBER( CurGameInfo, nPlayers )
,ARR_MEMBER( CurGameInfo, gameSeconds )
,ARR_MEMBER( CurGameInfo, boardSize )
,ARR_MEMBER( CurGameInfo, gameID )
,ARR_MEMBER( CurGameInfo, dictLang )
,ARR_MEMBER( CurGameInfo, forceChannel )
};
static const SetInfo gi_bools[] = {
ARR_MEMBER( CurGameInfo, hintsNotAllowed )
,ARR_MEMBER( CurGameInfo, timerEnabled )
,ARR_MEMBER( CurGameInfo, allowPickTiles )
,ARR_MEMBER( CurGameInfo, allowHintRect )
};
static const SetInfo pl_ints[] = {
ARR_MEMBER( LocalPlayer, robotIQ )
,ARR_MEMBER( LocalPlayer, secondsUsed )
};
static CurGameInfo*
makeGI( MPFORMAL JNIEnv* env, jobject jgi )
{
CurGameInfo* gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*gi) );
XP_UCHAR buf[256]; /* in case needs whole path */
getInts( env, (void*)gi, jgi, gi_ints, VSIZE(gi_ints) );
getBools( env, (void*)gi, jgi, gi_bools, VSIZE(gi_bools) );
/* 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. */
if ( 0 == gi->gameID ) {
while ( 0 == gi->gameID ) {
gi->gameID = getCurSeconds( env );
}
setInt( env, jgi, "gameID", gi->gameID );
}
gi->phoniesAction =
jenumFieldToInt( env, jgi, "phoniesAction",
PKG_PATH("jni/CurGameInfo$XWPhoniesChoice") );
gi->serverRole =
jenumFieldToInt( env, jgi, "serverRole",
PKG_PATH("jni/CurGameInfo$DeviceRole"));
getString( env, jgi, "dictName", buf, VSIZE(buf) );
gi->dictName = copyString( mpool, buf );
XP_ASSERT( gi->nPlayers <= MAX_NUM_PLAYERS );
jobject jplayers;
if ( getObject( env, jgi, "players", "[L" PKG_PATH("jni/LocalPlayer") ";",
&jplayers ) ) {
for ( int ii = 0; ii < gi->nPlayers; ++ii ) {
LocalPlayer* lp = &gi->players[ii];
jobject jlp = (*env)->GetObjectArrayElement( env, jplayers, ii );
XP_ASSERT( !!jlp );
getInts( env, (void*)lp, jlp, pl_ints, VSIZE(pl_ints) );
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 );
deleteLocalRef( env, jlp );
}
deleteLocalRef( env, jplayers );
} else {
XP_ASSERT(0);
}
return gi;
} /* makeGI */
static const SetInfo nli_ints[] = {
ARR_MEMBER( NetLaunchInfo, _conTypes ),
ARR_MEMBER( NetLaunchInfo, lang ),
ARR_MEMBER( NetLaunchInfo, forceChannel ),
ARR_MEMBER( NetLaunchInfo, nPlayersT ),
ARR_MEMBER( NetLaunchInfo, nPlayersH ),
ARR_MEMBER( NetLaunchInfo, gameID ),
ARR_MEMBER( NetLaunchInfo, osVers ),
};
static const SetInfo nli_bools[] = {
ARR_MEMBER( NetLaunchInfo, isGSM )
};
static const SetInfo nli_strs[] = {
ARR_MEMBER( NetLaunchInfo, dict ),
ARR_MEMBER( NetLaunchInfo, gameName ),
ARR_MEMBER( NetLaunchInfo, room ),
ARR_MEMBER( NetLaunchInfo, btName ),
ARR_MEMBER( NetLaunchInfo, btAddress ),
ARR_MEMBER( NetLaunchInfo, phone ),
ARR_MEMBER( NetLaunchInfo, inviteID ),
};
static void
loadNLI( JNIEnv* env, NetLaunchInfo* nli, jobject jnli )
{
getInts( env, (void*)nli, jnli, nli_ints, VSIZE(nli_ints) );
getBools( env, (void*)nli, jnli, nli_bools, VSIZE(nli_bools) );
getStrings( env, (void*)nli, jnli, nli_strs, VSIZE(nli_strs) );
}
static void
setNLI( JNIEnv* env, jobject jnli, const NetLaunchInfo* nli )
{
setInts( env, jnli, (void*)nli, nli_ints, VSIZE(nli_ints) );
setBools( env, jnli, (void*)nli, nli_bools, VSIZE(nli_bools) );
setStrings( env, jnli, (void*)nli, nli_strs, VSIZE(nli_strs) );
}
static void
setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi )
{
// set fields
setInts( env, jgi, (void*)gi, gi_ints, VSIZE(gi_ints) );
setBools( env, jgi, (void*)gi, gi_bools, VSIZE(gi_bools) );
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 ) ) {
for ( int ii = 0; ii < gi->nPlayers; ++ii ) {
const LocalPlayer* lp = &gi->players[ii];
jobject jlp = (*env)->GetObjectArrayElement( env, jplayers, ii );
XP_ASSERT( !!jlp );
setInts( env, jlp, (void*)lp, pl_ints, VSIZE(pl_ints) );
setBool( env, jlp, "isLocal", lp->isLocal );
setString( env, jlp, "name", lp->name );
setString( env, jlp, "password", lp->password );
setString( env, jlp, "dictName", lp->dictName );
deleteLocalRef( env, jlp );
}
deleteLocalRef( env, jplayers );
} else {
XP_ASSERT(0);
}
} /* setJGI */
#ifdef COMMON_LAYOUT
static const SetInfo bd_ints[] = {
ARR_MEMBER( BoardDims, left )
,ARR_MEMBER( BoardDims, top )
,ARR_MEMBER( BoardDims, width )
,ARR_MEMBER( BoardDims, height )
,ARR_MEMBER( BoardDims, scoreLeft )
,ARR_MEMBER( BoardDims, scoreHt )
,ARR_MEMBER( BoardDims, scoreWidth )
,ARR_MEMBER( BoardDims, boardWidth )
,ARR_MEMBER( BoardDims, boardHt )
,ARR_MEMBER( BoardDims, trayLeft )
,ARR_MEMBER( BoardDims, trayTop )
,ARR_MEMBER( BoardDims, trayWidth )
,ARR_MEMBER( BoardDims, trayHt )
,ARR_MEMBER( BoardDims, cellSize )
,ARR_MEMBER( BoardDims, maxCellSize )
,ARR_MEMBER( BoardDims, timerWidth )
};
static void
dimsJToC( JNIEnv* env, BoardDims* out, jobject jdims )
{
getInts( env, (void*)out, jdims, bd_ints, VSIZE(bd_ints) );
}
static void
dimsCtoJ( JNIEnv* env, jobject jdims, const BoardDims* in )
{
setInts( env, jdims, (void*)in, bd_ints, VSIZE(bd_ints) );
}
#endif
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, jint jniGlobalPtr, jobject jgi )
{
jbyteArray result;
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
#ifdef MEM_DEBUG
MemPoolCtx* mpool = getMPool( state );
#endif
CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi );
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) state->vtMgr,
NULL, 0, NULL );
game_saveToStream( NULL, gi, stream, 0 );
destroyGI( MPPARM(mpool) &gi );
result = streamToBArray( env, stream );
stream_destroy( stream );
releaseMPool( state );
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_gi_1from_1stream
( JNIEnv* env, jclass C, jint jniGlobalPtr, jobject jgi, jbyteArray jstream )
{
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
#ifdef MEM_DEBUG
MemPoolCtx* mpool = getMPool( state );
#endif
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env,
state->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 );
releaseMPool( state );
}
JNIEXPORT jbyteArray JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_nli_1to_1stream
( JNIEnv* env, jclass C, jint jniGlobalPtr, jobject njli )
{
LOG_FUNC();
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
#ifdef MEM_DEBUG
MemPoolCtx* mpool = getMPool( state );
#endif
jbyteArray result;
NetLaunchInfo nli = {0};
loadNLI( env, &nli, njli );
/* CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi ); */
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) state->vtMgr,
NULL, 0, NULL );
nli_saveToStream( &nli, stream );
result = streamToBArray( env, stream );
stream_destroy( stream );
releaseMPool( state );
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_nli_1from_1stream
( JNIEnv* env, jclass C, jint jniGlobalPtr, jobject jnli, jbyteArray jstream )
{
LOG_FUNC();
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
#ifdef MEM_DEBUG
MemPoolCtx* mpool = getMPool( state );
#endif
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env,
state->vtMgr, jstream );
NetLaunchInfo nli = {0};
if ( nli_makeFromStream( &nli, stream ) ) {
setNLI( env, jnli, &nli );
} else {
XP_LOGF( "%s: game_makeFromStream failed", __func__ );
}
stream_destroy( stream );
releaseMPool( state );
}
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 =
#ifdef XWFEATURE_BLUETOOTH
# if defined VARIANT_xw4 || defined VARIANT_xw4fdroid
(*env)->NewStringUTF( env, XW_BT_UUID )
# elif defined VARIANT_xw4d
(*env)->NewStringUTF( env, XW_BT_UUID_DBG )
# endif
#else
NULL
#endif
;
return jstr;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1ref
( JNIEnv* env, jclass C, jint dictPtr )
{
if ( 0 != dictPtr ) {
DictionaryCtxt* dict = (DictionaryCtxt*)dictPtr;
dict_ref( dict );
}
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1unref
( JNIEnv* env, jclass C, jint dictPtr )
{
if ( 0 != dictPtr ) {
DictionaryCtxt* dict = (DictionaryCtxt*)dictPtr;
dict_unref( dict );
}
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes,
jstring jname, jstring jpath, jboolean check, jobject jinfo )
{
jboolean result = false;
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
map_thread( &state->ti, env );
#ifdef MEM_DEBUG
MemPoolCtx* mpool = getMPool( state );
#endif
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, state->dictMgr,
state->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_unref( dict );
result = true;
}
releaseMPool( state );
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;
XP_ASSERT( !!dict1 );
const DictionaryCtxt* dict2 = (DictionaryCtxt*)dictPtr2;
XP_ASSERT( !!dict2 );
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 );
}
struct _JNIState {
XWGame game;
JNIGlobalState* globalJNI;
AndGameGlobals globals;
// pthread_mutex_t msgMutex;
XP_U16 curSaveCount;
XP_U16 lastSavedSize;
#ifdef DEBUG
const char* envSetterFunc;
#endif
MPSLOT
};
#define XWJNI_START() { \
JNIState* state = getState( env, gamePtr ); \
MPSLOT; \
MPASSIGN( mpool, state->mpool); \
XP_ASSERT( !!state->globalJNI ); \
map_thread( &state->globalJNI->ti, env ); \
#define XWJNI_START_GLOBALS() \
XWJNI_START() \
AndGameGlobals* globals = &state->globals; \
#define XWJNI_END() \
} \
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_initJNI
( JNIEnv* env, jclass C, int jniGlobalPtr, jint seed )
{
/* Why am I doing this twice? */
/* struct timeval tv; */
/* gettimeofday( &tv, NULL ); */
/* srandom( tv.tv_sec ); */
#ifdef MEM_DEBUG
MemPoolCtx* mpool = ((JNIGlobalState*)jniGlobalPtr)->mpool;
#endif
JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) );
state->globalJNI = (JNIGlobalState*)jniGlobalPtr;
AndGameGlobals* globals = &state->globals;
globals->dutil = state->globalJNI->dutil;
globals->state = (JNIState*)state;
MPASSIGN( state->mpool, mpool );
globals->vtMgr = make_vtablemgr(MPPARM_NOCOMMA(mpool));
/* pthread_mutex_init( &state->msgMutex, NULL ); */
XP_LOGF( "%s: initing srand with %d", __func__, seed );
srandom( seed );
LOG_RETURNF( "%p", state );
return (jint) state;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_envDone
( JNIEnv* env, jclass C, int jniGlobalPtr )
{
JNIGlobalState* globalJNI = (JNIGlobalState*)jniGlobalPtr;
MAP_REMOVE( &globalJNI->ti, env );
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject j_gi,
jobjectArray j_names, jobjectArray j_dicts, jobjectArray j_paths,
jstring j_lang, jobject j_util, jobject j_draw, jobject j_cp,
jobject j_procs )
{
XWJNI_START_GLOBALS();
EnvThreadInfo* ti = &state->globalJNI->ti;
CurGameInfo* gi = makeGI( MPPARM(mpool) env, j_gi );
globals->gi = gi;
globals->util = makeUtil( MPPARM(mpool) ti, j_util, gi,
globals );
globals->jniutil = state->globalJNI->jniutil;
DrawCtx* dctx = NULL;
if ( !!j_draw ) {
dctx = makeDraw( MPPARM(mpool) ti, j_draw );
}
globals->dctx = dctx;
globals->xportProcs = makeXportProcs( MPPARM(mpool) ti, 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(state->globalJNI->mpool) env, state->globalJNI->dictMgr,
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 );
dict_unref( dict ); /* game owns it now */
model_setPlayerDicts( state->game.model, &dicts );
dict_unref_all( &dicts );
XWJNI_END();
} /* makeNewGame */
JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose
( JNIEnv* env, jclass claz, GamePtrType gamePtr )
{
JNIState* state = getState( env, gamePtr );
LOG_FUNC();
#ifdef MEM_DEBUG
MemPoolCtx* mpool = state->mpool;
#endif
AndGameGlobals* globals = &state->globals;
destroyGI( MPPARM(mpool) &globals->gi );
game_dispose( &state->game );
destroyDraw( &globals->dctx );
destroyXportProcs( &globals->xportProcs );
destroyUtil( &globals->util );
vtmgr_destroy( MPPARM(mpool) globals->vtMgr );
MAP_REMOVE( &state->globalJNI->ti, env );
/* pthread_mutex_destroy( &state->msgMutex ); */
XP_FREE( mpool, state );
} /* game_dispose */
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream
( JNIEnv* env, jclass C, GamePtrType gamePtr, jbyteArray jstream, jobject /*out*/jgi,
jobjectArray jdictNames, jobjectArray jdicts, jobjectArray jpaths,
jstring jlang, jobject jutil, jobject jdraw, jobject jcp, jobject jprocs )
{
jboolean result;
DictionaryCtxt* dict;
PlayerDicts dicts;
XWJNI_START_GLOBALS();
EnvThreadInfo* ti = &state->globalJNI->ti;
globals->gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*globals->gi) );
globals->util = makeUtil( MPPARM(mpool) ti, jutil, globals->gi, globals);
globals->jniutil = state->globalJNI->jniutil;
makeDicts( MPPARM(state->globalJNI->mpool) env, state->globalJNI->dictMgr,
globals->jniutil, &dict, &dicts, jdictNames, jdicts, jpaths,
jlang );
if ( !!jdraw ) {
globals->dctx = makeDraw( MPPARM(mpool) ti, jdraw );
}
globals->xportProcs = makeXportProcs( MPPARM(mpool) ti, 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 );
dict_unref( dict ); /* game owns it now */
dict_unref_all( &dicts );
if ( result ) {
XP_ASSERT( 0 != globals->gi->gameID );
if ( !!jgi ) {
setJGI( env, jgi, globals->gi );
}
} else {
destroyDraw( &globals->dctx );
destroyXportProcs( &globals->xportProcs );
destroyUtil( &globals->util );
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, GamePtrType 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, GamePtrType gamePtr )
{
XWJNI_START();
game_saveSucceeded( &state->game, state->curSaveCount );
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setDraw
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject jdraw )
{
XWJNI_START_GLOBALS();
DrawCtx* newDraw = makeDraw( MPPARM(mpool) &state->globalJNI->ti, jdraw );
board_setDraw( state->game.board, newDraw );
destroyDraw( &globals->dctx );
globals->dctx = newDraw;
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1invalAll
( JNIEnv *env, jclass C, GamePtrType 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, GamePtrType 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_1drawSnapshot
( JNIEnv *env, jclass C, GamePtrType gamePtr, jobject jdraw, jint width,
jint height )
{
XWJNI_START();
DrawCtx* newDraw = makeDraw( MPPARM(mpool) &state->globalJNI->ti, jdraw );
board_drawSnapshot( state->game.board, newDraw, width, height );
destroyDraw( &newDraw );
XWJNI_END();
}
#ifdef COMMON_LAYOUT
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1figureLayout
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject jgi, jint left, jint top,
jint width, jint height, jint scorePct, jint trayPct, jint scoreWidth,
jint fontWidth, jint fontHt, jboolean squareTiles, jobject jdims )
{
XWJNI_START();
CurGameInfo* gi = makeGI( MPPARM(mpool) env, jgi );
BoardDims dims;
board_figureLayout( state->game.board, gi, left, top, width, height,
115, scorePct, trayPct, scoreWidth,
fontWidth, fontHt, squareTiles,
((!!jdims) ? &dims : NULL) );
destroyGI( MPPARM(mpool) &gi );
if ( !!jdims ) {
dimsCtoJ( env, jdims, &dims );
}
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1applyLayout
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject jdims )
{
XWJNI_START();
BoardDims dims;
dimsJToC( env, &dims, jdims );
board_applyLayout( state->game.board, &dims );
XWJNI_END();
}
#else
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setPos
(JNIEnv *env, jclass C, GamePtrType 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 void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1setScoreboardLoc
( JNIEnv *env, jclass C, GamePtrType 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, GamePtrType 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, GamePtrType 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();
}
#endif
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1zoom
( JNIEnv* env, jclass C, GamePtrType 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;
}
#ifdef XWFEATURE_ACTIVERECT
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1getActiveRect
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject jrect, jintArray dims )
{
jboolean result;
XWJNI_START();
XP_Rect rect;
XP_U16 nCols, nRows;
result = board_getActiveRect( state->game.board, &rect, &nCols, &nRows );
if ( result ) {
setInt( env, jrect, "left", rect.left );
setInt( env, jrect, "top", rect.top );
setInt( env, jrect, "right", rect.left + rect.width );
setInt( env, jrect, "bottom", rect.top + rect.height );
if ( !!dims ) {
setIntInArray( env, dims, 0, nCols );
setIntInArray( env, dims, 1, nRows );
}
}
XWJNI_END();
return result;
}
#endif
#ifdef POINTER_SUPPORT
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1handlePenDown
(JNIEnv *env, jclass C, GamePtrType 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, GamePtrType 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, GamePtrType 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_1containsPt
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint xx, jint yy )
{
jboolean result;
XWJNI_START();
result = board_containsPt( state->game.board, xx, yy );
XWJNI_END();
return result;
}
#endif
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1juggleTray
(JNIEnv* env, jclass C, GamePtrType 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, GamePtrType gamePtr)
{
jboolean result;
XWJNI_START();
result = board_getTrayVisState( state->game.board );
XWJNI_END();
return result;
}
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1getSelPlayer
(JNIEnv* env, jclass C, GamePtrType gamePtr)
{
jint result;
XWJNI_START();
result = board_getSelPlayer( state->game.board );
XWJNI_END();
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1passwordProvided
(JNIEnv* env, jclass C, GamePtrType gamePtr, jint player, jstring jpasswd )
{
jboolean result;
XWJNI_START();
const char* passwd = (*env)->GetStringUTFChars( env, jpasswd, NULL );
result = board_passwordProvided( state->game.board, player, passwd );
(*env)->ReleaseStringUTFChars( env, jpasswd, passwd );
XWJNI_END();
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1hideTray
(JNIEnv* env, jclass C, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType 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_1setBlankValue
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint player,
jint col, jint row, jint tile )
{
jboolean result;
XWJNI_START();
result = board_setBlankValue( state->game.board, player, col, row, tile );
XWJNI_END();
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1toggle_1showValues
( JNIEnv* env, jclass C, GamePtrType 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, GamePtrType gamePtr, jboolean phoniesConfirmed,
jboolean turnConfirmed, jintArray jNewTiles )
{
jboolean result;
XWJNI_START();
TrayTileSet* newTilesP = NULL;
TrayTileSet newTiles;
if ( jNewTiles != NULL ) {
tilesArrayToTileSet( env, jNewTiles, &newTiles );
newTilesP = &newTiles;
}
result = board_commitTurn( state->game.board, phoniesConfirmed,
turnConfirmed, newTilesP );
XWJNI_END();
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1flip
(JNIEnv* env, jclass C, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType gamePtr )
{
jboolean result;
XWJNI_START();
XP_ASSERT( !!state->game.server );
result = server_do( state->game.server );
XWJNI_END();
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1tilesPicked
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint player, jintArray jNewTiles )
{
XWJNI_START();
TrayTileSet newTiles;
tilesArrayToTileSet( env, jNewTiles, &newTiles );
server_tilesPicked( state->game.server, player, &newTiles );
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1resetEngine
(JNIEnv* env, jclass C, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType gamePtr )
{
jint result;
XWJNI_START();
XP_ASSERT( !!state->game.model );
result = model_getNMoves( state->game.model );
XWJNI_END();
return result;
}
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_model_1getNumTilesInTray
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint player )
{
jint result;
XWJNI_START();
XP_ASSERT( !!state->game.model );
result = model_getNumTilesInTray( state->game.model, player );
XWJNI_END();
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_model_1getPlayersLastScore
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint player, jobject jlmi )
{
XWJNI_START();
XP_ASSERT( !!state->game.model );
LastMoveInfo lmi;
XP_Bool valid = model_getPlayersLastScore( state->game.model,
player, &lmi );
setBool( env, jlmi, "isValid", valid );
if ( valid ) {
setInt( env, jlmi, "score", lmi.score );
setInt( env, jlmi, "nTiles", lmi.nTiles );
setInt( env, jlmi, "moveType", lmi.moveType );
setString( env, jlmi, "name", lmi.name );
setString( env, jlmi, "word", lmi.word );
}
XWJNI_END();
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1writeFinalScores
( JNIEnv* env, jclass C, GamePtrType 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 )
{
AndGameGlobals* globals = (AndGameGlobals*)closure;
JNIState* state = (JNIState*)globals->state;
XP_ASSERT( !!state->game.comms );
comms_send( state->game.comms, stream );
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1initClientConnection
( JNIEnv* env, jclass C, GamePtrType gamePtr )
{
jboolean result;
LOG_FUNC();
XWJNI_START_GLOBALS();
XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals );
stream_setOnCloseProc( stream, and_send_on_close );
result = server_initClientConnection( state->game.server, stream );
XWJNI_END();
LOG_RETURNF( "%s", boolToStr(result) );
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1start
( JNIEnv* env, jclass C, GamePtrType gamePtr )
{
XWJNI_START();
CommsCtxt* comms = state->game.comms;
if ( !!comms ) {
comms_start( comms );
}
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1stop
( JNIEnv* env, jclass C, GamePtrType gamePtr )
{
XWJNI_START();
CommsCtxt* comms = state->game.comms;
if ( !!comms ) {
comms_stop( comms );
}
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1resetSame
( JNIEnv* env, jclass C, GamePtrType 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, GamePtrType 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, GamePtrType gamePtr )
{
jobjectArray result = NULL;
XWJNI_START();
XP_ASSERT( state->game.comms );
if ( !!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 ( int 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, GamePtrType gamePtr, jobject jaddr )
{
XWJNI_START();
if ( state->game.comms ) {
CommsAddrRec addr = {0};
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, GamePtrType gamePtr, jbyteArray jstream, jobject jaddr )
{
jboolean result;
XWJNI_START_GLOBALS();
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, globals->vtMgr,
jstream );
CommsAddrRec* addrp = NULL;
CommsAddrRec addr = {0};
XP_ASSERT( !!jaddr );
if ( NULL != jaddr ) {
getJAddrRec( env, &addr, jaddr );
addrp = &addr;
}
result = game_receiveMessage( &state->game, stream, addrp );
stream_destroy( stream );
XWJNI_END();
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1summarize
( JNIEnv* env, jclass C, GamePtrType 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 );
XP_Bool isLocal = XP_FALSE;
setInt( env, jsummary, "turn",
server_getCurrentTurn( state->game.server, &isLocal ) );
setBool( env, jsummary, "turnIsLocal", isLocal );
setInt( env, jsummary, "lastMoveTime",
server_getLastMoveTime(state->game.server) );
if ( !!state->game.comms ) {
CommsAddrRec addr;
CommsCtxt* comms = state->game.comms;
comms_getAddr( comms, &addr );
setInt( env, jsummary, "seed", comms_getChannelSeed( comms ) );
setInt( env, jsummary, "missingPlayers",
server_getMissingPlayers( state->game.server ) );
setInt( env, jsummary, "nPacketsPending",
comms_countPendingPackets( state->game.comms ) );
setTypeSetFieldIn( env, &addr, jsummary, "conTypes" );
CommsConnType typ;
for ( XP_U32 st = 0; addr_iter( &addr, &typ, &st ); ) {
switch( typ ) {
case COMMS_CONN_RELAY: {
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 );
}
break;
#if defined XWFEATURE_BLUETOOTH || defined XWFEATURE_SMS || defined XWFEATURE_P2P
case COMMS_CONN_BT:
case COMMS_CONN_P2P:
case COMMS_CONN_SMS: {
CommsAddrRec addrs[MAX_NUM_PLAYERS];
XP_U16 count = VSIZE(addrs);
comms_getAddrs( comms, addrs, &count );
const XP_UCHAR* addrps[count];
for ( int ii = 0; ii < count; ++ii ) {
switch ( typ ) {
case COMMS_CONN_BT: addrps[ii] = (XP_UCHAR*)&addrs[ii].u.bt.btAddr; break;
case COMMS_CONN_P2P: addrps[ii] = (XP_UCHAR*)&addrs[ii].u.p2p.mac_addr; break;
case COMMS_CONN_SMS: addrps[ii] = (XP_UCHAR*)&addrs[ii].u.sms.phone; break;
default: XP_ASSERT(0); break;
}
XP_LOGF( "%s: adding btaddr/phone/mac %s", __func__, addrps[ii] );
}
jobjectArray jaddrs = makeStringArray( env, count, addrps );
setObject( env, jsummary, "remoteDevs", "[Ljava/lang/String;",
jaddrs );
deleteLocalRef( env, jaddrs );
}
break;
#endif
default:
XP_ASSERT(0);
}
}
}
XP_U16 nPlayers = model_getNPlayers( model );
jint jvals[nPlayers];
if ( gameOver ) {
ScoresArray scores;
model_figureFinalScores( model, &scores, NULL );
for ( int ii = 0; ii < nPlayers; ++ii ) {
jvals[ii] = scores.arr[ii];
}
} else {
for ( int ii = 0; ii < nPlayers; ++ii ) {
jvals[ii] = model_getPlayerScore( model, ii );
}
}
jintArray jarr = makeIntArray( env, nPlayers, jvals, sizeof(jvals[0]) );
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, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType 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, GamePtrType gamePtr, jobject jgi )
{
XWJNI_START_GLOBALS();
setJGI( env, jgi, globals->gi );
XWJNI_END();
}
static const SetInfo gsi_ints[] = {
ARR_MEMBER( GameStateInfo, visTileCount ),
ARR_MEMBER( GameStateInfo, nPendingMessages ),
ARR_MEMBER( GameStateInfo, trayVisState ),
};
static const SetInfo gsi_bools[] = {
ARR_MEMBER( GameStateInfo,canHint ),
ARR_MEMBER( GameStateInfo, canUndo ),
ARR_MEMBER( GameStateInfo, canRedo ),
ARR_MEMBER( GameStateInfo, inTrade ),
ARR_MEMBER( GameStateInfo, tradeTilesSelected ),
ARR_MEMBER( GameStateInfo, canChat ),
ARR_MEMBER( GameStateInfo, canShuffle ),
ARR_MEMBER( GameStateInfo, curTurnSelected ),
ARR_MEMBER( GameStateInfo, canHideRack ),
ARR_MEMBER( GameStateInfo, canTrade ),
};
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1getState
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject jgsi )
{
XWJNI_START();
GameStateInfo info;
game_getState( &state->game, &info );
setInts( env, jgsi, (void*)&info, gsi_ints, VSIZE(gsi_ints) );
setBools( env, jgsi, (void*)&info, gsi_bools, VSIZE(gsi_bools) );
XWJNI_END();
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1hasComms
( JNIEnv* env, jclass C, GamePtrType 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, GamePtrType gamePtr, jobject jgi, jstring jname,
jbyteArray jDictBytes, jstring jpath )
{
XWJNI_START_GLOBALS();
DictionaryCtxt* dict = makeDict( MPPARM(state->globalJNI->mpool) env,
state->globalJNI->dictMgr,
globals->jniutil, jname, jDictBytes,
jpath, NULL, false );
game_changeDict( MPPARM(mpool) &state->game, globals->gi, dict );
dict_unref( dict );
setJGI( env, jgi, globals->gi );
XWJNI_END();
return XP_FALSE; /* no need to redraw */
}
#endif
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1resendAll
( JNIEnv* env, jclass C, GamePtrType gamePtr, jboolean force, jobject jFilter,
jboolean thenAck )
{
jint result;
XWJNI_START();
CommsCtxt* comms = state->game.comms;
XP_ASSERT( !!comms );
CommsConnType filter =
NULL == jFilter ? COMMS_CONN_NONE : jEnumToInt( env, jFilter );
result = comms_resendAll( comms, filter, force );
if ( thenAck ) {
#ifdef XWFEATURE_COMMSACK
comms_ackAny( comms );
#endif
}
XWJNI_END();
return result;
}
#ifdef XWFEATURE_COMMSACK
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1ackAny
( JNIEnv* env, jclass C, GamePtrType 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, GamePtrType gamePtr, jobject failedTyp )
{
XWJNI_START();
XP_ASSERT( !!state->game.comms );
CommsConnType typ = jEnumToInt( env, failedTyp );
(void)comms_transportFailed( state->game.comms, typ );
XWJNI_END();
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1isConnected
( JNIEnv* env, jclass C, GamePtrType gamePtr )
{
jboolean result;
XWJNI_START();
result = NULL != state->game.comms && comms_isConnected( state->game.comms );
XWJNI_END();
return result;
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1formatRelayID
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint indx )
{
jstring result = NULL;
XWJNI_START();
XP_UCHAR buf[64];
XP_U16 len = sizeof(buf);
if ( comms_formatRelayID( state->game.comms, indx, buf, &len ) ) {
XP_ASSERT( len < sizeof(buf) );
LOG_RETURNF( "%s", buf );
result = (*env)->NewStringUTF( env, buf );
}
XWJNI_END();
return result;
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1getStats
( JNIEnv* env, jclass C, GamePtrType gamePtr )
{
jstring result = NULL;
#ifdef DEBUG
XWJNI_START_GLOBALS();
if ( NULL != state->game.comms ) {
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
NULL, 0, NULL );
comms_getStats( state->game.comms, stream );
result = streamToJString( env, stream );
stream_destroy( stream );
}
XWJNI_END();
#endif
return result;
}
#ifdef DEBUG
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1setAddrDisabled
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject jConnTyp,
jboolean forSend, jboolean val )
{
XWJNI_START();
if ( NULL != state->game.comms ) {
CommsConnType connType = jEnumToInt( env, jConnTyp );
comms_setAddrDisabled( state->game.comms, connType, forSend, val );
}
XWJNI_END();
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_comms_1getAddrDisabled
( JNIEnv* env, jclass C, GamePtrType gamePtr, jobject jConnTyp,
jboolean forSend )
{
jboolean result = XP_FALSE;
XWJNI_START();
if ( NULL != state->game.comms ) {
CommsConnType connType = jEnumToInt( env, jConnTyp );
result = comms_getAddrDisabled( state->game.comms, connType, forSend );
}
XWJNI_END();
return result;
}
#endif
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_haveEnv
( JNIEnv* env, jclass C, jint jniGlobalPtr )
{
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
jboolean result = NULL != prvEnvForMe(&state->ti);
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1endGame
( JNIEnv* env, jclass C, GamePtrType gamePtr )
{
XWJNI_START();
XP_ASSERT( !!state->game.server );
server_endGame( state->game.server );
XWJNI_END();
}
#ifdef XWFEATURE_CHAT
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1sendChat
( JNIEnv* env, jclass C, GamePtrType gamePtr, jstring jmsg )
{
XWJNI_START();
XP_ASSERT( !!state->game.server );
const char* msg = (*env)->GetStringUTFChars( env, jmsg, NULL );
board_sendChat( state->game.board, msg );
(*env)->ReleaseStringUTFChars( env, jmsg, msg );
XWJNI_END();
}
#endif
#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;
static void makeIndex( DictIterData* data );
static void freeIndices( DictIterData* data );
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1init
( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jDictBytes, jstring jname,
jstring jpath )
{
jint closure = 0;
JNIGlobalState* state = (JNIGlobalState*)jniGlobalPtr;
DictionaryCtxt* dict = makeDict( MPPARM(state->mpool) env, state->dictMgr,
state->jniutil, jname, jDictBytes, jpath, NULL,
false );
if ( !!dict ) {
DictIterData* data = XP_CALLOC( state->mpool, sizeof(*data) );
data->env = env;
data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(state->mpool) );
data->jniutil = state->jniutil;
data->dict = dict;
data->depth = 2;
#ifdef MEM_DEBUG
data->mpool = state->mpool;
#endif
closure = (int)data;
}
return closure;
}
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_unref( data->dict );
freeIndices( data );
vtmgr_destroy( MPPARM(mpool) data->vtMgr );
XP_FREE( mpool, data );
}
}
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,
sizeof(lens.lens[0]) );
}
}
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 );
XP_U16 depth = data->depth;
for ( int 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,
sizeof(data->idata.indices[0]) );
}
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;
}
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 */
#endif /* XWFEATURE_BOARDWORDS */