Mag.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. using Microsoft.Xna.Framework;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. namespace OpenVIII
  7. {
  8. public static class BinaryReaderExt
  9. {
  10. #region Methods
  11. public static Vector3 ReadVertex(this BinaryReader br) => new Vector3(br.ReadVertexDim(), 0 - br.ReadVertexDim(), br.ReadVertexDim());
  12. public static float ReadVertexDim(this BinaryReader br) => br.ReadInt16() / 2000.0f;
  13. #endregion Methods
  14. }
  15. }
  16. namespace OpenVIII.Battle
  17. {
  18. /// <summary>
  19. /// Magic Obj Reader
  20. /// </summary>
  21. /// <see cref="http://forums.qhimm.com/index.php?topic=15056.msg211220"/>
  22. /// <seealso cref="http://forums.qhimm.com/index.php?topic=16283.0"/>
  23. /// <seealso cref="http://forums.qhimm.com/index.php?topic=15906.0"/>
  24. /// <seealso cref="http://wiki.ffrtt.ru/index.php?title=FF8/FileFormat_magfiles"/>
  25. /// <seealso cref="https://github.com/MaKiPL/FF8-Rinoa-s-Toolset/blob/master/SerahToolkit_SharpGL/GF_AlternativeTexture.cs"/>
  26. /// <seealso cref="https://github.com/MaKiPL/FF8-Rinoa-s-Toolset/blob/master/SerahToolkit_SharpGL/GfEnviro.cs"/>
  27. public class Mag
  28. {
  29. #region Fields
  30. public uint
  31. pBones,
  32. pTextureLimit,
  33. pGeometry,
  34. pSCOT,
  35. pTexture;
  36. private static readonly ushort[] _knownPolygons = new ushort[]
  37. {
  38. 0x2,
  39. 0x6,
  40. 0x7,
  41. 0x8,
  42. 0x9,
  43. 0xC,
  44. 0x10,
  45. 0x12,
  46. 0x11,
  47. 0x13,
  48. };
  49. public List<Geometry> Geometries;
  50. #endregion Fields
  51. #region Constructors
  52. public Mag(string filename, BinaryReader br)
  53. {
  54. FileName = filename;
  55. br.BaseStream.Seek(0, SeekOrigin.Begin);
  56. if (TryReadTIM(br) == null)
  57. {
  58. isTIM = false;
  59. br.BaseStream.Seek(0, SeekOrigin.Begin);
  60. //Offset Description
  61. //0x00 Probably always null
  62. uint pPadding = br.ReadUInt32();
  63. if (pPadding != 0)
  64. return;
  65. //0x04 Probably bones/ animation data, might be 0x00
  66. pBones = br.ReadUInt32();
  67. //0x08 Unknown(used to determinate texture size *), might be 0x64
  68. pTextureLimit = br.ReadUInt32();
  69. //0x0C Geometry pointer, might be 0xAC
  70. pGeometry = br.ReadUInt32();
  71. //0x10 SCOT pointer, might be 0x00
  72. pSCOT = br.ReadUInt32();
  73. //0x14 Texture pointer, might be 0x30
  74. pTexture = br.ReadUInt32();
  75. //0x18 == 0x98
  76. //0x1C == 0xAC
  77. if (pBones > br.BaseStream.Length ||
  78. pTextureLimit > br.BaseStream.Length ||
  79. pGeometry > br.BaseStream.Length ||
  80. pSCOT > br.BaseStream.Length ||
  81. pTexture > br.BaseStream.Length)
  82. {
  83. return;
  84. }
  85. if (FileName == "c:\\ff8\\data\\eng\\battle\\mag201_b.19")
  86. {
  87. }
  88. else if (FileName == "c:\\ff8\\data\\eng\\battle\\mag204_b.04")
  89. {
  90. }
  91. isPackedMag = true;
  92. ReadGeometry(br);
  93. ReadTextures(br);
  94. }
  95. }
  96. #endregion Constructors
  97. #region Properties
  98. public byte DataType => getValue(2);
  99. public string FileName { get; private set; }
  100. public byte IDnumber => getValue(1);
  101. public bool isPackedMag { get; private set; } = false;
  102. public bool isTIM { get; private set; } = false;
  103. public byte SequenceNumber => getValue(3);
  104. public TIM2[] TIM { get; private set; }
  105. private bool bFileNameTest => FileName != null && Path.GetExtension(FileName).Trim('.').Length == 3;
  106. /// <summary>
  107. /// if quit loop because unknown type.
  108. /// </summary>
  109. public int UnknownType { get; private set; } = int.MinValue;
  110. #endregion Properties
  111. #region Methods
  112. public TIM2[] TryReadTIM(BinaryReader br)
  113. {
  114. try
  115. {
  116. TIM2 tim = new TIM2(br, noExec: true);
  117. if (tim.NOT_TIM)
  118. return TIM = null;
  119. isTIM = true;
  120. return TIM = new TIM2[] { tim };
  121. }
  122. catch (InvalidDataException)
  123. {
  124. return TIM = null;
  125. }
  126. }
  127. private byte getValue(int index) => bFileNameTest ? byte.Parse(FileName.Substring(FileName.Length - index, 1),
  128. System.Globalization.NumberStyles.HexNumber) : (byte)0xff;
  129. private void ReadGeometry(BinaryReader br)
  130. {
  131. if (pGeometry == 0) return;
  132. br.BaseStream.Seek(pGeometry, SeekOrigin.Begin);
  133. int count = br.ReadInt32();
  134. //count = count > 0x24 ? 0x24 : count; // unsure why.
  135. List<uint> positions = new List<uint>();
  136. while (count-- > 0)
  137. {
  138. uint pos = br.ReadUInt32();
  139. if (pos > 0 && pos + pGeometry < br.BaseStream.Length)
  140. positions.Add(pos + pGeometry);
  141. }
  142. Geometries = new List<Geometry>(positions.Count);
  143. foreach (uint pos in positions)
  144. {
  145. Geometry g = new Geometry();
  146. const int PassFromStart = 24;
  147. bool OnlyVertex = true;
  148. br.BaseStream.Seek(pos, SeekOrigin.Begin);
  149. count = br.ReadInt32();
  150. if (count > 12u)
  151. continue;
  152. if (count != 2u)
  153. OnlyVertex = false;
  154. br.BaseStream.Seek(pos + 8, SeekOrigin.Begin);
  155. uint _relativeJump = br.ReadUInt32() + pos;
  156. br.BaseStream.Seek(pos + PassFromStart, SeekOrigin.Begin);
  157. int _vertexCount = br.ReadUInt16() * 8;
  158. br.BaseStream.Seek(pos + PassFromStart - 4, SeekOrigin.Begin);
  159. uint _verticesOffset = br.ReadUInt16() + pos;
  160. ReadVertices();
  161. if (OnlyVertex) { continue; }
  162. br.BaseStream.Seek(_relativeJump, SeekOrigin.Begin);
  163. ushort _polygonType = br.ReadUInt16();
  164. ushort polygons = br.ReadUInt16();
  165. bool _generatefaces;
  166. bool isknownpolygon() => _knownPolygons.Any(x => x == _polygonType);
  167. long localoffset = _relativeJump + 4;
  168. int safeHandle = 0;
  169. while (!(_generatefaces = !isknownpolygon()))
  170. {
  171. switch (_polygonType)
  172. {
  173. case 0x2:
  174. case 0x7:
  175. GetTriangle(20, 0xC);
  176. break;
  177. case 0x6:
  178. GetTriangle(12, 0x4);
  179. break;
  180. case 0x8:
  181. GetTriangle(20, 0xA);
  182. break;
  183. case 0x9:
  184. GetTriangle(28, 0x12);
  185. break;
  186. case 0xC:
  187. GetQuad(28, 0x14);
  188. break;
  189. case 0x12:
  190. GetQuad(24, 0xC);
  191. break;
  192. case 0x13:
  193. GetQuad(36, 0x18);
  194. break;
  195. case 0x10:
  196. GetQuad(12, 0x4);
  197. break;
  198. case 0x11:
  199. GetQuad(24, 0x10);
  200. break;
  201. }
  202. br.BaseStream.Seek(localoffset + safeHandle, SeekOrigin.Begin);
  203. if (br.BaseStream.Position + 4 > br.BaseStream.Length) return;
  204. if (br.ReadUInt32() == uint.MaxValue)
  205. break;
  206. localoffset += safeHandle;
  207. _polygonType = br.ReadUInt16();
  208. polygons = br.ReadUInt16();
  209. localoffset += 4;
  210. void GetTriangle(int cnt, int off0)
  211. {
  212. int off1 = off0 + 2, off2 = off0 + 4;
  213. int size = polygons * cnt;
  214. for (int i = 0; i < size; i += cnt)
  215. {
  216. g.Triangles.Add(new Vector3(GetPolygonIndex(localoffset + i + off0),
  217. GetPolygonIndex(localoffset + i + off1),
  218. GetPolygonIndex(localoffset + i + off2)));
  219. }
  220. safeHandle = size;
  221. }
  222. void GetQuad(int cnt, int off0)
  223. {
  224. int off1 = off0 + 2, off2 = off0 + 6, off3 = off0 + 4;
  225. int size = polygons * cnt;
  226. for (int i = 0; i < size; i += cnt)
  227. {
  228. g.Quads.Add(new Vector4(GetPolygonIndex(localoffset + i + off0),
  229. GetPolygonIndex(localoffset + i + off1),
  230. GetPolygonIndex(localoffset + i + off2),
  231. GetPolygonIndex(localoffset + i + off3)));
  232. }
  233. safeHandle = size;
  234. }
  235. ushort GetPolygonIndex(long offset)
  236. {
  237. br.BaseStream.Seek(offset, SeekOrigin.Begin);
  238. ushort temp = checked((ushort)(br.ReadUInt16() / 8));
  239. Debug.Assert(temp < g.Vertices.Count);
  240. //return temp == 0 ? 1 : (temp / 8) + 1;
  241. return temp;
  242. }
  243. }
  244. if (_generatefaces)
  245. {
  246. UnknownType = _polygonType;
  247. }
  248. void ReadVertices()
  249. {
  250. br.BaseStream.Seek(_verticesOffset, SeekOrigin.Begin);
  251. if (_vertexCount % 8 != 0) return;
  252. for (int i = 0; i < _vertexCount / 8; i++)
  253. {
  254. br.BaseStream.Seek(_verticesOffset + i * 8, SeekOrigin.Begin);
  255. g.Vertices.Add(br.ReadVertex());
  256. }
  257. }
  258. Geometries.Add(g);
  259. }
  260. }
  261. private TIM2[] ReadTextures(BinaryReader br)
  262. { //this doesn't sound like a tim file per documentation.
  263. if (pTexture == 0 ||
  264. pTexture >= pTextureLimit ||
  265. pTexture + pTextureLimit >= br.BaseStream.Length ||
  266. (pTextureLimit - pTexture) % 4 != 0
  267. ) return null;
  268. br.BaseStream.Seek(pTexture, SeekOrigin.Begin);
  269. List<uint> positions = new List<uint>();
  270. //Debug.Assert(pTextureSize > 0 && pTextureSize < br.BaseStream.Length);
  271. while (pTextureLimit > 0 && br.BaseStream.Position < pTextureLimit && br.BaseStream.Position + 4 < br.BaseStream.Length)
  272. {
  273. uint pos = br.ReadUInt32();
  274. if (pos != 0)
  275. positions.Add(pos + pTexture);
  276. }
  277. if (positions.Count > 0)
  278. {
  279. //textures here?
  280. return null;
  281. }
  282. else
  283. return null;
  284. }
  285. #endregion Methods
  286. #region Classes
  287. public class Geometry
  288. {
  289. #region Constructors
  290. public Geometry()
  291. {
  292. Vertices = new List<Vector3>();
  293. Triangles = new List<Vector3>();
  294. Quads = new List<Vector4>();
  295. }
  296. #endregion Constructors
  297. #region Properties
  298. public List<Vector4> Quads { get; private set; }
  299. public List<Vector3> Triangles { get; private set; }
  300. public List<Vector3> Vertices { get; private set; }
  301. #endregion Properties
  302. }
  303. #endregion Classes
  304. }
  305. }