TextureHandler.cs 13 KB

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