| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- using ManagedSquish;
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Threading.Tasks;
- namespace LibraryEditor
- {
- public class WTLLibrary
- {
- private readonly string _fileName;
- public WTLImage[] Images;
- private BinaryReader _bReader;
- private int _count;
- private FileStream _fStream;
- private int[] _indexList;
- private bool _initialized;
- public bool IsNewVersion { get; private set; }
- public WTLLibrary(string filename)
- {
- _fileName = Path.ChangeExtension(filename, null);
- Initialize();
- }
- public void Initialize()
- {
- _initialized = true;
- if (!File.Exists(_fileName + ".wtl")) return;
- _fStream = new FileStream(_fileName + ".wtl", FileMode.Open, FileAccess.ReadWrite);
- _bReader = new BinaryReader(_fStream);
- LoadImageInfo();
- for (int i = 0; i < _count; i++)
- {
- CheckMImage(i);
- }
- }
- private void LoadImageInfo()
- {
- _fStream.Seek(2, SeekOrigin.Begin);
- var version = System.Text.Encoding.UTF8.GetString(_bReader.ReadBytes(20)).TrimEnd('\0');
- IsNewVersion = version == "ILIB v2.0-WEMADE";
- _fStream.Seek(28, SeekOrigin.Begin);
- _count = _bReader.ReadInt32();
- _indexList = new int[_count];
- Images = new WTLImage[_count];
- if (IsNewVersion)
- {
- _bReader.BaseStream.Seek(_fStream.Length - _count * 4, SeekOrigin.Begin);
- }
- for (int i = 0; i < _count; i++)
- _indexList[i] = _bReader.ReadInt32();
- }
- public void Close()
- {
- if (_fStream != null)
- _fStream.Dispose();
- if (_bReader != null)
- _bReader.Dispose();
- }
- public void CheckMImage(int index)
- {
- if (!_initialized)
- Initialize();
- if (index < 0 || index >= Images.Length) return;
- if (_indexList[index] == 0)
- {
- Images[index] = new WTLImage(index, IsNewVersion);
- return;
- }
- WTLImage image = Images[index];
- if (image == null)
- {
- _fStream.Seek(_indexList[index], SeekOrigin.Begin);
- image = new WTLImage(index, IsNewVersion, _bReader);
- Images[index] = image;
- image.CreateTexture(_bReader);
- }
- }
- public void ToMLibrary()
- {
- string fileName = Path.ChangeExtension(_fileName, ".Lib");
- if (File.Exists(fileName))
- File.Delete(fileName);
- MLibraryV2 library = new MLibraryV2(fileName) { Images = new List<MLibraryV2.MImage>(), IndexList = new List<int>(), Count = Images.Length };
- //library.Save();
- for (int i = 0; i < library.Count; i++)
- library.Images.Add(null);
- ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 8 };
- try
- {
- Parallel.For(0, Images.Length, options, i =>
- {
- WTLImage image = Images[i];
- if (image.HasMask)
- library.Images[i] = new MLibraryV2.MImage(image.Image, image.MaskImage) { X = image.X, Y = image.Y, ShadowX = image.ShadowX, ShadowY = image.ShadowY, Shadow = image.Shadow, MaskX = image.MaskX, MaskY = image.MaskY };
- else
- library.Images[i] = new MLibraryV2.MImage(image.Image) { X = image.X, Y = image.Y, ShadowX = image.ShadowX, ShadowY = image.ShadowY, Shadow = image.Shadow };
- });
- }
- catch (System.Exception)
- {
- throw;
- }
- finally
- {
- library.Save();
- }
- // Operation finished.
- // System.Windows.Forms.MessageBox.Show("Converted " + fileName + " successfully.",
- // "Shanda Information",
- // System.Windows.Forms.MessageBoxButtons.OK,
- // System.Windows.Forms.MessageBoxIcon.Information,
- // System.Windows.Forms.MessageBoxDefaultButton.Button1);
- }
- }
- public class WTLImage
- {
- public readonly short Width, Height, X, Y, ShadowX, ShadowY;
- public readonly int Length;
- public int DataOffset { get; }
- public readonly byte Shadow;
- public int Index { get; }
- public bool IsNewVersion { get; }
- public byte ImageTextureType { get; }
- public byte MaskTextureType { get; }
- private byte[] _fBytes;
- public Bitmap Image;
- public readonly Boolean HasMask;
- public short MaskWidth, MaskHeight, MaskX, MaskY;
- public int MaskLength;
- private byte[] _MaskfBytes;
- public Bitmap MaskImage;
- public WTLImage(int index, bool isNewVersion)
- {
- Index = index;
- IsNewVersion = isNewVersion;
- _fBytes = new byte[0];
- _MaskfBytes = new byte[0];
- }
- public WTLImage(int index, bool isNewVersion, BinaryReader bReader)
- {
- Index = index;
- IsNewVersion = isNewVersion;
- Width = bReader.ReadInt16();
- Height = bReader.ReadInt16();
- X = bReader.ReadInt16();
- Y = bReader.ReadInt16();
- ShadowX = bReader.ReadInt16();
- ShadowY = bReader.ReadInt16();
- if (IsNewVersion)
- {
- var imageU1 = bReader.ReadByte();
- ImageTextureType = bReader.ReadByte();
- var maskU1 = bReader.ReadByte();
- MaskTextureType = bReader.ReadByte();
- HasMask = MaskTextureType > 0;
- Length = bReader.ReadInt32();
- if (Length % 4 > 0) Length += 4 - (Length % 4);
- DataOffset = (int)bReader.BaseStream.Position;
- }
- else
- {
- Length = bReader.ReadByte() | bReader.ReadByte() << 8 | bReader.ReadByte() << 16;
- Shadow = bReader.ReadByte();
- HasMask = ((Shadow >> 7) == 1) ? true : false;
- }
- }
- public unsafe void CreateTexture(BinaryReader bReader)
- {
- Image = ReadImage(bReader, Length, Width, Height, ImageTextureType);
- if (HasMask)
- {
- if (IsNewVersion)
- {
- MaskWidth = Width;
- MaskHeight = Height;
- MaskX = X;
- MaskY = Y;
- MaskLength = bReader.ReadInt32();
- }
- else
- {
- MaskWidth = bReader.ReadInt16();
- MaskHeight = bReader.ReadInt16();
- MaskX = bReader.ReadInt16();
- MaskY = bReader.ReadInt16();
- bReader.ReadInt16();//mask shadow x
- bReader.ReadInt16();//mask shadow y
- MaskLength = bReader.ReadByte() | bReader.ReadByte() << 8 | bReader.ReadByte() << 16;
- bReader.ReadByte(); //mask shadow
- }
- MaskImage = ReadImage(bReader, MaskLength, MaskWidth, MaskHeight, MaskTextureType);
- }
- }
- private unsafe Bitmap DecompressV1Texture(BinaryReader bReader, int imageLength, short outputWidth, short outputHeight)
- {
- const int size = 8;
- int offset = 0, blockOffSet = 0;
- List<byte> countList = new List<byte>();
- int tWidth = 2;
- while (tWidth < Width)
- tWidth *= 2;
- outputHeight = (short)(Height + (4 - Height % 4) % 4);
- outputWidth = (short)(Width + (4 - Width % 4) % 4);
- _fBytes = bReader.ReadBytes(imageLength);
- Bitmap output = new Bitmap(outputWidth, outputHeight);
- if (_fBytes.Length != imageLength) return null;
- BitmapData data = output.LockBits(new Rectangle(0, 0, outputWidth, outputHeight), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
- byte* pixels = (byte*)data.Scan0;
- int cap = outputWidth * outputHeight * 4;
- int currentx = 0;
- while (blockOffSet < imageLength)
- {
- countList.Clear();
- for (int i = 0; i < 8; i++)
- countList.Add(_fBytes[blockOffSet++]);
- for (int i = 0; i < countList.Count; i++)
- {
- int count = countList[i];
- if (i % 2 == 0)
- {
- if (currentx >= tWidth)
- currentx -= tWidth;
- for (int off = 0; off < count; off++)
- {
- if (currentx < outputWidth)
- offset++;
- currentx += 4;
- if (currentx >= tWidth)
- currentx -= tWidth;
- }
- continue;
- }
- for (int c = 0; c < count; c++)
- {
- if (blockOffSet >= _fBytes.Length)
- break;
- byte[] newPixels = new byte[64];
- byte[] block = new byte[size];
- Array.Copy(_fBytes, blockOffSet, block, 0, size);
- blockOffSet += size;
- DecompressBlock(newPixels, block);
- int pixelOffSet = 0;
- byte[] sourcePixel = new byte[4];
- for (int py = 0; py < 4; py++)
- {
- for (int px = 0; px < 4; px++)
- {
- int blockx = offset % (outputWidth / 4);
- int blocky = offset / (outputWidth / 4);
- int x = blockx * 4;
- int y = blocky * 4;
- int destPixel = ((y + py) * outputWidth) * 4 + (x + px) * 4;
- Array.Copy(newPixels, pixelOffSet, sourcePixel, 0, 4);
- pixelOffSet += 4;
- if (destPixel + 4 > cap)
- break;
- for (int pc = 0; pc < 4; pc++)
- pixels[destPixel + pc] = sourcePixel[pc];
- }
- }
- offset++;
- if (currentx >= outputWidth)
- currentx -= outputWidth;
- currentx += 4;
- }
- }
- }
- output.UnlockBits(data);
- return output;
- }
- private unsafe Bitmap DecompressV2Texture(BinaryReader bReader, int imageLength, short outputWidth, short outputHeight, byte textureType)
- {
- var buffer = bReader.ReadBytes(imageLength);
- int w = Width + (4 - Width % 4) % 4;
- int a = 1;
- while (true)
- {
- a *= 2;
- if (a >= w)
- {
- w = a;
- break;
- }
- }
- int h = Height + (4 - Height % 4) % 4;
- int e = w * h / 2;
- SquishFlags type;
- switch (textureType)
- {
- case 0:
- case 1:
- type = SquishFlags.Dxt1;
- break;
- case 3:
- type = SquishFlags.Dxt3;
- break;
- case 5:
- type = SquishFlags.Dxt5;
- break;
- default:
- throw new NotImplementedException();
- }
- var decompressedBuffer = Ionic.Zlib.DeflateStream.UncompressBuffer(buffer);
- var bitmap = new Bitmap(w, h);
- BitmapData data = bitmap.LockBits(
- new Rectangle(0, 0, w, h),
- ImageLockMode.WriteOnly,
- PixelFormat.Format32bppRgb
- );
- fixed (byte* source = decompressedBuffer)
- Squish.DecompressImage(data.Scan0, w, h, (IntPtr)source, type);
- byte* dest = (byte*)data.Scan0;
- for (int i = 0; i < w * h * 4; i += 4)
- {
- //Reverse Red/Blue
- byte b = dest[i];
- dest[i] = dest[i + 2];
- dest[i + 2] = b;
- }
- bitmap.UnlockBits(data);
- Rectangle cloneRect = new Rectangle(0, 0, outputWidth, outputHeight);
- PixelFormat format = bitmap.PixelFormat;
- Bitmap cloneBitmap = bitmap.Clone(cloneRect, format);
- return cloneBitmap;
- }
- public unsafe Bitmap ReadImage(BinaryReader bReader, int imageLength, short outputWidth, short outputHeight, byte textureType)
- {
- return IsNewVersion
- ? DecompressV2Texture(bReader, imageLength, outputWidth, outputHeight, textureType)
- : DecompressV1Texture(bReader, imageLength, outputWidth, outputHeight);
- }
- private static void DecompressBlock(IList<byte> newPixels, byte[] block)
- {
- byte[] colours = new byte[8];
- Array.Copy(block, 0, colours, 0, 8);
- byte[] codes = new byte[16];
- int a = Unpack(block, 0, codes, 0);
- int b = Unpack(block, 2, codes, 4);
- for (int i = 0; i < 3; i++)
- {
- int c = codes[i];
- int d = codes[4 + i];
- if (a <= b)
- {
- codes[8 + i] = (byte)((c + d) / 2);
- codes[12 + i] = 0;
- }
- else
- {
- codes[8 + i] = (byte)((2 * c + d) / 3);
- codes[12 + i] = (byte)((c + 2 * d) / 3);
- }
- }
- codes[8 + 3] = 255;
- codes[12 + 3] = (a <= b) ? (byte)0 : (byte)255;
- for (int i = 0; i < 4; i++)
- {
- if ((codes[i * 4] == 0) && (codes[(i * 4) + 1] == 0) && (codes[(i * 4) + 2] == 0) && (codes[(i * 4) + 3] == 255))
- { //dont ever use pure black cause that gives transparency issues
- codes[i * 4] = 1;
- codes[(i * 4) + 1] = 1;
- codes[(i * 4) + 2] = 1;
- }
- }
- byte[] indices = new byte[16];
- for (int i = 0; i < 4; i++)
- {
- byte packed = block[4 + i];
- indices[0 + i * 4] = (byte)(packed & 0x3);
- indices[1 + i * 4] = (byte)((packed >> 2) & 0x3);
- indices[2 + i * 4] = (byte)((packed >> 4) & 0x3);
- indices[3 + i * 4] = (byte)((packed >> 6) & 0x3);
- }
- for (int i = 0; i < 16; i++)
- {
- byte offset = (byte)(4 * indices[i]);
- for (int j = 0; j < 4; j++)
- newPixels[4 * i + j] = codes[offset + j];
- }
- }
- private static int Unpack(IList<byte> packed, int srcOffset, IList<byte> colour, int dstOffSet)
- {
- int value = packed[0 + srcOffset] | (packed[1 + srcOffset] << 8);
- // get components in the stored range
- byte red = (byte)((value >> 11) & 0x1F);
- byte green = (byte)((value >> 5) & 0x3F);
- byte blue = (byte)(value & 0x1F);
- // Scale up to 24 Bit
- colour[2 + dstOffSet] = (byte)((red << 3) | (red >> 2));
- colour[1 + dstOffSet] = (byte)((green << 2) | (green >> 4));
- colour[0 + dstOffSet] = (byte)((blue << 3) | (blue >> 2));
- colour[3 + dstOffSet] = 255;
- //*/
- return value;
- }
- }
- }
|