WTLLibrary.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. using ManagedSquish;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Drawing;
  5. using System.Drawing.Imaging;
  6. using System.IO;
  7. using System.Threading.Tasks;
  8. namespace LibraryEditor
  9. {
  10. public class WTLLibrary
  11. {
  12. private readonly string _fileName;
  13. public WTLImage[] Images;
  14. private BinaryReader _bReader;
  15. private int _count;
  16. private FileStream _fStream;
  17. private int[] _indexList;
  18. private bool _initialized;
  19. public bool IsNewVersion { get; private set; }
  20. public WTLLibrary(string filename)
  21. {
  22. _fileName = Path.ChangeExtension(filename, null);
  23. Initialize();
  24. }
  25. public void Initialize()
  26. {
  27. _initialized = true;
  28. if (!File.Exists(_fileName + ".wtl")) return;
  29. _fStream = new FileStream(_fileName + ".wtl", FileMode.Open, FileAccess.ReadWrite);
  30. _bReader = new BinaryReader(_fStream);
  31. LoadImageInfo();
  32. for (int i = 0; i < _count; i++)
  33. {
  34. CheckMImage(i);
  35. }
  36. }
  37. private void LoadImageInfo()
  38. {
  39. _fStream.Seek(2, SeekOrigin.Begin);
  40. var version = System.Text.Encoding.UTF8.GetString(_bReader.ReadBytes(20)).TrimEnd('\0');
  41. IsNewVersion = version == "ILIB v2.0-WEMADE";
  42. _fStream.Seek(28, SeekOrigin.Begin);
  43. _count = _bReader.ReadInt32();
  44. _indexList = new int[_count];
  45. Images = new WTLImage[_count];
  46. if (IsNewVersion)
  47. {
  48. _bReader.BaseStream.Seek(_fStream.Length - _count * 4, SeekOrigin.Begin);
  49. }
  50. for (int i = 0; i < _count; i++)
  51. _indexList[i] = _bReader.ReadInt32();
  52. }
  53. public void Close()
  54. {
  55. if (_fStream != null)
  56. _fStream.Dispose();
  57. if (_bReader != null)
  58. _bReader.Dispose();
  59. }
  60. public void CheckMImage(int index)
  61. {
  62. if (!_initialized)
  63. Initialize();
  64. if (index < 0 || index >= Images.Length) return;
  65. if (_indexList[index] == 0)
  66. {
  67. Images[index] = new WTLImage(index, IsNewVersion);
  68. return;
  69. }
  70. WTLImage image = Images[index];
  71. if (image == null)
  72. {
  73. _fStream.Seek(_indexList[index], SeekOrigin.Begin);
  74. image = new WTLImage(index, IsNewVersion, _bReader);
  75. Images[index] = image;
  76. image.CreateTexture(_bReader);
  77. }
  78. }
  79. public void ToMLibrary()
  80. {
  81. string fileName = Path.ChangeExtension(_fileName, ".Lib");
  82. if (File.Exists(fileName))
  83. File.Delete(fileName);
  84. MLibraryV2 library = new MLibraryV2(fileName) { Images = new List<MLibraryV2.MImage>(), IndexList = new List<int>(), Count = Images.Length };
  85. //library.Save();
  86. for (int i = 0; i < library.Count; i++)
  87. library.Images.Add(null);
  88. ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = 8 };
  89. try
  90. {
  91. Parallel.For(0, Images.Length, options, i =>
  92. {
  93. WTLImage image = Images[i];
  94. if (image.HasMask)
  95. 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 };
  96. else
  97. library.Images[i] = new MLibraryV2.MImage(image.Image) { X = image.X, Y = image.Y, ShadowX = image.ShadowX, ShadowY = image.ShadowY, Shadow = image.Shadow };
  98. });
  99. }
  100. catch (System.Exception)
  101. {
  102. throw;
  103. }
  104. finally
  105. {
  106. library.Save();
  107. }
  108. // Operation finished.
  109. // System.Windows.Forms.MessageBox.Show("Converted " + fileName + " successfully.",
  110. // "Shanda Information",
  111. // System.Windows.Forms.MessageBoxButtons.OK,
  112. // System.Windows.Forms.MessageBoxIcon.Information,
  113. // System.Windows.Forms.MessageBoxDefaultButton.Button1);
  114. }
  115. }
  116. public class WTLImage
  117. {
  118. public readonly short Width, Height, X, Y, ShadowX, ShadowY;
  119. public readonly int Length;
  120. public int DataOffset { get; }
  121. public readonly byte Shadow;
  122. public int Index { get; }
  123. public bool IsNewVersion { get; }
  124. public byte ImageTextureType { get; }
  125. public byte MaskTextureType { get; }
  126. private byte[] _fBytes;
  127. public Bitmap Image;
  128. public readonly Boolean HasMask;
  129. public short MaskWidth, MaskHeight, MaskX, MaskY;
  130. public int MaskLength;
  131. private byte[] _MaskfBytes;
  132. public Bitmap MaskImage;
  133. public WTLImage(int index, bool isNewVersion)
  134. {
  135. Index = index;
  136. IsNewVersion = isNewVersion;
  137. _fBytes = new byte[0];
  138. _MaskfBytes = new byte[0];
  139. }
  140. public WTLImage(int index, bool isNewVersion, BinaryReader bReader)
  141. {
  142. Index = index;
  143. IsNewVersion = isNewVersion;
  144. Width = bReader.ReadInt16();
  145. Height = bReader.ReadInt16();
  146. X = bReader.ReadInt16();
  147. Y = bReader.ReadInt16();
  148. ShadowX = bReader.ReadInt16();
  149. ShadowY = bReader.ReadInt16();
  150. if (IsNewVersion)
  151. {
  152. var imageU1 = bReader.ReadByte();
  153. ImageTextureType = bReader.ReadByte();
  154. var maskU1 = bReader.ReadByte();
  155. MaskTextureType = bReader.ReadByte();
  156. HasMask = MaskTextureType > 0;
  157. Length = bReader.ReadInt32();
  158. if (Length % 4 > 0) Length += 4 - (Length % 4);
  159. DataOffset = (int)bReader.BaseStream.Position;
  160. }
  161. else
  162. {
  163. Length = bReader.ReadByte() | bReader.ReadByte() << 8 | bReader.ReadByte() << 16;
  164. Shadow = bReader.ReadByte();
  165. HasMask = ((Shadow >> 7) == 1) ? true : false;
  166. }
  167. }
  168. public unsafe void CreateTexture(BinaryReader bReader)
  169. {
  170. Image = ReadImage(bReader, Length, Width, Height, ImageTextureType);
  171. if (HasMask)
  172. {
  173. if (IsNewVersion)
  174. {
  175. MaskWidth = Width;
  176. MaskHeight = Height;
  177. MaskX = X;
  178. MaskY = Y;
  179. MaskLength = bReader.ReadInt32();
  180. }
  181. else
  182. {
  183. MaskWidth = bReader.ReadInt16();
  184. MaskHeight = bReader.ReadInt16();
  185. MaskX = bReader.ReadInt16();
  186. MaskY = bReader.ReadInt16();
  187. bReader.ReadInt16();//mask shadow x
  188. bReader.ReadInt16();//mask shadow y
  189. MaskLength = bReader.ReadByte() | bReader.ReadByte() << 8 | bReader.ReadByte() << 16;
  190. bReader.ReadByte(); //mask shadow
  191. }
  192. MaskImage = ReadImage(bReader, MaskLength, MaskWidth, MaskHeight, MaskTextureType);
  193. }
  194. }
  195. private unsafe Bitmap DecompressV1Texture(BinaryReader bReader, int imageLength, short outputWidth, short outputHeight)
  196. {
  197. const int size = 8;
  198. int offset = 0, blockOffSet = 0;
  199. List<byte> countList = new List<byte>();
  200. int tWidth = 2;
  201. while (tWidth < Width)
  202. tWidth *= 2;
  203. outputHeight = (short)(Height + (4 - Height % 4) % 4);
  204. outputWidth = (short)(Width + (4 - Width % 4) % 4);
  205. _fBytes = bReader.ReadBytes(imageLength);
  206. Bitmap output = new Bitmap(outputWidth, outputHeight);
  207. if (_fBytes.Length != imageLength) return null;
  208. BitmapData data = output.LockBits(new Rectangle(0, 0, outputWidth, outputHeight), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
  209. byte* pixels = (byte*)data.Scan0;
  210. int cap = outputWidth * outputHeight * 4;
  211. int currentx = 0;
  212. while (blockOffSet < imageLength)
  213. {
  214. countList.Clear();
  215. for (int i = 0; i < 8; i++)
  216. countList.Add(_fBytes[blockOffSet++]);
  217. for (int i = 0; i < countList.Count; i++)
  218. {
  219. int count = countList[i];
  220. if (i % 2 == 0)
  221. {
  222. if (currentx >= tWidth)
  223. currentx -= tWidth;
  224. for (int off = 0; off < count; off++)
  225. {
  226. if (currentx < outputWidth)
  227. offset++;
  228. currentx += 4;
  229. if (currentx >= tWidth)
  230. currentx -= tWidth;
  231. }
  232. continue;
  233. }
  234. for (int c = 0; c < count; c++)
  235. {
  236. if (blockOffSet >= _fBytes.Length)
  237. break;
  238. byte[] newPixels = new byte[64];
  239. byte[] block = new byte[size];
  240. Array.Copy(_fBytes, blockOffSet, block, 0, size);
  241. blockOffSet += size;
  242. DecompressBlock(newPixels, block);
  243. int pixelOffSet = 0;
  244. byte[] sourcePixel = new byte[4];
  245. for (int py = 0; py < 4; py++)
  246. {
  247. for (int px = 0; px < 4; px++)
  248. {
  249. int blockx = offset % (outputWidth / 4);
  250. int blocky = offset / (outputWidth / 4);
  251. int x = blockx * 4;
  252. int y = blocky * 4;
  253. int destPixel = ((y + py) * outputWidth) * 4 + (x + px) * 4;
  254. Array.Copy(newPixels, pixelOffSet, sourcePixel, 0, 4);
  255. pixelOffSet += 4;
  256. if (destPixel + 4 > cap)
  257. break;
  258. for (int pc = 0; pc < 4; pc++)
  259. pixels[destPixel + pc] = sourcePixel[pc];
  260. }
  261. }
  262. offset++;
  263. if (currentx >= outputWidth)
  264. currentx -= outputWidth;
  265. currentx += 4;
  266. }
  267. }
  268. }
  269. output.UnlockBits(data);
  270. return output;
  271. }
  272. private unsafe Bitmap DecompressV2Texture(BinaryReader bReader, int imageLength, short outputWidth, short outputHeight, byte textureType)
  273. {
  274. var buffer = bReader.ReadBytes(imageLength);
  275. int w = Width + (4 - Width % 4) % 4;
  276. int a = 1;
  277. while (true)
  278. {
  279. a *= 2;
  280. if (a >= w)
  281. {
  282. w = a;
  283. break;
  284. }
  285. }
  286. int h = Height + (4 - Height % 4) % 4;
  287. int e = w * h / 2;
  288. SquishFlags type;
  289. switch (textureType)
  290. {
  291. case 0:
  292. case 1:
  293. type = SquishFlags.Dxt1;
  294. break;
  295. case 3:
  296. type = SquishFlags.Dxt3;
  297. break;
  298. case 5:
  299. type = SquishFlags.Dxt5;
  300. break;
  301. default:
  302. throw new NotImplementedException();
  303. }
  304. var decompressedBuffer = Ionic.Zlib.DeflateStream.UncompressBuffer(buffer);
  305. var bitmap = new Bitmap(w, h);
  306. BitmapData data = bitmap.LockBits(
  307. new Rectangle(0, 0, w, h),
  308. ImageLockMode.WriteOnly,
  309. PixelFormat.Format32bppRgb
  310. );
  311. fixed (byte* source = decompressedBuffer)
  312. Squish.DecompressImage(data.Scan0, w, h, (IntPtr)source, type);
  313. byte* dest = (byte*)data.Scan0;
  314. for (int i = 0; i < w * h * 4; i += 4)
  315. {
  316. //Reverse Red/Blue
  317. byte b = dest[i];
  318. dest[i] = dest[i + 2];
  319. dest[i + 2] = b;
  320. }
  321. bitmap.UnlockBits(data);
  322. Rectangle cloneRect = new Rectangle(0, 0, outputWidth, outputHeight);
  323. PixelFormat format = bitmap.PixelFormat;
  324. Bitmap cloneBitmap = bitmap.Clone(cloneRect, format);
  325. return cloneBitmap;
  326. }
  327. public unsafe Bitmap ReadImage(BinaryReader bReader, int imageLength, short outputWidth, short outputHeight, byte textureType)
  328. {
  329. return IsNewVersion
  330. ? DecompressV2Texture(bReader, imageLength, outputWidth, outputHeight, textureType)
  331. : DecompressV1Texture(bReader, imageLength, outputWidth, outputHeight);
  332. }
  333. private static void DecompressBlock(IList<byte> newPixels, byte[] block)
  334. {
  335. byte[] colours = new byte[8];
  336. Array.Copy(block, 0, colours, 0, 8);
  337. byte[] codes = new byte[16];
  338. int a = Unpack(block, 0, codes, 0);
  339. int b = Unpack(block, 2, codes, 4);
  340. for (int i = 0; i < 3; i++)
  341. {
  342. int c = codes[i];
  343. int d = codes[4 + i];
  344. if (a <= b)
  345. {
  346. codes[8 + i] = (byte)((c + d) / 2);
  347. codes[12 + i] = 0;
  348. }
  349. else
  350. {
  351. codes[8 + i] = (byte)((2 * c + d) / 3);
  352. codes[12 + i] = (byte)((c + 2 * d) / 3);
  353. }
  354. }
  355. codes[8 + 3] = 255;
  356. codes[12 + 3] = (a <= b) ? (byte)0 : (byte)255;
  357. for (int i = 0; i < 4; i++)
  358. {
  359. if ((codes[i * 4] == 0) && (codes[(i * 4) + 1] == 0) && (codes[(i * 4) + 2] == 0) && (codes[(i * 4) + 3] == 255))
  360. { //dont ever use pure black cause that gives transparency issues
  361. codes[i * 4] = 1;
  362. codes[(i * 4) + 1] = 1;
  363. codes[(i * 4) + 2] = 1;
  364. }
  365. }
  366. byte[] indices = new byte[16];
  367. for (int i = 0; i < 4; i++)
  368. {
  369. byte packed = block[4 + i];
  370. indices[0 + i * 4] = (byte)(packed & 0x3);
  371. indices[1 + i * 4] = (byte)((packed >> 2) & 0x3);
  372. indices[2 + i * 4] = (byte)((packed >> 4) & 0x3);
  373. indices[3 + i * 4] = (byte)((packed >> 6) & 0x3);
  374. }
  375. for (int i = 0; i < 16; i++)
  376. {
  377. byte offset = (byte)(4 * indices[i]);
  378. for (int j = 0; j < 4; j++)
  379. newPixels[4 * i + j] = codes[offset + j];
  380. }
  381. }
  382. private static int Unpack(IList<byte> packed, int srcOffset, IList<byte> colour, int dstOffSet)
  383. {
  384. int value = packed[0 + srcOffset] | (packed[1 + srcOffset] << 8);
  385. // get components in the stored range
  386. byte red = (byte)((value >> 11) & 0x1F);
  387. byte green = (byte)((value >> 5) & 0x3F);
  388. byte blue = (byte)(value & 0x1F);
  389. // Scale up to 24 Bit
  390. colour[2 + dstOffSet] = (byte)((red << 3) | (red >> 2));
  391. colour[1 + dstOffSet] = (byte)((green << 2) | (green >> 4));
  392. colour[0 + dstOffSet] = (byte)((blue << 3) | (blue >> 2));
  393. colour[3 + dstOffSet] = 255;
  394. //*/
  395. return value;
  396. }
  397. }
  398. }