/// <summary> /// Loads a collection of chunks from a Quetzal file. /// </summary> /// <param name="stream">The stream to read from.</param> /// <returns>A new <see cref="Quetzal"/> instance initialized /// from the stream.</returns> /// <remarks> /// Duplicate chunks are not supported by this class. Only the last /// chunk of a given type will be available. /// </remarks> public static Quetzal FromStream(Stream stream) { Quetzal result = new Quetzal(); uint type = BigEndian.ReadInt32(stream); if (type != FORM && type != LIST && type != CAT_) { throw new ArgumentException("Invalid IFF type"); } int length = (int)BigEndian.ReadInt32(stream); byte[] buffer = new byte[length]; int amountRead = stream.Read(buffer, 0, (int)length); if (amountRead < length) { throw new ArgumentException("Quetzal file is too short"); } stream = new MemoryStream(buffer); type = BigEndian.ReadInt32(stream); if (type != IFZS) { throw new ArgumentException("Wrong IFF sub-type: not a Quetzal file"); } while (stream.Position < stream.Length) { type = BigEndian.ReadInt32(stream); length = (int)BigEndian.ReadInt32(stream); byte[] chunk = new byte[length]; amountRead = stream.Read(chunk, 0, length); if (amountRead < length) { throw new ArgumentException("Chunk extends past end of file"); } result.chunks[type] = chunk; } return(result); }
/// <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> /// 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)); }