/// <summary> /// Reconstitutes a changed block of memory by applying a compressed /// set of differences to the original block from the game file. /// </summary> /// <param name="original">The original block of memory.</param> /// <param name="delta">The RLE-compressed set of differences, /// prefixed with a 4-byte length. This length may be larger than /// the original block, but not smaller.</param> /// <returns>The changed block of memory. The length of this array is /// specified at the beginning of <paramref name="delta"/>.</returns> public static byte[] DecompressMemory(byte[] original, byte[] delta) { MemoryStream mstr = new MemoryStream(delta); uint length = BigEndian.ReadInt32(mstr); if (length < original.Length) { throw new ArgumentException("Compressed block's length tag must be no less than original block's size"); } byte[] result = new byte[length]; int rp = 0; for (int i = 4; i < delta.Length; i++) { byte b = delta[i]; if (b == 0) { int repeats = delta[++i] + 1; Array.Copy(original, rp, result, rp, repeats); rp += repeats; } else { result[rp] = (byte)(original[rp] ^ b); rp++; } } while (rp < original.Length) { result[rp] = original[rp]; rp++; } return(result); }
/// <summary> /// Initializes a new allocator from a previous saved heap state. /// </summary> /// <param name="savedHeap">A byte array describing the heap state, /// as returned by the <see cref="Save"/> method.</param> /// <param name="requester">A delegate to request more memory.</param> public HeapAllocator(byte[] savedHeap, MemoryRequester requester) { this.heapAddress = BigEndian.ReadInt32(savedHeap, 0); this.setEndMem = requester; this.blocks = new List <HeapEntry>(); this.freeList = new List <HeapEntry>(); uint numBlocks = BigEndian.ReadInt32(savedHeap, 4); blocks.Capacity = (int)numBlocks; uint nextAddress = heapAddress; for (uint i = 0; i < numBlocks; i++) { uint start = BigEndian.ReadInt32(savedHeap, 8 * i + 8); uint length = BigEndian.ReadInt32(savedHeap, 8 * i + 12); blocks.Add(new HeapEntry(start, length)); if (nextAddress < start) { freeList.Add(new HeapEntry(nextAddress, start - nextAddress)); } nextAddress = start + length; } endMem = nextAddress; heapExtent = nextAddress - heapAddress; if (setEndMem(endMem) == false) { throw new ArgumentException("Can't allocate VM memory to fit saved heap"); } blocks.Sort(entryComparer); freeList.Sort(entryComparer); }
/// <summary> /// Writes the chunks to a Quetzal file. /// </summary> /// <param name="stream">The stream to write to.</param> public void WriteToStream(Stream stream) { BigEndian.WriteInt32(stream, FORM); // IFF tag BigEndian.WriteInt32(stream, 0); // file length (filled in later) BigEndian.WriteInt32(stream, IFZS); // FORM sub-ID for Quetzal uint totalSize = 4; // includes sub-ID foreach (KeyValuePair <uint, byte[]> pair in chunks) { BigEndian.WriteInt32(stream, pair.Key); // chunk type BigEndian.WriteInt32(stream, (uint)pair.Value.Length); // chunk length stream.Write(pair.Value, 0, pair.Value.Length); // chunk data totalSize += 8 + (uint)(pair.Value.Length); } if (totalSize % 2 == 1) { stream.WriteByte(0); // padding (not counted in file length) } stream.Seek(4, SeekOrigin.Begin); BigEndian.WriteInt32(stream, totalSize); //stream.SetLength(totalSize); }
/// <summary> /// Reads a big-endian double word from memory. /// </summary> /// <param name="offset">The address to read from.</param> /// <returns>The 32-bit value at the specified address.</returns> public uint ReadInt32(uint offset) { return(BigEndian.ReadInt32(memory, offset)); }
/// <summary> /// Reads a big-endian word from memory. /// </summary> /// <param name="offset">The address to read from</param> /// <returns>The word at the specified address.</returns> public ushort ReadInt16(uint offset) { return(BigEndian.ReadInt16(memory, offset)); }