Strings.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. namespace FF8
  7. {
  8. /// <summary>
  9. /// Loads strings from FF8 files
  10. /// </summary>
  11. public class Strings
  12. {
  13. #region Fields
  14. private readonly string[] filenames = new string[] { "mngrp.bin", "mngrphd.bin", "areames.dc1", "namedic.bin", "kernel.bin" };
  15. private string ArchiveString;
  16. private ArchiveWorker aw;
  17. //temp storage for locations isn't kept long term.
  18. private Dictionary<uint, uint> BinMSG;
  19. private Dictionary<uint, List<uint>> ComplexStr;
  20. private Dictionary<FileID, Stringfile> files;
  21. private FileID last;
  22. private BinaryReader localbr;
  23. private MemoryStream localms;
  24. /// <summary>
  25. /// Colly's list of string pointers. Adapted. Some strings might be missing.
  26. /// </summary>
  27. /// <see cref="http://www.balamb.pl/qh/kernel-pointers.htm"/>
  28. public Dictionary<uint, Tuple<uint, uint, uint>> Kernel_LocSTR
  29. { get; private set; }
  30. private bool opened = false;
  31. private uint[] StringsLoc;
  32. private uint[] StringsPadLoc;
  33. public Dictionary<FileID, Stringfile> Files { get => files; }
  34. public string[] Filenames => filenames;
  35. #endregion Fields
  36. #region Constructors
  37. public Strings() => init();
  38. #endregion Constructors
  39. #region Enums
  40. /// <summary>
  41. /// filenames of files with strings and id's for structs that hold the data.
  42. /// </summary>
  43. public enum FileID : uint
  44. {
  45. MNGRP = 0,
  46. /// <summary>
  47. /// only used as holder for the mngrp's map filename
  48. /// </summary>
  49. MNGRP_MAP = 1,
  50. AREAMES = 2,
  51. NAMEDIC = 3,
  52. KERNEL = 4,
  53. }
  54. /// <summary>
  55. /// Todo make an enum to id every section.
  56. /// </summary>
  57. public enum SectionID : uint
  58. {
  59. tkmnmes1 = 0,
  60. tkmnmes2 = 1,
  61. tkmnmes3 = 2,
  62. }
  63. #endregion Enums
  64. #region Methods
  65. public void Close()
  66. {
  67. if (opened)
  68. {
  69. localbr.Close();
  70. localbr.Dispose();
  71. opened = false;
  72. }
  73. }
  74. public void GetAW(FileID fileID, bool force = false)
  75. {
  76. switch (fileID)
  77. {
  78. case FileID.MNGRP:
  79. case FileID.MNGRP_MAP:
  80. case FileID.AREAMES:
  81. default:
  82. ArchiveString = Memory.Archives.A_MENU;
  83. break;
  84. case FileID.NAMEDIC:
  85. case FileID.KERNEL:
  86. ArchiveString = Memory.Archives.A_MAIN;
  87. break;
  88. }
  89. if (aw == null || aw.GetPath() != ArchiveString || force)
  90. aw = new ArchiveWorker(ArchiveString);
  91. }
  92. public void Open(FileID fileID)
  93. {
  94. if (opened)
  95. throw new Exception("Must close before opening again");
  96. GetAW(fileID);
  97. try
  98. {
  99. localms = new MemoryStream(aw.GetBinaryFile(
  100. aw.GetListOfFiles().First(x => x.IndexOf(filenames[(int)fileID], StringComparison.OrdinalIgnoreCase) >= 0)));
  101. }
  102. catch
  103. {
  104. GetAW(fileID, true);
  105. localms = new MemoryStream(aw.GetBinaryFile(
  106. aw.GetListOfFiles().First(x => x.IndexOf(filenames[(int)fileID], StringComparison.OrdinalIgnoreCase) >= 0)));
  107. }
  108. localbr = new BinaryReader(localms);
  109. opened = true;
  110. last = fileID;
  111. }
  112. //public byte[] Read(FileID fileID, SectionID sectionID, int stringID) => Read(fileID, (int)sectionID, stringID);
  113. /// <summary>
  114. /// Remember to Close() if done using
  115. /// </summary>
  116. /// <param name="fileID"></param>
  117. /// <param name="sectionID"></param>
  118. /// <param name="stringID"></param>
  119. /// <returns></returns>
  120. public FF8String Read(FileID fileID, int sectionID, int stringID)
  121. {
  122. switch (fileID)
  123. {
  124. case FileID.MNGRP_MAP:
  125. throw new Exception("map file has no string");
  126. case FileID.MNGRP:
  127. case FileID.AREAMES:
  128. default:
  129. return Read(fileID, files[fileID].sPositions[(uint)sectionID][stringID]);
  130. }
  131. }
  132. private void init()
  133. {
  134. files = new Dictionary<FileID, Stringfile>(2);
  135. GetAW(FileID.MNGRP, true);
  136. mngrp_init();
  137. GetAW(FileID.AREAMES);
  138. simple_init(FileID.AREAMES);
  139. GetAW(FileID.NAMEDIC, true);
  140. simple_init(FileID.NAMEDIC);
  141. GetAW(FileID.KERNEL);
  142. Kernel_init(FileID.KERNEL);
  143. }
  144. /// <summary>
  145. /// Fetch strings from kernel.bin
  146. /// </summary>
  147. /// <param name="fileID">Should be FileID.KERNEL</param>
  148. /// <see cref="http://www.balamb.pl/qh/kernel-pointers.htm"/>
  149. private void Kernel_init(FileID fileID)
  150. {
  151. files[fileID] = new Stringfile(new Dictionary<uint, List<uint>>(56), new List<Loc>(56));
  152. using (MemoryStream ms = new MemoryStream(aw.GetBinaryFile(
  153. aw.GetListOfFiles().First(x => x.IndexOf(filenames[(int)fileID], StringComparison.OrdinalIgnoreCase) >= 0))))
  154. using (BinaryReader br = new BinaryReader(ms))
  155. {
  156. kernel_GetFileLocations(br);
  157. Kernel_LocSTR = new Dictionary<uint, Tuple<uint, uint, uint>> {
  158. //working
  159. {0, new Tuple<uint, uint, uint>(31,2,4) },
  160. {1, new Tuple<uint, uint, uint>(32,2,56) },
  161. {2, new Tuple<uint, uint, uint>(33,2,128) },
  162. {3, new Tuple<uint, uint, uint>(34,1,18) },//38,58,178, or 78
  163. {4, new Tuple<uint, uint, uint>(35,1,10) },
  164. {5, new Tuple<uint, uint, uint>(36,2,20) },
  165. {6, new Tuple<uint, uint, uint>(37,1,34) },//+1interval 70 //character names here.
  166. {7, new Tuple<uint, uint, uint>(38,2,20) },
  167. {8, new Tuple<uint, uint, uint>(39,1,0) },
  168. {9, new Tuple<uint, uint, uint>(40,1,18) },
  169. {11, new Tuple<uint, uint, uint>(41,2,4) },
  170. {12, new Tuple<uint, uint, uint>(42,2,4) },
  171. {13, new Tuple<uint, uint, uint>(43,2,4) },
  172. {14, new Tuple<uint, uint, uint>(44,2,4) },
  173. {15, new Tuple<uint, uint, uint>(45,2,4) },
  174. {16, new Tuple<uint, uint, uint>(46,2,4) },
  175. {17, new Tuple<uint, uint, uint>(47,2,4) },
  176. {18, new Tuple<uint, uint, uint>(48,2,20) },
  177. {19, new Tuple<uint, uint, uint>(49,2,12) },
  178. {21, new Tuple<uint, uint, uint>(50,2,20) },
  179. {22, new Tuple<uint, uint, uint>(51,2,28) },
  180. {24, new Tuple<uint, uint, uint>(52,2,4) },
  181. {25, new Tuple<uint, uint, uint>(53,1,18) },
  182. {28, new Tuple<uint, uint, uint>(54,1,10) },
  183. {30, new Tuple<uint, uint, uint>(55,1,0) },
  184. };
  185. for (uint key = 0; key < files[fileID].subPositions.Count; key++)
  186. {
  187. Loc fpos = files[fileID].subPositions[(int)key];
  188. bool pad = (Array.IndexOf(StringsPadLoc, key) >= 0);
  189. //if (pad || Array.IndexOf(StringsLoc, key) >= 0)
  190. // mngrp_get_string_offsets(br, fileID, key, pad);
  191. //else
  192. if (Kernel_LocSTR.ContainsKey(key))
  193. {
  194. mngrp_get_string_BinMSG(br, fileID, key, files[fileID].subPositions[(int)(Kernel_LocSTR[key].Item1)].seek, Kernel_LocSTR[key].Item2, Kernel_LocSTR[key].Item3);
  195. }
  196. //else if (ComplexStr.ContainsKey(key))
  197. //{
  198. // Mngrp_get_string_ComplexStr(br, fileID, key, ComplexStr[key]);
  199. //}
  200. }
  201. }
  202. }
  203. private void mngrp_get_string_BinMSG(BinaryReader br, FileID fileID, uint key, uint msgPos, uint grab = 0, uint skip = 0)
  204. {
  205. Loc fpos = files[fileID].subPositions[(int)key];
  206. br.BaseStream.Seek(fpos.seek, SeekOrigin.Begin);
  207. if (files[fileID].sPositions.ContainsKey(key))
  208. {
  209. }
  210. else
  211. {
  212. ushort b = 0;
  213. ushort last = b;
  214. files[fileID].sPositions.Add(key, new List<uint>());
  215. uint g = 1;
  216. while (br.BaseStream.Position < fpos.max)
  217. {
  218. b = br.ReadUInt16();
  219. if (last > b)
  220. break;
  221. else
  222. {
  223. if (b != 0xFFFF)
  224. {
  225. files[fileID].sPositions[key].Add(b + msgPos);
  226. last = b;
  227. }
  228. else
  229. files[fileID].sPositions[key].Add(0);
  230. if (grab > 0 && ++g > grab)
  231. {
  232. br.BaseStream.Seek(skip, SeekOrigin.Current);
  233. g = 1;
  234. }
  235. }
  236. }
  237. }
  238. }
  239. private void Mngrp_get_string_ComplexStr(BinaryReader br, FileID fileID, uint key, List<uint> list)
  240. {
  241. uint[] fPaddings;
  242. fPaddings = mngrp_read_padding(br, files[fileID].subPositions[(int)key], 1);
  243. files[fileID].sPositions.Add(key, new List<uint>());
  244. for (uint p = 0; p < fPaddings.Length; p += 2)
  245. {
  246. key = list[(int)fPaddings[(int)p + 1]];
  247. Loc fpos = files[fileID].subPositions[(int)key];
  248. uint fpad = fPaddings[p] + fpos.seek;
  249. br.BaseStream.Seek(fpad, SeekOrigin.Begin);
  250. if (!files[fileID].sPositions.ContainsKey(key))
  251. files[fileID].sPositions.Add(key, new List<uint>());
  252. br.BaseStream.Seek(fpad + 6, SeekOrigin.Begin);
  253. //byte[] UNK = br.ReadBytes(6);
  254. ushort len = br.ReadUInt16();
  255. uint stop = (uint)(br.BaseStream.Position + len - 9); //6 for UNK, 2 for len 1, for end null
  256. files[fileID].sPositions[key].Add((uint)br.BaseStream.Position);
  257. //entry contains possible more than one string so I am scanning for null
  258. while (br.BaseStream.Position + 1 < stop)
  259. {
  260. byte b = br.ReadByte();
  261. if (b == 0) files[fileID].sPositions[key].Add((uint)br.BaseStream.Position);
  262. }
  263. }
  264. }
  265. /// <summary>
  266. /// TODO: make this work with more than one file.
  267. /// </summary>
  268. /// <param name="br"></param>
  269. /// <param name="spos"></param>
  270. /// <param name="key"></param>
  271. /// <param name="pad"></param>
  272. private void mngrp_get_string_offsets(BinaryReader br, FileID fileID, uint key, bool pad = false)
  273. {
  274. Loc fpos = files[fileID].subPositions[(int)key];
  275. uint[] fPaddings = pad ? mngrp_read_padding(br, fpos) : (new uint[] { 1 });
  276. files[fileID].sPositions.Add(key, new List<uint>());
  277. for (uint p = 0; p < fPaddings.Length; p++)
  278. {
  279. if (fPaddings[p] <= 0) continue;
  280. uint fpad = pad ? fPaddings[p] + fpos.seek : fpos.seek;
  281. br.BaseStream.Seek(fpad, SeekOrigin.Begin);
  282. if (br.BaseStream.Position + 4 < br.BaseStream.Length)
  283. {
  284. int count = br.ReadUInt16();
  285. for (int i = 0; i < count && br.BaseStream.Position + 2 < br.BaseStream.Length; i++)
  286. {
  287. uint c = br.ReadUInt16();
  288. if (c < br.BaseStream.Length && c != 0)
  289. {
  290. c += fpad;
  291. files[fileID].sPositions[key].Add(c);
  292. }
  293. }
  294. }
  295. }
  296. }
  297. private void mngrp_GetFileLocations()
  298. {
  299. FileID fileID = FileID.MNGRP;
  300. using (MemoryStream ms = new MemoryStream(aw.GetBinaryFile(
  301. aw.GetListOfFiles().First(x => x.IndexOf(filenames[(int)FileID.MNGRP_MAP], StringComparison.OrdinalIgnoreCase) >= 0))))
  302. using (BinaryReader br = new BinaryReader(ms))
  303. {
  304. while (ms.Position < ms.Length)
  305. {
  306. Loc loc = new Loc() { seek = br.ReadUInt32(), length = br.ReadUInt32() };
  307. if (loc.seek != 0xFFFFFFFF && loc.length != 0x00000000)
  308. {
  309. loc.seek--;
  310. files[fileID].subPositions.Add(loc);
  311. }
  312. }
  313. }
  314. }
  315. private void kernel_GetFileLocations(BinaryReader br)
  316. {
  317. FileID fileID = FileID.KERNEL;
  318. uint count = br.ReadUInt32();
  319. while (count-- > 0)
  320. {
  321. Loc l = new Loc { seek = br.ReadUInt32() };
  322. if (count <= 0) l.length = (uint)br.BaseStream.Length - l.seek;
  323. else
  324. {
  325. l.length = br.ReadUInt32() - l.seek;
  326. br.BaseStream.Seek(-4, SeekOrigin.Current);
  327. }
  328. files[fileID].subPositions.Add(l);
  329. }
  330. }
  331. private void mngrp_init()
  332. {
  333. FileID fileID = FileID.MNGRP;
  334. files[fileID] = new Stringfile(new Dictionary<uint, List<uint>>(118), new List<Loc>(118));
  335. mngrp_GetFileLocations();
  336. using (MemoryStream ms = new MemoryStream(aw.GetBinaryFile(
  337. aw.GetListOfFiles().First(x => x.IndexOf(filenames[(int)FileID.MNGRP], StringComparison.OrdinalIgnoreCase) >= 0))))
  338. using (BinaryReader br = new BinaryReader(ms))
  339. {
  340. //string contain padding values at start of file
  341. //then location data before strings
  342. StringsPadLoc = new uint[] { (uint)SectionID.tkmnmes1, (uint)SectionID.tkmnmes2, (uint)SectionID.tkmnmes3 };
  343. //only location data before strings
  344. StringsLoc = new uint[] { 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
  345. 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87, 88, 116};
  346. //complexstr has locations in first file,
  347. //and they have 8 bytes of stuff at the start of each entry, 6 bytes UNK and ushort length?
  348. //also can have multiple null ending strings per entry.
  349. ComplexStr = new Dictionary<uint, List<uint>> { { 74, new List<uint> { 75, 76, 77, 78, 79, 80 } } };
  350. //these files come in pairs. the bin has string offsets and 6 bytes of other data
  351. //msg is where the strings are.
  352. BinMSG = new Dictionary<uint, uint>
  353. {{106,111},{107,112},{108,113},{109,114},{110,115}};
  354. for (uint key = 0; key < files[fileID].subPositions.Count; key++)
  355. {
  356. Loc fpos = files[fileID].subPositions[(int)key];
  357. bool pad = (Array.IndexOf(StringsPadLoc, key) >= 0);
  358. if (pad || Array.IndexOf(StringsLoc, key) >= 0)
  359. mngrp_get_string_offsets(br, fileID, key, pad);
  360. else if (BinMSG.ContainsKey(key))
  361. {
  362. mngrp_get_string_BinMSG(br, fileID, key, files[fileID].subPositions[(int)BinMSG[key]].seek, 1, 6);
  363. }
  364. else if (ComplexStr.ContainsKey(key))
  365. {
  366. Mngrp_get_string_ComplexStr(br, fileID, key, ComplexStr[key]);
  367. }
  368. }
  369. }
  370. }
  371. private uint[] mngrp_read_padding(BinaryReader br, Loc fpos, int type = 0)
  372. {
  373. uint[] fPaddings = null;
  374. br.BaseStream.Seek(fpos.seek, SeekOrigin.Begin);
  375. uint size = type == 0 ? br.ReadUInt16() : br.ReadUInt32();
  376. fPaddings = new uint[type == 0 ? size : size * type * 2];
  377. for (int i = 0; i < fPaddings.Length; i += 1 + type)
  378. {
  379. fPaddings[i] = br.ReadUInt16();
  380. if (type == 0 && fPaddings[i] + fpos.seek >= fpos.max)
  381. fPaddings[i] = 0;
  382. //if (fPaddings[i] != 0)
  383. // fPaddings[i] += fpos.seek;
  384. for (int j = 1; j < type + 1; j++)
  385. {
  386. fPaddings[i + j] = br.ReadUInt16();
  387. }
  388. }
  389. return fPaddings;
  390. }
  391. public FF8String Read(FileID fileID, uint pos)
  392. {
  393. //switching archive make sure we are closed before opening another.
  394. if (aw != null || aw.GetPath() != ArchiveString || last != fileID)
  395. Close();
  396. if (!opened)
  397. Open(fileID);
  398. return Read(localbr, fileID, pos);
  399. }
  400. public FF8String Read(BinaryReader br, FileID fid, uint pos)
  401. {
  402. if (pos == 0)
  403. return new FF8String("");
  404. if (pos < br.BaseStream.Length)
  405. using (MemoryStream os = new MemoryStream(50))
  406. {
  407. br.BaseStream.Seek(pos, SeekOrigin.Begin);
  408. int c = 0;
  409. byte b = 0;
  410. do
  411. {
  412. if (br.BaseStream.Position > br.BaseStream.Length) break;
  413. //sometimes strings start with 00 or 01. But there is another 00 at the end.
  414. //I think it's for SeeD test like 1 is right and 0 is wrong. for now i skip them.
  415. b = br.ReadByte();
  416. if (b != 0 && b != 1)
  417. {
  418. os.WriteByte(b);
  419. }
  420. c++;
  421. }
  422. while (b != 0 || c == 0);
  423. if (os.Length > 0)
  424. return os.ToArray();
  425. }
  426. return null;
  427. }
  428. private void simple_init(FileID fileID)
  429. {
  430. string[] list = aw.GetListOfFiles();
  431. string index = list.First(x => x.IndexOf(filenames[(int)fileID], StringComparison.OrdinalIgnoreCase) >= 0);
  432. using (MemoryStream ms = new MemoryStream(aw.GetBinaryFile(index)))
  433. using (BinaryReader br = new BinaryReader(ms))
  434. {
  435. if (!files.ContainsKey(fileID))
  436. files[fileID] = new Stringfile(new Dictionary<uint, List<uint>>(1), new List<Loc>(1) { new Loc { seek = 0, length = uint.MaxValue } });
  437. mngrp_get_string_offsets(br, fileID, 0);
  438. }
  439. }
  440. public FF8String GetName(Characters c, Saves.Data d = null) => GetName((Faces.ID)c, d);
  441. public FF8String GetName(GFs gf, Saves.Data d = null) => GetName((Faces.ID)((int)gf+16), d);
  442. public FF8String GetName(Faces.ID id, Saves.Data d = null )
  443. {
  444. if (d == null)
  445. d = Memory.State;
  446. switch (id)
  447. {
  448. case Faces.ID.Squall_Leonhart:
  449. return d.Squallsname?? Read(FileID.MNGRP,2,92);
  450. case Faces.ID.Rinoa_Heartilly:
  451. return d.Rinoasname ?? Read(FileID.MNGRP, 2, 93);
  452. case Faces.ID.Angelo:
  453. return d.Angelosname ?? Read(FileID.MNGRP, 2, 94);
  454. case Faces.ID.Boko:
  455. return d.Bokosname ?? Read(FileID.MNGRP, 2, 135);
  456. case Faces.ID.Zell_Dincht:
  457. case Faces.ID.Irvine_Kinneas:
  458. case Faces.ID.Quistis_Trepe:
  459. case Faces.ID.Selphie_Tilmitt:
  460. case Faces.ID.Seifer_Almasy:
  461. case Faces.ID.Edea_Kramer:
  462. case Faces.ID.Laguna_Loire:
  463. case Faces.ID.Kiros_Seagill:
  464. case Faces.ID.Ward_Zabac:
  465. return Read(FileID.KERNEL, 6, (int)id);
  466. case Faces.ID.Quezacotl:
  467. case Faces.ID.Shiva:
  468. case Faces.ID.Ifrit:
  469. case Faces.ID.Siren:
  470. case Faces.ID.Brothers:
  471. case Faces.ID.Diablos:
  472. case Faces.ID.Carbuncle:
  473. case Faces.ID.Leviathan:
  474. case Faces.ID.Pandemona:
  475. case Faces.ID.Cerberus:
  476. case Faces.ID.Alexander:
  477. case Faces.ID.Doomtrain:
  478. case Faces.ID.Bahamut:
  479. case Faces.ID.Cactuar:
  480. case Faces.ID.Tonberry:
  481. case Faces.ID.Eden:
  482. return d.GFs[(GFs)((int)id - 16)].Name ?? Read(FileID.MNGRP, 2, 95-16 + (int)id);
  483. case Faces.ID.Griever:
  484. return d.Grieversname?? Read(FileID.MNGRP, 2, 135);
  485. case Faces.ID.MiniMog:
  486. return Read(FileID.KERNEL, 0, 72); // also in KERNEL, 12, 36
  487. default:
  488. return new FF8String();
  489. }
  490. }
  491. #endregion Methods
  492. //private byte[] Read(FileID fid, uint pos)
  493. //{
  494. // try
  495. // {
  496. // Open(fid);
  497. // return Read(localbr, fid, pos);
  498. // }
  499. // finally
  500. // {
  501. // Close();
  502. // }
  503. //}
  504. //private void readfile()
  505. //{
  506. // //text is prescrabbled and is ready to draw to screen using font renderer
  507. // //based on what I see here some parts of menu ignore \n and some will not //example when
  508. // you highlight a item to refine it will only show only on one line up above. // 1 will
  509. // refine into 20 Thundaras //and when you select it the whole string will show. // Coral
  510. // Fragment: // 1 will refine // into 20 Thundaras
  511. // //m000.msg = 104 strings //example = Coral Fragment:\n1 will refine \ninto 20 Thundaras\0
  512. // //I think this one is any item that turns into magic //___ Mag-RF items? except for
  513. // upgrade abilities
  514. // //m001.msg = 145 strings //same format differnt items //example = Tent:\n4 will refine
  515. // into \n1 Mega-Potion\0 //I think this one is any item that turns into another item //___
  516. // Med-RF items? except for upgrade abilities //guessing Ammo-RF is in here too.
  517. // //m002.msg = 10 strings //same format differnt items //example = Fire:\n5 will refine
  518. // \ninto 1 Fira\0 //this one is magic tha turns into higher level magic //first 4 are Mid
  519. // Mag-RF //last 6 are High Mag-RF
  520. // //m003.msg = 12 strings //same format differnt items //example = Elixer:\n10 will refine
  521. // \ninto 1 Megalixir\0 //this one is Med items tha turns into higher level Med items //all
  522. // 12 are Med LV Up
  523. // //m004.msg = 110 strings //same format differnt items //example = Geezard Card:\n1 will
  524. // refine \ninto 5 Screws\0 //this one is converts cards into items //all 110 are Card Mod
  525. // //mwepon.msg = 34 strings //all strings are " " or " " kinda a odd file.
  526. // //pet_exp.msg = 18 strings //format: ability name\0description\0 //{0x0340} = Angelo's
  527. // name //example: {0x0340} Rush\0 Damage one enemy\0 //list of Angelo's attack names and descriptions
  528. // //namedic.bin 32 strings
  529. // //Seems to be location names.
  530. // //start of file
  531. // // UIint16 Count
  532. // // UIint16[Count]Location
  533. // //at each location
  534. // // Byte[Count][Bytes to null]
  535. //}
  536. }
  537. }