TEX.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.IO;
  6. namespace OpenVIII
  7. {
  8. /// <summary>
  9. /// TEX file handler class. TEX files are packages of textures and Palettes.
  10. /// </summary>
  11. /// <remarks>
  12. /// I borrowed this from my Rinoa's toolset, but modified to aim for buffer rather than file-work
  13. /// </remarks>
  14. /// <see cref="https://github.com/MaKiPL/FF8-Rinoa-s-Toolset/blob/master/SerahToolkit_SharpGL/FF8_Core/TEX.cs"/>
  15. /// <seealso cref="https://github.com/myst6re/vincent-tim/blob/master/TexFile.cpp"/>
  16. public sealed class TEX : Texture_Base
  17. {
  18. #region Fields
  19. /// <summary>
  20. /// Raw data of TEX file
  21. /// </summary>
  22. private byte[] _buffer;
  23. private Texture _texture;
  24. #endregion Fields
  25. #region Constructors
  26. public TEX(byte[] buffer) => Load(buffer);
  27. public TEX()
  28. {
  29. }
  30. #endregion Constructors
  31. ///// <summary>
  32. ///// Contains header info and Palette data of TEX file.
  33. ///// </summary>
  34. //public Texture TextureData => texture; //added to get texture data outside of class.
  35. #region Properties
  36. public bool CLP => _texture.PaletteFlag != 0;
  37. public override byte GetBytesPerPixel => _texture.BytesPerPixel;
  38. public override int GetClutCount => _texture.NumOfCluts;
  39. public override int GetClutSize => (int)_texture.PaletteSize;
  40. public override int GetColorsCountPerPalette => _texture.NumOfColors;
  41. public override int GetHeight => _texture.Height;
  42. public override int GetOrigX => 0;
  43. public override int GetOrigY => 0;
  44. public override int GetWidth => _texture.Width;
  45. /// <summary>
  46. /// size of header section
  47. /// </summary>
  48. private int HeaderSize => _texture.Version <= 1 ? 0xEC : 0xF0;
  49. /// <summary>
  50. /// size of palette section
  51. /// </summary>
  52. private int PaletteSectionSize => (int)(_texture.PaletteSize * 4);
  53. /// <summary>
  54. /// start of texture section
  55. /// </summary>
  56. private int TextureLocator => HeaderSize + PaletteSectionSize;
  57. #endregion Properties
  58. #region Methods
  59. public override void ForceSetClutColors(ushort newNumOfColors) => _texture.NumOfColors = newNumOfColors;
  60. public override void ForceSetClutCount(ushort newClut) => _texture.NumOfCluts = (byte)newClut;
  61. public override Color[] GetClutColors(ushort clut)
  62. {
  63. if (!CLP || _texture.NumOfCluts == 0)
  64. return null;
  65. if (clut >= _texture.NumOfCluts)
  66. throw new Exception($"Desired palette is incorrect use -1 for default or use a smaller number: {clut} > {_texture.NumOfCluts}");
  67. var colors = new Color[_texture.NumOfColors];
  68. var k = 0;
  69. for (var i = clut * _texture.NumOfColors * 4; i < _texture.PaletteData.Length && k < colors.Length; i += 4)
  70. {
  71. colors[k].B = _texture.PaletteData[i];
  72. colors[k].G = _texture.PaletteData[i + 1];
  73. colors[k].R = _texture.PaletteData[i + 2];
  74. colors[k].A = _texture.PaletteData[i + 3];
  75. k++;
  76. }
  77. return colors;
  78. }
  79. /// <summary>
  80. /// Get Texture2D converted to 32bit color
  81. /// </summary>
  82. /// <param name="forcePalette">Desired Palette, see texture.NumOfPalettes. -1 is default.</param>
  83. /// <param name="colors">Override colors of palette; Array size must match texture.NumOfColorsPerPalette</param>
  84. /// <returns>32bit Texture2D</returns>
  85. /// <remarks>
  86. /// Some palettes are 256 but the game only uses 16 colors might need to make the restriction
  87. /// more lax and allow any size array and only throw errors if the color key is greater than
  88. /// size of array. Or we could treat any of those bad matches as transparent.
  89. /// </remarks>
  90. public override Texture2D GetTexture(Color[] colors)
  91. {
  92. if (Memory.Graphics.GraphicsDevice == null) return null;
  93. if (_texture.PaletteFlag != 0)
  94. {
  95. if (colors == null) throw new ArgumentNullException(nameof(colors));
  96. //if (colors != null && colors.Length != texture.NumOfColors)
  97. //{
  98. // //if (colors.Length > texture.NumOfColors) //truncate colors to the correct amount. in some
  99. // // colors = colors.Take(texture.NumOfColors).ToArray();
  100. // //else // might need to expand the array the other way if we get more mismatches.
  101. // //Array.Resize(ref colors,texture.NumOfColors);
  102. // //throw new Exception($" custom colors parameter set but array size to match palette size: {texture.NumOfColors}");
  103. //}
  104. MemoryStream ms;
  105. using (var br = new BinaryReader(ms = new MemoryStream(_buffer)))
  106. {
  107. ms.Seek(TextureLocator, SeekOrigin.Begin);
  108. var convertBuffer = new TextureBuffer(_texture.Width, _texture.Height);
  109. for (var i = 0; i < convertBuffer.Length && ms.Position < ms.Length; i++)
  110. {
  111. var colorKey = br.ReadByte();
  112. if (colorKey > colors.Length) continue;
  113. convertBuffer[i] = colors[colorKey];
  114. }
  115. return convertBuffer.GetTexture();
  116. }
  117. }
  118. if (_texture.BytesPerPixel == 2)
  119. {
  120. MemoryStream ms;
  121. using (var br = new BinaryReader(ms = new MemoryStream(_buffer)))
  122. {
  123. ms.Seek(TextureLocator, SeekOrigin.Begin);
  124. var convertBuffer = new TextureBuffer(_texture.Width, _texture.Height);
  125. for (var i = 0; ms.Position + 2 < ms.Length; i++)
  126. {
  127. convertBuffer[i] = ABGR1555toRGBA32bit(br.ReadUInt16());
  128. }
  129. return convertBuffer.GetTexture();
  130. }
  131. }
  132. if (_texture.BytesPerPixel != 3) return null;
  133. {
  134. // not tested but vincent tim had support for it so i guess it's possible RGB or BGR
  135. MemoryStream ms;
  136. using (var br = new BinaryReader(ms = new MemoryStream(_buffer)))
  137. {
  138. ms.Seek(TextureLocator, SeekOrigin.Begin);
  139. var convertBuffer = new TextureBuffer(_texture.Width, _texture.Height);
  140. var color = new Color { A = 0xFF };
  141. for (var i = 0; ms.Position + 3 < ms.Length; i++)
  142. {
  143. //RGB or BGR so might need to reorder things to RGB
  144. color.B = br.ReadByte();
  145. color.G = br.ReadByte();
  146. color.R = br.ReadByte();
  147. convertBuffer[i] = color;
  148. }
  149. return convertBuffer.GetTexture();
  150. }
  151. }
  152. }
  153. public override Texture2D GetTexture(ushort clut) => GetTexture(GetClutColors(clut));
  154. public override void Load(byte[] buffer, uint offset = 0)
  155. {
  156. _texture = new Texture();
  157. this._buffer = buffer;
  158. ReadParameters();
  159. }
  160. /// <summary>
  161. /// Writes the Tim file to the hard drive.
  162. /// </summary>
  163. /// <param name="path">Path where you want file to be saved.</param>
  164. public override void Save(string path)
  165. {
  166. using (var bw = new BinaryWriter(File.Create(path)))
  167. {
  168. bw.Write(_buffer);
  169. }
  170. }
  171. public override void SaveCLUT(string path)
  172. {
  173. using (var clut = new Texture2D(Memory.Graphics.GraphicsDevice, _texture.NumOfColors, _texture.NumOfCluts))
  174. {
  175. for (ushort i = 0; i < _texture.NumOfCluts; i++)
  176. {
  177. clut.SetData(0, new Rectangle(0, i, _texture.NumOfColors, 1), GetClutColors(i), 0, _texture.NumOfColors);
  178. }
  179. Extended.Save_As_PNG(clut, path, _texture.NumOfColors, _texture.NumOfCluts);
  180. }
  181. }
  182. /// <summary>
  183. /// Read header data from TEX file.
  184. /// </summary>
  185. /// <see cref="https://github.com/MaKiPL/FF8-Rinoa-s-Toolset/blob/master/SerahToolkit_SharpGL/FF8_Core/TEX.cs"/>
  186. /// <seealso cref="https://github.com/myst6re/vincent-tim/blob/master/TexFile.h"/>
  187. private void ReadParameters()
  188. {
  189. _texture.Version = BitConverter.ToUInt32(_buffer, 0x00);
  190. _texture.Width = (int)BitConverter.ToUInt32(_buffer, 0x3C); //nothing will be uint size big.
  191. _texture.Height = (int)BitConverter.ToUInt32(_buffer, 0x40);
  192. _texture.BytesPerPixel = _buffer[0x68];
  193. _texture.NumOfCluts = _buffer[0x30];
  194. _texture.NumOfColors = BitConverter.ToInt32(_buffer, 0x34);
  195. _texture.BitDepth = BitConverter.ToUInt32(_buffer, 0x38);
  196. _texture.PaletteFlag = _buffer[0x4C];
  197. _texture.PaletteSize = BitConverter.ToUInt32(_buffer, 0x58);
  198. if (_texture.PaletteFlag == 0) return;
  199. _texture.PaletteData = new byte[PaletteSectionSize];
  200. Buffer.BlockCopy(_buffer, 0xF0, _texture.PaletteData, 0, PaletteSectionSize);
  201. }
  202. #endregion Methods
  203. #region Structs
  204. /// <summary>
  205. /// Contains Header info and Palette data of TEX file.
  206. /// </summary>
  207. /// <see cref="https://github.com/MaKiPL/FF8-Rinoa-s-Toolset/blob/master/SerahToolkit_SharpGL/FF8_Core/TEX.cs"/>
  208. /// <seealso cref="https://github.com/myst6re/vincent-tim/blob/master/TexFile.h"/>
  209. private struct Texture
  210. {
  211. #region Fields
  212. /// <summary>
  213. /// 0x38
  214. /// </summary>
  215. [SuppressMessage("ReSharper", "NotAccessedField.Local")] public uint BitDepth;
  216. /// <summary>
  217. /// 0x68
  218. /// </summary>
  219. public byte BytesPerPixel;
  220. /// <summary>
  221. /// 0x40
  222. /// </summary>
  223. public int Height;
  224. /// <summary>
  225. /// 0x30
  226. /// </summary>
  227. public byte NumOfCluts;
  228. /// <summary>
  229. /// 0x34
  230. /// </summary>
  231. public int NumOfColors;
  232. /// <summary>
  233. /// 0xF0 for ff8;0xEC for ff7; size = PaletteSize * 4;
  234. /// </summary>
  235. public byte[] PaletteData;
  236. /// <summary>
  237. /// 0x4C
  238. /// </summary>
  239. public byte PaletteFlag;
  240. /// <summary>
  241. /// 0x58
  242. /// </summary>
  243. public uint PaletteSize;
  244. /// <summary>
  245. /// 0x00; 1=FF7 | 2=FF8
  246. /// </summary>
  247. public uint Version;
  248. /// <summary>
  249. /// 0x3C
  250. /// </summary>
  251. public int Width;
  252. #endregion Fields
  253. }
  254. #endregion Structs
  255. //public struct Color
  256. //{
  257. // #region Fields
  258. // public byte Alpha; public byte Blue; public byte Green; public byte Red;
  259. // #endregion Fields
  260. //}
  261. }
  262. }