TextureHandler.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.IO;
  5. using System.Linq;
  6. namespace FF8
  7. {
  8. /// <summary>
  9. /// This contains functions to Load Highres mod versions of textures and get scale vector.
  10. /// </summary>
  11. internal class TextureHandler
  12. {
  13. #region Fields
  14. private TEX _classic;
  15. #endregion Fields
  16. #region Constructors
  17. public bool Modded { get; private set; } = false;
  18. public TextureHandler(string filename, uint cols = 1, uint rows = 1, int pallet = -1)
  19. {
  20. if (cols == 1 && rows == 1)
  21. {
  22. ArchiveWorker aw = new ArchiveWorker(Memory.Archives.A_MENU);
  23. filename = aw.GetListOfFiles().First(x => x.IndexOf(filename, StringComparison.OrdinalIgnoreCase) >= 0);
  24. TEX tex = new TEX(ArchiveWorker.GetBinaryFile(Memory.Archives.A_MENU, filename));
  25. Init(filename, tex, cols, rows);
  26. }
  27. else
  28. Init(filename, null, cols, rows);
  29. }
  30. public TextureHandler(string filename, TEX classic, uint cols = 1, uint rows = 1, int pallet = -1) => Init(filename, classic, cols, rows, pallet);
  31. public void Init(string filename, TEX classic, uint cols = 1, uint rows = 1, int pallet = -1)
  32. {
  33. Classic = classic;
  34. Size = Vector2.Zero;
  35. Count = cols * rows;
  36. Textures = new Texture2D[cols, rows];
  37. StartOffset = 0;
  38. Rows = rows;
  39. Cols = cols;
  40. Filename = filename;
  41. Pallet = pallet;
  42. //load textures;
  43. Init();
  44. //unload Classic
  45. Classic = null;
  46. }
  47. #endregion Constructors
  48. #region Properties
  49. /// <summary>
  50. /// Original sub 256x265 texture, required for fallback when issues happen.
  51. /// </summary>
  52. public TEX Classic { get => _classic; private set { _classic = value; if (value != null) ClassicSize = new Vector2(value.TextureData.Width, value.TextureData.Height); } }
  53. /// <summary>
  54. /// X = width and Y = height. The Size of original texture. Will be used in scaling
  55. /// </summary>
  56. public Vector2 ClassicSize { get; private set; }
  57. public uint Count { get; protected set; }
  58. /// <summary>
  59. /// Scale vector from original to big
  60. /// </summary>
  61. public Vector2 ScaleFactor => Size == Vector2.Zero || ClassicSize == Vector2.Zero ? Vector2.One : Size / ClassicSize;
  62. /// <summary>
  63. /// X = width and Y = height. The Size of big version texture. Will be used in scaling
  64. /// </summary>
  65. public Vector2 Size { get; private set; }
  66. protected uint Cols { get; set; }
  67. protected string Filename { get; set; }
  68. public int Pallet { get; protected set; }
  69. protected uint Rows { get; set; }
  70. /// <summary>
  71. /// Scale vector big to modded or orignal to modded.
  72. /// </summary>
  73. //protected Vector2[,] Scales { get; private set; }
  74. protected uint StartOffset { get; set; }
  75. protected Texture2D[,] Textures { get; private set; }
  76. #endregion Properties
  77. #region Indexers
  78. public Texture2D this[int c, int r] => Textures[c, r];
  79. #endregion Indexers
  80. #region Methods
  81. public static explicit operator Texture2D(TextureHandler t)
  82. {
  83. if (t.Count == 1)
  84. return t[0, 0];
  85. throw new Exception("TextureHandler can only be cast to Texture2D if there is only one texture in the array use [cols,rows] instead");
  86. //return null;
  87. }
  88. public static implicit operator Rectangle(TextureHandler v) => new Rectangle(new Point(0), v.Size.ToPoint());
  89. public static Vector2 GetScale(Vector2 _old, Vector2 _new) => _new / _old;
  90. public static Vector2 GetScale(Vector2 _old, Texture2D _new) => new Vector2(_new.Width / _old.X, _new.Height / _old.Y);
  91. public static Vector2 GetScale(TEX _old, Texture2D _new) => new Vector2((float)_new.Width / _old.TextureData.Width, (float)_new.Height / _old.TextureData.Height);
  92. public static Vector2 GetScale(Texture2D _old, Texture2D _new) => new Vector2((float)_new.Width / _old.Width, (float)_new.Height / _old.Height);
  93. /// <summary>
  94. /// Load Texture from a mod
  95. /// </summary>
  96. /// <param name="path"></param>
  97. /// <returns></returns>
  98. public static Texture2D LoadPNG(string path, int pallet = -1)
  99. {
  100. string bn = Path.GetFileNameWithoutExtension(path);
  101. string prefix = bn.Substring(0, 2);
  102. string pngpath = Path.Combine(Memory.FF8DIR, "textures", prefix, bn);
  103. string suffix = pallet > -1 ? $"{pallet + 13}" : "";
  104. suffix += ".png";
  105. if (Directory.Exists(pngpath))
  106. {
  107. try
  108. {
  109. pngpath = Directory.GetFiles(pngpath).Last(x => (x.IndexOf(bn, StringComparison.OrdinalIgnoreCase) >= 0 && x.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)));
  110. using (FileStream fs = File.OpenRead(pngpath))
  111. {
  112. return Texture2D.FromStream(Memory.graphics.GraphicsDevice, fs);
  113. }
  114. }
  115. catch (InvalidOperationException)
  116. {
  117. // couldn't find a match.
  118. }
  119. }
  120. return null;
  121. }
  122. public static Rectangle Scale(Rectangle mat1, Vector2 mat2)
  123. {
  124. mat1.Location = (mat1.Location.ToVector2() * mat2).ToPoint();
  125. mat1.Size = (mat1.Size.ToVector2() * mat2).ToPoint();
  126. return mat1;
  127. }
  128. public static Vector2 ToVector2(Texture2D t) => new Vector2(t.Width, t.Height);
  129. public static Vector2 ToVector2(TextureHandler t) => new Vector2(t.ClassicSize.X, t.ClassicSize.Y);
  130. public static Rectangle ToRectangle(Texture2D t) => new Rectangle(0, 0, t.Width, t.Height);
  131. public static Rectangle ToRectangle(TextureHandler t) => new Rectangle(0, 0, (int)t.ClassicSize.X, (int)t.ClassicSize.Y);
  132. public static Rectangle ToRectangle(Vector2 loc, Vector2 size) => new Rectangle(loc.ToPoint(), size.ToPoint());
  133. public static Texture2D UseBest(Texture2D _old, Texture2D _new) => UseBest(_old, _new, out Vector2 scale);
  134. public static Texture2D UseBest(Texture2D _old, Texture2D _new, out Vector2 scale)
  135. {
  136. if (_new == null)
  137. {
  138. scale = Vector2.One;
  139. return _old;
  140. }
  141. else
  142. {
  143. scale = GetScale(_old, _new);
  144. _old.Dispose();
  145. return _new;
  146. }
  147. }
  148. public static Texture2D UseBest(TEX _old, Texture2D _new, int pallet = 0) => UseBest(_old, _new, out Vector2 scale, pallet);
  149. public static Texture2D UseBest(TEX _old, Texture2D _new, out Vector2 scale, int pallet = 0)
  150. {
  151. Texture2D tex;
  152. if (_new == null)
  153. {
  154. scale = Vector2.One;
  155. if (_old.TextureData.NumOfPalettes <= 1)
  156. return _old.GetTexture();
  157. tex = _old.GetTexture(pallet);
  158. return tex;
  159. }
  160. else
  161. {
  162. scale = GetScale(_old, _new);
  163. return _new;
  164. }
  165. }
  166. public void Draw(Rectangle dst, Rectangle? src, Color color)
  167. {
  168. Vector2 dstOffset = Vector2.Zero;
  169. if (src != null)
  170. {
  171. bool drawn = false;
  172. Vector2 offset = Vector2.Zero;
  173. Rectangle dst2 = new Rectangle();
  174. Rectangle cnt = new Rectangle();
  175. for (uint r = 0; r < Rows; r++)
  176. {
  177. offset.X = 0;
  178. //dstOffset.X = 0;
  179. for (uint c = 0; c < Cols; c++)
  180. {
  181. drawn = false;
  182. dst2 = new Rectangle();
  183. //if all the pieces of Scales are correct they should all have the same scale.
  184. Rectangle _src = Scale(src.Value, ScaleFactor);
  185. cnt = ToRectangle(Textures[c, r]);
  186. cnt.Offset(offset);
  187. if (cnt.Contains(_src))
  188. {
  189. //got lucky the whole thing is in this rectangle
  190. _src.Location = (GetOffset(cnt, _src)).ToPoint();
  191. Memory.spriteBatch.Draw(Textures[c, r], dst,_src, color);
  192. return;
  193. }
  194. else if (cnt.Intersects(_src))
  195. {
  196. //Gotta draw more than once.
  197. Rectangle src2 = Rectangle.Intersect(cnt, _src);
  198. src2.Location = (GetOffset(cnt, src2)).ToPoint();
  199. dst2 = Scale(dst, GetScale(_src.Size, src2.Size));
  200. dst2.Location = (dst.Location);
  201. dst2.Offset(dstOffset);
  202. Memory.spriteBatch.Draw(Textures[c, r], dst2, src2, color);
  203. drawn = true;
  204. dstOffset.X += dst2.Width;
  205. }
  206. offset.X += cnt.Width;
  207. }
  208. offset.Y += cnt.Height;
  209. if (drawn)
  210. dstOffset.Y += dst2.Height;
  211. }
  212. }
  213. //drawing texture directly
  214. else
  215. {
  216. Vector2 dstV = Vector2.Zero;
  217. dstOffset.X += dst.X;
  218. dstOffset.Y += dst.X;
  219. for (uint r = 0; r < Rows; r++)
  220. {
  221. for (uint c = 0; c < Cols; c++)
  222. {
  223. Vector2 scale = GetScale(Size, dst.Size.ToVector2());
  224. dstV = ToVector2(Textures[c, r]) * scale;
  225. Memory.spriteBatch.Draw(Textures[c, r], dstOffset, null, color, 0f, Vector2.Zero, scale, SpriteEffects.None, 0f);
  226. dstOffset.X += dstV.X;
  227. }
  228. dstOffset.Y += dstV.Y;
  229. }
  230. }
  231. }
  232. public Vector2 GetScale(int cols = 0, int rows = 0) => ScaleFactor;
  233. protected void Init()
  234. {
  235. Vector2 size = Vector2.Zero;
  236. Vector2 oldsize = Vector2.Zero;
  237. TEX tex = null;
  238. uint c2 = 0;
  239. uint r2 = 0;
  240. for (uint r = 0; r < Rows; r++)
  241. {
  242. for (uint c = 0; c < Cols; c++)
  243. {
  244. ArchiveWorker aw = new ArchiveWorker(Memory.Archives.A_MENU);
  245. string path = aw.GetListOfFiles().First(x => (x.IndexOf(string.Format(Filename, c + r * Cols + StartOffset), StringComparison.OrdinalIgnoreCase) >= 0));
  246. tex = new TEX(ArchiveWorker.GetBinaryFile(Memory.Archives.A_MENU, path));
  247. if (Classic == null && c2 < Cols) oldsize.X += tex.TextureData.Width;
  248. Texture2D pngTex = LoadPNG(path, Pallet);
  249. Textures[c, r] = (UseBest(tex, pngTex, Pallet));
  250. if (pngTex != null) Modded = true;
  251. if (c2 < Cols) size.X += Textures[c2++, r2].Width;
  252. }
  253. if (Classic == null && r2 < Rows) oldsize.Y += tex.TextureData.Height;
  254. if (r2 < Rows) size.Y += Textures[c2 - 1, r2++].Height;
  255. }
  256. Size = size;
  257. if (Classic == null) ClassicSize = oldsize;
  258. }
  259. public static Vector2 GetOffset(Rectangle old, Rectangle @new) => GetOffset(old.Location.ToVector2(), @new.Location.ToVector2());
  260. public static Vector2 GetOffset(Point oldLoc, Point newLoc) => GetOffset(oldLoc.ToVector2(), newLoc.ToVector2());
  261. public static Vector2 Abs(Vector2 v) => new Vector2(Math.Abs(v.X), Math.Abs(v.Y));
  262. public static Vector2 GetOffset(Vector2 oldLoc, Vector2 newLoc) => Abs(oldLoc-newLoc);
  263. public static Vector2 GetScale(Point oldSize, Point newSize) => GetScale(oldSize.ToVector2(), newSize.ToVector2());
  264. #endregion Methods
  265. }
  266. }