wmset.cs 55 KB

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