private static SpriteIndexInfo[] WriteSprites(BinaryWriter writer, SpriteSetHeader header, List <SpriteEntry> sprites) { List <SpriteIndexInfo> spriteIndexData = new List <SpriteIndexInfo>(); int spriteIndex = 0; int listIndex = 0; while (listIndex < sprites.Count) { if (sprites[listIndex].Index == spriteIndex) { SpriteIndexInfo spriteWritten = WriteSprite(writer, header, sprites[listIndex].Sprite); spriteIndexData.Add(spriteWritten); ++listIndex; } else { SpriteIndexInfo spriteEmpty = new SpriteIndexInfo(writer.BaseStream.Position); spriteIndexData.Add(spriteEmpty); writer.Write((UInt16)0); } ++spriteIndex; } return(spriteIndexData.ToArray()); }
private static Bitmap ReadSprite(BinaryReader reader, SpriteSetHeader header) { // TODO(adm244): maybe return PixelFormat instead of bytesPerPixel? byte[] buffer = ReadSprite(reader, header, out int width, out int height, out int bytesPerPixel); if (buffer == null) { return(null); } PixelFormat format = PixelFormatExtension.FromBytesPerPixel(bytesPerPixel); if (format == PixelFormat.Indexed) { return(new Bitmap(width, height, buffer, format, header.Palette)); } Bitmap bitmap = new Bitmap(width, height, buffer, format); // NOTE(adm244): since AGS doesn't support 24bpp RLE images it converts them to 32bpp // (even if it won't be compressed, you know, 'just in case' case...) // in the process alpha channel gets set to 0 (transparent) instead of 255 (opaque) // which leads to a problem of fully transparent images (and "features" in GDI decoders) // // to resolve this issue, we check if alpha channel is used and if not discard it if (format == PixelFormat.Argb32 && !IsAlphaChannelUsed(buffer)) { bitmap = bitmap.Convert(PixelFormat.Rgb24); } return(bitmap); }
private static void WriteSpriteSetHeader(BinaryWriter writer, SpriteSetHeader header, int spritesCount) { writer.Write((UInt16)header.Version); writer.Write((char[])SpriteSetSignature.ToCharArray()); if (header.Version >= 6) { writer.Write((byte)header.Compression); writer.Write((UInt32)header.FileID); } if (header.Version < 5) { AGSGraphics.WritePalette(writer, header.Palette); } if (header.Version < 11) { writer.Write((UInt16)spritesCount); } else { writer.Write((Int32)spritesCount); } }
public static SpriteSetHeader ReadFromFile(string filepath) { SpriteSetHeader header = new SpriteSetHeader(); using (FileStream stream = new FileStream(filepath, FileMode.Open)) { using (BinaryReader reader = new BinaryReader(stream, Encoding.Latin1)) { header.Version = reader.ReadInt16(); header.Compression = CompressionType.Unknown; byte compressionType = reader.ReadByte(); if (Enum.IsDefined(typeof(CompressionType), (int)compressionType)) { header.Compression = (CompressionType)compressionType; } header.FileID = reader.ReadUInt32(); header.SpritesCount = reader.ReadUInt16(); if (header.Version < 5) { header.Palette = AGSGraphics.ReadPalette(reader); } } } return(header); }
private static bool UnpackSpritesInternal(string spriteFilePath, string targetFolderPath) { Console.Write("Opening {0}...", spriteFilePath); SpriteSetHeader header = null; using (FileStream stream = new FileStream(spriteFilePath, FileMode.Open)) { using (BinaryReader reader = new BinaryReader(stream, Encoding.Latin1)) { Console.WriteLine(" Done!"); Console.Write("Parsing {0}...", spriteFilePath); header = ReadSpriteSetHeader(reader); Console.WriteLine(" Done!"); Console.WriteLine("Extracting..."); //TODO(adm244): read sprindex.dat for (int index = 0; index <= header.SpritesCount; ++index) { Console.Write(string.Format("\tExtracting spr{0:D5}...", index)); Bitmap sprite = ReadSprite(reader, header); if (sprite == null) { Console.WriteLine(" Skipping (empty)."); continue; } SaveSprite(sprite, targetFolderPath, index); Console.WriteLine(" Done!"); } } } //TODO(adm244): should probably check if file were _actually_ read if (header == null) { Console.WriteLine("Error! Could not read a file."); return(false); } Console.WriteLine("Done!"); Console.Write("Writting meta file..."); header.WriteMetaFile(targetFolderPath); Console.WriteLine(" Done!"); return(header.SpritesCount > 0); }
//TODO(adm244): ReadSpriteIndexFile private static void WriteSpriteIndexFile(string outputFolder, SpriteSetHeader header, SpriteIndexInfo[] spriteIndexInfo, int version) { // FIXME(adm244): check all filepaths so that they ALL are either RELATIVE or ABSOLUTE // because for now some files are saved in a working directory (relative paths) // and some in other places (absolute paths) string targetFilepath = Path.Combine(outputFolder, SpriteSetIndexFileName); using (FileStream stream = new FileStream(targetFilepath, FileMode.Create)) { using (BinaryWriter writer = new BinaryWriter(stream, Encoding.Latin1)) { writer.Write((char[])SpriteSetIndexSignature.ToCharArray()); writer.Write((UInt32)version); if (version >= 2) { writer.Write((UInt32)(header.FileID)); } writer.Write((UInt32)(spriteIndexInfo.Length - 1)); writer.Write((UInt32)(spriteIndexInfo.Length)); for (int i = 0; i < spriteIndexInfo.Length; ++i) { writer.Write((UInt16)spriteIndexInfo[i].Width); } for (int i = 0; i < spriteIndexInfo.Length; ++i) { writer.Write((UInt16)spriteIndexInfo[i].Height); } if (version <= 2) { for (int i = 0; i < spriteIndexInfo.Length; ++i) { writer.Write((UInt32)spriteIndexInfo[i].Offset); } } else { for (int i = 0; i < spriteIndexInfo.Length; ++i) { writer.Write((UInt64)spriteIndexInfo[i].Offset); } } } } }
private static SpriteIndexInfo WriteSprite(BinaryWriter writer, SpriteSetHeader header, Bitmap sprite) { SpriteIndexInfo spriteIndexData = new SpriteIndexInfo(); spriteIndexData.Width = sprite.Width; spriteIndexData.Height = sprite.Height; spriteIndexData.Offset = writer.BaseStream.Position; //NOTE(adm244): AGS doesn't support 24bpp RLE compressed images, so we convert them to 32bpp (null alpha) // ALSO, AGS seems to treat 24bpp images as RGB while all others as BGR (!) // so let's just NOT use 24bpp and convert them to 32bpp if (sprite.Format == PixelFormat.Rgb24) { sprite = sprite.Convert(PixelFormat.Argb32, discardAlpha: true); } writer.Write((UInt16)sprite.BytesPerPixel); writer.Write((UInt16)sprite.Width); writer.Write((UInt16)sprite.Height); byte[] buffer = sprite.GetPixels(); if (header.Compression == CompressionType.RLE) { buffer = CompressRLE(buffer, sprite.Width, sprite.Height, sprite.BytesPerPixel); } if (header.Version >= 6) { if (header.Compression == CompressionType.RLE) { writer.Write((UInt32)buffer.Length); } } else if (header.Version == 5) { writer.Write((UInt32)buffer.Length); } writer.Write((byte[])buffer); return(spriteIndexData); }
private static void PackSpritesInternal(string outputFilepath, string headerFilepath, params string[] filepaths) { SpriteSetHeader header = SpriteSetHeader.ReadFromFile(headerFilepath); List <SpriteEntry> sprites = GetSortedSpritesList(filepaths); using (FileStream stream = new FileStream(outputFilepath, FileMode.Create)) { using (BinaryWriter writer = new BinaryWriter(stream, Encoding.Latin1)) { int spritesCount = GetLargestIndex(sprites); WriteSpriteSetHeader(writer, header, spritesCount); SpriteIndexInfo[] spritesWritten = WriteSprites(writer, header, sprites); // HACK(adm244): temp solution string indexFilepath = Path.GetDirectoryName(outputFilepath); int version = GetSpriteIndexVersion(header); WriteSpriteIndexFile(indexFilepath, header, spritesWritten, version); } } }
private static int GetSpriteIndexVersion(SpriteSetHeader header) { switch (header.Version) { // NOTE(adm244): version 5 is between 3.1.0 and 3.1.2 // 3.1.0 has version 4; 3.1.2 has version 6 // guess it was never released? why bump it then? case 4: case 5: return(1); case 6: return(2); case 10: case 11: return(header.Version); default: throw new NotSupportedException( $"Cannot determine sprite index file version.\n\nUnknown sprite set version: {header.Version}"); } }
private static byte[] ReadSprite(BinaryReader reader, SpriteSetHeader header, out int width, out int height, out int bytesPerPixel) { width = 0; height = 0; bytesPerPixel = reader.ReadUInt16(); if (bytesPerPixel == 0) { return(null); } width = reader.ReadUInt16(); height = reader.ReadUInt16(); long size = (long)width * height * bytesPerPixel; long sizeUncompressed = size; if (header.Version >= 6) { if (header.Compression == CompressionType.RLE) { size = reader.ReadUInt32(); } } else if (header.Version == 5) { size = reader.ReadUInt32(); } if (header.Compression == CompressionType.RLE) { return(DecompressRLE(reader, size, sizeUncompressed, bytesPerPixel)); } return(reader.ReadBytes((int)size)); }