Ejemplo n.º 1
0
        /*
         * Restore a saved game using Quetzal format. Return 2 if OK, 0 if an error
         * occurred before any damage was done, -1 on a fatal error.
         */

        internal static zword RestoreQuetzal(FileStream svf, System.IO.Stream stf)
        {
            zlong pc;
            zword i, tmpw;
            zword fatal = 0;    /* Set to -1 when errors must be fatal. */
            zbyte skip, progress = GOT_NONE;
            int   x, y;

            /* Check it's really an `IFZS' file. */


            if (!TryReadLong(svf, out zlong tmpl) ||
                !TryReadLong(svf, out zlong ifzslen) ||
                !TryReadLong(svf, out zlong currlen))
            {
                return(0);
            }

            if (tmpl != ID_FORM || currlen != ID_IFZS)
            {
                Text.PrintString("This is not a saved game file!\n");
                return(0);
            }
            if (((ifzslen & 1) > 0) || ifzslen < 4) /* Sanity checks. */ return {
                (0);
            }
            ifzslen -= 4;

            /* Read each chunk and process it. */
            while (ifzslen > 0)
            {
                /* Read chunk header. */
                if (ifzslen < 8) /* Couldn't contain a chunk. */ return {
                    (0);
                }
                if (!TryReadLong(svf, out tmpl) ||
                    !TryReadLong(svf, out currlen))
                {
                    return(0);
                }

                ifzslen -= 8;   /* Reduce remaining by size of header. */

                /* Handle chunk body. */
                if (ifzslen < currlen) /* Chunk goes past EOF?! */ return {
                    (0);
                }
                skip     = (byte)(currlen & 1);
                ifzslen -= currlen + skip;

                switch (tmpl)
                {
                /* `IFhd' header chunk; must be first in file. */
                case 1229351012:     // IFhd
                    if ((progress & GOT_HEADER) > 0)
                    {
                        Text.PrintString("Save file has two IFZS chunks!\n");
                        return(fatal);
                    }
                    progress |= GOT_HEADER;
                    if (currlen < 13 || !TryReadWord(svf, out tmpw))
                    {
                        return(fatal);
                    }

                    if (tmpw != Main.h_release)
                    {
                        progress = GOT_ERROR;
                    }

                    for (i = ZMachine.H_SERIAL; i < ZMachine.H_SERIAL + 6; ++i)
                    {
                        if ((x = svf.ReadByte()) == -1)
                        {
                            return(fatal);
                        }
                        if (x != FastMem.ZMData[FastMem.Zmp + i])
                        {
                            progress = GOT_ERROR;
                        }
                    }

                    if (!TryReadWord(svf, out tmpw))
                    {
                        return(fatal);
                    }
                    if (tmpw != Main.h_checksum)
                    {
                        progress = GOT_ERROR;
                    }

                    if ((progress & GOT_ERROR) > 0)
                    {
                        Text.PrintString("File was not saved from this story!\n");
                        return(fatal);
                    }
                    if ((x = svf.ReadByte()) == -1)
                    {
                        return(fatal);
                    }
                    pc = (zlong)x << 16;
                    if ((x = svf.ReadByte()) == -1)
                    {
                        return(fatal);
                    }
                    pc |= (zlong)x << 8;
                    if ((x = svf.ReadByte()) == -1)
                    {
                        return(fatal);
                    }
                    pc   |= (zlong)x;
                    fatal = zword.MaxValue; /* Setting PC means errors must be fatal. */     // TODO make sure this works
                    FastMem.SetPc(pc);

                    for (i = 13; i < currlen; ++i)
                    {
                        svf.ReadByte();         /* Skip rest of chunk. */
                    }
                    break;

                /* `Stks' stacks chunk; restoring this is quite complex. ;) */
                case 1400138611:     // ID_Stks:
                    if ((progress & GOT_STACK) > 0)
                    {
                        Text.PrintString("File contains two stack chunks!\n");
                        break;
                    }
                    progress |= GOT_STACK;

                    fatal = zword.MaxValue;;            /* Setting SP means errors must be fatal. */
                    // sp = stack + General.STACK_SIZE;
                    Main.sp = Main.Stack.Length;

                    /*
                     * All versions other than V6 may use evaluation stack outside
                     * any function context. As a result a faked function context
                     * will be present in the file here. We skip this context, but
                     * load the associated stack onto the stack proper...
                     */
                    if (Main.h_version != ZMachine.V6)
                    {
                        if (currlen < 8)
                        {
                            return(fatal);
                        }
                        for (i = 0; i < 6; ++i)
                        {
                            if (svf.ReadByte() != 0)
                            {
                                return(fatal);
                            }
                        }
                        if (!TryReadWord(svf, out tmpw))
                        {
                            return(fatal);
                        }
                        if (tmpw > General.STACK_SIZE)
                        {
                            Text.PrintString("Save-file has too much stack (and I can't cope).\n");
                            return(fatal);
                        }
                        currlen -= 8;
                        if (currlen < tmpw * 2)
                        {
                            return(fatal);
                        }
                        for (i = 0; i < tmpw; ++i)
                        {
                            // if (!read_word(svf, --sp)) return fatal;
                            if (!TryReadWord(svf, out Main.Stack[--Main.sp]))
                            {
                                return(fatal);
                            }
                        }
                        currlen -= (zword)(tmpw * 2);
                    }

                    /* We now proceed to load the main block of stack frames. */
                    for (Main.fp = Main.Stack.Length, Main.frame_count = 0;
                         currlen > 0;
                         currlen -= 8, ++Main.frame_count)
                    {
                        if (currlen < 8)
                        {
                            return(fatal);
                        }
                        if (Main.sp < 4)        /* No space for frame. */
                        {
                            Text.PrintString("Save-file has too much stack (and I can't cope).\n");
                            return(fatal);
                        }

                        /* Read PC, procedure flag and formal param count. */
                        if (!TryReadLong(svf, out tmpl))
                        {
                            return(fatal);
                        }
                        y    = (int)(tmpl & 0x0F);      /* Number of formals. */
                        tmpw = (zword)(y << 8);

                        /* Read result variable. */
                        if ((x = svf.ReadByte()) == -1)
                        {
                            return(fatal);
                        }

                        /* Check the procedure flag... */
                        if ((tmpl & 0x10) > 0)
                        {
                            tmpw  |= 0x1000;    /* It's a procedure. */
                            tmpl >>= 8;         /* Shift to get PC value. */
                        }
                        else
                        {
                            /* Functions have type 0, so no need to or anything. */
                            tmpl >>= 8;         /* Shift to get PC value. */
                            --tmpl;             /* Point at result byte. */
                            /* Sanity check on result variable... */
                            if (FastMem.ZMData[FastMem.Zmp + tmpl] != (zbyte)x)
                            {
                                Text.PrintString("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
                                return(fatal);
                            }
                        }

                        Main.Stack[--Main.sp] = (zword)(tmpl >> 9);     /* High part of PC */
                        Main.Stack[--Main.sp] = (zword)(tmpl & 0x1FF);  /* Low part of PC */
                        Main.Stack[--Main.sp] = (zword)(Main.fp - 1);   /* FP */

                        /* Read and process argument mask. */
                        if ((x = svf.ReadByte()) == -1)
                        {
                            return(fatal);
                        }
                        ++x;            /* Should now be a power of 2 */
                        for (i = 0; i < 8; ++i)
                        {
                            if ((x & (1 << i)) > 0)
                            {
                                break;
                            }
                        }

                        if ((x ^ (1 << i)) > 0)     /* Not a power of 2 */
                        {
                            Text.PrintString("Save-file uses incomplete argument lists (which I can't handle)\n");
                            return(fatal);
                        }
                        // *--sp = tmpw | i;
                        Main.Stack[--Main.sp] = (zword)(tmpw | i);
                        Main.fp = Main.sp;      /* FP for next frame. */

                        /* Read amount of eval stack used. */
                        if (!TryReadWord(svf, out tmpw))
                        {
                            return(fatal);
                        }

                        tmpw += (zword)y;       /* Amount of stack + number of locals. */
                        // if (sp - stack <= tmpw) {
                        if (Main.sp <= tmpw)
                        {
                            Text.PrintString("Save-file has too much stack (and I can't cope).\n");
                            return(fatal);
                        }
                        if (currlen < tmpw * 2)
                        {
                            return(fatal);
                        }
                        for (i = 0; i < tmpw; ++i)
                        {
                            if (!TryReadWord(svf, out Main.Stack[--Main.sp]))
                            {
                                return(fatal);
                            }
                        }
                        currlen -= (zword)(tmpw * 2);
                    }
                    /* End of `Stks' processing... */
                    break;

                /* Any more special chunk types must go in HERE or ABOVE. */
                /* `CMem' compressed memory chunk; uncompress it. */
                case 1129145709:                      // CMem
                    if ((progress & GOT_MEMORY) == 0) /* Don't complain if two. */
                    {
                        stf.Position = 0;             // (void) fseek (stf, 0, SEEK_SET);
                        i            = 0;             /* Bytes written to data area. */
                        for (; currlen > 0; --currlen)
                        {
                            if ((x = svf.ReadByte()) == -1)
                            {
                                return(fatal);
                            }
                            if (x == 0)     /* Start run. */
                            {
                                /* Check for bogus run. */
                                if (currlen < 2)
                                {
                                    Text.PrintString("File contains bogus `CMem' chunk.\n");
                                    for (; currlen > 0; --currlen)
                                    {
                                        svf.ReadByte();         /* Skip rest. */
                                    }
                                    currlen = 1;
                                    i       = 0xFFFF;
                                    break;     /* Keep going; may be a `UMem' too. */
                                }
                                /* Copy story file to memory during the run. */
                                --currlen;
                                if ((x = svf.ReadByte()) == -1)
                                {
                                    return(fatal);
                                }
                                for (; x >= 0 && i < Main.h_dynamic_size; --x, ++i)
                                {
                                    if ((y = stf.ReadByte()) == -1)
                                    {
                                        return(fatal);
                                    }
                                    else
                                    {
                                        FastMem.ZMData[FastMem.Zmp + i] = (zbyte)y;
                                    }
                                }
                            }
                            else        /* Not a run. */
                            {
                                if ((y = stf.ReadByte()) == -1)
                                {
                                    return(fatal);
                                }
                                FastMem.ZMData[FastMem.Zmp + i] = (zbyte)(x ^ y);
                                ++i;
                            }
                            /* Make sure we don't load too much. */
                            if (i > Main.h_dynamic_size)
                            {
                                Text.PrintString("warning: `CMem' chunk too long!\n");
                                for (; currlen > 1; --currlen)
                                {
                                    svf.ReadByte(); /* Skip rest. */
                                }
                                break;              /* Keep going; there may be a `UMem' too. */
                            }
                        }
                        /* If chunk is short, assume a run. */
                        for (; i < Main.h_dynamic_size; ++i)
                        {
                            if ((y = stf.ReadByte()) == -1)
                            {
                                return(fatal);
                            }
                            else
                            {
                                FastMem.ZMData[FastMem.Zmp + i] = (zbyte)y;
                            }
                        }

                        if (currlen == 0)
                        {
                            progress |= GOT_MEMORY;     /* Only if succeeded. */
                        }
                        break;
                    }
                    goto default;

                /* Fall right thru (to default) if already GOT_MEMORY */
                /* `UMem' uncompressed memory chunk; load it. */
                case 1431135597:                      // ID_UMem:
                    if ((progress & GOT_MEMORY) == 0) /* Don't complain if two. */
                    {
                        /* Must be exactly the right size. */
                        if (currlen == Main.h_dynamic_size)
                        {
                            int read = svf.Read(FastMem.ZMData, (int)FastMem.Zmp, (int)currlen);
                            if (read == currlen)
                            {
                                progress |= GOT_MEMORY;         /* Only on success. */
                                break;
                            }
                            //if (fread(zmp, currlen, 1, svf) == 1) {
                            //}
                        }
                        else
                        {
                            Text.PrintString("`UMem' chunk wrong size!\n");
                        }
                        /* Fall into default action (skip chunk) on errors. */
                    }
                    goto default;

                /* Fall thru (to default) if already GOT_MEMORY */
                /* Unrecognised chunk type; skip it. */
                default:
                    // (void) fseek (svf, currlen, SEEK_CUR);	/* Skip chunk. */
                    svf.Position += currlen;
                    break;
                }
                if (skip > 0)
                {
                    svf.ReadByte();     /* Skip pad byte. */
                }
            }

            /*
             * We've reached the end of the file. For the restoration to have been a
             * success, we must have had one of each of the required chunks.
             */
            if ((progress & GOT_HEADER) == 0)
            {
                Text.PrintString("error: no valid header (`IFhd') chunk in file.\n");
            }
            if ((progress & GOT_STACK) == 0)
            {
                Text.PrintString("error: no valid stack (`Stks') chunk in file.\n");
            }
            if ((progress & GOT_MEMORY) == 0)
            {
                Text.PrintString("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
            }

            return((ushort)(progress == GOT_ALL ? 2 : fatal));
        }