Extended.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Runtime.InteropServices;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. namespace OpenVIII
  11. {
  12. //Class that provides language extensions made by Maki
  13. public static class Extended
  14. {
  15. public enum Languages
  16. {
  17. en,
  18. fr,
  19. de,
  20. es,
  21. it,
  22. }
  23. //WORLD MAP COORDINATES HELPER
  24. /// <summary>
  25. /// Indicates vanilla game X axis coordinate for minimum possible X - visually the nearest to left on worldmap
  26. /// </summary>
  27. public const int WORLD_COORDS_MINLEFT = unchecked((int)0xFFFE0000);
  28. /// <summary>
  29. /// Indicates vanilla game X axis coordinate for maximum possible X - visually the furthest to right on worldmap
  30. /// </summary>
  31. public const int WORLD_COORDS_MAXRIGHT = unchecked((int)0x0001FFFF);
  32. /// <summary>
  33. /// zero based range of maximum left-right for worldmap coordinates
  34. /// </summary>
  35. public static readonly int WORLD_COORDS_XRANGE = Math.Abs(WORLD_COORDS_MINLEFT) + WORLD_COORDS_MAXRIGHT;
  36. /// <summary>
  37. /// Indicates vanilla game Y axis coordinate for minimum possible Y - visually at the semi top of worldmap
  38. /// </summary>
  39. public const int WORLD_COORDS_MINTOP = unchecked((int)0xFFFE8000);
  40. /// <summary>
  41. /// Indicates vanilla game Y axis coordinate for maximum possible Y - visually at the very bottom of worldmap
  42. /// </summary>
  43. public const int WORLD_COORDS_MAXBOTTOM = unchecked((int)0x00017FFF);
  44. /// <summary>
  45. /// zero based range of maximum left-right for worldmap coordinates
  46. /// </summary>
  47. public static readonly int WORLD_COORDS_ZRANGE = Math.Abs(WORLD_COORDS_MINTOP) + WORLD_COORDS_MAXBOTTOM;
  48. /// <summary>
  49. /// Indicates the OpenVIII coordinate system which shows maximum X axis. Minimum is always zero (it's the nearest to left side of worldmap)
  50. /// </summary>
  51. public const int WORLD_OPENVIII_MAXRIGHT = -(32 * 512);
  52. /// <summary>
  53. /// Indicates the OpenVIII coordinate system which shows maximum Y axis. Minimum is always zero (it's the furthest to bottom of worldmap)
  54. /// </summary>
  55. public const int WORLD_OPENVIII_MAXBOTTOM = -(24 * 512);
  56. /// <summary>
  57. /// Result of Vanilla.Y * x = openviii.Y equation
  58. /// </summary>
  59. public const float WORLD_COORD_YHELPER = -0.06f;
  60. /// <summary>
  61. /// This method converts vanilla world map coordinates to openVIII equivalent.
  62. /// </summary>
  63. /// <param name="x"></param>
  64. /// <returns></returns>
  65. public static float ConvertVanillaWorldXAxisToOpenVIII(float x)
  66. {
  67. float newX = x + Math.Abs(WORLD_COORDS_MINLEFT);
  68. newX /= WORLD_COORDS_XRANGE; //normalized
  69. newX *= WORLD_OPENVIII_MAXRIGHT;
  70. return newX;
  71. }
  72. /// <summary>
  73. /// This method converts vanilla world map coordinates to openVIII equivalent.
  74. /// </summary>
  75. /// <param name="x"></param>
  76. /// <returns></returns>
  77. public static float ConvertVanillaWorldZAxisToOpenVIII(float z)
  78. {
  79. float newZ = z + Math.Abs(WORLD_COORDS_MINTOP);
  80. newZ /= WORLD_COORDS_ZRANGE; //normalized
  81. newZ *= WORLD_OPENVIII_MAXBOTTOM;
  82. return newZ;
  83. }
  84. /// <summary>
  85. /// This method converts vanilla world map coordinates to openVIII equivalent
  86. /// </summary>
  87. /// <param name="y"></param>
  88. /// <returns></returns>
  89. public static float ConvertVanillaWorldYAxisToOpenVIII(float y)
  90. => y * WORLD_COORD_YHELPER;
  91. //https://stackoverflow.com/a/2887/4509036
  92. public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
  93. {
  94. var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
  95. try
  96. {
  97. return (T)Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());
  98. }
  99. finally
  100. {
  101. handle.Free();
  102. }
  103. }
  104. #if DEBUG || _WINDOWS
  105. public static void DumpBuffer(byte[] buffer, string path)
  106. => System.IO.File.WriteAllBytes(path, buffer);
  107. public static void DumpBuffer(System.IO.MemoryStream ms, string path)
  108. => System.IO.File.WriteAllBytes(path, ms.GetBuffer());
  109. public static void DumpBuffer(System.IO.MemoryStream ms)
  110. => System.IO.File.WriteAllBytes(GetUnixFullPath(System.IO.Path.Combine(Memory.FF8DIR, "debugUnpack.debug")), ms.GetBuffer());
  111. public static void DumpTexture(Texture2D tex, string s)
  112. {
  113. if (Directory.Exists(Path.GetDirectoryName(s)))
  114. using (System.IO.FileStream fs = new System.IO.FileStream(s, System.IO.FileMode.Create, System.IO.FileAccess.Write))
  115. tex.SaveAsPng(fs, tex.Width, tex.Height);
  116. }
  117. #endif
  118. ///<summary>Detect if an object is a number</summary>
  119. ///<see cref="https://stackoverflow.com/questions/1130698/checking-if-an-object-is-a-number-in-c-sharp"/>
  120. public static bool IsNumber(object value) => value is sbyte
  121. || value is byte
  122. || value is short
  123. || value is ushort
  124. || value is int
  125. || value is uint
  126. || value is long
  127. || value is ulong
  128. || value is float
  129. || value is double
  130. || value is decimal;
  131. public static double Distance3D(Vector3 xo, Vector3 xa) => Vector3.Distance(xo, xa);
  132. /// <summary>
  133. /// Real-time collision 3D Raycast on plane + barycentric calculation
  134. /// </summary>
  135. /// <param name="R">ray with origin + given direction</param>
  136. /// <param name="a">vertex A</param>
  137. /// <param name="b">vertex B</param>
  138. /// <param name="c">vertex C</param>
  139. /// <param name="barycentric">out param for barycentric vector</param>
  140. /// <returns>0 - not applicable, 1- intersects;</returns>
  141. public static int RayIntersection3D(Ray R, Vector3 a, Vector3 b, Vector3 c, out Vector3 barycentric)
  142. {
  143. barycentric = Vector3.Zero;
  144. Vector3 p1 = b - a;
  145. Vector3 p2 = c - a;
  146. Vector3 lhs = Vector3.Cross(p1, p2);
  147. if (lhs == Vector3.Zero)
  148. return -1;
  149. Vector3 direction = R.Direction;
  150. Vector3 rhs = R.Position - a;
  151. float dot00 = -Vector3.Dot(lhs, rhs);
  152. float dot01 = Vector3.Dot(lhs, direction);
  153. if (Math.Abs(dot01) < 1E-08f)
  154. if (dot00 == 0f)
  155. return 2;
  156. else return 0;
  157. else
  158. {
  159. float dot02 = dot00 / dot01;
  160. if (dot02 < 0.0)
  161. return 0;
  162. barycentric = R.Position + dot02 * direction;
  163. float dot10 = Vector3.Dot(p1, p1);
  164. float dot11 = Vector3.Dot(p1, p2);
  165. float dot12 = Vector3.Dot(p2, p2);
  166. Vector3 lhs2 = barycentric - a;
  167. float dot21 = Vector3.Dot(lhs2, p1);
  168. float dot22 = Vector3.Dot(lhs2, p2);
  169. float dot30 = dot11 * dot11 - dot10 * dot12;
  170. float dot31 = (dot11 * dot22 - dot12 * dot21) / dot30;
  171. if (dot31 < 0.0 || dot31 > 1.0)
  172. return 0;
  173. float dot32 = (dot11 * dot21 - dot10 * dot22) / dot30;
  174. if (dot32 < 0.0 || (dot31 + dot32) > 1.0)
  175. return 0;
  176. return 1;
  177. }
  178. }
  179. public static BoundingBox GetBoundingBox(Vector3 a, Vector3 b, Vector3 c)
  180. {
  181. float Minx = (new float[] { a.X, b.X, c.X }).Min();
  182. float Maxx = (new float[] { a.X, b.X, c.X }).Max();
  183. float Miny = (new float[] { a.Y, b.Y, c.Y }).Min();
  184. float Maxy = (new float[] { a.Y, b.Y, c.Y }).Max();
  185. float Minz = (new float[] { a.Z, b.Z, c.Z }).Min();
  186. float Maxz = (new float[] { a.Z, b.Z, c.Z }).Max();
  187. Vector3 min = new Vector3(Minx, Miny, Minz);
  188. Vector3 max = new Vector3(Maxx, Maxy, Maxz);
  189. return new BoundingBox(min, max);
  190. }
  191. /// <summary>
  192. /// Provides VertexPositionTexture[] based on translatePosition and the scale. Result is plane geometry - 4 verts, 2 tris with UV of 0.0-1.0f
  193. /// </summary>
  194. /// <param name="translatePosition"></param>
  195. /// <param name="scale"></param>
  196. /// <returns></returns>
  197. public static VertexPositionTexture[] GetShadowPlane(Vector3 translatePosition, float scale=1f)
  198. {
  199. /*
  200. * THREE----ZERO
  201. * | |
  202. * | |
  203. * | |
  204. * | |
  205. * TWO------ONE*/
  206. Vector3 zero = new Vector3(1f * scale +translatePosition.X , translatePosition.Y, 1f * scale + translatePosition.Z);
  207. Vector3 one = new Vector3(1f * scale + translatePosition.X, translatePosition.Y, translatePosition.Z);
  208. Vector3 two = translatePosition;
  209. Vector3 three = new Vector3(translatePosition.X, translatePosition.Y, 1f * scale + translatePosition.Z);
  210. VertexPositionTexture[] vpt = new VertexPositionTexture[]
  211. {
  212. new VertexPositionTexture(zero, new Vector2(1f,1f)),
  213. new VertexPositionTexture(one, new Vector2(1f,0f)),
  214. new VertexPositionTexture(two, Vector2.Zero),
  215. new VertexPositionTexture(two, Vector2.Zero),
  216. new VertexPositionTexture(three, new Vector2(0f,1f)),
  217. new VertexPositionTexture(zero, new Vector2(1f,1f)),
  218. };
  219. return vpt;
  220. }
  221. /// <summary>
  222. /// Some debug text is crashing due to brackets not appearing in chartable. This function removes brackets inside string
  223. /// </summary>
  224. /// <param name="s"></param>
  225. /// <returns></returns>
  226. public static string RemoveBrackets(string s) => s.Replace('{', ' ').Replace('}', ' ');
  227. public static bool GetBit(byte @object, int positionFromRight) => ((@object >> positionFromRight) & 1) > 0;
  228. public static bool GetBit(int @object, int positionFromRight) => ((@object >> positionFromRight) & 1) > 0;
  229. /// <summary>
  230. /// Reads given char[] until null terminator, but returns as byte[]- to be used with FF8String
  231. /// </summary>
  232. /// <param name=""></param>
  233. /// <returns></returns>
  234. public static byte[] GetBinaryString(BinaryReader br)
  235. {
  236. List<byte> bb = new List<byte>();
  237. byte b;
  238. while ((b = br.ReadByte()) != 0x00)
  239. bb.Add(b);
  240. return bb.ToArray();
  241. }
  242. public static bool IsLinux
  243. {
  244. get
  245. {
  246. int p = (int)Environment.OSVersion.Platform;
  247. return (p == 4) || (p == 6) || (p == 128);
  248. }
  249. }
  250. public static string GetUnixFullPath(string pt)
  251. {
  252. #if _WINDOWS
  253. return System.IO.Path.GetFullPath(pt.Replace('/', '\\'));
  254. #else
  255. return System.IO.Path.GetFullPath(pt.Replace("\\", "/"));
  256. #endif
  257. }
  258. public static bool In(int _in, Vector2 range) =>
  259. _in >= range.X && _in <= range.Y;
  260. public static bool In(float _in, Vector2 range) => _in >= range.X && _in <= range.Y;
  261. //: false;
  262. public static bool In(int _in, int min, int max) => In(_in, new Vector2(min, max));
  263. public static bool In(float _in, float min, float max) => In(_in, new Vector2(min, max));
  264. public static Matrix GetRotationMatrixX(float angle)
  265. => new Matrix(
  266. 1, 0, 0, 0,
  267. 0, (float)Math.Cos(Radians(angle)), -(float)Math.Sin(Radians(angle)), 0,
  268. 0, (float)Math.Sin(Radians(angle)), (float)Math.Cos(Radians(angle)), 0,
  269. 0, 0, 0, 0);
  270. public static Matrix GetRotationMatrixY(double angle)
  271. => new Matrix(
  272. (float)Math.Cos(Radians(angle)), 0, (float)Math.Sin(Radians(angle)), 0,
  273. 0, 1, 0, 0,
  274. -(float)Math.Sin(Radians(angle)), 0, (float)Math.Cos(Radians(angle)), 0,
  275. 0, 0, 0, 0);
  276. public static Matrix GetRotationMatrixZ(float angle)
  277. => new Matrix(
  278. (float)Math.Cos(Radians(angle)), -(float)Math.Sin(Radians(angle)), 0, 0,
  279. (float)Math.Sin(Radians(angle)), (float)Math.Cos(Radians(angle)), 0, 0,
  280. 0, 0, 1, 0,
  281. 0, 0, 0, 0);
  282. /// <summary>
  283. /// This Matrix operation performs Matrix multiplication and transposing in-place
  284. /// </summary>
  285. /// <param name="a"></param>
  286. /// <param name="b"></param>
  287. /// <returns></returns>
  288. public static Matrix MatrixMultiply_transpose(Matrix a, Matrix b)
  289. => new Matrix(
  290. b.M11 * a.M11 + b.M21 * a.M12 + b.M31 * a.M13, b.M11 * a.M21 + b.M21 * a.M22 + b.M31 * a.M23, b.M11 * a.M31 + b.M21 * a.M32 + b.M31 * a.M33, 0,
  291. b.M12 * a.M11 + b.M22 * a.M12 + b.M32 * a.M13, b.M12 * a.M21 + b.M22 * a.M22 + b.M32 * a.M23, b.M12 * a.M31 + b.M22 * a.M32 + b.M32 * a.M33, 0,
  292. b.M13 * a.M11 + b.M23 * a.M12 + b.M33 * a.M13, b.M13 * a.M21 + b.M23 * a.M22 + b.M33 * a.M23, b.M13 * a.M31 + b.M23 * a.M32 + b.M33 * a.M33, 0,
  293. 0, 0, 0, 0);
  294. //This is the first time I had issue with precision. Cosinus from 270o was different for float and double. MathHelper is broken...
  295. public static double Radians(double angle) => angle * Math.PI / 180;
  296. public static double Cos(double angle) => Math.Cos(Radians(angle));
  297. public static double Sin(double angle) => Math.Sin(Radians(angle));
  298. /// <summary>
  299. /// Converts short to float via x/4096f
  300. /// </summary>
  301. /// <param name="x"></param>
  302. /// <returns></returns>
  303. public static float S16ToFloat(short x) => x / 4096f;
  304. public static string GetLanguageShort(bool bUseAlternative = false)
  305. {
  306. string languageIndicator = Memory.languages.ToString();
  307. return bUseAlternative ? languageIndicator == "en" ? "us" : languageIndicator : languageIndicator;
  308. }
  309. public static Vector3 ShrinkVector4ToVector3(Vector4 reference, bool bMirrorY = false)
  310. {
  311. float x, y, z;
  312. reference.Deconstruct(out x, out y, out z, out _);
  313. if (bMirrorY)
  314. y = -y;
  315. return new Vector3(x, y, z);
  316. }
  317. /// <summary>
  318. /// Converts short to float via x/4096f
  319. /// </summary>
  320. /// <param name="x">mockup of short. I.e. short(50) -> float(50) -> 50.0f / 4096f</param>
  321. /// <returns></returns>
  322. public static float S16ToFloat(float x) => x / 4096f;
  323. /// <summary>
  324. /// Converts Vector3 containing direct short>float to Vector3 that XYZ are treated by S16ToFloat
  325. /// </summary>
  326. /// <param name="vec"></param>
  327. /// <returns></returns>
  328. public static Vector3 S16VectorToFloat(Vector3 vec) => new Vector3(
  329. S16ToFloat(vec.X),
  330. S16ToFloat(vec.Y),
  331. S16ToFloat(vec.Z));
  332. public static ushort UshortLittleEndian(ushort ushort_)
  333. => (ushort)((ushort_ << 8) | (ushort_ >> 8));
  334. public static short ShortLittleEndian(short ushort_)
  335. => (short)((ushort_ << 8) | (ushort_ >> 8));
  336. public static uint UintLittleEndian(uint uint_)
  337. => (uint_ << 24) | ((uint_ << 8) & 0x00FF0000) |
  338. ((uint_ >> 8) & 0x0000FF00) | (uint_ >> 24);
  339. public static int UintLittleEndian(int uint_)
  340. => (uint_ << 24) | ((uint_ << 8) & 0x00FF0000) |
  341. ((uint_ >> 8) & 0x0000FF00) | (uint_ >> 24);
  342. public static int ClampOverload(int a, int min, int max)
  343. => a < min ? max - Math.Abs(a) : a > max ? a - max : a;
  344. public static float ClampOverload(float a, float min, float max)
  345. => a < min ? max - Math.Abs(a) : a > max ? a - max : a;
  346. public static bool bRequestedBackBuffer = false;
  347. public static bool bBackBufferAvailable = false;
  348. private static Texture2D backBufferTexture;
  349. public static Texture2D BackBufferTexture
  350. {
  351. get
  352. {
  353. if (bBackBufferAvailable)
  354. {
  355. bBackBufferAvailable = false;
  356. return backBufferTexture;
  357. }
  358. else
  359. {
  360. return null;
  361. }
  362. }
  363. set
  364. {
  365. backBufferTexture = value;
  366. }
  367. }
  368. /// <summary>
  369. /// Makes a request AFTER base.draw() to dump the backBuffer- after that the callback happens to
  370. /// postBackBufferDelegate, so make sure to set this parameter to method you want
  371. /// </summary>
  372. /// <param name="callbackMethod"></param>
  373. public static void RequestBackBuffer()
  374. {
  375. bBackBufferAvailable = false;
  376. bRequestedBackBuffer = true;
  377. }
  378. public delegate void PostBackBufferDelegate();
  379. public static PostBackBufferDelegate postBackBufferDelegate = EmptyPostBackBufferDelegate;
  380. public static void EmptyPostBackBufferDelegate()
  381. {
  382. ;
  383. }
  384. }
  385. }