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
///
public abstract partial class SP2
{
#region Constructors
protected SP2()
{ }
#endregion Constructors
#region Enums
///
/// enum to be added to class when implemented
///
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 FORCE_ORIGINAL { 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()
{
T r = new T();
r.DefaultValues();
r.Init();
return r;
}
///
/// Draw Item
///
///
///
///
public virtual void Draw(Enum id, Rectangle dst, float fade = 1)
{
Rectangle src = GetEntry(id).GetRectangle;
TextureHandler tex = GetTexture(id);
tex.Draw(dst, src, Color.White * fade);
}
public virtual void Draw(Enum id, Rectangle dst, Vector2 fill, float fade = 1)
{
Rectangle src = GetEntry(id).GetRectangle;
if (fill == Vector2.UnitX)
{
float r = (float)dst.Height / dst.Width;
src.Height = (int)Math.Round(src.Height * r);
}
else if (fill == Vector2.UnitY)
{
float r = (float)dst.Width / dst.Height;
src.Width = (int)Math.Round(src.Width * r);
}
TextureHandler tex = GetTexture(id);
tex.Draw(dst, src, Color.White * fade);
}
public virtual Entry GetEntry(Enum id)
{
return GetEntry(Convert.ToUInt32(id));
}
public virtual Entry GetEntry(UInt32 id)
{
if (EntriesPerTexture <= 0 && Entries.ContainsKey(id))
return Entries[id];
else if (Entries.ContainsKey((uint)(id % EntriesPerTexture)))
return Entries[(uint)(id % EntriesPerTexture)];
return null;
}
public virtual TextureHandler GetTexture(Enum id, int file = -1)
{
int pos = Convert.ToInt32(id);
int File = file >= 0 ? file : GetEntry((uint)pos).File;
//check if we set a custom file and we have a pos more then set entriespertexture
if (File <= 0)
{
if (EntriesPerTexture > 0 && pos >= EntriesPerTexture)
File = (pos / EntriesPerTexture);
}
if (File > 0)
{
int j = (int)Props.Sum(x => x.Count);
if (File >= j)
{
File %= j;
}
}
return Textures[(int)File];
}
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)
{
Entry 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)
{
Rectangle r = entry.GetRectangle;
Vector2 s = texture.ScaleFactor;
Vector2 o = options.HasFlag(Box_Options.UseOffset) ? entry.Offset : Vector2.Zero;
VertexPositionTexture[] vpt = new VertexPositionTexture[6];
float left = 0f, right = 0f, bottom = 0f, top = 0f;
if (options.HasFlag(Box_Options.Left))
{
left = 0;
right = r.Width;
}
else if (options.HasFlag(Box_Options.Right))
{
left = -r.Width;
right = 0;
}
else// (options.HasFlag(Box_Options.Center))
{
left = -r.Width / 2f;
right = r.Width / 2f;
}
if (options.HasFlag(Box_Options.Top))
{
bottom = 0;
top = r.Height;
}
else if (options.HasFlag(Box_Options.Buttom))
{
bottom = -r.Height;
top = 0;
}
else //(options.HasFlag(Box_Options.Middle))
{
bottom = -r.Height / 2f;
top = r.Height / 2f;
}
VertexPositionTexture[] v = new VertexPositionTexture[]
{
new VertexPositionTexture(new Vector3(left+o.X,top+o.Y,z)*scale,new Vector2(r.Right*s.X/texture.Width,r.Top*s.Y/texture.Height)),
new VertexPositionTexture(new Vector3(right+o.X,top+o.Y,z)*scale,new Vector2(r.Left*s.X/texture.Width,r.Top*s.Y/texture.Height)),
new VertexPositionTexture(new Vector3(right+o.X,bottom+o.Y,z)*scale,new Vector2(r.Left*s.X/texture.Width,r.Bottom*s.Y/texture.Height)),
new VertexPositionTexture(new Vector3(left+o.X,bottom+o.Y,z)*scale,new Vector2(r.Right*s.X/texture.Width,r.Bottom*s.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)
{
ArchiveWorker aw = new ArchiveWorker(ArchiveString);
InitEntries(aw);
InsertCustomEntries();
InitTextures(aw);
}
}
protected virtual void InitEntries(ArchiveWorker aw = null)
{
if (Entries == null)
{
if (aw == null)
aw = new ArchiveWorker(ArchiveString);
MemoryStream ms = null;
ushort[] locs;
using (BinaryReader br = new BinaryReader(
ms = new MemoryStream(ArchiveWorker.GetBinaryFile(ArchiveString,
aw.GetListOfFiles().First(x => x.IndexOf(IndexFilename, StringComparison.OrdinalIgnoreCase) >= 0)))))
{
Count = br.ReadUInt32();
locs = new ushort[Count];//br.ReadUInt32(); 32 valid values in face.sp2 rest is invalid
Entries = new Dictionary((int)Count);
for (uint i = 0; i < Count; i++)
{
locs[i] = br.ReadUInt16();
ms.Seek(2, SeekOrigin.Current);
}
byte fid = 0;
Entry Last = null;
for (uint i = 0; i < Count; i++)
{
ms.Seek(locs[i] + 6, SeekOrigin.Begin);
byte 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, locs[i], Last, ref fid);
Last = Entries[i];
}
ms = null;
}
}
}
protected virtual void InitTextures(ArchiveWorker aw = null) where T : Texture_Base, new()
{
int count = (int)Props.Sum(x => x.Count);
if (Textures == null)
Textures = new List(count);
if (Textures.Count <= 0)
{
if (aw == null)
aw = new ArchiveWorker(ArchiveString);
T tex;
Scale = new Dictionary(count);
int b = 0;
for (int j = 0; j < Props.Count; j++)
for (uint i = 0; i < Props[j].Count; i++)
{
string path = aw.GetListOfFiles().First(x => x.IndexOf(string.Format(Props[j].Filename, i + TextureStartOffset), StringComparison.OrdinalIgnoreCase) > -1);
tex = new T();
tex.Load(aw.GetBinaryFile(path));
if (Props[j].Big != null && FORCE_ORIGINAL == false && b < Props[j].Big.Count)
{
TextureHandler th = TextureHandler.Create(Props[j].Big[b].Filename, tex, 2, Props[j].Big[b++].Split / 2);
Textures.Add(th);
Scale[i] = Vector2.One;
}
else
{
TextureHandler th = TextureHandler.Create(path, tex);
Textures.Add(th);
Scale[i] = th.GetScale(); //scale might not be used outside of texturehandler.
}
}
}
}
protected virtual void InsertCustomEntries()
{
}
#endregion Methods
}
}