Memory.cs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Content;
  3. using Microsoft.Xna.Framework.Graphics;
  4. using OpenVIII.Kernel;
  5. using System;
  6. using System.Collections.Concurrent;
  7. using System.Collections.Generic;
  8. using System.ComponentModel;
  9. using System.Diagnostics.CodeAnalysis;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Threading;
  13. using System.Threading.Tasks;
  14. namespace OpenVIII
  15. {
  16. /// <summary>
  17. /// Battle Speed Settings
  18. /// </summary>
  19. /// <see cref="https://gamefaqs.gamespot.com/ps/197343-final-fantasy-viii/faqs/58936"/>
  20. [SuppressMessage("ReSharper", "UnusedMember.Global")]
  21. public enum BattleSpeed : byte
  22. {
  23. Fastest = 1,
  24. Fast,
  25. Normal,
  26. Slow,
  27. Slowest,
  28. }
  29. public enum Module : sbyte
  30. {
  31. Battle = 3,
  32. BattleSwirl = 4,
  33. Field = 5,
  34. FieldDebug = -5,
  35. BattleDebug = -3,
  36. MovieTest = -9,
  37. OvertureDebug = -12,
  38. MainMenuDebug = -13,
  39. WorldDebug = -17,
  40. FaceTest = -20,
  41. IconTest = -21,
  42. CardTest = -22,
  43. FieldModelTest = -51,
  44. }
  45. public enum ScaleMode
  46. {
  47. /// <summary>
  48. /// scale object to have the same height as viewport
  49. /// </summary>
  50. FitVertical,
  51. /// <summary>
  52. /// scale object to have the same width as viewport
  53. /// </summary>
  54. FitHorizontal,
  55. /// <summary>
  56. /// Same as FitVertical unless width is too large, then it becomes FitHorizontal
  57. /// </summary>
  58. FitBoth,
  59. /// <summary>
  60. /// fill the entire viewport
  61. /// </summary>
  62. Stretch
  63. }
  64. /// <summary>
  65. /// Speed Mod
  66. /// </summary>
  67. /// <see cref="https://gamefaqs.gamespot.com/ps/197343-final-fantasy-viii/faqs/58936"/>
  68. [SuppressMessage("ReSharper", "UnusedMember.Global")]
  69. public enum SpeedMod : byte
  70. {
  71. Stop = 0,
  72. Slow = 1,
  73. Normal = 2,
  74. Haste = 3,
  75. AlwaysFull = 0xFF //not sure what i should set this too.
  76. }
  77. [SuppressMessage("ReSharper", "UnusedMember.Global")]
  78. public static partial class Memory
  79. {
  80. #region Fields
  81. public const int PreferredViewportHeight = 720;
  82. //original resolution I am working on, therefore if user scales it we need to proportionally scale everything
  83. public const int PreferredViewportWidth = 1280;
  84. public const ScaleMode ScaleMode = OpenVIII.ScaleMode.Stretch;
  85. /// <summary>
  86. /// add. seems to work if colors are pre-blended like if they overlap the colors combined ahead of time. Used for light.
  87. /// </summary>
  88. /// <see cref="http://community.monogame.net/t/solved-custom-blendstate-advice/11006"/>
  89. public static readonly BlendState BlendStateAdd = new BlendState
  90. {
  91. ColorWriteChannels = ColorWriteChannels.Blue | ColorWriteChannels.Green | ColorWriteChannels.Red,
  92. ColorSourceBlend = Blend.One,
  93. //AlphaSourceBlend = Blend.One,
  94. ColorDestinationBlend = Blend.One,
  95. //AlphaDestinationBlend = Blend.One,
  96. ColorBlendFunction = BlendFunction.Add
  97. };
  98. /// <summary>
  99. /// untested add with blend factor. You set the GraphicsDevice.BlendFactor before drawing.
  100. /// </summary>
  101. public static readonly BlendState BlendStateAddBlendFactor = new BlendState
  102. {
  103. ColorWriteChannels = ColorWriteChannels.Blue | ColorWriteChannels.Green | ColorWriteChannels.Red,
  104. ColorSourceBlend = Blend.BlendFactor,
  105. //AlphaSourceBlend = Blend.One,
  106. ColorDestinationBlend = Blend.One,
  107. //AlphaDestinationBlend = Blend.One,
  108. ColorBlendFunction = BlendFunction.Add
  109. };
  110. public static readonly BlendState BlendStateBasicAdd = new BlendState()
  111. {
  112. ColorSourceBlend = Blend.SourceColor,
  113. ColorDestinationBlend = Blend.DestinationColor,
  114. ColorBlendFunction = BlendFunction.Add,
  115. AlphaSourceBlend = Blend.SourceAlpha,
  116. AlphaDestinationBlend = Blend.DestinationAlpha,
  117. AlphaBlendFunction = BlendFunction.Add
  118. };
  119. public static readonly BlendState BlendStateForceDraw = new BlendState()
  120. {
  121. ColorSourceBlend = Blend.SourceColor,
  122. ColorDestinationBlend = Blend.SourceColor,
  123. ColorBlendFunction = BlendFunction.Add,
  124. AlphaSourceBlend = Blend.SourceAlpha,
  125. AlphaDestinationBlend = Blend.DestinationAlpha,
  126. AlphaBlendFunction = BlendFunction.Add,
  127. };
  128. /// <summary>
  129. /// subtract. Used for windows/glass makes color darker of things behind this layer.
  130. /// </summary>
  131. /// <see cref="http://community.monogame.net/t/solved-custom-blendstate-advice/11006"/>
  132. public static readonly BlendState BlendStateSubtract = new BlendState
  133. {
  134. ColorWriteChannels = ColorWriteChannels.Blue | ColorWriteChannels.Green | ColorWriteChannels.Red,
  135. ColorSourceBlend = Blend.One,
  136. //AlphaSourceBlend = Blend.One,
  137. ColorDestinationBlend = Blend.One,
  138. //AlphaDestinationBlend = Blend.One,
  139. ColorBlendFunction = BlendFunction.ReverseSubtract
  140. };
  141. public static readonly Dictionary<MusicId, List<string>> DicMusic = new Dictionary<MusicId, List<string>>();
  142. public static Card.Game CardGame;
  143. public static Cards Cards;
  144. public static ContentManager Content;
  145. public static GraphicModes CurrentGraphicMode;
  146. public static IReadOnlyDictionary<byte, FF8String> DrawPointMagic = new Dictionary<byte, FF8String>()
  147. {
  148. {0, "Cure - Balamb Garden courtyard"},
  149. {1, "Blizzard - Balamb Garden training center"},
  150. {2, "Full-Life - Balamb Garden MD level"},
  151. {3, "Esuna - Balamb Garden library next to the book shelf"},
  152. {4, "Demi - Balamb Garden cafeteria (only during Garden Riot)"},
  153. {5, "Bio - Balamb Garden B2 floor"},
  154. {6, "Thunder - Balamb outside junk shop"},
  155. {7, "Cure - Balamb harbor"},
  156. {8, "Fire - Fire Cavern"},
  157. {9, "Silence - Dollet town square"},
  158. {10, "Blind - Dollet Communications Tower"},
  159. {11, "Scan - Timber Pub Aurora back alley"},
  160. {12, "Cure - Timber outside the pub"},
  161. {13, "Blizzaga - Timber Maniacs Building left room"},
  162. {14, "Haste - Galbadia Garden lobby"},
  163. {15, "Life - Galbadia Garden changing rooms"},
  164. {16, "Shell - Galbadia Garden courtyard"},
  165. {17, "Protect - Galbadia Garden ice rink"},
  166. {18, "Double - Galbadia Garden auditorium"},
  167. {19, "Aura - Outside Galbadia Garden during Garden war"},
  168. {20, "Cure - Timber forests in a Laguna dream"},
  169. {21, "Water - Timber forests in a Laguna dream"},
  170. {22, "Thundara - Deling City park"},
  171. {23, "Zombie - Deling City Sewers"},
  172. {24, "Esuna - Deling City Sewers"},
  173. {25, "Bio - Deling City Sewers"},
  174. {26, "Fira"},
  175. {27, "Berserk - D-District Prison Floor 9 - right cell"},
  176. {28, "Thundaga - D-District Prison Floor 11 - right cell"},
  177. {29, "Aero - Outside D-District Prison"},
  178. {30, "Blizzara - Missile Base - control room"},
  179. {31, "Blind - Missile Base room with G-Soldiers who ask to deliver a message"},
  180. {32, "Full-Life - Missile Base - silo room"},
  181. {33, "Drain - Winhill road south from town square"},
  182. {34, "Dispel - Winhill town square"},
  183. {35, "Curaga - Winhill Laguna's room in the dream"},
  184. {36, "Reflect - Winhill east road"},
  185. {37, "Protect - Tomb of the Unknown King - outside"},
  186. {38, "Float - Tomb of the Unknown King - north room"},
  187. {39, "Cura - Tomb of the Unknown King - east room"},
  188. {40, "Haste - Fishermans Horizon abandoned train station"},
  189. {41, "Shell - Fishermans Horizon junk shop"},
  190. {42, "Regen - Fishermans Horizon overlooking the sun panel"},
  191. {43, "Full-Life - Fishermans Horizon Master Fisherman's fishing spot"},
  192. {44, "Ultima - Fishermans Horizon mayor's house"},
  193. {45, "Thundaga - Great Salt Lake past the dinosaur skeleton"},
  194. {46, "Meteor - Great Salt Lake dinosaur skeleton"},
  195. {47, "Curaga - Esthar city streets near city entrance"},
  196. {48, "Blizzard - Esthar outside palace"},
  197. {49, "Quake - Esthar outside Odine's Lab"},
  198. {50, "Tornado - Esthar shopping mall"},
  199. {51, "Double - Esthar Odine's Lab in a Laguna dream"},
  200. {52, "Pain"},
  201. {53, "Flare - Esthar Odine's Lab in a Laguna dream"},
  202. {54, "Stop - Sorceress Memorial"},
  203. {55, "Stop"},
  204. {56, "Life - Tears' Point entrance"},
  205. {57, "Reflect - Tears' Point middle"},
  206. {58, "Death - Lunatic Pandora Laboratory in a Laguna dream"},
  207. {59, "Holy - Lunatic Pandora near Elevator #1"},
  208. {60, "Silence - Lunatic Pandora"},
  209. {61, "Ultima - Lunatic Pandora"},
  210. {62, "Confuse"},
  211. {63, "Break - Lunatic Pandora on the way to fight Adel"},
  212. {64, "Meteor - Lunatic Pandora entrance"},
  213. {65, "Curaga - Lunatic Pandora elevator room"},
  214. {66, "Slow"},
  215. {67, "Curaga - Edea's Orphanage"},
  216. {68, "Flare"},
  217. {69, "Holy"},
  218. {70, "Sleep - Centra Excavation Site"},
  219. {71, "Confuse - Centra Excavation Site"},
  220. {72, "Aero - Centra Ruins right ladder after the lift"},
  221. {73, "Drain - Centra Ruins platform after the first staircase"},
  222. {74, "Pain - Centra Ruins next to the dome"},
  223. {75, "Thundaga - Trabia Garden in front of the statue"},
  224. {76, "Zombie - Trabia Garden cemetery"},
  225. {77, "Aura - Trabia Garden stage"},
  226. {78, "Ultima - Shumi Village - above ground"},
  227. {79, "Blizzaga - Shumi Village - outside elder's house"},
  228. {80, "Firaga - Shumi Village workshop"},
  229. {81, "Tornado"},
  230. {82, "Holy - White SeeD Ship"},
  231. {83, "Cura - Ragnarok room with a red Propagator"},
  232. {84, "Life - Ragnarok hangar upstairs"},
  233. {85, "Full-Life - Ragnarok room with save point"},
  234. {86, "Dispel - Deep Sea Research Center second level"},
  235. {87, "Esuna - Deep Sea Research Center secret room"},
  236. {88, "Triple - Deep Sea Research Center third screen on the way to Ultima Weapon's lair"},
  237. {89, "Ultima - Deep Sea Research Center fifth screen on the way to Ultima Weapon's lair"},
  238. {90, "Meltdown - Lunar Base room before the escape pods"},
  239. {91, "Meteor - Lunar Base Ellone's room"},
  240. {92, "Haste"},
  241. {93, "Slow"},
  242. {94, "Curaga"},
  243. {95, "Life"},
  244. {96, "Stop"},
  245. {97, "Regen"},
  246. {98, "Double"},
  247. {99, "Triple"},
  248. {100, "Flare - Ultimecia Castle outside"},
  249. {101, "Curaga - Ultimecia Castle storage room"},
  250. {102, "Cura - Ultimecia Castle passageway"},
  251. {103, "Scan"},
  252. {104, "Esuna"},
  253. {105, "Slow - Ultimecia Castle courtyard"},
  254. {106, "Dispel - Ultimecia Castle chapel"},
  255. {107, "Stop - Ultimecia Castle clock tower"},
  256. {108, "Life"},
  257. {109, "Flare"},
  258. {110, "Aura - Ultimecia Castle wine cellar"},
  259. {111, "Holy - Ultimecia Castle treasure room"},
  260. {112, "Meteor"},
  261. {113, "Meltdown - Ultimecia Castle art gallery"},
  262. {114, "Ultima - Ultimecia Castle armory"},
  263. {115, "Full-Life - Ultimecia Castle prison"},
  264. {116, "Triple"},
  265. {117, "Fire"},
  266. {118, "Fire"},
  267. {119, "Fire"},
  268. {120, "Fire"},
  269. {121, "Fire"},
  270. {122, "Fire"},
  271. {123, "Fire"},
  272. {124, "Fire"},
  273. {125, "Fire"},
  274. {126, "Fire"},
  275. {127, "Fire"},
  276. {128, "Cure"},
  277. {129, "Esuna"},
  278. {130, "Thunder"},
  279. {131, "Fira"},
  280. {132, "Thundara"},
  281. {133, "Blizzara"},
  282. {134, "Blizzard"},
  283. {135, "Fire"},
  284. {136, "Cure"},
  285. {137, "Water"},
  286. {138, "Cura"},
  287. {139, "Esuna"},
  288. {140, "Scan"},
  289. {141, "Shell"},
  290. {142, "Haste"},
  291. {143, "Aero"},
  292. {144, "Bio"},
  293. {145, "Life"},
  294. {146, "Demi"},
  295. {147, "Protect"},
  296. {148, "Holy"},
  297. {149, "Thundaga"},
  298. {150, "Stop"},
  299. {151, "Firaga"},
  300. {152, "Regen"},
  301. {153, "Blizzaga"},
  302. {154, "Confuse"},
  303. {155, "Flare"},
  304. {156, "Dispel"},
  305. {157, "Slow"},
  306. {158, "Quake"},
  307. {159, "Curaga"},
  308. {160, "Tornado"},
  309. {161, "Full-Life"},
  310. {162, "Reflect"},
  311. {163, "Aura"},
  312. {164, "Quake"},
  313. {165, "Double"},
  314. {166, "Break"},
  315. {167, "Meteor"},
  316. {168, "Ultima"},
  317. {169, "Triple"},
  318. {170, "Confuse"},
  319. {171, "Blind"},
  320. {172, "Quake"},
  321. {173, "Sleep"},
  322. {174, "Silence"},
  323. {175, "Flare"},
  324. {176, "Death"},
  325. {177, "Drain"},
  326. {178, "Pain"},
  327. {179, "Berserk"},
  328. {180, "Float"},
  329. {181, "Zombie"},
  330. {182, "Meltdown"},
  331. {183, "Ultima"},
  332. {184, "Tornado"},
  333. {185, "Quake"},
  334. {186, "Meteor"},
  335. {187, "Holy"},
  336. {188, "Flare"},
  337. {189, "Aura"},
  338. {190, "Ultima"},
  339. {191, "Triple"},
  340. {192, "Full-Life"},
  341. {193, "Tornado"},
  342. {194, "Quake"},
  343. {195, "Meteor"},
  344. {196, "Holy"},
  345. {197, "Flare"},
  346. {198, "Aura"},
  347. {199, "Ultima"},
  348. {200, "Triple"},
  349. {201, "Full-Life"},
  350. {202, "Tornado"},
  351. {203, "Quake"},
  352. {204, "Meteor"},
  353. {205, "Holy"},
  354. {206, "Flare"},
  355. {207, "Aura"},
  356. {208, "Ultima"},
  357. {209, "Triple"},
  358. {210, "Full-Life"},
  359. {211, "Ultima"},
  360. {212, "Meteor"},
  361. {213, "Holy"},
  362. {214, "Flare"},
  363. {215, "Aura"},
  364. {216, "Ultima"},
  365. {217, "Triple"},
  366. {218, "Full-Life"},
  367. {219, "Meteor"},
  368. {220, "Holy"},
  369. {221, "Triple"},
  370. {222, "Aura"},
  371. {223, "Ultima"},
  372. {224, "Triple"},
  373. {225, "Full-Life"},
  374. {226, "Meteor"},
  375. {227, "Holy"},
  376. {228, "Flare"},
  377. {229, "Aura"},
  378. {230, "Ultima"},
  379. {231, "Triple"},
  380. {232, "Full-Life"},
  381. {233, "Meteor"},
  382. {234, "Triple"},
  383. {235, "Flare"},
  384. {236, "Aura"},
  385. {237, "Ultima"},
  386. {238, "Triple"},
  387. {239, "Full-Life"},
  388. {240, "Meteor"},
  389. {241, "Holy"},
  390. {242, "Flare"},
  391. {243, "Aura"},
  392. {244, "Ultima"},
  393. {245, "Blizzard"},
  394. {246, "Cure"},
  395. {247, "Dispel"},
  396. {248, "Confuse"},
  397. {249, "Meteor"},
  398. {250, "Double"},
  399. {251, "Aura"},
  400. {252, "Holy"},
  401. {253, "Flare"},
  402. {254, "Ultima"},
  403. {255, "Scan"}
  404. };
  405. public static bool EnableDumpingData = false;
  406. public static Faces Faces;
  407. public static List<Task> FfccLeftOverTask = new List<Task>();
  408. public static Font Font;
  409. public static GraphicsDeviceManager Graphics;
  410. public static Icons Icons;
  411. public static Core.ImGuiRenderer ImGui;
  412. public static Task InitTask;
  413. public static Input2 Input2;
  414. public static bool IsActive = true;
  415. public static KernelBin KernelBin;
  416. public static Extended.Languages Languages = Extended.Languages.en;
  417. public static Log Log;
  418. public static ConcurrentQueue<Action> MainThreadOnlyActions;
  419. /// <summary>
  420. /// Random number generator seeded with time.
  421. /// </summary>
  422. /// <remarks>creates global random class for all sort of things</remarks>
  423. public static Random Random;
  424. /// <summary>
  425. /// Battle music pointer. Set by SET BATTLE MUSIC in field module or by world module. Default=6
  426. /// </summary>
  427. public static int SetBattleMusic = 6;
  428. public static VertexPositionTexture[] ShadowGeometry;
  429. public static Texture2D ShadowTexture;
  430. public static IReadOnlyDictionary<ushort, FF8String> SongsOGG = new Dictionary<ushort, FF8String>()
  431. {
  432. {0,"Lose" },
  433. {1,"The Winner" },
  434. {4,"Never Look Back" },
  435. {5,"Don't Be Afraid" },
  436. {7,"Dead End" },
  437. {8,"Starting Up" },
  438. {9,"Intruders" },
  439. {12,"Don't Be Afraid (X-ATM092)" },
  440. {13,"Force Your Way" },
  441. {14,"FITHOS LUSEC WECOS VINOSEC (No Intro)" },
  442. {15,"Unrest" },
  443. {16,"The Stage is Set" },
  444. {17,"The Landing" },
  445. {18,"Love Grows" },
  446. {19,"Waltz for the Moon" },
  447. {20,"Ami" },
  448. {21,"Find Your Way" },
  449. {22,"Julia" },
  450. {23,"FITHOS LUSEC WECOS VINOSEC" },
  451. {24,"SeeD" },
  452. {25,"Tell Me" },
  453. {26,"Balamb GARDEN" },
  454. {27,"Fear" },
  455. {28,"Dance with the Balamb-Fish" },
  456. {29,"Cactus Jack" },
  457. {35,"The Mission" },
  458. {36,"SUCCESSION OF WITCHES" },
  459. {41,"Blue Fields" },
  460. {42,"Breezy" },
  461. {43,"Concert" },
  462. {46,"Timber Owls" },
  463. {47,"Fragments of Memories" },
  464. {48,"Fisherman's Horizon" },
  465. {49,"Heresy" },
  466. {51,"My Mind" },
  467. {52,"Where I Belong" },
  468. {53,"Starting Up (Looped)" },
  469. {54,"Truth" },
  470. {55,"Trust Me" },
  471. {56,"Galbadia GARDEN" },
  472. {57,"Martial Law" },
  473. {58,"Under Her Control" },
  474. {59,"Only a Plank Between One and Perdition" },
  475. {60,"Junction" },
  476. {61,"Roses and Wine" },
  477. {62,"The Man with the Machine Gun" },
  478. {63,"A Sacrifice" },
  479. {64,"ODEKA ke Chocobo" },
  480. {65,"Drifting" },
  481. {66,"Wounded" },
  482. {67,"Jailed" },
  483. {68,"Retaliation" },
  484. {69,"The Oath" },
  485. {70,"Shuffle or Boogie" },
  486. {71,"Rivals" },
  487. {72,"Blue Sky" },
  488. {73,"Premonition" },
  489. {75,"Galbadia GARDEN (No Intro)" },
  490. {76,"Maybe I'm a Lion" },
  491. {77,"The Castle" },
  492. {78,"Movin'" },
  493. {79,"Overture" },
  494. {80,"The Spy" },
  495. {81,"Mods de Chocobo" },
  496. {82,"The Salt Flats" },
  497. {83,"The Residents" },
  498. {84,"Lunatic Pandora" },
  499. {85,"Silence and Motion" },
  500. {86,"Tears of the Moon" },
  501. {88,"Tears of the Moon (Alternate)" },
  502. {89,"Ride On" },
  503. {90,"The Legendary Beast" },
  504. {91,"Slide Show Part 1" },
  505. {92,"Slide Show Part 2" },
  506. {93,"The Extreme" },
  507. {96,"The Successor" },
  508. {97,"Compression of Time" },
  509. {99,"The Landing (No Intro)" },
  510. {512,"The Loser" },
  511. {513,"Eyes on Me" },
  512. {514,"Irish Jig (Concert)" },
  513. {515,"Eyes on Me (Concert)" },
  514. {516,"Movin' (No Intro)" },
  515. {517,"The Landing (Alternate)" },
  516. {518,"The Landing (Alternate - No Intro)" },
  517. {519,"Galbadia GARDEN (Alternate)" },
  518. };
  519. public static IReadOnlyDictionary<ushort, string> SongsSGT = new Dictionary<ushort, string>()
  520. {
  521. {0, "Lose" },
  522. {1, "Win" },
  523. {2, "Open" },
  524. {3, "Combat" },
  525. {4, "Run" },
  526. {5, "Battle" },
  527. {6, "Funsui" },
  528. {7, "End" },
  529. {8, "Antenna" },
  530. {9, "Waiting" },
  531. {10, "Ante " },
  532. {11, "Wind" },
  533. {12, "Crab" },
  534. {13, "Battle2" },
  535. {14, "Friend2" },
  536. {15, "Fuan2" },
  537. {16, "March2" },
  538. {17, "Land" },
  539. {18, "Julia" },
  540. {19, "Waltz" },
  541. {20, "Friend " },
  542. {21, "Dungeon" },
  543. {22, "Pianosolo" },
  544. {23, "Parade" },
  545. {24, "March1" },
  546. {25, "Secret" },
  547. {26, "Garden" },
  548. {27, "Fuan " },
  549. {28, "Polka2" },
  550. {29, "Anthem" },
  551. {30, "FlangChorus" },
  552. {31, "DubChorus" },
  553. {32, "SoloChorus" },
  554. {33, "FemaleChorus" },
  555. {34, "Chorus" },
  556. {35, "M7F5" },
  557. {36, "Sorceress" },
  558. {37, "Reet" },
  559. {38, "Soyo" },
  560. {39, "Rouka" },
  561. {40, "Night" },
  562. {41, "Field" },
  563. {42, "Guitar" },
  564. {43, "Concert" },
  565. {44, "Sea" },
  566. {45, "Silent" },
  567. {46, "Resistance" },
  568. {47, "Kaiso" },
  569. {48, "Horizon" },
  570. {49, "Master" },
  571. {50, "Battle2" },
  572. {51, "Rinoa" },
  573. {52, "Trabia" },
  574. {53, "Horizon2" },
  575. {54, "Truth" },
  576. {55, "Prison" },
  577. {56, "GalbadiaGarden" },
  578. {57, "Timber" },
  579. {58, "Galbadia " },
  580. {59, "Pinchi" },
  581. {60, "Scene1" },
  582. {61, "Pub" },
  583. {62, "Bat3" },
  584. {63, "Stage" },
  585. {64, "Choco" },
  586. {65, "White" },
  587. {66, "Majomv" },
  588. {67, "Musho" },
  589. {68, "Missile" },
  590. {69, "Speech" },
  591. {70, "Card" },
  592. {71, "Gomon" },
  593. {72, "Soto" },
  594. {73, "Majobat" },
  595. {74, "Train" },
  596. {75, "Garden2" },
  597. {76, "Bossbat2" },
  598. {77, "LastDungeon" },
  599. {78, "Gafly" },
  600. {79, "Demo" },
  601. {80, "Spy" },
  602. {81, "VoiceDeChocobo" },
  603. {82, "Salt" },
  604. {83, "Alien" },
  605. {84, "Sekichu" },
  606. {85, "Esta" },
  607. {86, "Moonmv" },
  608. {87, "Mdmotor" },
  609. {88, "Moonmv2" },
  610. {89, "Fly" },
  611. {90, "BossBat1" },
  612. {91, "Rag1" },
  613. {92, "Rag2" },
  614. {93, "LastBoss" },
  615. {94, "Lastwhite" },
  616. {95, "Lasbl" },
  617. {96, "Keisho" },
  618. {97, "Compression" },
  619. };
  620. public static SpriteBatch SpriteBatch;
  621. public static Strings Strings;
  622. public static int Year = 2013;
  623. private static ushort _currentMusic;
  624. // need to dynamically detect if 2000/2013/2019, maybe need 2000 1.2 as well.
  625. private static string _ff8Dir;
  626. private static string _ff8DirData;
  627. private static int _mainThreadID;
  628. private static Module _module = Module.OvertureDebug;
  629. private static ushort _previousMusic;
  630. /// <summary>
  631. /// Stores current save state. When you save this is wrote. When you load this is replaced.
  632. /// </summary>
  633. private static Saves.Data _state = new Saves.Data();
  634. #endregion Fields
  635. #region Events
  636. public static event EventHandler<Module> ModuleChangeEvent;
  637. #endregion Events
  638. #region Enums
  639. public enum GraphicModes
  640. {
  641. OpenGL,
  642. DirectX
  643. };
  644. #endregion Enums
  645. #region Properties
  646. public static string[] Arguments { get; set; }
  647. public static float BattleStageScale { get; internal set; } = 100f;
  648. public static float CameraScale { get; internal set; } = 100f;
  649. public static Point Center => new Point(Graphics.GraphicsDevice.Viewport.Width / 2, Graphics.GraphicsDevice.Viewport.Height / 2);
  650. public static BattleSpeed CurrentBattleSpeed => State?.Configuration?.BattleSpeed ?? BattleSpeed.Normal;
  651. public static TimeSpan DateTimeNow => TimeSpan.FromTicks(DateTime.Now.Ticks);
  652. public static TimeSpan ElapsedGameTime => GameTime?.ElapsedGameTime ?? TimeSpan.Zero;
  653. /// <summary>
  654. /// Active battle encounter. Set by field or battle module. You shouldn't change it in-battle.
  655. /// </summary>
  656. public static Battle.Encounters Encounters { get; set; }
  657. public static float EnemyCoordinateScale { get; internal set; } = 100f;
  658. public static string FF8Dir
  659. {
  660. get => _ff8Dir;
  661. private set => _ff8Dir = value;
  662. }
  663. public static string FF8DirData
  664. {
  665. get => _ff8DirData;
  666. private set => _ff8DirData = value;
  667. }
  668. public static string FF8DirDataLang { get; private set; }
  669. /// <summary>
  670. /// Game time value. Could be null check for null.
  671. /// </summary>
  672. public static GameTime GameTime { get; set; }
  673. public static bool Initiated { get; private set; }
  674. public static Saves.Data InitState { get; private set; }
  675. public static bool IsMainThread => Thread.CurrentThread.ManagedThreadId == _mainThreadID;
  676. public static bool IsMouseVisible { get; set; } = false;
  677. public static Magazine Magazines { get; private set; }
  678. public static ItemsInMenu MItems { get; private set; }
  679. public static Module Module
  680. {
  681. get => _module; set
  682. {
  683. if (_module == value) return;
  684. _module = value;
  685. ModuleChangeEvent?.Invoke(null, value);
  686. }
  687. }
  688. public static ushort MusicIndex
  689. {
  690. get
  691. {
  692. if (DicMusic.Count > 0)
  693. {
  694. var max = (ushort)DicMusic.Keys.Max();
  695. var min = (ushort)DicMusic.Keys.Min();
  696. while ((_previousMusic > _currentMusic || _previousMusic == ushort.MinValue && _currentMusic == ushort.MaxValue) &&
  697. !DicMusic.ContainsKey((MusicId)_currentMusic))
  698. {
  699. if (max < _currentMusic)
  700. {
  701. _currentMusic = max;
  702. }
  703. else
  704. {
  705. _currentMusic--;
  706. }
  707. }
  708. while (DicMusic.Count > 0 && _previousMusic < _currentMusic && !DicMusic.ContainsKey((MusicId)_currentMusic))
  709. {
  710. if (max < _currentMusic)
  711. {
  712. _currentMusic = min;
  713. }
  714. else
  715. {
  716. _currentMusic++;
  717. }
  718. }
  719. return _currentMusic;
  720. }
  721. else return 0;
  722. }
  723. set
  724. {
  725. _previousMusic = _currentMusic;
  726. _currentMusic = value;
  727. }
  728. }
  729. public static Saves.Data PrevState { get; set; }
  730. public static Saves.Data State
  731. {
  732. get => _state; set
  733. {
  734. _state = value;
  735. if (_state != null)
  736. _state.LoadTime = GameTime?.TotalGameTime ?? new TimeSpan();
  737. }
  738. }
  739. /// <summary>
  740. /// If true by the end of Update() will skip the next Draw()
  741. /// </summary>
  742. public static bool SuppressDraw { get; set; }
  743. public static bool Threaded { get; private set; } = true;
  744. public static CancellationToken Token { get; private set; }
  745. public static CancellationTokenSource TokenSource { get; private set; }
  746. public static TimeSpan TotalGameTime => GameTime?.TotalGameTime ?? TimeSpan.Zero;
  747. #endregion Properties
  748. #region Methods
  749. public static void Init(GraphicsDeviceManager graphics, SpriteBatch spriteBatch, ContentManager content, string[] arguments)
  750. {
  751. if (Log == null) Log = new Log();
  752. Log.WriteLine($"{nameof(Memory)} :: {nameof(Init)}");
  753. Log.WriteLine($"{nameof(GraphicsDeviceManager)} :: {graphics}");
  754. Log.WriteLine($"{nameof(GraphicsDeviceManager)} :: {nameof(graphics.GraphicsDevice.Adapter.CurrentDisplayMode)} :: {graphics?.GraphicsDevice.Adapter.CurrentDisplayMode}");
  755. if (graphics != null)
  756. foreach (var i in graphics.GraphicsDevice.Adapter.SupportedDisplayModes)
  757. Log.WriteLine($"{nameof(GraphicsDeviceManager)} :: {nameof(graphics.GraphicsDevice.Adapter.SupportedDisplayModes)} :: {i}");
  758. //Log.WriteLine($"{nameof(GraphicsDeviceManager)} :: {graphics.GraphicsDevice.Adapter.DeviceName}");
  759. //Log.WriteLine($"{nameof(SpriteBatch)} :: {spriteBatch}");
  760. Log.WriteLine($"{nameof(ContentManager)} :: {content}");
  761. Log.WriteLine($"{nameof(Random)} :: new");
  762. Random = new Random((int)DateTime.Now.Ticks);
  763. Log.WriteLine($"{nameof(Memory)} :: {nameof(_mainThreadID)} = {nameof(Thread)} :: {nameof(Thread.CurrentThread)} :: {nameof(Thread.ManagedThreadId)} = {Thread.CurrentThread.ManagedThreadId}");
  764. _mainThreadID = Thread.CurrentThread.ManagedThreadId;
  765. Log.WriteLine($"{nameof(Memory)} :: {nameof(MainThreadOnlyActions)}");
  766. MainThreadOnlyActions = new ConcurrentQueue<Action>();
  767. FF8Dir = GameLocation.Current.DataPath;
  768. void SetData() =>
  769. FF8DirData = Extended.GetUnixFullPath(Path.Combine(FF8Dir, "Data"));
  770. SetData();
  771. var languageSet = false;
  772. void setLang(string lang)
  773. {
  774. switch (lang.ToLower())
  775. {
  776. case "2":
  777. case "de":
  778. Languages = Extended.Languages.de;
  779. break;
  780. case "0":
  781. case "en":
  782. Languages = Extended.Languages.en;
  783. break;
  784. case "3":
  785. case "es":
  786. Languages = Extended.Languages.es;
  787. break;
  788. case "1":
  789. case "fr":
  790. Languages = Extended.Languages.fr;
  791. break;
  792. case "4":
  793. case "it":
  794. Languages = Extended.Languages.it;
  795. break;
  796. case "5":
  797. case "jp":
  798. Languages = Extended.Languages.jp;
  799. break;
  800. default:
  801. throw new InvalidEnumArgumentException($"{nameof(Memory)}::{nameof(Init)}::{nameof(lang)} ({lang}) is not a supported language code. (de,en,es,fr,it,jp)");
  802. }
  803. languageSet = true;
  804. }
  805. if (arguments != null && arguments.Length > 0)
  806. {
  807. IEnumerable<string[]> splitArguments = (from a in arguments
  808. where a.Contains('=')
  809. select a.Trim().Split(new[] { '=' }, 2)).OrderByDescending(x => x[0], StringComparer.OrdinalIgnoreCase);
  810. foreach (var s in splitArguments)
  811. {
  812. bool test(string @in, ref string @out)
  813. {
  814. if (!s[0].Equals(@in, StringComparison.OrdinalIgnoreCase)) return false;
  815. @out = s[1].Trim(('"'));
  816. return true;
  817. }
  818. var lang = "";
  819. if (test("dir", ref _ff8Dir)) //override ff8 directory
  820. {
  821. if (!Directory.Exists(_ff8Dir))
  822. throw new DirectoryNotFoundException(
  823. $"{nameof(Memory)}::{nameof(Init)}::{nameof(arguments)}::{nameof(_ff8Dir)} ({s[0]}) Cannot find path: \"{_ff8Dir}\"");
  824. SetData();
  825. }
  826. else if (test("data", ref _ff8DirData)) //override data folder location
  827. {
  828. if (!Directory.Exists(_ff8DirData))
  829. throw new DirectoryNotFoundException(
  830. $"{nameof(Memory)}::{nameof(Init)}::{nameof(arguments)}::{nameof(_ff8DirData)} ({s[0]}) Cannot find path: \"{_ff8DirData}\"");
  831. }
  832. else if (test("lang", ref lang)) //override language
  833. {
  834. setLang(lang);
  835. }
  836. }
  837. }
  838. Log.WriteLine($"{nameof(Memory)} :: {nameof(FF8Dir)} = {FF8Dir}");
  839. Log.WriteLine($"{nameof(Memory)} :: {nameof(FF8DirData)} = {FF8DirData}");
  840. var langDatPath = Path.Combine(FF8Dir, "lang.dat");
  841. if (!languageSet && File.Exists(langDatPath))
  842. using (var streamReader = new StreamReader(
  843. new FileStream(langDatPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), System.Text.Encoding.UTF8))
  844. {
  845. var lang = streamReader.ReadLine()?.Trim();
  846. setLang(lang);
  847. }
  848. Log.WriteLine($"{nameof(Extended)} :: {nameof(Extended.GetLanguageShort)} = {Extended.GetLanguageShort()}");
  849. var testDir = Extended.GetUnixFullPath(Path.Combine(FF8DirData, $"lang-{Extended.GetLanguageShort()}"));
  850. FF8DirDataLang = Directory.Exists(testDir) ? testDir : FF8DirData;
  851. Log.WriteLine($"{nameof(Memory)} :: {nameof(FF8DirDataLang)} = {FF8DirDataLang}");
  852. Archives.Init();
  853. Graphics = graphics;
  854. SpriteBatch = spriteBatch;
  855. Content = content;
  856. Arguments = arguments;
  857. TokenSource = new CancellationTokenSource();
  858. Token = TokenSource.Token;
  859. Threaded = false;
  860. if (Threaded)
  861. {
  862. InitTask = new Task<int>(InitTaskMethod, Token);
  863. InitTask.Start();
  864. }
  865. else
  866. InitTaskMethod(Token);
  867. }
  868. public static int InitTaskMethod(object obj)
  869. {
  870. Log.WriteLine($"{nameof(Memory)} :: {nameof(Memory)} :: {nameof(Init)}");
  871. var token = (CancellationToken)obj;
  872. if (!token.IsCancellationRequested)
  873. Strings = new Strings();
  874. // requires strings because it uses an array generated in strings.
  875. // saves data will reference kernel_bin.
  876. if (!token.IsCancellationRequested)
  877. KernelBin = KernelBin.CreateInstance();
  878. var actions = new List<Action>()
  879. {
  880. // this has a soft requirement on kernel_bin. It checks for null so should work without it.
  881. () => {MItems = ItemsInMenu.Read(); },
  882. Saves.Init,
  883. //loads all save games from steam2013 or cd2000 or steam2019 directories. first come first serve.
  884. //TODO allow choosing of which save folder to use.
  885. //this initializes the field module, it's worth to have this at the beginning
  886. Fields.Initializer.Init,
  887. //this initializes the encounters
  888. InitDebuggerBattle.Init,
  889. };
  890. if (Graphics?.GraphicsDevice != null) // all below require graphics to work. to load textures graphics device needed.
  891. {
  892. actions.AddRange(new Action[]
  893. {
  894. //this initializes the fonts and drawing system- holds fonts in-memory
  895. () => { Font = new Font(); },
  896. // card images in menu.
  897. () => { Cards = Cards.Load(); },
  898. () => { CardGame = new Card.Game(); },
  899. () => { Faces = Faces.Load(); },
  900. () => { Icons = Icons.Load(); },
  901. () => { Magazines = Magazine.Load(); }
  902. });
  903. }
  904. actions.Add(() =>
  905. {
  906. InitState = Saves.Data.LoadInitOut();
  907. State = InitState?.Clone();
  908. });
  909. var tasks = new List<Task>();
  910. if (!token.IsCancellationRequested)
  911. {
  912. if (Threaded)
  913. {
  914. tasks.AddRange(from a in actions where !token.IsCancellationRequested select Task.Run(a, token));
  915. Task.WhenAll(tasks.ToArray()).GetAwaiter().GetResult();
  916. }
  917. else
  918. foreach (var a in actions.Where(a => !token.IsCancellationRequested))
  919. {
  920. a.Invoke();
  921. }
  922. if (Graphics?.GraphicsDevice != null) // all below require graphics to work. to load textures graphics device needed.
  923. {
  924. // requires font, faces, and icons. currently cards only used in debug menu. will
  925. // have support for cards when added to menu.
  926. if (!token.IsCancellationRequested)
  927. Menu.InitStaticMembers();
  928. }
  929. }
  930. //EXE_Offsets test = new EXE_Offsets();
  931. Initiated = true;
  932. //ArchiveBase.PurgeCache();//remove files probably no longer needed.
  933. return 0;
  934. }
  935. public static bool ProcessActions(Action[] actions)
  936. {
  937. if (Threaded)
  938. {
  939. var tasks = new List<Task>(actions.Length);
  940. actions.ForEach(x => { if (!Token.IsCancellationRequested) tasks.Add(Task.Run(x, Token)); });
  941. //Some code that cannot be threaded on init.
  942. if (!Task.WaitAll(tasks.ToArray(), 10000))
  943. throw new TimeoutException("Task took too long!");
  944. }
  945. else actions.ForEach(x => x.Invoke());
  946. return !Token.IsCancellationRequested;
  947. }
  948. public static bool[] ProcessFunctions(Func<bool>[] functions)
  949. {
  950. if (!Threaded) return functions.Select(x => x.Invoke()).ToArray();
  951. //var tasks = new List<Task<bool>>(functions.Length);
  952. //functions.ForEach(x => { if (!Token.IsCancellationRequested) tasks.Add(Task.Run(x, Token)); });
  953. var tasks = functions.Select(x => Task.Run(x, Token));
  954. //Some code that cannot be threaded on init.
  955. return Task.WhenAll(tasks.ToArray()).GetAwaiter().GetResult();
  956. //if (!Task.WaitAll(tasks.ToArray(), 10000))
  957. // throw new TimeoutException("Task took too long!");
  958. }
  959. public static Vector2 Scale(float width = PreferredViewportWidth, float height = PreferredViewportHeight, ScaleMode scaleMode = ScaleMode, int targetX = 0, int targetY = 0)
  960. {
  961. if (targetX == 0)
  962. targetX = Graphics.GraphicsDevice.Viewport.Width;
  963. if (targetY == 0)
  964. targetY = Graphics.GraphicsDevice.Viewport.Height;
  965. var h = targetX / width;
  966. var v = targetY / height;
  967. switch (scaleMode)
  968. {
  969. case ScaleMode.FitHorizontal:
  970. return new Vector2(h, h);
  971. case ScaleMode.FitVertical:
  972. return new Vector2(v, v);
  973. case ScaleMode.FitBoth:
  974. return (v * width > targetX) ? new Vector2(h, h) : new Vector2(v, v);
  975. case ScaleMode.Stretch:
  976. return new Vector2(h, v);
  977. default:
  978. throw new ArgumentOutOfRangeException(nameof(scaleMode), scaleMode, null);
  979. }
  980. }
  981. //ogg and sgt files have same 3 digit prefix.
  982. public static void SpriteBatchEnd() => SpriteBatch.End();
  983. public static void SpriteBatchStart(BlendState bs = null, SamplerState ss = null) =>
  984. SpriteBatch.Begin(SpriteSortMode.Deferred, bs ?? BlendState.AlphaBlend, ss ?? SamplerState.PointClamp, Graphics.GraphicsDevice.DepthStencilState);
  985. public static void SpriteBatchStartAlpha(SpriteSortMode sortMode = SpriteSortMode.Deferred, SamplerState ss = null, Matrix? tm = null) =>
  986. SpriteBatch.Begin(sortMode: sortMode, blendState: BlendState.AlphaBlend, samplerState: ss ?? SamplerState.PointClamp, transformMatrix: tm);
  987. public static void SpriteBatchStartStencil(SamplerState ss = null) =>
  988. SpriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.Opaque, ss ?? SamplerState.PointClamp, Graphics.GraphicsDevice.DepthStencilState);
  989. public static void Update()
  990. {
  991. Action a = null;
  992. while (IsMainThread && (MainThreadOnlyActions?.TryDequeue(out a) ?? false))
  993. { a.Invoke(); }
  994. for (var i = 0; IsMainThread && i < FfccLeftOverTask.Count; i++)
  995. {
  996. if (!FfccLeftOverTask[i].IsCompleted) continue;
  997. FfccLeftOverTask[i].Dispose();
  998. FfccLeftOverTask.RemoveAt(i--);
  999. }
  1000. }
  1001. #endregion Methods
  1002. #region Classes
  1003. public static class FieldHolder
  1004. {
  1005. #region Fields
  1006. //public static string[] MapList;
  1007. public static ushort FieldID = 756;
  1008. public static string[] Fields;
  1009. #endregion Fields
  1010. //public static int[] FieldMemory;
  1011. #region Methods
  1012. public static string GetString(ushort? inputFieldID = null) => Fields?.ElementAtOrDefault(inputFieldID ?? FieldID);
  1013. #endregion Methods
  1014. }
  1015. #endregion Classes
  1016. }
  1017. }