TEX.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.IO;
  5. namespace FF8
  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
  16. {
  17. #region Fields
  18. /// <summary>
  19. /// Ratio needed to convert 16 bit to 32 bit color
  20. /// </summary>
  21. /// <seealso cref="https://github.com/myst6re/vincent-tim/blob/master/PsColor.h"/>
  22. public const double COEFF_COLOR = (double)255 / 31;
  23. /// <summary>
  24. /// Raw data of TEX file
  25. /// </summary>
  26. private byte[] buffer;
  27. private Texture texture;
  28. #endregion Fields
  29. #region Constructors
  30. public TEX(byte[] buffer)
  31. {
  32. texture = new Texture();
  33. this.buffer = buffer;
  34. ReadParameters();
  35. }
  36. #endregion Constructors
  37. #region Properties
  38. /// <summary>
  39. /// Contains header info and Palette data of TEX file.
  40. /// </summary>
  41. public Texture TextureData => texture; //added to get texturedata outside of class.
  42. /// <summary>
  43. /// start of texture section
  44. /// </summary>
  45. private int TextureLocator => Headersize + PaletteSectionSize;
  46. /// <summary>
  47. /// size of palette section
  48. /// </summary>
  49. private int PaletteSectionSize => (int)(texture.PaletteSize * 4);
  50. /// <summary>
  51. /// size of header section
  52. /// </summary>
  53. private int Headersize => texture.Version <= 1 ? 0xEC : 0xF0;
  54. #endregion Properties
  55. #region Methods
  56. /// <summary>
  57. /// Read header data from TEX file.
  58. /// </summary>
  59. /// <see cref="https://github.com/MaKiPL/FF8-Rinoa-s-Toolset/blob/master/SerahToolkit_SharpGL/FF8_Core/TEX.cs"/>
  60. /// <seealso cref="https://github.com/myst6re/vincent-tim/blob/master/TexFile.h"/>
  61. private void ReadParameters()
  62. {
  63. texture.Version = BitConverter.ToUInt32(buffer, 0x00);
  64. texture.Width = BitConverter.ToUInt32(buffer, 0x3C);
  65. texture.Height = BitConverter.ToUInt32(buffer, 0x40);
  66. texture.bytesPerPixel = buffer[0x68];
  67. texture.NumOfPalettes = buffer[0x30];
  68. texture.NumOfColorsPerPalette = BitConverter.ToUInt32(buffer, 0x34);
  69. texture.bitDepth = BitConverter.ToUInt32(buffer, 0x38);
  70. texture.PaletteFlag = buffer[0x4C];
  71. texture.PaletteSize = BitConverter.ToUInt32(buffer, 0x58);
  72. if (texture.PaletteFlag != 0)
  73. {
  74. texture.paletteData = new byte[PaletteSectionSize];
  75. Buffer.BlockCopy(buffer, 0xF0, texture.paletteData, 0, PaletteSectionSize);
  76. }
  77. }
  78. public Color[] GetPallets(int forcePalette)
  79. {
  80. if (forcePalette < 0) forcePalette = 0;
  81. Color[] colors = new Color[texture.NumOfColorsPerPalette];
  82. int k = 0;
  83. for (uint i = (uint)(forcePalette * texture.NumOfColorsPerPalette * 4); i < texture.paletteData.Length && k < colors.Length; i += 4)
  84. {
  85. colors[k].B = texture.paletteData[i];
  86. colors[k].G = texture.paletteData[i + 1];
  87. colors[k].R = texture.paletteData[i + 2];
  88. colors[k].A = texture.paletteData[i + 3];
  89. k++;
  90. }
  91. return colors;
  92. }
  93. /// <summary>
  94. /// Get Texture2D converted to 32bit color
  95. /// </summary>
  96. /// <param name="forcePalette">Desired Palette, see texture.NumOfPalettes. -1 is default.</param>
  97. /// <param name="colors">Overide colors of pallet; Array size must match texture.NumOfColorsPerPalette</param>
  98. /// <returns>32bit Texture2D</returns>
  99. public Texture2D GetTexture(int forcePalette = -1, Color[] colors = null)
  100. {
  101. uint ImageSizeBytes = texture.Width * texture.Height * 4;
  102. if (texture.PaletteFlag != 0)
  103. {
  104. if (colors != null && colors.Length != texture.NumOfColorsPerPalette)
  105. throw new Exception($" custom colors parameter set but array size to match pallet size: {texture.NumOfColorsPerPalette}");
  106. else if (colors == null)
  107. {
  108. if (forcePalette >= texture.NumOfPalettes) //prevents exception for forcing a palette that doesn't exist.
  109. throw new Exception($"Desired pallet is incorrect use -1 for default or use a smaller number: {forcePalette} > {texture.NumOfPalettes}");
  110. colors = GetPallets(forcePalette);
  111. }
  112. Texture2D bmp = new Texture2D(Memory.graphics.GraphicsDevice, (int)texture.Width, (int)texture.Height, false, SurfaceFormat.Color);
  113. int localTextureLocator = TextureLocator;
  114. Color[] convertBuffer = new Color[ImageSizeBytes / 4];
  115. for (int i = 0; i < convertBuffer.Length; i++)
  116. {
  117. byte colorkey = buffer[localTextureLocator++];
  118. convertBuffer[i] = colors[colorkey];
  119. }
  120. bmp.SetData(convertBuffer);
  121. return bmp;
  122. }
  123. else
  124. {
  125. if (texture.bytesPerPixel == 2)
  126. {
  127. //working code
  128. Texture2D bmp = new Texture2D(Memory.graphics.GraphicsDevice, (int)texture.Width, (int)texture.Height, false, SurfaceFormat.Color);
  129. using (MemoryStream os = new MemoryStream((int)(ImageSizeBytes)))
  130. using (BinaryWriter bw = new BinaryWriter(os))
  131. {
  132. using (MemoryStream ms = new MemoryStream(buffer))
  133. using (BinaryReader br = new BinaryReader(ms))
  134. {
  135. ms.Seek(TextureLocator, SeekOrigin.Begin);
  136. while (ms.Position < ms.Length)
  137. bw.Write(FromPsColor(br.ReadUInt16()), 0, 4);
  138. }
  139. bmp.SetData(os.GetBuffer(), 0, (int)os.Length);
  140. }
  141. return bmp;
  142. }
  143. if (texture.bytesPerPixel == 3)
  144. {
  145. // not tested but vincent tim had support for it so i guess it's possible RGB or
  146. // BGR is a thing.
  147. Texture2D bmp = new Texture2D(Memory.graphics.GraphicsDevice, (int)texture.Width, (int)texture.Height, false, SurfaceFormat.Color);
  148. using (MemoryStream os = new MemoryStream((int)(ImageSizeBytes)))
  149. using (BinaryWriter bw = new BinaryWriter(os))
  150. {
  151. using (MemoryStream ms = new MemoryStream(buffer))
  152. using (BinaryReader br = new BinaryReader(ms))
  153. {
  154. ms.Seek(TextureLocator, SeekOrigin.Begin);
  155. while (ms.Position < ms.Length)
  156. {
  157. //RGB or BGR so might need to reorder things to RGB
  158. bw.Write(br.ReadBytes(3), 0, 3);
  159. //ALPHA
  160. bw.Write((byte)0xFF);
  161. }
  162. }
  163. bmp.SetData(os.GetBuffer(), 0, (int)os.Length);
  164. }
  165. return bmp;
  166. }
  167. return null;
  168. }
  169. }
  170. /// <summary>
  171. /// converts 16 bit color to 32bit with alpha. alpha needs to be set true manually per pixel
  172. /// unless you know the color value.
  173. /// </summary>
  174. /// <param name="color">16 bit color</param>
  175. /// <param name="useAlpha">area is visable or not</param>
  176. /// <returns>byte[4] red green blue alpha, i think</returns>
  177. /// <see cref="https://github.com/myst6re/vincent-tim/blob/master/PsColor.cpp"/>
  178. public static byte[] FromPsColor(ushort color, bool useAlpha = false) => new byte[] { (byte)Math.Round((color & 31) * COEFF_COLOR), (byte)Math.Round(((color >> 5) & 31) * COEFF_COLOR), (byte)Math.Round(((color >> 10) & 31) * COEFF_COLOR), (byte)(color == 0 && useAlpha ? 0 : 255) };
  179. #endregion Methods
  180. #region Structs
  181. /// <summary>
  182. /// Contains Header info and Palette data of TEX file.
  183. /// </summary>
  184. /// <see cref="https://github.com/MaKiPL/FF8-Rinoa-s-Toolset/blob/master/SerahToolkit_SharpGL/FF8_Core/TEX.cs"/>
  185. /// <seealso cref="https://github.com/myst6re/vincent-tim/blob/master/TexFile.h"/>
  186. public struct Texture
  187. {
  188. #region Fields
  189. /// <summary>
  190. /// 0x68
  191. /// </summary>
  192. public byte bytesPerPixel;
  193. /// <summary>
  194. /// 0x40
  195. /// </summary>
  196. public uint Height;
  197. /// <summary>
  198. /// 0x30
  199. /// </summary>
  200. public byte NumOfPalettes;
  201. /// <summary>
  202. /// 0xF0 for ff8;0xEC for ff7; size = PaletteSize * 4;
  203. /// </summary>
  204. public byte[] paletteData;
  205. /// <summary>
  206. /// 0x4C
  207. /// </summary>
  208. public byte PaletteFlag;
  209. /// <summary>
  210. /// 0x58
  211. /// </summary>
  212. public uint PaletteSize;
  213. /// <summary>
  214. /// 0x3C
  215. /// </summary>
  216. public uint Width;
  217. /// <summary>
  218. /// 0x38
  219. /// </summary>
  220. public uint bitDepth;
  221. /// <summary>
  222. /// 0x34
  223. /// </summary>
  224. public uint NumOfColorsPerPalette;
  225. /// <summary>
  226. /// 0x00; 1=FF7 | 2=FF8
  227. /// </summary>
  228. public uint Version;
  229. #endregion Fields
  230. }
  231. //public struct Color
  232. //{
  233. // #region Fields
  234. // public byte Alpha; public byte Blue; public byte Green; public byte Red;
  235. // #endregion Fields
  236. //}
  237. #endregion Structs
  238. }
  239. }