| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260 |
- using Microsoft.Xna.Framework;
- using Microsoft.Xna.Framework.Graphics;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Text.RegularExpressions;
- namespace OpenVIII.Fields
- {
- /// <summary>
- /// Background Tiles for field
- /// </summary>
- /// <see cref="https://github.com/myst6re/deling/blob/master/files/BackgroundFile.cpp"/>
- /// <seealso cref="https://github.com/myst6re/deling/blob/master/files/BackgroundFile.h"/>
- /// <seealso cref="http://wiki.ffrtt.ru/index.php?title=FF8/FileFormat_MAP"/>
- /// <seealso cref="http://wiki.ffrtt.ru/index.php?title=FF8/FileFormat_MIM"/>
- /// <seealso cref="http://forums.qhimm.com/index.php?topic=13444.msg264595#msg264595"/>
- /// <seealso cref="http://forums.qhimm.com/index.php?topic=13444.0"/>
- public partial class Background : IDisposable
- {
- #region Fields
- private const int BytesPerPalette = 2 * ColorsPerPalette;
- private const int ColorsPerPalette = 256;
- /// <summary>
- /// 4 bit has 2 columns per every byte so it expands to twice the width.
- /// </summary>
- private const int FourBitTexturePageWidth = 2 * TexturePageWidth;
- /// <summary>
- /// Standard texture page width.
- /// </summary>
- private const int TexturePageWidth = 128;
- private Dictionary<byte, List<TileQuadTexture>> _animations;
- private AlphaTestEffect _ate;
- private Vector3 _camPosition;
- private Vector3 _camTarget;
- /// <summary>
- /// Palettes/Color Lookup Tables
- /// </summary>
- private Cluts _cluts;
- public float Degrees;
- private bool _disposedValue;
- private BasicEffect _effect;
- private FPS_Camera _fpsCamera;
- private Rectangle _outputDims;
- private Matrix _projectionMatrix, _viewMatrix, _worldMatrix;
- private List<TileQuadTexture> _quads;
- private Dictionary<byte, TextureHandler> _textureIDs;
- private Dictionary<TextureIDPaletteID, TextureHandler> _textureIDsPalettes;
- private ConcurrentDictionary<ushort, ConcurrentDictionary<byte, ConcurrentDictionary<byte,
- ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>>>>
- _textures;
- private BackgroundTextureType _textureType;
- #endregion Fields
- #region Destructors
- // To detect redundant calls
- // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
- ~Background()
- {
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- Dispose(false);
- }
- #endregion Destructors
- #region Properties
- public TimeSpan CurrentTime { get; set; }
- public Tiles GetTiles { get; private set; }
- public bool HasSpriteBatchTexturesLoaded => GetTexturesReadyToDrawClassicSpriteBatch()?.Count > 0;
- public int Height { get => _outputDims.Height; private set => _outputDims.Height = value; }
- /*
- public bool Is4Bit => GetTiles?.Any(x => x.Is4Bit) ?? false;
- public bool IsAddBlendMode => GetTiles?.Any(x => x.BlendMode == BlendMode.add) ?? false;
- public bool IsHalfBlendMode => GetTiles?.Any(x => x.BlendMode == BlendMode.halfadd) ?? false;
- public bool IsQuarterBlendMode => GetTiles?.Any(x => x.BlendMode == BlendMode.quarteradd) ?? false;
- public bool IsSubtractBlendMode => GetTiles?.Any(x => x.BlendMode == BlendMode.subtract) ?? false;
- */
- public Vector3 MouseLocation { get; set; }
- public TimeSpan TotalTime { get; set; }
- public int Width { get => _outputDims.Width; private set => _outputDims.Width = value; }
- #endregion Properties
- #region Methods
- public static Background Load(byte[] mim, byte[] map)
- {
- if (mim == null || map == null)
- return null;
- var r = new Background
- {
- _textureType = BackgroundTextureType.GetTextureType(mim),
- _camTarget = Vector3.Zero,
- _camPosition = new Vector3(0f, 0f, -10f),
- _fpsCamera = new FPS_Camera(),
- Degrees = 90f
- };
- if (Memory.Graphics != null)
- {
- r._ate = new AlphaTestEffect(Memory.Graphics.GraphicsDevice);
- r._effect = new BasicEffect(Memory.Graphics.GraphicsDevice);
- }
- r._worldMatrix = Matrix.CreateWorld(r._camPosition, Vector3.
- Forward, Vector3.Up);
- r._viewMatrix = Matrix.CreateLookAt(r._camPosition, r._camTarget,
- Vector3.Up);
- r.LoadTiles(map);
- r.LoadPalettes(mim);
- r.DumpRawTexture(mim);
- var watch = Stopwatch.StartNew();
- try
- {
- if (!r.ParseBackgroundQuads(mim))
- {
- return null;
- }
- }
- finally
- {
- watch.Stop();
- Debug.WriteLine($"{nameof(ParseBackgroundQuads)} took {watch.ElapsedMilliseconds / 1000f} seconds.");
- }
- try
- {
- if (!r.ParseBackgroundClassicSpriteBatch(mim))
- {
- return null;
- }
- }
- finally
- {
- watch.Stop();
- Debug.WriteLine($"{nameof(ParseBackgroundClassicSpriteBatch)} took {watch.ElapsedMilliseconds / 1000f} seconds.");
- }
- return r;
- }
- public void Deswizzle()
- {
- using (var mask = new Texture2D(Memory.Graphics.GraphicsDevice, 4, 4))
- {
- mask.SetData(Enumerable.Range(0, 16).Select(x => Color.White).ToArray());
- var fieldName = Module.GetFieldName();
- var folder = Module.GetFolder(fieldName, "deswizzle");
- var scale = _quads[0].Texture.ScaleFactor;
- var tilesWidth = (int)(GetTiles.Width * scale.X);
- var tilesHeight = (int)(GetTiles.Height * scale.Y);
- //Matrix backup = projectionMatrix;
- //projectionMatrix = Matrix.CreateOrthographic(tiles.Width, tiles.Height, 0f, 100f);
- GetTiles.UniquePupuIDs();// make sure each layer has it's own ID.
- foreach (var pupuGroup in _quads.GroupBy(x => x.GetTile.PupuID)
- ) //group the quads by their pupu ID.
- {
- using (var outTex = new RenderTarget2D(Memory.Graphics.GraphicsDevice, tilesWidth, tilesHeight))
- {
- //start drawing
- Memory.Graphics.GraphicsDevice.SetRenderTarget(outTex);
- Memory.Graphics.GraphicsDevice.Clear(Color.TransparentBlack);
- Memory.SpriteBatchStartAlpha();
- foreach (var quad in pupuGroup)
- {
- var tile = (Tile)quad;
- //DrawBackgroundQuadsStart();
- //DrawBackgroundQuad(quad, true);
- var dst = tile.GetRectangle;
- dst.Offset(Math.Abs(GetTiles.TopLeft.X), Math.Abs(GetTiles.TopLeft.Y));
- var src = tile.Source;
- //src = src.Scale(scale);
- dst = dst.Scale(scale);
- quad.Texture.Draw(dst, src, Color.White);
- }
- Memory.SpriteBatchEnd();
- //end drawing
- Memory.Graphics.GraphicsDevice.SetRenderTarget(null);
- //set path
- var path = Path.Combine(folder,
- $"{fieldName}_{pupuGroup.Key:X8}.png");
- //save image.
- using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- {
- outTex.SaveAsPng(fs, tilesWidth, tilesHeight);
- }
- }
- using (var outTex = new RenderTarget2D(Memory.Graphics.GraphicsDevice, tilesWidth, tilesHeight))
- {
- //start drawing
- Memory.Graphics.GraphicsDevice.SetRenderTarget(outTex);
- Memory.Graphics.GraphicsDevice.Clear(Color.Black);
- Memory.SpriteBatchStartAlpha();
- var src = new Rectangle(0, 0, 4, 4);
- foreach (var quad in pupuGroup)
- {
- var tile = (Tile)quad;
- //DrawBackgroundQuadsStart();
- //DrawBackgroundQuad(quad, true);
- var dst = tile.GetRectangle;
- dst.Offset(Math.Abs(GetTiles.TopLeft.X), Math.Abs(GetTiles.TopLeft.Y));
- //src = src.Scale(scale);
- dst = dst.Scale(scale);
- Memory.SpriteBatch.Draw(mask, dst, src, Color.White);
- }
- Memory.SpriteBatchEnd();
- //end drawing
- Memory.Graphics.GraphicsDevice.SetRenderTarget(null);
- //set path
- var path = Path.Combine(folder,
- $"{fieldName}_{pupuGroup.Key:X8}_MASK.png");
- //save image.
- using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- {
- outTex.SaveAsPng(fs, tilesWidth, tilesHeight);
- }
- }
- }
- Process.Start(folder);
- //projectionMatrix = backup;
- }
- }
- // This code added to correctly implement the disposable pattern.
- public void Dispose() =>
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- Dispose(true);
- public void Draw()
- {
- //Memory.spriteBatch.GraphicsDevice.Clear(Color.Black);
- DrawBackgroundQuads();
- DrawWalkMesh();
- DrawSpriteBatch();
- }
- public void Reswizzle()
- {
- GetTiles.UniquePupuIDs();// make sure each layer has it's own ID.
- var fieldName = Module.GetFieldName();
- var folder = Module.GetFolder(fieldName, "deswizzle"); //goes from deswizzle folder
- if (!Directory.Exists(folder)) return;
- IEnumerable<string> files = Directory.EnumerateFiles(folder, "*.png").ToArray();
- folder = Module.GetFolder(fieldName, "reswizzle");
- var overlap = GetTiles.Select(x => x.TextureID).Distinct().ToDictionary(x => x, x => new HashSet<byte>());
- var texIDs = new ConcurrentDictionary<byte, TextureBuffer>();
- var texIDsPalette = new ConcurrentDictionary<TextureIDPaletteID, TextureBuffer>();
- var width = 0; var height = 0;
- //Vector2 origin = tiles.Origin;
- var lowest = GetTiles.TopLeft;
- var size = new Vector2(GetTiles.Width, GetTiles.Height);//new Point(Math.Abs(lowest.X) + highest.X + Tile.size, Math.Abs(lowest.Y) + highest.Y + Tile.size);
- var re = new Regex(@".+_([0-9A-F]{8}).png", RegexOptions.IgnoreCase | RegexOptions.Compiled);
- process();
- if (overlap.Any(x => x.Value.Count > 1))
- process(true);
- void process(bool doOverLap = false)
- {
- foreach (var file in files)
- {
- //Point highest = new Point(tiles.Max(x => x.X), tiles.Max(x => x.Y));
- var match = re.Match(file);
- if (match.Groups.Count > 1 && uint.TryParse(match.Groups[1].Value, NumberStyles.HexNumber,
- CultureInfo.InvariantCulture, out var pupuID))
- {
- using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
- using (var tex = Texture2D.FromStream(Memory.Graphics.GraphicsDevice, fs))
- {
- var scale = new Vector2(tex.Width, tex.Height) / size;
- width = (int)(256 * scale.X);
- height = (int)(256 * scale.Y);
- var inTex = new TextureBuffer(tex.Width, tex.Height);
- inTex.GetData(tex);
- foreach (var quad in _quads.Where(x =>
- x.GetTile.PupuID == pupuID &&
- (!doOverLap || overlap[x.GetTile.TextureID].Contains(x.GetTile.PaletteID))))
- {
- var tile = (Tile)quad;
- texIDs.TryAdd(tile.TextureID, new TextureBuffer(width, height, false));
- var src = (new Vector2(Math.Abs(lowest.X) + tile.X, Math.Abs(lowest.Y) + tile.Y) * scale).ToPoint();
- var dst = (new Vector2(tile.SourceX, tile.SourceY) * scale).ToPoint();
- if (!doOverLap)
- foreach (var p in from x in Enumerable.Range(0, (int)(Tile.Size * scale.X))
- from y in Enumerable.Range(0, (int)(Tile.Size * scale.Y))
- orderby y, x
- select new Point(x, y))
- {
- var input = inTex[src.X + p.X, src.Y + p.Y];
- var current = texIDs[tile.TextureID][dst.X + p.X, dst.Y + p.Y];
- var unscaledLocation = tile.Source.Location;
- unscaledLocation.Offset(p.ToVector2() / scale);
- var output = ChangeColor(current, input, unscaledLocation, tile.TextureID, overlap);
- if (!output.HasValue) break;
- if (output.Value.A != 0)
- texIDs[tile.TextureID][dst.X + p.X, dst.Y + p.Y] = output.Value;
-
- }
- else if (overlap[tile.TextureID].Count > 1)
- {
- var key = new TextureIDPaletteID { PaletteID = tile.PaletteID, TextureID = tile.TextureID };
- texIDsPalette.TryAdd(key, new TextureBuffer(width, height));
- foreach (var p in from x in Enumerable.Range(0, (int)(Tile.Size * scale.X))
- from y in Enumerable.Range(0, (int)(Tile.Size * scale.Y))
- select new Point(x, y))
- {
- var input = inTex[src.X + p.X, src.Y + p.Y];
- if (input.A != 0)
- {
- texIDsPalette[key][dst.X + p.X, dst.Y + p.Y] = inTex[src.X + p.X, src.Y + p.Y];
- }
- }
- }
- }
- }
- }
- }
- }
- //save new reswizzles
- foreach (var tid in texIDs)
- {
- var path = Path.Combine(folder,
- $"{fieldName}_{tid.Key}.png");
- //save image.
- using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- {
- using (var outTex = (Texture2D)tid.Value)
- outTex.SaveAsPng(fs, width, height);
- }
- }
- foreach (var tid in texIDsPalette)
- {
- var path = Path.Combine(folder,
- $"{fieldName}_{tid.Key.TextureID}_{tid.Key.PaletteID}.png");
- //save image.
- using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- {
- using (var outTex = (Texture2D)tid.Value)
- outTex.SaveAsPng(fs, width, height);
- }
- }
- Process.Start(folder);
- }
- public Tiles TilesUnderMouse() => new Tiles(GetTiles.Where(x =>
- x.X < MouseLocation.X && x.X + 16 > MouseLocation.X &&
- x.Y < MouseLocation.Y && x.Y + 16 > MouseLocation.Y).ToList());
- public void Update()
- {
- if ((CurrentTime += Memory.ElapsedGameTime) > TotalTime)
- {
- CurrentTime = TimeSpan.Zero;
- foreach (var a in _animations)
- {
- int i = a.Value.FirstOrDefault(x => x.Enabled)?.AnimationState ?? 0;
- int max = a.Value.Max(k => k.AnimationState);
- var i1 = i;
- a.Value.Where(x => x.AnimationState == i1).ForEach(x => x.Hide());
- if (++i >= max)
- i = 0;
- a.Value.Where(x => x.AnimationState == i).ForEach(x => x.Show());
- }
- }
- float tilesWidth = GetTiles.Width;
- float tilesHeight = GetTiles.Height;
- if (Module.Toggles.HasFlag(Toggles.Perspective)) //perspective mode shows gabs in the tiles.
- {
- //finds the min zoom out to fit the entire image in frame.
- var half = new Vector2(tilesWidth / 2f, tilesHeight / 2f);
- var fieldOfView = MathHelper.ToRadians(70);
- float getOppositeSide(float side, float angle)
- {
- return (float)(Math.Tan(angle) * side);
- }
- half.X = getOppositeSide(half.X, MathHelper.ToRadians(45));
- half.Y = getOppositeSide(half.Y, MathHelper.ToRadians(45));
- var minDistanceFromBG = -Math.Max(half.X, half.Y);
- if (_camPosition.Z > minDistanceFromBG)
- _camPosition.Z = minDistanceFromBG;
- _projectionMatrix = Matrix.CreatePerspectiveFieldOfView(fieldOfView, Memory.Graphics.GraphicsDevice.Viewport.AspectRatio, float.Epsilon, 1000f);
- _viewMatrix = !Module.Toggles.HasFlag(Toggles.Menu)
- ? _fpsCamera.Update(ref _camPosition, ref _camTarget, ref Degrees)
- : Matrix.CreateLookAt(_camPosition, _camTarget, Vector3.Up);
- }
- else
- {
- var vp = Memory.Graphics.GraphicsDevice.Viewport;
- var scale = Memory.Scale(tilesWidth, tilesHeight, ScaleMode.FitBoth);
- _projectionMatrix = Matrix.CreateOrthographic(vp.Width / scale.X, vp.Height / scale.Y, 0f, 100f);
- _viewMatrix = Matrix.CreateLookAt(Vector3.Forward * 10f, Vector3.Zero, Vector3.Up);
- }
- var ml = InputMouse.Location.ToVector2();
- var ml3d = Memory.Graphics.GraphicsDevice.Viewport.Unproject(ml.ToVector3(), _projectionMatrix, _viewMatrix, _worldMatrix);
- ml3d.Y *= -1;
- MouseLocation = ml3d;
- }
- protected virtual void Dispose(bool disposing)
- {
- if (_disposedValue) return;
- if (disposing)
- {
- // TODO: dispose managed state (managed objects).
- }
- _textureIDs?.ForEach(x => x.Value?.Dispose());
- _textureIDsPalettes?.ForEach(x => x.Value?.Dispose());
- // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
- // TODO: set large fields to null.
- _disposedValue = true;
- }
- private static Color Blend1(Color baseColor, Color color)
- {//ClassicSpriteBatch
- var r = new Color
- {
- R = (byte) MathHelper.Clamp(baseColor.R + color.R, 0, 255),
- G = (byte) MathHelper.Clamp(baseColor.G + color.G, 0, 255),
- B = (byte) MathHelper.Clamp(baseColor.B + color.B, 0, 255),
- A = 0xFF
- };
- return r;
- }
- private static Color Blend2(Color baseColor, Color color)
- {//ClassicSpriteBatch
- var r = new Color
- {
- R = (byte) MathHelper.Clamp(baseColor.R - color.R, 0, 255),
- G = (byte) MathHelper.Clamp(baseColor.G - color.G, 0, 255),
- B = (byte) MathHelper.Clamp(baseColor.B - color.B, 0, 255),
- A = 0xFF
- };
- return r;
- }
- private Color? ChangeColor(Color current, Color input, Point p, byte textureID, IReadOnlyDictionary<byte, HashSet<byte>> overlap)
- {
- if (input.A == 0) return Color.TransparentBlack;
- if (current.A == 0 || current == input)
- return input;
- IEnumerable<byte> o = (from tile in GetTiles
- // ReSharper disable once ImplicitlyCapturedClosure
- where tile.TextureID.Equals(textureID) && tile.ExpandedSource.Contains(p)
- select tile.PaletteID).Distinct().ToArray();
- if (o.Count() <= 1) return input; // two tiles same palette is drawing to same place
- o.ForEach(x => overlap[textureID].Add(x));
- return null;
- }
- private void DrawBackgroundQuad(TileQuadTexture quad, bool forceBlendModeNone = false, Vector2 scale2 = default)
- {
- VertexPositionTexture[] temp = quad;
- var tile = (Tile)quad;
- _ate.Texture = quad;
- if (scale2 != default)
- {
- temp = (VertexPositionTexture[])temp.Clone();
- var scale = Matrix.CreateScale(scale2.ToVector3());
- for (var i = 0; i < temp.Length; i++)
- {
- var t = temp[i];
- t.Position = Vector3.Transform(temp[i].Position, scale);
- temp[i] = t;
- }
- }
- DrawBackgroundQuadsSetBendMode(forceBlendModeNone ? BlendMode.None : tile.BlendMode);
- foreach (var pass in _ate.CurrentTechnique.Passes)
- {
- pass.Apply();
- Memory.Graphics.GraphicsDevice.DrawUserPrimitives(primitiveType: PrimitiveType.TriangleList,
- vertexData: temp, vertexOffset: 0, primitiveCount: 2);
- }
- }
- private void DrawBackgroundQuads()
- {
- if (!Module.Toggles.HasFlag(Toggles.Quad)) return;
- DrawBackgroundQuadsStart();
- foreach (var quad in _quads.Where(x => x.Enabled))
- {
- DrawBackgroundQuad(quad);
- }
- }
- private void DrawBackgroundQuadsSetBendMode(BlendMode bm)
- {
- _ate.Alpha = 1f;
- var half = new Color(.5f, .5f, .5f, 1f);
- var quarter = new Color(.25f, .25f, .25f, 1f);
- var full = Color.White;
- switch (bm)
- {
- //If we deswizzled and merged the (BlendModes != BlendMode.none) tiles
- // we can change SamplerState to Anisotropic.
- //But swizzled textures are a Texture Atlas so it will draw bad pixels from near by.
- default:
- Memory.Graphics.GraphicsDevice.BlendFactor = full;
- Memory.Graphics.GraphicsDevice.BlendState = BlendState.AlphaBlend;
- //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
- break;
- case BlendMode.Add:
- Memory.Graphics.GraphicsDevice.BlendFactor = full;
- Memory.Graphics.GraphicsDevice.BlendState = Memory.BlendStateAdd;
- //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
- break;
- case BlendMode.Subtract:
- Memory.Graphics.GraphicsDevice.BlendFactor = full;
- Memory.Graphics.GraphicsDevice.BlendState = Memory.BlendStateSubtract;
- _ate.Alpha = .85f; //doesn't darken so much.
- //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
- break;
- case BlendMode.HalfAdd:
- Memory.Graphics.GraphicsDevice.BlendFactor = half;
- Memory.Graphics.GraphicsDevice.BlendState = Memory.BlendStateAddBlendFactor;
- //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
- break;
- case BlendMode.QuarterAdd:
- Memory.Graphics.GraphicsDevice.BlendFactor = quarter;
- Memory.Graphics.GraphicsDevice.BlendState = Memory.BlendStateAddBlendFactor;
- //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
- break;
- }
- }
- private void DrawBackgroundQuadsStart()
- {
- Memory.Graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
- Memory.Graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
- _ate.Projection = _projectionMatrix; _ate.View = _viewMatrix; _ate.World = _worldMatrix;
- _effect.Projection = _projectionMatrix; _effect.View = _viewMatrix; _effect.World = _worldMatrix;
- Memory.Graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
- _effect.TextureEnabled = true;
- _ate.VertexColorEnabled = false;
- _effect.VertexColorEnabled = false;
- }
- private void DrawSpriteBatch()
- {
- if (!Module.Toggles.HasFlag(Toggles.ClassicSpriteBatch)) return;
- var drawTextures = GetTexturesReadyToDrawClassicSpriteBatch();
- var open = false;
- var lastBlendMode = BlendMode.None;
- var alpha = 1f;
- if (drawTextures != null)
- foreach (var kvp in drawTextures)
- {
- if (!open || lastBlendMode != kvp.Key)
- {
- if (open)
- Memory.SpriteBatchEnd();
- open = true;
- alpha = 1f;
- switch (kvp.Key)
- {
- default:
- Memory.SpriteBatchStartAlpha();
- break;
- case BlendMode.HalfAdd:
- Memory.SpriteBatchStart(bs: Memory.BlendStateAdd, ss: SamplerState.AnisotropicClamp);
- break;
- case BlendMode.QuarterAdd:
- Memory.SpriteBatchStart(bs: Memory.BlendStateAdd, ss: SamplerState.AnisotropicClamp);
- break;
- case BlendMode.Add:
- Memory.SpriteBatchStart(bs: Memory.BlendStateAdd, ss: SamplerState.AnisotropicClamp);
- break;
- case BlendMode.Subtract:
- alpha = .9f;
- Memory.SpriteBatchStart(bs: Memory.BlendStateSubtract, ss: SamplerState.AnisotropicClamp);
- break;
- }
- lastBlendMode = kvp.Key;
- }
- var tex = kvp.Value;
- var src = new Rectangle(0, 0, tex.Width, tex.Height);
- var dst = src;
- dst.Size = (dst.Size.ToVector2() * Memory.Scale(tex.Width, tex.Height, ScaleMode.FitBoth)).ToPoint();
- //In game I think we'd keep the field from leaving the screen edge but would center on the Squall and the party when it can.
- //I setup scaling after noticing the field didn't size with the screen. I set it to center on screen.
- dst.Offset(Memory.Center.X - dst.Center.X, Memory.Center.Y - dst.Center.Y);
- Memory.SpriteBatch.Draw(tex, dst, src, Color.White * alpha);
- //new Microsoft.Xna.Framework.Rectangle(0, 0, 1280 + (width - 320), 720 + (height - 224)),
- //new Microsoft.Xna.Framework.Rectangle(0, 0, tex.Width, tex.Height)
- }
- if (open)
- Memory.SpriteBatchEnd();
- }
- private void DrawWalkMesh()
- {//todo move into walk mesh class. was only because at the time I thought i'd need the background data.
- if (!Module.Toggles.HasFlag(Toggles.WalkMesh)) return;
- _effect.TextureEnabled = false;
- Memory.Graphics.GraphicsDevice.BlendFactor = Color.White;
- Memory.Graphics.GraphicsDevice.BlendState = BlendState.Opaque;
- Memory.Graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
- //using (DepthStencilState depthStencilState = new DepthStencilState() { DepthBufferEnable = true })
- using (var rasterizerState = new RasterizerState() { CullMode = CullMode.None })
- {
- Memory.Graphics.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;//depthStencilState;
- Memory.Graphics.GraphicsDevice.RasterizerState = rasterizerState;
- _ate.Texture = null;
- _ate.VertexColorEnabled = true;
- _effect.VertexColorEnabled = true;
- //camPosition = Module.Cameras[0].Position;
- _effect.World = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up);// Module.Cameras[0].CreateWorld();
- var fieldOfView = MathHelper.ToRadians(70);
- //effect.View = //Module.Cameras[0].CreateLookAt();
- //effect.Projection = Module.Cameras[0].CreateProjection();
- _effect.Projection = Matrix.CreatePerspectiveFieldOfView(fieldOfView,
- Memory.Graphics.GraphicsDevice.Viewport.AspectRatio, float.Epsilon, 1000f);
- _effect.View = !Module.Toggles.HasFlag(Toggles.Menu)
- ? _fpsCamera.Update(ref _camPosition, ref _camTarget, ref Degrees)
- : Matrix.CreateLookAt(_camPosition, _camTarget, Vector3.Up);
- foreach (var pass in _effect.CurrentTechnique.Passes)
- {
- pass.Apply();
- Memory.Graphics.GraphicsDevice.DrawUserPrimitives(primitiveType: PrimitiveType.TriangleList,
- vertexData: Module.WalkMesh.Vertices.ToArray(), vertexOffset: 0, primitiveCount: Module.WalkMesh.Count);
- }
- }
- }
- private void DumpRawTexture(byte[] mim)
- {
- if (!Memory.EnableDumpingData && !Module.Toggles.HasFlag(Toggles.DumpingData)) return;
- MemoryStream ms;
- var path = Path.Combine(Module.GetFolder(),
- $"{Module.GetFieldName()}_raw_{{0}}bit_{{1}}.png");
- using (var br = new BinaryReader(ms = new MemoryStream(mim)))
- {
- long startPixel = _textureType.PaletteSectionSize;
-
- process(8);
- process(4);
- process(16);
- process(24);
- process(24,true);
- void process(byte bit, bool alt = false)
- {
- var adj = bit / 8f;
- var textureTypeWidth = bit == 24? _textureType.Width :(int)(_textureType.Width / adj);
- var height = checked((int)Math.Ceiling(((float)mim.Length - _textureType.PaletteSectionSize) / _textureType.Width / (bit == 24 ? adj : 1f)));
- if (bit == 24 && alt)
- {
- textureTypeWidth = (int)Math.Ceiling(_textureType.Width / adj);
- height *= (int)adj;
- }
- var buffer = new TextureBuffer(textureTypeWidth, height, false);
- foreach (var clut in _cluts)
- {
- ms.Seek(startPixel, SeekOrigin.Begin);
- var i = 0;
- byte colorKey = 0;
- var lastRow = 0;
- while (ms.Position + Math.Ceiling(adj) < ms.Length)
- {
- var row = i / textureTypeWidth;
- Color input;
- if (bit == 24) //just to see if anything is there. don't think there is a real usage of 24 bit.
- {
- if (alt && lastRow != row && row % 3 ==0) i++;
- //i += 1;
- input = new Color
- {
- B = br.ReadByte(),
- G = br.ReadByte(),
- R = br.ReadByte(),
- A = 0xFF,
- };
- }
- else if (bit == 16)
- {
- //i += 1;
- input = Texture_Base.ABGR1555toRGBA32bit(br.ReadUInt16());
- }
- else if (bit == 8)
- {
- //i = checked((int)(ms.Position - startPixel));
- colorKey = br.ReadByte();
- input = clut.Value[colorKey];
- }
- else if (bit == 4)
- {
- //i++;
- if (i % 2 == 0)
- {
- colorKey = br.ReadByte();
- input = clut.Value[colorKey & 0xf];
- }
- else
- {
- input = clut.Value[(colorKey & 0xf0) >> 4];
- }
- }
- else throw new ArgumentException($"{nameof(bit)} is {bit}, it may only be 4 or 8.");
- if (i < buffer.Count)
- buffer[i] = input;
- else break;
- i++;
- lastRow = row;
- }
- using (var tex = (Texture2D)buffer)
- using (var fs = new FileStream(string.Format(path, bit, $"{clut.Key}{(alt?"a":"")}"), FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- {
- tex.SaveAsPng(fs, textureTypeWidth, height);
- }
- if (bit > 8) break;
- }
- }
- }
- }
- private void FindOverlappingTilesClassicSpriteBatch() => (from t1 in GetTiles
- from t2 in GetTiles
- where t1.TileID < t2.TileID
- where t1.BlendMode == BlendMode.None
- where t1.Intersect(t2)
- orderby t1.TileID, t2.TileID ascending
- select new[] { t1, t2 }
- ).ForEach(x => x[1].OverLapID = checked((byte)(x[0].OverLapID + 1)));
- private static byte GetColorKeyClassicSpriteBatch(IReadOnlyList<byte> mim, int textureWidth, int startPixel, int x, int y, bool is8Bit)
- {
- if (is8Bit)
- return mim[startPixel + x + y * textureWidth];
- var tempKey = mim[startPixel + x / 2 + y * textureWidth];
- if (x % 2 == 1)
- return checked((byte)((tempKey & 0xf0) >> 4));
- return checked((byte)(tempKey & 0xf));
- }
- private List<KeyValuePair<BlendMode, Texture2D>> GetTexturesReadyToDrawClassicSpriteBatch() =>
- _textures?.OrderByDescending(kvpZ => kvpZ.Key)
- .SelectMany(kvpLayerID => kvpLayerID.Value.OrderBy(x => kvpLayerID.Key)
- .SelectMany(kvpAnimationID => kvpAnimationID.Value.OrderBy(x => kvpAnimationID.Key))
- .SelectMany(kvpAnimationState => kvpAnimationState.Value.OrderBy(x => kvpAnimationState.Key))
- .SelectMany(kvpOverlapID => kvpOverlapID.Value.OrderBy(x => kvpOverlapID.Key))
- .SelectMany(kvpBlendMode => kvpBlendMode.Value)).ToList();
- /// <summary>
- /// Gets the TextureHandler used by the tile.
- /// </summary>
- /// <param name="tile"></param>
- /// <returns>Texture handler used by tile</returns>
- private TextureHandler GetTextureUsedByTile(Tile tile)
- {
- var textureIDsPalette = _textureIDsPalettes
- ?.FirstOrDefault(x => x.Key.PaletteID == tile.PaletteID && x.Key.TextureID == tile.TextureID).Value;
- if (textureIDsPalette != null)
- return textureIDsPalette;
- var tid = _textureIDs?.FirstOrDefault(x => x.Key == tile.TextureID).Value;
- return tid;
- }
- private void LoadPalettes(byte[] mim)
- {
- var offset = /*Memory.FieldHolder.FieldID == 76 ? 0 :*/ _textureType?.BytesSkippedPalettes ?? 0;
- var cluts = GetTiles != null
- ? new Cluts(
- GetTiles.Select(x => x.PaletteID).Distinct().ToDictionary(x => x, x => new Color[ColorsPerPalette]),
- false)
- :
- new Cluts(
- Enumerable.Range(0, 16).Select(x => (byte) x)
- .ToDictionary(x => x, x => new Color[ColorsPerPalette]), false);
- using (var br = new BinaryReader(new MemoryStream(mim)))
- foreach (var clut in cluts)
- {
- var palettePointer = offset + clut.Key * BytesPerPalette;
- br.BaseStream.Seek(palettePointer, SeekOrigin.Begin);
- for (var i = 0; i < ColorsPerPalette; i++)
- clut.Value[i] = Texture_Base.ABGR1555toRGBA32bit(br.ReadUInt16());
- }
- _cluts = cluts;
- SaveCluts();
- }
- private void LoadTiles(byte[] map) => GetTiles = map == null ? default : Tiles.Load(map, _textureType.Type);
- private int LoadUpscaleBackgrounds(string path)
- {
- if (Directory.Exists(path))
- {
- var files = Directory.EnumerateFiles(path, $"*{Module.GetFieldName()}*.png", SearchOption.AllDirectories).OrderBy(x => x.Length).ThenBy(x => x, StringComparer.OrdinalIgnoreCase).ToList();
- if (files.Count > 0)
- {
- _textureIDs = new Dictionary<byte, TextureHandler>();
- var escapedName = Regex.Escape(Module.GetFieldName());
- var regex = new Regex(@".+" + escapedName + @"_(\d{1,2})\.png", RegexOptions.IgnoreCase | RegexOptions.Compiled);
- foreach (var file in files.Select(x => regex.Match(x)))
- {
- if (file.Groups.Count <= 1 || !byte.TryParse(file.Groups[1].Value, out var b)) continue;
- if (b >= 13) b -= 13;
- if (_textureIDs.ContainsKey(b)) continue;
- var alt = $"{Module.GetFieldName()}_{b + 13}.png";
- _textureIDs.Add(b, TextureHandler.CreateFromPng(File.Exists(alt) ? alt : file.Value, 256, 256, 0, true, true));
- }
- SaveSwizzled(_textureIDs.ToDictionary(x => x.Key, x => (Texture2D)x.Value));
- _textureIDsPalettes = new Dictionary<TextureIDPaletteID, TextureHandler>();
- var regex2 = new Regex(@".+" + escapedName + @"_(\d{1,2})_(\d{1,2})\.png", RegexOptions.IgnoreCase | RegexOptions.Compiled);
- foreach (var file in files.Select(x => regex2.Match(x)))
- {
- if (file.Groups.Count > 1 && byte.TryParse(file.Groups[1].Value, out var b) && byte.TryParse(file.Groups[2].Value, out var b2))
- {
- if (b >= 13) b -= 13;
- TextureIDPaletteID textureIDPaletteID;
- if (!_textureIDsPalettes.ContainsKey(textureIDPaletteID = new TextureIDPaletteID { PaletteID = b2, TextureID = b }))
- {
- var alt = $"{Module.GetFieldName()}_{b + 13}_{b2}.png";
- _textureIDsPalettes.Add(textureIDPaletteID, TextureHandler.CreateFromPng(File.Exists(alt) ? alt : file.Value, 256, 256, b2, true, true));
- }
- }
- foreach (var groups in _textureIDsPalettes.Where(x => _textureIDsPalettes.Count(y => y.Key.TextureID == x.Key.TextureID) > 1).GroupBy(x => x.Key.PaletteID))
- foreach (var kvpGroup in groups)
- {
- var textureIDs =
- groups.ToDictionary(x => x.Key.TextureID, x => (Texture2D) x.Value);
- SaveSwizzled(textureIDs, $"_{kvpGroup.Key.PaletteID}");
- break;
- }
- }
- }
- }
- var count = (_textureIDs?.Count ?? 0) + (_textureIDsPalettes?.Count ?? 0);
- return count;
- }
- //private void SaveSwizzled(string suf = "") => SaveSwizzled(TextureIDs, suf);
- private void OldSaveDeswizzled()
- {
- if (!Memory.EnableDumpingData && (!Module.Toggles.HasFlag(Toggles.DumpingData) ||
- !Module.Toggles.HasFlag(Toggles.ClassicSpriteBatch))) return;
- var fieldName = Module.GetFieldName();
- var folder = Module.GetFolder(fieldName);
- foreach (var kvpZ in _textures)
- foreach (var kvpLayer in kvpZ
- .Value)
- foreach (var kvpAnimationID
- in
- kvpLayer.Value)
- foreach (var
- kvpAnimationState in kvpAnimationID.Value)
- foreach (var kvpOverlapID in kvpAnimationState.Value)
- foreach (var kvp in kvpOverlapID.Value)
- {
- var path = Path.Combine(folder,
- $"{fieldName}_{kvpZ.Key:D4}.{kvpLayer.Key}.{kvpAnimationID.Key}.{kvpAnimationState.Key}.{kvpOverlapID.Key}.{(int) kvp.Key}.png");
- using (var fs = new FileStream(path,
- FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- kvp.Value.SaveAsPng(
- fs,
- kvp.Value.Width, kvp.Value.Height);
- }
- }
- private bool ParseBackgroundClassicSpriteBatch(byte[] mim)
- {
- if (!Module.Toggles.HasFlag(Toggles.ClassicSpriteBatch)) return true;
- if (mim == null || (GetTiles?.Count ?? 0) == 0)
- return false;
- FindOverlappingTilesClassicSpriteBatch();
- //FindSameXYTilesSource();
- var lowest = new Point(GetTiles.Min(x => x.X), GetTiles.Min(x => x.Y));
- var maximum = new Point(GetTiles.Max(x => x.X), GetTiles.Max(x => x.Y));
- Height = Math.Abs(lowest.Y) + maximum.Y + Tile.Size; //224
- Width = Math.Abs(lowest.X) + maximum.X + Tile.Size; //320
- //Color[] finalImage = new Color[height * width]; //ARGB;
- //Color[] finalOverlapImage = new Color[height * width];
- //tex = new Texture2D(Memory.graphics.GraphicsDevice, width, height);
- //texOverlap = new Texture2D(Memory.graphics.GraphicsDevice, width, height);
- IEnumerable<byte> layers = GetTiles.Select(x => x.LayerID).Distinct().OrderBy(x => x).ToArray();
- Debug.WriteLine($"FieldID: {Memory.FieldHolder.FieldID}, Layers: {layers.Count()}, ({string.Join(",", layers)}) ");
- var sortedTiles = GetTiles.OrderBy(x => x.OverLapID).ThenByDescending(x => x.Z).ThenBy(x => x.LayerID).ThenBy(x => x.AnimationID).ThenBy(x => x.AnimationState).ThenBy(x => x.BlendMode).ToList();
- if (_textures != null)
- {
- foreach (var tex in GetTexturesReadyToDrawClassicSpriteBatch().Select(x => x.Value))
- tex.Dispose();
- }
- _textures = new ConcurrentDictionary<ushort, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>>>>();
- const ushort z = 0;
- byte layerID = 0;
- byte animationID = 0;
- byte animationState = 0;
- byte overlapID = 0;
- var blendMode = BlendMode.None;
- TextureBuffer textureBuffer = null;
- var hasColor = false;
- var dictLayerID = _textures.GetOrAdd(z, new ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>>>());
- void convertColorToTexture2d()
- {
- if (!hasColor || textureBuffer == null) return;
- hasColor = false;
- var dictAnimationID =
- dictLayerID.GetOrAdd(layerID,
- new ConcurrentDictionary<byte, ConcurrentDictionary<byte,
- ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>>());
- var
- dictAnimationState = dictAnimationID.GetOrAdd(animationID,
- new ConcurrentDictionary<byte,
- ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>());
- var dictOverlapID =
- dictAnimationState.GetOrAdd(animationState,
- new ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>());
- var dictBlend =
- dictOverlapID.GetOrAdd(overlapID, new ConcurrentDictionary<BlendMode, Texture2D>());
- var tex = dictBlend.GetOrAdd(blendMode, new Texture2D(Memory.Graphics.GraphicsDevice, Width, Height));
- textureBuffer.SetData(tex);
- }
- for (var i = 0; i < sortedTiles.Count; i++)
- {
- var previousTile = i > 0 ? sortedTiles[i - 1] : null;
- var tile = sortedTiles[i];
- if (textureBuffer == null || previousTile == null || previousTile.Z != tile.Z ||
- previousTile.LayerID != tile.LayerID || previousTile.BlendMode != tile.BlendMode ||
- previousTile.AnimationID != tile.AnimationID ||
- previousTile.AnimationState != tile.AnimationState || previousTile.OverLapID != tile.OverLapID)
- {
- convertColorToTexture2d();
- textureBuffer = new TextureBuffer(Width, Height, false);
- layerID = tile.LayerID;
- blendMode = tile.BlendMode;
- animationID = tile.AnimationID;
- animationState = tile.AnimationState;
- overlapID = tile.OverLapID;
- }
- var palettePointer = _textureType.BytesSkippedPalettes + tile.PaletteID * BytesPerPalette;
- var sourceImagePointer = BytesPerPalette * _textureType.Palettes;
- const int textureWidth = 128;
- var startPixel = sourceImagePointer + tile.SourceX + textureWidth * tile.TextureID + _textureType.Width * tile.SourceY;
- var real = new Point(Math.Abs(lowest.X) + tile.X, Math.Abs(lowest.Y) + tile.Y);
- var realDestinationPixel = real.Y * Width + real.X;
-
- if (tile.Is4Bit)
- {
- startPixel -= tile.SourceX / 2;
- }
- for (var y = 0; y < Tile.Size; y++)
- for (var x = 0; x < Tile.Size; x++)
- {
- var colorKey = GetColorKeyClassicSpriteBatch(mim, _textureType.Width, startPixel, x, y, tile.Is8Bit);
- var color16Bit = BitConverter.ToUInt16(mim, 2 * colorKey + palettePointer);
- if (color16Bit == 0) // 0 is Color.TransparentBlack So we skip it.
- continue;
- var color = Texture_Base.ABGR1555toRGBA32bit(color16Bit);
- var pos = realDestinationPixel + x + y * Width;
- var bufferedColor = textureBuffer[pos];
- if (blendMode < BlendMode.None)
- {
- if (color == Color.Black)
- continue;
- if (blendMode == BlendMode.Subtract)
- {
- if (bufferedColor.A != 0)
- color = Blend2(bufferedColor, color);
- }
- else
- {
- switch (blendMode)
- {
- case BlendMode.QuarterAdd:
- color = Color.Multiply(color, .25f);
- break;
- case BlendMode.HalfAdd:
- color = Color.Multiply(color, .5f);
- break;
- case BlendMode.Add:
- break;
- case BlendMode.Subtract:
- break;
- case BlendMode.None:
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- if (bufferedColor.A != 0)
- color = Blend1(bufferedColor, color);
- }
- }
- else if (bufferedColor.A != 0)
- {
- throw new Exception("Color is already set something may be wrong.");
- }
- color.A = 0xFF;
- textureBuffer[pos] = color;
- hasColor = true;
- }
- }
- convertColorToTexture2d(); // gets leftover colors from last batch and makes a texture.
- OldSaveDeswizzled();
- return true;
- }
- /// <summary>
- /// <para>Create a swizzeled Textures with one palette.</para>
- /// <para>Few exceptions where tiles conflict and need separate files.</para>
- /// </summary>
- /// <param name="mim">Image Data .mim file</param>
- /// <returns></returns>
- private bool ParseBackgroundQuads(byte[] mim)
- {
- if (mim == null || (GetTiles?.Count ?? 0) == 0)
- return false;
- var path = Path.Combine(Memory.FF8Dir, "textures");
- var count = LoadUpscaleBackgrounds(path);
- if (count <= 0)
- {
- var uniqueSetOfTileData = GetTiles.Where(x => x.Draw).Select(x => new
- {
- x.TextureID, x.BlendMode, loc = new Point(x.SourceX, x.SourceY), x.Depth, x.PaletteID, x.AnimationID
- }).Distinct().ToList().AsReadOnly();
- Width = uniqueSetOfTileData.Max(x => x.loc.X) + Tile.Size;
- Height = uniqueSetOfTileData.Max(x => x.loc.Y) + Tile.Size;
- IReadOnlyDictionary<byte, Texture2D> textureIDs = uniqueSetOfTileData.Select(x => x.TextureID)
- .Distinct()
- .ToDictionary(x => x,
- x => Memory.Graphics != null ? new Texture2D(Memory.Graphics.GraphicsDevice, 256, 256) : null);
- IReadOnlyDictionary<byte, HashSet<byte>> overlap = GetTiles.Select(x => x.TextureID).Distinct()
- .ToDictionary(x => x, x => new HashSet<byte>());
- using (var br = new BinaryReader(new MemoryStream(mim)))
- {
- foreach (var kvp in textureIDs)
- {
- GenTexture(kvp.Key, kvp.Value);
- }
- SaveSwizzled(textureIDs);
- var fieldName = Module.GetFieldName();
- _textureIDs = textureIDs.ToDictionary(x => x.Key,
- x => TextureHandler.Create($"{fieldName}_{x.Key}", new Texture2DWrapper(x.Value),
- ushort.MaxValue));
- if (overlap.Any(x => x.Value.Count > 1))
- {
- IReadOnlyDictionary<TextureIDPaletteID, Texture2D> textureIDsPalettes = uniqueSetOfTileData
- .Where(x => overlap[x.TextureID].Contains(x.PaletteID))
- .Select(x => new TextureIDPaletteID {TextureID = x.TextureID, PaletteID = x.PaletteID})
- .Distinct().ToDictionary(x => x,
- x => new Texture2D(Memory.Graphics.GraphicsDevice, 256, 256));
- _textureIDsPalettes = textureIDsPalettes.ToDictionary(x => x.Key,
- x => TextureHandler.Create($"{fieldName}_{x.Key.TextureID}", new Texture2DWrapper(x.Value),
- x.Key.PaletteID));
- foreach (var kvp in textureIDsPalettes)
- {
- GenTexture(kvp.Key.TextureID, kvp.Value, kvp.Key.PaletteID);
- }
- foreach (var groups in
- textureIDsPalettes
- .Where(x => textureIDsPalettes.Count(y => y.Key.TextureID == x.Key.TextureID) > 1)
- .GroupBy(x => x.Key.PaletteID))
- foreach (var kvpGroup in groups)
- {
- textureIDs =
- groups.ToDictionary(x => x.Key.TextureID, x => x.Value);
- SaveSwizzled(textureIDs, $"_{kvpGroup.Key.PaletteID}");
- break;
- }
- }
- void GenTexture(byte texID, Texture2D tex2d, byte? inPaletteID = null)
- {
- if (tex2d == null) return;
- var tex = new TextureBuffer(tex2d.Width, tex2d.Height, false);
- foreach (var tile in uniqueSetOfTileData.Where(x =>
- x.TextureID == texID && (!inPaletteID.HasValue || inPaletteID.Value == x.PaletteID)))
- {
- var is4Bit = Tile.Test4Bit(tile.Depth);
- var is8Bit = Tile.Test8Bit(tile.Depth);
- var is16Bit = Tile.Test16Bit(tile.Depth);
- byte colorKey = 0;
- foreach (var p in from x in Enumerable.Range(0, Tile.Size)
- from y in Enumerable.Range(0, Tile.Size)
- orderby y, x
- select new Point(x, y))
- {
- (var trueX, var trueY) = (tile.loc.X+p.X, tile.loc.Y + p.Y);
- var offsetX = is16Bit
- ? trueX * 2
- : trueX / (is4Bit ? 2 : 1);
- var texturePageOffset = TexturePageWidth * tile.TextureID;
- var offsetY = _textureType.Width * trueY ;
- var offset = _textureType.PaletteSectionSize +
- offsetX +
- texturePageOffset +
- offsetY;
- br.BaseStream.Seek(offset, SeekOrigin.Begin);
- var point = new Point(p.X + tile.loc.X, p.Y + tile.loc.Y);
- var paletteID = tile.PaletteID;
- Color input = default;
- if (is8Bit)
- {
- input = _cluts[paletteID][br.ReadByte()];
- }
- else if (is16Bit)
- {
- input = Texture_Base.ABGR1555toRGBA32bit(br.ReadUInt16());
- }
- else if (is4Bit)
- {
- if (p.X % 2 == 0)
- {
- colorKey = br.ReadByte();
- input = _cluts[paletteID][colorKey & 0xf];
- }
- else
- {
- input = _cluts[paletteID][(colorKey & 0xf0) >> 4];
- }
- }
- if (!inPaletteID.HasValue)
- // forcing a palette happens post overlap test. So shouldn't need to rerun test.
- {
- var current = tex[point.X, point.Y];
- var output = ChangeColor(current, input, point, tile.TextureID, overlap);
- if (!output.HasValue) break;
- if (output.Value.A != 0)
- tex[point.X, point.Y] = output.Value;
- }
- else
- {
- if (input.A != 0)
- tex[point.X, point.Y] = input;
- }
- }
- }
- tex.SetData(tex2d);
- }
- }
- }
- else
- {
- Debug.WriteLine($"Loaded {count} Textures from {path}");
- }
- //the sort here should be the default draw order. May need changed.
- _quads = GetTiles.Select(x => new TileQuadTexture(x, GetTextureUsedByTile(x), 1f)).Where(x => x.Enabled)
- .OrderByDescending(x => x.GetTile.Z)
- .ThenByDescending(x => x.GetTile.TileID)
- .ThenBy(x => x.GetTile.LayerID)
- .ThenBy(x => x.GetTile.AnimationID)
- .ThenBy(x => x.GetTile.AnimationState)
- .ThenByDescending(x => x.GetTile.BlendMode).ToList();
- _animations = _quads.Where(x => x.AnimationID != 0xFF).Select(x => x.AnimationID).Distinct().ToDictionary(x => x, x => _quads.Where(y => y.AnimationID == x).OrderBy(y => y.AnimationState).ToList());
- _animations.ForEach(x => x.Value.Where(y => y.AnimationState != 0).ForEach(w => w.Hide()));
- TotalTime = TimeSpan.FromMilliseconds(1000f / 10f);
- CurrentTime = TimeSpan.Zero;
- return true;
- }
- private void SaveCluts()
- {
- if (Memory.EnableDumpingData || Module.Toggles.HasFlag(Toggles.DumpingData))
- {
- var path = Path.Combine(Module.GetFolder(),
- $"{Module.GetFieldName()}_Clut.png");
- _cluts.Save(path);
- }
- }
- private static void SaveSwizzled(IReadOnlyDictionary<byte, Texture2D> textureIDs, string suf = "")
- {
- if (!Memory.EnableDumpingData && !Module.Toggles.HasFlag(Toggles.DumpingData)) return;
- var fieldName = Module.GetFieldName();
- var folder = Module.GetFolder(fieldName);
- foreach (var kvp in textureIDs)
- {
- var path = Path.Combine(folder,
- $"{fieldName}_{kvp.Key}{suf}.png");
- if (File.Exists(path))
- continue;
- using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
- { kvp.Value.SaveAsPng(fs, kvp.Value.Width, kvp.Value.Height); }
- }
- }
- #endregion Methods
- // TODO: uncomment the following line if the finalizer is overridden above.// GC.SuppressFinalize(this);
- }
- }
|