public MobdImage(SegmentStream stream, uint flags, Version version) { bool flipped; Width = stream.ReadUInt32(); Height = stream.ReadUInt32(); Pixels = new byte[Width * Height]; if (version == Version.KKND1) { flipped = (flags & 0x1) == 1; var isCompressed = stream.ReadUInt8() == 2; if (isCompressed) { DecompressKknd1(stream); } else { stream.ReadBytes(Pixels, 0, Pixels.Length); } } else { flipped = ((flags >> 31) & 0x1) == 1; var isCompressed = ((flags >> 27) & 0x1) == 1; var has256Colors = ((flags >> 26) & 0x1) == 1; if (isCompressed) { DecompressKknd2(stream, has256Colors); } else { stream.ReadBytes(Pixels, 0, Pixels.Length); } } if (!flipped) { return; } for (var i = 0; i < Height; i++) { var row = new byte[Width]; Array.Copy(Pixels, i * Width, row, 0, Width); Array.Reverse(row); Array.Copy(row, 0, Pixels, i * Width, Width); } }
public MobdFrame(SegmentStream stream, Version version) { OriginX = stream.ReadUInt32(); OriginY = stream.ReadUInt32(); /*Unk1 =*/ stream.ReadUInt32(); var renderFlagsOffset = stream.ReadUInt32(); /*var boxListOffset =*/ stream.ReadUInt32(); // we do not read boxes (2 points) /*Unk2 =*/ stream.ReadUInt32(); var pointListOffset = stream.ReadUInt32(); if (pointListOffset > 0) { var points = new List <MobdPoint>(); stream.Position = pointListOffset - stream.BaseOffset; while (true) { var boxId = stream.ReadUInt32(); if (boxId == 0xffffffff) { break; } stream.Position -= 4; points.Add(new MobdPoint(stream)); } Points = points.ToArray(); } stream.Position = renderFlagsOffset - stream.BaseOffset; RenderFlags = new MobdRenderFlags(stream, version); }
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 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 MobdRenderFlags(SegmentStream stream, Version version) { /*Type = */ stream.ReadASCII(4); var flags = stream.ReadUInt32(); if (version == Version.KKND2) { var paletteOffset = stream.ReadUInt32() - stream.BaseOffset; var returnPos = stream.Position; stream.Position = paletteOffset; stream.ReadUInt32(); // 00 00 00 80 stream.ReadUInt32(); // 00 00 00 80 stream.ReadUInt32(); // 00 00 00 80 var numColors = stream.ReadUInt16(); Palette = new uint[256]; for (var i = 0; i < numColors; i++) { var color16 = stream.ReadUInt16(); // aRRRRRGGGGGBBBBB var r = ((color16 & 0x7c00) >> 7) & 0xff; var g = ((color16 & 0x03e0) >> 2) & 0xff; var b = ((color16 & 0x001f) << 3) & 0xff; Palette[i] = (uint)((0xff << 24) | (r << 16) | (g << 8) | b); } stream.Position = returnPos; } var imageOffset = stream.ReadUInt32() - stream.BaseOffset; stream.Position = imageOffset; Image = new MobdImage(stream, flags, version); }
public MobdFrame(SegmentStream stream) { OffsetX = stream.ReadUInt32(); OffsetY = stream.ReadUInt32(); /*Unk1 =*/ stream.ReadUInt32(); var renderFlagsOffset = stream.ReadUInt32(); /*var boxListOffset =*/ stream.ReadUInt32(); // we do not read boxes (2 points, min and max) /*Unk2 =*/ stream.ReadUInt32(); var pointListOffset = stream.ReadUInt32(); // Theoretically we could also read the boxes here. // However they contain info about hitshaped etc. We define them in yaml to be able to tweak them. // But the points are required for turrets, muzzles and projectile launch offsets. if (pointListOffset > 0) { var points = new List <MobdPoint>(); stream.Position = pointListOffset - stream.BaseOffset; while (true) { var boxId = stream.ReadUInt32(); if (boxId == 0xffffffff) { break; } stream.Position -= 4; points.Add(new MobdPoint(stream)); } Points = points.ToArray(); } stream.Position = renderFlagsOffset - stream.BaseOffset; RenderFlags = new MobdRenderFlags(stream); }
public MobdRenderFlags(SegmentStream stream) { var type = new string(stream.ReadASCII(4).Reverse().ToArray()); var flags = stream.ReadUInt32(); var generation = Generation.Unknown; if (type == "SPRT") { generation = Generation.Gen1; } else if (type == "SPNS" || type == "SPRC") { generation = Generation.Gen2; } if (generation == Generation.Gen2) { var paletteOffset = stream.ReadUInt32() - stream.BaseOffset; var returnPos = stream.Position; stream.Position = paletteOffset; stream.ReadUInt32(); // 00 00 00 80 stream.ReadUInt32(); // 00 00 00 80 stream.ReadUInt32(); // 00 00 00 80 var numColors = stream.ReadUInt16(); Palette = new uint[256]; for (var i = 0; i < numColors; i++) { var color16 = stream.ReadUInt16(); // aRRRRRGGGGGBBBBB var r = ((color16 & 0x7c00) >> 7) & 0xff; var g = ((color16 & 0x03e0) >> 2) & 0xff; var b = ((color16 & 0x001f) << 3) & 0xff; Palette[i] = (uint)((0xff << 24) | (r << 16) | (g << 8) | b); } stream.Position = returnPos; } var imageOffset = stream.ReadUInt32() - stream.BaseOffset; stream.Position = imageOffset; Image = new MobdImage(stream, flags, generation); }
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 Mobd(SegmentStream stream, Version version) { var fileOffset = (uint)stream.BaseOffset; var firstFrameStart = stream.Length; var justReadFrameOffset = false; var animationOffsets = new List <uint>(); var rotationalAnimations = new List <MobdAnimation>(); var simpleAnimations = new List <MobdAnimation>(); while (stream.Position < firstFrameStart) { var value = stream.ReadUInt32(); // This parsing method is trash, because animation offsets are hardcoded in .exe but it seems to work. if ((value == 0xffffffff || value == 0x00000000) && justReadFrameOffset) { // terminator justReadFrameOffset = false; } else if (value - fileOffset > stream.Position && value - fileOffset < stream.Length) { // frame justReadFrameOffset = true; firstFrameStart = Math.Min(firstFrameStart, value - fileOffset); } else if (value - fileOffset < stream.Position && value >= fileOffset) { // animation pointer animationOffsets.Remove(value - fileOffset); var returnPosition = stream.Position; stream.Position = value - fileOffset; rotationalAnimations.Add(new MobdAnimation(stream, version)); stream.Position = returnPosition; } else if (value == 0) { // TODO filler ? Sprite Invisible? } else { animationOffsets.Add((uint)(stream.Position - 4)); } } foreach (var animationOffset in animationOffsets) { try { stream.Position = animationOffset; simpleAnimations.Add(new MobdAnimation(stream, version)); } catch (Exception) { // TODO crashes on kknd2, fix me! } } RotationalAnimations = rotationalAnimations.ToArray(); SimpleAnimations = simpleAnimations.ToArray(); }