using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenVIII
{
///
/// SP2 is a handler for .sp1 and .sp2 files. They are texture atlas coordinates
/// This stores the entries in Entry objects or EntryGroup objects
///
// ReSharper disable once InconsistentNaming
public abstract partial class SP2
{
#region Enums
///
/// enum to be added to class when implemented
///
// ReSharper disable once InconsistentNaming
public enum ID { NotImplemented }
#endregion Enums
#region Properties
///
/// Number of Entries
///
public uint Count { get; protected set; }
///
/// Number of Palettes
///
public uint PaletteCount { get; protected set; }
protected Memory.Archive ArchiveString { get; set; }
///
/// Dictionary of Entries
///
protected virtual Dictionary Entries { get; set; }
///
/// Entries per texture,ID MOD EntriesPerTexture to get current entry to use on this texture
///
public virtual int EntriesPerTexture { get; protected set; }
///
/// If true disable mods and high res textures.
///
protected bool ForceOriginal { get; set; } = false;
///
/// *.sp1 or *.sp2 that contains the entries or entrygroups. With Rectangle and offset information.
///
protected string IndexFilename { get; set; }
protected List Props { get; set; }
///
/// Should be Vector2.One unless reading a high res version of textures.
///
protected Dictionary Scale { get; set; }
///
/// List of textures
///
protected virtual List Textures { get; set; }
///
/// Some textures start with 1 and some start with 0. This is added to the current number in
/// when reading the files in.
///
protected int TextureStartOffset { get; set; }
#endregion Properties
#region Indexers
public Entry this[Enum id] => GetEntry(id);
#endregion Indexers
#region Methods
public static T Load() where T : SP2, new()
{
Memory.Log.WriteLine($"{nameof(SP2)} :: {nameof(Load)} :: {typeof(T)} ");
var r = new T();
r.DefaultValues();
r.Init();
return r;
}
///
/// Draw Item
///
///
///
///
public virtual void Draw(Enum id, Rectangle dst, float fade = 1)
{
var src = GetEntry(id).GetRectangle;
var tex = GetTexture(id);
tex.Draw(dst, src, Color.White * fade);
}
public virtual void Draw(Enum id, Rectangle dst, Vector2 fill, float fade = 1)
{
var entry = GetEntry(id);
if (entry != null)
{
var src = entry.GetRectangle;
if (fill == Vector2.UnitX)
{
var r = (float)dst.Height / dst.Width;
src.Height = (int)Math.Round(src.Height * r);
}
else if (fill == Vector2.UnitY)
{
var r = (float)dst.Width / dst.Height;
src.Width = (int)Math.Round(src.Width * r);
}
var tex = GetTexture(id);
tex?.Draw(dst, src, Color.White * fade);
}
}
public virtual Entry GetEntry(Enum id) => GetEntry(Convert.ToUInt32(id));
public virtual Entry GetEntry(uint id)
{
if (EntriesPerTexture <= 0 && Entries.ContainsKey(id))
return Entries[id];
else if (Entries?.ContainsKey((uint)(id % EntriesPerTexture))??false)
return Entries[(uint)(id % EntriesPerTexture)];
return null;
}
public virtual TextureHandler GetTexture(Enum id, int file = -1)
{
var pos = Convert.ToInt32(id);
file = file >= 0 ? file : GetEntry((uint)pos).File;
//check if we set a custom file and we have a pos more then set entries per texture
if (file <= 0)
{
if (EntriesPerTexture > 0 && pos >= EntriesPerTexture)
file = (pos / EntriesPerTexture);
}
if (file <= 0) return Textures.Count > file ? Textures[file] : null;
var j = (int)Props.Sum(x => x.Count);
if (file >= j)
{
file %= j;
}
return Textures.Count>file ? Textures[file] : null;
}
public virtual TextureHandler GetTexture(Enum id, out Vector2 scale)
{
scale = Scale[GetEntry(id).File];
return GetTexture(id);
}
public virtual void Trim(Enum ic, byte pal)
{
var entry = this[ic];
entry.SetTrimNonGroup(Textures[pal]);
}
protected virtual VertexPositionTexture_Texture2D Quad(Enum ic, byte pal, float scale = .25f, Box_Options options = Box_Options.Middle | Box_Options.Center, float z = 0f)
{
Trim(ic, pal);
return Quad(this[ic], Textures[pal], scale, options, z);
}
protected virtual VertexPositionTexture_Texture2D Quad(Entry entry, TextureHandler texture, float scale = .25f, Box_Options options = Box_Options.Middle | Box_Options.Center, float z = 0f)
{
var rectangle = entry.GetRectangle;
var scaleFactor = texture.ScaleFactor;
var offset = options.HasFlag(Box_Options.UseOffset) ? entry.Offset : Vector2.Zero;
var vpt = new VertexPositionTexture[6];
float left, right, bottom, top;
if (options.HasFlag(Box_Options.Left))
{
left = 0;
right = rectangle.Width;
}
else if (options.HasFlag(Box_Options.Right))
{
left = -rectangle.Width;
right = 0;
}
else// (options.HasFlag(Box_Options.Center))
{
left = -rectangle.Width / 2f;
right = rectangle.Width / 2f;
}
if (options.HasFlag(Box_Options.Top))
{
bottom = 0;
top = rectangle.Height;
}
else if (options.HasFlag(Box_Options.Buttom))
{
bottom = -rectangle.Height;
top = 0;
}
else //(options.HasFlag(Box_Options.Middle))
{
bottom = -rectangle.Height / 2f;
top = rectangle.Height / 2f;
}
var v = new VertexPositionTexture[]
{
new VertexPositionTexture(new Vector3(left+offset.X,top+offset.Y,z)*scale,new Vector2(rectangle.Right*scaleFactor.X/texture.Width,rectangle.Top*scaleFactor.Y/texture.Height)),
new VertexPositionTexture(new Vector3(right+offset.X,top+offset.Y,z)*scale,new Vector2(rectangle.Left*scaleFactor.X/texture.Width,rectangle.Top*scaleFactor.Y/texture.Height)),
new VertexPositionTexture(new Vector3(right+offset.X,bottom+offset.Y,z)*scale,new Vector2(rectangle.Left*scaleFactor.X/texture.Width,rectangle.Bottom*scaleFactor.Y/texture.Height)),
new VertexPositionTexture(new Vector3(left+offset.X,bottom+offset.Y,z)*scale,new Vector2(rectangle.Right*scaleFactor.X/texture.Width,rectangle.Bottom*scaleFactor.Y/texture.Height)),
};
vpt[0] = v[0];
vpt[1] = v[1];
vpt[2] = v[3];
vpt[3] = v[1];
vpt[4] = v[2];
vpt[5] = v[3];
return new VertexPositionTexture_Texture2D(vpt, texture);
}
protected virtual void DefaultValues()
{
Count = 0;
PaletteCount = 1;
EntriesPerTexture = 1;
Scale = null;
TextureStartOffset = 0;
IndexFilename = "";
Textures = null;
Entries = null;
ArchiveString = Memory.Archives.A_MENU;
}
protected virtual void Init()
{
if (Entries == null)
{
var aw = ArchiveWorker.Load(ArchiveString);
InitEntries(aw);
InsertCustomEntries();
InitTextures(aw);
}
}
protected virtual void InitEntries(ArchiveBase aw = null)
{
if (Entries != null) return;
if (aw == null)
aw = ArchiveWorker.Load(ArchiveString);
MemoryStream ms;
var buffer = aw.GetBinaryFile(IndexFilename);
if (buffer == null) return;
using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
{
Count = br.ReadUInt32();
var locations = new ushort[Count];
Entries = new Dictionary((int)Count);
for (uint i = 0; i < Count; i++)
{
locations[i] = br.ReadUInt16();
ms.Seek(2, SeekOrigin.Current);
}
byte fid = 0;
Entry last = null;
for (uint i = 0; i < Count; i++)
{
ms.Seek(locations[i] + 6, SeekOrigin.Begin);
var t = br.ReadByte();
if (t == 0 || t == 96) // known invalid entries in sp2 files have this value. there might be more to it.
{
Count = i;
break;
}
Entries[i] = new Entry();
Entries[i].LoadfromStreamSP2(br, locations[i], last, ref fid);
last = Entries[i];
}
}
}
protected virtual void InitTextures(ArchiveBase aw = null) where T : Texture_Base, new()
{
var count = (int)Props.Sum(x => x.Count);
if (Textures == null)
Textures = new List(count);
if (Textures.Count <= 0)
{
if (aw == null)
aw = ArchiveWorker.Load(ArchiveString);
T tex;
Scale = new Dictionary(count);
var b = 0;
for (var j = 0; j < Props.Count; j++)
for (uint i = 0; i < Props[j].Count; i++)
{
tex = new T();
var buffer = aw.GetBinaryFile(string.Format(Props[j].Filename, i + TextureStartOffset));
if (buffer != null)
{
tex.Load(buffer);
if (Props[j].Big != null && ForceOriginal == false && b < Props[j].Big.Count)
{
var th = TextureHandler.Create(Props[j].Big[b].Filename, tex, 2, Props[j].Big[b++].Split / 2);
Textures.Add(th);
Scale[i] = Vector2.One;
}
else
{
var th = TextureHandler.Create(Props[j].Filename, tex);
Textures.Add(th);
Scale[i] = th.GetScale(); //scale might not be used outside of texturehandler.
}
}
}
}
}
protected virtual void InsertCustomEntries()
{
}
#endregion Methods
}
}