StringsBase.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. namespace OpenVIII
  6. {
  7. /// <summary>
  8. /// Loads strings from FF8 files
  9. /// </summary>
  10. public partial class Strings
  11. {
  12. #region Classes
  13. public abstract class StringsBase : IReadOnlyDictionary<int, List<FF8StringReference>>
  14. {
  15. #region Fields
  16. protected Memory.Archive Archive;
  17. protected string[] FileNames;
  18. protected StringFile StringFiles;
  19. protected FF8StringReference.Settings Settings;
  20. #endregion Fields
  21. #region Constructors
  22. protected void SetValues(Memory.Archive archive, params string[] fileNames)
  23. {
  24. Memory.Log.WriteLine($"{nameof(StringsBase)}::{nameof(SetValues)} Archive= \"{archive}\", Files= {{{string.Join(", ", fileNames)}}}");
  25. Archive = archive;
  26. FileNames = fileNames;
  27. }
  28. #endregion Constructors
  29. #region Indexers
  30. public FF8StringReference this[int sectionID, int stringID] => StringFiles?[sectionID, stringID];
  31. #endregion Indexers
  32. #region Methods
  33. public Memory.Archive GetArchive() => Archive;
  34. public IReadOnlyList<string> GetFileNames() => FileNames;
  35. public StringFile GetFiles() => StringFiles;
  36. /// <summary>
  37. /// <para>
  38. /// So you read the pointers at location, you get so many pointers then skip so many
  39. /// bytes before getting more pointers. Do this till start of next section.
  40. /// </para>
  41. /// </summary>
  42. /// <param name="br">BinaryReader where data is.</param>
  43. /// <param name="filename">file you are reading from</param>
  44. /// <param name="pointerStart">Section where pointers are.</param>
  45. /// <param name="stringStart">Section where strings are</param>
  46. /// <param name="grab">Get so many pointers</param>
  47. /// <param name="skip">Then skip so many bytes</param>
  48. protected void Get_Strings_BinMSG(BinaryReader br, string filename, int pointerStart, uint stringStart, uint grab = 0, uint skip = 0)
  49. {
  50. var fPos = StringFiles.SubPositions[pointerStart];
  51. if (fPos.Seek > br.BaseStream.Length) return;
  52. br.BaseStream.Seek(fPos.Seek, SeekOrigin.Begin);
  53. if (StringFiles.SPositions.ContainsKey(pointerStart))
  54. {
  55. }
  56. else
  57. {
  58. ushort b = 0;
  59. var last = b;
  60. var tmp = new List<FF8StringReference>();
  61. uint g = 1;
  62. while (br.BaseStream.Position < fPos.Max)
  63. {
  64. b = br.ReadUInt16();
  65. if (last > b)
  66. break;
  67. else
  68. {
  69. if (b != 0xFFFF)
  70. {
  71. tmp.Add(new FF8StringReference(Archive, filename, b + stringStart, settings: Settings));
  72. last = b;
  73. }
  74. else
  75. tmp.Add(null);
  76. if (grab <= 0 || ++g <= grab) continue;
  77. br.BaseStream.Seek(skip, SeekOrigin.Current);
  78. g = 1;
  79. }
  80. }
  81. StringFiles.SPositions.Add(pointerStart, tmp);
  82. }
  83. }
  84. protected void Get_Strings_ComplexStr(BinaryReader br, string filename, int key, IReadOnlyList<int> list)
  85. {
  86. var fPaddings = MenuGroupReadPadding(br, StringFiles.SubPositions[key], 1);
  87. StringFiles.SPositions.Add(key, new List<FF8StringReference>());
  88. if (fPaddings == null) return;
  89. for (uint p = 0; p < fPaddings.Length; p += 2)
  90. {
  91. key = list[(int)fPaddings[(int)p + 1]];
  92. var fPos = StringFiles.SubPositions[(int)key];
  93. var fPad = fPaddings[p] + fPos.Seek;
  94. br.BaseStream.Seek(fPad, SeekOrigin.Begin);
  95. if (!StringFiles.SPositions.ContainsKey(key))
  96. StringFiles.SPositions.Add(key, new List<FF8StringReference>());
  97. br.BaseStream.Seek(fPad + 6, SeekOrigin.Begin);
  98. //byte[] UNK = br.ReadBytes(6);
  99. var len = br.ReadUInt16();
  100. var stop = (uint)(br.BaseStream.Position + len - 9); //6 for UNK, 2 for len 1, for end null
  101. StringFiles.SPositions[key].Add(new FF8StringReference(Archive, filename, (uint)br.BaseStream.Position, settings: Settings));
  102. //entry contains possible more than one string so I am scanning for null
  103. while (br.BaseStream.Position + 1 < stop)
  104. {
  105. var b = br.ReadByte();
  106. if (b == 0) StringFiles.SPositions[key].Add(new FF8StringReference(Archive, filename, (uint)br.BaseStream.Position, settings: Settings));
  107. }
  108. }
  109. }
  110. /// <summary>
  111. /// TODO: make this work with more than one file.
  112. /// </summary>
  113. /// <param name="br"></param>
  114. /// <param name="spos"></param>
  115. /// <param name="key"></param>
  116. /// <param name="pad"></param>
  117. protected void Get_Strings_Offsets(BinaryReader br, string filename, int key, bool pad = false)
  118. {
  119. var fPos = StringFiles.SubPositions[key];
  120. var fPaddings = pad ? MenuGroupReadPadding(br, fPos) : (new uint[] { 1 });
  121. if (fPaddings == null) return;
  122. StringFiles.SPositions.Add(key, new List<FF8StringReference>());
  123. for (uint p = 0; p < fPaddings.Length; p++)
  124. {
  125. if (fPaddings[p] <= 0) continue;
  126. var fPad = pad ? fPaddings[p] + fPos.Seek : fPos.Seek;
  127. if (fPad > br.BaseStream.Length) return;
  128. br.BaseStream.Seek(fPad, SeekOrigin.Begin);
  129. if (br.BaseStream.Position + 4 >= br.BaseStream.Length) continue;
  130. int count = br.ReadUInt16();
  131. for (var i = 0; i < count && br.BaseStream.Position + 2 < br.BaseStream.Length; i++)
  132. {
  133. uint c = br.ReadUInt16();
  134. if (c >= br.BaseStream.Length || c == 0) continue;
  135. c += fPad;
  136. //long loc =br.BaseStream.Position;
  137. //try
  138. //{
  139. // br.BaseStream.Seek(c, SeekOrigin.Begin);
  140. // if(br.ReadByte()!=0)
  141. StringFiles.SPositions[key].Add(new FF8StringReference(Archive, filename, c, settings: Settings));
  142. // }
  143. // finally
  144. //{
  145. // br.BaseStream.Seek(loc,SeekOrigin.Begin);
  146. //}
  147. }
  148. }
  149. }
  150. protected abstract void LoadArchiveFiles();
  151. protected uint[] MenuGroupReadPadding(BinaryReader br, Loc fPos, int type = 0)
  152. {
  153. if (fPos.Seek > br.BaseStream.Length) return null;
  154. br.BaseStream.Seek(fPos.Seek, SeekOrigin.Begin);
  155. var size = type == 0 ? br.ReadUInt16() : br.ReadUInt32();
  156. var fPaddings = new uint[type == 0 ? size : size * type * 2];
  157. for (var i = 0; i < fPaddings.Length; i += 1 + type)
  158. {
  159. fPaddings[i] = br.ReadUInt16();
  160. if (type == 0 && fPaddings[i] + fPos.Seek >= fPos.Max)
  161. fPaddings[i] = 0;
  162. for (var j = 1; j < type + 1; j++)
  163. {
  164. fPaddings[i + j] = br.ReadUInt16();
  165. }
  166. }
  167. return fPaddings;
  168. }
  169. protected abstract void DefaultValues();
  170. public static T Load<T>() where T : StringsBase, new()
  171. {
  172. Memory.Log.WriteLine($"{nameof(StringsBase)} :: {nameof(Load)} :: {typeof(T)}");
  173. var r = new T();
  174. r.DefaultValues();
  175. r.LoadArchiveFiles();
  176. return r;
  177. }
  178. protected void LoadArchiveFiles_Simple()
  179. {
  180. var aw = ArchiveWorker.Load(Archive, true);
  181. var buffer = aw.GetBinaryFile(FileNames[0],true);
  182. if (buffer == null) return;
  183. using (var br = new BinaryReader(new MemoryStream(buffer, true)))
  184. {
  185. StringFiles = new StringFile(1);
  186. StringFiles.SubPositions.Add(Loc.CreateInstance(0, uint.MaxValue));
  187. Get_Strings_Offsets(br, FileNames[0], 0);
  188. }
  189. }
  190. public IEnumerator<KeyValuePair<int, List<FF8StringReference>>> GetEnumerator()
  191. {
  192. return StringFiles.GetEnumerator();
  193. }
  194. IEnumerator IEnumerable.GetEnumerator()
  195. {
  196. return ((IEnumerable) StringFiles).GetEnumerator();
  197. }
  198. public int Count => StringFiles.Count;
  199. public bool ContainsKey(int key)
  200. {
  201. return StringFiles.ContainsKey(key);
  202. }
  203. public bool TryGetValue(int key, out List<FF8StringReference> value)
  204. {
  205. return StringFiles.TryGetValue(key, out value);
  206. }
  207. public List<FF8StringReference> this[int key] => ((IReadOnlyDictionary<int, List<FF8StringReference>>) StringFiles)[key];
  208. public IEnumerable<int> Keys => StringFiles.Keys;
  209. public IEnumerable<List<FF8StringReference>> Values => StringFiles.Values;
  210. }
  211. #endregion Methods
  212. }
  213. #endregion Classes
  214. }