Strings.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. namespace OpenVIII
  7. {
  8. /// <summary>
  9. /// Loads strings from FF8 files
  10. /// </summary>
  11. public partial class Strings : IReadOnlyDictionary<Strings.FileID, Strings.StringsBase>
  12. {
  13. #region Fields
  14. private static ConcurrentDictionary<FileID, StringsBase> _files;
  15. private static readonly object Lock = new object();
  16. #endregion Fields
  17. #region Constructors
  18. public Strings() => Init();
  19. #endregion Constructors
  20. #region Enums
  21. /// <summary>
  22. /// file names of files with strings and ID's for structs that hold the data.
  23. /// </summary>
  24. public enum FileID : uint
  25. {
  26. MenuGroup = 0,
  27. AreaNames = 2,
  28. Namedic = 3,
  29. Kernel = 4,
  30. }
  31. #endregion Enums
  32. #region Methods
  33. public FF8String GetName(Characters c, Saves.Data d = null) => GetName(c.ToFacesID(), d);
  34. public FF8String GetName(GFs gf, Saves.Data d = null) => GetName(gf.ToFacesID(), d);
  35. public FF8String GetName(Faces.ID id, Saves.Data d = null)
  36. {
  37. if (d == null)
  38. d = Memory.State;
  39. switch (id)
  40. {
  41. case Faces.ID.Squall_Leonhart:
  42. return d.SquallName ?? Read(FileID.MenuGroup, 2, 92);
  43. case Faces.ID.Rinoa_Heartilly:
  44. return d.RinoaName ?? Read(FileID.MenuGroup, 2, 93);
  45. case Faces.ID.Angelo:
  46. return d.AngeloName ?? Read(FileID.MenuGroup, 2, 94);
  47. case Faces.ID.Boko:
  48. return d.BokoName ?? Read(FileID.MenuGroup, 2, 135);
  49. case Faces.ID.Zell_Dincht:
  50. case Faces.ID.Irvine_Kinneas:
  51. case Faces.ID.Quistis_Trepe:
  52. case Faces.ID.Selphie_Tilmitt:
  53. case Faces.ID.Seifer_Almasy:
  54. case Faces.ID.Edea_Kramer:
  55. case Faces.ID.Laguna_Loire:
  56. case Faces.ID.Kiros_Seagill:
  57. case Faces.ID.Ward_Zabac:
  58. return Read(FileID.Kernel, 6, (int)id);
  59. case Faces.ID.Quezacotl:
  60. case Faces.ID.Shiva:
  61. case Faces.ID.Ifrit:
  62. case Faces.ID.Siren:
  63. case Faces.ID.Brothers:
  64. case Faces.ID.Diablos:
  65. case Faces.ID.Carbuncle:
  66. case Faces.ID.Leviathan:
  67. case Faces.ID.Pandemona:
  68. case Faces.ID.Cerberus:
  69. case Faces.ID.Alexander:
  70. case Faces.ID.Doomtrain:
  71. case Faces.ID.Bahamut:
  72. case Faces.ID.Cactuar:
  73. case Faces.ID.Tonberry:
  74. case Faces.ID.Eden:
  75. if (d.GFs != null && d.GFs.TryGetValue(id.ToGFs(), out var value) && value.Name != null &&
  76. value.Name.Length > 0)
  77. return value.Name;
  78. return Read(FileID.MenuGroup, 2, 95 + (int)(id.ToGFs()));
  79. case Faces.ID.Griever:
  80. return d.GrieverName ?? Read(FileID.MenuGroup, 2, 135);
  81. case Faces.ID.MiniMog:
  82. return Read(FileID.Kernel, 0, 72); // also in KERNEL, 12, 36
  83. case Faces.ID.Blank:
  84. return new FF8String();
  85. default:
  86. throw new ArgumentOutOfRangeException(nameof(id), id, null);
  87. }
  88. }
  89. /// <summary>
  90. /// Remember to Close() if done using
  91. /// </summary>
  92. /// <param name="fileID"></param>
  93. /// <param name="sectionID"></param>
  94. /// <param name="stringID"></param>
  95. /// <returns></returns>
  96. public FF8StringReference Read(FileID fileID, int sectionID, int stringID) => _files[fileID][sectionID, stringID];
  97. public FF8StringReference ReadByOffset(FileID fileID, int sectionID, int offset)
  98. {
  99. var r = new FF8StringReference(Memory.Strings[FileID.Kernel].GetArchive(),
  100. Memory.Strings[FileID.Kernel].GetFileNames()[0],
  101. Memory.Strings[FileID.Kernel].GetFiles()
  102. .SubPositions[(int) ((Kernel) Memory.Strings[FileID.Kernel]).StringLocations[0].StringLocation] +
  103. offset, settings: (FF8StringReference.Settings.Namedic | FF8StringReference.Settings.MultiCharByte));
  104. return r.Length>0 ? r : null;
  105. }
  106. public bool ContainsKey(FileID key)
  107. {
  108. return _files.ContainsKey(key);
  109. }
  110. public bool TryGetValue(FileID key, out StringsBase value)
  111. {
  112. return _files.TryGetValue(key, out value);
  113. }
  114. public StringsBase this[FileID id] => _files[id];
  115. public IEnumerable<FileID> Keys => ((IReadOnlyDictionary<FileID, StringsBase>) _files).Keys;
  116. public IEnumerable<StringsBase> Values => ((IReadOnlyDictionary<FileID, StringsBase>) _files).Values;
  117. private void Init()
  118. {
  119. lock (Lock)
  120. if (_files == null)
  121. {
  122. Memory.Log.WriteLine($"{nameof(Strings)} :: {nameof(Init)}");
  123. _files = new ConcurrentDictionary<FileID, StringsBase>();
  124. Func<bool>[] func = {
  125. () => _files.TryAdd(FileID.Namedic, Namedic.Load()), // area names and kernel require namedic
  126. //though there shouldn't be anything reading the strings till this is done processing
  127. //Task.WaitAll(tasks.ToArray());
  128. () => _files.TryAdd(FileID.MenuGroup, MenuGroup.Load()),
  129. () => _files.TryAdd(FileID.AreaNames, Areames.Load()),
  130. () => _files.TryAdd(FileID.Kernel, Kernel.Load()),
  131. };
  132. var tasks = Memory.ProcessFunctions(func);
  133. if (tasks.Any(x => !x))
  134. throw new ArgumentException($"{this}::Failed to add to dictionary...");
  135. }
  136. }
  137. #endregion Methods
  138. public IEnumerator<KeyValuePair<FileID, StringsBase>> GetEnumerator()
  139. {
  140. return _files.GetEnumerator();
  141. }
  142. IEnumerator IEnumerable.GetEnumerator()
  143. {
  144. return ((IEnumerable) _files).GetEnumerator();
  145. }
  146. public int Count => _files.Count;
  147. }
  148. }