/// <summary> /// Reads all the messages from the message byte code. /// </summary> /// <param name="messageData">Message byte code.</param> /// <param name="encodedMessages">True if messages are encoded.</param> /// <returns>Collection of messages.</returns> private static List <string> DecompileMessages(byte[] messageData, bool encodedMessages) { var messages = new List <string>(); int messagesOffset = 0; //////////////// // Messages header // [0] = Number of messages int numMessages = messageData[messagesOffset]; messagesOffset += 1; // [1][2] = Offset of end of messages messagesOffset += 2; // [3+] = Offset of each message (as per number of messages) // [x+] = Message data int messageDataOffset = messagesOffset + (numMessages * 2); if (numMessages > 0) { if (encodedMessages) { // decrypt the data in-place, this modifies the buffer passed as input var xform = new XorTransform(Key); xform.TransformBlock(messageData, messageDataOffset, messageData.Length - messageDataOffset, messageData, messageDataOffset); } // Go back to array of offset of each message for (int i = 0; i < numMessages; i++) { // [0][1] = Offset of message int messageOffset = (messageData[messagesOffset + 1] * 0x100) + messageData[messagesOffset]; messagesOffset += 2; string msg; if ((messageOffset - 2) >= 0) { msg = StringDecoder.GetNullTerminatedString(messageData, messageOffset + 1); } else { msg = string.Empty; } messages.Add(msg); } } return(messages); }
/// <summary> /// Decode the inventory resource from byte array. /// </summary> /// <param name="data">Source array (data should NOT be encrypted).</param> /// <param name="padded">Inventory data is padded.</param> /// <returns>Inventory resource.</returns> private static InventoryResource ReadInventory(byte[] data, bool padded) { int offset = 0; // [0][1] = Offset of the start of inventory item names int namesOffset = (data[offset + 1] * 0x100) + data[offset]; offset += 2; // [2] = Maximum number of animated objects (not sure what this is for) byte maxAnimatedObjects = data[offset]; offset += 1; if (padded) { offset++; } var items = new List <InventoryItem>(); // [3+] = three byte entry for each inventory item all of which conform to the following format while (offset <= namesOffset) { // [0][1] = Offset of inventory item name i int nameOffset = (data[offset + 1] * 0x100) + data[offset]; offset += 2; // [2] = Starting room number for inventory item i or 255 carried byte room = data[offset]; offset += 1; if (padded) { offset++; } // [offset + 3] string name = StringDecoder.GetNullTerminatedString(data, nameOffset + (padded ? 4 : 3)); items.Add(new InventoryItem(name, room)); } return(new InventoryResource(items.ToArray(), maxAnimatedObjects)); }
/// <summary> /// Decode the view resource from byte array. /// </summary> /// <param name="resourceIndex">Resource index.</param> /// <param name="viewData">View data.</param> /// <returns>View resource.</returns> public static ViewResource ReadView(byte resourceIndex, byte[] viewData) { var mapLoopHeaderPosToLoopNum = new Dictionary <int, int>(); // View header int viewOffset = 0; // [0] = unknown byte unknown1 = viewData[viewOffset]; viewOffset += 1; // [1] = unknown byte unknown2 = viewData[viewOffset]; viewOffset += 1; // [2] = number of loops int numLoops = viewData[viewOffset]; viewOffset += 1; // [3][4] = position of description int descPos = (viewData[viewOffset + 1] * 0x100) + viewData[viewOffset]; viewOffset += 2; string description = string.Empty; if (descPos > 0) { description = StringDecoder.GetNullTerminatedString(viewData, descPos); } var loops = new ViewLoop[numLoops]; for (int loop = 0; loop < numLoops; loop++) { var cels = new ViewCel[0]; int mirrorOfIndex = -1; // [0][1] = position of loop header int loopHeaderPos = (viewData[viewOffset + 1] * 0x100) + viewData[viewOffset]; viewOffset += 2; if (mapLoopHeaderPosToLoopNum.ContainsKey(loopHeaderPos)) { // We have already seen this loop, looks like this is a mirror mirrorOfIndex = mapLoopHeaderPosToLoopNum[loopHeaderPos]; } else { mapLoopHeaderPosToLoopNum.Add(loopHeaderPos, loop); // Loop header int loopOffset = loopHeaderPos; // [0] = number of cels in loop int numCels = viewData[loopOffset]; loopOffset += 1; cels = new ViewCel[numCels]; for (int cel = 0; cel < numCels; cel++) { // [0][1] = position of first cel header, relative to start of loop int celHeaderPos = (viewData[loopOffset + 1] * 0x100) + viewData[loopOffset]; loopOffset += 2; // Cel header int celOffset = loopHeaderPos + celHeaderPos; // [0] = width of cel in pixels (1 agi pixels = 2 ega pixels) byte celWidth = viewData[celOffset]; celOffset += 1; // [1] = height of cel byte celHeight = viewData[celOffset]; celOffset += 1; // [2] = transparency and cel mirroring // (high 4 bits are mirror info, low 4 bits are transparent color) byte transparentColor = (byte)(viewData[celOffset] & 0x0f); byte mirrorInfo = (byte)((viewData[celOffset] & 0xf0) >> 4); bool mirror = (mirrorInfo & 0x08) != 0; byte mirrorLoopNumber = (byte)(mirrorInfo & 0x07); celOffset += 1; // [+] = RLE encoding of pixels byte[] pixels = RleCompression.Decompress(viewData, celOffset, celWidth, celHeight, transparentColor); cels[cel] = new ViewCel(celWidth, celHeight, transparentColor, mirror, mirrorLoopNumber, pixels); } } loops[loop] = new ViewLoop(cels, mirrorOfIndex); } // Go through all the loops and process the ones that are marked as mirrors for (int loop = 0; loop < loops.Length; loop++) { if (loops[loop].IsMirror) { // Create the mirrored cels for this loop var originalLoop = loops[loops[loop].MirrorOfIndex]; var cels = new ViewCel[originalLoop.Cels.Length]; for (int cel = 0; cel < cels.Length; cel++) { ViewCel originalCel = originalLoop.Cels[cel]; cels[cel] = new ViewCel(originalCel.Width, originalCel.Height, originalCel.TransparentColor, originalCel.Mirror, originalCel.MirrorLoopNumber, originalCel.MirrorPixels()); } loops[loop] = new ViewLoop(cels, -1); } } return(new ViewResource(resourceIndex, loops, description, unknown1, unknown2)); }