Background.cs 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Text.RegularExpressions;
  11. namespace OpenVIII.Fields
  12. {
  13. /// <summary>
  14. /// Background Tiles for field
  15. /// </summary>
  16. /// <see cref="https://github.com/myst6re/deling/blob/master/files/BackgroundFile.cpp"/>
  17. /// <seealso cref="https://github.com/myst6re/deling/blob/master/files/BackgroundFile.h"/>
  18. /// <seealso cref="http://wiki.ffrtt.ru/index.php?title=FF8/FileFormat_MAP"/>
  19. /// <seealso cref="http://wiki.ffrtt.ru/index.php?title=FF8/FileFormat_MIM"/>
  20. /// <seealso cref="http://forums.qhimm.com/index.php?topic=13444.msg264595#msg264595"/>
  21. /// <seealso cref="http://forums.qhimm.com/index.php?topic=13444.0"/>
  22. public partial class Background : IDisposable
  23. {
  24. #region Fields
  25. private const int BytesPerPalette = 2 * ColorsPerPalette;
  26. private const int ColorsPerPalette = 256;
  27. /// <summary>
  28. /// 4 bit has 2 columns per every byte so it expands to twice the width.
  29. /// </summary>
  30. private const int FourBitTexturePageWidth = 2 * TexturePageWidth;
  31. /// <summary>
  32. /// Standard texture page width.
  33. /// </summary>
  34. private const int TexturePageWidth = 128;
  35. private Dictionary<byte, List<TileQuadTexture>> _animations;
  36. private AlphaTestEffect _ate;
  37. private Vector3 _camPosition;
  38. private Vector3 _camTarget;
  39. /// <summary>
  40. /// Palettes/Color Lookup Tables
  41. /// </summary>
  42. private Cluts _cluts;
  43. public float Degrees;
  44. private bool _disposedValue;
  45. private BasicEffect _effect;
  46. private FPS_Camera _fpsCamera;
  47. private Rectangle _outputDims;
  48. private Matrix _projectionMatrix, _viewMatrix, _worldMatrix;
  49. private List<TileQuadTexture> _quads;
  50. private Dictionary<byte, TextureHandler> _textureIDs;
  51. private Dictionary<TextureIDPaletteID, TextureHandler> _textureIDsPalettes;
  52. private ConcurrentDictionary<ushort, ConcurrentDictionary<byte, ConcurrentDictionary<byte,
  53. ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>>>>
  54. _textures;
  55. private BackgroundTextureType _textureType;
  56. #endregion Fields
  57. #region Destructors
  58. // To detect redundant calls
  59. // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
  60. ~Background()
  61. {
  62. // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  63. Dispose(false);
  64. }
  65. #endregion Destructors
  66. #region Properties
  67. public TimeSpan CurrentTime { get; set; }
  68. public Tiles GetTiles { get; private set; }
  69. public bool HasSpriteBatchTexturesLoaded => GetTexturesReadyToDrawClassicSpriteBatch()?.Count > 0;
  70. public int Height { get => _outputDims.Height; private set => _outputDims.Height = value; }
  71. /*
  72. public bool Is4Bit => GetTiles?.Any(x => x.Is4Bit) ?? false;
  73. public bool IsAddBlendMode => GetTiles?.Any(x => x.BlendMode == BlendMode.add) ?? false;
  74. public bool IsHalfBlendMode => GetTiles?.Any(x => x.BlendMode == BlendMode.halfadd) ?? false;
  75. public bool IsQuarterBlendMode => GetTiles?.Any(x => x.BlendMode == BlendMode.quarteradd) ?? false;
  76. public bool IsSubtractBlendMode => GetTiles?.Any(x => x.BlendMode == BlendMode.subtract) ?? false;
  77. */
  78. public Vector3 MouseLocation { get; set; }
  79. public TimeSpan TotalTime { get; set; }
  80. public int Width { get => _outputDims.Width; private set => _outputDims.Width = value; }
  81. #endregion Properties
  82. #region Methods
  83. public static Background Load(byte[] mim, byte[] map)
  84. {
  85. if (mim == null || map == null)
  86. return null;
  87. var r = new Background
  88. {
  89. _textureType = BackgroundTextureType.GetTextureType(mim),
  90. _camTarget = Vector3.Zero,
  91. _camPosition = new Vector3(0f, 0f, -10f),
  92. _fpsCamera = new FPS_Camera(),
  93. Degrees = 90f
  94. };
  95. if (Memory.Graphics != null)
  96. {
  97. r._ate = new AlphaTestEffect(Memory.Graphics.GraphicsDevice);
  98. r._effect = new BasicEffect(Memory.Graphics.GraphicsDevice);
  99. }
  100. r._worldMatrix = Matrix.CreateWorld(r._camPosition, Vector3.
  101. Forward, Vector3.Up);
  102. r._viewMatrix = Matrix.CreateLookAt(r._camPosition, r._camTarget,
  103. Vector3.Up);
  104. r.LoadTiles(map);
  105. r.LoadPalettes(mim);
  106. r.DumpRawTexture(mim);
  107. var watch = Stopwatch.StartNew();
  108. try
  109. {
  110. if (!r.ParseBackgroundQuads(mim))
  111. {
  112. return null;
  113. }
  114. }
  115. finally
  116. {
  117. watch.Stop();
  118. Debug.WriteLine($"{nameof(ParseBackgroundQuads)} took {watch.ElapsedMilliseconds / 1000f} seconds.");
  119. }
  120. try
  121. {
  122. if (!r.ParseBackgroundClassicSpriteBatch(mim))
  123. {
  124. return null;
  125. }
  126. }
  127. finally
  128. {
  129. watch.Stop();
  130. Debug.WriteLine($"{nameof(ParseBackgroundClassicSpriteBatch)} took {watch.ElapsedMilliseconds / 1000f} seconds.");
  131. }
  132. return r;
  133. }
  134. public void Deswizzle()
  135. {
  136. using (var mask = new Texture2D(Memory.Graphics.GraphicsDevice, 4, 4))
  137. {
  138. mask.SetData(Enumerable.Range(0, 16).Select(x => Color.White).ToArray());
  139. var fieldName = Module.GetFieldName();
  140. var folder = Module.GetFolder(fieldName, "deswizzle");
  141. var scale = _quads[0].Texture.ScaleFactor;
  142. var tilesWidth = (int)(GetTiles.Width * scale.X);
  143. var tilesHeight = (int)(GetTiles.Height * scale.Y);
  144. //Matrix backup = projectionMatrix;
  145. //projectionMatrix = Matrix.CreateOrthographic(tiles.Width, tiles.Height, 0f, 100f);
  146. GetTiles.UniquePupuIDs();// make sure each layer has it's own ID.
  147. foreach (var pupuGroup in _quads.GroupBy(x => x.GetTile.PupuID)
  148. ) //group the quads by their pupu ID.
  149. {
  150. using (var outTex = new RenderTarget2D(Memory.Graphics.GraphicsDevice, tilesWidth, tilesHeight))
  151. {
  152. //start drawing
  153. Memory.Graphics.GraphicsDevice.SetRenderTarget(outTex);
  154. Memory.Graphics.GraphicsDevice.Clear(Color.TransparentBlack);
  155. Memory.SpriteBatchStartAlpha();
  156. foreach (var quad in pupuGroup)
  157. {
  158. var tile = (Tile)quad;
  159. //DrawBackgroundQuadsStart();
  160. //DrawBackgroundQuad(quad, true);
  161. var dst = tile.GetRectangle;
  162. dst.Offset(Math.Abs(GetTiles.TopLeft.X), Math.Abs(GetTiles.TopLeft.Y));
  163. var src = tile.Source;
  164. //src = src.Scale(scale);
  165. dst = dst.Scale(scale);
  166. quad.Texture.Draw(dst, src, Color.White);
  167. }
  168. Memory.SpriteBatchEnd();
  169. //end drawing
  170. Memory.Graphics.GraphicsDevice.SetRenderTarget(null);
  171. //set path
  172. var path = Path.Combine(folder,
  173. $"{fieldName}_{pupuGroup.Key:X8}.png");
  174. //save image.
  175. using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
  176. {
  177. outTex.SaveAsPng(fs, tilesWidth, tilesHeight);
  178. }
  179. }
  180. using (var outTex = new RenderTarget2D(Memory.Graphics.GraphicsDevice, tilesWidth, tilesHeight))
  181. {
  182. //start drawing
  183. Memory.Graphics.GraphicsDevice.SetRenderTarget(outTex);
  184. Memory.Graphics.GraphicsDevice.Clear(Color.Black);
  185. Memory.SpriteBatchStartAlpha();
  186. var src = new Rectangle(0, 0, 4, 4);
  187. foreach (var quad in pupuGroup)
  188. {
  189. var tile = (Tile)quad;
  190. //DrawBackgroundQuadsStart();
  191. //DrawBackgroundQuad(quad, true);
  192. var dst = tile.GetRectangle;
  193. dst.Offset(Math.Abs(GetTiles.TopLeft.X), Math.Abs(GetTiles.TopLeft.Y));
  194. //src = src.Scale(scale);
  195. dst = dst.Scale(scale);
  196. Memory.SpriteBatch.Draw(mask, dst, src, Color.White);
  197. }
  198. Memory.SpriteBatchEnd();
  199. //end drawing
  200. Memory.Graphics.GraphicsDevice.SetRenderTarget(null);
  201. //set path
  202. var path = Path.Combine(folder,
  203. $"{fieldName}_{pupuGroup.Key:X8}_MASK.png");
  204. //save image.
  205. using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
  206. {
  207. outTex.SaveAsPng(fs, tilesWidth, tilesHeight);
  208. }
  209. }
  210. }
  211. Process.Start(folder);
  212. //projectionMatrix = backup;
  213. }
  214. }
  215. // This code added to correctly implement the disposable pattern.
  216. public void Dispose() =>
  217. // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  218. Dispose(true);
  219. public void Draw()
  220. {
  221. //Memory.spriteBatch.GraphicsDevice.Clear(Color.Black);
  222. DrawBackgroundQuads();
  223. DrawWalkMesh();
  224. DrawSpriteBatch();
  225. }
  226. public void Reswizzle()
  227. {
  228. GetTiles.UniquePupuIDs();// make sure each layer has it's own ID.
  229. var fieldName = Module.GetFieldName();
  230. var folder = Module.GetFolder(fieldName, "deswizzle"); //goes from deswizzle folder
  231. if (!Directory.Exists(folder)) return;
  232. IEnumerable<string> files = Directory.EnumerateFiles(folder, "*.png").ToArray();
  233. folder = Module.GetFolder(fieldName, "reswizzle");
  234. var overlap = GetTiles.Select(x => x.TextureID).Distinct().ToDictionary(x => x, x => new HashSet<byte>());
  235. var texIDs = new ConcurrentDictionary<byte, TextureBuffer>();
  236. var texIDsPalette = new ConcurrentDictionary<TextureIDPaletteID, TextureBuffer>();
  237. var width = 0; var height = 0;
  238. //Vector2 origin = tiles.Origin;
  239. var lowest = GetTiles.TopLeft;
  240. 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);
  241. var re = new Regex(@".+_([0-9A-F]{8}).png", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  242. process();
  243. if (overlap.Any(x => x.Value.Count > 1))
  244. process(true);
  245. void process(bool doOverLap = false)
  246. {
  247. foreach (var file in files)
  248. {
  249. //Point highest = new Point(tiles.Max(x => x.X), tiles.Max(x => x.Y));
  250. var match = re.Match(file);
  251. if (match.Groups.Count > 1 && uint.TryParse(match.Groups[1].Value, NumberStyles.HexNumber,
  252. CultureInfo.InvariantCulture, out var pupuID))
  253. {
  254. using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
  255. using (var tex = Texture2D.FromStream(Memory.Graphics.GraphicsDevice, fs))
  256. {
  257. var scale = new Vector2(tex.Width, tex.Height) / size;
  258. width = (int)(256 * scale.X);
  259. height = (int)(256 * scale.Y);
  260. var inTex = new TextureBuffer(tex.Width, tex.Height);
  261. inTex.GetData(tex);
  262. foreach (var quad in _quads.Where(x =>
  263. x.GetTile.PupuID == pupuID &&
  264. (!doOverLap || overlap[x.GetTile.TextureID].Contains(x.GetTile.PaletteID))))
  265. {
  266. var tile = (Tile)quad;
  267. texIDs.TryAdd(tile.TextureID, new TextureBuffer(width, height, false));
  268. var src = (new Vector2(Math.Abs(lowest.X) + tile.X, Math.Abs(lowest.Y) + tile.Y) * scale).ToPoint();
  269. var dst = (new Vector2(tile.SourceX, tile.SourceY) * scale).ToPoint();
  270. if (!doOverLap)
  271. foreach (var p in from x in Enumerable.Range(0, (int)(Tile.Size * scale.X))
  272. from y in Enumerable.Range(0, (int)(Tile.Size * scale.Y))
  273. orderby y, x
  274. select new Point(x, y))
  275. {
  276. var input = inTex[src.X + p.X, src.Y + p.Y];
  277. var current = texIDs[tile.TextureID][dst.X + p.X, dst.Y + p.Y];
  278. var unscaledLocation = tile.Source.Location;
  279. unscaledLocation.Offset(p.ToVector2() / scale);
  280. var output = ChangeColor(current, input, unscaledLocation, tile.TextureID, overlap);
  281. if (!output.HasValue) break;
  282. if (output.Value.A != 0)
  283. texIDs[tile.TextureID][dst.X + p.X, dst.Y + p.Y] = output.Value;
  284. }
  285. else if (overlap[tile.TextureID].Count > 1)
  286. {
  287. var key = new TextureIDPaletteID { PaletteID = tile.PaletteID, TextureID = tile.TextureID };
  288. texIDsPalette.TryAdd(key, new TextureBuffer(width, height));
  289. foreach (var p in from x in Enumerable.Range(0, (int)(Tile.Size * scale.X))
  290. from y in Enumerable.Range(0, (int)(Tile.Size * scale.Y))
  291. select new Point(x, y))
  292. {
  293. var input = inTex[src.X + p.X, src.Y + p.Y];
  294. if (input.A != 0)
  295. {
  296. texIDsPalette[key][dst.X + p.X, dst.Y + p.Y] = inTex[src.X + p.X, src.Y + p.Y];
  297. }
  298. }
  299. }
  300. }
  301. }
  302. }
  303. }
  304. }
  305. //save new reswizzles
  306. foreach (var tid in texIDs)
  307. {
  308. var path = Path.Combine(folder,
  309. $"{fieldName}_{tid.Key}.png");
  310. //save image.
  311. using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
  312. {
  313. using (var outTex = (Texture2D)tid.Value)
  314. outTex.SaveAsPng(fs, width, height);
  315. }
  316. }
  317. foreach (var tid in texIDsPalette)
  318. {
  319. var path = Path.Combine(folder,
  320. $"{fieldName}_{tid.Key.TextureID}_{tid.Key.PaletteID}.png");
  321. //save image.
  322. using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
  323. {
  324. using (var outTex = (Texture2D)tid.Value)
  325. outTex.SaveAsPng(fs, width, height);
  326. }
  327. }
  328. Process.Start(folder);
  329. }
  330. public Tiles TilesUnderMouse() => new Tiles(GetTiles.Where(x =>
  331. x.X < MouseLocation.X && x.X + 16 > MouseLocation.X &&
  332. x.Y < MouseLocation.Y && x.Y + 16 > MouseLocation.Y).ToList());
  333. public void Update()
  334. {
  335. if ((CurrentTime += Memory.ElapsedGameTime) > TotalTime)
  336. {
  337. CurrentTime = TimeSpan.Zero;
  338. foreach (var a in _animations)
  339. {
  340. int i = a.Value.FirstOrDefault(x => x.Enabled)?.AnimationState ?? 0;
  341. int max = a.Value.Max(k => k.AnimationState);
  342. var i1 = i;
  343. a.Value.Where(x => x.AnimationState == i1).ForEach(x => x.Hide());
  344. if (++i >= max)
  345. i = 0;
  346. a.Value.Where(x => x.AnimationState == i).ForEach(x => x.Show());
  347. }
  348. }
  349. float tilesWidth = GetTiles.Width;
  350. float tilesHeight = GetTiles.Height;
  351. if (Module.Toggles.HasFlag(Toggles.Perspective)) //perspective mode shows gabs in the tiles.
  352. {
  353. //finds the min zoom out to fit the entire image in frame.
  354. var half = new Vector2(tilesWidth / 2f, tilesHeight / 2f);
  355. var fieldOfView = MathHelper.ToRadians(70);
  356. float getOppositeSide(float side, float angle)
  357. {
  358. return (float)(Math.Tan(angle) * side);
  359. }
  360. half.X = getOppositeSide(half.X, MathHelper.ToRadians(45));
  361. half.Y = getOppositeSide(half.Y, MathHelper.ToRadians(45));
  362. var minDistanceFromBG = -Math.Max(half.X, half.Y);
  363. if (_camPosition.Z > minDistanceFromBG)
  364. _camPosition.Z = minDistanceFromBG;
  365. _projectionMatrix = Matrix.CreatePerspectiveFieldOfView(fieldOfView, Memory.Graphics.GraphicsDevice.Viewport.AspectRatio, float.Epsilon, 1000f);
  366. _viewMatrix = !Module.Toggles.HasFlag(Toggles.Menu)
  367. ? _fpsCamera.Update(ref _camPosition, ref _camTarget, ref Degrees)
  368. : Matrix.CreateLookAt(_camPosition, _camTarget, Vector3.Up);
  369. }
  370. else
  371. {
  372. var vp = Memory.Graphics.GraphicsDevice.Viewport;
  373. var scale = Memory.Scale(tilesWidth, tilesHeight, ScaleMode.FitBoth);
  374. _projectionMatrix = Matrix.CreateOrthographic(vp.Width / scale.X, vp.Height / scale.Y, 0f, 100f);
  375. _viewMatrix = Matrix.CreateLookAt(Vector3.Forward * 10f, Vector3.Zero, Vector3.Up);
  376. }
  377. var ml = InputMouse.Location.ToVector2();
  378. var ml3d = Memory.Graphics.GraphicsDevice.Viewport.Unproject(ml.ToVector3(), _projectionMatrix, _viewMatrix, _worldMatrix);
  379. ml3d.Y *= -1;
  380. MouseLocation = ml3d;
  381. }
  382. protected virtual void Dispose(bool disposing)
  383. {
  384. if (_disposedValue) return;
  385. if (disposing)
  386. {
  387. // TODO: dispose managed state (managed objects).
  388. }
  389. _textureIDs?.ForEach(x => x.Value?.Dispose());
  390. _textureIDsPalettes?.ForEach(x => x.Value?.Dispose());
  391. // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
  392. // TODO: set large fields to null.
  393. _disposedValue = true;
  394. }
  395. private static Color Blend1(Color baseColor, Color color)
  396. {//ClassicSpriteBatch
  397. var r = new Color
  398. {
  399. R = (byte) MathHelper.Clamp(baseColor.R + color.R, 0, 255),
  400. G = (byte) MathHelper.Clamp(baseColor.G + color.G, 0, 255),
  401. B = (byte) MathHelper.Clamp(baseColor.B + color.B, 0, 255),
  402. A = 0xFF
  403. };
  404. return r;
  405. }
  406. private static Color Blend2(Color baseColor, Color color)
  407. {//ClassicSpriteBatch
  408. var r = new Color
  409. {
  410. R = (byte) MathHelper.Clamp(baseColor.R - color.R, 0, 255),
  411. G = (byte) MathHelper.Clamp(baseColor.G - color.G, 0, 255),
  412. B = (byte) MathHelper.Clamp(baseColor.B - color.B, 0, 255),
  413. A = 0xFF
  414. };
  415. return r;
  416. }
  417. private Color? ChangeColor(Color current, Color input, Point p, byte textureID, IReadOnlyDictionary<byte, HashSet<byte>> overlap)
  418. {
  419. if (input.A == 0) return Color.TransparentBlack;
  420. if (current.A == 0 || current == input)
  421. return input;
  422. IEnumerable<byte> o = (from tile in GetTiles
  423. // ReSharper disable once ImplicitlyCapturedClosure
  424. where tile.TextureID.Equals(textureID) && tile.ExpandedSource.Contains(p)
  425. select tile.PaletteID).Distinct().ToArray();
  426. if (o.Count() <= 1) return input; // two tiles same palette is drawing to same place
  427. o.ForEach(x => overlap[textureID].Add(x));
  428. return null;
  429. }
  430. private void DrawBackgroundQuad(TileQuadTexture quad, bool forceBlendModeNone = false, Vector2 scale2 = default)
  431. {
  432. VertexPositionTexture[] temp = quad;
  433. var tile = (Tile)quad;
  434. _ate.Texture = quad;
  435. if (scale2 != default)
  436. {
  437. temp = (VertexPositionTexture[])temp.Clone();
  438. var scale = Matrix.CreateScale(scale2.ToVector3());
  439. for (var i = 0; i < temp.Length; i++)
  440. {
  441. var t = temp[i];
  442. t.Position = Vector3.Transform(temp[i].Position, scale);
  443. temp[i] = t;
  444. }
  445. }
  446. DrawBackgroundQuadsSetBendMode(forceBlendModeNone ? BlendMode.None : tile.BlendMode);
  447. foreach (var pass in _ate.CurrentTechnique.Passes)
  448. {
  449. pass.Apply();
  450. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(primitiveType: PrimitiveType.TriangleList,
  451. vertexData: temp, vertexOffset: 0, primitiveCount: 2);
  452. }
  453. }
  454. private void DrawBackgroundQuads()
  455. {
  456. if (!Module.Toggles.HasFlag(Toggles.Quad)) return;
  457. DrawBackgroundQuadsStart();
  458. foreach (var quad in _quads.Where(x => x.Enabled))
  459. {
  460. DrawBackgroundQuad(quad);
  461. }
  462. }
  463. private void DrawBackgroundQuadsSetBendMode(BlendMode bm)
  464. {
  465. _ate.Alpha = 1f;
  466. var half = new Color(.5f, .5f, .5f, 1f);
  467. var quarter = new Color(.25f, .25f, .25f, 1f);
  468. var full = Color.White;
  469. switch (bm)
  470. {
  471. //If we deswizzled and merged the (BlendModes != BlendMode.none) tiles
  472. // we can change SamplerState to Anisotropic.
  473. //But swizzled textures are a Texture Atlas so it will draw bad pixels from near by.
  474. default:
  475. Memory.Graphics.GraphicsDevice.BlendFactor = full;
  476. Memory.Graphics.GraphicsDevice.BlendState = BlendState.AlphaBlend;
  477. //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  478. break;
  479. case BlendMode.Add:
  480. Memory.Graphics.GraphicsDevice.BlendFactor = full;
  481. Memory.Graphics.GraphicsDevice.BlendState = Memory.BlendStateAdd;
  482. //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  483. break;
  484. case BlendMode.Subtract:
  485. Memory.Graphics.GraphicsDevice.BlendFactor = full;
  486. Memory.Graphics.GraphicsDevice.BlendState = Memory.BlendStateSubtract;
  487. _ate.Alpha = .85f; //doesn't darken so much.
  488. //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  489. break;
  490. case BlendMode.HalfAdd:
  491. Memory.Graphics.GraphicsDevice.BlendFactor = half;
  492. Memory.Graphics.GraphicsDevice.BlendState = Memory.BlendStateAddBlendFactor;
  493. //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  494. break;
  495. case BlendMode.QuarterAdd:
  496. Memory.Graphics.GraphicsDevice.BlendFactor = quarter;
  497. Memory.Graphics.GraphicsDevice.BlendState = Memory.BlendStateAddBlendFactor;
  498. //Memory.graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  499. break;
  500. }
  501. }
  502. private void DrawBackgroundQuadsStart()
  503. {
  504. Memory.Graphics.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
  505. Memory.Graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  506. _ate.Projection = _projectionMatrix; _ate.View = _viewMatrix; _ate.World = _worldMatrix;
  507. _effect.Projection = _projectionMatrix; _effect.View = _viewMatrix; _effect.World = _worldMatrix;
  508. Memory.Graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
  509. _effect.TextureEnabled = true;
  510. _ate.VertexColorEnabled = false;
  511. _effect.VertexColorEnabled = false;
  512. }
  513. private void DrawSpriteBatch()
  514. {
  515. if (!Module.Toggles.HasFlag(Toggles.ClassicSpriteBatch)) return;
  516. var drawTextures = GetTexturesReadyToDrawClassicSpriteBatch();
  517. var open = false;
  518. var lastBlendMode = BlendMode.None;
  519. var alpha = 1f;
  520. if (drawTextures != null)
  521. foreach (var kvp in drawTextures)
  522. {
  523. if (!open || lastBlendMode != kvp.Key)
  524. {
  525. if (open)
  526. Memory.SpriteBatchEnd();
  527. open = true;
  528. alpha = 1f;
  529. switch (kvp.Key)
  530. {
  531. default:
  532. Memory.SpriteBatchStartAlpha();
  533. break;
  534. case BlendMode.HalfAdd:
  535. Memory.SpriteBatchStart(bs: Memory.BlendStateAdd, ss: SamplerState.AnisotropicClamp);
  536. break;
  537. case BlendMode.QuarterAdd:
  538. Memory.SpriteBatchStart(bs: Memory.BlendStateAdd, ss: SamplerState.AnisotropicClamp);
  539. break;
  540. case BlendMode.Add:
  541. Memory.SpriteBatchStart(bs: Memory.BlendStateAdd, ss: SamplerState.AnisotropicClamp);
  542. break;
  543. case BlendMode.Subtract:
  544. alpha = .9f;
  545. Memory.SpriteBatchStart(bs: Memory.BlendStateSubtract, ss: SamplerState.AnisotropicClamp);
  546. break;
  547. }
  548. lastBlendMode = kvp.Key;
  549. }
  550. var tex = kvp.Value;
  551. var src = new Rectangle(0, 0, tex.Width, tex.Height);
  552. var dst = src;
  553. dst.Size = (dst.Size.ToVector2() * Memory.Scale(tex.Width, tex.Height, ScaleMode.FitBoth)).ToPoint();
  554. //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.
  555. //I setup scaling after noticing the field didn't size with the screen. I set it to center on screen.
  556. dst.Offset(Memory.Center.X - dst.Center.X, Memory.Center.Y - dst.Center.Y);
  557. Memory.SpriteBatch.Draw(tex, dst, src, Color.White * alpha);
  558. //new Microsoft.Xna.Framework.Rectangle(0, 0, 1280 + (width - 320), 720 + (height - 224)),
  559. //new Microsoft.Xna.Framework.Rectangle(0, 0, tex.Width, tex.Height)
  560. }
  561. if (open)
  562. Memory.SpriteBatchEnd();
  563. }
  564. private void DrawWalkMesh()
  565. {//todo move into walk mesh class. was only because at the time I thought i'd need the background data.
  566. if (!Module.Toggles.HasFlag(Toggles.WalkMesh)) return;
  567. _effect.TextureEnabled = false;
  568. Memory.Graphics.GraphicsDevice.BlendFactor = Color.White;
  569. Memory.Graphics.GraphicsDevice.BlendState = BlendState.Opaque;
  570. Memory.Graphics.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
  571. //using (DepthStencilState depthStencilState = new DepthStencilState() { DepthBufferEnable = true })
  572. using (var rasterizerState = new RasterizerState() { CullMode = CullMode.None })
  573. {
  574. Memory.Graphics.GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;//depthStencilState;
  575. Memory.Graphics.GraphicsDevice.RasterizerState = rasterizerState;
  576. _ate.Texture = null;
  577. _ate.VertexColorEnabled = true;
  578. _effect.VertexColorEnabled = true;
  579. //camPosition = Module.Cameras[0].Position;
  580. _effect.World = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up);// Module.Cameras[0].CreateWorld();
  581. var fieldOfView = MathHelper.ToRadians(70);
  582. //effect.View = //Module.Cameras[0].CreateLookAt();
  583. //effect.Projection = Module.Cameras[0].CreateProjection();
  584. _effect.Projection = Matrix.CreatePerspectiveFieldOfView(fieldOfView,
  585. Memory.Graphics.GraphicsDevice.Viewport.AspectRatio, float.Epsilon, 1000f);
  586. _effect.View = !Module.Toggles.HasFlag(Toggles.Menu)
  587. ? _fpsCamera.Update(ref _camPosition, ref _camTarget, ref Degrees)
  588. : Matrix.CreateLookAt(_camPosition, _camTarget, Vector3.Up);
  589. foreach (var pass in _effect.CurrentTechnique.Passes)
  590. {
  591. pass.Apply();
  592. Memory.Graphics.GraphicsDevice.DrawUserPrimitives(primitiveType: PrimitiveType.TriangleList,
  593. vertexData: Module.WalkMesh.Vertices.ToArray(), vertexOffset: 0, primitiveCount: Module.WalkMesh.Count);
  594. }
  595. }
  596. }
  597. private void DumpRawTexture(byte[] mim)
  598. {
  599. if (!Memory.EnableDumpingData && !Module.Toggles.HasFlag(Toggles.DumpingData)) return;
  600. MemoryStream ms;
  601. var path = Path.Combine(Module.GetFolder(),
  602. $"{Module.GetFieldName()}_raw_{{0}}bit_{{1}}.png");
  603. using (var br = new BinaryReader(ms = new MemoryStream(mim)))
  604. {
  605. long startPixel = _textureType.PaletteSectionSize;
  606. process(8);
  607. process(4);
  608. process(16);
  609. process(24);
  610. process(24,true);
  611. void process(byte bit, bool alt = false)
  612. {
  613. var adj = bit / 8f;
  614. var textureTypeWidth = bit == 24? _textureType.Width :(int)(_textureType.Width / adj);
  615. var height = checked((int)Math.Ceiling(((float)mim.Length - _textureType.PaletteSectionSize) / _textureType.Width / (bit == 24 ? adj : 1f)));
  616. if (bit == 24 && alt)
  617. {
  618. textureTypeWidth = (int)Math.Ceiling(_textureType.Width / adj);
  619. height *= (int)adj;
  620. }
  621. var buffer = new TextureBuffer(textureTypeWidth, height, false);
  622. foreach (var clut in _cluts)
  623. {
  624. ms.Seek(startPixel, SeekOrigin.Begin);
  625. var i = 0;
  626. byte colorKey = 0;
  627. var lastRow = 0;
  628. while (ms.Position + Math.Ceiling(adj) < ms.Length)
  629. {
  630. var row = i / textureTypeWidth;
  631. Color input;
  632. if (bit == 24) //just to see if anything is there. don't think there is a real usage of 24 bit.
  633. {
  634. if (alt && lastRow != row && row % 3 ==0) i++;
  635. //i += 1;
  636. input = new Color
  637. {
  638. B = br.ReadByte(),
  639. G = br.ReadByte(),
  640. R = br.ReadByte(),
  641. A = 0xFF,
  642. };
  643. }
  644. else if (bit == 16)
  645. {
  646. //i += 1;
  647. input = Texture_Base.ABGR1555toRGBA32bit(br.ReadUInt16());
  648. }
  649. else if (bit == 8)
  650. {
  651. //i = checked((int)(ms.Position - startPixel));
  652. colorKey = br.ReadByte();
  653. input = clut.Value[colorKey];
  654. }
  655. else if (bit == 4)
  656. {
  657. //i++;
  658. if (i % 2 == 0)
  659. {
  660. colorKey = br.ReadByte();
  661. input = clut.Value[colorKey & 0xf];
  662. }
  663. else
  664. {
  665. input = clut.Value[(colorKey & 0xf0) >> 4];
  666. }
  667. }
  668. else throw new ArgumentException($"{nameof(bit)} is {bit}, it may only be 4 or 8.");
  669. if (i < buffer.Count)
  670. buffer[i] = input;
  671. else break;
  672. i++;
  673. lastRow = row;
  674. }
  675. using (var tex = (Texture2D)buffer)
  676. using (var fs = new FileStream(string.Format(path, bit, $"{clut.Key}{(alt?"a":"")}"), FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
  677. {
  678. tex.SaveAsPng(fs, textureTypeWidth, height);
  679. }
  680. if (bit > 8) break;
  681. }
  682. }
  683. }
  684. }
  685. private void FindOverlappingTilesClassicSpriteBatch() => (from t1 in GetTiles
  686. from t2 in GetTiles
  687. where t1.TileID < t2.TileID
  688. where t1.BlendMode == BlendMode.None
  689. where t1.Intersect(t2)
  690. orderby t1.TileID, t2.TileID ascending
  691. select new[] { t1, t2 }
  692. ).ForEach(x => x[1].OverLapID = checked((byte)(x[0].OverLapID + 1)));
  693. private static byte GetColorKeyClassicSpriteBatch(IReadOnlyList<byte> mim, int textureWidth, int startPixel, int x, int y, bool is8Bit)
  694. {
  695. if (is8Bit)
  696. return mim[startPixel + x + y * textureWidth];
  697. var tempKey = mim[startPixel + x / 2 + y * textureWidth];
  698. if (x % 2 == 1)
  699. return checked((byte)((tempKey & 0xf0) >> 4));
  700. return checked((byte)(tempKey & 0xf));
  701. }
  702. private List<KeyValuePair<BlendMode, Texture2D>> GetTexturesReadyToDrawClassicSpriteBatch() =>
  703. _textures?.OrderByDescending(kvpZ => kvpZ.Key)
  704. .SelectMany(kvpLayerID => kvpLayerID.Value.OrderBy(x => kvpLayerID.Key)
  705. .SelectMany(kvpAnimationID => kvpAnimationID.Value.OrderBy(x => kvpAnimationID.Key))
  706. .SelectMany(kvpAnimationState => kvpAnimationState.Value.OrderBy(x => kvpAnimationState.Key))
  707. .SelectMany(kvpOverlapID => kvpOverlapID.Value.OrderBy(x => kvpOverlapID.Key))
  708. .SelectMany(kvpBlendMode => kvpBlendMode.Value)).ToList();
  709. /// <summary>
  710. /// Gets the TextureHandler used by the tile.
  711. /// </summary>
  712. /// <param name="tile"></param>
  713. /// <returns>Texture handler used by tile</returns>
  714. private TextureHandler GetTextureUsedByTile(Tile tile)
  715. {
  716. var textureIDsPalette = _textureIDsPalettes
  717. ?.FirstOrDefault(x => x.Key.PaletteID == tile.PaletteID && x.Key.TextureID == tile.TextureID).Value;
  718. if (textureIDsPalette != null)
  719. return textureIDsPalette;
  720. var tid = _textureIDs?.FirstOrDefault(x => x.Key == tile.TextureID).Value;
  721. return tid;
  722. }
  723. private void LoadPalettes(byte[] mim)
  724. {
  725. var offset = /*Memory.FieldHolder.FieldID == 76 ? 0 :*/ _textureType?.BytesSkippedPalettes ?? 0;
  726. var cluts = GetTiles != null
  727. ? new Cluts(
  728. GetTiles.Select(x => x.PaletteID).Distinct().ToDictionary(x => x, x => new Color[ColorsPerPalette]),
  729. false)
  730. :
  731. new Cluts(
  732. Enumerable.Range(0, 16).Select(x => (byte) x)
  733. .ToDictionary(x => x, x => new Color[ColorsPerPalette]), false);
  734. using (var br = new BinaryReader(new MemoryStream(mim)))
  735. foreach (var clut in cluts)
  736. {
  737. var palettePointer = offset + clut.Key * BytesPerPalette;
  738. br.BaseStream.Seek(palettePointer, SeekOrigin.Begin);
  739. for (var i = 0; i < ColorsPerPalette; i++)
  740. clut.Value[i] = Texture_Base.ABGR1555toRGBA32bit(br.ReadUInt16());
  741. }
  742. _cluts = cluts;
  743. SaveCluts();
  744. }
  745. private void LoadTiles(byte[] map) => GetTiles = map == null ? default : Tiles.Load(map, _textureType.Type);
  746. private int LoadUpscaleBackgrounds(string path)
  747. {
  748. if (Directory.Exists(path))
  749. {
  750. var files = Directory.EnumerateFiles(path, $"*{Module.GetFieldName()}*.png", SearchOption.AllDirectories).OrderBy(x => x.Length).ThenBy(x => x, StringComparer.OrdinalIgnoreCase).ToList();
  751. if (files.Count > 0)
  752. {
  753. _textureIDs = new Dictionary<byte, TextureHandler>();
  754. var escapedName = Regex.Escape(Module.GetFieldName());
  755. var regex = new Regex(@".+" + escapedName + @"_(\d{1,2})\.png", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  756. foreach (var file in files.Select(x => regex.Match(x)))
  757. {
  758. if (file.Groups.Count <= 1 || !byte.TryParse(file.Groups[1].Value, out var b)) continue;
  759. if (b >= 13) b -= 13;
  760. if (_textureIDs.ContainsKey(b)) continue;
  761. var alt = $"{Module.GetFieldName()}_{b + 13}.png";
  762. _textureIDs.Add(b, TextureHandler.CreateFromPng(File.Exists(alt) ? alt : file.Value, 256, 256, 0, true, true));
  763. }
  764. SaveSwizzled(_textureIDs.ToDictionary(x => x.Key, x => (Texture2D)x.Value));
  765. _textureIDsPalettes = new Dictionary<TextureIDPaletteID, TextureHandler>();
  766. var regex2 = new Regex(@".+" + escapedName + @"_(\d{1,2})_(\d{1,2})\.png", RegexOptions.IgnoreCase | RegexOptions.Compiled);
  767. foreach (var file in files.Select(x => regex2.Match(x)))
  768. {
  769. if (file.Groups.Count > 1 && byte.TryParse(file.Groups[1].Value, out var b) && byte.TryParse(file.Groups[2].Value, out var b2))
  770. {
  771. if (b >= 13) b -= 13;
  772. TextureIDPaletteID textureIDPaletteID;
  773. if (!_textureIDsPalettes.ContainsKey(textureIDPaletteID = new TextureIDPaletteID { PaletteID = b2, TextureID = b }))
  774. {
  775. var alt = $"{Module.GetFieldName()}_{b + 13}_{b2}.png";
  776. _textureIDsPalettes.Add(textureIDPaletteID, TextureHandler.CreateFromPng(File.Exists(alt) ? alt : file.Value, 256, 256, b2, true, true));
  777. }
  778. }
  779. foreach (var groups in _textureIDsPalettes.Where(x => _textureIDsPalettes.Count(y => y.Key.TextureID == x.Key.TextureID) > 1).GroupBy(x => x.Key.PaletteID))
  780. foreach (var kvpGroup in groups)
  781. {
  782. var textureIDs =
  783. groups.ToDictionary(x => x.Key.TextureID, x => (Texture2D) x.Value);
  784. SaveSwizzled(textureIDs, $"_{kvpGroup.Key.PaletteID}");
  785. break;
  786. }
  787. }
  788. }
  789. }
  790. var count = (_textureIDs?.Count ?? 0) + (_textureIDsPalettes?.Count ?? 0);
  791. return count;
  792. }
  793. //private void SaveSwizzled(string suf = "") => SaveSwizzled(TextureIDs, suf);
  794. private void OldSaveDeswizzled()
  795. {
  796. if (!Memory.EnableDumpingData && (!Module.Toggles.HasFlag(Toggles.DumpingData) ||
  797. !Module.Toggles.HasFlag(Toggles.ClassicSpriteBatch))) return;
  798. var fieldName = Module.GetFieldName();
  799. var folder = Module.GetFolder(fieldName);
  800. foreach (var kvpZ in _textures)
  801. foreach (var kvpLayer in kvpZ
  802. .Value)
  803. foreach (var kvpAnimationID
  804. in
  805. kvpLayer.Value)
  806. foreach (var
  807. kvpAnimationState in kvpAnimationID.Value)
  808. foreach (var kvpOverlapID in kvpAnimationState.Value)
  809. foreach (var kvp in kvpOverlapID.Value)
  810. {
  811. var path = Path.Combine(folder,
  812. $"{fieldName}_{kvpZ.Key:D4}.{kvpLayer.Key}.{kvpAnimationID.Key}.{kvpAnimationState.Key}.{kvpOverlapID.Key}.{(int) kvp.Key}.png");
  813. using (var fs = new FileStream(path,
  814. FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
  815. kvp.Value.SaveAsPng(
  816. fs,
  817. kvp.Value.Width, kvp.Value.Height);
  818. }
  819. }
  820. private bool ParseBackgroundClassicSpriteBatch(byte[] mim)
  821. {
  822. if (!Module.Toggles.HasFlag(Toggles.ClassicSpriteBatch)) return true;
  823. if (mim == null || (GetTiles?.Count ?? 0) == 0)
  824. return false;
  825. FindOverlappingTilesClassicSpriteBatch();
  826. //FindSameXYTilesSource();
  827. var lowest = new Point(GetTiles.Min(x => x.X), GetTiles.Min(x => x.Y));
  828. var maximum = new Point(GetTiles.Max(x => x.X), GetTiles.Max(x => x.Y));
  829. Height = Math.Abs(lowest.Y) + maximum.Y + Tile.Size; //224
  830. Width = Math.Abs(lowest.X) + maximum.X + Tile.Size; //320
  831. //Color[] finalImage = new Color[height * width]; //ARGB;
  832. //Color[] finalOverlapImage = new Color[height * width];
  833. //tex = new Texture2D(Memory.graphics.GraphicsDevice, width, height);
  834. //texOverlap = new Texture2D(Memory.graphics.GraphicsDevice, width, height);
  835. IEnumerable<byte> layers = GetTiles.Select(x => x.LayerID).Distinct().OrderBy(x => x).ToArray();
  836. Debug.WriteLine($"FieldID: {Memory.FieldHolder.FieldID}, Layers: {layers.Count()}, ({string.Join(",", layers)}) ");
  837. 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();
  838. if (_textures != null)
  839. {
  840. foreach (var tex in GetTexturesReadyToDrawClassicSpriteBatch().Select(x => x.Value))
  841. tex.Dispose();
  842. }
  843. _textures = new ConcurrentDictionary<ushort, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>>>>();
  844. const ushort z = 0;
  845. byte layerID = 0;
  846. byte animationID = 0;
  847. byte animationState = 0;
  848. byte overlapID = 0;
  849. var blendMode = BlendMode.None;
  850. TextureBuffer textureBuffer = null;
  851. var hasColor = false;
  852. var dictLayerID = _textures.GetOrAdd(z, new ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>>>());
  853. void convertColorToTexture2d()
  854. {
  855. if (!hasColor || textureBuffer == null) return;
  856. hasColor = false;
  857. var dictAnimationID =
  858. dictLayerID.GetOrAdd(layerID,
  859. new ConcurrentDictionary<byte, ConcurrentDictionary<byte,
  860. ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>>());
  861. var
  862. dictAnimationState = dictAnimationID.GetOrAdd(animationID,
  863. new ConcurrentDictionary<byte,
  864. ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>>());
  865. var dictOverlapID =
  866. dictAnimationState.GetOrAdd(animationState,
  867. new ConcurrentDictionary<byte, ConcurrentDictionary<BlendMode, Texture2D>>());
  868. var dictBlend =
  869. dictOverlapID.GetOrAdd(overlapID, new ConcurrentDictionary<BlendMode, Texture2D>());
  870. var tex = dictBlend.GetOrAdd(blendMode, new Texture2D(Memory.Graphics.GraphicsDevice, Width, Height));
  871. textureBuffer.SetData(tex);
  872. }
  873. for (var i = 0; i < sortedTiles.Count; i++)
  874. {
  875. var previousTile = i > 0 ? sortedTiles[i - 1] : null;
  876. var tile = sortedTiles[i];
  877. if (textureBuffer == null || previousTile == null || previousTile.Z != tile.Z ||
  878. previousTile.LayerID != tile.LayerID || previousTile.BlendMode != tile.BlendMode ||
  879. previousTile.AnimationID != tile.AnimationID ||
  880. previousTile.AnimationState != tile.AnimationState || previousTile.OverLapID != tile.OverLapID)
  881. {
  882. convertColorToTexture2d();
  883. textureBuffer = new TextureBuffer(Width, Height, false);
  884. layerID = tile.LayerID;
  885. blendMode = tile.BlendMode;
  886. animationID = tile.AnimationID;
  887. animationState = tile.AnimationState;
  888. overlapID = tile.OverLapID;
  889. }
  890. var palettePointer = _textureType.BytesSkippedPalettes + tile.PaletteID * BytesPerPalette;
  891. var sourceImagePointer = BytesPerPalette * _textureType.Palettes;
  892. const int textureWidth = 128;
  893. var startPixel = sourceImagePointer + tile.SourceX + textureWidth * tile.TextureID + _textureType.Width * tile.SourceY;
  894. var real = new Point(Math.Abs(lowest.X) + tile.X, Math.Abs(lowest.Y) + tile.Y);
  895. var realDestinationPixel = real.Y * Width + real.X;
  896. if (tile.Is4Bit)
  897. {
  898. startPixel -= tile.SourceX / 2;
  899. }
  900. for (var y = 0; y < Tile.Size; y++)
  901. for (var x = 0; x < Tile.Size; x++)
  902. {
  903. var colorKey = GetColorKeyClassicSpriteBatch(mim, _textureType.Width, startPixel, x, y, tile.Is8Bit);
  904. var color16Bit = BitConverter.ToUInt16(mim, 2 * colorKey + palettePointer);
  905. if (color16Bit == 0) // 0 is Color.TransparentBlack So we skip it.
  906. continue;
  907. var color = Texture_Base.ABGR1555toRGBA32bit(color16Bit);
  908. var pos = realDestinationPixel + x + y * Width;
  909. var bufferedColor = textureBuffer[pos];
  910. if (blendMode < BlendMode.None)
  911. {
  912. if (color == Color.Black)
  913. continue;
  914. if (blendMode == BlendMode.Subtract)
  915. {
  916. if (bufferedColor.A != 0)
  917. color = Blend2(bufferedColor, color);
  918. }
  919. else
  920. {
  921. switch (blendMode)
  922. {
  923. case BlendMode.QuarterAdd:
  924. color = Color.Multiply(color, .25f);
  925. break;
  926. case BlendMode.HalfAdd:
  927. color = Color.Multiply(color, .5f);
  928. break;
  929. case BlendMode.Add:
  930. break;
  931. case BlendMode.Subtract:
  932. break;
  933. case BlendMode.None:
  934. break;
  935. default:
  936. throw new ArgumentOutOfRangeException();
  937. }
  938. if (bufferedColor.A != 0)
  939. color = Blend1(bufferedColor, color);
  940. }
  941. }
  942. else if (bufferedColor.A != 0)
  943. {
  944. throw new Exception("Color is already set something may be wrong.");
  945. }
  946. color.A = 0xFF;
  947. textureBuffer[pos] = color;
  948. hasColor = true;
  949. }
  950. }
  951. convertColorToTexture2d(); // gets leftover colors from last batch and makes a texture.
  952. OldSaveDeswizzled();
  953. return true;
  954. }
  955. /// <summary>
  956. /// <para>Create a swizzeled Textures with one palette.</para>
  957. /// <para>Few exceptions where tiles conflict and need separate files.</para>
  958. /// </summary>
  959. /// <param name="mim">Image Data .mim file</param>
  960. /// <returns></returns>
  961. private bool ParseBackgroundQuads(byte[] mim)
  962. {
  963. if (mim == null || (GetTiles?.Count ?? 0) == 0)
  964. return false;
  965. var path = Path.Combine(Memory.FF8Dir, "textures");
  966. var count = LoadUpscaleBackgrounds(path);
  967. if (count <= 0)
  968. {
  969. var uniqueSetOfTileData = GetTiles.Where(x => x.Draw).Select(x => new
  970. {
  971. x.TextureID, x.BlendMode, loc = new Point(x.SourceX, x.SourceY), x.Depth, x.PaletteID, x.AnimationID
  972. }).Distinct().ToList().AsReadOnly();
  973. Width = uniqueSetOfTileData.Max(x => x.loc.X) + Tile.Size;
  974. Height = uniqueSetOfTileData.Max(x => x.loc.Y) + Tile.Size;
  975. IReadOnlyDictionary<byte, Texture2D> textureIDs = uniqueSetOfTileData.Select(x => x.TextureID)
  976. .Distinct()
  977. .ToDictionary(x => x,
  978. x => Memory.Graphics != null ? new Texture2D(Memory.Graphics.GraphicsDevice, 256, 256) : null);
  979. IReadOnlyDictionary<byte, HashSet<byte>> overlap = GetTiles.Select(x => x.TextureID).Distinct()
  980. .ToDictionary(x => x, x => new HashSet<byte>());
  981. using (var br = new BinaryReader(new MemoryStream(mim)))
  982. {
  983. foreach (var kvp in textureIDs)
  984. {
  985. GenTexture(kvp.Key, kvp.Value);
  986. }
  987. SaveSwizzled(textureIDs);
  988. var fieldName = Module.GetFieldName();
  989. _textureIDs = textureIDs.ToDictionary(x => x.Key,
  990. x => TextureHandler.Create($"{fieldName}_{x.Key}", new Texture2DWrapper(x.Value),
  991. ushort.MaxValue));
  992. if (overlap.Any(x => x.Value.Count > 1))
  993. {
  994. IReadOnlyDictionary<TextureIDPaletteID, Texture2D> textureIDsPalettes = uniqueSetOfTileData
  995. .Where(x => overlap[x.TextureID].Contains(x.PaletteID))
  996. .Select(x => new TextureIDPaletteID {TextureID = x.TextureID, PaletteID = x.PaletteID})
  997. .Distinct().ToDictionary(x => x,
  998. x => new Texture2D(Memory.Graphics.GraphicsDevice, 256, 256));
  999. _textureIDsPalettes = textureIDsPalettes.ToDictionary(x => x.Key,
  1000. x => TextureHandler.Create($"{fieldName}_{x.Key.TextureID}", new Texture2DWrapper(x.Value),
  1001. x.Key.PaletteID));
  1002. foreach (var kvp in textureIDsPalettes)
  1003. {
  1004. GenTexture(kvp.Key.TextureID, kvp.Value, kvp.Key.PaletteID);
  1005. }
  1006. foreach (var groups in
  1007. textureIDsPalettes
  1008. .Where(x => textureIDsPalettes.Count(y => y.Key.TextureID == x.Key.TextureID) > 1)
  1009. .GroupBy(x => x.Key.PaletteID))
  1010. foreach (var kvpGroup in groups)
  1011. {
  1012. textureIDs =
  1013. groups.ToDictionary(x => x.Key.TextureID, x => x.Value);
  1014. SaveSwizzled(textureIDs, $"_{kvpGroup.Key.PaletteID}");
  1015. break;
  1016. }
  1017. }
  1018. void GenTexture(byte texID, Texture2D tex2d, byte? inPaletteID = null)
  1019. {
  1020. if (tex2d == null) return;
  1021. var tex = new TextureBuffer(tex2d.Width, tex2d.Height, false);
  1022. foreach (var tile in uniqueSetOfTileData.Where(x =>
  1023. x.TextureID == texID && (!inPaletteID.HasValue || inPaletteID.Value == x.PaletteID)))
  1024. {
  1025. var is4Bit = Tile.Test4Bit(tile.Depth);
  1026. var is8Bit = Tile.Test8Bit(tile.Depth);
  1027. var is16Bit = Tile.Test16Bit(tile.Depth);
  1028. byte colorKey = 0;
  1029. foreach (var p in from x in Enumerable.Range(0, Tile.Size)
  1030. from y in Enumerable.Range(0, Tile.Size)
  1031. orderby y, x
  1032. select new Point(x, y))
  1033. {
  1034. (var trueX, var trueY) = (tile.loc.X+p.X, tile.loc.Y + p.Y);
  1035. var offsetX = is16Bit
  1036. ? trueX * 2
  1037. : trueX / (is4Bit ? 2 : 1);
  1038. var texturePageOffset = TexturePageWidth * tile.TextureID;
  1039. var offsetY = _textureType.Width * trueY ;
  1040. var offset = _textureType.PaletteSectionSize +
  1041. offsetX +
  1042. texturePageOffset +
  1043. offsetY;
  1044. br.BaseStream.Seek(offset, SeekOrigin.Begin);
  1045. var point = new Point(p.X + tile.loc.X, p.Y + tile.loc.Y);
  1046. var paletteID = tile.PaletteID;
  1047. Color input = default;
  1048. if (is8Bit)
  1049. {
  1050. input = _cluts[paletteID][br.ReadByte()];
  1051. }
  1052. else if (is16Bit)
  1053. {
  1054. input = Texture_Base.ABGR1555toRGBA32bit(br.ReadUInt16());
  1055. }
  1056. else if (is4Bit)
  1057. {
  1058. if (p.X % 2 == 0)
  1059. {
  1060. colorKey = br.ReadByte();
  1061. input = _cluts[paletteID][colorKey & 0xf];
  1062. }
  1063. else
  1064. {
  1065. input = _cluts[paletteID][(colorKey & 0xf0) >> 4];
  1066. }
  1067. }
  1068. if (!inPaletteID.HasValue)
  1069. // forcing a palette happens post overlap test. So shouldn't need to rerun test.
  1070. {
  1071. var current = tex[point.X, point.Y];
  1072. var output = ChangeColor(current, input, point, tile.TextureID, overlap);
  1073. if (!output.HasValue) break;
  1074. if (output.Value.A != 0)
  1075. tex[point.X, point.Y] = output.Value;
  1076. }
  1077. else
  1078. {
  1079. if (input.A != 0)
  1080. tex[point.X, point.Y] = input;
  1081. }
  1082. }
  1083. }
  1084. tex.SetData(tex2d);
  1085. }
  1086. }
  1087. }
  1088. else
  1089. {
  1090. Debug.WriteLine($"Loaded {count} Textures from {path}");
  1091. }
  1092. //the sort here should be the default draw order. May need changed.
  1093. _quads = GetTiles.Select(x => new TileQuadTexture(x, GetTextureUsedByTile(x), 1f)).Where(x => x.Enabled)
  1094. .OrderByDescending(x => x.GetTile.Z)
  1095. .ThenByDescending(x => x.GetTile.TileID)
  1096. .ThenBy(x => x.GetTile.LayerID)
  1097. .ThenBy(x => x.GetTile.AnimationID)
  1098. .ThenBy(x => x.GetTile.AnimationState)
  1099. .ThenByDescending(x => x.GetTile.BlendMode).ToList();
  1100. _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());
  1101. _animations.ForEach(x => x.Value.Where(y => y.AnimationState != 0).ForEach(w => w.Hide()));
  1102. TotalTime = TimeSpan.FromMilliseconds(1000f / 10f);
  1103. CurrentTime = TimeSpan.Zero;
  1104. return true;
  1105. }
  1106. private void SaveCluts()
  1107. {
  1108. if (Memory.EnableDumpingData || Module.Toggles.HasFlag(Toggles.DumpingData))
  1109. {
  1110. var path = Path.Combine(Module.GetFolder(),
  1111. $"{Module.GetFieldName()}_Clut.png");
  1112. _cluts.Save(path);
  1113. }
  1114. }
  1115. private static void SaveSwizzled(IReadOnlyDictionary<byte, Texture2D> textureIDs, string suf = "")
  1116. {
  1117. if (!Memory.EnableDumpingData && !Module.Toggles.HasFlag(Toggles.DumpingData)) return;
  1118. var fieldName = Module.GetFieldName();
  1119. var folder = Module.GetFolder(fieldName);
  1120. foreach (var kvp in textureIDs)
  1121. {
  1122. var path = Path.Combine(folder,
  1123. $"{fieldName}_{kvp.Key}{suf}.png");
  1124. if (File.Exists(path))
  1125. continue;
  1126. using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
  1127. { kvp.Value.SaveAsPng(fs, kvp.Value.Width, kvp.Value.Height); }
  1128. }
  1129. }
  1130. #endregion Methods
  1131. // TODO: uncomment the following line if the finalizer is overridden above.// GC.SuppressFinalize(this);
  1132. }
  1133. }