TEX.cs 11 KB

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