Merge branch 'android_branch' into android_translate

This commit is contained in:
Eric House 2020-07-25 12:50:48 -07:00
commit ba342c891d
54 changed files with 845 additions and 2047 deletions

4
README.md Normal file
View file

@ -0,0 +1,4 @@
# xwords
Clone of xwords repo from Sourceforge -- an open source implementation of rules of Scrabble(tm) for handhelds
Just adding now. I'll edit after pulling. :-)

View file

@ -1,6 +1,6 @@
def INITIAL_CLIENT_VERS = 9
def VERSION_CODE_BASE = 156
def VERSION_NAME = '4.4.160'
def VERSION_CODE_BASE = 157
def VERSION_NAME = '4.4.161'
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
def BUILD_INFO_NAME = "build-info.txt"
@ -36,10 +36,8 @@ if ( FABRIC_API_KEY && hasProperty('useCrashlytics') ) { // rm-for-fdroid
} // rm-for-fdroid
repositories {
google()
maven { url 'https://maven.fabric.io/public' } // rm-for-fdroid
maven {
url "https://repo.eclipse.org/content/repositories/paho-releases/"
}
maven { url 'https://maven.fabric.io/public' } // rm-for-fdroid
maven { url "https://repo.eclipse.org/content/repositories/paho-releases/" } // rm-for-fdroid
}
android {
@ -122,6 +120,7 @@ android {
resValue "string", "nfc_aid", "$NFC_AID_XW4"
externalNativeBuild.ndkBuild.cFlags += ['-DVARIANT_xw4fdroid']
externalNativeBuild.ndkBuild.arguments += ['XW_BT_UUID=' + XW_UUID]
buildConfigField "boolean", "OFFER_MQTT", "false"
}
xw4d {
@ -337,7 +336,7 @@ dependencies {
implementation 'com.github.eehouse:nbsproxy:v0.2.2'
implementation "org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.+"
implementation "org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.+" // rm-for-fdroid
}
task mkImages(type: Exec) {

View file

@ -13,10 +13,10 @@
</style>
</head>
<body>
<h2>CrossWords 4.4.160 release</h2>
<h2>CrossWords 4.4.161 release</h2>
<p>This release improves play via the internet by adding a second
(and redundant) type of server.</p>
<p>This minor release removes an under-construction image I added
recently. It made friends think they were being hacked.</p>
<div id="survey">
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
@ -26,17 +26,17 @@
<h3>New with this release</h3>
<ul>
<li>Improve internet/relay play by adding a second server type (MQTT)</li>
<li>Add "under construction" icon for when thumbnail isn't available</li>
<li>Enable new "block phonies" option that won't let you use a word not in the wordlist</li>
<li>Add new Japanese translations (via Weblate)</li>
<li>Remove under-construction image that flashed by as a thumbnail was loading</li>
<li>Make color of line separating board tiles configurable</li>
<li>Change how dialogs are handled internally. You should NOT notice!</li>
</ul>
<p>(The full changelog
is <a href="http://xwords.sf.net/and_changes.php">here</a>.)</p>
<h3>Next up</h3>
<h3>Coming soon</h3>
<ul>
<li>Add filtering to the wordlist browser</li>
<li>Improve move-via-NFC</li>
<li>Support duplicate-style play (popular in France)</li>
<li>Improve play-by-data-sms workaround

View file

@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.Button;
@ -74,7 +75,7 @@ public class BTInviteDelegate extends InviteDelegate {
pairs = new ArrayList<>();
} else {
for ( TwoStringPair pair : pairs ) {
alreadyHave = pair.str2.equals(devName);
alreadyHave = TextUtils.equals(pair.str2, devName);
if ( alreadyHave ) {
break;
}
@ -96,7 +97,7 @@ public class BTInviteDelegate extends InviteDelegate {
for ( Iterator<TwoStringPair> iter = pairs.iterator();
iter.hasNext(); ) {
TwoStringPair pair = iter.next();
if ( pair.str2.equals( dev ) ) {
if ( TextUtils.equals( pair.str2, dev ) ) {
iter.remove();
break;
}
@ -104,6 +105,18 @@ public class BTInviteDelegate extends InviteDelegate {
}
}
private void removeNulls()
{
for ( Iterator<TwoStringPair> iter = pairs.iterator();
iter.hasNext(); ) {
TwoStringPair pair = iter.next();
if ( TextUtils.isEmpty( pair.str2 ) ) {
Log.d( TAG, "removeNulls(): removing!!" );
iter.remove();
}
}
}
boolean empty() { return pairs == null || pairs.size() == 0; }
private void sort()
@ -315,6 +328,7 @@ public class BTInviteDelegate extends InviteDelegate {
if ( null == sPersisted ) {
try {
sPersisted = (Persisted)DBUtils.getSerializableFor( context, KEY_PERSIST );
sPersisted.removeNulls(); // clean up earlier mistakes
} catch ( Exception ex ) {} // NPE, de-serialization problems, etc.
if ( null == sPersisted ) {

View file

@ -49,7 +49,6 @@ public class BoardCanvas extends Canvas implements DrawCtx {
private static final String TAG = BoardCanvas.class.getSimpleName();
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
private static final int FRAME_GREY = 0xFF101010;
private static final int SCORE_HT_DROP = 2;
private static final boolean DEBUG_DRAWFRAMES = false;
private static final int NOT_TURN_ALPHA = 0x3FFFFFFF;
@ -442,7 +441,15 @@ public class BoardCanvas extends Canvas implements DrawCtx {
}
// frame the cell
m_strokePaint.setColor( adjustColor(FRAME_GREY) );
int frameColor = m_otherColors[CommonPrefs.COLOR_CELLLINE];
m_strokePaint.setColor( adjustColor(frameColor) );
if ( false ) {
// PENDING: fetch/calculate this a lot less frequently!!
int linePct = XWPrefs.getPrefsInt( m_activity, R.string.key_board_line_pct, 1 );
linePct = Math.min( 25, linePct );
int width = Math.max(1, (rect.width() * linePct) / 100);
m_strokePaint.setStrokeWidth( width );
}
drawRect( rect, m_strokePaint );
drawCrosshairs( rect, flags );
@ -559,7 +566,8 @@ public class BoardCanvas extends Canvas implements DrawCtx {
// the board (without scrolling), the right-most cells
// don't draw their right borders due to clipping, so draw
// for them.
m_strokePaint.setColor( adjustColor(FRAME_GREY) );
int frameColor = m_otherColors[CommonPrefs.COLOR_CELLLINE];
m_strokePaint.setColor( adjustColor(frameColor) );
int xx = rect.left + rect.width() - 1;
drawLine( xx, rect.top, xx, rect.top + rect.height(),
m_strokePaint );

View file

@ -315,8 +315,9 @@ public class BoardDelegate extends DelegateBase
int title = (Integer)params[0];
String msg = (String)params[1];
ab.setMessage( msg );
Assert.assertTrue( 0 != title );
ab.setTitle( title );
if ( 0 != title ) {
ab.setTitle( title );
}
ab.setPositiveButton( android.R.string.ok, null );
if ( DlgID.DLG_SCORES == dlgID ) {
if ( null != m_mySIS.words && m_mySIS.words.length > 0 ) {
@ -2488,7 +2489,7 @@ public class BoardDelegate extends DelegateBase
boolean banned = Perm.SEND_SMS.isBanned(m_activity);
int explID = banned
? R.string.banned_nbs_perms : R.string.missing_sms_perms;
DlgDelegate.ConfirmThenBuilder builder =
DlgDelegate.Builder builder =
makeConfirmThenBuilder( explID, Action.DROP_SMS_ACTION );
if ( banned ) {
builder.setActionPair( Action.PERMS_BANNED_INFO,
@ -2635,7 +2636,6 @@ public class BoardDelegate extends DelegateBase
switch ( dlgID ) {
case DLG_OKONLY:
case DLG_SCORES:
dlgTitle = R.string.info_title;
break;
case DLG_USEDICT:
case DLG_GETDICT:

View file

@ -1,59 +0,0 @@
/* -*- compile-command: "find-and-gradle.sh inXw4dDebug"; -*- */
/*
* Copyright 2017 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.
*/
package org.eehouse.android.xw4;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
import org.eehouse.android.xw4.loc.LocUtils;
public class ConfirmThenAlert extends DlgDelegateAlert {
public static ConfirmThenAlert newInstance( DlgState state )
{
ConfirmThenAlert result = new ConfirmThenAlert();
result.addStateArgument( state );
return result;
}
public ConfirmThenAlert() {}
@Override
public void populateBuilder( Context context, DlgState state,
AlertDialog.Builder builder )
{
NotAgainView naView = addNAView( state, builder );
OnClickListener lstnr = mkCallbackClickListener( naView );
builder.setTitle( state.m_titleId == 0 ? R.string.query_title : state.m_titleId )
.setPositiveButton( state.m_posButton, lstnr )
.setNegativeButton( state.m_negButton, lstnr );
if ( null != state.m_pair ) {
ActionPair pair = state.m_pair;
builder.setNeutralButton( pair.buttonStr,
mkCallbackClickListener( pair, naView ) );
}
}
}

View file

@ -181,7 +181,7 @@ public class ConnViaViewLayout extends LinearLayout {
break;
}
DlgDelegate.DlgDelegateBuilder builder = 0 != keyID
DlgDelegate.Builder builder = 0 != keyID
? m_dlgDlgt.makeNotAgainBuilder( msgID, keyID )
: m_dlgDlgt.makeOkOnlyBuilder( msgID )
.setActionPair( Action.PERMS_BANNED_INFO,

View file

@ -41,10 +41,8 @@ import android.widget.EditText;
import android.widget.TextView;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.ConfirmThenBuilder;
import org.eehouse.android.xw4.DlgDelegate.Builder;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
import org.eehouse.android.xw4.DlgDelegate.NotAgainBuilder;
import org.eehouse.android.xw4.DlgDelegate.OkOnlyBuilder;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
@ -485,17 +483,13 @@ public class DelegateBase implements DlgClickNotify,
DlgDelegateAlert df = null;
switch ( state.m_id ) {
case CONFIRM_THEN:
df = ConfirmThenAlert.newInstance( state );
case DIALOG_OKONLY:
case DIALOG_NOTAGAIN:
df = DlgDelegateAlert.newInstance( state );
break;
case DIALOG_ENABLESMS:
df = EnableSMSAlert.newInstance( state );
break;
case DIALOG_OKONLY:
df = OkOnlyAlert.newInstance( state );
break;
case DIALOG_NOTAGAIN:
df = NotAgainAlert.newInstance( state );
break;
case INVITE_CHOICES_THEN:
df = InviteChoicesAlert.newInstance( state );
break;
@ -521,33 +515,33 @@ public class DelegateBase implements DlgClickNotify,
return LocUtils.makeAlertBuilder( m_activity );
}
public NotAgainBuilder
public Builder
makeNotAgainBuilder( String msg, int key, Action action )
{
return m_dlgDelegate.makeNotAgainBuilder( msg, key, action );
}
public NotAgainBuilder
public Builder
makeNotAgainBuilder( int msgId, int key, Action action )
{
return m_dlgDelegate.makeNotAgainBuilder( msgId, key, action );
}
public NotAgainBuilder makeNotAgainBuilder( String msg, int key )
public Builder makeNotAgainBuilder( String msg, int key )
{
return m_dlgDelegate.makeNotAgainBuilder( msg, key );
}
public NotAgainBuilder makeNotAgainBuilder( int msgId, int key )
public Builder makeNotAgainBuilder( int msgID, int key )
{
return m_dlgDelegate.makeNotAgainBuilder( msgId, key );
return m_dlgDelegate.makeNotAgainBuilder( msgID, key );
}
public ConfirmThenBuilder makeConfirmThenBuilder( String msg, Action action ) {
public Builder makeConfirmThenBuilder( String msg, Action action ) {
return m_dlgDelegate.makeConfirmThenBuilder( msg, action );
}
public ConfirmThenBuilder makeConfirmThenBuilder( int msgId, Action action ) {
public Builder makeConfirmThenBuilder( int msgId, Action action ) {
return m_dlgDelegate.makeConfirmThenBuilder( msgId, action );
}
@ -578,12 +572,12 @@ public class DelegateBase implements DlgClickNotify,
m_dlgDelegate.showInviteChoicesThen( action, info );
}
public OkOnlyBuilder makeOkOnlyBuilder( int msgId )
public Builder makeOkOnlyBuilder( int msgID )
{
return m_dlgDelegate.makeOkOnlyBuilder( msgId );
return m_dlgDelegate.makeOkOnlyBuilder( msgID );
}
public OkOnlyBuilder makeOkOnlyBuilder( String msg )
public Builder makeOkOnlyBuilder( String msg )
{
return m_dlgDelegate.makeOkOnlyBuilder( msg );
}
@ -722,12 +716,10 @@ public class DelegateBase implements DlgClickNotify,
final int key = notAgainKey;
runOnUiThread( new Runnable() {
public void run() {
DlgDelegate.DlgDelegateBuilder builder;
if ( 0 == key ) {
builder = makeOkOnlyBuilder( msg );
} else {
builder = makeNotAgainBuilder( msg, key );
}
Builder builder = 0 == key
? makeOkOnlyBuilder( msg )
: makeNotAgainBuilder( msg, key )
;
builder.show();
}
});

View file

@ -123,10 +123,6 @@ public class DlgDelegate {
ASKED_PHONE_STATE,
PERMS_QUERY,
PERMS_BANNED_INFO,
// Sent when not-again checkbox checked
SET_NA_DEFAULTNAME,
SET_GOT_LANGDICT,
} // Action enum
public static class ActionPair implements Serializable {
@ -154,139 +150,169 @@ public class DlgDelegate {
}
}
public abstract class DlgDelegateBuilder {
protected String m_msgString;
protected int m_prefsNAKey;
protected Action m_onNA;
protected int m_posButton = android.R.string.ok;
protected int m_negButton = android.R.string.cancel;
protected Action m_action;
protected Object[] m_params;
protected int m_titleId = 0;
protected ActionPair m_actionPair;
public class Builder {
private final DlgState mState;
public DlgDelegateBuilder( String msg, Action action )
{ m_msgString = msg; m_action = action; }
Builder( DlgID dlgID ) {
mState = new DlgState( dlgID )
.setPosButton( android.R.string.ok ) // default
.setAction( Action.SKIP_CALLBACK )
;
}
public DlgDelegateBuilder( int msgId, Action action )
{ this( getString(msgId), action );}
Builder setMessage( String msg )
{
mState.setMsg( msg );
return this;
}
public DlgDelegateBuilder setAction( Action action )
{ m_action = action; return this; }
Builder setMessageID( int msgID )
{
mState.setMsg( getString( msgID ) );
return this;
}
public DlgDelegateBuilder setNAKey( int keyId )
{ m_prefsNAKey = keyId; return this; }
Builder setActionPair( Action action, int strID )
{
mState.setActionPair( new ActionPair( action, strID ) );
return this;
}
public DlgDelegateBuilder setOnNA( Action onNA )
{ m_onNA = onNA; return this; }
Builder setAction( Action action )
{
mState.setAction( action );
return this;
}
public DlgDelegateBuilder setPosButton( int id )
{ m_posButton = id; return this; }
Builder setPosButton( int strID )
{
mState.setPosButton( strID );
return this;
}
public DlgDelegateBuilder setNegButton( int id )
{ m_negButton = id; return this; }
Builder setNegButton( int strID )
{
mState.setNegButton( strID );
return this;
}
public DlgDelegateBuilder setParams( Object... params )
{ m_params = params; return this; }
Builder setTitle( int strID )
{
mState.setTitle( strID );
return this;
}
public DlgDelegateBuilder setTitle( int titleId )
{ m_titleId = titleId; return this; }
Builder setParams( Object... params )
{
mState.setParams( params );
return this;
}
public DlgDelegateBuilder setActionPair( ActionPair pr )
{ m_actionPair = pr; return this; }
Builder setNAKey( int keyID )
{
mState.setPrefsNAKey( keyID );
return this;
}
public DlgDelegateBuilder setActionPair( Action actn, int id )
{ return setActionPair( new ActionPair( actn, id ) ); }
abstract void show();
}
public class OkOnlyBuilder extends DlgDelegateBuilder {
public OkOnlyBuilder(String msg) { super( msg, Action.SKIP_CALLBACK ); }
public OkOnlyBuilder(int msgId) { super( msgId, Action.SKIP_CALLBACK ); }
@Override
public void show()
{
showOKOnlyDialogThen( m_msgString, m_action, m_actionPair,
m_params, m_titleId );
int naKey = mState.m_prefsNAKey;
final Action action = mState.m_action;
// Log.d( TAG, "show(): key: %d; action: %s", naKey, action );
if ( 0 == naKey || ! XWPrefs.getPrefsBoolean( m_activity, naKey, false ) ) {
m_dlgt.show( mState );
} else if ( Action.SKIP_CALLBACK != action ) {
post( new Runnable() {
@Override
public void run() {
Log.d( TAG, "calling onPosButton()" );
XWActivity xwact = (XWActivity)m_activity;
xwact.onPosButton( action, mState.m_params );
}
});
}
}
}
public class ConfirmThenBuilder extends DlgDelegateBuilder {
public ConfirmThenBuilder(String msg, Action action) {super(msg, action);}
public ConfirmThenBuilder(int msgId, Action action) {super(msgId, action);}
@Override
public void show()
{
showConfirmThen( m_prefsNAKey, m_onNA, m_msgString, m_posButton,
m_negButton, m_action, m_titleId, m_actionPair,
m_params );
}
}
public class NotAgainBuilder extends DlgDelegateBuilder {
public NotAgainBuilder(String msg, int key, Action action)
{ super(msg, action); setNAKey( key ); }
public NotAgainBuilder(int msgId, int key, Action action)
{ super(msgId, action); m_prefsNAKey = key; }
public NotAgainBuilder( String msg, int key )
{ super( msg, Action.SKIP_CALLBACK ); m_prefsNAKey = key; }
public NotAgainBuilder( int msgId, int key )
{ super( msgId, Action.SKIP_CALLBACK ); m_prefsNAKey = key; }
@Override
public void show()
{
showNotAgainDlgThen( m_msgString, m_prefsNAKey,
m_action, m_actionPair,
m_params );
}
}
public OkOnlyBuilder makeOkOnlyBuilder( String msg )
public Builder makeOkOnlyBuilder( String msg )
{
return new OkOnlyBuilder( msg );
}
public OkOnlyBuilder makeOkOnlyBuilder( int msgId )
{
return new OkOnlyBuilder( msgId );
// return new OkOnlyBuilder( msg );
Builder builder = new Builder( DlgID.DIALOG_OKONLY )
.setMessage( msg )
;
return builder;
}
public ConfirmThenBuilder makeConfirmThenBuilder( String msg, Action action )
public Builder makeOkOnlyBuilder( int msgID )
{
return new ConfirmThenBuilder( msg, action );
Builder builder = new Builder( DlgID.DIALOG_OKONLY )
.setMessageID( msgID )
;
return builder;
}
public ConfirmThenBuilder makeConfirmThenBuilder(int msgId, Action action)
private Builder makeConfirmThenBuilder( Action action )
{
return new ConfirmThenBuilder( msgId, action );
return new Builder( DlgID.CONFIRM_THEN )
.setAction( action )
.setNegButton( android.R.string.cancel )
;
}
public Builder makeConfirmThenBuilder( String msg, Action action )
{
return makeConfirmThenBuilder( action )
.setMessage( msg )
;
}
public NotAgainBuilder makeNotAgainBuilder( int msgId, int key,
Action action )
public Builder makeConfirmThenBuilder( int msgID, Action action )
{
return new NotAgainBuilder( msgId, key, action );
return makeConfirmThenBuilder( action )
.setMessageID( msgID )
;
}
public NotAgainBuilder makeNotAgainBuilder( String msg, int key,
Action action )
private Builder makeNotAgainBuilder( int key )
{
return new NotAgainBuilder( msg, key, action );
return new Builder( DlgID.DIALOG_NOTAGAIN )
.setNAKey( key )
.setAction( Action.SKIP_CALLBACK )
.setTitle( R.string.newbie_title )
;
}
public NotAgainBuilder makeNotAgainBuilder( String msg, int key )
public Builder makeNotAgainBuilder( String msg, int key, Action action )
{
return new NotAgainBuilder( msg, key );
return makeNotAgainBuilder( key )
.setMessage( msg )
.setAction( action )
;
}
public NotAgainBuilder makeNotAgainBuilder( int msgId, int key ) {
return new NotAgainBuilder( msgId, key );
public Builder makeNotAgainBuilder( int msgID, int key, Action action )
{
return makeNotAgainBuilder( key )
.setMessageID( msgID )
.setAction( action )
;
}
public Builder makeNotAgainBuilder( String msg, int key )
{
return makeNotAgainBuilder( key )
.setMessage( msg )
;
}
public Builder makeNotAgainBuilder( int msgID, int key )
{
return makeNotAgainBuilder( key )
.setMessageID( msgID )
;
}
public static final int SMS_BTN = AlertDialog.BUTTON_POSITIVE;
@ -309,11 +335,10 @@ public class DlgDelegate {
void inviteChoiceMade( Action action, InviteMeans means, Object... params );
}
public interface HasDlgDelegate {
OkOnlyBuilder makeOkOnlyBuilder( int msgID );
OkOnlyBuilder makeOkOnlyBuilder( String msg );
NotAgainBuilder makeNotAgainBuilder( int msgID, int prefsKey,
Action action );
NotAgainBuilder makeNotAgainBuilder( int msgID, int prefsKey );
Builder makeOkOnlyBuilder( int msgID );
Builder makeOkOnlyBuilder( String msg );
Builder makeNotAgainBuilder( int msgID, int prefsKey, Action action );
Builder makeNotAgainBuilder( int msgID, int prefsKey );
}
private Activity m_activity;
@ -337,19 +362,6 @@ public class DlgDelegate {
stopProgress();
}
private void showOKOnlyDialogThen( String msg, Action action,
ActionPair more, Object[] params,
int titleId )
{
DlgState state = new DlgState( DlgID.DIALOG_OKONLY )
.setMsg( msg )
.setParams( params )
.setTitle( titleId )
.setActionPair( more )
.setAction(action);
m_dlgt.show( state );
}
// Puts up alert asking to choose a reason to enable SMS, and on dismiss
// calls onPosButton/onNegButton with the action and in params a Boolean
// indicating whether enabling is now ok.
@ -361,62 +373,6 @@ public class DlgDelegate {
m_dlgt.show( state );
}
private void showNotAgainDlgThen( String msg, int prefsKey,
final Action action, ActionPair more,
final Object[] params )
{
if ( 0 != prefsKey
&& XWPrefs.getPrefsBoolean( m_activity, prefsKey, false ) ) {
// If it's set, do the action without bothering with the
// dialog
if ( Action.SKIP_CALLBACK != action ) {
post( new Runnable() {
public void run() {
XWActivity xwact = (XWActivity)m_activity;
xwact.onPosButton( action, params );
}
});
}
} else {
DlgState state = new DlgState( DlgID.DIALOG_NOTAGAIN )
.setMsg( msg)
.setPrefsKey( prefsKey )
.setAction( action )
.setActionPair( more )
.setParams( params );
m_dlgt.show( state );
}
}
private void showConfirmThen( int nakey, Action onNA, String msg,
int posButton, int negButton,
final Action action, int titleId,
ActionPair more, final Object[] params )
{
if ( 0 == nakey ||
! XWPrefs.getPrefsBoolean( m_activity, nakey, false ) ) {
DlgState state = new DlgState( DlgID.CONFIRM_THEN )
.setOnNA( onNA )
.setMsg( msg )
.setPosButton( posButton )
.setNegButton( negButton )
.setAction( action )
.setTitle( titleId )
.setActionPair( more )
.setParams( params )
.setPrefsKey( nakey )
;
m_dlgt.show( state );
} else if ( Action.SKIP_CALLBACK != action ) {
post( new Runnable() {
public void run() {
XWActivity xwact = (XWActivity)m_activity;
xwact.onDismissed( action, params );
}
});
}
}
public void showInviteChoicesThen( final Action action,
SentInvitesInfo info )
{

View file

@ -1,6 +1,7 @@
/* -*- compile-command: "find-and-gradle.sh inXw4dDebug"; -*- */
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
/*
* Copyright 2017 by Eric House (xwords@eehouse.org). All rights reserved.
* Copyright 2017 - 2020 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
@ -37,13 +38,20 @@ import org.eehouse.android.xw4.loc.LocUtils;
/** Abstract superclass for Alerts that have moved from and are still created
* inside DlgDelegate
*/
abstract class DlgDelegateAlert extends XWDialogFragment {
public class DlgDelegateAlert extends XWDialogFragment {
private static final String TAG = DlgDelegateAlert.class.getSimpleName();
private static final String STATE_KEY = "STATE_KEY";
private DlgState m_state;
public DlgDelegateAlert() {}
public static DlgDelegateAlert newInstance( DlgState state )
{
DlgDelegateAlert result = new DlgDelegateAlert();
result.addStateArgument( state );
return result;
}
protected final DlgState getState( Bundle sis )
{
if ( m_state == null ) {
@ -63,18 +71,37 @@ abstract class DlgDelegateAlert extends XWDialogFragment {
setArguments( state.toBundle() );
}
abstract void populateBuilder( Context context, DlgState state,
AlertDialog.Builder builder );
protected void populateBuilder( Context context, DlgState state,
AlertDialog.Builder builder )
{
Log.d( TAG, "populateBuilder()" );
NotAgainView naView = addNAView( state, builder );
OnClickListener lstnr = mkCallbackClickListener( naView );
if ( 0 != state.m_posButton ) {
builder.setPositiveButton( state.m_posButton, lstnr );
}
if ( 0 != state.m_negButton ) {
builder.setNegativeButton( state.m_negButton, lstnr );
}
if ( null != state.m_pair ) {
ActionPair pair = state.m_pair;
builder.setNeutralButton( pair.buttonStr,
mkCallbackClickListener( pair, naView ) );
}
}
Dialog create( AlertDialog.Builder builder ) { return builder.create(); }
protected NotAgainView addNAView( DlgState state, AlertDialog.Builder builder )
private NotAgainView addNAView( DlgState state, AlertDialog.Builder builder )
{
Context context = getActivity();
NotAgainView naView =
((NotAgainView)LocUtils.inflate( context, R.layout.not_again_view ))
.setMessage( state.m_msg )
.setShowNACheckbox( 0 != state.m_prefsNAKey );
.setShowNACheckbox( 0 != state.m_prefsNAKey )
;
builder.setView( naView );
@ -89,6 +116,10 @@ abstract class DlgDelegateAlert extends XWDialogFragment {
AlertDialog.Builder builder = LocUtils.makeAlertBuilder( context );
if ( 0 != state.m_titleId ) {
builder.setTitle( state.m_titleId );
}
populateBuilder( context, state, builder );
return create( builder );
@ -131,9 +162,6 @@ abstract class DlgDelegateAlert extends XWDialogFragment {
if ( 0 != state.m_prefsNAKey ) {
XWPrefs.setPrefsBoolean( getActivity(), m_state.m_prefsNAKey,
true );
} else if ( null != state.m_onNAChecked ) {
DlgClickNotify notify = (DlgClickNotify)getActivity();
notify.onPosButton( m_state.m_onNAChecked );
}
}
}

View file

@ -46,7 +46,6 @@ public class DlgState implements Parcelable {
public int m_prefsNAKey;
// These can't be serialized!!!!
public Object[] m_params;
public Action m_onNAChecked;
public int m_titleId;
public DlgState( DlgID dlgID )
@ -56,7 +55,7 @@ public class DlgState implements Parcelable {
public DlgState setMsg( String msg )
{ m_msg = msg; return this; }
public DlgState setPrefsKey( int key )
public DlgState setPrefsNAKey( int key )
{ m_prefsNAKey = key; return this; }
public DlgState setAction( Action action )
{ m_action = action; return this; }
@ -76,8 +75,6 @@ public class DlgState implements Parcelable {
}
public DlgState setActionPair( ActionPair pair )
{ m_pair = pair; return this; }
public DlgState setOnNA( Action na )
{ m_onNAChecked = na; return this; }
public DlgState setPosButton( int id )
{ m_posButton = id; return this; }
public DlgState setNegButton( int id )
@ -88,6 +85,7 @@ public class DlgState implements Parcelable {
@Override
public String toString()
{
String result;
if ( BuildConfig.DEBUG) {
String params = "";
if ( null != m_params ) {
@ -97,14 +95,22 @@ public class DlgState implements Parcelable {
}
params = TextUtils.join( ",", strs );
}
return String.format("[id: %s; msg: %s; key: %s; action: %s; pair %s; "
+ "na: %s; pos: %d; neg: %d; title: %d; "
+ "params: %s]",
m_id, m_msg, m_prefsNAKey, m_action, m_pair, m_onNAChecked,
m_posButton, m_negButton, m_titleId, params );
result = new StringBuffer()
.append("{id: ").append(m_id)
.append(", msg: \"").append(m_msg)
.append("\", naKey: ").append(m_prefsNAKey)
.append(", action: ").append(m_action)
.append(", pair ").append(m_pair)
.append(", pos: ").append(m_posButton)
.append(", neg: ").append(m_negButton)
.append(", title: ").append(m_titleId)
.append(", params: [").append(params)
.append("]}")
.toString();
} else {
return super.toString();
result = super.toString();
}
return result;
}
// I only need this if BuildConfig.DEBUG is true...
@ -125,7 +131,6 @@ public class DlgState implements Parcelable {
&& ((null == m_pair) ? (null == other.m_pair) : m_pair.equals(other.m_pair))
&& m_prefsNAKey == other.m_prefsNAKey
&& Arrays.deepEquals( m_params, other.m_params )
&& m_onNAChecked == other.m_onNAChecked
&& m_titleId == other.m_titleId;
}
} else {
@ -159,7 +164,6 @@ public class DlgState implements Parcelable {
out.writeInt( m_negButton );
out.writeInt( null == m_action ? -1 : m_action.ordinal() );
out.writeInt( m_prefsNAKey );
out.writeInt( null == m_onNAChecked ? -1 : m_onNAChecked.ordinal() );
out.writeInt( m_titleId );
out.writeString( m_msg );
out.writeSerializable( m_params );
@ -168,7 +172,7 @@ public class DlgState implements Parcelable {
private void testCanParcelize()
{
if (BuildConfig.DEBUG) {
if ( BuildConfig.DEBUG ) {
Parcel parcel = Parcel.obtain();
writeToParcel(parcel, 0);
@ -192,8 +196,6 @@ public class DlgState implements Parcelable {
int tmp = in.readInt();
Action action = 0 > tmp ? null : Action.values()[tmp];
int prefsKey = in.readInt();
tmp = in.readInt();
Action onNA = 0 > tmp ? null : Action.values()[tmp];
int titleId = in.readInt();
String msg = in.readString();
Object[] params = (Object[])in.readSerializable();
@ -203,8 +205,7 @@ public class DlgState implements Parcelable {
.setPosButton( posButton )
.setNegButton( negButton )
.setAction( action )
.setPrefsKey( prefsKey )
.setOnNA( onNA )
.setPrefsNAKey( prefsKey )
.setTitle(titleId)
.setParams(params)
.setActionPair(pair)

View file

@ -462,6 +462,7 @@ public class GameListItem extends LinearLayout
}
long m_rowid;
GameListItem m_item;
int m_nTries = 0;
}
private static LinkedBlockingQueue<ThumbQueueElem> s_queue
= new LinkedBlockingQueue<>();
@ -489,6 +490,11 @@ public class GameListItem extends LinearLayout
ThumbQueueElem elem;
try {
elem = s_queue.take();
if ( 0 < elem.m_nTries ) {
// This is a second pass. give whatever caused
// failure time to go away
Thread.sleep(200);
}
} catch ( InterruptedException ie ) {
Log.w( TAG, "interrupted; killing s_thumbThread" );
break;
@ -501,16 +507,22 @@ public class GameListItem extends LinearLayout
thumb = GameUtils.loadMakeBitmap( activity, rowid );
}
final GameListItem item = elem.m_item;
final Bitmap ft = thumb;
activity.runOnUiThread( new Runnable() {
public void run() {
ImageView iview = item.m_thumb;
if ( null != iview ) {
iview.setImageBitmap( ft );
if ( null == thumb ) {
if ( ++elem.m_nTries < 3 ) {
s_queue.add( elem );
}
} else {
final GameListItem item = elem.m_item;
final Bitmap ft = thumb;
activity.runOnUiThread( new Runnable() {
public void run() {
ImageView iview = item.m_thumb;
if ( null != iview ) {
iview.setImageBitmap( ft );
}
}
}
});
});
}
}
}
});

View file

@ -1452,15 +1452,6 @@ public class GamesListDelegate extends ListDelegateBase
}
break;
case SET_NA_DEFAULTNAME:
XWPrefs.setPrefsBoolean( m_activity, R.string.key_notagain_dfltname,
true );
break;
case SET_GOT_LANGDICT:
XWPrefs.setPrefsBoolean( m_activity, R.string.key_got_langdict,
true );
break;
default:
handled = super.onPosButton( action, params );
}
@ -2001,7 +1992,7 @@ public class GamesListDelegate extends ListDelegateBase
// DEBUG only
case R.id.games_game_markbad:
Quarantine.recordOpened( selRowIDs[0] );
Quarantine.markBad( selRowIDs[0] );
break;
default:
@ -2438,7 +2429,7 @@ public class GamesListDelegate extends ListDelegateBase
boolean handled = false;
String msg = intent.getStringExtra( ALERT_MSG );
if ( null != msg ) {
DlgDelegate.DlgDelegateBuilder builder =
DlgDelegate.Builder builder =
makeOkOnlyBuilder( msg );
if ( intent.getBooleanExtra( WITH_EMAIL, false ) ) {
builder.setActionPair( Action.SEND_EMAIL,
@ -2497,7 +2488,8 @@ public class GamesListDelegate extends ListDelegateBase
xlateLang( lang ) );
makeConfirmThenBuilder( msg, Action.DWNLD_LOC_DICT )
.setPosButton( R.string.button_download )
.setOnNA( Action.SET_GOT_LANGDICT )
.setNegButton( R.string.button_no )
.setNAKey( R.string.key_got_langdict )
.setParams( lang, name )
.show();
}
@ -2754,7 +2746,7 @@ public class GamesListDelegate extends ListDelegateBase
String msg = getQuantityString( R.plurals.confirm_reset_fmt,
rowIDs.length, rowIDs.length );
makeConfirmThenBuilder( msg, Action.RESET_GAMES )
.setPosButton(R.string.button_reset)
.setPosButton( R.string.button_reset )
.setParams( rowIDs )
.show();
}
@ -2798,7 +2790,7 @@ public class GamesListDelegate extends ListDelegateBase
name2 );
makeConfirmThenBuilder( msg, Action.NEW_GAME_DFLT_NAME )
.setOnNA( Action.SET_NA_DEFAULTNAME )
.setNAKey( R.string.key_notagain_dfltname )
.setNegButton( R.string.button_later )
.setParams( name, doConfigure )
.show();

View file

@ -1,54 +0,0 @@
/* -*- compile-command: "find-and-gradle.sh inXw4dDebug"; -*- */
/*
* Copyright 2017 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.
*/
package org.eehouse.android.xw4;
import android.app.AlertDialog;
import android.content.Context;
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
public class NotAgainAlert extends DlgDelegateAlert {
private static final String TAG = NotAgainAlert.class.getSimpleName();
public static NotAgainAlert newInstance( DlgState state )
{
NotAgainAlert result = new NotAgainAlert();
result.addStateArgument( state );
return result;
}
public NotAgainAlert() {}
@Override
public void populateBuilder( Context context, DlgState state,
AlertDialog.Builder builder )
{
NotAgainView naView = addNAView( state, builder );
builder.setTitle( R.string.newbie_title )
.setPositiveButton( android.R.string.ok,
mkCallbackClickListener( naView ) );
if ( null != state.m_pair ) {
ActionPair pair = state.m_pair;
builder.setNegativeButton( pair.buttonStr,
mkCallbackClickListener( pair, naView ) );
}
}
}

View file

@ -1,53 +0,0 @@
/* -*- compile-command: "find-and-gradle.sh inXw4dDebug"; -*- */
/*
* Copyright 2017 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.
*/
package org.eehouse.android.xw4;
import android.app.AlertDialog;
import android.content.Context;
import org.eehouse.android.xw4.DlgDelegate.ActionPair;
public class OkOnlyAlert extends DlgDelegateAlert {
private static final String TAG = OkOnlyAlert.class.getSimpleName();
public static OkOnlyAlert newInstance( DlgState state )
{
OkOnlyAlert result = new OkOnlyAlert();
result.addStateArgument( state );
return result;
}
public OkOnlyAlert() {}
@Override
public void populateBuilder( Context context, DlgState state,
AlertDialog.Builder builder )
{
builder.setTitle( state.m_titleId == 0 ? R.string.info_title : state.m_titleId )
.setMessage( state.m_msg )
.setPositiveButton( android.R.string.ok, null )
;
ActionPair pair = state.m_pair;
if ( null != pair ) {
builder.setNeutralButton( pair.buttonStr, mkCallbackClickListener( pair ) );
}
}
}

View file

@ -26,11 +26,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.ConfirmThenBuilder;
import org.eehouse.android.xw4.DlgDelegate.NotAgainBuilder;
import org.eehouse.android.xw4.DlgDelegate.OkOnlyBuilder;
import org.eehouse.android.xw4.DlgDelegate.Builder;
import org.eehouse.android.xw4.loc.LocUtils;
public class PrefsActivity extends PreferenceActivity
@ -102,32 +99,32 @@ public class PrefsActivity extends PreferenceActivity
m_dlgt.onActivityResult( rc, resultCode, data );
}
public OkOnlyBuilder makeOkOnlyBuilder( int msgId )
public Builder makeOkOnlyBuilder( int msgID )
{
return m_dlgt.makeOkOnlyBuilder( msgId );
return m_dlgt.makeOkOnlyBuilder( msgID );
}
public OkOnlyBuilder makeOkOnlyBuilder( String msg )
public Builder makeOkOnlyBuilder( String msg )
{
return m_dlgt.makeOkOnlyBuilder( msg );
}
public NotAgainBuilder makeNotAgainBuilder(int msgId, int key, Action action)
public Builder makeNotAgainBuilder(int msgID, int key, Action action)
{
return m_dlgt.makeNotAgainBuilder( msgId, key, action );
return m_dlgt.makeNotAgainBuilder( msgID, key, action );
}
public NotAgainBuilder makeNotAgainBuilder( int msgId, int key )
public Builder makeNotAgainBuilder( int msgID, int key )
{
return m_dlgt.makeNotAgainBuilder( msgId, key );
return m_dlgt.makeNotAgainBuilder( msgID, key );
}
public ConfirmThenBuilder makeConfirmThenBuilder(String msg, Action action)
public Builder makeConfirmThenBuilder( String msg, Action action )
{
return m_dlgt.makeConfirmThenBuilder( msg, action );
}
public ConfirmThenBuilder makeConfirmThenBuilder(int msgID, Action action)
public Builder makeConfirmThenBuilder( int msgID, Action action )
{
return m_dlgt.makeConfirmThenBuilder( msgID, action );
}

View file

@ -102,6 +102,7 @@ public class PrefsDelegate extends DelegateBase
R.string.key_empty,
R.string.key_background,
R.string.key_clr_bonushint,
R.string.key_cellline,
};
for ( int colorKey : colorKeys ) {
editor.remove( getString(colorKey) );
@ -242,7 +243,9 @@ public class PrefsDelegate extends DelegateBase
case R.string.key_mqtt_host:
case R.string.key_mqtt_port:
case R.string.key_mqtt_qos:
MQTTUtils.onConfigChanged( m_activity );
if ( BuildConfig.OFFER_MQTT ) {
MQTTUtils.onConfigChanged( m_activity );
}
break;
default:
Assert.failDbg();

View file

@ -30,6 +30,7 @@ import java.io.Serializable;
public class Quarantine {
private static final String TAG = Quarantine.class.getSimpleName();
private static final String DATA_KEY = TAG + "/key";
private static final int BAD_COUNT = 2;
public static boolean safeToOpen( long rowid )
{
@ -37,7 +38,7 @@ public class Quarantine {
synchronized ( sDataRef ) {
count = get().getFor( rowid );
}
boolean result = count == 0; // Not too strict?
boolean result = count < BAD_COUNT;
if ( !result ) {
Log.d( TAG, "safeToOpen(%d) => %b (count=%d)", rowid, result, count );
}
@ -70,6 +71,17 @@ public class Quarantine {
}
}
public static void markBad( long rowid )
{
synchronized ( sDataRef ) {
for ( int ii = 0; ii < BAD_COUNT; ++ii ) {
get().increment( rowid );
}
store();
Log.d( TAG, "markBad(%d): %s", rowid, sDataRef[0].toString() );
}
}
private static class Data implements Serializable {
private HashMap<Long, Integer> mCounts = new HashMap<>();

View file

@ -204,23 +204,22 @@ public class XWActivity extends FragmentActivity
// This are a hack! I need some way to build fragment-based alerts from
// inside fragment-based alerts.
public DlgDelegate.NotAgainBuilder makeNotAgainBuilder( String msg, int keyId )
public DlgDelegate.Builder makeNotAgainBuilder( String msg, int keyID )
{
return m_dlgt.makeNotAgainBuilder( msg, keyId );
return m_dlgt.makeNotAgainBuilder( msg, keyID );
}
public DlgDelegate.NotAgainBuilder makeNotAgainBuilder( int msgID, int keyId )
public DlgDelegate.Builder makeNotAgainBuilder( int msgID, int keyID )
{
return m_dlgt.makeNotAgainBuilder( msgID, keyId );
return m_dlgt.makeNotAgainBuilder( msgID, keyID );
}
public DlgDelegate.ConfirmThenBuilder makeConfirmThenBuilder( int msgID,
Action action )
public DlgDelegate.Builder makeConfirmThenBuilder( int msgID, Action action )
{
return m_dlgt.makeConfirmThenBuilder( msgID, action );
}
public DlgDelegate.OkOnlyBuilder makeOkOnlyBuilder( int msgID )
public DlgDelegate.Builder makeOkOnlyBuilder( int msgID )
{
return m_dlgt.makeOkOnlyBuilder( msgID );
}
@ -288,7 +287,7 @@ public class XWActivity extends FragmentActivity
df.show( fm, tag );
}
} catch (IllegalStateException ise ) {
Log.d( TAG, "error showing tag %s (df: %s)", tag, df );
Log.d( TAG, "error showing tag %s (df: %s; msg: %s)", tag, df, ise );
// DLG_SCORES is causing this for non-belongsOnBackStack() case
// Assert.assertFalse( BuildConfig.DEBUG );
}

View file

@ -102,7 +102,9 @@ public class XWApp extends Application
DupeModeTimer.init( this );
MQTTUtils.init( this );
if ( BuildConfig.OFFER_MQTT ) {
MQTTUtils.init( this );
}
}
@OnLifecycleEvent(ON_ANY)

View file

@ -207,19 +207,20 @@ public class XWPrefs {
public static int getPrefsInt( Context context, int keyID, int defaultValue )
{
String key = context.getString( keyID );
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences( context );
int result;
try {
result = sp.getInt( key, defaultValue );
// If it's in a pref, it'll be a string (editable) So will get CCE
} catch ( ClassCastException cce ) {
String asStr = sp.getString( key, String.format( "%d", defaultValue ) );
int result = defaultValue;
if ( null != context ) {
String key = context.getString( keyID );
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences( context );
try {
result = Integer.parseInt( asStr );
} catch ( Exception ex ) {
result = defaultValue;
result = sp.getInt( key, defaultValue );
// If it's in a pref, it'll be a string (editable) So will get CCE
} catch ( ClassCastException cce ) {
String asStr = sp.getString( key, String.format( "%d", defaultValue ) );
try {
result = Integer.parseInt( asStr );
} catch ( Exception ex ) {
}
}
}
return result;

View file

@ -40,7 +40,8 @@ public class CommonPrefs extends XWPrefs {
public static final int COLOR_FOCUS = 2;
public static final int COLOR_BACKGRND = 3;
public static final int COLOR_BONUSHINT = 4;
public static final int COLOR_LAST = 5;
public static final int COLOR_CELLLINE = 5;
public static final int COLOR_LAST = 6;
private static CommonPrefs s_cp = null;
@ -108,6 +109,7 @@ public class CommonPrefs extends XWPrefs {
R.string.key_clr_crosshairs,
R.string.key_background,
R.string.key_clr_bonushint,
R.string.key_cellline,
};
for ( int ii = 0; ii < idsOther.length; ++ii ) {
otherColors[ii] = prefToColor( context, sp, idsOther[ii] );

View file

@ -1,4 +0,0 @@
<vector android:height="96dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="96dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M440,228.246l14.06,3.515a8.005,8.005 0,0 0,5.519 -0.605l32,-16a8,8 0,0 0,3.85 -10.126l-19.113,-47.782 3.645,-36.451a7.997,7.997 0,0 0,-0.805 -4.374l-16,-32a7.999,7.999 0,0 0,-6.023 -4.342l-56,-8a8.015,8.015 0,0 0,-2.124 -0.019L344,78.938v-27.043A35.894,35.894 0,0 0,274.054 40.543l-1.046,3.136 -14.665,14.664A8.001,8.001 0,0 0,264 72h16L280,88a31.792,31.792 0,0 0,3.575 14.669l-40.012,26.675a7.998,7.998 0,0 0,-2.718 3.078l-32,64a8.005,8.005 0,0 0,-0.812 4.303l7.788,85.67 -14.373,7.739 -19.791,-19.791a8.002,8.002 0,0 0,-9.626 -1.289l-13.395,7.654a127.825,127.825 0,0 0,-59.81 76.276l-1.984,6.944a51.601,51.601 0,0 0,-13.614 2.562l-12.583,4.194a35.471,35.471 0,0 1,-27.261 -1.937l-15.807,-7.903A8,8 0,0 0,16 368L16,488a8,8 0,0 0,8 8L312,496a8.001,8.001 0,0 0,7.692 -5.802l31.657,-110.8 15.122,-22.684 9.938,29.815a8.014,8.014 0,0 0,1.249 2.348l80,104A8,8 0,0 0,464 496h24a8,8 0,0 0,7.397 -11.046L440,350.417L440,228.246ZM477.838,204.137 L455.069,215.521 425.94,208.239a8,8 0,0 0,-5.938 0.831l-24.32,14.031 -7.546,-11.319L411.793,199.044a8.006,8.006 0,0 0,3.797 -4.514l7.132,-21.396 5.813,-3.875 3.514,31.625A8.001,8.001 0,0 0,440 208h24a7.999,7.999 0,0 0,7.96 -7.204l0.908,-9.084ZM241.089,312.289L232.687,295.485l141.299,-76.084 7.808,11.712ZM270.983,213.452 L292.951,202.469L312.177,234.511l-52.108,28.058ZM294.381,114.694a31.969,31.969 0,0 0,48.8 -19.529L352,94.063L352,130.584l-30.073,12.029L294.157,114.843ZM314.343,157.657a8,8 0,0 0,8.628 1.77l40,-16A7.999,7.999 0,0 0,368 136L368,92.063l5.3,-0.663 27.207,47.613 7.167,28.666L401.414,186.46 326.28,226.917l-23.264,-38.774 -29.702,-59.404 7.266,-4.844ZM417.558,141.247 L425.142,138.719 426.546,151.355 421.008,155.047ZM463.81,121.508L456.76,192h-9.6l-6.911,-62.199 15.113,-25.188ZM399.929,88.071l43.012,6.145L426.662,121.346l-14.97,4.99L390.5,89.25ZM285.657,53.657a8.007,8.007 0,0 0,1.932 -3.127l1.644,-4.927A19.894,19.894 0,0 1,328 51.895L328,56L283.313,56ZM328,72L328,88a16,16 0,0 1,-32 0L296,72ZM254.205,141.478 L259.897,137.683 285.267,188.422 264.351,198.88 234.468,180.951ZM224.173,201.542l3.119,-6.237 27.648,16.589 -13.489,60.7L231.137,278.147ZM114.212,361.38a111.911,111.911 0,0 1,52.362 -66.779l8.09,-4.623 19.679,19.68a8,8 0,0 0,9.45 1.387l14.8,-7.969 8.612,17.225 -15.203,8.771a7.999,7.999 0,0 0,-3.763 8.87l6.443,25.773L214.11,364a112.252,112.252 0,0 1,-100.646 -0.003ZM32,480L32,380.944l4.229,2.114a51.374,51.374 0,0 0,39.478 2.806l12.582,-4.194a35.804,35.804 0,0 1,28.636 2.661l44.959,24.978a35.751,35.751 0,0 1,13.278 12.844l3.047,5.078a51.603,51.603 0,0 0,51.653 24.585,35.872 35.872,0 0,1 34.763,15.542L273.052,480ZM227.599,435.976a35.635,35.635 0,0 1,-35.67 -16.978l-3.047,-5.077a51.779,51.779 0,0 0,-19.229 -18.599l-6.144,-3.414c0.09,0 0.179,0.004 0.269,0.004a128.43,128.43 0,0 0,57.487 -13.603l6.312,-3.155a7.998,7.998 0,0 0,4.184 -9.096l-6.536,-26.146L313.26,289.125l-24.779,68.141a8.036,8.036 0,0 0,-0.466 2.235l-6.52,104.32 -3.559,-5.339A51.971,51.971 0,0 0,227.599 435.976ZM337.344,371.563a8.014,8.014 0,0 0,-1.036 2.239L305.966,480h-9.45l7.396,-118.35 30.894,-84.956L424,225.237L424,275.471l-36.116,21.669a8.005,8.005 0,0 0,-2.54 2.423ZM476.055,480h-8.115l-76.776,-99.809 -13.447,-40.343 19.942,-29.914L424,294.13L424,352a8.006,8.006 0,0 0,0.603 3.046Z"/>
</vector>

View file

@ -66,7 +66,6 @@
</RelativeLayout>
<ImageView android:id="@+id/thumbnail"
android:src="@drawable/ic_uc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"

View file

@ -31,8 +31,10 @@
<string name="key_tile_back">key_clr_tile_back</string>
<string name="key_empty">key_clr_empty</string>
<string name="key_background">key_clr_background</string>
<string name="key_cellline">key_cellline</string>
<string name="key_clr_crosshairs">key_clr_crosshairs</string>
<string name="key_clr_bonushint">key_clr_bonushint</string>
<string name="key_board_line_pct">key_board_line_pct</string>
<string name="key_relay_host">key_relay_host</string>
<string name="key_relay_port">key_relay_port2</string>

View file

@ -850,6 +850,9 @@
<string name="keep_screenon">Keep screen on</string>
<!-- clarification of above -->
<string name="keep_screenon_summary">Keep board screen on 10 mins</string>
<!-- Preference title: cell line thickness as pct of cell width -->
<string name="board_line_pct">Cell line thickness (percent, limit 1 to 25)</string>
<!--
############################################################
# :Screens:
@ -889,6 +892,8 @@
<!-- the color of text, e.g. "2L", shown on a bonus square on the
board -->
<string name="key_bonushint">In-square bonus hint</string>
<!-- The color of the lines delimiting cells on the board -->
<string name="cellline">Board lines</string>
<!--
############################################################
# :Dialogs:
@ -1496,8 +1501,7 @@
Eric House and translators -->
<string name="about_credits">Toolbar icons by Sarah Chu. Navbar
icons from the Noun Project: \"archive\" by Trendy; \"rematch\" by
Becris; and \"swap\" by iconomania. \"Under Construction\" icon by
ultimatearm from flaticon.com</string>
Becris; and \"swap\" by iconomania.</string>
<!-- text of dialog showing the set of changes made since the last
release -->
<string name="changes_title">Recent changes</string>
@ -2058,7 +2062,7 @@
wordlists and view the ones you already have.\n\nWhat wordlists
you have installed determines:\n• What languages you can play
in\n• How smart the robot player is\n• What words are
legal.\n\nCheck the \"Show downloadable\" box at the top to see
legal\n\nCheck the \"Show downloadable\" box at the top to see
what\'s available.</string>
<string name="force_tablet_title">Use tablet (side-by-side) layout?</string>
<string name="force_tablet_default">Use default for my device</string>

View file

@ -241,8 +241,21 @@
android:title="@string/background"
android:defaultValue="0xFFFFFF"
/>
<org.eehouse.android.xw4.EditColorPreference
android:key="@string/key_cellline"
android:title="@string/cellline"
android:defaultValue="0x101010"
/>
</PreferenceScreen>
<!-- <org.eehouse.android.xw4.XWEditTextPreference -->
<!-- android:key="@string/key_board_line_pct" -->
<!-- android:title="@string/board_line_pct" -->
<!-- android:defaultValue="1" -->
<!-- android:numeric="decimal" -->
<!-- /> -->
</PreferenceScreen>
<PreferenceScreen android:title="@string/prefs_behavior"

View file

@ -0,0 +1 @@
../../../../../../xw4SMS/java/org/eehouse/android/xw4/MQTTUtils.java

View file

@ -0,0 +1 @@
../../../../../../xw4SMS/java/org/eehouse/android/xw4/MQTTUtils.java

View file

@ -0,0 +1,81 @@
/* -*- compile-command: "find-and-gradle.sh -PuseCrashlytics insXw4dDeb"; -*- */
/*
* Copyright 2020 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.
*/
package org.eehouse.android.xw4;
import android.content.Context;
import org.eehouse.android.xw4.jni.CommsAddrRec;
public class MQTTUtils {
private static final String TAG = MQTTUtils.class.getSimpleName();
public static void init( Context context )
{
logUnimpl( "init" );
}
public static void onResume( Context context )
{
logUnimpl( "onResume" );
}
public static int send( Context context, String addressee, int gameID, byte[] buf )
{
logUnimpl( "send" );
return -1;
}
public static void handleMessage( Context context, CommsAddrRec from,
int gameID, byte[] data )
{
logUnimpl( "handleMessage" );
}
public static void handleGameGone( Context context, CommsAddrRec from, int gameID )
{
logUnimpl( "handleGameGone" );
}
public static void gameDied( String devID, int gameID )
{
logUnimpl( "gameDied" );
}
public static void timerFired( Context context )
{
logUnimpl( "timerFired" );
}
static void onConfigChanged( Context context )
{
logUnimpl( "onConfigChanged" );
}
public static void inviteRemote( Context context, String invitee, NetLaunchInfo nli )
{
logUnimpl( "inviteRemote" );
}
private static void logUnimpl( String name )
{
Log.d( TAG, "%s(): UNIMPLEMENTED", name );
}
}

View file

@ -1,2 +0,0 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" id="outline" viewBox="0 0 512 512" width="512" height="512"><path d="M440,228.24609l14.05957,3.51465a8.00478,8.00478,0,0,0,5.51855-.60547l32-16a8.00029,8.00029,0,0,0,3.84961-10.126l-19.11279-47.78223,3.645-36.45117a7.99681,7.99681,0,0,0-.80469-4.374l-16-32a7.99926,7.99926,0,0,0-6.02343-4.3418l-56-8a8.015,8.015,0,0,0-2.124-.01856L344,78.9375v-27.043A35.89421,35.89421,0,0,0,274.05371,40.543l-1.0459,3.13574-14.665,14.66406A8.00066,8.00066,0,0,0,264,72h16V88a31.79215,31.79215,0,0,0,3.57471,14.66895l-40.01221,26.6748a7.99763,7.99763,0,0,0-2.71777,3.07813l-32,64a8.00531,8.00531,0,0,0-.81153,4.30273l7.78809,85.66992-14.37305,7.73926-19.791-19.791a8.00152,8.00152,0,0,0-9.626-1.28906l-13.39453,7.6543a127.82537,127.82537,0,0,0-59.80957,76.27637l-1.98438,6.94433a51.60075,51.60075,0,0,0-13.61425,2.56152l-12.583,4.19434a35.471,35.471,0,0,1-27.26074-1.93652l-15.80665-7.90332A7.99951,7.99951,0,0,0,16,368V488a8.00039,8.00039,0,0,0,8,8H312a8.00054,8.00054,0,0,0,7.69238-5.80176l31.65723-110.7998,15.12207-22.6836,9.93848,29.81543a8.01375,8.01375,0,0,0,1.249,2.34766l80,104A8.00015,8.00015,0,0,0,464,496h24a7.99987,7.99987,0,0,0,7.39746-11.0459L440,350.417V228.24609Zm37.83789-24.10937-22.76855,11.38476-29.12891-7.28222a8.00008,8.00008,0,0,0-5.93848.83105l-24.32007,14.03076-7.54565-11.31884L411.793,199.044a8.00572,8.00572,0,0,0,3.79687-4.51368l7.13184-21.39648,5.81323-3.87549,3.51392,31.62549A8.00054,8.00054,0,0,0,440,208h24a7.99881,7.99881,0,0,0,7.96-7.2041l.90845-9.08374Zm-236.749,108.15234L232.687,295.48535l141.29932-76.084,7.80786,11.71191Zm29.89453-98.83691,21.96777-10.9834L312.17651,234.511l-52.10766,28.05786Zm23.39746-98.75806a31.9685,31.9685,0,0,0,48.80029-19.5293L352,94.0625V130.584l-30.07324,12.0293L294.15723,114.843Zm19.96191,42.96314a8.00019,8.00019,0,0,0,8.62793,1.7705l40-16A7.99873,7.99873,0,0,0,368,136V92.0625l5.3-.6626,27.20727,47.61255,7.16651,28.66626L401.41406,186.46,326.28,226.91675l-23.26441-38.77417-29.7019-59.40381,7.26636-4.84424Zm103.21485-16.41016,7.584-2.52832,1.40405,12.63623-5.53808,3.6919Zm46.252-19.73926L456.75977,192h-9.59961l-6.91065-62.199,15.11255-25.18775ZM399.92871,88.07129l43.01172,6.14453L426.66235,121.346l-14.97046,4.99023L390.49976,89.25ZM285.65723,53.65723a8.00738,8.00738,0,0,0,1.93164-3.127l1.64355-4.92675A19.89417,19.89417,0,0,1,328,51.89453V56H283.31348ZM328,72V88a16,16,0,0,1-32,0V72Zm-73.79492,69.47754,5.69189-3.79468,25.36988,50.7395-20.91626,10.458-29.88233-17.92944ZM224.17285,201.542l3.11865-6.23706,27.64795,16.58887-13.48877,60.70044L231.137,278.14746ZM114.21191,361.37988a111.91061,111.91061,0,0,1,52.36231-66.77929l8.08984-4.62305,19.67871,19.67969a8.00026,8.00026,0,0,0,9.4502,1.38672l14.80005-7.96924,8.6123,17.22461-15.20337,8.771a7.99882,7.99882,0,0,0-3.76269,8.87012l6.44336,25.77344L214.11035,364a112.2521,112.2521,0,0,1-100.64648-.00293ZM32,480V380.94434l4.22852,2.11425a51.37429,51.37429,0,0,0,39.47753,2.80567l12.582-4.19434a35.80373,35.80373,0,0,1,28.63574,2.66113l44.959,24.97754a35.751,35.751,0,0,1,13.27832,12.84375l3.04688,5.07813a51.60349,51.60349,0,0,0,51.65332,24.585,35.87241,35.87241,0,0,1,34.76269,15.542L273.05176,480Zm195.59863-44.02441a35.635,35.635,0,0,1-35.66992-16.97754l-3.04687-5.07715a51.77949,51.77949,0,0,0-19.22852-18.59863l-6.14429-3.41358c.0896.00049.17945.0044.26929.0044a128.43035,128.43035,0,0,0,57.4873-13.60254l6.31153-3.15528a7.99817,7.99817,0,0,0,4.18359-9.0957l-6.53613-26.14551L313.26,289.125l-24.77856,68.14062a8.03576,8.03576,0,0,0-.46583,2.23536l-6.52,104.32031-3.55909-5.33887A51.971,51.971,0,0,0,227.59863,435.97559ZM337.34375,371.5625a8.01379,8.01379,0,0,0-1.03613,2.23926L305.96582,480h-9.4502l7.39649-118.34961,30.89355-84.956L424,225.23657V275.4707l-36.11621,21.66895a8.00472,8.00472,0,0,0-2.54,2.42285ZM476.05469,480h-8.11524l-76.77636-99.80859-13.44727-40.34327,19.94238-29.91357L424,294.12988V352a8.00562,8.00562,0,0,0,.60254,3.0459Z"/></svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -81,7 +81,9 @@ dvc_store( XW_DUtilCtxt* dutil, XWEnv xwe )
#endif
#define MQTT_DEVID_KEY "mqtt_devid_key"
#define SUPPORT_OLD /* only needed for a short while */
#define MQTT_DEVID_KEY_OLD "mqtt_devid_key"
#define MQTT_DEVID_KEY PERSIST_KEY("mqtt_devid_key")
void
dvc_getMQTTDevID( XW_DUtilCtxt* dutil, XWEnv xwe, MQTTDevID* devID )
@ -89,8 +91,20 @@ dvc_getMQTTDevID( XW_DUtilCtxt* dutil, XWEnv xwe, MQTTDevID* devID )
MQTTDevID tmp = 0;
XP_U16 len = sizeof(tmp);
dutil_loadPtr( dutil, xwe, MQTT_DEVID_KEY, &tmp, &len );
#ifdef SUPPORT_OLD
if ( len == 0 ) {
len = sizeof(tmp);
dutil_loadPtr( dutil, xwe, MQTT_DEVID_KEY_OLD, &tmp, &len );
if ( len == sizeof(tmp) ) { /* got the old key; now store it */
XP_LOGFF( "storing using new key" );
dutil_storePtr( dutil, xwe, MQTT_DEVID_KEY, &tmp, sizeof(tmp) );
}
}
#endif
// XP_LOGFF( "len: %d; sizeof(tmp): %d", len, sizeof(tmp) );
if ( len != sizeof(tmp) ) { /* we have it!!! */
if ( len != sizeof(tmp) ) { /* not found, or bogus somehow */
tmp = XP_RANDOM();
tmp <<= 32;
tmp |= XP_RANDOM();

View file

@ -1,5 +1,6 @@
# -*- mode: makefile; compile-command: "make -f Makefile.CollegeEng"; -*-
# Copyright 2002 by Eric House (xwords@eehouse.org). All rights reserved.
# Copyright 2002 - 2020 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
@ -22,10 +23,10 @@ DICTNOTE = "From the old PalmOS app Niggle"
include ../Makefile.langcommon
SOURCEDICT ?= $(XWDICTPATH)/English/CollegeEng.dict.gz
SOURCEDICT ?= $(XWDICTPATH)/English/CollegeEng.dict
$(XWLANG)Main.dict.gz: $(SOURCEDICT) Makefile
zcat $< | tr -d '\r' | tr [a-z] [A-Z] | grep -e "^[A-Z]\{2,15\}$$" | \
cat $< | tr -d '\r' | tr [a-z] [A-Z] | grep -e "^[A-Z]\{2,15\}$$" | \
gzip -c > $@
# Everything but creating of the Main.dict file is inherited from the

View file

@ -3,6 +3,20 @@
LANGCODE:hu_HU
CHARSET: utf-8
BUILD_FLAGS:ALLOWS_DUPLICATES
LANGFILTER: tr -d '\r'
LANGFILTER: | tr [aábcdeéfghiíjklmnnyoóöőprtuúüűvzs] [AÁBCDEÉFGHIÍJKLMNNYOÓÖŐPRTUÚÜŰVZS]
LANGFILTER: | sed -e 's,^\(.*\)CS\(.*\)$,\11\2\n\1CS\2,g'
LANGFILTER: | sed -e 's,GY,2,g'
LANGFILTER: | sed -e 's,LY,3,g'
LANGFILTER: | sed -e 's,NY,4,g'
LANGFILTER: | sed -e 's,^\(.*\)SZ\(.*\)$,\15\2\n\1SZ\2,g'
LANGFILTER: | sed -e 's,TY,6,g'
LANGFILTER: | sed -e 's,^\(.*\)ZS\(.*\)$,\17\2\n\1ZS\2,g'
LANGFILTER: | grep '^[1-7AÁBCDEÉFGHIÍJKLMNOÓÖŐPRSTUÚÜŰVZ]\{2,15\}$'
LANGFILTER: | tr '1234567' '\001\002\003\004\005\006\007'
# High bit means "official". Next 7 bits are an enum where
# Hungarian==0x14. Low byte is padding
XLOC_HEADER:0x9400

View file

@ -1,32 +0,0 @@
# -*-mode: Makefile -*-
# Copyright 2002-2004 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.
XWLANG=Italian
LANGCODE=it_IT
TARGET_TYPE ?= WINCE
include ../Makefile.langcommon
$(XWLANG)Main.dict.gz: $(XWDICTPATH)/Italian/ITALIANO.txt.gz
zcat $< | tr a-z A-Z | grep '^[A-IL-VZ]*$$' | gzip >$@
# Everything but creating of the Main.dict file is inherited from the
# "parent" Makefile.langcommon in the parent directory.
clean: clean_common
rm -f $(XWLANG)Main.dict.gz *.bin $(XWLANG)*.pdb $(XWLANG)*.seb

View file

@ -0,0 +1 @@
./Makefile.zinga

View file

@ -23,7 +23,7 @@ byodfiles.tgz: byodfiles.tar
byodfiles.tar: dict2dawg
rm -f $@ langinfo
tar cvf $@ ./dict2dawg ./dict2dawg.cpp ./par.pl ./xloc.pl ./xloc.pm
tar cvf $@ ./dict2dawg ./dict2dawg.cpp ./par.pl ./xloc.py
for dir in $$(ls .); do \
if [ $$dir = "Hëx" ]; then \
:; \

View file

@ -16,9 +16,11 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
ifndef XWDICTPATH
$(error XWDICTPATH is not set)
endif
# This breaks generating byod files on otherwise-unconfigured
# machines. Move it perhaps?
# ifndef XWDICTPATH
# $(error XWDICTPATH is not set)
# endif
ifneq ($(DIRTY_LIST),)
BOWDLERIZER = ../remove-dirty.py $(DIRTY_LIST)
@ -187,12 +189,13 @@ $(XWLANG)%.atts: #recreate it each time based on params
echo '_PERM|global+read-only|"r"' >> $@
# the files to export for byod
byodbins: table.bin values.bin frankspecials.bin info.txt
byodbins: table.bin charcount.bin values.bin frankspecials.bin info.txt
else
ifeq ($(TARGET_TYPE),WINCE)
byodbins: table.bin charcount.bin values.bin frankspecials.bin info.txt
### WINCE section here ###
all: $(XWLANG)2to8.xwd $(XWLANG)2to9.xwd $(XWLANG)2to15.xwd
../mkxwdcab.pl -f $<
@ -213,11 +216,11 @@ endif
# For each entry in the table whose face < 32, there needs to be a pair of
# pbitm files and a string giving the printing form
frankspecials.bin: ../frank_mkspecials.pl $(BMPFILES)
frankspecials.bin: ../frank_mkspecials.py $(BMPFILES)
$< $(BLANK_INFO) $(LANG_SPECIAL_INFO) > $@
$(XWLANG)%.$(FRANK_EXT): dawg$(XWLANG)%.stamp $(XWLANG)%_flags.bin $(XWLANG)%_newheader.bin $(XWLANG)_charcount.bin table.bin values.bin frankspecials.bin
cat $(XWLANG)$*_flags.bin $(XWLANG)$*_newheader.bin $(XWLANG)_charcount.bin table.bin values.bin \
$(XWLANG)%.$(FRANK_EXT): dawg$(XWLANG)%.stamp $(XWLANG)%_flags.bin $(XWLANG)%_newheader.bin charcount.bin table.bin values.bin frankspecials.bin
cat $(XWLANG)$*_flags.bin $(XWLANG)$*_newheader.bin charcount.bin table.bin values.bin \
frankspecials.bin $(XWLANG)StartLoc.bin \
$$(ls dawg$(XWLANG)$*_*.bin) > $@
cp $@ saveme.bin
@ -254,7 +257,7 @@ dawg$(XWLANG)%.stamp: $(XWLANG)Main.dict.gz $(DICT2DAWG) table.bin ../Makefile.l
start=$$(echo $@ | sed -e 's/dawg$(XWLANG)\([0-9]*\)to[0-9]*.stamp/\1/'); \
end=$$(echo $@ | sed -e 's/dawg$(XWLANG)[0-9]*to\([0-9]*\).stamp/\1/'); \
echo $${start} and $${end}; \
zcat $< | $(BOWDLERIZER) | $(DICT2DAWG) $(DICT2DAWGARGS) $(TABLE_ARG) table.bin -b 28000 \
zcat $< | $(BOWDLERIZER) | $(DICT2DAWG) $(DICT2DAWGARGS) $(TABLE_ARG) table.bin \
-ob dawg$(XWLANG)$* $(ENCP) \
-sn $(XWLANG)StartLoc.bin -min $${start} -max $${end} \
-wc $(XWLANG)$*_wordcount.bin $(FORCE_4) -ns $(XWLANG)$*_nodesize.bin
@ -269,24 +272,24 @@ allbins:
$(MAKE) TARGET_TYPE=FRANK byodbins
rm palmspecials.bin
table.bin: ../xloc.pl
table.bin: ../xloc.py
ifdef NEWDAWG
perl -I../ ../xloc.pl $(ENCP) -tn -out $@
../xloc.py $(ENCP) -tn -out $@
else
perl -I../ ../xloc.pl -t -out $@
error
endif
values.bin: ../xloc.pl
perl -I../ ../xloc.pl -v -out $@
values.bin: ../xloc.py
../xloc.py -v -out $@
# a binary file, two bytes, one giving the size of tiles data and the
# other the number of tiles in the dict. Tiles data is utf-8 and so
# number is not derivable from size.
$(XWLANG)_charcount.bin: table.bin ../xloc.pl
charcount.bin: table.bin ../xloc.py
SIZ=$$(ls -l $< | awk '{print $$5}'); \
perl -e "print pack(\"c\",$$SIZ)" > $@
TMP=/tmp/tmp$$$$; \
perl -I../ ../xloc.pl -s -out $$TMP; \
../xloc.py -s -out $$TMP; \
cat $$TMP >> $@; \
rm -f $$TMP

View file

@ -1,10 +0,0 @@
#!/usr/bin/perl
# Print all ascii characters (for pasting into Makefiles etc. when you
# don't know what key combination produces them)
use strict;
for ( my $i = int(' '); $i <= 255; ++$i ) {
printf "%.3d: %c\n", $i, $i;
}

View file

@ -66,7 +66,8 @@ def eatBitmap( fh ):
if nCols > 0:
nRows = int(oneByteFmt.unpack(fh.read(oneByteFmt.size))[0])
nBytes = ((nRows*nCols)+7) // 8
print('eatBitmap(): skipping', nBytes, 'bytes; nCols:', nCols, 'nRows:', nRows)
print('eatBitmap(): skipping {} bytes; nCols: {}, nRows: {}:'.format(nBytes,nCols, nRows),\
file=sys.stderr)
fh.read(nBytes)
def loadSpecialData( fh, data ):
@ -187,8 +188,7 @@ def process(args):
numFaces = int(oneByteFmt.unpack(dawg.read(oneByteFmt.size))[0])
if not isUTF8:
numFaceBytes = numFaces * 2
if numFaces > 64:
error("too many faces: " + numFaces)
assert numFaces <= 64, 'too many faces: {}'.format(numFaces)
print( 'numFaceBytes: {}, numFaces: {}'.format(numFaceBytes, numFaces), file=sys.stderr )
print( 'TODO: confirm checksum', file=sys.stderr )
@ -227,7 +227,9 @@ def process(args):
# assert len(words) == nWords
if args.DUMP_WORDS:
for word in words:
print(word)
# if we're piped to head we'll get an exception, so just exit
try: print(word)
except: break
def mkParser():
parser = argparse.ArgumentParser()

View file

@ -1,857 +0,0 @@
#!/usr/bin/perl
##############################################################################
# adapted from C++ code Copyright (C) 2000 Falk Hueffner
# This version Copyright (C) 2002 Eric House (xwords@eehouse.org)
#
# 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
##############################################################################
# inputs: 0. Name of file mapping letters to 0..31 values. In English
# case just contains A..Z. This will be used to translate the tries
# on output.
# 1. Max number of bytes per binary output file.
#
# 2. Basename of binary files for output.
# 3. Name of file to which to write the number of the
# startNode, since I'm not rewriting a bunch of code to expect Falk's
# '*' node at the start.
#
# In STDIN, the text file to be compressed. It absolutely
# must be sorted. The sort doesn't have to follow the order in the
# map file, however.
# This is meant eventually to be runnable as part of a cgi system for
# letting users generate Crosswords dicts online.
use strict;
use POSIX;
my $gFirstDiff;
my @gCurrentWord;
my $gCurrentWord; # save so can check for sortedness
my $gDone = 0;
my @gInputStrings;
my $gNeedsSort = 1; # read from cmd line eventually
my @gNodes; # final array of nodes
my $gNBytesPerOutfile = 0xFFFFFFFF;
my $gTableFile;
my $gOutFileBase;
my $gStartNodeOut;
my $gInFileName;
my $gKillIfMissing = 1;
my $gTermChar = '/n';
my $gDumpText = 0; # dump the dict as text after?
my $gCountFile;
my $gBytesPerNodeFile; # where to write whether node size 3 or 4
my $gWordCount = 0;
my %gTableHash;
my $gBlankIndex;
my @gRevMap;
my $debug = 0;
my %gSubsHash;
my $gForceFour = 0; # use four bytes regardless of need?
my $gNBytesPerNode;
my $gUseUnicode;
main();
exit;
##############################################################################
sub main() {
if ( !parseARGV() ) {
usage();
exit();
}
makeTableHash();
my $infile;
if ( $gInFileName ) {
open $infile, "<$gInFileName";
} else {
$infile = \*STDIN;
}
@gInputStrings = parseAndSort( $infile );
if ( $gInFileName ) {
close $infile;
}
# Do I need this stupid thing? Better to move the first row to
# the front of the array and patch everything else. Or fix the
# non-palm dictionary format to include the offset of the first
# node.
my $dummyNode = 0xFFFFFFFF;
@gNodes = ( $dummyNode );
readNextWord();
my $firstRootChildOffset = buildNode(0);
moveTopToFront( \$firstRootChildOffset );
if ( $gStartNodeOut ) {
writeOutStartNode( $gStartNodeOut, $firstRootChildOffset );
}
print STDERR "\n... dumping table ...\n" if $debug;
printNodes( \@gNodes, "done with main" ) if $debug;
# write out the number of nodes if requested
if ( $gCountFile ) {
open OFILE, "> $gCountFile";
print OFILE pack( "N", $gWordCount );
close OFILE;
print STDERR "wrote out: got $gWordCount words\n";
}
if ( $gOutFileBase ) {
emitNodes( $gNBytesPerOutfile, $gOutFileBase );
}
if ( $gDumpText && @gNodes > 0 ) {
printOneLevel( $firstRootChildOffset, "" );
}
if ( $gBytesPerNodeFile ) {
open OFILE, "> $gBytesPerNodeFile";
print OFILE $gNBytesPerNode;
close OFILE;
}
print STDERR "Used $gNBytesPerNode per node.\n";
} # main
# We now have an array of nodes with the last subarray being the
# logical top of the tree. Move them to the start, fixing all fco
# refs, so that legacy code like Palm can assume top==0.
#
# Note: It'd probably be a bit faster to integrate this with emitNodes
# -- unless I need to have an in-memory list that can be used for
# lookups. But that's best for debugging, so keep it this way for now.
#
# Also Note: the first node is a dummy that can and should be tossed
# now.
sub moveTopToFront($) {
my ( $firstRef ) = @_;
my $firstChild = ${$firstRef};
${$firstRef} = 0;
my @lastSub;
if ( $firstChild > 0 ) {
# remove the last (the root) subarray
@lastSub = splice( @gNodes, $firstChild );
} else {
die "there should be no words!!" if $gWordCount != 0;
}
# remove the first (garbage) node
shift @gNodes;
my $diff;
if ( $firstChild > 0 ) {
# -1 because all move down by 1; see prev line
$diff = @lastSub - 1;
die "something wrong with len\n" if $diff < 0;
} else {
$diff = 0;
}
# stick it on the front
splice( @gNodes, 0, 0, @lastSub);
# We add $diff to everything. There's no subtracting because
# nobody had any refs to the top list.
for ( my $i = 0; $i < @gNodes; ++$i ) {
my $fco = TrieNodeGetFirstChildOffset( $gNodes[$i] );
if ( $fco != 0 ) { # 0 means NONE, not 0th!!
TrieNodeSetFirstChildOffset( \$gNodes[$i], $fco+$diff );
}
}
} # moveTopToFront
sub buildNode {
my ( $depth ) = @_;
if ( @gCurrentWord == $depth ) {
# End of word reached. If the next word isn't a continuation
# of the current one, then we've reached the bottom of the
# recursion tree.
readNextWord();
if ($gFirstDiff < $depth || $gDone) {
return 0;
}
}
my @newedges;
do {
my $letter = $gCurrentWord[$depth];
my $isTerminal = @gCurrentWord - 1 == $depth ? 1:0;
my $nodeOffset = buildNode($depth+1);
my $newNode = MakeTrieNode($letter, $isTerminal, $nodeOffset);
push( @newedges, $newNode );
} while ( ($gFirstDiff == $depth) && !$gDone);
TrieNodeSetIsLastSibling( \@newedges[@newedges-1], 1 );
return addNodes( \@newedges );
} # buildNode
sub addNodes {
my ( $newedgesR ) = @_;
my $found = findSubArray( $newedgesR );
if ( $found >= 0 ) {
die "0 is an invalid match!!!" if $found == 0;
return $found;
} else {
my $firstFreeIndex = @gNodes;
print STDERR "adding...\n" if $debug;
printNodes( $newedgesR ) if $debug;
push @gNodes, (@{$newedgesR});
registerSubArray( $newedgesR, $firstFreeIndex );
return $firstFreeIndex;
}
} # addNodes
sub printNode {
my ( $index, $node ) = @_;
print STDERR "[$index] ";
my $letter = TrieNodeGetLetter($node);
printf( STDERR
"letter=%d(%s); isTerminal=%d; isLastSib=%d; fco=%d;\n",
$letter, "" . $gRevMap[$letter],
TrieNodeGetIsTerminal($node),
TrieNodeGetIsLastSibling($node),
TrieNodeGetFirstChildOffset($node));
} # printNode
sub printNodes {
my ( $nodesR, $name ) = @_;
my $len = @{$nodesR};
# print "printNodes($name): len = $len\n";
for ( my $i = 0; $i < $len; ++$i ) {
my $node = ${$nodesR}[$i];
printNode( $i, $node );
}
}
# Hashing. We'll keep a hash of offsets into the existing nodes
# array, and as the key use a string that represents the entire sub
# array. Since the key is what we're matching for, there should never
# be more than one value per hash and so we don't need buckets.
# Return -1 if there's no match.
sub findSubArray {
my ( $newedgesR ) = @_;
my $key = join('', @{$newedgesR});
if ( exists( $gSubsHash{$key} ) ) {
return $gSubsHash{$key};
} else {
return -1;
}
} # findSubArray
# add to the hash
sub registerSubArray {
my ( $edgesR, $nodeLoc ) = @_;
my $key = join( '', @{$edgesR} );
if ( exists $gSubsHash{$key} ) {
die "entry for key shouldn't exist!!";
} else {
$gSubsHash{$key} = $nodeLoc;
}
} # registerSubArray
sub toWord($) {
my ( $tileARef ) = @_;
my $word = "";
foreach my $tile (@$tileARef) {
foreach my $letter (keys (%gTableHash) ) {
if ( $tile == $gTableHash{$letter} ) {
$word .= $letter;
last;
}
}
}
return $word;
}
sub readNextWord() {
my @word;
if ( !$gDone ) {
$gDone = @gInputStrings == 0;
if ( !$gDone ) {
@word = @{shift @gInputStrings};
} else {
print STDERR "gDone set to true\n" if $debug;
}
print STDERR "got word: ", join(',',@word), "\n" if $debug;
}
my $numCommonLetters = 0;
my $len = @word;
if ( @gCurrentWord < $len ) {
$len = @gCurrentWord;
}
while ( @gCurrentWord[$numCommonLetters] eq @word[$numCommonLetters]
&& $numCommonLetters < $len) {
++$numCommonLetters;
}
$gFirstDiff = $numCommonLetters;
if ( #$debug &&
@gCurrentWord > 0 && @word > 0
&& !firstBeforeSecond( \@gCurrentWord, \@word ) ) {
die "words ", join(",",@gCurrentWord), " (" . toWord(\@gCurrentWord) .
") and " . join(",", @word) . " (" . toWord(\@word) .
") out of order";
}
@gCurrentWord = @word;
} # readNextWord
sub firstBeforeSecond {
my ( $firstR, $secondR ) = @_;
for ( my $i = 0; ; ++$i ) {
# if we reach the end of the first word/list, we're done.
if ( $i == @{$firstR} ) {
die "duplicate!!!" if $i == @{$secondR};
return 1;
# but if we reach the second end first, we've failed
} elsif ( $i == @{$secondR} ) {
return 0;
}
my $diff = ${$firstR}[$i] <=> ${$secondR}[$i];
if ( $diff == 0 ) {
next;
} else {
return $diff < 0;
}
}
} # firstBeforeSecond
# passed to sort. Should remain unprototyped for effeciency's sake
sub cmpWords {
my $lenA = @{$a};
my $lenB = @{$b};
my $min = $lenA > $lenB? $lenB: $lenA;
for ( my $i = 0; $i < $min; ++$i ) {
my $ac = ${$a}[$i];
my $bc = ${$b}[$i];
my $res = $ac <=> $bc;
if ( $res != 0 ) {
return $res; # we're done
}
}
# If we got here, they match up to their common length. Longer is
# greater.
my $res = @{$a} <=> @{$b};
return $res; # which is longer?
} # cmpWords
sub parseAndSort() {
my ( $infile ) = @_;
my @wordlist;
my @word;
my $lastWord;
WORDLOOP:
for ( ; ; ) {
my $dropWord = 0;
splice @word; # empty it
# for each byte
for ( ; ; ) {
my $byt = getc($infile);
if ( $byt eq undef ) {
last WORDLOOP;
} elsif ( $byt eq $gTermChar ) {
if ( !$dropWord ) {
push @wordlist, [ @word ];
++$gWordCount;
}
$lastWord = "";
next WORDLOOP;
} elsif ( exists( $gTableHash{$byt} ) ) {
if ( !$dropWord ) {
push @word, $gTableHash{$byt};
die "word too long" if @word > 15;
if ( $gKillIfMissing ) {
$lastWord .= $byt;
}
}
} elsif ($gKillIfMissing) {
die "$0: chr $byt (", $byt+0, ") not in map file $gTableFile\n"
. "last word was $lastWord\n";
} else {
$dropWord = 1;
splice @word; # lose anything we already have
}
}
}
if ( $gNeedsSort && ($gWordCount > 0) ) {
print STDERR "starting sort...\n" if $debug;
@wordlist = sort cmpWords @wordlist;
print STDERR "sort finished\n" if $debug;
}
print STDERR "length of list is ", @wordlist + 0, ".\n" if $debug;
return @wordlist;
} # parseAndSort
# Print binary representation of trie array. This isn't used yet, but
# eventually it'll want to dump to multiple files appropriate for Palm
# that can be catenated together on other platforms. There'll need to
# be a file giving the offset of the first node too. Also, might want
# to move to 4-byte representation when the input can't otherwise be
# handled.
sub dumpNodes {
for ( my $i = 0; $i < @gNodes; ++$i ) {
my $node = $gNodes[$i];
my $bstr = pack( "I", $node );
print STDOUT $bstr;
}
}
##############################################################################
# Little node-field setters and getters to hide what bits represent
# what.
#
# high bit (31) is ACCEPTING bit
# next bit (30) is LAST_SIBLING bit
# next 6 bits (29-24) are tile bit (allowing alphabets of 64 letters)
# final 24 bits (23-0) are the index of the first child (fco)
#
##############################################################################
sub TrieNodeSetIsTerminal {
my ( $nodeR, $isTerminal ) = @_;
if ( $isTerminal ) {
${$nodeR} |= (1 << 31);
} else {
${$nodeR} &= ~(1 << 31);
}
}
sub TrieNodeGetIsTerminal {
my ( $node ) = @_;
return ($node & (1 << 31)) != 0;
}
sub TrieNodeSetIsLastSibling {
my ( $nodeR, $isLastSibling ) = @_;
if ( $isLastSibling ) {
${$nodeR} |= (1 << 30);
} else {
${$nodeR} &= ~(1 << 30);
}
}
sub TrieNodeGetIsLastSibling {
my ( $node ) = @_;
return ($node & (1 << 30)) != 0;
}
sub TrieNodeSetLetter {
my ( $nodeR, $letter ) = @_;
die "$0: letter ", $letter, " too big" if $letter >= 64;
my $mask = ~(0x3F << 24);
${$nodeR} &= $mask; # clear all the bits
${$nodeR} |= ($letter << 24); # set new ones
}
sub TrieNodeGetLetter {
my ( $node ) = @_;
$node >>= 24;
$node &= 0x3F; # is 3f ok for 3-byte case???
return $node;
}
sub TrieNodeSetFirstChildOffset {
my ( $nodeR, $fco ) = @_;
die "$0: $fco larger than 24 bits" if ($fco & 0xFF000000) != 0;
my $mask = ~0x00FFFFFF;
${$nodeR} &= $mask; # clear all the bits
${$nodeR} |= $fco; # set new ones
}
sub TrieNodeGetFirstChildOffset {
my ( $node ) = @_;
$node &= 0x00FFFFFF; # 24 bits
return $node;
}
sub MakeTrieNode {
my ( $letter, $isTerminal, $firstChildOffset, $isLastSibling ) = @_;
my $result = 0;
TrieNodeSetIsTerminal( \$result, $isTerminal );
TrieNodeSetIsLastSibling( \$result, $isLastSibling );
TrieNodeSetLetter( \$result, $letter );
TrieNodeSetFirstChildOffset( \$result, $firstChildOffset );
return $result;
} # MakeTrieNode
# Caller may need to know the offset of the first top-level node.
# Write it here.
sub writeOutStartNode {
my ( $startNodeOut, $firstRootChildOffset ) = @_;
open NODEOUT, ">$startNodeOut";
print NODEOUT pack( "N", $firstRootChildOffset );
close NODEOUT;
} # writeOutStartNode
# build the hash for translating. I'm using a hash assuming it'll be
# fast. Key is the letter; value is the 0..31 value to be output.
sub makeTableHash {
my $i;
open TABLEFILE, "< $gTableFile";
splice @gRevMap; # empty it
for ( $i = 0; ; ++$i ) {
my $ch = getc(TABLEFILE);
if ( $ch eq undef ) {
last;
}
if ( $gUseUnicode ) { # skip the first byte each time: tmp HACK!!!
$ch = getc(TABLEFILE);
}
if ( $ch eq undef ) {
last;
}
push @gRevMap, $ch;
if ( ord($ch) == 0 ) { # blank
$gBlankIndex = $i;
next; # we want to increment i when blank seen since
# it is a tile value
}
die "$0: $gTableFile too large\n" if $i > 64;
die "$0: only blank (0) can be 64th char\n" if ($i == 64 && $ch != 0);
$gTableHash{$ch} = $i;
}
close TABLEFILE;
} # makeTableHash
# emitNodes. "input" is $gNodes. From it we write up to
# $nBytesPerOutfile to files named $outFileBase0..n, mapping the
# letter field down to 5 bits with a hash built from $tableFile. If
# at any point we encounter a letter not in the hash we fail with an
# error.
sub emitNodes($$) {
my ( $nBytesPerOutfile, $outFileBase ) = @_;
# now do the emit.
# is 17 bits enough?
printf STDERR ("There are %d (0x%x) nodes in this DAWG.\n",
0 + @gNodes, 0 + @gNodes );
my $nTiles = 0 + keys(%gTableHash); # blank is not included in this count!
if ( @gNodes > 0x1FFFF || $gForceFour || $nTiles > 32 ) {
$gNBytesPerNode = 4;
} elsif ( $nTiles < 32 ) {
$gNBytesPerNode = 3;
} else {
if ( $gBlankIndex == 32 ) { # blank
print STDERR "blank's at 32; 3-byte-nodes still ok\n";
$gNBytesPerNode = 3;
} else {
die "$0: move blank to last position in info.txt for smaller DAWG";
}
}
my $nextIndex = 0;
my $nextFileNum = 0;
for ( $nextFileNum = 0; ; ++$nextFileNum ) {
if ( $nextIndex >= @gNodes ) {
last; # we're done
}
die "Too many outfiles; infinite loop?" if $nextFileNum > 99;
my $outName = sprintf("${outFileBase}_%03d.bin", $nextFileNum);
open OUTFILE, "> $outName";
binmode( OUTFILE );
my $curSize = 0;
while ( $nextIndex < @gNodes ) {
# scan to find the next terminal
my $i;
for ( $i = $nextIndex;
!TrieNodeGetIsLastSibling($gNodes[$i]);
++$i ) {
# do nothing but a sanity check
if ( $i >= @gNodes) {
die "bad trie format: last node not last sibling" ;
}
}
++$i; # move beyond the terminal
my $nextSize = ($i - $nextIndex) * $gNBytesPerNode;
if ($curSize + $nextSize > $nBytesPerOutfile) {
last;
} else {
# emit the subarray
while ( $nextIndex < $i ) {
outputNode( $gNodes[$nextIndex], $gNBytesPerNode,
\*OUTFILE );
++$nextIndex;
}
$curSize += $nextSize;
}
}
close OUTFILE;
}
} # emitNodes
sub printWord {
my ( $str ) = @_;
print STDERR "$str\n";
}
# print out the entire dictionary, as text, to STDERR.
sub printOneLevel {
my ( $index, $str ) = @_;
for ( ; ; ) {
my $newStr = $str;
my $node = $gNodes[$index++];
my $lindx = $gRevMap[TrieNodeGetLetter($node)];
if ( ord($lindx) >= 0x20 ) {
$newStr .= "$lindx";
} else {
print STDERR "sub space" if $debug;
$newStr .= "\\" . chr('0'+$lindx);
}
if ( TrieNodeGetIsTerminal($node) ) {
printWord( $newStr );
}
my $fco = TrieNodeGetFirstChildOffset( $node );
if ( $fco != 0 ) {
printOneLevel( $fco, $newStr );
}
if ( TrieNodeGetIsLastSibling($node) ) {
last;
}
}
}
sub outputNode ($$$) {
my ( $node, $nBytes, $outfile ) = @_;
my $fco = TrieNodeGetFirstChildOffset($node);
my $fourthByte;
if ( $nBytes == 4 ) {
$fourthByte = $fco >> 16;
die "$0: fco too big" if $fourthByte > 0xFF;
$fco &= 0xFFFF;
}
# Formats are different depending on whether it's to have 3- or
# 4-byte nodes.
# Here's what the three-byte node looks like. 16 bits plus one
# burried in the last byte for the next node address, five for a
# character/tile and one each for accepting and last-edge.
# 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
# |-------- 16 bits of next node address -------| | | | |-tile indx-|
# | | |
# accepting bit ---+ | |
# last edge bit ------+ |
# ---- last bit (17th on next node addr)---------+
# The four-byte format adds a byte at the right end for
# addressing, but removes the extra bit (5) in order to let the
# chars field be six bits. Bits 7 and 6 remain the same.
# write the fco (less that one bit). We want two bytes worth
# in three-byte mode, and three in four-byte mode
# first two bytes are low-word of fco, regardless of format
for ( my $i = 1; $i >= 0; --$i ) {
my $tmp = ($fco >> ($i * 8)) & 0xFF;
print $outfile pack( "C", $tmp );
}
$fco >>= 16; # it should now be 1 or 0
die "fco not 1 or 0" if $fco > 1;
my $chIn5 = TrieNodeGetLetter($node);
my $bits = $chIn5;
die "$0: char $bits too big" if $bits > 0x1F && $nBytes == 3;
if ( TrieNodeGetIsLastSibling($node) ) {
$bits |= 0x40;
}
if ( TrieNodeGetIsTerminal($node) ) {
$bits |= 0x80;
}
# We set the 17th next-node bit only in 3-byte case (where char is
# 5 bits)
if ( $nBytes == 3 && $fco != 0 ) {
$bits |= 0x20;
}
print $outfile pack( "C", $bits );
# the final byte, if in use
if ( $nBytes == 4 ) {
print $outfile pack( "C", $fourthByte );
}
} # outputNode
sub usage {
print STDERR "usage: $0 \n"
. "\t[-b bytesPerFile] (default = 0xFFFFFFFF)\n"
. "\t-m mapFile\n"
. "\t-mn mapFile (unicode)\n"
. "\t-ob outFileBase\n"
. "\t-sn start node out file\n"
. "\t[-if input file name] -- default = stdin\n"
. "\t[-term ch] (word terminator -- default = '\\0'\n"
. "\t[-nosort] (input already sorted in accord with -m; " .
" default=sort'\n"
. "\t[-dump] (write dictionary as text to STDERR for testing)\n"
. "\t[-force4](use 4 bytes per node regardless of need)\n"
. "\t[-r] (reject words with letters not in mapfile)\n"
. "\t[-k] (kill if any letters no in mapfile -- default)\n"
. "\t[-debug] (print a bunch of stuff)\n"
;
} # usage
sub parseARGV {
my $arg;
while ( my $arg = shift(@ARGV) ) {
SWITCH: {
if ($arg =~ /-b/) {$gNBytesPerOutfile = shift(@ARGV), last SWITCH;}
if ($arg =~ /-mn/) {$gTableFile = shift(@ARGV);
$gUseUnicode = 1;
last SWITCH;}
if ($arg =~ /-m/) {$gTableFile = shift(@ARGV); last SWITCH;}
if ($arg =~ /-ob/) {$gOutFileBase = shift(@ARGV), last SWITCH;}
if ($arg =~ /-sn/) {$gStartNodeOut = shift(@ARGV), last SWITCH;}
if ($arg =~ /-if/) {$gInFileName = shift(@ARGV), last SWITCH;}
if ($arg =~ /-r/) {$gKillIfMissing = 0; last SWITCH;}
if ($arg =~ /-k/) {$gKillIfMissing = 1; last SWITCH;}
if ($arg =~ /-term/) {$gTermChar = chr(shift(@ARGV)); last SWITCH;}
if ($arg =~ /-dump/) {$gDumpText = 1; last SWITCH;}
if ($arg =~ /-nosort/) {$gNeedsSort = 0; last SWITCH;}
if ($arg =~ /-wc/) {$gCountFile = shift(@ARGV); last SWITCH;}
if ($arg =~ /-ns/) {$gBytesPerNodeFile = shift(@ARGV); last SWITCH;}
if ($arg =~ /-force4/) {$gForceFour = 1; last SWITCH;}
# accept -fsize for compatibility with c++ version (but drop it)
if ($arg =~ /-fsize/) {shift(@ARGV); last SWITCH;}
if ($arg =~ /-debug/) {$debug = 1; last SWITCH;}
die "unexpected arg $arg\n";
}
}
print STDERR "gNBytesPerOutfile=$gNBytesPerOutfile\n" if $debug;
print STDERR "gTableFile=$gTableFile\n" if $debug;
print STDERR "gOutFileBase=$gOutFileBase\n" if $debug;
print STDERR "gStartNodeOut=$gStartNodeOut\n" if $debug;
printf STDERR "gTermChar=%s(%d)\n", $gTermChar, ord($gTermChar) if $debug;
return $gTableFile;
} # parseARGV

View file

@ -1,76 +0,0 @@
#!/usr/bin/perl
# print stats about in input stream that's assumed to be a dictionary.
# Counts and percentages of each letter, as well as total numbers of
# words. This is not part of the dictionary build process. I use it
# for creating info.txt files for new languages and debugging the
# creation of dictionaries from new wordlists.
#
# Something like this might form the basis for choosing counts and
# values for tiles without using the conventions established by
# Scrabble players. This isn't enough, though: the frequency of
# letter tuples and triples -- how often letters appear together -- is
# a better indicator than just letter count.
use strict;
my @wordSizeCounts;
my %letterCounts;
my $wordCount;
my $letterCount;
my $enc = "utf8"; # this could be a cmdline arg....
if ( $enc ) {
binmode( STDOUT, ":encoding($enc)" ) ;
binmode( STDIN, ":encoding($enc)" ) ;
}
while (<>) {
chomp;
++$wordSizeCounts[length];
++$wordCount;
foreach my $letter (split( / */ ) ) {
my $ii = ord($letter);
# special-case the bogus chars we add for "specials"
die "$0: this is a letter?: $ii" if $ii <= 32 && $ii >= 4 && $ii != 0;
++$letterCounts{$letter};
++$letterCount;
}
}
print "Number of words: $wordCount\n";
print "Number of letters: $letterCount\n\n";
print "**** word sizes ****\n";
print "SIZE COUNT PERCENT\n";
my $pctTotal = 0.0;
my $wordTotal = 0;
for ( my $i = 1 ; $i <= 99; ++$i ) {
my $count = $wordSizeCounts[$i];
$wordTotal += $count;
if ( $count > 0 ) {
my $pct = (100.00 * $count)/$wordCount;
$pctTotal += $pct;
printf "%2d %6d %.2f\n", $i, $count, $pct;
}
}
printf "-------------------------------\n";
printf " %6d %.2f\n", $wordTotal, $pctTotal;
print "\n\n**** Letter counts ****\n";
print " ASCII ORD HEX PCT (of $letterCount)\n";
my $lineNo = 1;
foreach my $key (sort keys %letterCounts) {
my $count = $letterCounts{$key};
my $pct = (100.00 * $count) / $letterCount;
printf( "%2d: %3s %3d %x %5.2f (%d)\n",
$lineNo, $key, ord($key), ord($key), $pct, $count );
++$lineNo;
}
print "\n";

80
xwords4/dawg/dictstats.py Executable file
View file

@ -0,0 +1,80 @@
#!/usr/bin/env python3
import sys
"""
print stats about in input stream that's assumed to be a dictionary.
Counts and percentages of each letter, as well as total numbers of
words. This is not part of the dictionary build process. I use it
for creating info.txt files for new languages and debugging the
creation of dictionaries from new wordlists.
Something like this might form the basis for choosing counts and
values for tiles without using the conventions established by
Scrabble players. This isn't enough, though: the frequency of
letter tuples and triples -- how often letters appear together -- is
a better indicator than just letter count.
"""
def main():
wordSizeCounts = {}
letterCounts = {}
wordCount = 0
letterCount = 0
enc = 'utf8' # this could be a cmdline arg....
for line in sys.stdin.readlines():
line = line.strip()
length = len(line)
if not length in wordSizeCounts: wordSizeCounts[length] = 0
wordSizeCounts[length] += 1
wordCount += 1
for letter in line:
ii = ord(letter)
# perl did this: die "$0: this is a letter?: $ii" if $ii <= 32 && $ii >= 4 && $ii != 0;
assert ii > 32 or ii < 4 or ii == 0, 'letter {} out of range'.format(ii)
if not letter in letterCounts: letterCounts[letter] = 0
letterCounts[letter] += 1
letterCount += 1
print( 'Number of words: {}'.format(wordCount))
print( 'Number of letters: {}'.format(letterCount))
print('')
print( '**** word sizes ****' )
print( 'SIZE COUNT PERCENT' )
pctTotal = 0.0
wordTotal = 0
for ii in sorted(wordSizeCounts):
count = wordSizeCounts[ii]
wordTotal += count
pct = (100.00 * count)/wordCount
pctTotal += pct
print( '{:2d} {:6d} {:02.2f}'.format(ii, count, pct))
print( '-------------------------------' )
print(' {:6d} {:.2f}'.format( wordTotal, pctTotal))
print('')
lineNo = 1
pctTotal = 0.0
print( '**** Letter counts ****' )
print( ' ASCII ORD HEX PCT (of {})'.format(letterCount))
for letter in sorted(letterCounts):
count = letterCounts[letter]
pct = (100.00 * count) / letterCount
pctTotal += pct
print( '{:2d}: {: >6s} {:2d} {:x} {:5.2f} ({:d})' \
.format(lineNo, letter, ord(letter), ord(letter), pct, count ) )
lineNo += 1
print('percent total {:.2f}'.format( pctTotal))
print('')
##############################################################################
if __name__ == '__main__':
main()

View file

@ -1,47 +0,0 @@
#!/usr/bin/perl
# Copyright 2001 by Eric House (xwords@eehouse.org)
#
# 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.
# Given arguments consisting of triples, first a string and then pbitm
# files representing bitmaps. For each triple, print out the string and
# then the converted bitmaps.
use strict;
while ( @ARGV ) {
my $str = shift();
my $largebmp = shift();
my $smallbmp = shift();
doOne( $str, $largebmp, $smallbmp );
}
sub doOne {
my ( $str, $largebmp, $smallbmp ) = @_;
print pack( "C", length($str) );
print $str;
print STDERR "looking at $largebmp", "\n";
die "file $largebmp does not exist\n" unless -e $largebmp;
print `cat $largebmp | ../pbitm2bin.pl`;
die "file $smallbmp does not exist\n" unless -e $smallbmp;
print `cat $smallbmp | ../pbitm2bin.pl`;
}

View file

@ -0,0 +1,49 @@
#!/usr/bin/env python3
# Copyright 2001 - 2020 by Eric House (xwords@eehouse.org)
#
# 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.
# Given arguments consisting of triples, first a string and then pbitm
# files representing bitmaps. For each triple, print out the string and
# then the converted bitmaps.
import os, subprocess, struct, sys
def doFile(bmp):
assert os.path.exists(bmp), 'no such file: {}'.format(bmp)
with open(bmp, 'r') as file:
subprocess.run(['../pbitm2bin.py'], stdin=file)
def doOne( face, largebmp, smallbmp ):
# print('doOne({}, {}, {})'.format(face, largebmp, smallbmp), file=sys.stderr)
with os.fdopen(sys.stdout.fileno(), 'wb', closefd=False) as stdout:
encoded = face.encode('utf-8')
stdout.write(struct.pack('B', len(encoded)))
stdout.write(encoded)
for fil in [ largebmp, smallbmp ]:
print('looking at {}'.format(fil), file=sys.stderr)
doFile(fil)
def main():
argv = sys.argv
for first in range(1, len(argv), 3):
(face, largebmp, smallbmp) = argv[first:first+3]
doOne(face, largebmp, smallbmp)
##############################################################################
if __name__ == '__main__':
main()

7
xwords4/dawg/mkbyodbins.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
for INFO in $(ls */info.txt); do
DIR=$(dirname $INFO)
echo "*** processing $(basename $DIR) ***"
(cd $DIR && make clean byodbins)
done

View file

@ -1,234 +0,0 @@
#!/usr/bin/perl
# Copyright 2002 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.
# Only enough of par's features to support building a crosswords dict
# pdb
use strict;
my $debug = 0;
# stolen from par source
my $PRC_FLAGS_RESOURCE = (0x1<<0);
my $PRC_FLAGS_READONLY = (0x1<<1);
my $PRC_FLAGS_DIRTY = (0x1<<2);
my $PRC_FLAGS_BACKUP = (0x1<<3);
my $PRC_FLAGS_NEWER = (0x1<<4);
my $PRC_FLAGS_RESET = (0x1<<5);
my $PRC_FLAGS_COPYPREVENT = (0x1<<6);
my $PRC_FLAGS_STREAM = (0x1<<7);
my $PRC_FLAGS_HIDDEN = (0x1<<8);
my $PRC_FLAGS_LAUNCHABLE = (0x1<<9);
my $PRC_FLAGS_RECYCLABLE = (0x1<<10);
my $PRC_FLAGS_BUNDLE = (0x1<<11);
my $PRC_FLAGS_OPEN = (0x1<<15);
my $gAttrs = 0;
my $gVersion = 1; # par defaults this to 1
my $cmd = shift( @ARGV );
die "only 'c' supported now" if $cmd ne "c" && $cmd ne "-c";
readHOptions( \@ARGV );
my $dbfile = shift( @ARGV );
my $name = shift( @ARGV );
die "name $name too long" if length($name) > 31;
my $type = shift( @ARGV );
die "type $type must be of length 4" if length($type) != 4;
my $cid = shift( @ARGV );
die "cid $cid must be of length 4" if length($cid) != 4;
my @fileNames;
my @fileLengths;
my $nFiles = 0;
while ( @ARGV > 0 ) {
my $filename = shift( @ARGV );
push @fileNames, $filename;
push @fileLengths, -s $filename;
++$nFiles;
}
# from par's prcp.h; thanks djw!
# typedef struct prc_file_t {
# prc_byte_t name[32];
# prc_byte_t flags[2];
# prc_byte_t version[2];
# prc_byte_t ctime[4];
# prc_byte_t mtime[4];
# prc_byte_t btime[4];
# prc_byte_t modnum[4];
# prc_byte_t appinfo[4];
# prc_byte_t sortinfo[4];
# prc_byte_t type[4];
# prc_byte_t cid[4];
# prc_byte_t unique_id_seed[4];
# prc_byte_t next_record_list[4];
# prc_byte_t nrecords[2];
# } prc_file_t;
my $str;
my $offset = 0;
open OUTFILE, "> $dbfile" or die "couldn't open outfile $dbfile for writing";
# print the string, then pad with 0s
$offset = length($name);
print OUTFILE $name;
while ( $offset < 32 ) {
print OUTFILE pack("c", 0);
++$offset;
}
$str = pack("n", $gAttrs); # flags
print OUTFILE $str;
$offset += length($str);
$str = pack("n", $gVersion); # version
print OUTFILE $str;
$offset += length($str);
my $time = time() + 2082844800;
$str = pack("NNN", $time, $time, 0); # ctime, mtime, btime
print OUTFILE $str;
$offset += length($str);
$str = pack("N", 0 ); # mod num
print OUTFILE $str;
$offset += length($str);
$str = pack("N", 0 ); # appinfo
print OUTFILE $str;
$offset += length($str);
$str = pack("N", 0 ); # sortinfo
print OUTFILE $str;
$offset += length($str);
print OUTFILE $type; # type
print OUTFILE $cid; # cid
$offset += 8;
$str = pack("NN", 0, 0 ); # unique_id_seed, next_record_list
print OUTFILE $str;
$offset += length($str);
$str = pack("n", $nFiles ); # nrecords
print OUTFILE $str;
$offset += length($str);
$offset += $nFiles * 8;
$offset += 2; # djw adds 2 bytes after size list; see below
foreach my $len ( @fileLengths ) {
print OUTFILE pack( "N", $offset );
print OUTFILE pack( "N", 0 );
$offset += $len;
}
print OUTFILE pack( "n", 0 ); # djw does this sans comment: flush.c, line 87
foreach my $file ( @fileNames ) {
open INFILE, "<$file" or die "couldn't open infile $file\n";
my $buffer;
while ( read INFILE, $buffer, 1024 ) {
print OUTFILE $buffer;
}
close INFILE;
}
close OUTFILE;
exit 0;
##############################################################################
# Subroutines
##############################################################################
sub readHOptions {
my ( $argvR ) = @_;
for ( ; ; ) {
my $opt = ${$argvR}[0];
if ( $opt !~ /^-/ ) {
last;
}
# it starts with a '-': use it; else don't consume anything
shift @{$argvR};
if ( $opt eq "-a" ) {
my $attrs = shift @{$argvR};
processAttrString( $attrs );
} elsif ( $opt eq "-v" ) {
$gVersion = shift @{$argvR};
} else {
die "what's with \"$opt\": -a and -v are the only hattrs supported";
}
}
} # readHOptions
sub processAttrString {
my ( $attrs ) = @_;
foreach my $flag ( split /\|/, $attrs ) {
print STDERR "looking at flag $flag\n" if $debug;
if ( $flag =~ /resource/ ) {
$gAttrs |= $PRC_FLAGS_RESOURCE;
die "resource attr not supported";
} elsif ( $flag =~ /readonly/ ) {
$gAttrs |= $PRC_FLAGS_READONLY;
} elsif ( $flag =~ /dirty/ ) {
$gAttrs |= $PRC_FLAGS_DIRTY;
} elsif ( $flag =~ /backup/ ) {
$gAttrs |= $PRC_FLAGS_BACKUP;
} elsif ( $flag =~ /newer/ ) {
$gAttrs |= $PRC_FLAGS_NEWER;
} elsif ( $flag =~ /reset/ ) {
$gAttrs |= $PRC_FLAGS_RESET;
} elsif ( $flag =~ /copyprevent/ ) {
$gAttrs |= $PRC_FLAGS_COPYPREVENT;
} elsif ( $flag =~ /stream/ ) {
$gAttrs |= $PRC_FLAGS_STREAM;
die "stream attr not supported";
} elsif ( $flag =~ /hidden/ ) {
$gAttrs |= $PRC_FLAGS_HIDDEN;
} elsif ( $flag =~ /launchable/ ) {
$gAttrs |= $PRC_FLAGS_LAUNCHABLE;
} elsif ( $flag =~ /recyclable/ ) {
$gAttrs |= $PRC_FLAGS_RECYCLABLE;
} elsif ( $flag =~ /bundle/ ) {
$gAttrs |= $PRC_FLAGS_BUNDLE;
} elsif ( $flag =~ /open/ ) {
$gAttrs |= $PRC_FLAGS_OPEN;
} else {
die "flag $flag not supportd";
}
}
} # processAttrString

68
xwords4/dawg/pbitm2bin.py Executable file
View file

@ -0,0 +1,68 @@
#!/usr/bin/env python3
#
# Copyright 2001-2020 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.
#
#
# Given a pbitm on stdin, a text bitmap file where '#' indicates a set
# bit and '-' indicates a clear bit, convert into binary form (on
# stdout) where there's one bit per bit plus a byte each for the width
# and height. Nothing for bitdepth at this point. And no padding: if
# the number of bits in a row isn't a multiple of 8 then one byte will
# hold the last bits of one row and the first of another.
import os, struct, sys
lines = [ line.strip() for line in sys.stdin ]
nRows = len(lines)
nCols = 0
bits = ''
# first gather information and sanity-check the data
for line in lines:
lineLen = len(line)
if nCols == 0:
nCols = lineLen
else:
assert nCols == lineLen, 'line of inconsistent length'
bits += line
with os.fdopen(sys.stdout.fileno(), "wb", closefd=False) as stdout:
stdout.write(struct.pack('B', nCols))
# if we've been given an empty file, print out a single null byte
# and be done. That'll be the convention for "non-existant
# bitmap".
if nCols > 0:
stdout.write(struct.pack( 'B', nRows ) )
print( 'emitting {}x{} bitmap'.format(nCols, nRows), file=sys.stderr)
while bits:
cur = bits[:8]
bits = bits[8:]
byt = 0
for indx in range(len(cur)):
ch = cur[indx]
assert ch == '-' or ch == '#', "char {} neither '#' nor '-'".format(ch)
if ch == '#': byt |= 1 << (7 - indx)
stdout.write(struct.pack( 'B', byt ))
stdout.flush()

View file

@ -1,76 +0,0 @@
#!/usr/bin/perl
# Copyright 2002 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.
# test and wrapper file for xloc.pm
use strict;
use xloc;
my $unicode = -1;
my $doval = 0;
my $dosize = 0;
my $enc;
my $outfile;
my $arg;
while ( $arg = $ARGV[0] ) {
if ( $arg eq '-enc' ) {
$enc = $ARGV[1];
shift @ARGV;
} elsif ( $arg eq "-tn" ) {
$unicode = 1;
} elsif ( $arg eq "-t" ) {
$unicode = 0;
} elsif ( $arg eq "-v" ) {
$doval = 1;
} elsif ( $arg eq "-s" ) {
$dosize = 1;
} elsif ( $arg eq '-out' ) {
$outfile = $ARGV[1];
shift @ARGV;
} else {
die "unknown arg $arg\n";
}
shift @ARGV;
}
my $infoFile = "info.txt";
die "info file $infoFile not found\n" if ! -s $infoFile;
my $xlocToken = xloc::ParseTileInfo($infoFile, $enc);
if ( $enc ) {
open OUTFILE, ">:encoding($enc)", "$outfile"
or die "couldn't open $outfile";
} else {
open OUTFILE, ">$outfile" or die "couldn't open $outfile";
}
# For f*cking windoze linefeeds
# binmode( OUTFILE );
if ( $unicode ne -1 ) {
xloc::WriteMapFile( $xlocToken, $unicode, \*OUTFILE );
} elsif ( $dosize ) {
my $count = xloc::GetNTiles( $xlocToken );
print OUTFILE pack("c", $count );
} elsif ( $doval ) {
xloc::WriteValuesFile( $xlocToken, \*OUTFILE );
}
close OUTFILE;

View file

@ -1,194 +0,0 @@
#!/usr/bin/perl
# Copyright 2002-2014 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.
# The idea here is that all that matters about a language is stored in
# one file (possibly excepting rules for prepping a dictionary).
# There's a list of tile faces, counts and values, and also some
# name-value pairs as needed. The pairs come first, and then a list
# of tiles.
package xloc;
use strict;
use warnings;
# force output in utf8
use open qw/:std :utf8/;
BEGIN {
use Exporter ();
our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
$VERSION = 1.00;
@ISA = qw(Exporter);
@EXPORT = qw(&ParseTileInfo &GetNTiles &TileFace &TileValue
&TileCount &GetValue &WriteMapFile &WriteValuesFile);
%EXPORT_TAGS = ( );
}
# Returns what's meant to be an opaque object that can be passed back
# for queries. It's a hash with name-value pairs and an _INFO entry
# containing a list of tile info lists.
sub ParseTileInfo($$) {
my ( $filePath, $enc ) = @_;
my %result;
if ( $enc ) {
open( INPUT, "<:encoding($enc)", "$filePath" )
or die "couldn't open $filePath";
} else {
open( INPUT, "<$filePath" )
or die "couldn't open $filePath";
}
my $inTiles = 0;
my @tiles;
while ( <INPUT> ) {
chomp;
s/\#.*$//;
s/^\s*$//; # nuke all-white-space lines
next if !length;
if ( $inTiles ) {
if ( /<END_TILES>/ ) {
last;
} else {
my ( $count, $val, $face ) = m/^\s*(\w+)\s+(\w+)\s+(.*)\s*$/;
push @tiles, [ $count, $val, $face ];
}
} elsif ( /\w:/ ) {
my ( $nam, $val ) = split ':', $_, 2;
$result{$nam} .= $val;
} elsif ( /<BEGIN_TILES>/ ) {
$inTiles = 1;
}
}
close INPUT;
$result{"_TILES"} = [ @tiles ];
return \%result;
}
sub GetNTiles($) {
my ( $hashR ) = @_;
my $listR = ${$hashR}{"_TILES"};
return 0 + @{$listR};
}
sub GetValue($$) {
my ( $hashR, $name ) = @_;
return ${$hashR}{$name};
}
sub printLetters($$) {
my ( $str, $fhr ) = @_;
my @letters = split( /\|/, $str );
$str = join( " ", @letters );
for ( my $key = 0; $key < length($str); ++$key ) {
my $chr = substr( $str, $key, 1 );
print $fhr pack( "U", ord($chr) );
}
}
sub WriteMapFile($$$) {
my ( $hashR, $unicode, $fhr ) = @_;
my $count = GetNTiles($hashR);
my $specialCount = 0;
for ( my $i = 0; $i < $count; ++$i ) {
my $tileR = GetNthTile( $hashR, $i );
my $str = ${$tileR}[2];
if ( $str =~ /\'(.(\|.)*)\'/ ) {
printLetters( $1, $fhr );
} elsif ( $str =~ /\"(.+)\"/ ) {
print $fhr pack( "c", $specialCount++ );
} elsif ( $str =~ /(\d+)/ ) {
print $fhr pack( "n", $1 );
} else {
die "WriteMapFile: unrecognized face format $str, elem $i";
}
}
} # WriteMapFile
sub WriteValuesFile($$) {
my ( $hashR, $fhr ) = @_;
my $header = GetValue( $hashR,"XLOC_HEADER" );
die "no XLOC_HEADER found" if ! $header;
print STDERR "header is $header\n";
print $fhr pack( "n", hex($header) );
my $count = GetNTiles($hashR);
for ( my $i = 0; $i < $count; ++$i ) {
my $tileR = GetNthTile( $hashR, $i );
print $fhr pack( "c", TileValue($tileR) );
print $fhr pack( "c", TileCount($tileR) );
}
} # WriteValuesFile
sub GetNthTile($$) {
my ( $hashR, $n ) = @_;
my $listR = ${$hashR}{"_TILES"};
return ${$listR}[$n];
}
sub TileFace($) {
my ( $tileR ) = @_;
my $result;
my $str = ${$tileR}[2];
if ( $str =~ /\'(.(\|.)*)\'/ ) {
$result = $1;
} elsif ( $str =~ /\"(.+)\"/ ) {
$result = $1;
} elsif ( $str =~ /(\d+)/ ) {
$result = chr($1);
} else {
die "TileFace: unrecognized face format: $str";
}
return $result;
}
sub TileValue($) {
my ( $tileR ) = @_;
return ${$tileR}[0];
}
sub TileCount($) {
my ( $tileR ) = @_;
return ${$tileR}[1];
}
1;

132
xwords4/dawg/xloc.py Executable file
View file

@ -0,0 +1,132 @@
#!/usr/bin/env python3
import argparse, os, re, struct, sys
def errorOut(msg):
print('ERROR: {}'.format(msg))
sys.exit(1)
def mkParser():
parser = argparse.ArgumentParser()
parser.add_argument('-enc', dest = 'ENCODING', type = str, help = 'use this encoding' )
parser.add_argument('-tn', dest = 'DO_TABLE', action = 'store_true', help = 'output table file' )
# parser.add_argument('-tn', dest = 'UNICODE', default = False,
# action = 'store_true', help = 'assume unicode')
# parser.add_argument('-t', dest = 'UNICODE', type = str, default = True,
# action = 'store_false', help = 'DO NOT assume unicode')
parser.add_argument('-v', dest = 'DO_VALS', action = 'store_true', help = 'output values file' )
parser.add_argument('-s', dest = 'DO_SIZE', action = 'store_true', help = 'output size file')
parser.add_argument('-out', dest = 'OUTFILE', type = str, help = 'outfile path')
parser.add_argument('--table-file', dest = 'TABLE_FILE', type = str, help = 'write table file here')
parser.add_argument('--size-file', dest = 'SIZE_FILE', type = str, help = 'write size file here')
parser.add_argument('--vals-file', dest = 'VALS_FILE', type = str, help = 'write vals file here')
return parser
sPreComment = re.compile('^(.*)#.*$')
sVarAssign = re.compile('^(\w+):(.*)$')
sBeginTiles = re.compile('^<BEGIN_TILES>$')
sEndTiles = re.compile('^<END_TILES>$')
sSingleCharMatch = re.compile("'(.(\|.)*)'")
sSpecialsMatch = re.compile('{"(.+)"(,.+)?}')
def parseTileInfo(infoFile, encoding):
result = {'_TILES' : []}
with open(infoFile, 'rt') as file:
data = file.read()
# if encoding:
# data = data.decode(encoding)
data = data.split('\n')
inTiles = False
tiles = []
for line in data:
# print('line at start: {}'.format(line))
match = sPreComment.match(line)
if match:
line = match.group(1)
# print('line sans comment: {}'.format(line))
if 0 == len(line):continue
if inTiles:
if sEndTiles.match(line):
break
else:
(count, val, face) = line.split(None, 2)
result['_TILES'].append((count, val, face))
elif sBeginTiles.match(line):
inTiles = True
else:
match = sVarAssign.match(line)
if match:
var = match.group(1)
if not var in result: result[var] = ''
result[var] += match.group(2)
return result
def printLetters( letters, outfile ):
letters = letters.split('|')
letters = ' '.join(letters)
outfile.write(letters.encode('utf8'))
def writeMapFile(xlocToken, outfile):
print('writeMapFile()')
tiles = xlocToken['_TILES']
specialCount = 0
for tile in tiles:
face = tile[2]
match = sSingleCharMatch.match(face)
if match:
printLetters( match.group(1), outfile )
continue
match = sSpecialsMatch.match(face)
if match:
print('specials char: {}'.format(match.group(1)))
outfile.write(struct.pack('B', specialCount ))
specialCount += 1
continue
print('bad/unmatched face: {}'.format(face))
assert False
def writeValuesFile(xlocToken, outfile):
header = xlocToken.get('XLOC_HEADER') or errorOut('no XLOC_HEADER found')
print('writing header: {}'.format(header))
outfile.write(struct.pack('!H', int(header, 16)))
for tile in xlocToken['_TILES']:
val = int(tile[0])
count = int(tile[1])
outfile.write(struct.pack('BB', val, count))
def main():
print('{}.main {} called'.format(sys.argv[0], sys.argv[1:]))
args = mkParser().parse_args()
infoFile = 'info.txt'
if not os.path.exists(infoFile):
errorOut('{} not found'.format(infoFile))
xlocToken = parseTileInfo(infoFile, args.ENCODING)
if args.DO_TABLE or args.TABLE_FILE:
path = args.TABLE_FILE or args.OUTFILE
with open(path, 'wb') as outfile:
writeMapFile(xlocToken, outfile);
if args.DO_SIZE or args.SIZE_FILE:
path = args.SIZE_FILE or args.OUTFILE
with open(path, 'wb') as outfile:
count = len(xlocToken['_TILES'])
outfile.write(struct.pack('B', count))
if args.DO_VALS or args.VALS_FILE:
path = args.VALS_FILE or args.OUTFILE
with open(path, 'wb') as outfile:
writeValuesFile( xlocToken, outfile )
##############################################################################
if __name__ == '__main__':
main()

View file

@ -330,18 +330,20 @@ linux_dutil_loadPtr( XW_DUtilCtxt* duc, XWEnv XP_UNUSED(xwe), const XP_UCHAR* ke
*lenp = buflen;
} else {
gchar* tmp = XP_MALLOC( duc->mpool, buflen );
res = db_fetch( pDb, key, tmp, &buflen );
gint tmpLen = buflen;
res = db_fetch( pDb, key, tmp, &tmpLen );
XP_ASSERT( buflen == tmpLen );
XP_ASSERT( res == SUCCESS );
XP_ASSERT( tmp[buflen-1] == '\0' );
gsize out_len;
guchar* txt = g_base64_decode( (const gchar*)tmp, &out_len );
guchar* binp = g_base64_decode( tmp, &out_len );
if ( out_len <= *lenp ) {
XP_MEMCPY( data, txt, out_len );
XP_MEMCPY( data, binp, out_len );
*lenp = out_len;
}
XP_FREEP( duc->mpool, &tmp );
g_free( txt );
g_free( binp );
}
} else {
*lenp = 0; /* doesn't exist */

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3
import re, os, sys, getopt, shutil, threading, requests, json, glob
import re, os, sys, shutil, threading, requests, json, glob
import argparse, datetime, random, signal, subprocess, time
from shutil import rmtree