using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace LibraryEditor
{
///
/// V2 Library
/// Uses DXT5 Images
/// Supports Frames (When at LibVersion 3)
///
public sealed class MLibraryV2
{
public const int LibVersion = 3;
public static bool Load = true;
public string FileName;
public List Images = new List();
public FrameSet Frames = new FrameSet();
public List IndexList = new List();
public int Count;
private bool _initialized;
private BinaryReader _reader;
private FileStream _stream;
public MLibraryV2(string filename)
{
FileName = filename;
Initialize();
Close();
}
public void Initialize()
{
int CurrentVersion;
_initialized = true;
if (!File.Exists(FileName))
return;
_stream = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite);
_reader = new BinaryReader(_stream);
CurrentVersion = _reader.ReadInt32();
if (CurrentVersion < 2)
{
MessageBox.Show("Wrong version, expecting lib version: " + LibVersion.ToString() + " found version: " + CurrentVersion.ToString() + ".", "Failed to open", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
return;
}
Count = _reader.ReadInt32();
Images = new List();
IndexList = new List();
int frameSeek = 0;
if (CurrentVersion >= 3)
{
frameSeek = _reader.ReadInt32();
}
for (int i = 0; i < Count; i++)
IndexList.Add(_reader.ReadInt32());
for (int i = 0; i < Count; i++)
Images.Add(null);
for (int i = 0; i < Count; i++)
CheckImage(i);
if (CurrentVersion >= 3)
{
_stream.Seek(frameSeek, SeekOrigin.Begin);
var frameCount = _reader.ReadInt32();
for (int i = 0; i < frameCount; i++)
{
Frames.Add((MirAction)_reader.ReadByte(), new Frame(_reader));
}
}
}
public void Close()
{
if (_stream != null)
_stream.Dispose();
}
public void Save()
{
Close();
MemoryStream stream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(stream);
Count = Images.Count;
IndexList.Clear();
int offSet = (4 + 4 + 4) + (Count * 4);
for (int i = 0; i < Count; i++)
{
IndexList.Add((int)stream.Length + offSet);
Images[i].Save(writer);
}
var frameSeek = (int)stream.Length + offSet;
writer.Flush();
byte[] fBytes = stream.ToArray();
_stream = File.Create(FileName);
writer = new BinaryWriter(_stream);
writer.Write(LibVersion);
writer.Write(Count);
writer.Write(frameSeek);
for (int i = 0; i < Count; i++)
writer.Write(IndexList[i]);
writer.Write(fBytes);
writer.Write(Frames.Keys.Count);
foreach (var action in Frames.Keys)
{
writer.Write((byte)action);
Frames[action].Save(writer);
}
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, bool removeBlack = true)
{
MImage mImage = new MImage(image, removeBlack) { X = x, Y = y };
Count++;
Images.Add(mImage);
}
public void AddImage(Bitmap image, Bitmap maskImage, short x, short y, bool removeBlack = true)
{
MImage mImage = new MImage(image, maskImage, removeBlack) { X = x, Y = y };
Count++;
Images.Add(mImage);
}
public void ReplaceImage(int Index, Bitmap image, short x, short y, bool removeBlack = true)
{
MImage mImage = new MImage(image, removeBlack) { X = x, Y = y };
Images[Index] = mImage;
}
public void InsertImage(int index, Bitmap image, short x, short y, bool removeBlack = true)
{
MImage mImage = new MImage(image, removeBlack) { 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 <= 24)
{
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, ShadowX, ShadowY;
public byte Shadow;
public int Length;
public byte[] FBytes;
public bool TextureValid;
public Bitmap Image, Preview;
//layer 2:
public short MaskWidth, MaskHeight, MaskX, MaskY;
public int MaskLength;
public byte[] MaskFBytes;
public Bitmap MaskImage;
public Boolean HasMask;
public MImage(BinaryReader reader)
{
//read layer 1
Width = reader.ReadInt16();
Height = reader.ReadInt16();
X = reader.ReadInt16();
Y = reader.ReadInt16();
ShadowX = reader.ReadInt16();
ShadowY = reader.ReadInt16();
Shadow = reader.ReadByte();
Length = reader.ReadInt32();
FBytes = reader.ReadBytes(Length);
//check if there's a second layer and read it
HasMask = ((Shadow >> 7) == 1) ? true : false;
if (HasMask)
{
MaskWidth = reader.ReadInt16();
MaskHeight = reader.ReadInt16();
MaskX = reader.ReadInt16();
MaskY = reader.ReadInt16();
MaskLength = reader.ReadInt32();
MaskFBytes = reader.ReadBytes(MaskLength);
}
}
public MImage(byte[] image, short Width, short Height)//only use this when converting from old to new type!
{
FBytes = image;
this.Width = Width;
this.Height = Height;
}
public MImage(Bitmap image, bool removeBlack = true)
{
if (image == null)
{
FBytes = new byte[0];
return;
}
Width = (short)image.Width;
Height = (short)image.Height;
Image = image;// FixImageSize(image);
FBytes = ConvertBitmapToArray(Image, removeBlack);
}
public MImage(Bitmap image, Bitmap Maskimage, bool removeBlack = true)
{
if (image == null)
{
FBytes = new byte[0];
return;
}
Width = (short)image.Width;
Height = (short)image.Height;
Image = image;// FixImageSize(image);
FBytes = ConvertBitmapToArray(Image, removeBlack);
if (Maskimage == null)
{
MaskFBytes = new byte[0];
return;
}
HasMask = true;
MaskWidth = (short)Maskimage.Width;
MaskHeight = (short)Maskimage.Height;
MaskImage = Maskimage;// FixImageSize(Maskimage);
MaskFBytes = ConvertBitmapToArray(MaskImage, removeBlack);
}
private Bitmap FixImageSize(Bitmap input)
{
int w = input.Width + (4 - input.Width % 4) % 4;
int h = input.Height + (4 - input.Height % 4) % 4;
if (input.Width != w || input.Height != h)
{
Bitmap temp = new Bitmap(w, h);
using (Graphics g = Graphics.FromImage(temp))
{
g.Clear(Color.Transparent);
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawImage(input, 0, 0);
g.Save();
}
input.Dispose();
input = temp;
}
return input;
}
private unsafe byte[] ConvertBitmapToArray(Bitmap input, bool removeBlack = true)
{
BitmapData data = input.LockBits(new Rectangle(0, 0, input.Width, input.Height), ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
byte[] pixels = new byte[input.Width * input.Height * 4];
Marshal.Copy(data.Scan0, pixels, 0, pixels.Length);
input.UnlockBits(data);
if (removeBlack)
{
for (int i = 0; i < pixels.Length; i += 4)
{
if (pixels[i] == 0 && pixels[i + 1] == 0 && pixels[i + 2] == 0)
pixels[i + 3] = 0; //Make Transparent
}
}
byte[] compressedBytes;
compressedBytes = Compress(pixels);
return compressedBytes;
}
public unsafe void CreateTexture(BinaryReader reader)
{
int w = Width;// +(4 - Width % 4) % 4;
int h = Height;// +(4 - Height % 4) % 4;
if (w < 2 || h < 2) return;
Image = new Bitmap(w, h);
BitmapData data = Image.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
byte[] dest = Decompress(FBytes);
Marshal.Copy(dest, 0, data.Scan0, dest.Length);
Image.UnlockBits(data);
dest = null;
if (HasMask)
{
w = MaskWidth;// +(4 - MaskWidth % 4) % 4;
h = MaskHeight;// +(4 - MaskHeight % 4) % 4;
if (w == 0 || h == 0)
{
return;
}
try
{
MaskImage = new Bitmap(w, h);
data = MaskImage.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
dest = Decompress(MaskFBytes);
Marshal.Copy(dest, 0, data.Scan0, dest.Length);
MaskImage.UnlockBits(data);
}
catch (Exception ex)
{
File.AppendAllText(@".\Error.txt",
string.Format("[{0}] {1}{2}", DateTime.Now, ex, Environment.NewLine));
}
}
dest = null;
}
public void Save(BinaryWriter writer)
{
writer.Write(Width);
writer.Write(Height);
writer.Write(X);
writer.Write(Y);
writer.Write(ShadowX);
writer.Write(ShadowY);
writer.Write(HasMask ? (byte)(Shadow | 0x80) : (byte)Shadow);
writer.Write(FBytes.Length);
writer.Write(FBytes);
if (HasMask)
{
writer.Write(MaskWidth);
writer.Write(MaskHeight);
writer.Write(MaskX);
writer.Write(MaskY);
writer.Write(MaskFBytes.Length);
writer.Write(MaskFBytes);
}
}
public static byte[] Compress(byte[] raw)
{
using (MemoryStream memory = new MemoryStream())
{
using (GZipStream gzip = new GZipStream(memory,
CompressionMode.Compress, true))
{
gzip.Write(raw, 0, raw.Length);
}
return memory.ToArray();
}
}
static byte[] Decompress(byte[] gzip)
{
// Create a GZIP stream with decompression mode.
// ... Then create a buffer and write into while reading from the GZIP stream.
using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
{
const int size = 4096;
byte[] buffer = new byte[size];
using (MemoryStream memory = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, size);
if (count > 0)
{
memory.Write(buffer, 0, count);
}
}
while (count > 0);
return memory.ToArray();
}
}
}
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.Low;//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();
}
}
}
}
}