Mag.cs 13 KB

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