using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.IO;
using System.Linq;
namespace FF8
{
///
/// This contains functions to Load Highres mod versions of textures and get scale vector.
///
public class TextureHandler
{
#region Fields
private TEX _classic;
#endregion Fields
#region Constructors
public TextureHandler(string filename, uint cols) : this(filename, cols, 1)
{
}
public TextureHandler(string filename, uint cols, uint rows)
{
if (cols == 1 && rows == 1)
{
ArchiveWorker aw = new ArchiveWorker(Memory.Archives.A_MENU);
filename = aw.GetListOfFiles().First(x => x.IndexOf(filename, StringComparison.OrdinalIgnoreCase) >= 0);
TEX tex = new TEX(aw.GetBinaryFile(filename));
Init(filename, tex, cols, rows);
}
else
Init(filename, null, cols, rows);
}
public TextureHandler(string filename, TEX classic, int pallet = -1, Color[] colors = null) => Init(filename, classic, 1, 1, pallet: pallet, colors: colors);
public TextureHandler(string filename, TEX classic, uint cols, uint rows, int pallet = -1, Color[] colors = null) => Init(filename, classic, cols, rows, pallet, colors);
#endregion Constructors
#region Properties
///
/// Original sub 256x265 texture, required for fallback when issues happen.
///
public TEX Classic { get => _classic; private set { _classic = value; if (value != null) ClassicSize = new Vector2(value.TextureData.Width, value.TextureData.Height); } }
///
/// X = width and Y = height. The Size of original texture. Will be used in scaling
///
public Vector2 ClassicSize { get; private set; }
public Color[] Colors { get; private set; }
public uint Count { get; protected set; }
public bool Modded { get; private set; } = false;
public int Pallet { get; protected set; }
///
/// Scale vector from original to big
///
public Vector2 ScaleFactor => Size == Vector2.Zero || ClassicSize == Vector2.Zero ? Vector2.One : Size / ClassicSize;
///
/// X = width and Y = height. The Size of big version texture. Will be used in scaling
///
public Vector2 Size { get; private set; }
protected uint Cols { get; set; }
protected string Filename { get; set; }
protected uint Rows { get; set; }
protected uint StartOffset { get; set; }
///
/// Scale vector big to modded or orignal to modded.
///
//protected Vector2[,] Scales { get; private set; }
protected Texture2D[,] Textures { get; private set; }
#endregion Properties
#region Indexers
public Texture2D this[int c, int r] => Textures[c, r];
#endregion Indexers
#region Methods
public static Vector2 Abs(Vector2 v) => new Vector2(Math.Abs(v.X), Math.Abs(v.Y));
public static explicit operator Texture2D(TextureHandler t)
{
if (t.Count == 1)
return t[0, 0];
throw new Exception("TextureHandler can only be cast to Texture2D if there is only one texture in the array use [cols,rows] instead");
//return null;
}
public static Vector2 GetOffset(Rectangle old, Rectangle @new) => GetOffset(old.Location.ToVector2(), @new.Location.ToVector2());
public static Vector2 GetOffset(Point oldLoc, Point newLoc) => GetOffset(oldLoc.ToVector2(), newLoc.ToVector2());
public static Vector2 GetOffset(Vector2 oldLoc, Vector2 newLoc) => Abs(oldLoc - newLoc);
public static Vector2 GetScale(Vector2 _old, Vector2 _new) => _new / _old;
public static Vector2 GetScale(Vector2 _old, Texture2D _new) => new Vector2(_new.Width / _old.X, _new.Height / _old.Y);
public static Vector2 GetScale(TEX _old, Texture2D _new) => new Vector2((float)_new.Width / _old.TextureData.Width, (float)_new.Height / _old.TextureData.Height);
public static Vector2 GetScale(Texture2D _old, Texture2D _new) => new Vector2((float)_new.Width / _old.Width, (float)_new.Height / _old.Height);
public static Vector2 GetScale(Point oldSize, Point newSize) => GetScale(oldSize.ToVector2(), newSize.ToVector2());
public static implicit operator Rectangle(TextureHandler v) => new Rectangle(new Point(0), v.Size.ToPoint());
///
/// Load Texture from a mod
///
///
///
public static Texture2D LoadPNG(string path, int pallet = -1)
{
string bn = Path.GetFileNameWithoutExtension(path);
string prefix = bn.Substring(0, 2);
string pngpath = Path.Combine(Memory.FF8DIR, "textures", prefix, bn);
// this isn't working correctly unless mod authors have the this-> 13=0, 14=1... for pallets
//https://github.com/MaKiPL/OpenVIII/issues/73
string suffix = pallet > -1 ? $"{pallet + 13}" : "";
suffix += ".png";
if (Directory.Exists(pngpath))
{
try
{
pngpath = Directory.GetFiles(pngpath).Last(x =>
(x.IndexOf(bn, StringComparison.OrdinalIgnoreCase) >= 0 &&
x.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)));
using (FileStream fs = File.OpenRead(pngpath))
{
return Texture2D.FromStream(Memory.graphics.GraphicsDevice, fs);
}
}
catch (InvalidOperationException)
{
// couldn't find a match.
}
}
return null;
}
public static Rectangle Scale(Rectangle mat1, Vector2 mat2)
{
mat1.Location = (mat1.Location.ToVector2() * mat2).ToPoint();
mat1.Size = (mat1.Size.ToVector2() * mat2).ToPoint();
return mat1;
}
public static Rectangle ToRectangle(Texture2D t) => new Rectangle(0, 0, t.Width, t.Height);
public static Rectangle ToRectangle(TextureHandler t) => new Rectangle(0, 0, (int)t.ClassicSize.X, (int)t.ClassicSize.Y);
public static Rectangle ToRectangle(Vector2 loc, Vector2 size) => new Rectangle(loc.ToPoint(), size.ToPoint());
public static Vector2 ToVector2(Texture2D t) => new Vector2(t.Width, t.Height);
public static Vector2 ToVector2(TextureHandler t) => new Vector2(t.ClassicSize.X, t.ClassicSize.Y);
public static Texture2D UseBest(Texture2D _old, Texture2D _new) => UseBest(_old, _new, out Vector2 scale);
public static Texture2D UseBest(Texture2D _old, Texture2D _new, out Vector2 scale)
{
if (_new == null)
{
scale = Vector2.One;
return _old;
}
else
{
scale = GetScale(_old, _new);
_old.Dispose();
return _new;
}
}
public static Texture2D UseBest(TEX _old, Texture2D _new, int pallet = 0, Color[] colors = null) => UseBest(_old, _new, out Vector2 scale, pallet, colors);
public static Texture2D UseBest(TEX _old, Texture2D _new, out Vector2 scale, int pallet = 0, Color[] colors = null)
{
Texture2D tex;
if (_new == null)
{
scale = Vector2.One;
if (_old.TextureData.NumOfPalettes <= 1)
return _old.GetTexture();
tex = _old.GetTexture(pallet, colors);
return tex;
}
else
{
scale = GetScale(_old, _new);
return _new;
}
}
public void Draw(Rectangle dst, Rectangle? src, Color color)
{
Vector2 dstOffset = Vector2.Zero;
if (src != null)
{
bool drawn = false;
Vector2 offset = Vector2.Zero;
Rectangle dst2 = new Rectangle();
Rectangle cnt = new Rectangle();
for (uint r = 0; r < Rows; r++)
{
offset.X = 0;
//dstOffset.X = 0;
for (uint c = 0; c < Cols; c++)
{
drawn = false;
dst2 = new Rectangle();
//if all the pieces of Scales are correct they should all have the same scale.
Rectangle _src = Scale(src.Value, ScaleFactor);
cnt = ToRectangle(Textures[c, r]);
cnt.Offset(offset);
if (cnt.Contains(_src))
{
//got lucky the whole thing is in this rectangle
_src.Location = (GetOffset(cnt, _src)).ToPoint();
Memory.spriteBatch.Draw(Textures[c, r], dst, _src, color);
return;
}
else if (cnt.Intersects(_src))
{
//Gotta draw more than once.
Rectangle src2 = Rectangle.Intersect(cnt, _src);
src2.Location = (GetOffset(cnt, src2)).ToPoint();
dst2 = Scale(dst, GetScale(_src.Size, src2.Size));
dst2.Location = (dst.Location);
dst2.Offset(dstOffset);
Memory.spriteBatch.Draw(Textures[c, r], dst2, src2, color);
drawn = true;
dstOffset.X += dst2.Width;
}
offset.X += cnt.Width;
}
offset.Y += cnt.Height;
if (drawn)
dstOffset.Y += dst2.Height;
}
}
//drawing texture directly
else
{
Vector2 dstV = Vector2.Zero;
dstOffset.X += dst.X;
dstOffset.Y += dst.X;
for (uint r = 0; r < Rows; r++)
{
for (uint c = 0; c < Cols; c++)
{
Vector2 scale = GetScale(Size, dst.Size.ToVector2());
dstV = ToVector2(Textures[c, r]) * scale;
Memory.spriteBatch.Draw(Textures[c, r], dstOffset, null, color, 0f, Vector2.Zero, scale, SpriteEffects.None, 0f);
dstOffset.X += dstV.X;
}
dstOffset.Y += dstV.Y;
}
}
}
public Vector2 GetScale(int cols = 0, int rows = 0) => ScaleFactor;
protected void Init()
{
Vector2 size = Vector2.Zero;
Vector2 oldsize = Vector2.Zero;
TEX tex = null;
uint c2 = 0;
uint r2 = 0;
for (uint r = 0; r < Rows; r++)
{
for (uint c = 0; c < Cols; c++)
{
ArchiveWorker aw = new ArchiveWorker(Memory.Archives.A_MENU);
string path = aw.GetListOfFiles().First(x => (x.IndexOf(string.Format(Filename, c + r * Cols + StartOffset), StringComparison.OrdinalIgnoreCase) >= 0));
tex = new TEX(ArchiveWorker.GetBinaryFile(Memory.Archives.A_MENU, path));
if (Classic == null && c2 < Cols) oldsize.X += tex.TextureData.Width;
Texture2D pngTex = LoadPNG(path, Pallet);
Textures[c, r] = (UseBest(tex, pngTex, Pallet, Colors));
if (pngTex != null) Modded = true;
if (c2 < Cols) size.X += Textures[c2++, r2].Width;
}
if (Classic == null && r2 < Rows) oldsize.Y += tex.TextureData.Height;
if (r2 < Rows) size.Y += Textures[c2 - 1, r2++].Height;
}
Size = size;
if (Classic == null) ClassicSize = oldsize;
}
private void Init(string filename, TEX classic, uint cols, uint rows, int pallet = -1, Color[] colors = null)
{
Classic = classic;
Size = Vector2.Zero;
Count = cols * rows;
Textures = new Texture2D[cols, rows];
StartOffset = 0;
Rows = rows;
Cols = cols;
Filename = filename;
Pallet = pallet;
Colors = colors;
//load textures;
Init();
//unload Classic
Classic = null;
}
#endregion Methods
}
}