mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-04-02 18:30:44 +02:00
Implement auto-save of old game when starting a new game
The 20 most recently auto-saved games are kept in the file DroidFish/pgn/.autosave.pgn. Overwriting an existing game when saving a new game also auto-saves the old game.
This commit is contained in:
parent
ce544c6be8
commit
683a238bff
@ -588,7 +588,7 @@ public class DroidFish extends Activity
|
|||||||
ctrl.startGame();
|
ctrl.startGame();
|
||||||
if (intentPgnOrFen != null) {
|
if (intentPgnOrFen != null) {
|
||||||
try {
|
try {
|
||||||
ctrl.setFENOrPGN(intentPgnOrFen);
|
ctrl.setFENOrPGN(intentPgnOrFen, true);
|
||||||
setBoardFlip(true);
|
setBoardFlip(true);
|
||||||
} catch (ChessParseError e) {
|
} catch (ChessParseError e) {
|
||||||
// If FEN corresponds to illegal chess position, go into edit board mode.
|
// If FEN corresponds to illegal chess position, go into edit board mode.
|
||||||
@ -1019,6 +1019,8 @@ public class DroidFish extends Activity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int serializeVersion = 4;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
@ -1026,7 +1028,7 @@ public class DroidFish extends Activity
|
|||||||
byte[] data = ctrl.toByteArray();
|
byte[] data = ctrl.toByteArray();
|
||||||
byte[] token = data == null ? null : cache.storeBytes(data);
|
byte[] token = data == null ? null : cache.storeBytes(data);
|
||||||
outState.putByteArray("gameStateT", token);
|
outState.putByteArray("gameStateT", token);
|
||||||
outState.putInt("gameStateVersion", 3);
|
outState.putInt("gameStateVersion", serializeVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1049,7 +1051,7 @@ public class DroidFish extends Activity
|
|||||||
Editor editor = settings.edit();
|
Editor editor = settings.edit();
|
||||||
String dataStr = byteArrToString(data);
|
String dataStr = byteArrToString(data);
|
||||||
editor.putString("gameState", dataStr);
|
editor.putString("gameState", dataStr);
|
||||||
editor.putInt("gameStateVersion", 3);
|
editor.putInt("gameStateVersion", serializeVersion);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
lastVisibleMillis = System.currentTimeMillis();
|
lastVisibleMillis = System.currentTimeMillis();
|
||||||
@ -1568,16 +1570,17 @@ public class DroidFish extends Activity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static private final int RESULT_EDITBOARD = 0;
|
static private final int RESULT_EDITBOARD = 0;
|
||||||
static private final int RESULT_SETTINGS = 1;
|
static private final int RESULT_SETTINGS = 1;
|
||||||
static private final int RESULT_LOAD_PGN = 2;
|
static private final int RESULT_LOAD_PGN = 2;
|
||||||
static private final int RESULT_LOAD_FEN = 3;
|
static private final int RESULT_LOAD_FEN = 3;
|
||||||
static private final int RESULT_SELECT_SCID = 4;
|
static private final int RESULT_SAVE_PGN = 4;
|
||||||
static private final int RESULT_OI_PGN_SAVE = 5;
|
static private final int RESULT_SELECT_SCID = 5;
|
||||||
static private final int RESULT_OI_PGN_LOAD = 6;
|
static private final int RESULT_OI_PGN_SAVE = 6;
|
||||||
static private final int RESULT_OI_FEN_LOAD = 7;
|
static private final int RESULT_OI_PGN_LOAD = 7;
|
||||||
static private final int RESULT_GET_FEN = 8;
|
static private final int RESULT_OI_FEN_LOAD = 8;
|
||||||
static private final int RESULT_EDITOPTIONS = 9;
|
static private final int RESULT_GET_FEN = 9;
|
||||||
|
static private final int RESULT_EDITOPTIONS = 10;
|
||||||
|
|
||||||
private void startEditBoard(String fen) {
|
private void startEditBoard(String fen) {
|
||||||
Intent i = new Intent(DroidFish.this, EditBoard.class);
|
Intent i = new Intent(DroidFish.this, EditBoard.class);
|
||||||
@ -1595,7 +1598,7 @@ public class DroidFish extends Activity
|
|||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
try {
|
try {
|
||||||
String fen = data.getAction();
|
String fen = data.getAction();
|
||||||
ctrl.setFENOrPGN(fen);
|
ctrl.setFENOrPGN(fen, true);
|
||||||
setBoardFlip(false);
|
setBoardFlip(false);
|
||||||
} catch (ChessParseError ignore) {
|
} catch (ChessParseError ignore) {
|
||||||
}
|
}
|
||||||
@ -1609,13 +1612,19 @@ public class DroidFish extends Activity
|
|||||||
int modeNr = ctrl.getGameMode().getModeNr();
|
int modeNr = ctrl.getGameMode().getModeNr();
|
||||||
if ((modeNr != GameMode.ANALYSIS) && (modeNr != GameMode.EDIT_GAME))
|
if ((modeNr != GameMode.ANALYSIS) && (modeNr != GameMode.EDIT_GAME))
|
||||||
newGameMode(GameMode.EDIT_GAME);
|
newGameMode(GameMode.EDIT_GAME);
|
||||||
ctrl.setFENOrPGN(pgn);
|
ctrl.setFENOrPGN(pgn, false);
|
||||||
setBoardFlip(true);
|
setBoardFlip(true);
|
||||||
} catch (ChessParseError e) {
|
} catch (ChessParseError e) {
|
||||||
DroidFishApp.toast(getParseErrString(e), Toast.LENGTH_SHORT);
|
DroidFishApp.toast(getParseErrString(e), Toast.LENGTH_SHORT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case RESULT_SAVE_PGN:
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
long hash = data.getLongExtra("org.petero.droidfish.treeHash", -1);
|
||||||
|
ctrl.setLastSaveHash(hash);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case RESULT_SELECT_SCID:
|
case RESULT_SELECT_SCID:
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
String pathName = data.getAction();
|
String pathName = data.getAction();
|
||||||
@ -1662,13 +1671,13 @@ public class DroidFish extends Activity
|
|||||||
String pathName = getFilePathFromUri(data.getData());
|
String pathName = getFilePathFromUri(data.getData());
|
||||||
loadFENFromFile(pathName);
|
loadFENFromFile(pathName);
|
||||||
}
|
}
|
||||||
setFenHelper(fen);
|
setFenHelper(fen, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RESULT_LOAD_FEN:
|
case RESULT_LOAD_FEN:
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
String fen = data.getAction();
|
String fen = data.getAction();
|
||||||
setFenHelper(fen);
|
setFenHelper(fen, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RESULT_EDITOPTIONS:
|
case RESULT_EDITOPTIONS:
|
||||||
@ -2090,7 +2099,6 @@ public class DroidFish extends Activity
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
gameMode = new GameMode(gameModeType);
|
gameMode = new GameMode(gameModeType);
|
||||||
}
|
}
|
||||||
// savePGNToFile(".autosave.pgn", true);
|
|
||||||
TimeControlData tcData = new TimeControlData();
|
TimeControlData tcData = new TimeControlData();
|
||||||
tcData.setTimeControl(timeControl, movesPerSession, timeIncrement);
|
tcData.setTimeControl(timeControl, movesPerSession, timeIncrement);
|
||||||
speech.flushQueue();
|
speech.flushQueue();
|
||||||
@ -2161,10 +2169,10 @@ public class DroidFish extends Activity
|
|||||||
writer.close();
|
writer.close();
|
||||||
loadPGNFromFile(fn);
|
loadPGNFromFile(fn);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
ctrl.setFENOrPGN(fenPgnData);
|
ctrl.setFENOrPGN(fenPgnData, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctrl.setFENOrPGN(fenPgnData);
|
ctrl.setFENOrPGN(fenPgnData, true);
|
||||||
}
|
}
|
||||||
setBoardFlip(true);
|
setBoardFlip(true);
|
||||||
} catch (ChessParseError e) {
|
} catch (ChessParseError e) {
|
||||||
@ -3476,8 +3484,33 @@ public class DroidFish extends Activity
|
|||||||
i.setAction("org.petero.droidfish.saveFile");
|
i.setAction("org.petero.droidfish.saveFile");
|
||||||
i.putExtra("org.petero.droidfish.pathname", pathName);
|
i.putExtra("org.petero.droidfish.pathname", pathName);
|
||||||
i.putExtra("org.petero.droidfish.pgn", pgnToken);
|
i.putExtra("org.petero.droidfish.pgn", pgnToken);
|
||||||
i.putExtra("org.petero.droidfish.silent", false);
|
setEditPGNBackup(i, pathName);
|
||||||
startActivity(i);
|
startActivityForResult(i, RESULT_SAVE_PGN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set a Boolean value in the Intent to decide if backups should be made
|
||||||
|
* when games in a PGN file are overwritten or deleted. */
|
||||||
|
private void setEditPGNBackup(Intent i, String pathName) {
|
||||||
|
boolean backup = storageAvailable() && !pathName.equals(getAutoSaveFile());
|
||||||
|
i.putExtra("org.petero.droidfish.backup", backup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the full path to the auto-save file. */
|
||||||
|
private static String getAutoSaveFile() {
|
||||||
|
String sep = File.separator;
|
||||||
|
return Environment.getExternalStorageDirectory() + sep + pgnDir + sep + ".autosave.pgn";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void autoSaveGameIfAllowed(String pgn) {
|
||||||
|
if (storageAvailable())
|
||||||
|
autoSaveGame(pgn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save a copy of the pgn data in the .autosave.pgn file. */
|
||||||
|
public static void autoSaveGame(String pgn) {
|
||||||
|
PGNFile pgnFile = new PGNFile(getAutoSaveFile());
|
||||||
|
pgnFile.autoSave(pgn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load a PGN game from a file. */
|
/** Load a PGN game from a file. */
|
||||||
@ -3489,6 +3522,7 @@ public class DroidFish extends Activity
|
|||||||
Intent i = new Intent(DroidFish.this, EditPGNLoad.class);
|
Intent i = new Intent(DroidFish.this, EditPGNLoad.class);
|
||||||
i.setAction("org.petero.droidfish.loadFile");
|
i.setAction("org.petero.droidfish.loadFile");
|
||||||
i.putExtra("org.petero.droidfish.pathname", pathName);
|
i.putExtra("org.petero.droidfish.pathname", pathName);
|
||||||
|
setEditPGNBackup(i, pathName);
|
||||||
startActivityForResult(i, RESULT_LOAD_PGN);
|
startActivityForResult(i, RESULT_LOAD_PGN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3506,11 +3540,11 @@ public class DroidFish extends Activity
|
|||||||
startActivityForResult(i, RESULT_LOAD_FEN);
|
startActivityForResult(i, RESULT_LOAD_FEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFenHelper(String fen) {
|
private void setFenHelper(String fen, boolean setModified) {
|
||||||
if (fen == null)
|
if (fen == null)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
ctrl.setFENOrPGN(fen);
|
ctrl.setFENOrPGN(fen, setModified);
|
||||||
} catch (ChessParseError e) {
|
} catch (ChessParseError e) {
|
||||||
// If FEN corresponds to illegal chess position, go into edit board mode.
|
// If FEN corresponds to illegal chess position, go into edit board mode.
|
||||||
try {
|
try {
|
||||||
|
@ -108,4 +108,7 @@ public interface GUIInterface {
|
|||||||
|
|
||||||
/** Return true if only main-line moves are to be kept. */
|
/** Return true if only main-line moves are to be kept. */
|
||||||
boolean discardVariations();
|
boolean discardVariations();
|
||||||
|
|
||||||
|
/** Save the current game to the auto-save file, if storage permission has been granted. */
|
||||||
|
void autoSaveGameIfAllowed(String pgn);
|
||||||
}
|
}
|
||||||
|
@ -128,4 +128,26 @@ public final class Util {
|
|||||||
((MoveListView) v).setTextColor(fg);
|
((MoveListView) v).setTextColor(fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return a hash value for a string, with better quality than String.hashCode(). */
|
||||||
|
public static long stringHash(String s) {
|
||||||
|
int n = s.length();
|
||||||
|
long h = n;
|
||||||
|
for (int i = 0; i < n; i += 4) {
|
||||||
|
long tmp = s.charAt(i) & 0xffff;
|
||||||
|
try {
|
||||||
|
tmp = (tmp << 16) | (s.charAt(i+1) & 0xffff);
|
||||||
|
tmp = (tmp << 16) | (s.charAt(i+2) & 0xffff);
|
||||||
|
tmp = (tmp << 16) | (s.charAt(i+3) & 0xffff);
|
||||||
|
} catch (IndexOutOfBoundsException ignore) {}
|
||||||
|
|
||||||
|
h += tmp;
|
||||||
|
|
||||||
|
h *= 0x7CF9ADC6FE4A7653L;
|
||||||
|
h ^= h >>> 37;
|
||||||
|
h *= 0xC25D3F49433E7607L;
|
||||||
|
h ^= h >>> 43;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ import android.os.Bundle;
|
|||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Pair;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -45,6 +44,7 @@ import androidx.appcompat.app.AppCompatActivity;
|
|||||||
import androidx.databinding.DataBindingUtil;
|
import androidx.databinding.DataBindingUtil;
|
||||||
|
|
||||||
import org.petero.droidfish.ColorTheme;
|
import org.petero.droidfish.ColorTheme;
|
||||||
|
import org.petero.droidfish.DroidFish;
|
||||||
import org.petero.droidfish.DroidFishApp;
|
import org.petero.droidfish.DroidFishApp;
|
||||||
import org.petero.droidfish.ObjectCache;
|
import org.petero.droidfish.ObjectCache;
|
||||||
import org.petero.droidfish.R;
|
import org.petero.droidfish.R;
|
||||||
@ -74,6 +74,7 @@ public abstract class EditPGN extends AppCompatActivity {
|
|||||||
private String lastFileName = "";
|
private String lastFileName = "";
|
||||||
private long lastModTime = -1;
|
private long lastModTime = -1;
|
||||||
private boolean useRegExp = false;
|
private boolean useRegExp = false;
|
||||||
|
private boolean backup = false; // If true, backup PGN games before overwriting
|
||||||
|
|
||||||
private Thread workThread = null;
|
private Thread workThread = null;
|
||||||
private boolean canceled = false;
|
private boolean canceled = false;
|
||||||
@ -109,6 +110,7 @@ public abstract class EditPGN extends AppCompatActivity {
|
|||||||
Intent i = getIntent();
|
Intent i = getIntent();
|
||||||
String action = i.getAction();
|
String action = i.getAction();
|
||||||
String fileName = i.getStringExtra("org.petero.droidfish.pathname");
|
String fileName = i.getStringExtra("org.petero.droidfish.pathname");
|
||||||
|
backup = i.getBooleanExtra("org.petero.droidfish.backup", false);
|
||||||
canceled = false;
|
canceled = false;
|
||||||
if ("org.petero.droidfish.loadFile".equals(action)) {
|
if ("org.petero.droidfish.loadFile".equals(action)) {
|
||||||
pgnFile = new PGNFile(fileName);
|
pgnFile = new PGNFile(fileName);
|
||||||
@ -167,36 +169,37 @@ public abstract class EditPGN extends AppCompatActivity {
|
|||||||
loadGame = false;
|
loadGame = false;
|
||||||
String token = i.getStringExtra("org.petero.droidfish.pgn");
|
String token = i.getStringExtra("org.petero.droidfish.pgn");
|
||||||
pgnToSave = (new ObjectCache()).retrieveString(token);
|
pgnToSave = (new ObjectCache()).retrieveString(token);
|
||||||
boolean silent = i.getBooleanExtra("org.petero.droidfish.silent", false);
|
pgnFile = new PGNFile(fileName);
|
||||||
if (silent) { // Silently append to file
|
showDialog(PROGRESS_DIALOG);
|
||||||
PGNFile pgnFile2 = new PGNFile(fileName);
|
workThread = new Thread(() -> {
|
||||||
pgnFile2.appendPGN(pgnToSave);
|
if (!readFile())
|
||||||
} else {
|
return;
|
||||||
pgnFile = new PGNFile(fileName);
|
runOnUiThread(() -> {
|
||||||
showDialog(PROGRESS_DIALOG);
|
if (canceled) {
|
||||||
workThread = new Thread(() -> {
|
setResult(RESULT_CANCELED);
|
||||||
if (!readFile())
|
finish();
|
||||||
return;
|
} else if (gamesInFile.isEmpty()) {
|
||||||
runOnUiThread(() -> {
|
pgnFile.appendPGN(pgnToSave, false);
|
||||||
if (canceled) {
|
saveFileFinished();
|
||||||
setResult(RESULT_CANCELED);
|
} else {
|
||||||
finish();
|
showList();
|
||||||
} else if (gamesInFile.isEmpty()) {
|
}
|
||||||
pgnFile.appendPGN(pgnToSave);
|
|
||||||
finish();
|
|
||||||
} else {
|
|
||||||
showList();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
workThread.start();
|
});
|
||||||
}
|
workThread.start();
|
||||||
} else { // Unsupported action
|
} else { // Unsupported action
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveFileFinished() {
|
||||||
|
Intent i = new Intent();
|
||||||
|
i.putExtra("org.petero.droidfish.treeHash", Util.stringHash(pgnToSave));
|
||||||
|
setResult(RESULT_OK, i);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context newBase) {
|
protected void attachBaseContext(Context newBase) {
|
||||||
super.attachBaseContext(DroidFishApp.setLanguage(newBase, false));
|
super.attachBaseContext(DroidFishApp.setLanguage(newBase, false));
|
||||||
@ -403,8 +406,9 @@ public abstract class EditPGN extends AppCompatActivity {
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pgnFile.replacePGN(pgnToSave, giToReplace);
|
doBackup(giToReplace);
|
||||||
finish();
|
pgnFile.replacePGN(pgnToSave, giToReplace, false);
|
||||||
|
saveFileFinished();
|
||||||
});
|
});
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
@ -475,6 +479,7 @@ public abstract class EditPGN extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void deleteGame(GameInfo gi) {
|
private void deleteGame(GameInfo gi) {
|
||||||
|
doBackup(gi);
|
||||||
if (pgnFile.deleteGame(gi, gamesInFile)) {
|
if (pgnFile.deleteGame(gi, gamesInFile)) {
|
||||||
createAdapter();
|
createAdapter();
|
||||||
String s = binding.selectGameFilter.getText().toString();
|
String s = binding.selectGameFilter.getText().toString();
|
||||||
@ -485,6 +490,15 @@ public abstract class EditPGN extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void doBackup(GameInfo gi) {
|
||||||
|
if (!backup)
|
||||||
|
return;
|
||||||
|
String pgn = pgnFile.readOneGame(gi);
|
||||||
|
if (pgn == null || pgn.isEmpty())
|
||||||
|
return;
|
||||||
|
DroidFish.autoSaveGame(pgn);
|
||||||
|
}
|
||||||
|
|
||||||
private void createAdapter() {
|
private void createAdapter() {
|
||||||
aa = new GameAdapter<GameInfo>(this, R.layout.select_game_list_item, gamesInFile) {
|
aa = new GameAdapter<GameInfo>(this, R.layout.select_game_list_item, gamesInFile) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -121,6 +121,7 @@ public class PGNFile {
|
|||||||
public void reset() {
|
public void reset() {
|
||||||
len = 0;
|
len = 0;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new String(buf, 0, len);
|
return new String(buf, 0, len);
|
||||||
}
|
}
|
||||||
@ -421,11 +422,37 @@ public class PGNFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Append PGN to the end of this PGN file. */
|
/** Append PGN to the end of this PGN file. */
|
||||||
public void appendPGN(String pgn) {
|
public void appendPGN(String pgn, boolean silent) {
|
||||||
mkDirs();
|
mkDirs();
|
||||||
try (FileWriter fw = new FileWriter(fileName, true)) {
|
try (FileWriter fw = new FileWriter(fileName, true)) {
|
||||||
fw.write(pgn);
|
fw.write(pgn);
|
||||||
DroidFishApp.toast(R.string.game_saved, Toast.LENGTH_SHORT);
|
if (!silent)
|
||||||
|
DroidFishApp.toast(R.string.game_saved, Toast.LENGTH_SHORT);
|
||||||
|
} catch (IOException e) {
|
||||||
|
DroidFishApp.toast(R.string.failed_to_save_game, Toast.LENGTH_SHORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save a PGN game first in the file and remove games at the end of the file
|
||||||
|
* to enforce a maximum number of games in the auto-save file. */
|
||||||
|
public void autoSave(String pgn) {
|
||||||
|
final int maxAutoSaveGames = 20;
|
||||||
|
try {
|
||||||
|
if (!fileName.exists()) {
|
||||||
|
appendPGN(pgn, true);
|
||||||
|
} else {
|
||||||
|
ArrayList<GameInfo> gamesInFile = getGameInfo(null, null);
|
||||||
|
for (int i = gamesInFile.size() - 1; i >= 0; i--) {
|
||||||
|
GameInfo gi = gamesInFile.get(i);
|
||||||
|
String oldGame = readOneGame(gi);
|
||||||
|
if (pgn.equals(oldGame))
|
||||||
|
deleteGame(gi, gamesInFile);
|
||||||
|
}
|
||||||
|
while (gamesInFile.size() > maxAutoSaveGames - 1)
|
||||||
|
deleteGame(gamesInFile.get(gamesInFile.size() - 1), gamesInFile);
|
||||||
|
GameInfo gi = new GameInfo().setNull(0);
|
||||||
|
replacePGN(pgn, gi, true);
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
DroidFishApp.toast(R.string.failed_to_save_game, Toast.LENGTH_SHORT);
|
DroidFishApp.toast(R.string.failed_to_save_game, Toast.LENGTH_SHORT);
|
||||||
}
|
}
|
||||||
@ -463,7 +490,7 @@ public class PGNFile {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void replacePGN(String pgnToSave, GameInfo gi) {
|
void replacePGN(String pgnToSave, GameInfo gi, boolean silent) {
|
||||||
try {
|
try {
|
||||||
File tmpFile = new File(fileName + ".tmp_delete");
|
File tmpFile = new File(fileName + ".tmp_delete");
|
||||||
try (RandomAccessFile fileReader = new RandomAccessFile(fileName, "r");
|
try (RandomAccessFile fileReader = new RandomAccessFile(fileName, "r");
|
||||||
@ -475,7 +502,8 @@ public class PGNFile {
|
|||||||
}
|
}
|
||||||
if (!tmpFile.renameTo(fileName))
|
if (!tmpFile.renameTo(fileName))
|
||||||
throw new IOException();
|
throw new IOException();
|
||||||
DroidFishApp.toast(R.string.game_saved, Toast.LENGTH_SHORT);
|
if (!silent)
|
||||||
|
DroidFishApp.toast(R.string.game_saved, Toast.LENGTH_SHORT);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
DroidFishApp.toast(R.string.failed_to_save_game, Toast.LENGTH_SHORT);
|
DroidFishApp.toast(R.string.failed_to_save_game, Toast.LENGTH_SHORT);
|
||||||
}
|
}
|
||||||
@ -494,6 +522,7 @@ public class PGNFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Delete the file. */
|
||||||
boolean delete() {
|
boolean delete() {
|
||||||
return fileName.delete();
|
return fileName.delete();
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ import org.petero.droidfish.gamelogic.GameTree.Node;
|
|||||||
/** The glue between the chess engine and the GUI. */
|
/** The glue between the chess engine and the GUI. */
|
||||||
public class DroidChessController {
|
public class DroidChessController {
|
||||||
private DroidComputerPlayer computerPlayer = null;
|
private DroidComputerPlayer computerPlayer = null;
|
||||||
private PgnToken.PgnTokenReceiver gameTextListener = null;
|
private PgnToken.PgnTokenReceiver gameTextListener;
|
||||||
private BookOptions bookOptions = new BookOptions();
|
private BookOptions bookOptions = new BookOptions();
|
||||||
private EngineOptions engineOptions = new EngineOptions();
|
private EngineOptions engineOptions = new EngineOptions();
|
||||||
private Game game = null;
|
private Game game = null;
|
||||||
@ -94,10 +94,25 @@ public class DroidChessController {
|
|||||||
}
|
}
|
||||||
computerPlayer.queueStartEngine(searchId, engine);
|
computerPlayer.queueStartEngine(searchId, engine);
|
||||||
searchId++;
|
searchId++;
|
||||||
|
Game oldGame = game;
|
||||||
game = new Game(gameTextListener, tcData);
|
game = new Game(gameTextListener, tcData);
|
||||||
computerPlayer.uciNewGame();
|
computerPlayer.uciNewGame();
|
||||||
setPlayerNames(game);
|
setPlayerNames(game);
|
||||||
updateGameMode();
|
updateGameMode();
|
||||||
|
game.resetModified(pgnOptions);
|
||||||
|
autoSaveOldGame(oldGame, game.treeHashSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Save old game if has been modified since start/load of game and is
|
||||||
|
* not equal to the new game. */
|
||||||
|
private void autoSaveOldGame(Game oldGame, long newGameHash) {
|
||||||
|
if (oldGame == null)
|
||||||
|
return;
|
||||||
|
String pgn = oldGame.tree.toPGN(pgnOptions);
|
||||||
|
long oldGameOrigHash = oldGame.treeHashSignature;
|
||||||
|
long oldGameCurrHash = Util.stringHash(pgn);
|
||||||
|
if (oldGameCurrHash != oldGameOrigHash && oldGameCurrHash != newGameHash)
|
||||||
|
gui.autoSaveGameIfAllowed(pgn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start playing a new game. Should be called after newGame(). */
|
/** Start playing a new game. Should be called after newGame(). */
|
||||||
@ -137,7 +152,7 @@ public class DroidChessController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameMode getGameMode() {
|
public final GameMode getGameMode() {
|
||||||
return gameMode;
|
return gameMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +260,7 @@ public class DroidChessController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Parse a string as FEN or PGN data. */
|
/** Parse a string as FEN or PGN data. */
|
||||||
public final synchronized void setFENOrPGN(String fenPgn) throws ChessParseError {
|
public final synchronized void setFENOrPGN(String fenPgn, boolean setModified) throws ChessParseError {
|
||||||
if (!fenPgn.isEmpty() && fenPgn.charAt(0) == '\ufeff')
|
if (!fenPgn.isEmpty() && fenPgn.charAt(0) == '\ufeff')
|
||||||
fenPgn = fenPgn.substring(1); // Remove BOM
|
fenPgn = fenPgn.substring(1); // Remove BOM
|
||||||
Game newGame = new Game(gameTextListener, game.timeController.tcData);
|
Game newGame = new Game(gameTextListener, game.timeController.tcData);
|
||||||
@ -260,6 +275,7 @@ public class DroidChessController {
|
|||||||
newGame.tree.translateMoves();
|
newGame.tree.translateMoves();
|
||||||
}
|
}
|
||||||
searchId++;
|
searchId++;
|
||||||
|
Game oldGame = game;
|
||||||
game = newGame;
|
game = newGame;
|
||||||
gameTextListener.clear();
|
gameTextListener.clear();
|
||||||
updateGameMode();
|
updateGameMode();
|
||||||
@ -268,6 +284,14 @@ public class DroidChessController {
|
|||||||
updateComputeThreads();
|
updateComputeThreads();
|
||||||
gui.setSelection(-1);
|
gui.setSelection(-1);
|
||||||
updateGUI();
|
updateGUI();
|
||||||
|
game.resetModified(pgnOptions);
|
||||||
|
autoSaveOldGame(oldGame, game.treeHashSignature);
|
||||||
|
if (setModified)
|
||||||
|
game.treeHashSignature = oldGame.treeHashSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final synchronized void setLastSaveHash(long hash) {
|
||||||
|
game.treeHashSignature = hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** True if human's turn to make a move. (True in analysis mode.) */
|
/** True if human's turn to make a move. (True in analysis mode.) */
|
||||||
|
@ -27,11 +27,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.petero.droidfish.PGNOptions;
|
import org.petero.droidfish.PGNOptions;
|
||||||
|
import org.petero.droidfish.Util;
|
||||||
import org.petero.droidfish.gamelogic.GameTree.Node;
|
import org.petero.droidfish.gamelogic.GameTree.Node;
|
||||||
|
|
||||||
public class Game {
|
public class Game {
|
||||||
boolean pendingDrawOffer;
|
boolean pendingDrawOffer;
|
||||||
public GameTree tree;
|
public GameTree tree;
|
||||||
|
long treeHashSignature; // Hash corresponding to "tree" when last saved to file
|
||||||
TimeControl timeController;
|
TimeControl timeController;
|
||||||
private boolean gamePaused;
|
private boolean gamePaused;
|
||||||
/** If true, add new moves as mainline moves. */
|
/** If true, add new moves as mainline moves. */
|
||||||
@ -51,6 +53,8 @@ public class Game {
|
|||||||
/** De-serialize from input stream. */
|
/** De-serialize from input stream. */
|
||||||
final void readFromStream(DataInputStream dis, int version) throws IOException, ChessParseError {
|
final void readFromStream(DataInputStream dis, int version) throws IOException, ChessParseError {
|
||||||
tree.readFromStream(dis, version);
|
tree.readFromStream(dis, version);
|
||||||
|
if (version >= 4)
|
||||||
|
treeHashSignature = dis.readLong();
|
||||||
if (version >= 3)
|
if (version >= 3)
|
||||||
timeController.readFromStream(dis, version);
|
timeController.readFromStream(dis, version);
|
||||||
updateTimeControl(true);
|
updateTimeControl(true);
|
||||||
@ -59,9 +63,15 @@ public class Game {
|
|||||||
/** Serialize to output stream. */
|
/** Serialize to output stream. */
|
||||||
final synchronized void writeToStream(DataOutputStream dos) throws IOException {
|
final synchronized void writeToStream(DataOutputStream dos) throws IOException {
|
||||||
tree.writeToStream(dos);
|
tree.writeToStream(dos);
|
||||||
|
dos.writeLong(treeHashSignature);
|
||||||
timeController.writeToStream(dos);
|
timeController.writeToStream(dos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set game to "not modified" state. */
|
||||||
|
final void resetModified(PGNOptions options) {
|
||||||
|
treeHashSignature = Util.stringHash(tree.toPGN(options));
|
||||||
|
}
|
||||||
|
|
||||||
public final void setGamePaused(boolean gamePaused) {
|
public final void setGamePaused(boolean gamePaused) {
|
||||||
if (gamePaused != this.gamePaused) {
|
if (gamePaused != this.gamePaused) {
|
||||||
this.gamePaused = gamePaused;
|
this.gamePaused = gamePaused;
|
||||||
|
@ -458,6 +458,15 @@ game with the selected position.
|
|||||||
|
|
||||||
Saving a position to a FEN/EPD file has not been implemented.
|
Saving a position to a FEN/EPD file has not been implemented.
|
||||||
|
|
||||||
|
## Autosave
|
||||||
|
|
||||||
|
When an action is performed that causes the current game to be discarded, the
|
||||||
|
game is automatically saved in the file `DroidFish/pgn/.autosave.pgn` before
|
||||||
|
being discarded. The autosave file has a maximum size of 20 games and the most
|
||||||
|
recently autosaved game is stored first in the file. If the number of games
|
||||||
|
becomes too large, the oldest stored game is removed from the file.
|
||||||
|
|
||||||
|
|
||||||
## OI File Manager
|
## OI File Manager
|
||||||
|
|
||||||
If the [*OI File Manager*](https://play.google.com/store/apps/details?id=org.openintents.filemanager)
|
If the [*OI File Manager*](https://play.google.com/store/apps/details?id=org.openintents.filemanager)
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user