using ManagedSquish; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; namespace LibraryEditor { /// /// V0 Library /// DXT1 Images /// No versioning supported /// public sealed class MLibraryV0 { public static bool Load = true; public string FileName; public List Images = new List(); public List IndexList = new List(); public int Count; private bool _initialized; private BinaryReader _reader; private FileStream _stream; public MLibraryV0(string filename) { FileName = filename; Initialize(); Close(); } public void Initialize() { _initialized = true; if (!File.Exists(FileName)) return; _stream = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite); _reader = new BinaryReader(_stream); Count = _reader.ReadInt32(); Images = new List(); IndexList = new List(); for (int i = 0; i < Count; i++) IndexList.Add(_reader.ReadInt32()); for (int i = 0; i < Count; i++) Images.Add(null); Load = false;//dont need to process all images when converting for (int i = 0; i < Count; i++) CheckImage(i); } public void Close() { if (_stream != null) _stream.Dispose(); // if (_reader != null) // _reader.Dispose(); } public void Save() { Close(); MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); Count = Images.Count; IndexList.Clear(); int offSet = 4 + Count * 4; for (int i = 0; i < Count; i++) { IndexList.Add((int)stream.Length + offSet); Images[i].Save(writer); } writer.Flush(); byte[] fBytes = stream.ToArray(); // writer.Dispose(); _stream = File.Create(FileName); writer = new BinaryWriter(_stream); writer.Write(Count); for (int i = 0; i < Count; i++) writer.Write(IndexList[i]); writer.Write(fBytes); writer.Flush(); writer.Close(); writer.Dispose(); Close(); } private void CheckImage(int index) { if (!_initialized) Initialize(); if (Images == null || index < 0 || index >= Images.Count) return; if (Images[index] == null) { _stream.Position = IndexList[index]; Images[index] = new MImage(_reader); } if (!Load) return; MImage mi = Images[index]; if (!mi.TextureValid) { _stream.Seek(IndexList[index] + 12, SeekOrigin.Begin); mi.CreateTexture(_reader); } } public Point GetOffSet(int index) { if (!_initialized) Initialize(); if (Images == null || index < 0 || index >= Images.Count) return Point.Empty; if (Images[index] == null) { _stream.Seek(IndexList[index], SeekOrigin.Begin); Images[index] = new MImage(_reader); } return new Point(Images[index].X, Images[index].Y); } public Size GetSize(int index) { if (!_initialized) Initialize(); if (Images == null || index < 0 || index >= Images.Count) return Size.Empty; if (Images[index] == null) { _stream.Seek(IndexList[index], SeekOrigin.Begin); Images[index] = new MImage(_reader); } return new Size(Images[index].Width, Images[index].Height); } public MImage GetMImage(int index) { if (index < 0 || index >= Images.Count) return null; return Images[index]; } public Bitmap GetPreview(int index) { if (index < 0 || index >= Images.Count) return new Bitmap(1, 1); MImage image = Images[index]; if (image == null || image.Image == null) return new Bitmap(1, 1); if (image.Preview == null) image.CreatePreview(); return image.Preview; } public void AddImage(Bitmap image, short x, short y) { MImage mImage = new MImage(image) { X = x, Y = y }; Count++; Images.Add(mImage); } public void InsertImage(int index, Bitmap image, short x, short y) { MImage mImage = new MImage(image) { X = x, Y = y }; Count++; Images.Insert(index, mImage); } public void RemoveImage(int index) { if (Images == null || Count <= 1) { Count = 0; Images = new List(); return; } Count--; Images.RemoveAt(index); } public static bool CompareBytes(byte[] a, byte[] b) { if (a == b) return true; if (a == null || b == null || a.Length != b.Length) return false; for (int i = 0; i < a.Length; i++) if (a[i] != b[i]) return false; return true; } public void RemoveBlanks(bool safe = false) { for (int i = Count - 1; i >= 0; i--) { if (Images[i].FBytes == null || Images[i].FBytes.Length <= 8) { if (!safe) RemoveImage(i); else if (Images[i].X == 0 && Images[i].Y == 0) RemoveImage(i); } } } public sealed class MImage { public short Width, Height, X, Y; public int Length; public bool TextureValid; public Bitmap Image, Preview; public byte[] FBytes; public MImage(BinaryReader reader) { Width = reader.ReadInt16(); Height = reader.ReadInt16(); X = reader.ReadInt16(); Y = reader.ReadInt16(); Length = reader.ReadInt32(); FBytes = reader.ReadBytes(Length); } public unsafe MImage(Bitmap image) { if (image == null) { FBytes = new byte[0]; return; } Width = (short)image.Width; Height = (short)image.Height; int w = image.Width + (4 - image.Width % 4) % 4; int h = image.Height + (4 - image.Height % 4) % 4; if (image.Width != w || image.Height != h) { Bitmap temp = new Bitmap(w, h); using (Graphics g = Graphics.FromImage(temp)) { g.Clear(Color.Transparent); g.DrawImage(image, 0, 0); g.Save(); } image.Dispose(); image = temp; } Image = image; BitmapData data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); byte[] pixels = new byte[image.Width * image.Height * 4]; Marshal.Copy(data.Scan0, pixels, 0, pixels.Length); image.UnlockBits(data); for (int i = 0; i < pixels.Length; i += 4) { //Reverse Red/Blue byte b = pixels[i]; pixels[i] = pixels[i + 2]; pixels[i + 2] = b; if (pixels[i] == 0 && pixels[i + 1] == 0 && pixels[i + 2] == 0) pixels[i + 3] = 0; //Make Transparent } int count = Squish.GetStorageRequirements(image.Width, image.Height, SquishFlags.Dxt1); FBytes = new byte[count]; fixed (byte* dest = FBytes) fixed (byte* source = pixels) { Squish.CompressImage((IntPtr)source, image.Width, image.Height, (IntPtr)dest, SquishFlags.Dxt1); } } public void Save(BinaryWriter writer) { writer.Write(Width); writer.Write(Height); writer.Write(X); writer.Write(Y); writer.Write(FBytes.Length); writer.Write(FBytes); } public unsafe void CreateTexture(BinaryReader reader) { int w = Width + (4 - Width % 4) % 4; int h = Height + (4 - Height % 4) % 4; if (w == 0 || h == 0) { return; } Image = new Bitmap(w, h); BitmapData data = Image.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); fixed (byte* source = FBytes) Squish.DecompressImage(data.Scan0, w, h, (IntPtr)source, SquishFlags.Dxt1); byte* dest = (byte*)data.Scan0; for (int i = 0; i < h * w * 4; i += 4) { //Reverse Red/Blue byte b = dest[i]; dest[i] = dest[i + 2]; dest[i + 2] = b; } Image.UnlockBits(data); } public void CreatePreview() { if (Image == null) { Preview = new Bitmap(1, 1); return; } Preview = new Bitmap(64, 64); using (Graphics g = Graphics.FromImage(Preview)) { g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.Clear(Color.Transparent); int w = Math.Min((int)Width, 64); int h = Math.Min((int)Height, 64); g.DrawImage(Image, new Rectangle((64 - w) / 2, (64 - h) / 2, w, h), new Rectangle(0, 0, Width, Height), GraphicsUnit.Pixel); g.Save(); } } } } }