Example #1
0
        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;
        }
Example #2
0
        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;
        }
Example #3
0
        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;
        }
Example #4
0
        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;
        }
Example #5
0
 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;
 }
Example #6
0
        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;
        }
Example #7
0
 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;
 }
Example #8
0
 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);
 }
Example #9
0
 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;
 }
Example #10
0
 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;
 }
Example #11
0
 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);
 }
Example #12
0
        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);
        }
Example #13
0
        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},
            };
        }