TEX.cs 10 KB

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