wmset.cs 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398
  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. namespace OpenVIII.World
  9. {
  10. public class Wmset : IDisposable
  11. {
  12. private const int WMSET_SECTION_COUNT = 48;
  13. private byte[] buffer;
  14. private int[] sectionPointers;
  15. /// <summary>
  16. /// wmset file is pseudo-archive containing 48 sections in which every 'chunk' has different data and meaning
  17. /// </summary>
  18. /// <param name="wmsetBuffer"></param>
  19. public Wmset(byte[] wmsetBuffer)
  20. {
  21. buffer = wmsetBuffer;
  22. sectionPointers = new int[WMSET_SECTION_COUNT];
  23. using (var br = new BinaryReader(new MemoryStream(buffer)))
  24. {
  25. for (var i = 0; i < sectionPointers.Length; i++)
  26. sectionPointers[i] = br.ReadInt32();
  27. }
  28. //if there's no section method either uncommented or commented out, then the section that is lacking is 4 byte NULL
  29. Section1(); //======FINISHED [Encounter setting]
  30. Section2(); //======FINISHED [World map regions]
  31. //Section3(); //=encounter - looks like it's some supply helping data or roll data? Not sure, it's way before getting encounters
  32. Section4(); //======FINISHED [Encounter pointer]
  33. Section6(); //======FINISHED [Encounter pointer (Lunar cry)]
  34. Section8(); //wm2field WIP - DEBUG data only, almost completed
  35. Section9(); //======FINISHED [Field to world map coordinates]
  36. Section11(); //??????
  37. //Section12(); //[UNKNOWN] Scripts- maybe some additional warp zones?
  38. //Section13(); // 12b per entry- SUB_548940 -> locations
  39. Section14(); //======FINISHED [Side quest strings]
  40. Section16(); //======FINISHED [objects and vehicles]
  41. //Section18(); //?????
  42. //Section19(); //=something with regions: it's that island with many drawpoints- regions setting
  43. //Section31(); //????? - referenced by FullScreen map - prob. 12 bytes per entry, where 3rd byte is locationPointer
  44. Section32(); //======FINISHED [location names]
  45. Section33(); //=SKY GRADIENT/REGION COLOURING
  46. //Section34(); //related to 14- something with side quests
  47. //Section35(); //=draw points
  48. //Section36(); //?????????
  49. //Section37(); //Ufo, Pupu and Thrusta encounters
  50. Section38(); //======FINISHED [textures archive]
  51. Section39(); //======FINISHED [textures of roads, train tracks and bridges]
  52. Section42(); //======FINISHED [object and vehicle textures]
  53. Section41(); //======FINISHED [texture animation for water] *DO NOT MOVE UP- sec38 needs to be parsed before this
  54. Section17(); //======FINISHED [texture animation for beach] *DO NOT MOVE UP- sec38 needs to be parsed before this
  55. //AKAO BELOW
  56. //Section20();
  57. //Section21();
  58. //Section43();
  59. //Section44();
  60. //Section45();
  61. //Section46();
  62. //Section47();
  63. //Section48();
  64. //-not important in openviii implementation/ sections used for limited memory solutions/psx hardware limitations
  65. //Section7(); //helpers for roads textures- as we have it working and no issues this will be useless to reverse/investigate
  66. //Section10(); //Looks like it's a helper for model and texture on world map objects. By altering some variables
  67. //you are able to use e.g. Selphie texture on Ragnarok- 0xFF13 - arg=texIndex
  68. //Section29(); //water block for limited memory rendering- not important in today
  69. //Section40(); //can't understand this one
  70. }
  71. /// <summary>
  72. /// Every section can have inner-sections. Pointers to different textures or models
  73. /// </summary>
  74. /// <param name="br"></param>
  75. /// <returns></returns>
  76. private int[] GetInnerPointers(BinaryReader br)
  77. {
  78. var innerSections = new List<int>();
  79. var eof = -1;
  80. while ((eof = br.ReadInt32()) != 0)
  81. innerSections.Add(eof);
  82. return innerSections.ToArray();
  83. }
  84. #region Section 1 - Encounter setting
  85. /// <summary>
  86. /// Section1 helps providing the correct encounter based on groundId and regionId, then provides the pointer to struct of section4
  87. /// </summary>
  88. private const int SECTION1ENTRYCOUNT = 96;
  89. [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 4)]
  90. private struct EncounterHelper
  91. {
  92. public byte regionId;
  93. public byte groundId;
  94. public ushort encounterPointer;
  95. }
  96. private EncounterHelper[] encounterHelpEntries;
  97. private void Section1()
  98. {
  99. MemoryStream ms;
  100. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  101. {
  102. ms.Seek(sectionPointers[1 - 1], SeekOrigin.Begin);
  103. ms.Seek(4, SeekOrigin.Current); //skip first DWORD- it's EOF of global file
  104. var encounterHelperList = new List<EncounterHelper>();
  105. while (true)
  106. {
  107. var entry = Extended.ByteArrayToStructure<EncounterHelper>(br.ReadBytes(4));
  108. if (entry.groundId == 0 && entry.regionId == 0 && entry.encounterPointer == 0)
  109. break;
  110. encounterHelperList.Add(entry);
  111. }
  112. encounterHelpEntries = encounterHelperList.ToArray();
  113. }
  114. }
  115. public ushort GetEncounterHelperPointer(int regionId, int groundId)
  116. {
  117. var encList = encounterHelpEntries.Where(x => x.groundId == groundId && x.regionId == regionId);
  118. if (!encList.Any())
  119. return 0xFFFF;
  120. return encList.First().encounterPointer; //always first, if there are two the same entries then it doesn't make sense- priority is over that one that is first
  121. }
  122. #endregion Section 1 - Encounter setting
  123. #region Section 2 - world map regions
  124. private byte[] regionsBuffer;
  125. private void Section2()
  126. {
  127. MemoryStream ms;
  128. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  129. {
  130. ms.Seek(sectionPointers[2 - 1], SeekOrigin.Begin);
  131. regionsBuffer = br.ReadBytes(768);
  132. }
  133. }
  134. public byte GetWorldRegionByBlock(int blockId) => regionsBuffer[blockId];
  135. public byte GetWorldRegionBySegmentPosition(int x, int y)
  136. {
  137. var regionIndex = y * 32 + x;
  138. if (regionIndex >= regionsBuffer.Length || regionIndex < 0)
  139. return 255;
  140. else return regionsBuffer[y * 32 + x]; // I had got an exception here. For out of range. //Ok- fixed
  141. }
  142. #endregion Section 2 - world map regions
  143. #region Section 4 - Encounter pointer
  144. private const int SEC4_ENC_PER_CHUNK = 8; //there are 8 scene.out pointers per one block/entry
  145. public ushort[][] Encounters { get; private set; }
  146. private void Section4()
  147. {
  148. MemoryStream ms;
  149. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  150. {
  151. ms.Seek(sectionPointers[4 - 1], SeekOrigin.Begin);
  152. var encounterList = new List<ushort[]>();
  153. while (true)
  154. {
  155. var dwordTester = br.ReadUInt32();
  156. if (dwordTester == 0)
  157. break;
  158. ms.Seek(-4, SeekOrigin.Current);
  159. var sceneOutPointers = new ushort[SEC4_ENC_PER_CHUNK];
  160. for (var i = 0; i < SEC4_ENC_PER_CHUNK; i++)
  161. sceneOutPointers[i] = br.ReadUInt16();
  162. encounterList.Add(sceneOutPointers);
  163. }
  164. Encounters = encounterList.ToArray();
  165. }
  166. }
  167. public ushort[] GetEncounters(int pointer) => Encounters[pointer];
  168. #endregion Section 4 - Encounter pointer
  169. #region Section 6 - Encounter pointer (Lunar Cry)
  170. public ushort[][] EncountersLunar { get; private set; }
  171. private void Section6()
  172. {
  173. MemoryStream ms;
  174. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  175. {
  176. ms.Seek(sectionPointers[6 - 1], SeekOrigin.Begin);
  177. var encounterList = new List<ushort[]>();
  178. while (true)
  179. {
  180. var dwordTester = br.ReadUInt32();
  181. if (dwordTester == 0)
  182. break;
  183. ms.Seek(-4, SeekOrigin.Current);
  184. var sceneOutPointers = new ushort[SEC4_ENC_PER_CHUNK];
  185. for (var i = 0; i < SEC4_ENC_PER_CHUNK; i++)
  186. sceneOutPointers[i] = br.ReadUInt16();
  187. encounterList.Add(sceneOutPointers);
  188. }
  189. EncountersLunar = encounterList.ToArray();
  190. }
  191. }
  192. public ushort[] GetEncountersLunar(int pointer) => EncountersLunar[pointer];
  193. #endregion Section 6 - Encounter pointer (Lunar Cry)
  194. #region Section 8 - World map to field
  195. public enum worldScriptOpcodes : ushort
  196. {
  197. Mode1 = 0xFF01,
  198. flagBelow = 0xFF02, //word 2036BDE, on disc3 it's 0xECE
  199. flagAbove = 0xFF03, //see above
  200. Mode2 = 0xFF04, //probably so-called null animation
  201. EndZone = 0xFF05, //this splits the conditions- always at the end or in the middle of two zones
  202. SetRegionId = 0xFF06, //for example 283 (If I remember correctly) for Balamb
  203. playerPositionUnk = 0xFF07, //probably X and Y for worldmap
  204. wm2fieldEntryId = 0xFF08,
  205. CheckForVehicle = 0xFF09,
  206. Set1 = 0xFF0A,
  207. Set3If1OrNot4 = 0xFF0B,
  208. Set3IfNot2or5 = 0xFF0C,
  209. Set6IfNot2Or5 = 0xFF0D,
  210. squallXlocAbove = 0xFF0F,
  211. squallZLocAbove = 0xFF10,
  212. squallXlocBelow = 0xFF11,
  213. squallZlocBelow = 0xFF12,
  214. SetTexture = 0xFF13,
  215. ScriptEnd = 0xFF16,
  216. setUnk2 = 0xFF17,
  217. unkBelov2 = 0xFF1A,
  218. ReturnMinusOne = 0xFF1E, //return -1
  219. checkVehicleArgument = 0xFF25, //checks argument for vehicle Id
  220. }
  221. public struct worldMapScript
  222. {
  223. public worldScriptOpcodes opcode;
  224. public int argument;
  225. }
  226. public struct section8WarpZone
  227. {
  228. public int field;
  229. public bool bAlreadySetField; //only for openviii and testing purpouses- based on conditions one thing can
  230. //point to different fields. This is to make sure only first fieldId is set
  231. public int segmentId;
  232. public worldMapScript[] conditions;
  233. }
  234. public section8WarpZone[] section8WarpZones;
  235. /// <summary>
  236. /// Section is responsible for world map to field transition and works as a factor of conditions
  237. /// You have to read the header of 0xFF01 and read conditions up to 0xFF16
  238. /// The game engine checks given condition and returns TRUE or FALSE. If something fails, then it
  239. /// moves forward with checking. Currently we know wm2field pointer, vehicle conditions and
  240. /// that's more or less all we know
  241. /// </summary>
  242. public void Section8()
  243. {
  244. var localZones = new List<section8WarpZone>();
  245. MemoryStream ms;
  246. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  247. {
  248. ms.Seek(sectionPointers[8 - 1], SeekOrigin.Begin);
  249. var innerSec = GetInnerPointers(br);
  250. for (var i = 0; i < innerSec.Length; i++)
  251. {
  252. ms.Seek(sectionPointers[8 - 1] + innerSec[i], SeekOrigin.Begin);
  253. short Mode = 0;
  254. Mode = (short)br.ReadUInt32();
  255. var localZone = new section8WarpZone
  256. {
  257. field = -1
  258. };
  259. var conditions = new List<worldMapScript>();
  260. while (true)
  261. {
  262. var opcode = (worldScriptOpcodes)br.ReadUInt16();
  263. var argument = br.ReadUInt16();
  264. if (opcode == worldScriptOpcodes.ScriptEnd) //?? maybe
  265. {
  266. localZone.conditions = conditions.ToArray();
  267. localZones.Add(localZone);
  268. break;
  269. }
  270. switch (opcode)
  271. {
  272. case worldScriptOpcodes.SetRegionId:
  273. conditions.Add(new worldMapScript() { argument = argument, opcode = opcode });
  274. localZone.segmentId = argument;
  275. break;
  276. case worldScriptOpcodes.wm2fieldEntryId:
  277. conditions.Add(new worldMapScript() { argument = argument, opcode = opcode });
  278. localZone.field = argument;
  279. break;
  280. default:
  281. conditions.Add(new worldMapScript() { argument = argument, opcode = opcode });
  282. break;
  283. }
  284. }
  285. }
  286. }
  287. section8WarpZones = localZones.ToArray();
  288. }
  289. #endregion Section 8 - World map to field
  290. #region Section 9 - Field to World map
  291. public Vector3[] fieldToWorldMapLocations;
  292. private void Section9()
  293. {
  294. MemoryStream ms;
  295. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  296. {
  297. ms.Seek(sectionPointers[9 - 1], SeekOrigin.Begin);
  298. var sectionSize = sectionPointers[10 - 1] - sectionPointers[9 - 1] - 4;
  299. var entriesCount = sectionSize / 12;
  300. fieldToWorldMapLocations = new Vector3[entriesCount];
  301. for (var i = 0; i < entriesCount; i++)
  302. {
  303. var x = br.ReadInt32();
  304. var z = br.ReadInt32();
  305. int y = br.ReadInt16();
  306. fieldToWorldMapLocations[i].X = Extended.ConvertVanillaWorldXAxisToOpenVIII(x);
  307. fieldToWorldMapLocations[i].Y = Extended.ConvertVanillaWorldYAxisToOpenVIII(y);
  308. fieldToWorldMapLocations[i].Z = Extended.ConvertVanillaWorldZAxisToOpenVIII(z);
  309. ms.Seek(2, SeekOrigin.Current);
  310. }
  311. }
  312. }
  313. #endregion Section 9 - Field to World map
  314. //#region Section 10 - [UNKNOWN]/ Scripts
  315. //public static worldMapScript[][] section10Scripts;
  316. ///// <summary>
  317. ///// This file follows some schema of 0xFF01->0xFF04
  318. ///// </summary>
  319. //private void Section10()
  320. //{
  321. // List<List<worldMapScript>> scripts = new List<List<worldMapScript>>();
  322. // MemoryStream ms;
  323. // using (BinaryReader br = new BinaryReader(ms = new MemoryStream(buffer)))
  324. // {
  325. // ms.Seek(sectionPointers[8 - 1], SeekOrigin.Begin);
  326. // int[] innerSec = GetInnerPointers(br);
  327. // for (int i = 0; i < innerSec.Length; i++)
  328. // {
  329. // ms.Seek(sectionPointers[8 - 1] + innerSec[i], SeekOrigin.Begin);
  330. // short Mode = 0;
  331. // Mode = (short)br.ReadUInt32();
  332. // List<worldMapScript> localScript = new List<worldMapScript>();
  333. // while (true)
  334. // {
  335. // worldScriptOpcodes opcode = (worldScriptOpcodes)br.ReadUInt16();
  336. // ushort argument = br.ReadUInt16();
  337. // if (opcode == worldScriptOpcodes.EndZone) //?? maybe
  338. // {
  339. // scripts.Add(localScript);
  340. // break;
  341. // }
  342. // localScript.Add(new worldMapScript() { argument = argument, opcode = opcode });
  343. // }
  344. // }
  345. // }
  346. // section10Scripts = scripts.Select(x => x.ToArray()).ToArray();
  347. //}
  348. //#endregion Section 10 - [UNKNOWN]/ Scripts
  349. #region Section 11 - [UNKNOWN]
  350. public Vector3[] sec11Locations;
  351. private void Section11()
  352. {
  353. MemoryStream ms;
  354. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  355. {
  356. ms.Seek(sectionPointers[11 - 1], SeekOrigin.Begin);
  357. var entriesCount = sectionPointers[12 - 1] - sectionPointers[11 - 1] - 4;
  358. entriesCount /= 16;
  359. entriesCount--; //first entry is null
  360. ms.Seek(16, SeekOrigin.Current); //pass first entry, it's null
  361. sec11Locations = new Vector3[entriesCount];
  362. for(var i = 0; i<entriesCount; i++)
  363. {
  364. var x = br.ReadInt32();
  365. var z = br.ReadInt32();
  366. var y = br.ReadInt32();
  367. var x_ = Extended.ConvertVanillaWorldXAxisToOpenVIII(x);
  368. var y_ = Extended.ConvertVanillaWorldYAxisToOpenVIII(y);
  369. var z_ = Extended.ConvertVanillaWorldZAxisToOpenVIII(z);
  370. int unk = br.ReadInt16();
  371. int unk2 = br.ReadInt16();
  372. sec11Locations[i] = new Vector3(x_, y_, z_);
  373. }
  374. }
  375. //16 per entry- it's related to sec10
  376. /*
  377. v26 = wmsetS11 + 16 * v17;
  378. v83 = *(_DWORD *)v26;
  379. v84 = *(_DWORD *)(v26 + 4);
  380. v85 = *(_DWORD *)(v26 + 8);
  381. v86 = *(_DWORD *)(v26 + 12);
  382. v88 = *(_WORD *)(v26 + 12);
  383. v89 = *(_DWORD *)(v26 + 12) >> 16;
  384. v87 = 0;
  385. sub_5450D0(v2, v13, (unsigned __int8)v90, (_BYTE)v90 != 80 ? 0 : 4, (int)&v87, (int)&v83);
  386. */
  387. }
  388. #endregion Section 11 - [UNKNOWN]
  389. #region Section 14 - Side quest strings
  390. private FF8String[] sideQuestDialogues;
  391. private void Section14()
  392. {
  393. MemoryStream ms;
  394. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  395. {
  396. ms.Seek(sectionPointers[14 - 1], SeekOrigin.Begin);
  397. var innerSec = GetInnerPointers(br);
  398. sideQuestDialogues = new FF8String[innerSec.Length];
  399. for (var i = 0; i < innerSec.Length; i++)
  400. {
  401. ms.Seek(sectionPointers[14 - 1] + innerSec[i], SeekOrigin.Begin);
  402. sideQuestDialogues[i] = Extended.GetBinaryString(br);
  403. }
  404. }
  405. }
  406. public FF8String GetSection14Text(int index) => sideQuestDialogues[index];
  407. #endregion Section 14 - Side quest strings
  408. #region Section 16 - World map objects and vehicles
  409. private struct s16Model
  410. {
  411. public ushort cTriangles;
  412. public ushort cQuads;
  413. public ushort texPage;
  414. public ushort cVerts;
  415. public s16Triangle[] triangles;
  416. public s16Quad[] quads;
  417. public Vector4[] vertices;
  418. }
  419. [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 12)]
  420. private struct s16Triangle
  421. {
  422. public byte A, B, C;
  423. public byte semitransp;
  424. public byte ua, va;
  425. public byte ub, vb;
  426. public byte uc, vc;
  427. public ushort clut;
  428. }
  429. [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 16)]
  430. private struct s16Quad
  431. {
  432. public byte A, B, C, D;
  433. public byte ua, va;
  434. public byte ub, vb;
  435. public byte uc, vc;
  436. public byte ud, vd;
  437. public ushort clut;
  438. public byte semitransp;
  439. public byte unk;
  440. }
  441. private s16Model[] s16Models;
  442. private const float s16Scale = 16f;
  443. private void Section16()
  444. {
  445. MemoryStream ms;
  446. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  447. {
  448. ms.Seek(sectionPointers[16 - 1], SeekOrigin.Begin);
  449. var innerSec = GetInnerPointers(br);
  450. s16Models = new s16Model[innerSec.Length];
  451. for (var i = 0; i < innerSec.Length; i++)
  452. {
  453. ms.Seek(sectionPointers[16 - 1] + innerSec[i], SeekOrigin.Begin);
  454. s16Models[i].cTriangles = br.ReadUInt16();
  455. s16Models[i].cQuads = br.ReadUInt16();
  456. s16Models[i].texPage = br.ReadUInt16();
  457. s16Models[i].cVerts = br.ReadUInt16();
  458. s16Models[i].triangles = new s16Triangle[s16Models[i].cTriangles];
  459. s16Models[i].quads = new s16Quad[s16Models[i].cQuads];
  460. s16Models[i].vertices = new Vector4[s16Models[i].cVerts];
  461. for (var n = 0; n < s16Models[i].cTriangles; n++)
  462. s16Models[i].triangles[n] = Extended.ByteArrayToStructure<s16Triangle>(br.ReadBytes(Marshal.SizeOf(typeof(s16Triangle))));
  463. for (var n = 0; n < s16Models[i].cQuads; n++)
  464. s16Models[i].quads[n] = Extended.ByteArrayToStructure<s16Quad>(br.ReadBytes(Marshal.SizeOf(typeof(s16Quad))));
  465. for (var n = 0; n < s16Models[i].cVerts; n++)
  466. s16Models[i].vertices[n] = new Vector4(
  467. br.ReadInt16() / s16Scale,
  468. br.ReadInt16() / s16Scale,
  469. br.ReadInt16() / s16Scale,
  470. br.ReadUInt16()
  471. );
  472. }
  473. }
  474. }
  475. /// <summary>
  476. /// Gets simple geometry for vehicles. Takes the objectId, local Vector3 and quaternion for rotation. Returns tuple with data for
  477. /// renderer and byte[] with clut Ids
  478. /// </summary>
  479. /// <param name="objectId"></param>
  480. /// <param name="localTranslation"></param>
  481. /// <param name="rotation"></param>
  482. /// <param name="textureResolution">texture resolution for custom UV recalculation</param>
  483. /// <param name="textureOriginVector">texture vector for origin (x-832 based) and y zero based for custom UV recalculation</param>
  484. /// <returns></returns>
  485. public Tuple<VertexPositionTexture[], byte[]> GetVehicleGeometry(int objectId, Vector3 localTranslation, Quaternion rotation, Vector2? textureResolution = null, Vector2? textureOriginVector = null)
  486. {
  487. //This ones are simple- static meshes that are translated only by basic localTranslation and quaternion. Nothing much
  488. var vptList = new List<VertexPositionTexture>();
  489. var vptTextureIndexList = new List<byte>();
  490. //step 1. grab triangles
  491. if (objectId > s16Models.Length)
  492. return new Tuple<VertexPositionTexture[], byte[]>(new VertexPositionTexture[0], new byte[0]); //error
  493. var Model = s16Models[objectId];
  494. var texWidth = 256f;
  495. var texHeight = 256f;
  496. byte localXadd = 0;
  497. byte localYadd = 0;
  498. if (textureResolution != null)
  499. { texWidth = textureResolution.Value.X; texHeight = textureResolution.Value.Y; };
  500. if (textureOriginVector != null)
  501. { localXadd = (byte)textureOriginVector.Value.X; localYadd = (byte)textureOriginVector.Value.Y; }
  502. for (var i = 0; i < Model.cTriangles; i++)
  503. {
  504. var a = Extended.ShrinkVector4ToVector3(Model.vertices[Model.triangles[i].A], true);
  505. var b = Extended.ShrinkVector4ToVector3(Model.vertices[Model.triangles[i].B], true);
  506. var c = Extended.ShrinkVector4ToVector3(Model.vertices[Model.triangles[i].C], true);
  507. a = Vector3.Transform(a, Matrix.CreateFromQuaternion(rotation));
  508. b = Vector3.Transform(b, Matrix.CreateFromQuaternion(rotation));
  509. c = Vector3.Transform(c, Matrix.CreateFromQuaternion(rotation));
  510. a += localTranslation;
  511. b += localTranslation;
  512. c += localTranslation;
  513. vptList.Add(new VertexPositionTexture(
  514. a, new Vector2(
  515. (Model.triangles[i].ua - localXadd) / texWidth,
  516. (Model.triangles[i].va - localYadd) / texHeight)
  517. ));
  518. vptList.Add(new VertexPositionTexture(
  519. b, new Vector2(
  520. (Model.triangles[i].ub - localXadd) / texWidth,
  521. (Model.triangles[i].vb - localYadd) / texHeight)
  522. ));
  523. vptList.Add(new VertexPositionTexture(
  524. c, new Vector2(
  525. (Model.triangles[i].uc - localXadd) / texWidth,
  526. (Model.triangles[i].vc - localYadd) / texHeight)
  527. ));
  528. vptTextureIndexList.Add((byte)Model.triangles[i].clut);
  529. vptTextureIndexList.Add((byte)Model.triangles[i].clut);
  530. vptTextureIndexList.Add((byte)Model.triangles[i].clut);
  531. }
  532. for (var i = 0; i < Model.cQuads; i++)
  533. {
  534. var a = Extended.ShrinkVector4ToVector3(Model.vertices[Model.quads[i].A], true);
  535. var b = Extended.ShrinkVector4ToVector3(Model.vertices[Model.quads[i].B], true);
  536. var c = Extended.ShrinkVector4ToVector3(Model.vertices[Model.quads[i].C], true);
  537. var d = Extended.ShrinkVector4ToVector3(Model.vertices[Model.quads[i].D], true);
  538. a = Vector3.Transform(a, Matrix.CreateFromQuaternion(rotation));
  539. b = Vector3.Transform(b, Matrix.CreateFromQuaternion(rotation));
  540. c = Vector3.Transform(c, Matrix.CreateFromQuaternion(rotation));
  541. d = Vector3.Transform(d, Matrix.CreateFromQuaternion(rotation));
  542. a += localTranslation;
  543. b += localTranslation;
  544. c += localTranslation;
  545. d += localTranslation;
  546. vptList.Add(new VertexPositionTexture(
  547. a, new Vector2(
  548. (Model.quads[i].ua - localXadd) / texWidth,
  549. (Model.quads[i].va - localYadd) / texHeight)
  550. ));
  551. vptList.Add(new VertexPositionTexture(
  552. b, new Vector2(
  553. (Model.quads[i].ub - localXadd) / texWidth,
  554. (Model.quads[i].vb - localYadd) / texHeight)
  555. ));
  556. vptList.Add(new VertexPositionTexture(
  557. d, new Vector2(
  558. (Model.quads[i].ud - localXadd) / texWidth,
  559. (Model.quads[i].vd - localYadd) / texHeight)
  560. ));
  561. vptList.Add(new VertexPositionTexture(
  562. a, new Vector2(
  563. (Model.quads[i].ua - localXadd) / texWidth,
  564. (Model.quads[i].va - localYadd) / texHeight)
  565. ));
  566. vptList.Add(new VertexPositionTexture(
  567. c, new Vector2(
  568. (Model.quads[i].uc - localXadd) / texWidth,
  569. (Model.quads[i].vc - localYadd) / texHeight)
  570. ));
  571. vptList.Add(new VertexPositionTexture(
  572. d, new Vector2(
  573. (Model.quads[i].ud - localXadd) / texWidth,
  574. (Model.quads[i].vd - localYadd) / texHeight)
  575. ));
  576. vptTextureIndexList.Add((byte)Model.quads[i].clut);
  577. vptTextureIndexList.Add((byte)Model.quads[i].clut);
  578. vptTextureIndexList.Add((byte)Model.quads[i].clut);
  579. vptTextureIndexList.Add((byte)Model.quads[i].clut);
  580. vptTextureIndexList.Add((byte)Model.quads[i].clut);
  581. vptTextureIndexList.Add((byte)Model.quads[i].clut);
  582. }
  583. return new Tuple<VertexPositionTexture[], byte[]>(vptList.ToArray(), vptTextureIndexList.ToArray());
  584. }
  585. public int GetVehicleModelsCount() => s16Models.Length;
  586. #endregion Section 16 - World map objects and vehicles
  587. #region Section 17 + 41 - World map texture animations for beach[17] and water[41]
  588. /*
  589. * Section 17 is responsible for beach texture animations. It's a double file- first it contains chunks. Every chunk contains
  590. * animation frames information.
  591. * There's also new texture structure; I haven't seen such structures earlier. Palette is grabbed from section38
  592. */
  593. //MODDING NOTE: Thanks to OpenVIII you can now create bigger chunks than 64x64/64x32 without worrying about SSIGPU VRAM!
  594. [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 8)]
  595. public struct textureAnimation
  596. {
  597. /// <summary>
  598. /// Unknown
  599. /// </summary>
  600. public byte unk;
  601. /// <summary>
  602. /// Animation speed/timeout between frames- usually 0x20. 1 is the fastest. 0 is invalid. 0xFF is long
  603. /// </summary>
  604. public byte animTimeout;
  605. /// <summary>
  606. /// Frames Count- controls how many frames are valid. Usually 4
  607. /// </summary>
  608. public byte framesCount;
  609. /// <summary>
  610. /// If true then frames loop backward. For example 0-1-2-3-2-1-0. If false, then 0-1-2-3-0-1-2-3
  611. /// </summary>
  612. public byte bLooping;
  613. /// <summary>
  614. /// Unused in openviii. It is starting VRAM offset for this texture to be stored
  615. /// </summary>
  616. public ushort vramOrigX;
  617. /// <summary>
  618. /// Unused in openviii. It is starting VRAM offset for this texture to be stored
  619. /// </summary>
  620. public ushort vramOrigY;
  621. /// <summary>
  622. /// Contains texture data for given frame
  623. /// </summary>
  624. /// Did you know that there's no way to tell C# to not marshal one field?
  625. public Texture2D[] framesTextures;
  626. /// <summary>
  627. /// Contains palette data for given frame- because section41 is palette (version 17)
  628. /// </summary>
  629. public Color[][] framesPalette;
  630. /// <summary>
  631. /// OpenVIII helper value- if true then index is incrementing- if false then index is decrementing
  632. /// </summary>
  633. public bool bIncrementing;
  634. /// <summary>
  635. /// OpenVIII helper value- holds current frame index
  636. /// </summary>
  637. public int currentAnimationIndex;
  638. /// <summary>
  639. /// OpenVIII helper value- holds total deltaTime to be used with timeout calculation
  640. /// </summary>
  641. public TimeSpan deltaTime;
  642. }
  643. private textureAnimation[] beachAnimations;
  644. private textureAnimation[] waterAnimations;
  645. public void Section17()
  646. {
  647. if (Memory.Graphics?.GraphicsDevice != null)
  648. {
  649. int[] innerPointers;
  650. MemoryStream ms;
  651. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  652. {
  653. ms.Seek(sectionPointers[17 - 1], SeekOrigin.Begin);
  654. innerPointers = GetInnerPointers(br);
  655. BeachAnimations = new textureAnimation[innerPointers.Length];
  656. for (var i = 0; i < innerPointers.Length; i++)
  657. BeachAnimations[i] = textureAnimation_ParseBlock(sectionPointers[17 - 1] + innerPointers[i], i, ms, br);
  658. //for (int i = 0; i < beachAnimations.Length; i++)
  659. // for (int n = 0; n < beachAnimations[i].framesCount; n++)
  660. // Extended.DumpTexture(beachAnimations[i].framesTextures[n], $"D:\\b_{i}_{n}.png");
  661. }
  662. }
  663. }
  664. public void Section41()
  665. {
  666. int[] innerPointers;
  667. MemoryStream ms;
  668. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  669. {
  670. ms.Seek(sectionPointers[41 - 1], SeekOrigin.Begin);
  671. innerPointers = GetInnerPointers(br);
  672. waterAnimations = new textureAnimation[innerPointers.Length];
  673. for (var i = 0; i < innerPointers.Length; i++)
  674. waterAnimations[i] = textureAnimation_ParseBlock(sectionPointers[41 - 1] + innerPointers[i], -1, ms, br);
  675. }
  676. }
  677. private const int sec17_imageHeaderSize = 12; //dword;dword; sizeDword
  678. /// <summary>
  679. /// Valid for section 41 and 17!
  680. /// </summary>
  681. /// <param name="offset"></param>
  682. /// <param name="texturePointer">-1 means section41</param>
  683. /// <param name="ms"></param>
  684. /// <param name="br"></param>
  685. /// <returns></returns>
  686. private textureAnimation textureAnimation_ParseBlock(int offset, int texturePointer, MemoryStream ms, BinaryReader br)
  687. {
  688. ms.Seek(offset, SeekOrigin.Begin);
  689. //beachAnimation animation = Extended.ByteArrayToStructure<beachAnimation>(br.ReadBytes(8));
  690. var animation = new textureAnimation() //not using Extension because Marshalling doesn't go well with [][] even
  691. //though it's not really even used!
  692. {
  693. unk = br.ReadByte(),
  694. animTimeout = br.ReadByte(),
  695. framesCount = br.ReadByte(),
  696. bLooping = br.ReadByte(),
  697. vramOrigX = br.ReadUInt16(),
  698. vramOrigY = br.ReadUInt16()
  699. };
  700. if (texturePointer == -1)
  701. ms.Seek(4, SeekOrigin.Current); //there are two WORD's that feel like they have again the palette X and Y but why??
  702. var preImagePosition = (uint)ms.Position;
  703. var imagePointers = new uint[animation.framesCount];
  704. Color[] palette = null;
  705. Color[][] customPalette = null;
  706. if (texturePointer != -1)
  707. palette = GetWorldMapTexturePalette(texturePointer == 0 ?
  708. Section38_textures.beach : Section38_textures.beachE, 0);
  709. for (var i = 0; i < animation.framesCount; i++)
  710. imagePointers[i] = br.ReadUInt32() + preImagePosition;
  711. Texture2D[] animationFrames = null;
  712. if (texturePointer != -1)
  713. animationFrames = new Texture2D[imagePointers.Length];
  714. else
  715. {
  716. customPalette = new Color[imagePointers.Length][];
  717. animationFrames = new Texture2D[imagePointers.Length];
  718. }
  719. for (var i = 0; i < animation.framesCount; i++)
  720. {
  721. ms.Seek(imagePointers[i], SeekOrigin.Begin);
  722. var unknownHeader = br.ReadUInt32();
  723. var unknownHeader_ = br.ReadUInt32();
  724. if (unknownHeader != 18 && unknownHeader != 17) //I don't know why 18 [those are some version or flags?]
  725. throw new Exception("wmset::section17::texturePointerHeader != 17 or 18");
  726. if (unknownHeader_ != 1) //I don't know why 1
  727. throw new Exception("wmset::section17::texturePointerHeader+4 != 1");
  728. _ = br.ReadUInt32() - sec17_imageHeaderSize; //imageSize, but doesn't really matter here
  729. _ = br.ReadUInt32(); //unknown
  730. var width = (ushort)(br.ReadUInt16() * 2);
  731. var height = br.ReadUInt16();
  732. Texture2D texture;
  733. Color[] texBuffer;
  734. if (texturePointer != -1)
  735. {
  736. texture = new Texture2D(Memory.Graphics.GraphicsDevice, width, height, false, SurfaceFormat.Color);
  737. texBuffer = new Color[width * height]; //32bpp because Color is ARGB byte : struct
  738. if (palette.Length == 16)
  739. {
  740. for (var m = 0; m < texBuffer.Length; m += 2)
  741. {
  742. var b = br.ReadByte();
  743. texBuffer[m] = palette[b & 0xF];
  744. texBuffer[m + 1] = palette[b >> 4];
  745. }
  746. }
  747. else
  748. for (var m = 0; m < texBuffer.Length; m++)
  749. {
  750. var b = br.ReadByte();
  751. texBuffer[m] = palette[b];
  752. }
  753. texture.SetData(texBuffer);
  754. animationFrames[i] = texture;
  755. }
  756. else
  757. {
  758. width /= 2;
  759. customPalette[i] = new Color[width]; //in sec41 width is the number of colours
  760. for (var m = 0; m < width; m++)
  761. {
  762. var color = br.ReadUInt16();
  763. customPalette[i][m] = Texture_Base.ABGR1555toRGBA32bit(color);
  764. }
  765. }
  766. }
  767. animation.framesTextures = animationFrames;
  768. animation.framesPalette = customPalette;
  769. return animation;
  770. }
  771. public textureAnimation GetBeachAnimation(int animationId) => BeachAnimations[animationId];
  772. /// <summary>
  773. /// Gets chunk from beachAnim atlas (because they are structured 2x2)
  774. /// </summary>
  775. /// <param name="animationId">index of the animation wanted- there are two beach anim sets and one unknown</param>
  776. /// <param name="frameId">naturally the frame/keyframe of the animation</param>
  777. /// <returns></returns>
  778. public Texture2D GetBeachAnimationTextureFrame(int animationId, int frameId)
  779. => BeachAnimations[animationId].framesTextures[frameId];
  780. public Color[] GetWaterAnimationPalettes(int animId, int frameId) => waterAnimations[animId].framesPalette[frameId].ToArray();
  781. public Texture2D GetWaterAnimationTextureFrame(int animationId, int frameId) => WaterAnimations[animationId].framesTextures[frameId];
  782. internal textureAnimation[] BeachAnimations { get => beachAnimations; set => beachAnimations = value; }
  783. internal textureAnimation[] WaterAnimations { get => waterAnimations; set => waterAnimations = value; }
  784. #endregion Section 17 + 41 - World map texture animations for beach[17] and water[41]
  785. #region Section 32 - World map location names
  786. private FF8String[] locationsNames;
  787. private void Section32()
  788. {
  789. MemoryStream ms;
  790. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  791. {
  792. ms.Seek(sectionPointers[32 - 1], SeekOrigin.Begin);
  793. var innerSec = GetInnerPointers(br);
  794. locationsNames = new FF8String[innerSec.Length];
  795. for (var i = 0; i < locationsNames.Length; i++)
  796. {
  797. ms.Seek(sectionPointers[32 - 1] + innerSec[i], SeekOrigin.Begin);
  798. locationsNames[i] = Extended.GetBinaryString(br);
  799. }
  800. }
  801. }
  802. public FF8String GetLocationName(int index) => locationsNames[index];
  803. public int GetlocationNamesLength => locationsNames.Length;
  804. #endregion Section 32 - World map location names
  805. #region Section 33 - Sky and ambient colour
  806. [StructLayout(LayoutKind.Sequential,Pack =1, Size =52)]
  807. public struct sec33SkyEntry
  808. {
  809. public int positionX;
  810. public int positionY;
  811. public int positionZ;
  812. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  813. public byte[] shadows;
  814. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  815. public byte[] vehicles;
  816. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  817. public byte[] skyGradientTop;
  818. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  819. public byte[] skyGradientCenter;
  820. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  821. public byte[] skyGradientBottom;
  822. public byte unk1_1;
  823. public byte unk1_2; //unk1_2 - R value [0-127]
  824. public byte unk1_3;
  825. public byte unk1_4; //unk1_4 = R value [0-127]
  826. public byte unk2_1;
  827. public byte unk2_2; //unk2_2 - G value [0-127]
  828. public byte unk2_3;
  829. public byte unk2_4; //unk2_4 = G value [0-127]
  830. public byte unk3_1;
  831. public byte unk3_2; //unk3_2 - B value [0-127]
  832. public byte unk3_3;
  833. public byte unk3_4; //unk3_4 = B value [0-127]
  834. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  835. public byte[] unk4; //wrong place- one of the byte was switch for full colorization of wm
  836. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  837. public byte[] unk5; //wrong place- one of the byte was switch for full colorization of wm
  838. public Color GetShadowsColor() =>
  839. new Color(shadows[0], shadows[1], shadows[2], (byte)255);
  840. public Color GetVehiclesColor() =>
  841. new Color(vehicles[0], vehicles[1], vehicles[2], (byte)255);
  842. public Color GetTopBGColor() =>
  843. new Color(skyGradientTop[0], skyGradientTop[1], skyGradientTop[2], (byte)255);
  844. public Color GetCenterBGColor() =>
  845. new Color(skyGradientCenter[0], skyGradientCenter[1], skyGradientCenter[2], (byte)255);
  846. public Color GetBottomBGColor() =>
  847. new Color(skyGradientBottom[0], skyGradientBottom[1], skyGradientBottom[2], (byte)255);
  848. public Vector3 GetLocation() =>
  849. new Vector3()
  850. {
  851. X = Extended.ConvertVanillaWorldXAxisToOpenVIII(positionX),
  852. Y = Extended.ConvertVanillaWorldYAxisToOpenVIII(positionZ),
  853. Z = Extended.ConvertVanillaWorldZAxisToOpenVIII(positionY)
  854. };
  855. }
  856. public sec33SkyEntry[] skyColors;
  857. private void Section33()
  858. {
  859. using (var ms = new MemoryStream(buffer)) //didn't know you can skip braces with using
  860. using (var br = new BinaryReader(ms))
  861. {
  862. ms.Seek(sectionPointers[33 - 1], SeekOrigin.Begin);
  863. var innerSec = GetInnerPointers(br);
  864. var skyEntries = new List<sec33SkyEntry>();
  865. for (var i = 0; i < innerSec.Length; i++)
  866. {
  867. ms.Seek(sectionPointers[33 - 1] + innerSec[i], SeekOrigin.Begin);
  868. skyEntries.Add(Extended.ByteArrayToStructure<sec33SkyEntry>(br.ReadBytes(52)));
  869. }
  870. skyColors = skyEntries.ToArray();
  871. }
  872. }
  873. #endregion
  874. #region Section 38 - World map textures archive
  875. /// <summary>
  876. /// Section 38: World map textures archive
  877. /// </summary>
  878. ///
  879. private List<TextureHandler[]> sec38_textures;
  880. private List<Color[][]> sec38_pals; //because other sections rely on palettes of wmset.38
  881. private TIM2 waterTim;
  882. private TIM2 waterTim2;
  883. private TIM2 waterTim3;
  884. private TIM2 waterTim4;
  885. public enum Section38_textures
  886. {
  887. wmtex0,
  888. wmtex1,
  889. wmtex2,
  890. wmtex3,
  891. wmtex4,
  892. wmtex5,
  893. wmtex6,
  894. wmtex7,
  895. waterTex,
  896. moon,
  897. clouds,
  898. worldmapMinimap,
  899. wmunk12,
  900. wmunk13,
  901. wmfx14,
  902. wmfx_bush,
  903. waterTex2,
  904. waterTex3,
  905. waterTex4,
  906. waterfall,
  907. waterTex5,
  908. beach,
  909. beachE,
  910. waterTex6,
  911. minimapPointer,
  912. minimapGunbladePointer,
  913. wmfx26,
  914. wmunk27,
  915. wmfx28,
  916. wmunk29,
  917. wmunk30,
  918. wmunk31,
  919. wmfx32,
  920. wmunk33,
  921. shadowBig,
  922. magicBarrier
  923. }
  924. private void Section38()
  925. {
  926. sec38_pals = new List<Color[][]>();
  927. MemoryStream ms;
  928. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  929. {
  930. ms.Seek(sectionPointers[38 - 1], SeekOrigin.Begin);
  931. var innerSec = GetInnerPointers(br);
  932. sec38_textures = new List<TextureHandler[]>();
  933. for (var i = 0; i < innerSec.Length; i++)
  934. {
  935. var tim = new TIM2(buffer, (uint)(sectionPointers[38 - 1] + innerSec[i]));
  936. if (i == (int)Section38_textures.waterTex2)
  937. waterTim = tim;
  938. if (i == (int)Section38_textures.waterTex3)
  939. waterTim2 = tim;
  940. if (i == (int)Section38_textures.waterTex4)
  941. waterTim3 = tim;
  942. if (i == (int)Section38_textures.waterfall)
  943. waterTim4 = tim;
  944. if (tim.GetBytesPerPixel == 4)
  945. if (tim.GetClutSize != (tim.GetClutCount * tim.GetColorsCountPerPalette)) //broken header, force our own values
  946. {
  947. tim.ForceSetClutColors(16);
  948. tim.ForceSetClutCount((ushort)(tim.GetClutSize / 16));
  949. }
  950. sec38_textures.Add(new TextureHandler[tim.GetClutCount]);
  951. sec38_pals.Add(new Color[tim.GetClutCount][]);
  952. for (ushort k = 0; k < sec38_textures[i].Length; k++)
  953. {
  954. var table = tim.GetPalette(k);
  955. sec38_pals[i][k] = table;
  956. sec38_textures[i][k] = TextureHandler.Create($"wmset_tim38_{(i + 1).ToString("D2")}.tim", tim, k, table);
  957. }
  958. }
  959. }
  960. CreateWaterTextureAtlas();
  961. }
  962. private const int WATERBLOCKTEXW = 64; //should be probably dynamic to conform mods
  963. private const int WATERBLOCKTEXH = 64;
  964. private Texture2D waterAtlas;
  965. /// <summary>
  966. /// Creates atlas with WaterBlockWidth*4 px based on 8BPP TIMs (for animated WM). Therefore user can replace
  967. /// individual texture without breaking the atlas as it's created basing on TextureHandler
  968. /// but their size must conform 4x2 layout where each block has the SAME resolution
  969. /// </summary>
  970. private void CreateWaterTextureAtlas()
  971. {
  972. /*
  973. * for animated water materials there's ANOTHER water texture that is NOT watertex, but is rather built from various
  974. * blocks in independent TIMs- they are NOT 4BPP but 8BPP- although they are not correctly ordered- once again
  975. * their VRAM x and y kick-in, but we got it covered- the blocks are 4x4 (64px per block). The blocks order is:
  976. * 0 64 128 192
  977. * 0 [24] [17] [22] [23]
  978. * 64 [18] [19] [20] [21]
  979. */
  980. if (Memory.Graphics?.GraphicsDevice != null)
  981. {
  982. waterAtlas = new Texture2D(Memory.Graphics.GraphicsDevice, WATERBLOCKTEXW << 2, WATERBLOCKTEXH << 1, false, SurfaceFormat.Color); //64<<2 is 256
  983. WaterTextureAtlasPutChunk(Section38_textures.waterTex6, 0, 0); // 0 0
  984. WaterTextureAtlasPutChunk(Section38_textures.waterTex2, WATERBLOCKTEXW, 0); // 64 0
  985. WaterTextureAtlasPutChunk(Section38_textures.beach, WATERBLOCKTEXW * 2, 0); // 128 0
  986. WaterTextureAtlasPutChunk(Section38_textures.beachE, WATERBLOCKTEXW * 3, 0); // 192 0
  987. WaterTextureAtlasPutChunk(Section38_textures.waterTex3, 0, WATERBLOCKTEXH); // 0 64
  988. WaterTextureAtlasPutChunk(Section38_textures.waterTex4, WATERBLOCKTEXW, WATERBLOCKTEXH); // 64 64
  989. WaterTextureAtlasPutChunk(Section38_textures.waterfall, WATERBLOCKTEXW * 2, WATERBLOCKTEXH); // 128 64
  990. WaterTextureAtlasPutChunk(Section38_textures.waterTex5, WATERBLOCKTEXW * 3, WATERBLOCKTEXH); // 192 64
  991. Extended.DumpTexture(waterAtlas, "D:/test.png");
  992. }
  993. }
  994. private void WaterTextureAtlasPutChunk(Section38_textures textureIndex, int x, int y)
  995. {
  996. var atlasChunk = (Texture2D)GetWorldMapTexture(textureIndex, 0); //there's only one clut
  997. var chunkBuffer = new byte[WATERBLOCKTEXW * WATERBLOCKTEXH * 4];
  998. atlasChunk.GetData(level: 0, rect: new Rectangle(0, 0, WATERBLOCKTEXW, WATERBLOCKTEXH), chunkBuffer, 0, chunkBuffer.Length);
  999. waterAtlas.SetData(0, new Rectangle(x, y, WATERBLOCKTEXW, WATERBLOCKTEXH), chunkBuffer, 0, chunkBuffer.Length);
  1000. }
  1001. private void UpdateWaterTextureAtlasChunk(Texture2D animatedChunk, int x, int y)
  1002. {
  1003. var chunkBuffer = new byte[WATERBLOCKTEXW * WATERBLOCKTEXH * 4];
  1004. animatedChunk.GetData(level: 0, rect: new Rectangle(0, 0, WATERBLOCKTEXW, WATERBLOCKTEXH), chunkBuffer, 0, chunkBuffer.Length);
  1005. waterAtlas.SetData(0, new Rectangle(x, y, WATERBLOCKTEXW, WATERBLOCKTEXH), chunkBuffer, 0, chunkBuffer.Length);
  1006. }
  1007. /// <summary>
  1008. /// Gets textures from section 38
  1009. /// </summary>
  1010. /// <param name="index"></param>
  1011. /// <param name="clut"></param>
  1012. /// <returns></returns>
  1013. public TextureHandler GetWorldMapTexture(Section38_textures index, int clut)
  1014. => sec38_textures[(int)index][clut];
  1015. /// <summary>
  1016. /// Gets the pre-made atlas from TIMs- do not change to TextureHandler! This atlas
  1017. /// is final product from moddable TextureHandlers- it shouldn't be directly changed
  1018. /// </summary>
  1019. /// <returns></returns>
  1020. public Texture2D GetWorldMapWaterTexture() => waterAtlas;
  1021. public Color[] GetWorldMapTexturePalette(Section38_textures index, int clut)
  1022. => sec38_pals[(int)index][clut];
  1023. public void UpdateWorldMapWaterTexturePaletteForAnimation(int index, Color[] palette)
  1024. {
  1025. if (waterTim == null)
  1026. return;
  1027. Texture2D animatedChunk = null;
  1028. int x = 0, y = 0;
  1029. switch (index)
  1030. {
  1031. case 0:
  1032. animatedChunk = waterTim2.GetTexture(palette);
  1033. x = 0;
  1034. y = WATERBLOCKTEXH;
  1035. break;
  1036. case 3:
  1037. animatedChunk = waterTim.GetTexture(palette);
  1038. x = WATERBLOCKTEXW;
  1039. y = 0;
  1040. break;
  1041. case 2:
  1042. animatedChunk = waterTim4.GetTexture(palette);
  1043. x = WATERBLOCKTEXW * 2;
  1044. y = WATERBLOCKTEXH;
  1045. break;
  1046. case 1:
  1047. animatedChunk = waterTim3.GetTexture(palette);
  1048. x = WATERBLOCKTEXW;
  1049. y = WATERBLOCKTEXH;
  1050. break;
  1051. }
  1052. UpdateWaterTextureAtlasChunk(animatedChunk, x, y);
  1053. }
  1054. #endregion Section 38 - World map textures archive
  1055. #region Section 39 - Textures of roads, train tracks and bridges
  1056. private const int SEC39_VRAM_STARTX = 832; //this is beginning of origX to map to one texture
  1057. private const int SEC39_VRAM_STARTY = 256; //used to map VRAM, but here it's used to create new atlas
  1058. private const int VRAM_TEXBLOCKWIDTH = 256; //wm faces ask VRAM, not texture, so the block is 256px in VRAM + additional chunks from other files that we deal normally as Tex2D[]
  1059. private const int VRAM_TEXBLOCKHEIGHT = 256; //see above
  1060. private const int VRAM_BLOCKSIZE = 32; // =VRAM_BLOCKSTEP*4 - one origX of 16 is actually 16/2=8*32=finalXorig
  1061. private const int VRAM_BLOCKSTEP = 8;
  1062. private TextureHandler sec39_texture;
  1063. /// <summary>
  1064. /// Section 39: Textures of roads, train tracks and bridge
  1065. /// </summary>
  1066. private void Section39()
  1067. {
  1068. if (Memory.Graphics?.GraphicsDevice != null)
  1069. {
  1070. MemoryStream ms;
  1071. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  1072. {
  1073. ms.Seek(sectionPointers[39 - 1], SeekOrigin.Begin);
  1074. var innerSec = GetInnerPointers(br);
  1075. var sec39_texture = new Texture2D(Memory.Graphics.GraphicsDevice, VRAM_TEXBLOCKWIDTH, VRAM_TEXBLOCKHEIGHT, false, SurfaceFormat.Color);
  1076. for (var i = 0; i < innerSec.Length; i++)
  1077. {
  1078. var tim = new TIM2(buffer, (uint)(sectionPointers[39 - 1] + innerSec[i]));
  1079. var atlasChunk = tim.GetTexture(0);
  1080. var chunkBuffer = new byte[atlasChunk.Width * atlasChunk.Height * 4];
  1081. atlasChunk.GetData(chunkBuffer, 0, chunkBuffer.Length);
  1082. var newX = tim.GetOrigX - SEC39_VRAM_STARTX;
  1083. var newY = tim.GetOrigY - SEC39_VRAM_STARTY;
  1084. newX = (newX / VRAM_BLOCKSTEP) * VRAM_BLOCKSIZE;
  1085. sec39_texture.SetData(0, new Rectangle(newX, newY, atlasChunk.Width, atlasChunk.Height), chunkBuffer, 0, chunkBuffer.Length);
  1086. }
  1087. this.sec39_texture = TextureHandler.Create($"wmset_tim39.tim", new Texture2DWrapper(sec39_texture), 0, null);
  1088. //sec39_texture = TextureHandler.Create($"wmset_tim{(i + 1).ToString("D2")}.tim", new TIM2(buffer, (uint)(sectionPointers[39 - 1] + innerSec[i])), k, null);
  1089. }
  1090. }
  1091. }
  1092. public enum Section39_Textures
  1093. {
  1094. train,
  1095. bridgeTrack,
  1096. trainMetal,
  1097. trainMetalCrossTrain,
  1098. TrainCrossTrainMetal,
  1099. asphalt,
  1100. dirtWay,
  1101. dirtWay2,
  1102. dirtWay3,
  1103. desertWay,
  1104. desertWay2,
  1105. desertWay3,
  1106. unknownMetal
  1107. }
  1108. /// <summary>
  1109. /// Gets textures from Section39
  1110. /// </summary>
  1111. /// <returns></returns>
  1112. public Texture2D GetRoadsMiscTextures() => (Texture2D)sec39_texture;
  1113. #endregion Section 39 - Textures of roads, train tracks and bridges
  1114. #region Section 42 - objects and vehicles textures
  1115. private const int SEC42_VRAM_STARTX = 832; //this is beginning of origX to map to one texture
  1116. private TextureHandler[][] vehicleTextures;
  1117. private Vector2[] timOrigHolder;
  1118. public enum VehicleTextureEnum
  1119. {
  1120. BalambGarden,
  1121. BalambHalo,
  1122. TrainA_locomotive,
  1123. TrainA_cab,
  1124. TrainB_locomotive,
  1125. TrainB_cab,
  1126. TrainC_locomotive,
  1127. TrainC_cab,
  1128. TrainC_cab2,
  1129. Car1,
  1130. Car2,
  1131. Car3,
  1132. TrainRoadBlockade,
  1133. unk2,
  1134. Vessel,
  1135. GalbadiaGarden,
  1136. GalbadiaHalo,
  1137. Car4,
  1138. Car5,
  1139. Car6,
  1140. Car7,
  1141. Car8,
  1142. Car9,
  1143. Car10,
  1144. WhiteSeed,
  1145. LunaticPandora,
  1146. LunaticPandora2,
  1147. LunaticPanodra_inside,
  1148. UltimeciaGate,
  1149. UltimeciaBarrier,
  1150. Cactuar,
  1151. RoadBlockade,
  1152. UltimeciaBarrier2
  1153. }
  1154. private void Section42()
  1155. {
  1156. var vehTextures = new List<TextureHandler[]>();
  1157. var timOriginHolderList = new List<Vector2>(); //VRAM atlas, holds X and Y origins for atlasing- here for calculating new UV
  1158. MemoryStream ms;
  1159. using (var br = new BinaryReader(ms = new MemoryStream(buffer)))
  1160. {
  1161. ms.Seek(sectionPointers[42 - 1], SeekOrigin.Begin);
  1162. var innerSec = GetInnerPointers(br);
  1163. for (var i = 0; i < innerSec.Length; i++)
  1164. {
  1165. var tim = new TIM2(buffer, (uint)(sectionPointers[42 - 1] + innerSec[i]));
  1166. timOriginHolderList.Add(new Vector2((tim.GetOrigX - SEC42_VRAM_STARTX) * 4, tim.GetOrigY));
  1167. vehTextures.Add(new TextureHandler[tim.GetClutCount]);
  1168. for (ushort k = 0; k < vehTextures[i].Length; k++)
  1169. vehTextures[i][k] = TextureHandler.Create($"wmset_tim42_{(i + 1).ToString("D2")}.tim", tim, k, null);
  1170. }
  1171. }
  1172. vehicleTextures = vehTextures.ToArray();
  1173. timOrigHolder = timOriginHolderList.ToArray();
  1174. }
  1175. public TextureHandler GetVehicleTexture(int index, int clut)
  1176. => vehicleTextures[index][clut];
  1177. public TextureHandler GetVehicleTexture(VehicleTextureEnum index, int clut) => vehicleTextures[(int)index][clut];
  1178. /// <summary>
  1179. /// Gets X and Y tim origin (psx VRAM) for recalculating UV
  1180. /// </summary>
  1181. /// <param name="index"></param>
  1182. /// <param name="clut"></param>
  1183. /// <returns></returns>
  1184. public Vector2 GetVehicleTextureOriginVector(VehicleTextureEnum index, int clut) => timOrigHolder[(int)index];
  1185. /// <summary>
  1186. /// Gets X and Y tim origin (psx VRAM) for recalculating UV
  1187. /// </summary>
  1188. /// <param name="index"></param>
  1189. /// <param name="clut"></param>
  1190. /// <returns></returns>
  1191. public Vector2 GetVehicleTextureOriginVector(int index, int clut) => timOrigHolder[index];
  1192. #region IDisposable Support
  1193. private bool disposedValue = false; // To detect redundant calls
  1194. protected virtual void Dispose(bool disposing)
  1195. {
  1196. if (!disposedValue)
  1197. {
  1198. if (disposing)
  1199. {
  1200. // TODO: dispose managed state (managed objects).
  1201. }
  1202. // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
  1203. // TODO: set large fields to null.
  1204. waterAtlas?.Dispose();
  1205. disposedValue = true;
  1206. }
  1207. }
  1208. // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
  1209. // ~wmset() {
  1210. // // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  1211. // Dispose(false);
  1212. // }
  1213. // This code added to correctly implement the disposable pattern.
  1214. public void Dispose() =>
  1215. // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
  1216. Dispose(true);// TODO: uncomment the following line if the finalizer is overridden above.// GC.SuppressFinalize(this);
  1217. #endregion IDisposable Support
  1218. #endregion Section 42 - objects and vehicles textures
  1219. }
  1220. }
  1221. /*
  1222. * Snippet for section w/ inner pointers
  1223. *
  1224. *
  1225. using (MemoryStream ms = new MemoryStream(buffer))
  1226. using (BinaryReader br = new BinaryReader(ms))
  1227. {
  1228. ms.Seek(sectionPointers[8 - 1], SeekOrigin.Begin);
  1229. var innerSec = GetInnerPointers(br);
  1230. }
  1231. */