public Mobd(SegmentStream stream) { // This is damn ugly, but it seems MOBD uses offsets from lvl start. var fileOffset = (uint)stream.BaseOffset; var firstFrameStart = stream.Length; var animationOffsets = new List <uint>(); var rotationalAnimations = new List <MobdAnimation>(); var simpleAnimations = new List <MobdAnimation>(); while (stream.Position < firstFrameStart) { var value = stream.ReadInt32(); if (value == 0 || (value - fileOffset < stream.Position && value >= fileOffset)) { stream.Position -= 4; break; } animationOffsets.Add((uint)(stream.Position - 4)); while (true) { value = stream.ReadInt32(); if (value == -1 || value == 0) { break; } firstFrameStart = Math.Min(firstFrameStart, value - fileOffset); } } while (stream.Position < firstFrameStart) { var value = stream.ReadUInt32(); if (value == 0) { continue; } animationOffsets.Remove(value - fileOffset); var returnPosition = stream.Position; stream.Position = value - fileOffset; rotationalAnimations.Add(new MobdAnimation(stream)); stream.Position = returnPosition; } foreach (var animationOffset in animationOffsets) { stream.Position = animationOffset; simpleAnimations.Add(new MobdAnimation(stream)); } RotationalAnimations = rotationalAnimations.ToArray(); SimpleAnimations = simpleAnimations.ToArray(); }
public override bool TryParseSprite(Stream s, string filename, out ISpriteFrame[] frames, out TypeDictionary metadata) { metadata = null; if (!IsDdf(s)) { frames = null; return(false); } var aniStream = s as DdfPackageLoader.AniSegmentStream; aniStream.Position = aniStream.AniPosition; frames = new ISpriteFrame[aniStream.ReadUInt32()]; for (var i = 0; i < frames.Length; i++) { var metaName = aniStream.ReadASCII(32).Replace("\0", string.Empty); aniStream.Position += 4 * 2; var numScripts = aniStream.ReadUInt32(); aniStream.Position += 32 * numScripts; var returnPosition = aniStream.Position; long metaPosition; if (aniStream.MetaIndex.TryGetValue(metaName, out metaPosition)) { var metaStream = new SegmentStream(aniStream, 0, aniStream.Length); metaStream.Position = metaPosition; var ddfName = metaStream.ReadASCII(32).Replace("\0", string.Empty); metaStream.Position += 4 * 3; var offset = new float2(metaStream.ReadInt32(), metaStream.ReadInt32()); long ddfPosition; if (aniStream.DdfIndex.TryGetValue(ddfName, out ddfPosition)) { var ddfStream = aniStream.DdfStream; ddfStream.Position = ddfPosition; frames[i] = new DdfSpriteFrame(ddfStream, offset); } } aniStream.Position = returnPosition; } return(true); }
public MobdAnimation(SegmentStream stream) { // OpenRA needs the same amount of frames per facing, filling up missing frames: var missingFrameWorkaround = 0; // Beetle => 10,10,10,8,10,10,10,10,10,10,10,10,10,10,10,10 missingFrameWorkaround += stream.BaseStream.Position == 174278 ? 2 : 0; // Flame => 9,9,9,9,8,8,9,9,9,9,9,9,9,9,9,9 missingFrameWorkaround += stream.BaseStream.Position == 2010426 || stream.BaseStream.Position == 2010466 ? 1 : 0; // Gort => 10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 missingFrameWorkaround += stream.BaseStream.Position == 2094122 ? 1 : 0; // TODO add gen2 ones here! (Worm projectile, ...) // TODO this is likely the animation speed. // Pattern is 0x00aabbcc // 0x00000010 // 0x00aaaa2a // flipping the bytes to 0xccbbaa00 makes more sence: // 0x10000000 // 0x2aaaaa00 // Notes: // 0x10000000 is the most common value // cc is never 00 // aa and bb often consist of the same value: 0000 1111 8888 aaaa ... /*Unk1 =*/ stream.ReadUInt32(); var frames = new List <MobdFrame>(); while (true) { var value = stream.ReadInt32(); if (value == 0 || value == -1) { break; // TODO 0 might mean "repeat", -1 might mean "do not repeat" } var returnPosition = stream.Position; stream.Position = value - stream.BaseOffset; var frame = new MobdFrame(stream); frames.Add(frame); if (missingFrameWorkaround-- > 0) { frames.Add(frame); } stream.Position = returnPosition; } Frames = frames.ToArray(); }
public Blit(SegmentStream stream) { // This is damn ugly, but it seems BLIT uses offsets from lvl start. var basePosition = (int)((SegmentStream)stream.BaseStream).BaseStream.Position - 8; Frames = new BlitFrame[stream.ReadInt32()]; var frameOffsets = new int[Frames.Length]; stream.ReadInt32(); // Unk var paletteOffset = stream.ReadInt32() - basePosition; var identifier = new string(stream.ReadASCII(4).Reverse().ToArray()); if (identifier != "BLT8") { throw new Exception("Unknwon blit type."); } for (var i = 0; i < Frames.Length; i++) { frameOffsets[i] = stream.ReadInt32() - basePosition; } stream.Position = paletteOffset; var palette = new byte[256 * 4]; for (var i = 0; i < palette.Length;) { var color16 = stream.ReadUInt16(); // aRRRRRGGGGGBBBBB palette[i++] = (byte)(((color16 & 0x001f) << 3) & 0xff); palette[i++] = (byte)(((color16 & 0x03e0) >> 2) & 0xff); palette[i++] = (byte)(((color16 & 0x7c00) >> 7) & 0xff); palette[i++] = 0xff; } for (var i = 0; i < Frames.Length; i++) { stream.Position = frameOffsets[i]; Frames[i] = new BlitFrame(stream, palette); } }
public MobdAnimation(SegmentStream stream, Version version) { // This are actually broken / bugged files. As we can not fix the containers, we have them hardcoded here: var missingFrameWorkaround = 0; // Beetle => 10,10,10,8,10,10,10,10,10,10,10,10,10,10,10,10 missingFrameWorkaround += stream.BaseStream.Position == 174278 ? 2 : 0; // Flame => 9,9,9,9,8,8,9,9,9,9,9,9,9,9,9,9 missingFrameWorkaround += stream.BaseStream.Position == 2010426 || stream.BaseStream.Position == 2010466 ? 1 : 0; // Gort => 10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 missingFrameWorkaround += stream.BaseStream.Position == 2094122 ? 1 : 0; // TODO add kknd2 bugged ones here! (Worm projectile, ...) /*Unk1 = */ stream.ReadUInt32(); // TODO this is likely the animation speed var frames = new List <MobdFrame>(); while (true) { var value = stream.ReadInt32(); if (value == 0 || value == -1) { break; // TODO this is most likely repeat pattern. 1,2,3,1,2,3 or 1,2,3,2,1,2,3 } var returnPosition = stream.Position; stream.Position = value - stream.BaseOffset; var frame = new MobdFrame(stream, version); frames.Add(frame); if (missingFrameWorkaround-- > 0) { frames.Add(frame); } stream.Position = returnPosition; } Frames = frames.ToArray(); if (version == Version.KKND2) { // KKnD 2 uses offsets only for frames they are used on instead of the whole animation. var points = new List <MobdPoint>(); foreach (var frame in Frames) { if (frame.Points == null) { continue; } foreach (var point in frame.Points) { if (point.Id == 0) { points.Add(new MobdPoint { X = point.X, Y = point.Y, Z = point.Z, Id = points.Count }); } } } foreach (var frame in Frames) { frame.Points = points.ToArray(); } } }
public Mapd(SegmentStream stream) { // This is damn ugly, but it seems MAPD uses offsets from lvl start. var basePosition = ((SegmentStream)stream.BaseStream).BaseStream.Position - 8; var test = stream.ReadInt32(); stream.Position += test * 4; var generation = stream.ReadInt32() == 256 ? Generation.Gen1 : Generation.Gen2; stream.Position -= (test + 2) * 4; if (generation == Generation.Gen2) { stream.ReadInt32(); // Unk } var layerOffsets = new int[stream.ReadInt32()]; Layers = new MapdLayer[layerOffsets.Length]; for (var i = 0; i < layerOffsets.Length; i++) { layerOffsets[i] = stream.ReadInt32(); } var palette = new byte[stream.ReadInt32() * 4]; if (generation == Generation.Gen2) { for (var i = 0; i < palette.Length;) { var color16 = stream.ReadUInt16(); // aRRRRRGGGGGBBBBB palette[i++] = (byte)(((color16 & 0x7c00) >> 7) & 0xff); palette[i++] = (byte)(((color16 & 0x03e0) >> 2) & 0xff); palette[i++] = (byte)(((color16 & 0x001f) << 3) & 0xff); palette[i++] = 0xff; } } else { stream.Read(palette); for (var i = 0; i < palette.Length / 4; i++) { palette[i * 4 + 3] = 0xff; } } for (var i = 0; i < Layers.Length; i++) { stream.Position = layerOffsets[i] - basePosition; var type = new string(stream.ReadASCII(4).Reverse().ToArray()); if (type != "SCRL") { throw new Exception("Unknown type."); } var tileWidth = stream.ReadInt32(); var tileHeight = stream.ReadInt32(); var tilesX = stream.ReadInt32(); var tilesY = stream.ReadInt32(); if (generation == Generation.Gen2) { stream.ReadInt32(); // Unk stream.ReadInt32(); // Unk stream.ReadInt32(); // Unk } var tilePixels = new Dictionary <int, byte[]>(); var tiles = new List <int>(); for (var y = 0; y < tilesY; y++) { for (var x = 0; x < tilesX; x++) { var tile = stream.ReadInt32(); if (generation == Generation.Gen2) { tile -= tile % 4; } tiles.Add(tile); if (tile != 0 && !tilePixels.ContainsKey(tile)) { tilePixels.Add(tile, new byte[tileWidth * tileHeight * 4]); } } } foreach (var(offset, pixels) in tilePixels) { stream.Position = offset - basePosition; if (generation == Generation.Gen1) { stream.ReadInt32(); // Unk } for (var y = 0; y < tileHeight; y++) { for (var x = 0; x < tileWidth; x++) { var index = stream.ReadByte(); if (index == 0 && i != 0) { continue; } Array.Copy(palette, index * 4, pixels, (y * tileWidth + x) * 4, 4); } } } var layer = new MapdLayer(tilesX * tileWidth, tilesY * tileHeight); Layers[i] = layer; for (var y = 0; y < tilesY; y++) { for (var x = 0; x < tilesX; x++) { var tile = tiles[y * tilesX + x]; if (tile == 0) { continue; } var pixels = tilePixels[tile]; var offset = (y * tileHeight * tilesX + x) * tileWidth; for (var row = 0; row < tileHeight; row++) { Array.Copy(pixels, row * tileWidth * 4, layer.Pixels, (offset + row * layer.Width) * 4, tileWidth * 4); } } } } }