Add Glulxe and Git. They compile but don't work yet.
[rodin/chimara.git] / interpreters / git / savefile.c
diff --git a/interpreters/git/savefile.c b/interpreters/git/savefile.c
new file mode 100644 (file)
index 0000000..88894fe
--- /dev/null
@@ -0,0 +1,318 @@
+// $Id: savefile.c,v 1.6 2003/10/20 16:05:06 iain Exp $
+
+#include "git.h"
+
+static void writeWord (git_sint32 word)
+{
+    char buffer [4];
+    write32 (buffer, word);
+    glk_put_buffer (buffer, 4);
+}
+
+static git_uint32 readWord (strid_t file)
+{
+    char buffer [4];
+    glk_get_buffer_stream (file, buffer, 4);
+    return (git_uint32) read32 (buffer);
+}
+
+static int sort_heap_summary(const void *p1, const void *p2)
+{
+    const glui32 *v1 = (const glui32 *)p1;
+    const glui32 *v2 = (const glui32 *)p2;
+
+    if (v1 < v2)
+        return -1;
+    if (v1 > v2)
+        return 1;
+    return 0;
+}
+
+git_sint32 restoreFromFile (git_sint32 * base, git_sint32 id,
+    git_uint32 protectPos, git_uint32 protectSize)
+{
+    git_uint32 protectEnd = protectPos + protectSize;
+    git_uint32 i;
+    strid_t file;
+    glui32 fileSize, fileStart;
+
+    int gotIdent = 0;
+    int gotMemory = 0;
+    int gotStack = 0;
+    int gotHeap = 0;
+
+    // Find out what stream they want to use, and make sure it's valid.
+    file = git_find_stream_by_id (id);
+    if (file == 0)
+        return 1;
+
+    // Read IFF header.
+    if (readWord (file) != read32("FORM"))
+        return 1; // Not an IFF file.
+    
+    fileSize = readWord (file);
+    fileStart = glk_stream_get_position (file);
+    
+    if (readWord (file) != read32("IFZS"))
+        return 1; // Not a Quetzal file.
+    
+    // Discard the current heap.
+    heap_clear();
+    
+    // Read all the chunks.
+    
+    while (glk_stream_get_position(file) < fileStart + fileSize)
+    {
+        git_uint32 chunkType, chunkSize, chunkStart;
+        chunkType = readWord (file);
+        chunkSize = readWord (file);
+        chunkStart = glk_stream_get_position (file);
+
+        if (chunkType == read32("IFhd"))
+        {
+            if (gotIdent)
+                return 1;
+
+            gotIdent = 1;
+
+            if (chunkSize != 128)
+                return 1;
+
+            for (i = 0 ; i < 128 ; ++i)
+            {
+                glui32 c = glk_get_char_stream (file);
+                if (gRom [i] != c)
+                    return 1;
+            }
+        }
+        else if (chunkType == read32("Stks"))
+        {
+            if (gotStack)
+                return 1;
+
+            gotStack = 1;
+
+            if (chunkSize & 3)
+                return 1;
+
+            gStackPointer = base;
+            for ( ; chunkSize > 0 ; chunkSize -= 4)
+                *gStackPointer++ = readWord(file);
+        }
+        else if (chunkType == read32("CMem"))
+        {
+            git_uint32 bytesRead = 0;
+            if (gotMemory)
+                return 1;
+
+            gotMemory = 1;
+
+            if (resizeMemory (readWord(file), 1))
+                fatalError ("Can't resize memory map");
+
+            bytesRead = 4;
+            i = gRamStart;
+            while (i < gExtStart && bytesRead < chunkSize)
+            {
+                int mult = 0;
+                char c = (char) glk_get_char_stream(file);
+                ++bytesRead;
+                
+                if (c == 0)
+                {
+                    mult = (unsigned char) glk_get_char_stream(file);
+                    ++bytesRead;
+                }
+                
+                for (++mult ; mult > 0 ; --mult, ++i)
+                    if (i >= protectEnd || i < protectPos)
+                        gRam [i] = gRom [i] ^ c;
+            }
+
+            while (i < gEndMem && bytesRead < chunkSize)
+            {
+                int mult = 0;
+                char c = (char) glk_get_char_stream(file);
+                ++bytesRead;
+                
+                if (c == 0)
+                {
+                    mult = (unsigned char) glk_get_char_stream(file);
+                    ++bytesRead;
+                }
+                
+                for (++mult ; mult > 0 ; --mult, ++i)
+                    if (i >= protectEnd || i < protectPos)
+                        gRam [i] = c;
+            }
+
+            while (i < gExtStart)
+                if (i >= protectEnd || i < protectPos)
+                    gRam [i] = gRom [i], ++i;
+
+            while (i < gEndMem)
+                if (i >= protectEnd || i < protectPos)
+                    gRam [i] = 0, ++i;
+
+            if (bytesRead != chunkSize)
+                return 1; // Too much data!
+
+            if (chunkSize & 1)
+                glk_get_char_stream (file);
+        }
+        else if (chunkType == read32("MAll"))
+        {
+            glui32 heapSize = 0;
+            glui32 * heap = 0;
+
+            if (gotHeap)
+                return 1;
+
+            gotHeap = 1;
+
+            if (chunkSize & 3)
+                return 1;
+
+            if (chunkSize > 0)
+            {
+                heap = malloc (chunkSize);
+                heapSize = chunkSize / 4;
+                for (i = 0 ; i < heapSize ; ++i)
+                    heap[i] = readWord(file);
+
+                /* The summary might have come from any interpreter, so it could
+                  be out of order. We'll sort it. */
+                qsort(heap+2, (heapSize-2)/2, 8, &sort_heap_summary);
+
+                if (heap_apply_summary (heapSize, heap))
+                    fatalError ("Couldn't apply heap summary");
+                free (heap);
+            }
+        }
+        else
+        {
+            // Unknown chunk type -- just skip it.
+            glk_stream_set_position (file, (chunkSize + 1) & ~1, seekmode_Current);
+        }
+    }
+
+    // Make sure we have all the chunks we need.
+
+    if (!gotIdent)
+        fatalError ("No ident chunk in save file");
+
+    if (!gotStack)
+        fatalError ("No stack chunk in save file");
+
+    if (!gotMemory)
+        fatalError ("No memory chunk in save file");
+
+    // If we reach this point, we restored successfully.
+
+    return 0;
+}
+
+git_sint32 saveToFile (git_sint32 * base, git_sint32 * sp, git_sint32 id)
+{
+    git_uint32 n, zeroCount;
+    glui32 fileSize, fileSizePos;
+    glui32 memSize, memSizePos;
+    glui32 heapSize;
+    glui32* heap;
+
+    strid_t file, oldFile;
+
+    // Find out what stream they want to use, and make sure it's valid.
+    file = git_find_stream_by_id (id);
+    if (file == 0)
+        return 1;
+
+    // Get the state of the heap.
+    if (heap_get_summary (&heapSize, &heap))
+        fatalError ("Couldn't get heap summary");
+
+    // Make the given stream the default.
+    oldFile = glk_stream_get_current ();
+    glk_stream_set_current (file);
+
+    // Write Quetzal header.
+    glk_put_string ("FORM");
+
+    fileSizePos = glk_stream_get_position (file);
+    writeWord (0);
+
+    glk_put_string ("IFZS");
+
+    // Header chunk.
+    glk_put_string ("IFhd");
+    writeWord (128);
+    glk_put_buffer ((char *) gRom, 128);
+
+    // Stack chunk.
+    glk_put_string ("Stks");
+    writeWord ((sp - base) * 4);
+    for (n = 0 ; n < (git_uint32) (sp - base) ; ++n)
+        writeWord (base [n]);
+
+    // Heap chunk.
+    if (heap != 0)
+    {
+        glk_put_string ("MAll");
+        writeWord (heapSize * 4);
+        for (n = 0 ; n < heapSize ; ++n)
+            writeWord (heap [n]);
+        free(heap);
+    }
+
+    // Memory chunk.
+    glk_put_string ("CMem");
+    memSizePos = glk_stream_get_position (file);
+    writeWord (0);
+
+    writeWord (gEndMem);
+    for (zeroCount = 0, n = gRamStart ; n < gEndMem ; ++n)
+    {
+        unsigned char romC = (n < gExtStart) ? gRom[n] : 0;
+        unsigned char c = ((git_uint32) romC) ^ ((git_uint32) gRam[n]);
+        if (c == 0)
+            ++zeroCount;
+        else
+        {
+            for ( ; zeroCount > 256 ; zeroCount -= 256)
+            {
+                glk_put_char (0);
+                glk_put_char (0xff);
+            }
+
+            if (zeroCount > 0)
+            {
+                glk_put_char (0);
+                glk_put_char ((char) (zeroCount - 1));
+                zeroCount = 0;
+            }
+
+            glk_put_char (c);
+        }
+    }
+    // Note: we don't bother writing out any remaining zeroes,
+    // because the memory is padded out with zeroes on restore.
+
+    memSize = glk_stream_get_position (file) - memSizePos - 4;
+    if (memSize & 1)
+       glk_put_char (0);
+
+    // Back up and fill in the lengths.
+    fileSize = glk_stream_get_position (file) - fileSizePos - 4;
+
+    glk_stream_set_position (file, fileSizePos, seekmode_Start);
+    writeWord (fileSize);
+
+    glk_stream_set_position (file, memSizePos, seekmode_Start);
+    writeWord (memSize);
+
+    // Restore the previous default stream.
+    glk_stream_set_current (oldFile);
+
+    // And we're done.
+    return 0;
+}