changeset 76:d7c38ce83421

working on disk-backup for save-states
author Robert McIntyre <rlm@mit.edu>
date Thu, 08 Mar 2012 19:48:54 -0600
parents eb7d4efe0f34
children 9ba461a5c60f
files clojure/com/aurellem/fragments.clj clojure/com/aurellem/gb_driver.clj java/src/com/aurellem/gb/Gb.java src/clojure/clojure.cpp src/gb/GB.cpp src/gb/GB.h
diffstat 6 files changed, 134 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
     1.1 --- a/clojure/com/aurellem/fragments.clj	Thu Mar 08 06:01:09 2012 -0600
     1.2 +++ b/clojure/com/aurellem/fragments.clj	Thu Mar 08 19:48:54 2012 -0600
     1.3 @@ -17,18 +17,19 @@
     1.4                  (repeat (- start-frames n 1) []))
     1.5          moves (drop start moves)]
     1.6      (goto start)
     1.7 -    (dorun (map step moves))
     1.8 +    (dorun (map step! moves))
     1.9      (nth (registers) 2)))
    1.10  
    1.11 -
    1.12  (defn earliest-press
    1.13    [start]
    1.14    (print "determining bad program-counter...")
    1.15 +  (reset)
    1.16 +  (dorun (dotimes [_ start-frames] (step)))
    1.17    (let [bad-counter (play-start 0 0)]
    1.18      (println bad-counter)
    1.19      (loop [n start]
    1.20        (print "trying" n "...")
    1.21 -      (let [nth-counter (play-start n)]
    1.22 +      (let [nth-counter (play-start (dec n) n)]
    1.23          (println "got" nth-counter)
    1.24          (if (= nth-counter bad-counter)
    1.25            (recur (inc n)) n)))))
     2.1 --- a/clojure/com/aurellem/gb_driver.clj	Thu Mar 08 06:01:09 2012 -0600
     2.2 +++ b/clojure/com/aurellem/gb_driver.clj	Thu Mar 08 19:48:54 2012 -0600
     2.3 @@ -5,6 +5,15 @@
     2.4  
     2.5  (Gb/loadVBA)
     2.6  
     2.7 +(def ^:dynamic *max-history* 1e4)
     2.8 +
     2.9 +(def ^:dynamic *backup-saves-to-disk* true)
    2.10 +
    2.11 +(def ^:dynamic *save-history* true)
    2.12 +
    2.13 +(def ^:dynamic *save-state-cache*
    2.14 +  (File. "/home/r/proj/pokemon-escape/save-states/"))
    2.15 +
    2.16  (def yellow-rom-image
    2.17    (File. "/home/r/proj/pokemon-escape/roms/yellow.gbc"))
    2.18  
    2.19 @@ -24,9 +33,7 @@
    2.20  
    2.21  (defn cpu-data [size arr-fn]
    2.22    (let [store (int-array size)]
    2.23 -    (fn [] 
    2.24 -      (arr-fn store)
    2.25 -      store)))
    2.26 +    (fn [] (arr-fn store) store)))
    2.27  
    2.28  (def ram
    2.29    (cpu-data (Gb/getRAMSize) #(Gb/getRAM %)))
    2.30 @@ -73,35 +80,74 @@
    2.31              (recur (conj buttons button) (rest masks))
    2.32              (recur buttons (rest masks)))))))
    2.33  
    2.34 +(defrecord SaveState [frame save-data])
    2.35  
    2.36 -(defn save-state [] (Gb/saveState))
    2.37 +(defn frame [] @current-frame)
    2.38  
    2.39 -(def history (atom {}))
    2.40 +(defn save-state []
    2.41 +  (SaveState.
    2.42 +   (frame)
    2.43 +   (Gb/saveState)))
    2.44 +
    2.45 +(defn load-state [#^SaveState save]
    2.46 +  (reset! current-frame (:frame save))
    2.47 +  (Gb/loadState (:save-data save)))
    2.48 +
    2.49 +(def empty-history (sorted-map))
    2.50 +
    2.51 +(def history (atom empty-history))
    2.52 +
    2.53 +(defn frame->disk-save [frame]
    2.54 +  (File. *save-state-cache*
    2.55 +         (format "%07d.sav" frame)))
    2.56 +
    2.57 +(defn get-save-from-disk [frame]
    2.58 +  (let [save (frame->disk-save frame)]
    2.59 +    (if (.exists save)
    2.60 +      (let [buf (Gb/saveBuffer)
    2.61 +            bytes (org.apache.commons.io.FileUtils/readFileToByteArray
    2.62 +                   save)]
    2.63 +        (.put buf bytes)
    2.64 +        (.flip buf)
    2.65 +        (SaveState. frame buf)))))
    2.66 +
    2.67 +(defn store-save-to-disk [^SaveState save]
    2.68 +  (let [buf (:save-data save)
    2.69 +        bytes (byte-array (.limit buf))
    2.70 +        dest (frame->disk-save (:frame save))]
    2.71 +    (.get buf bytes)
    2.72 +    (org.apache.commons.io.FileUtils/writeByteArrayToFile
    2.73 +     dest bytes)
    2.74 +    (.rewind buf)))
    2.75 +
    2.76 +(defn find-save-state [frame]
    2.77 +  (let [save (@history frame)]
    2.78 +    (if (not (nil? save)) save
    2.79 +        (get-save-from-disk frame))))
    2.80  
    2.81  (defn goto [frame]
    2.82 -  (let [save (@history frame)]
    2.83 -    (if (not (nil? save))
    2.84 +  (let [save (find-save-state frame)]
    2.85 +    (if (nil? save)
    2.86 +      (println frame "is not in history")
    2.87        (do
    2.88          (reset! current-frame frame)
    2.89 -        (Gb/loadState save))
    2.90 -      (println "no backup state"))))
    2.91 +        (load-state save)))))
    2.92  
    2.93 -(defn clear-history [] (reset! history {}))
    2.94 +(defn clear-history [] (reset! history empty-history))
    2.95  
    2.96  (defn rewind
    2.97 -  ([n] (goto (- @current-frame n)))
    2.98 -  ([] (rewind 1)))
    2.99 -  
   2.100 +  ([] (rewind 1))
   2.101 +  ([n] (goto (- @current-frame n))))
   2.102 +
   2.103  (defn backup-state [frame]
   2.104 -  (swap! history #(assoc % frame (save-state))))
   2.105 -
   2.106 -(def ^:dynamic *save-history* true)
   2.107 +  (swap! history #(assoc % frame (save-state)))
   2.108 +  (if (> (count @history) *max-history*)
   2.109 +    (swap! history #(dissoc % (first (first %))))))
   2.110  
   2.111  (defn advance []
   2.112 -  (swap! current-frame inc)
   2.113    (if *save-history*
   2.114 -    (let [save (save-state)]
   2.115 -      (backup-state @current-frame))))
   2.116 +    (backup-state @current-frame))
   2.117 +  (swap! current-frame inc))
   2.118  
   2.119  (defn step
   2.120    ([] (advance) (Gb/step))
   2.121 @@ -114,7 +160,13 @@
   2.122  (defn step! [& args]
   2.123    (binding [*save-history* false]
   2.124      (apply step args)))
   2.125 +  
   2.126 +(defn play
   2.127 +  ([n] (dorun (dotimes [_ n] (step)))) 
   2.128 +  ([] (play Integer/MAX_VALUE)))
   2.129  
   2.130 -(defn frame [] @current-frame)
   2.131 -  
   2.132 -
   2.133 +(defn buf-seq [buffer]
   2.134 +  (let [bytes (byte-array (.capacity buffer))]
   2.135 +    (.get buffer bytes)
   2.136 +    (.rewind buffer)
   2.137 +    (seq bytes)))
   2.138 \ No newline at end of file
     3.1 --- a/java/src/com/aurellem/gb/Gb.java	Thu Mar 08 06:01:09 2012 -0600
     3.2 +++ b/java/src/com/aurellem/gb/Gb.java	Thu Mar 08 19:48:54 2012 -0600
     3.3 @@ -32,24 +32,49 @@
     3.4  
     3.5      public static native void shutdown();
     3.6  
     3.7 -    public static native void saveState(ByteBuffer buffer, int size);
     3.8 +    public static native long saveState(ByteBuffer buffer, int size);
     3.9  
    3.10      public static native void loadState(ByteBuffer buffer, int size);
    3.11  
    3.12 -    public static final int SAVE_SIZE = 90000;
    3.13 +    public static final int MAX_SAVE_SIZE = 90000;
    3.14  
    3.15 -    public static ByteBuffer saveState(){
    3.16 +    public static ByteBuffer createDirectByteBuffer(int capacity){
    3.17 +	byte[] zeros = new byte[capacity];
    3.18  	ByteBuffer buf = 
    3.19 -	    ByteBuffer.allocateDirect(SAVE_SIZE)
    3.20 +	    ByteBuffer.allocateDirect(capacity)
    3.21  	              .order(ByteOrder.nativeOrder());
    3.22 +	buf.put(zeros);
    3.23  	buf.clear();
    3.24 -	saveState(buf, SAVE_SIZE);
    3.25 -	buf.flip();
    3.26  	return buf;
    3.27      }
    3.28  
    3.29 +    public static ByteBuffer saveBuffer(){
    3.30 +	return createDirectByteBuffer(MAX_SAVE_SIZE);
    3.31 +    }
    3.32 +
    3.33 +    public static ByteBuffer saveState(){
    3.34 +	ByteBuffer buf = saveBuffer();
    3.35 +	
    3.36 +	saveState(buf, buf.capacity());
    3.37 +	
    3.38 +	// determine the extent of the saved data
    3.39 +	int position = buf.capacity() - 1;
    3.40 +	for (int i = position; i > 0; i--){
    3.41 +	    if (0 != buf.get(i)){
    3.42 +		position = i;
    3.43 +		break;
    3.44 +	    }}
    3.45 +	System.out.println("Position: " + position);
    3.46 +	byte[] saveArray = new byte[position];
    3.47 +	ByteBuffer save = createDirectByteBuffer(position);
    3.48 +	buf.get(saveArray, 0 , position);
    3.49 +	save.put(saveArray);
    3.50 +	save.rewind();
    3.51 +	return save;
    3.52 +    }
    3.53 +
    3.54      public static void loadState(ByteBuffer saveState){
    3.55 -	loadState(saveState, SAVE_SIZE);
    3.56 +	loadState(saveState, MAX_SAVE_SIZE);
    3.57      }
    3.58  
    3.59      public static native int getROMSize();
     4.1 --- a/src/clojure/clojure.cpp	Thu Mar 08 06:01:09 2012 -0600
     4.2 +++ b/src/clojure/clojure.cpp	Thu Mar 08 19:48:54 2012 -0600
     4.3 @@ -79,13 +79,14 @@
     4.4  /*
     4.5   * Class:     com_aurellem_gb_Gb
     4.6   * Method:    saveState
     4.7 - * Signature: (Ljava/nio/ByteBuffer;)V
     4.8 + * Signature: (Ljava/nio/ByteBuffer;I)J
     4.9   */
    4.10 -JNIEXPORT void JNICALL Java_com_aurellem_gb_Gb_saveState
    4.11 +JNIEXPORT jlong JNICALL Java_com_aurellem_gb_Gb_saveState
    4.12  (JNIEnv *env, jclass clazz, jobject buffer, jint size){
    4.13    char* buffer_address = 
    4.14      ((char*) env->GetDirectBufferAddress(buffer));
    4.15 -  gbWriteMemSaveState(buffer_address, size);
    4.16 +  long limit = gbWriteMemSaveStatePos(buffer_address, size);
    4.17 +  return limit;
    4.18  }
    4.19  
    4.20  /*
     5.1 --- a/src/gb/GB.cpp	Thu Mar 08 06:01:09 2012 -0600
     5.2 +++ b/src/gb/GB.cpp	Thu Mar 08 19:48:54 2012 -0600
     5.3 @@ -2463,14 +2463,16 @@
     5.4  
     5.5  bool gbWriteSaveStateToStream(gzFile gzFile)
     5.6  {
     5.7 +  
     5.8    utilWriteInt(gzFile, GBSAVE_GAME_VERSION);
     5.9  
    5.10 +  
    5.11    utilGzWrite(gzFile, &gbRom[0x134], 15);
    5.12  
    5.13    utilWriteData(gzFile, gbSaveGameStruct);
    5.14 -
    5.15 +  
    5.16    utilGzWrite(gzFile, &IFF, 2);
    5.17 -
    5.18 +  
    5.19    if (gbSgbMode)
    5.20      {
    5.21        gbSgbSaveGame(gzFile);
    5.22 @@ -2549,11 +2551,12 @@
    5.23      utilGzWrite(gzFile, &GBSystemCounters.laggedLast, sizeof(GBSystemCounters.laggedLast));
    5.24    }
    5.25  
    5.26 +  utilWriteInt(gzFile, 0x07); // RLM this is the end of file marker.
    5.27    return true;
    5.28  }
    5.29  
    5.30 -bool gbWriteMemSaveState(char *memory, int available)
    5.31 -{
    5.32 +
    5.33 +long gbWriteMemSaveStatePos(char *memory, int available){
    5.34    gzFile gzFile = utilMemGzOpen(memory, available, "w");
    5.35  
    5.36    if (gzFile == NULL)
    5.37 @@ -2565,12 +2568,21 @@
    5.38  
    5.39    long pos = utilGzTell(gzFile) + 8;
    5.40  
    5.41 -  if (pos >= (available))
    5.42 -    res = false;
    5.43 +  if (pos >= available){
    5.44 +    pos = 0;
    5.45 +  }
    5.46  
    5.47    utilGzClose(gzFile);
    5.48  
    5.49 -  return res;
    5.50 +  return pos;
    5.51 +
    5.52 +}
    5.53 +
    5.54 +bool gbWriteMemSaveState(char *memory, int available)
    5.55 +{
    5.56 +  long pos = gbWriteMemSaveStatePos(memory, available);
    5.57 +  if (pos > 0) { return true; }
    5.58 +  else{ return false; }
    5.59  }
    5.60  
    5.61  bool gbWriteSaveState(const char *name)
     6.1 --- a/src/gb/GB.h	Thu Mar 08 06:01:09 2012 -0600
     6.2 +++ b/src/gb/GB.h	Thu Mar 08 19:48:54 2012 -0600
     6.3 @@ -51,6 +51,7 @@
     6.4  extern void storeWRam(int32 *);
     6.5  extern void storeVRam(int32 *);
     6.6  extern void storeRegisters(int32 *);
     6.7 +extern long gbWriteMemSaveStatePos(char *, int);
     6.8  
     6.9  extern struct EmulatedSystem GBSystem;
    6.10  extern struct EmulatedSystemCounters &GBSystemCounters;