Background.cs 60 KB

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