Items_In_Menu.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics.CodeAnalysis;
  5. using System.IO;
  6. using System.Linq;
  7. namespace OpenVIII
  8. {
  9. [Flags]
  10. [SuppressMessage("ReSharper", "UnusedMember.Global")]
  11. public enum ItemTarget : byte
  12. {
  13. None = 0x0,
  14. Useable = 0x1,//usable
  15. Character = 0x2,
  16. GF = 0x4,
  17. All = 0x8,
  18. All2 = 0x10,// all two?
  19. KO = 0x20,
  20. BlueMagic = 0x40,//blue magic?
  21. Compatibility = 0x80,//compatibility
  22. }
  23. public enum ItemType : byte
  24. {
  25. Heal = 0x00,
  26. Revive = 0x01,
  27. HealGF = 0x03,
  28. ReviveGF = 0x04,
  29. SavePointHeal = 0x06,
  30. Battle = 0x07,
  31. Ammo = 0x08,
  32. Magazine = 0x09,
  33. /// <summary>
  34. /// Refine or None
  35. /// </summary>
  36. None = 0x0A,
  37. /// <summary>
  38. /// Rename
  39. /// </summary>
  40. GF = 0x0B,
  41. /// <summary>
  42. /// Rename
  43. /// </summary>
  44. Angelo = 0x0C,
  45. /// <summary>
  46. /// Rename
  47. /// </summary>
  48. Chocobo = 0x0D,
  49. /// <summary>
  50. /// start battle
  51. /// </summary>
  52. Lamp = 0x0E,
  53. /// <summary>
  54. /// get Doomtrain
  55. /// </summary>
  56. SolomonRing = 0x0F,
  57. GFCompatibility = 0x10,
  58. GFLearn = 0x11,
  59. GFForget = 0x12,
  60. /// <summary>
  61. /// blue magic only shows quistis in target list, should detect if she knows the spell to
  62. /// grey it out item.
  63. /// </summary>
  64. BlueMagic = 0x13, // learn
  65. Stat = 0x14,
  66. CureAbnormalStatus = 0x15,
  67. }
  68. /// <summary>
  69. /// Effects of Items inside the in game menu.
  70. /// </summary>
  71. public struct ItemInMenu
  72. {
  73. #region Fields
  74. private const int BulletOffset = 101;
  75. private byte _b2;
  76. private byte _b3;
  77. private ItemTarget _itemTarget;
  78. private ItemType _itemType;
  79. private Dictionary<ItemType, Func<Faces.ID, bool, bool>> _useActions;
  80. #endregion Fields
  81. #region Properties
  82. private bool All => (ItemTarget & (ItemTarget.All | ItemTarget.All2)) != 0;
  83. private Kernel.AttackType AttackType => Battle?.AttackType ?? Kernel.AttackType.None;
  84. public Kernel.BattleItemData Battle
  85. {
  86. get
  87. {
  88. if (Memory.KernelBin.BattleItemsData != null)
  89. return (Memory.KernelBin.BattleItemsData.Count) > ID
  90. ? Memory.KernelBin.BattleItemsData[ID]
  91. : null;
  92. return null;
  93. }
  94. }
  95. /// <summary>
  96. /// Which persistant statuses are removed.
  97. /// </summary>
  98. public Kernel.PersistentStatuses CleansedStatuses
  99. {
  100. get
  101. {
  102. var ret = Kernel.PersistentStatuses.None;
  103. if (ItemType == ItemType.HealGF || ItemType == ItemType.Heal || ItemType == ItemType.Revive || ItemType == ItemType.ReviveGF ||
  104. ItemType == ItemType.CureAbnormalStatus || ItemType == ItemType.SavePointHeal)
  105. ret = (Kernel.PersistentStatuses)_b3;
  106. return ret;
  107. }
  108. }
  109. public FF8String Description => Battle?.Description ?? NonBattle?.Description;
  110. public byte FadedPalette => 7;
  111. /// <summary>
  112. /// How much healing is done
  113. /// </summary>
  114. public ushort Heals
  115. {
  116. get
  117. {
  118. if (ItemType != ItemType.Heal && ItemType != ItemType.HealGF &&
  119. ItemType != ItemType.SavePointHeal) return 0;
  120. if (AttackType == Kernel.AttackType.GivePercentageHP)
  121. return (byte)((_b2 * 100) / byte.MaxValue);
  122. return (ushort)(_b2 * 0x32);
  123. //else if (Type == _Type.Revive || Type == _Type.ReviveGF)
  124. // return 0; // 12.5%
  125. }
  126. }
  127. public Icons.ID Icon { get; private set; }
  128. /// <summary>
  129. /// Item ID
  130. /// </summary>
  131. public byte ID { get; private set; }
  132. /// <summary>
  133. /// Who is targeted and 0x01 seems to be a useable item in menu item. Magazine values don't
  134. /// seem to corrispond.
  135. /// </summary>
  136. public ItemTarget ItemTarget => ItemType == ItemType.Magazine ? ItemTarget.None : _itemTarget;
  137. /// <summary>
  138. /// Type of item.
  139. /// </summary>
  140. public ItemType ItemType
  141. {
  142. get
  143. {
  144. if (ID == 0) _itemType = ItemType.None;
  145. return _itemType;
  146. }
  147. private set => _itemType = value;
  148. }
  149. /// <summary>
  150. /// Abilities a GF can learn
  151. /// </summary>
  152. public Kernel.Abilities Learn => ItemType == ItemType.GFLearn ? (Kernel.Abilities)_b2 : Kernel.Abilities.None;
  153. public Kernel.BlueMagic LearnedBlueMagic => ItemType == ItemType.BlueMagic ? (Kernel.BlueMagic)_b2 : Kernel.BlueMagic.None;
  154. public FF8String Name => Battle?.Name ?? NonBattle?.Name;
  155. public Kernel.NonBattleItemsData NonBattle => Battle == null ? Memory.KernelBin.NonBattleItemsData[ID - (Memory.KernelBin.BattleItemsData?.Count ?? 0)] : null;
  156. public byte Palette => 9;
  157. public Kernel.ShotIrvineLimitBreak Shot
  158. {
  159. get
  160. {
  161. if (Memory.KernelBin.ShotIrvineLimitBreak == null) return null;
  162. var id = ID - BulletOffset;
  163. return (Memory.KernelBin.ShotIrvineLimitBreak.Count) < id ||
  164. id < 0
  165. ? null
  166. : Memory.KernelBin.ShotIrvineLimitBreak[id];
  167. }
  168. }
  169. public Kernel.Stat Stat => ItemType == ItemType.Stat ? (Kernel.Stat)_b3 : Kernel.Stat.None;
  170. public byte StatIncrease => (byte)(ItemType == ItemType.Stat ? _b2 : 0);
  171. /*
  172. /// <summary>
  173. /// Target in byte form
  174. /// </summary>
  175. private byte TargetByte => (byte)_target;
  176. */
  177. private IReadOnlyDictionary<ItemType, Func<Faces.ID, bool, bool>> UseActions
  178. {
  179. get
  180. {
  181. if (_useActions == null)
  182. {
  183. _useActions = new Dictionary<ItemType, Func<Faces.ID, bool, bool>>
  184. {
  185. {ItemType.Heal, HealAction },
  186. {ItemType.Revive, ReviveAction },
  187. {ItemType.HealGF, HealGFAction },
  188. {ItemType.ReviveGF, ReviveGFAction },
  189. {ItemType.SavePointHeal, SavePointHealAction },
  190. {ItemType.Battle, BattleAction },
  191. {ItemType.Ammo, AmmoAction },
  192. {ItemType.Magazine, MagazineAction },
  193. {ItemType.None, NoneAction },
  194. {ItemType.GF, GFAction },
  195. {ItemType.Angelo, AngeloAction },
  196. {ItemType.Chocobo, ChocoboAction },
  197. {ItemType.Lamp, LampAction },
  198. {ItemType.SolomonRing, SolomonRingAction },
  199. {ItemType.GFCompatibility, GFCompatibilityAction },
  200. {ItemType.GFLearn, GFLearnAction },
  201. {ItemType.GFForget, GFForgetAction },
  202. {ItemType.BlueMagic, Blue_MagicAction },
  203. {ItemType.Stat, StatAction },
  204. {ItemType.CureAbnormalStatus, Cure_Abnormal_StatusAction },
  205. };
  206. }
  207. return _useActions;
  208. }
  209. }
  210. #endregion Properties
  211. #region Methods
  212. public static ItemInMenu Read(BinaryReader br, byte i)
  213. {
  214. Memory.Log.WriteLine($"{nameof(ItemInMenu)} :: {nameof(Read)} :: {i}");
  215. var tmp = new ItemInMenu
  216. {
  217. ItemType = (ItemType)br.ReadByte(),
  218. _itemTarget = (ItemTarget)br.ReadByte(),
  219. _b2 = br.ReadByte(),
  220. _b3 = br.ReadByte(),
  221. ID = i
  222. };
  223. switch (tmp.ItemType)
  224. {
  225. case ItemType.Heal:
  226. case ItemType.Revive:
  227. case ItemType.CureAbnormalStatus:
  228. tmp.Icon = Icons.ID.Item_Recovery;
  229. break;
  230. case ItemType.HealGF:
  231. case ItemType.ReviveGF:
  232. case ItemType.GF:
  233. case ItemType.GFForget:
  234. case ItemType.GFLearn:
  235. tmp.Icon = Icons.ID.Item_GF;
  236. break;
  237. case ItemType.Battle:
  238. case ItemType.Angelo:
  239. case ItemType.Chocobo: // i'm not sure about this one i have no chocobo items.
  240. case ItemType.SolomonRing:
  241. case ItemType.Lamp:
  242. tmp.Icon = Icons.ID.Item_Battle;
  243. break;
  244. case ItemType.SavePointHeal:
  245. tmp.Icon = Icons.ID.Item_Tent;
  246. break;
  247. case ItemType.Ammo:
  248. tmp.Icon = Icons.ID.Item_Ammo;
  249. break;
  250. case ItemType.Magazine:
  251. tmp.Icon = Icons.ID.Item_Magazine;
  252. break;
  253. case ItemType.None:
  254. case ItemType.BlueMagic:
  255. case ItemType.GFCompatibility:
  256. case ItemType.Stat:
  257. tmp.Icon = Icons.ID.Item_Misc;
  258. break;
  259. default:
  260. tmp.Icon = Icons.ID.None;
  261. break;
  262. }
  263. return tmp;
  264. }
  265. /// <summary>
  266. /// How much Compatability is added to gf. neg is how much is removed from other gfs.
  267. /// </summary>
  268. /// <param name="gf">Which GF is effected</param>
  269. /// <param name="neg">How much Comptability is lost by all other GFs</param>
  270. /// <returns>Total compatability gained by selected character for this GF.</returns>
  271. /// <see cref="https://gamefaqs.gamespot.com/ps/197343-final-fantasy-viii/faqs/6110"/>
  272. public byte Compatibility(out GFs gf, out sbyte neg)
  273. {
  274. gf = 0; neg = 0;
  275. byte ret = 0;
  276. if (ItemType == ItemType.GFCompatibility)
  277. {
  278. gf = (GFs)_b2;
  279. switch (_b3)
  280. {
  281. case 0x08://Weak C Item
  282. ret = 0x01;
  283. neg = (sbyte)(gf == GFs.All ? 0x00 : -0x01);
  284. break;
  285. case 0x10://Strong C Item
  286. ret = 0x03;
  287. neg = (sbyte)(gf == GFs.All ? 0x00 : -0x02);
  288. break;
  289. case 0x65: //LuvLuvG
  290. ret = 0x14;
  291. neg = 0x00;
  292. break;
  293. }
  294. }
  295. return ret;
  296. }
  297. /// <summary>
  298. /// If True Quistis can learn the ability. Else Quistis already knows the ability.
  299. /// </summary>
  300. /// <returns></returns>
  301. public bool TestBlueMagic()
  302. {
  303. if (LearnedBlueMagic != Kernel.BlueMagic.None)
  304. return !Memory.State.LimitBreakQuistisUnlockedBlueMagic[(int)LearnedBlueMagic];
  305. return false;
  306. }
  307. public bool TestCharacter(ref Faces.ID id, out Characters character)
  308. {
  309. character = id.ToCharacters();
  310. if (ItemType == ItemType.BlueMagic)
  311. {
  312. return character == Characters.Quistis_Trepe && TestBlueMagic();
  313. }
  314. var teamLaguna = character == Characters.Laguna_Loire ||
  315. character == Characters.Kiros_Seagill ||
  316. character == Characters.Ward_Zabac;
  317. if(Memory.State.TeamLaguna && !teamLaguna)
  318. return false;
  319. if (!Memory.State.TeamLaguna && teamLaguna)
  320. return false;
  321. return ItemTarget.HasFlag(ItemTarget.Character)&& (Memory.State[character]?.Available ?? false);
  322. }
  323. public bool TestGF(ref Faces.ID id, out GFs gf)
  324. {
  325. gf = id.ToGFs();
  326. if (ItemType == ItemType.Angelo && id == Faces.ID.Angelo)
  327. {
  328. return true;
  329. }
  330. if (ItemType == ItemType.Chocobo && id == Faces.ID.Boko)
  331. {
  332. return true;
  333. }
  334. if (gf == GFs.Blank || gf == GFs.All || (ItemTarget & ItemTarget.GF) == 0)
  335. return false;
  336. if (Memory.State?.GFs != null && Memory.State.GFs.ContainsKey(gf))// && Memory.State.GFs[gf].VisibleInMenu)
  337. {
  338. if (ItemType == ItemType.GFLearn && (!Memory.State.GFs[gf].TestGFCanLearn(Learn) || Memory.State.GFs[gf].MaxGFAbilities))
  339. return false;
  340. return true;
  341. }
  342. return false;
  343. }
  344. public override string ToString() => Name ?? base.ToString();
  345. public bool Use(Faces.ID obj, bool battle = false)
  346. {
  347. if (UseActions.ContainsKey(ItemType))
  348. {
  349. var ret = GFAction(UseActions[ItemType], obj, battle);
  350. ret = CharAction(UseActions[ItemType], obj, battle) || ret;
  351. if (ret)
  352. {
  353. for (var i = 0; i < Memory.State.Items.Count; i++)
  354. {
  355. if (Memory.State.Items[i].ID == ID)
  356. Memory.State.Items[i].UsedOne();
  357. }
  358. //Memory.State.Items.Where(m => m.ID == ID).ForEach(x => x.UsedOne());
  359. return true;
  360. }
  361. }
  362. return false;
  363. }
  364. public bool ValidTarget(bool b = false)
  365. {
  366. if (ItemType == ItemType.Magazine) return true; //TODO pressing okay display Magazine.
  367. else if (ItemType == ItemType.SolomonRing) return !Memory.State[GFs.Doomtrain]?.Exists ?? true; //TODO detect doomtrain and return false if unlocked
  368. else if (ItemType == ItemType.Lamp) return !Memory.State[GFs.Diablos]?.Exists ?? true; //TODO detect diablo and return false if unlocked
  369. Faces.ID _face;
  370. foreach (var face in (Faces.ID[])Enum.GetValues(typeof(Faces.ID)))
  371. {
  372. _face = face;
  373. if (TestCharacter(ref _face, out _) || TestGF(ref _face, out _))
  374. {
  375. return true;
  376. }
  377. }
  378. if (Battle != null && b) return true;
  379. return false;
  380. }
  381. private static bool ChocoboAction(Faces.ID obj, bool battle = false) => false;
  382. private static bool GFAction(Faces.ID obj, bool battle = false) => false;
  383. private static bool GFCompatibilityAction(Faces.ID obj, bool battle = false) => false;
  384. /// <summary>
  385. /// Spawn a menu of GF's unlocked abilities and you select one to remove.
  386. /// </summary>
  387. private static bool GFForgetAction(Faces.ID obj, bool battle = false) => false;
  388. private bool AmmoAction(Faces.ID obj, bool battle = false) => false;
  389. private bool AngeloAction(Faces.ID obj, bool battle = false) => false;
  390. private bool BattleAction(Faces.ID obj, bool battle = false) => false;
  391. private bool Blue_MagicAction(Faces.ID obj, bool battle = false) => false;
  392. private bool CharAction(Func<Faces.ID, bool, bool> func, Faces.ID obj, bool battle = false)
  393. {
  394. var ret = false;
  395. if (All)
  396. {
  397. foreach (var c in Memory.State.PartyData)
  398. {
  399. obj = c.ToFacesID();
  400. if (TestCharacter(ref obj, out _))
  401. ret = func(obj, battle) || ret;
  402. }
  403. return ret;
  404. }
  405. if (TestCharacter(ref obj, out _))
  406. ret = func(obj, battle);
  407. return ret;
  408. }
  409. private bool Cure_Abnormal_StatusAction(Faces.ID obj, bool battle = false) => Cure_Abnormal_StatusAction(Memory.State[obj]);
  410. private bool Cure_Abnormal_StatusAction(IStatusEffects c)
  411. {
  412. var ret = false;
  413. if (c != null && !c.StatusImmune)
  414. ret = c.DealStatus(CleansedStatuses, Battle?.Statuses1, Battle?.AttackType ?? Kernel.AttackType.CurativeItem, Battle?.AttackFlags);
  415. return ret;
  416. }
  417. private bool GFAction(Func<Faces.ID, bool, bool> func, Faces.ID obj, bool battle = false)
  418. {
  419. var ret = false;
  420. if (All)
  421. {
  422. foreach (var c in Memory.State.GFs.Where(x => x.Value.Exists))
  423. {
  424. obj = c.Key.ToFacesID();
  425. if (TestGF(ref obj, out _))
  426. ret = func(obj, battle) || ret;
  427. }
  428. return ret;
  429. }
  430. if (TestGF(ref obj, out _))
  431. ret = func(obj, battle);
  432. return ret;
  433. }
  434. /// <summary>
  435. /// GF learns new skill if below max abilities
  436. /// </summary>
  437. private bool GFLearnAction(Faces.ID obj, bool battle = false) => GFLearnAction(Memory.State[obj.ToGFs()]);
  438. private bool GFLearnAction(Saves.GFData gf) => gf.Learn(Learn);
  439. private bool HealAction(Faces.ID obj, bool battle = false) =>
  440. //Memory.State[obj].ChangeHP(-Heals);
  441. //return true;
  442. //bool ret = false;
  443. //if (!Memory.State[obj.ToCharacters()]?.StatusImmune ?? true && Cleansed_Statuses != Kernel_bin.Persistant_Statuses.None)
  444. // ret = Memory.State[obj.ToCharacters()]?.DealStatus(Cleansed_Statuses, Battle?.Statuses1, Battle?.Attack_Type ?? Kernel_bin.Attack_Type.Curative_Item, Battle?.Attack_Flags) ?? false;
  445. //ret = Memory.State[obj.ToCharacters()]?.DealDamage(Heals, Battle?.Attack_Type ?? Kernel_bin.Attack_Type.Curative_Item, Battle?.Attack_Flags) ?? false || ret;
  446. //return ret;
  447. HealAction(Memory.State[obj], battle);
  448. private bool HealAction(Damageable c, bool battle)
  449. {
  450. //c.ChangeHP(-Heals);
  451. var ret = false;
  452. if (c != null && !ZombieCheck(c.Statuses0, battle))
  453. {
  454. if (!c.StatusImmune && CleansedStatuses != Kernel.PersistentStatuses.None)
  455. ret = c.DealStatus(CleansedStatuses, Battle?.Statuses1, Battle?.AttackType ?? Kernel.AttackType.CurativeItem, Battle?.AttackFlags);
  456. ret = c.DealDamage(Heals, Battle?.AttackType ?? Kernel.AttackType.CurativeItem, Battle?.AttackFlags) || ret;
  457. }
  458. return ret;
  459. }
  460. private bool HealGFAction(Faces.ID obj, bool battle = false) => HealAction(Memory.State[obj.ToGFs()], battle);
  461. private bool LampAction(Faces.ID obj, bool battle = false) => false;
  462. private bool MagazineAction(Faces.ID obj, bool battle = false) =>
  463. //TODO display magazine
  464. false;
  465. private bool NoneAction(Faces.ID obj, bool battle = false) => false;
  466. private bool ReviveAction(Faces.ID obj, bool battle = false) => ReviveAction(Memory.State[obj], battle);
  467. private bool ReviveAction(Damageable c, bool battle)
  468. {
  469. var ret = false;
  470. if (c != null && !ZombieCheck(c.Statuses0, battle))
  471. {
  472. if (!c.StatusImmune && CleansedStatuses != Kernel.PersistentStatuses.None)
  473. ret = c.DealStatus(CleansedStatuses, Battle?.Statuses1, Battle?.AttackType ?? Kernel.AttackType.CurativeItem, Battle?.AttackFlags);
  474. ret = c.DealDamage(0, Battle?.AttackType ?? Kernel.AttackType.Revive, Battle?.AttackFlags) || ret;
  475. }
  476. return ret;
  477. }
  478. private bool ReviveGFAction(Faces.ID obj, bool battle = false) => ReviveAction(Memory.State[obj], battle);
  479. private bool SavePointHealAction(Faces.ID obj, bool battle = false) => false;
  480. private bool SolomonRingAction(Faces.ID obj, bool battle = false) => false;
  481. private bool StatAction(Faces.ID obj, bool battle = false) => false;
  482. private bool ZombieCheck(Kernel.PersistentStatuses statuses0, bool battle = false) =>
  483. (statuses0 & Kernel.PersistentStatuses.Zombie) != 0 &&
  484. (CleansedStatuses & Kernel.PersistentStatuses.Zombie) == 0 &&
  485. !battle;
  486. #endregion Methods
  487. }
  488. /// <summary>
  489. /// Info on Items in menus. Items have a slightly limited effect in menues. See
  490. /// Kernel_bin.Battle_Items for effects in battle.
  491. /// </summary>
  492. /// <see cref="http://forums.qhimm.com/index.php?topic=17098.0"/>
  493. public class ItemsInMenu : IReadOnlyList<ItemInMenu>
  494. {
  495. #region Fields
  496. private readonly List<ItemInMenu> _items;
  497. #endregion Fields
  498. #region Constructors
  499. private ItemsInMenu()
  500. {
  501. Memory.Log.WriteLine($"{nameof(ItemsInMenu)} :: new ");
  502. _items = new List<ItemInMenu>();
  503. }
  504. #endregion Constructors
  505. #region Properties
  506. public int Count => _items.Count;
  507. public IReadOnlyList<ItemInMenu> Items => _items;
  508. #endregion Properties
  509. #region Indexers
  510. public ItemInMenu this[byte item] => Items[item];
  511. public ItemInMenu this[Saves.Item item] => Items[item.ID];
  512. public ItemInMenu this[int index] => _items[index];
  513. #endregion Indexers
  514. #region Methods
  515. public static ItemsInMenu Read()
  516. {
  517. var aw = ArchiveWorker.Load(Memory.Archives.A_MENU);
  518. var buffer = aw.GetBinaryFile("mitem.bin");
  519. if (buffer != null)
  520. using (var br = new BinaryReader(new MemoryStream(buffer)))
  521. {
  522. return Read(br);
  523. }
  524. return default;
  525. }
  526. public IEnumerator<ItemInMenu> GetEnumerator() => _items.GetEnumerator();
  527. IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_items).GetEnumerator();
  528. private static ItemsInMenu Read(BinaryReader br)
  529. {
  530. Memory.Log.WriteLine($"{nameof(ItemsInMenu)} :: {nameof(Read)} ");
  531. var ret = new ItemsInMenu();
  532. for (byte i = 0; br.BaseStream.Position + 4 <= br.BaseStream.Length; i++)
  533. {
  534. var tmp = ItemInMenu.Read(br, i);
  535. ret._items.Add(tmp);
  536. }
  537. return ret;
  538. }
  539. #endregion Methods
  540. }
  541. }