# HG changeset patch # User Robert McIntyre # Date 1331257734 21600 # Node ID d7c38ce834214484eb317b9ed379c6a2d87d7fc0 # Parent eb7d4efe0f34eef1024db689dfb43a92e3ed3c14 working on disk-backup for save-states diff -r eb7d4efe0f34 -r d7c38ce83421 clojure/com/aurellem/fragments.clj --- a/clojure/com/aurellem/fragments.clj Thu Mar 08 06:01:09 2012 -0600 +++ b/clojure/com/aurellem/fragments.clj Thu Mar 08 19:48:54 2012 -0600 @@ -17,18 +17,19 @@ (repeat (- start-frames n 1) [])) moves (drop start moves)] (goto start) - (dorun (map step moves)) + (dorun (map step! moves)) (nth (registers) 2))) - (defn earliest-press [start] (print "determining bad program-counter...") + (reset) + (dorun (dotimes [_ start-frames] (step))) (let [bad-counter (play-start 0 0)] (println bad-counter) (loop [n start] (print "trying" n "...") - (let [nth-counter (play-start n)] + (let [nth-counter (play-start (dec n) n)] (println "got" nth-counter) (if (= nth-counter bad-counter) (recur (inc n)) n))))) diff -r eb7d4efe0f34 -r d7c38ce83421 clojure/com/aurellem/gb_driver.clj --- a/clojure/com/aurellem/gb_driver.clj Thu Mar 08 06:01:09 2012 -0600 +++ b/clojure/com/aurellem/gb_driver.clj Thu Mar 08 19:48:54 2012 -0600 @@ -5,6 +5,15 @@ (Gb/loadVBA) +(def ^:dynamic *max-history* 1e4) + +(def ^:dynamic *backup-saves-to-disk* true) + +(def ^:dynamic *save-history* true) + +(def ^:dynamic *save-state-cache* + (File. "/home/r/proj/pokemon-escape/save-states/")) + (def yellow-rom-image (File. "/home/r/proj/pokemon-escape/roms/yellow.gbc")) @@ -24,9 +33,7 @@ (defn cpu-data [size arr-fn] (let [store (int-array size)] - (fn [] - (arr-fn store) - store))) + (fn [] (arr-fn store) store))) (def ram (cpu-data (Gb/getRAMSize) #(Gb/getRAM %))) @@ -73,35 +80,74 @@ (recur (conj buttons button) (rest masks)) (recur buttons (rest masks))))))) +(defrecord SaveState [frame save-data]) -(defn save-state [] (Gb/saveState)) +(defn frame [] @current-frame) -(def history (atom {})) +(defn save-state [] + (SaveState. + (frame) + (Gb/saveState))) + +(defn load-state [#^SaveState save] + (reset! current-frame (:frame save)) + (Gb/loadState (:save-data save))) + +(def empty-history (sorted-map)) + +(def history (atom empty-history)) + +(defn frame->disk-save [frame] + (File. *save-state-cache* + (format "%07d.sav" frame))) + +(defn get-save-from-disk [frame] + (let [save (frame->disk-save frame)] + (if (.exists save) + (let [buf (Gb/saveBuffer) + bytes (org.apache.commons.io.FileUtils/readFileToByteArray + save)] + (.put buf bytes) + (.flip buf) + (SaveState. frame buf))))) + +(defn store-save-to-disk [^SaveState save] + (let [buf (:save-data save) + bytes (byte-array (.limit buf)) + dest (frame->disk-save (:frame save))] + (.get buf bytes) + (org.apache.commons.io.FileUtils/writeByteArrayToFile + dest bytes) + (.rewind buf))) + +(defn find-save-state [frame] + (let [save (@history frame)] + (if (not (nil? save)) save + (get-save-from-disk frame)))) (defn goto [frame] - (let [save (@history frame)] - (if (not (nil? save)) + (let [save (find-save-state frame)] + (if (nil? save) + (println frame "is not in history") (do (reset! current-frame frame) - (Gb/loadState save)) - (println "no backup state")))) + (load-state save))))) -(defn clear-history [] (reset! history {})) +(defn clear-history [] (reset! history empty-history)) (defn rewind - ([n] (goto (- @current-frame n))) - ([] (rewind 1))) - + ([] (rewind 1)) + ([n] (goto (- @current-frame n)))) + (defn backup-state [frame] - (swap! history #(assoc % frame (save-state)))) - -(def ^:dynamic *save-history* true) + (swap! history #(assoc % frame (save-state))) + (if (> (count @history) *max-history*) + (swap! history #(dissoc % (first (first %)))))) (defn advance [] - (swap! current-frame inc) (if *save-history* - (let [save (save-state)] - (backup-state @current-frame)))) + (backup-state @current-frame)) + (swap! current-frame inc)) (defn step ([] (advance) (Gb/step)) @@ -114,7 +160,13 @@ (defn step! [& args] (binding [*save-history* false] (apply step args))) + +(defn play + ([n] (dorun (dotimes [_ n] (step)))) + ([] (play Integer/MAX_VALUE))) -(defn frame [] @current-frame) - - +(defn buf-seq [buffer] + (let [bytes (byte-array (.capacity buffer))] + (.get buffer bytes) + (.rewind buffer) + (seq bytes))) \ No newline at end of file diff -r eb7d4efe0f34 -r d7c38ce83421 java/src/com/aurellem/gb/Gb.java --- a/java/src/com/aurellem/gb/Gb.java Thu Mar 08 06:01:09 2012 -0600 +++ b/java/src/com/aurellem/gb/Gb.java Thu Mar 08 19:48:54 2012 -0600 @@ -32,24 +32,49 @@ public static native void shutdown(); - public static native void saveState(ByteBuffer buffer, int size); + public static native long saveState(ByteBuffer buffer, int size); public static native void loadState(ByteBuffer buffer, int size); - public static final int SAVE_SIZE = 90000; + public static final int MAX_SAVE_SIZE = 90000; - public static ByteBuffer saveState(){ + public static ByteBuffer createDirectByteBuffer(int capacity){ + byte[] zeros = new byte[capacity]; ByteBuffer buf = - ByteBuffer.allocateDirect(SAVE_SIZE) + ByteBuffer.allocateDirect(capacity) .order(ByteOrder.nativeOrder()); + buf.put(zeros); buf.clear(); - saveState(buf, SAVE_SIZE); - buf.flip(); return buf; } + public static ByteBuffer saveBuffer(){ + return createDirectByteBuffer(MAX_SAVE_SIZE); + } + + public static ByteBuffer saveState(){ + ByteBuffer buf = saveBuffer(); + + saveState(buf, buf.capacity()); + + // determine the extent of the saved data + int position = buf.capacity() - 1; + for (int i = position; i > 0; i--){ + if (0 != buf.get(i)){ + position = i; + break; + }} + System.out.println("Position: " + position); + byte[] saveArray = new byte[position]; + ByteBuffer save = createDirectByteBuffer(position); + buf.get(saveArray, 0 , position); + save.put(saveArray); + save.rewind(); + return save; + } + public static void loadState(ByteBuffer saveState){ - loadState(saveState, SAVE_SIZE); + loadState(saveState, MAX_SAVE_SIZE); } public static native int getROMSize(); diff -r eb7d4efe0f34 -r d7c38ce83421 src/clojure/clojure.cpp --- a/src/clojure/clojure.cpp Thu Mar 08 06:01:09 2012 -0600 +++ b/src/clojure/clojure.cpp Thu Mar 08 19:48:54 2012 -0600 @@ -79,13 +79,14 @@ /* * Class: com_aurellem_gb_Gb * Method: saveState - * Signature: (Ljava/nio/ByteBuffer;)V + * Signature: (Ljava/nio/ByteBuffer;I)J */ -JNIEXPORT void JNICALL Java_com_aurellem_gb_Gb_saveState +JNIEXPORT jlong JNICALL Java_com_aurellem_gb_Gb_saveState (JNIEnv *env, jclass clazz, jobject buffer, jint size){ char* buffer_address = ((char*) env->GetDirectBufferAddress(buffer)); - gbWriteMemSaveState(buffer_address, size); + long limit = gbWriteMemSaveStatePos(buffer_address, size); + return limit; } /* diff -r eb7d4efe0f34 -r d7c38ce83421 src/gb/GB.cpp --- a/src/gb/GB.cpp Thu Mar 08 06:01:09 2012 -0600 +++ b/src/gb/GB.cpp Thu Mar 08 19:48:54 2012 -0600 @@ -2463,14 +2463,16 @@ bool gbWriteSaveStateToStream(gzFile gzFile) { + utilWriteInt(gzFile, GBSAVE_GAME_VERSION); + utilGzWrite(gzFile, &gbRom[0x134], 15); utilWriteData(gzFile, gbSaveGameStruct); - + utilGzWrite(gzFile, &IFF, 2); - + if (gbSgbMode) { gbSgbSaveGame(gzFile); @@ -2549,11 +2551,12 @@ utilGzWrite(gzFile, &GBSystemCounters.laggedLast, sizeof(GBSystemCounters.laggedLast)); } + utilWriteInt(gzFile, 0x07); // RLM this is the end of file marker. return true; } -bool gbWriteMemSaveState(char *memory, int available) -{ + +long gbWriteMemSaveStatePos(char *memory, int available){ gzFile gzFile = utilMemGzOpen(memory, available, "w"); if (gzFile == NULL) @@ -2565,12 +2568,21 @@ long pos = utilGzTell(gzFile) + 8; - if (pos >= (available)) - res = false; + if (pos >= available){ + pos = 0; + } utilGzClose(gzFile); - return res; + return pos; + +} + +bool gbWriteMemSaveState(char *memory, int available) +{ + long pos = gbWriteMemSaveStatePos(memory, available); + if (pos > 0) { return true; } + else{ return false; } } bool gbWriteSaveState(const char *name) diff -r eb7d4efe0f34 -r d7c38ce83421 src/gb/GB.h --- a/src/gb/GB.h Thu Mar 08 06:01:09 2012 -0600 +++ b/src/gb/GB.h Thu Mar 08 19:48:54 2012 -0600 @@ -51,6 +51,7 @@ extern void storeWRam(int32 *); extern void storeVRam(int32 *); extern void storeRegisters(int32 *); +extern long gbWriteMemSaveStatePos(char *, int); extern struct EmulatedSystem GBSystem; extern struct EmulatedSystemCounters &GBSystemCounters;