Add Glulxe and Git. They compile but don't work yet.
[rodin/chimara.git] / interpreters / git / saveundo.c
diff --git a/interpreters/git/saveundo.c b/interpreters/git/saveundo.c
new file mode 100644 (file)
index 0000000..13620f3
--- /dev/null
@@ -0,0 +1,347 @@
+// $Id: saveundo.c,v 1.15 2003/10/20 16:05:06 iain Exp $
+
+#include "git.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+typedef const git_uint8 * MemoryPage;
+typedef MemoryPage * MemoryMap;
+
+typedef struct UndoRecord UndoRecord;
+
+struct UndoRecord
+{
+    git_uint32   endMem;
+    MemoryMap    memoryMap;
+    git_sint32   stackSize;
+    git_sint32 * stack;
+    glui32       heapSize;
+    glui32     * heap;
+    UndoRecord * prev;
+    UndoRecord * next;
+};
+
+static UndoRecord * gUndo = NULL;
+static git_uint32 gUndoSize = 0;
+static git_uint32 gMaxUndoSize = 256 * 1024;
+
+static void reserveSpace (git_uint32);
+static void deleteRecord (UndoRecord * u);
+
+void initUndo (git_uint32 size)
+{
+    gMaxUndoSize = size;
+    gUndoSize = 0;
+    gUndo = NULL;
+}
+
+int saveUndo (git_sint32 * base, git_sint32 * sp)
+{
+    git_uint32 undoSize = sizeof(UndoRecord);
+    git_uint32 mapSize = sizeof(MemoryPage*) * (gEndMem - gRamStart) / 256;
+    git_uint32 stackSize = sizeof(git_sint32*) * (sp - base);
+    git_uint32 totalSize = undoSize + mapSize + stackSize;
+
+    git_uint32 addr = gRamStart; // Address in glulx memory.
+    git_uint32 slot = 0;         // Slot in our memory map.
+    
+    UndoRecord * undo = malloc (undoSize);
+    if (undo == NULL)
+        fatalError ("Couldn't allocate undo record");
+        
+    undo->endMem = gEndMem;
+    undo->memoryMap = malloc (mapSize);
+    undo->stackSize = stackSize;
+    undo->stack = malloc (stackSize);
+    undo->prev = NULL;
+    undo->next = NULL;
+
+    if (undo->memoryMap == NULL || undo->stack == NULL)
+        fatalError ("Couldn't allocate memory for undo");
+
+    // Save the stack.
+    memcpy (undo->stack, base, undo->stackSize);
+
+    // Are we diffing against the previous undo record,
+    // or against the initial gamefile state?
+    if (gUndo == NULL)
+    {
+        // We're diffing against the gamefile.        
+        for ( ; addr < gExtStart ; addr += 256, ++slot)
+        {
+            if (memcmp (gRom + addr, gRam + addr, 256) != 0)
+            {
+                // We need to save this page.
+                git_uint8 * page = malloc(256);
+                if (page == NULL)
+                    fatalError ("Couldn't allocate memory for undo");
+                    
+                memcpy (page, gRam + addr, 256);
+                undo->memoryMap[slot] = page;
+                totalSize += 256;
+            }
+            else
+            {
+                // We don't need to save this page.
+                // Just make it point into ROM.
+                undo->memoryMap[slot] = gRom + addr;
+            }
+        }
+
+        // If the memory map has been extended, save the exended area
+        for (addr = gExtStart ; addr < gEndMem ; addr += 256, ++slot)
+        {
+            git_uint8 * page = malloc(256);
+            if (page == NULL)
+                fatalError ("Couldn't allocate memory for undo");
+                
+            memcpy (page, gRam + addr, 256);
+            undo->memoryMap[slot] = page;
+            totalSize += 256;
+        }
+    }
+    else
+    {
+        // We're diffing against the most recent undo record.
+        git_uint32 endMem = (gUndo->endMem < gEndMem) ? gUndo->endMem : gEndMem;
+        for ( ; addr < endMem ; addr += 256, ++slot)
+        {
+            if (memcmp (gUndo->memoryMap [slot], gRam + addr, 256) != 0)
+            {
+                // We need to save this page.
+                git_uint8 * page = malloc(256);
+                memcpy (page, gRam + addr, 256);
+                undo->memoryMap[slot] = page;
+                totalSize += 256;
+            }
+            else
+            {
+                // We don't need to save this page. Just copy
+                // the pointer from the previous undo record.
+                undo->memoryMap[slot] = gUndo->memoryMap[slot];
+            }
+        }
+
+        // If the memory map has been extended, save the exended area
+        for (addr = endMem ; addr < gEndMem ; addr += 256, ++slot)
+        {
+            git_uint8 * page = malloc(256);
+            if (page == NULL)
+                fatalError ("Couldn't allocate memory for undo");
+                
+            memcpy (page, gRam + addr, 256);
+            undo->memoryMap[slot] = page;
+            totalSize += 256;
+        }
+    }
+
+    // Save the heap.
+    if (heap_get_summary (&(undo->heapSize), &(undo->heap)))
+        fatalError ("Couldn't get heap summary");
+    totalSize += undo->heapSize * 4;
+
+    // Link this record into the undo list.
+    
+    undo->prev = gUndo;
+    if (gUndo)
+        gUndo->next = undo;
+    
+    gUndo = undo;
+    gUndoSize += totalSize;
+
+    // Delete old records until we have enough free space.
+    reserveSpace (0);
+
+    // And we're done.
+    return 0;
+}
+
+int restoreUndo (git_sint32* base, git_uint32 protectPos, git_uint32 protectSize)
+{
+    if (gUndo == NULL)
+    {
+        // Nothing to undo!
+        return 1;
+    }
+    else
+    {
+        UndoRecord * undo = gUndo;
+
+        git_uint32 addr = gRamStart;     // Address in glulx memory.
+        MemoryMap map = undo->memoryMap; // Glulx memory map.
+
+        // Restore the size of the memory map
+        heap_clear ();
+        resizeMemory (undo->endMem, 1);
+
+        // Restore the stack.
+        memcpy (base, undo->stack, undo->stackSize);
+        gStackPointer = base + (undo->stackSize / sizeof(git_sint32));
+
+        // Restore the contents of RAM.
+
+        if (protectSize > 0 && protectPos < gEndMem)
+        {
+            for ( ; addr < (protectPos & ~0xff) ; addr += 256, ++map)
+                memcpy (gRam + addr, *map, 256);
+            
+            memcpy (gRam + addr, *map, protectPos & 0xff);
+            protectSize += protectPos & 0xff;
+            
+            while (protectSize > 256)
+                addr += 256, ++map, protectSize -= 256;
+
+            if (addr < gEndMem)
+            {
+                memcpy (gRam + addr + protectSize,
+                        *map + protectSize,
+                        256 - protectSize);
+            }
+            addr += 256, ++map;
+        }
+
+        for ( ; addr < gEndMem ; addr += 256, ++map)
+            memcpy (gRam + addr, *map, 256);
+
+        // Restore the heap.
+        if (heap_apply_summary (undo->heapSize, undo->heap))
+            fatalError ("Couldn't apply heap summary");
+
+        // Delete the undo record.
+
+        gUndo = undo->prev;
+        deleteRecord (undo);
+
+        if (gUndo)
+            gUndo->next = NULL;
+        else
+            assert (gUndoSize == 0);
+
+        // And we're done.
+        return 0;
+    }
+}
+
+void resetUndo ()
+{
+    reserveSpace (gMaxUndoSize);
+    assert (gUndo == NULL);
+    assert (gUndoSize == 0);
+}
+
+void shutdownUndo ()
+{
+    resetUndo();
+}
+
+static void reserveSpace (git_uint32 n)
+{
+    UndoRecord * u = gUndo;
+    if (u == NULL)
+        return;
+
+    // Find the oldest undo record.
+
+    while (u->prev)
+        u = u->prev;
+
+    // Delete records until we've freed up the required amount of space.
+
+    while (gUndoSize + n > gMaxUndoSize)
+    {
+        if (u->next)
+        {
+            assert (u->next->prev == u);
+            u = u->next;
+
+            deleteRecord (u->prev);
+            u->prev = NULL;
+        }
+        else
+        {
+            assert (u == gUndo);
+            gUndo = NULL;
+
+            deleteRecord (u);
+            assert (gUndoSize == 0);
+            break;
+        }
+    }
+}
+
+static void deleteRecord (UndoRecord * u)
+{
+    git_uint32 addr = gRamStart; // Address in glulx memory.
+    git_uint32 slot = 0;         // Slot in our memory map.
+
+    // Zero out all the slots which are duplicates
+    // of pages held in older undo records.
+
+    if (u->prev)
+    {
+        // We're diffing against the previous undo record.
+        while (addr < u->endMem && addr < u->prev->endMem)
+        {
+            if (u->memoryMap [slot] == u->prev->memoryMap [slot])
+                u->memoryMap [slot] = NULL;
+            addr += 256, ++slot;
+        }
+    }
+    else
+    {
+        // We're diffing against the gamefile.
+        while (addr < u->endMem && addr < gExtStart)
+        {
+            if (u->memoryMap [slot] == (gRom + addr))
+                u->memoryMap [slot] = NULL;
+            addr += 256, ++slot;
+        }
+    }
+
+    // Zero out all the slots which are duplicates
+    // of newer undo records.
+
+    if (u->next)
+    {
+        addr = gRamStart;
+        slot = 0;
+
+        while (addr < u->endMem && addr < u->next->endMem)
+        {
+            if (u->memoryMap [slot] == u->next->memoryMap [slot])
+                u->memoryMap [slot] = NULL;
+            addr += 256, ++slot;
+        }
+    }
+
+    // Free all the slots which are owned by this record only.
+
+    addr = gRamStart;
+    slot = 0;
+    while (addr < u->endMem)
+    {
+        if (u->memoryMap [slot])
+        {
+            free ((void*) u->memoryMap [slot]);
+            gUndoSize -= 256;
+        }
+        addr += 256, ++slot;
+    }
+
+    // Free the memory map itself.
+    free ((void*) u->memoryMap);
+    gUndoSize -= sizeof(MemoryPage*) * (u->endMem - gRamStart) / 256;
+
+    // Free the stack.
+    free (u->stack);
+    gUndoSize -= u->stackSize;
+
+    // Free the heap.
+    free (u->heap);
+    gUndoSize -= u->heapSize * 4;
+
+    // Finally, free the record.
+    free (u);
+    gUndoSize -= sizeof(UndoRecord);
+}