public static Level LoadLevel(byte[] data, bool loadAsDiff) { var level = new Level(); var stream = new BitStream(data); var buffer = new byte[6]; stream.Read(buffer, 48); if (loadAsDiff) throw new NotImplementedException(); if (buffer[0] != 'D' || buffer[1] != 'F' || buffer[2] != '_' || buffer[3] != 'L' || buffer[4] != 'V' || buffer[5] != 'L') throw new InvalidDataException(); stream.Read(buffer, 16); ushort levelFormatVersion = Util.MakeU16(buffer); int thingCount = 0; if (levelFormatVersion >= 0x2b) { stream.Read(buffer, 32); // ignored stream.Read(buffer, 32); thingCount = Util.MakeI32(buffer); LoadLevelMetadata(stream); } if (levelFormatVersion >= 0x2c) { stream.Read(buffer, 32); int thumbnailSize = Util.MakeI32(buffer); if (!loadAsDiff) { var thumbnailBuffer = new byte[thumbnailSize]; stream.Read(thumbnailBuffer, thumbnailSize * 8); } } level.Tags = Util.ReadKeyValueList(stream); var itemsOffset = ((stream.BitPosition - 1) / 8 + 1) + (4 * thingCount); for (var t = 0; t < thingCount; ++t) { stream.Read(buffer, 32); var ptr = itemsOffset + Util.MakeI32(buffer); var itemBytes = Util.MakeI32(data, ptr); var item = new byte[itemBytes]; Array.Copy(data, ptr, item, 0, itemBytes); level.Blocks.Add(LoadBlock(item)); } return level; }
private static Block LoadBlock(byte[] data) { var block = new Block(); var outerStream = new BitStream(data); var buf = new byte[4]; outerStream.Read(buf, 32); // total wrapper data size outerStream.Read(buf, 32); var inflatedSize = Util.MakeI32(buf); outerStream.Read(buf, 16); block.X = Util.MakeI16(buf); outerStream.Read(buf, 16); block.Y = Util.MakeI16(buf); outerStream.Read(buf, 16); // ignored outerStream.Read(buf, 16); var sliceCount = Util.MakeU16(buf); outerStream.Read(buf, 8); var extraEagerSlice = buf[0] != 0; Trace("block {0:X4} {1:X4} {2}", block.X, block.Y, extraEagerSlice); // in the original source it's 17, which is the total of the sizes of the fields read above. // however, here we pass 19 instead of 17 so as to skip the 2-byte zlib header of 78 9C if (data[17] != 0x78 || data[18] != 0x9c) throw new InvalidDataException(); var deflate = new DeflateStream(new MemoryStream(data, 19, data.Length - 19), CompressionMode.Decompress); var innerData = new byte[inflatedSize]; deflate.Read(innerData, 0, inflatedSize); // var innerStream = new BitStream(innerData); // innerStream.Read(buf, 32); // var innerDataSize = Util.MakeI32(buf); // innerStream.Read(buf, 16); // var more = Util.MakeU16(buf) > 4; // innerStream.Read(buf, 8); // var something3 = buf[0]; // innerStream.Read(buf, 8); // var something4 = buf[0]; // innerStream.Read(buf, 8); // ignored // if (more) { // innerStream.Read(buf, 32); // innerStream.Read(buf, 16); // innerStream.Read(buf, 16); // } // Trace("{0:X4} {1:X4} {2:X4} | {3}", something1, something2, somethingCount, Hexify(innerData)); var innerStream = new BitStream(innerData); var innerDataPos = 0; for (var i = 0; i <= sliceCount; ++i) { if (i == sliceCount && !extraEagerSlice) break; var innerDataSize = Util.MakeI32(innerData, innerDataPos); innerStream.BitPosition = innerDataPos * 8; // the game streams in all but the optional last slice. uncomment // this conditional to match the actual game's loading order //if (i >= sliceCount) block.Slices.Add(LoadSlice(innerStream)); innerDataPos += innerDataSize; } return block; }
private static SliceHeader ReadSliceHeader(BitStream stream) { var header = new SliceHeader(); var buf = new byte[4]; header.HeaderSize = 25; stream.Read(buf, 32); header.TotalSize = Util.MakeI32(buf); stream.Read(buf, 16); header.Field8 = Util.MakeU16(buf); stream.Read(buf, 8); header.X = buf[0]; stream.Read(buf, 8); header.Y = buf[0]; stream.Read(buf, 8); header.FieldC = buf[0]; if (header.Field8 > 4) { stream.Read(buf, 32); ; header.Field14 = Util.MakeI32(buf); stream.Read(buf, 16); header.FilthCount = Util.MakeU16(buf); stream.Read(buf, 16); header.EnemyCount = Util.MakeU16(buf); } if (header.Field8 > 5) { stream.Read(buf, 16); header.TileEdgeCount = Util.MakeU16(buf); stream.Read(buf, 16); header.FilthBlocks = Util.MakeU16(buf); } if (header.Field8 < 6) header.HeaderSize -= 4; if (header.Field8 < 5) header.HeaderSize -= 8; stream.Read(buf, 32); header.Kinds = Util.MakeI32(buf); return header; }
private static Entity ReadSingleEntity(BitStream stream) { var buf = new byte[4]; stream.Read(buf, 32); var uid = Util.MakeI32(buf); Debug.Assert((uid & 0x80000000) == 0); //if ((something & 0x80000000) == 0) { stream.Read(buf, 6); var count = buf[0]; var chars = new char[64]; stream.ReadPackedText(chars, count); var name = new string(chars, 0, count); //} var entity = new Entity { Uid = uid, Kind = name }; entity.X = stream.ReadFloat(32, 8); entity.Y = stream.ReadFloat(32, 8); stream.Read(buf, 16); entity.Rotation = (float)(Util.MakeU16(buf) / 65536.0 * 2 * Math.PI); stream.Read(buf, 8); entity.Field28 = buf[0]; stream.Read(buf, 1); entity.FlipHorz = buf[0] != 0; // buf[0] != 0 ? 1 : -1 stream.Read(buf, 1); entity.FlipVert = buf[0] != 0; // buf[0] != 0 ? 1 : -1; stream.Read(buf, 1); entity.Field34 = buf[2] != 0; entity.Tags = Util.ReadKeyValueList(stream); Trace("entity {0} {1:N} {2:N} {3:X4} {4:X2} {5} {6} {7} {8}", name, entity.X, entity.Y, entity.Rotation, entity.Field28, entity.FlipHorz, entity.FlipVert, entity.Field34, Util.DumpKeyValueList(entity.Tags)); return entity; }
private static List<Tile> LoadTiles(BitStream stream) { var ret = new List<Tile>(); var buf = new byte[14]; stream.Read(buf, 8); var count = buf[0]; for (var i = 0; i < count; ++i) { stream.Read(buf, 8); var layer = buf[0]; stream.Read(buf, 10); var c2 = Util.MakeU16(buf); var count2 = c2 != 0 ? c2 : 1024; Debug.Assert(count2 != 1024); for (var j = 0; j < count2; ++j) { stream.Read(buf, 5); var somethingP = buf[0]; stream.Read(buf, 5); var somethingQ = buf[0]; stream.Read(buf, 104); ret.Add(LoadSingleTile(somethingP, somethingQ, layer, buf)); } } return ret; }
private static Slice LoadSlice(BitStream stream) { var slice = new Slice(); slice.Header = ReadSliceHeader(stream); Trace("slice {0:X4} {1:X2} {2:X2} {3:X2} {4:X8} {5} {6} {7} {8}", slice.Header.Field8, slice.Header.X, slice.Header.Y, slice.Header.FieldC, slice.Header.Field14, slice.Header.FilthCount, slice.Header.EnemyCount, slice.Header.TileEdgeCount, slice.Header.FilthBlocks); // Trace(header.Kinds); if ((slice.Header.Kinds & 1) != 0) slice.Tiles = LoadTiles(stream); if ((slice.Header.Kinds & 2) != 0) slice.Filth = LoadFilth(stream); if ((slice.Header.Kinds & 8) != 0) slice.Props = LoadProps(stream); if ((slice.Header.Kinds & 4) != 0) slice.Entities = LoadEntities(stream); return slice; }
private static List<Prop> LoadProps(BitStream stream) { var ret = new List<Prop>(); var buf = new byte[4]; stream.Read(buf, 16); var count = Util.MakeU16(buf); for (var i = 0; i < count; ++i) { stream.Read(buf, 32); var something = Util.MakeI32(buf); if (something >= 0) { var prop = new Prop(); prop.Field4 = something; stream.Read(buf, 8); prop.LayerGroup = buf[0]; stream.Read(buf, 8); prop.LayerSub = buf[0]; prop.X = stream.ReadFloat(28, 4); prop.Y = stream.ReadFloat(28, 4); stream.Read(buf, 16); prop.Rotation = (float) (Util.MakeU16(buf) / 65536.0 * 2 * Math.PI); stream.Read(buf, 1); prop.FlipHorz = buf[0] == 0; // buf[0] != 0 ? 1 : -1; stream.Read(buf, 1); prop.FlipVert = buf[0] == 0; // buf[0] != 0 ? 1 : -1; stream.Read(buf, 8); prop.PropSet = buf[0]; stream.Read(buf, 12); prop.PropGroup = Util.MakeU16(buf); stream.Read(buf, 12); prop.PropIndex = Util.MakeU16(buf); stream.Read(buf, 8); prop.Palette = buf[0]; Trace("prop {0:N} {1:N} {2:N} {3:X8} {4:X8} {5:X2} {6:X4} {7:X4} {8:X2} {9:X2} {10:X2}", prop.X, prop.Y, prop.Rotation, prop.FlipHorz, prop.FlipVert, prop.PropSet, prop.PropGroup, prop.PropIndex, prop.Palette, prop.LayerGroup, prop.LayerSub); ret.Add(prop); } else throw new NotImplementedException(); } return ret; }
private static void LoadLevelMetadata(BitStream stream) { var buffer = new byte[6]; stream.Read(buffer, 48); if (buffer[0] != 'D' || buffer[1] != 'F' || buffer[2] != '_' || buffer[3] != 'M' || buffer[4] != 'T' || buffer[5] != 'D') throw new InvalidDataException("Level metadata invalid!"); stream.Read(buffer, 16); ushort formatVersion = Util.MakeU16(buffer); if (formatVersion < 2) throw new InvalidDataException("Level metadata out of date!."); stream.Read(buffer, 32); // byte count; not important stream.Read(buffer, 32); int field_24 = Util.MakeI32(buffer); stream.Read(buffer, 32); int field_28 = Util.MakeI32(buffer); stream.Read(buffer, 32); // ignored stream.Read(buffer, 32); int nested_field_3c = Util.MakeI32(buffer); }
private static List<Filth> LoadFilth(BitStream stream) { var ret = new List<Filth>(); var buf = new byte[12]; stream.Read(buf, 10); var c = Util.MakeU16(buf); var count = c != 0 ? c : 1024; for (var i = 0; i < count; ++i) { stream.Read(buf, 5); var x = buf[0]; stream.Read(buf, 5); var y = buf[0]; stream.Read(buf, 96); ret.Add(LoadSingleFilth(x, y, buf.ToArray())); } return ret; }
private static List<Entity> LoadEntities(BitStream stream) { var ret = new List<Entity>(); var buf = new byte[4]; stream.Read(buf, 16); var count = Util.MakeU16(buf); for (var i = 0; i < count; ++i) { var oldBitPos = stream.BitPosition; stream.Read(buf, 32); var x = Util.MakeI32(buf); if ((x & 0x80000000) == 0) { stream.BitPosition = oldBitPos; ret.Add(ReadSingleEntity(stream)); } } return ret; }
private static void ReadStats(string filename) { var data = ReadFile(filename); var stream = new BitStream(data.Skip(12).ToArray()); var pairs = Util.ReadKeyValueList(stream); // HAAAACK var jobj = JsonConvert.SerializeObject(pairs.Select(p => { if (p.Item2 is List<object> && ((List<object>) p.Item2)[0] is List<Tuple<string, object>>) return Tuple.Create(p.Item1, (object)((List<object>) p.Item2).Select(q => (List<Tuple<string, object>>) q).Select(q => q.ToDictionary(r => r.Item1, r => r.Item2)).ToList()); return p; }).ToDictionary(p => p.Item1, p => p.Item2)); Console.WriteLine(jobj); }
private static Tuple<List<SpriteGroup>, List<SpriteTexture>> ExtractSpriteInfo(string name, byte[] data) { var groups = new List<SpriteGroup>(); var textures = new List<SpriteTexture>(); var stream = new BitStream(data); var buffer = new byte[32]; stream.Read(buffer, 48); if (buffer[0] != 'D' || buffer[1] != 'F' || buffer[2] != '_' || buffer[3] != 'S' || buffer[4] != 'P' || buffer[5] != 'R') { Console.WriteLine("ignoring malformed sprite file: " + name); return null; } stream.Read(buffer, 16); var version = Util.MakeU16(buffer); if (version != 0x2c) throw new InvalidDataException(); stream.Read(buffer, 16); var groupCount = Util.MakeU16(buffer); stream.Read(buffer, 16); var textureCount = Util.MakeU16(buffer); stream.Read(buffer, 16); var unk3 = Util.MakeU16(buffer); stream.Read(buffer, 16); var unk4 = Util.MakeU16(buffer); stream.Read(buffer, 16); var unk5 = Util.MakeU16(buffer); stream.Read(buffer, 32); var pixelDataOffset = Util.MakeI32(buffer); stream.Read(buffer, 32); //var pixelDataLength = Util.MakeI32(buffer); for (var i = 0; i < groupCount; ++i) { var group = new SpriteGroup(); stream.Read(buffer, 8); var count = buffer[0]; stream.Read(buffer, count * 8); group.Name = Encoding.ASCII.GetString(buffer, 0, count); stream.Read(buffer, 8); count = buffer[0]; stream.Read(buffer, count * 8); group.Prefix = Encoding.ASCII.GetString(buffer, 0, count); stream.Read(buffer, 32); var extraLen = Util.MakeI32(buffer); if (extraLen != 0) { var buf = new byte[extraLen]; stream.Read(buf, extraLen * 8); Console.WriteLine("(ignoring {0} bytes of extra data in '{1}')", extraLen, name); } stream.Read(buffer, 16); var count1 = Util.MakeI16(buffer); stream.Read(buffer, 16); var count2 = Util.MakeI16(buffer); stream.Read(buffer, 16); var unk11 = Util.MakeI16(buffer); stream.Read(buffer, 16); var unk12 = Util.MakeI16(buffer); stream.Read(buffer, 16); var unk13 = Util.MakeI16(buffer); stream.Read(buffer, 8); var unk14 = buffer[0]; Trace("group {0}{1} {2} {3} {4} {5}", group.Prefix, group.Name, unk11, unk12, unk13, unk14); for (var j = 0; j < count1; ++j) { var frame = new SpriteFrame(); stream.Read(buffer, 16); frame.Field4 = Util.MakeI16(buffer); stream.Read(buffer, 16); frame.Field0 = Util.MakeI16(buffer); stream.Read(buffer, 64); frame.Rect1 = Rectangle.FromLTRB(Util.MakeI16(buffer, 2), Util.MakeI16(buffer, 0), Util.MakeI16(buffer, 6), Util.MakeI16(buffer, 4)); stream.Read(buffer, 64); frame.Rect2 = Rectangle.FromLTRB(Util.MakeI16(buffer, 2), Util.MakeI16(buffer, 0), Util.MakeI16(buffer, 6), Util.MakeI16(buffer, 4)); stream.Read(buffer, 32); frame.ChunksOffset = Util.MakeI32(buffer); stream.Read(buffer, 16); frame.ChunkCount = Util.MakeI16(buffer); stream.Read(buffer, 8); frame.Field28 = buffer[0]; group.Frames.Add(frame); Trace(" frame {0:X4} {1:X4} {2} {3} {4:X8} {5:X4} {6:X2}", frame.Field4, frame.Field0, frame.Rect1, frame.Rect2, frame.ChunksOffset, frame.ChunkCount, frame.Field28); } for (var j = 0; j < count2; ++j) { var chunk = new SpriteChunk(); stream.Read(buffer, 16); chunk.TextureIndex = Util.MakeI16(buffer); stream.Read(buffer, 32); chunk.SourceRect = Rectangle.FromLTRB(buffer[1], buffer[0], buffer[3], buffer[2]); stream.Read(buffer, 16); chunk.X = Util.MakeI16(buffer); stream.Read(buffer, 16); chunk.Y = Util.MakeI16(buffer); group.Chunks.Add(chunk); Trace(" chunk {0:X4} {1} {2:X4} {3:X4}", chunk.TextureIndex, chunk.SourceRect, chunk.X, chunk.Y); } groups.Add(group); } stream.BitPosition = (pixelDataOffset + 0x1a) * 8; for (var i = 0; i < textureCount; ++i) { var texture = new SpriteTexture(); stream.Read(buffer, 8); texture.Unk1 = buffer[0]; stream.Read(buffer, 8); texture.Unk2 = buffer[0]; stream.Read(buffer, 32); var zlen = Util.MakeI32(buffer); var zbuf = new byte[zlen]; stream.Read(zbuf, zlen * 8); // skip the 2-byte zlib header of 78 9C if (zbuf[0] != 0x78 || zbuf[1] != 0x9c) throw new InvalidDataException(); var deflate = new DeflateStream(new MemoryStream(zbuf, 2, zbuf.Length - 2), CompressionMode.Decompress); texture.Path = Path.Combine(App.IntermediatePath, name, string.Format("{0:X2}_{1:X2}.png", texture.Unk1, texture.Unk2)); using (var image = GetImageFromPixelData(deflate, 102, 102)) image.Save(texture.Path); Trace("texture {0:X2} {1:X2} byte[{2}]", texture.Unk1, texture.Unk2, "..."); textures.Add(texture); } return Tuple.Create(groups, textures); }
public static JObject Read(byte[] data) { var s1 = new BinaryReader(new MemoryStream(data)); if (!s1.ReadBytes(6).SequenceEqual(new[]{'D', 'F', '_', 'R', 'P', 'L'}.Select(c => (byte) c))) throw new InvalidDataException(); var unk1 = s1.ReadByte(); var user = Encoding.ASCII.GetString(s1.ReadBytes(s1.ReadInt16())); if (!s1.ReadBytes(6).SequenceEqual(new[]{'D', 'F', '_', 'R', 'P', 'L'}.Select(c => (byte) c))) throw new InvalidDataException(); var unk2 = s1.ReadByte(); var unk3 = s1.ReadInt16(); var uncompressedDataLen = s1.ReadInt32(); var frames = s1.ReadInt32(); var character = s1.ReadByte(); var level = Encoding.ASCII.GetString(s1.ReadBytes(s1.ReadByte())); s1.ReadInt16(); // skip two-byte zlib header var deflate = new DeflateStream(s1.BaseStream, CompressionMode.Decompress); var innerData = new byte[uncompressedDataLen]; deflate.Read(innerData, 0, uncompressedDataLen); var s2 = new BinaryReader(new MemoryStream(innerData)); var inputsTotalLen = s2.ReadInt32(); var inputs = JArray.FromObject(Enumerable.Range(0, 7).Select(i => { var raw = new BitStream(s2.ReadBytes(s2.ReadInt32())); var ret = new StringBuilder(); var buf = new byte[1]; var oldVal = 0; while (true) { raw.Read(buf, 8); var fs = buf[0]; Console.WriteLine(fs); if (fs == 0xff) break; raw.Read(buf, i >= 5 ? 4 : 2); var val = buf[0]; Console.WriteLine(val); for (var z = 0; z <= fs; ++z) ret.Append((char) (oldVal + (oldVal < 10 ? '0' : 'a' - 10))); oldVal = val; } ret.Remove(0, 1); return ret.ToString(); })); Debug.Assert(s2.BaseStream.Position == inputsTotalLen + 4); var sync = JArray.FromObject(Enumerable.Range(0, s2.ReadInt32()).Select(i => { var entityUid = s2.ReadInt32(); var unkA = s2.ReadInt32(); // this looks like a 1-based index of the entity sync info in a scrambled order? var corrections = JArray.FromObject(Enumerable.Range(0, s2.ReadInt32()).Select(j => { return Enumerable.Range(0, 5).Select(k => s2.ReadInt32()); })); return new JObject { {"entity_uid", entityUid}, // {"unkA", unkA}, {"corrections", corrections}, }; })); return new JObject { {"user", user}, {"level", level}, // {"unk1", unk1}, // {"unk2", unk2}, // {"unk3", unk3}, {"frames", frames}, {"character", character}, {"inputs", inputs}, {"sync", sync}, }; }