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 } }