Saves.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Text.RegularExpressions;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. namespace OpenVIII
  9. {
  10. /// <summary>
  11. /// parse data from save game files
  12. /// </summary>
  13. /// <see cref="http://wiki.ffrtt.ru/index.php/FF8/GameSaveFormat#The_save_format"/>
  14. /// <seealso cref="https://github.com/myst6re/hyne"/>
  15. /// <seealso cref="https://github.com/myst6re/hyne/blob/master/SaveData.h"/>
  16. /// <seealso cref="https://cdn.discordapp.com/attachments/552838120895283210/570733614656913408/ff8_save.zip"/>
  17. /// <remarks>
  18. /// antiquechrono was helping. he even wrote a whole class using kaitai. Though I donno if we
  19. /// wanna use kaitai. LordUrQuan helped get info on CD2000 version.
  20. /// </remarks>
  21. public static partial class Saves
  22. {
  23. private const int SteamOffset = 0x184;
  24. /// <summary>
  25. /// Documents\Square Enix\FINAL FANTASY VIII Steam\user_#######
  26. /// </summary>
  27. public static string Steam2013Folder { get; private set; }
  28. /// <summary>
  29. /// FF8DIR\Saves
  30. /// </summary>
  31. public static string CD2000Folder { get; private set; }
  32. /// <summary>
  33. /// Documents\My Games\FINAL FANTASY VIII Remastered\Steam\#################\game_data\user\saves
  34. /// </summary>
  35. public static string Steam2019Folder { get; private set; }
  36. public static Data[,] FileList { get; private set; }
  37. public static void Init()
  38. {
  39. FileList = new Data[2, 30];
  40. CD2000Folder = Path.Combine(Memory.FF8DIR, "Save");
  41. Steam2013Folder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Square Enix", "FINAL FANTASY VIII Steam");
  42. //C:\Users\pcvii\OneDrive\Documents\My Games\FINAL FANTASY VIII Remastered\Steam\76561198027029474\game_data\user\saves
  43. Steam2019Folder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "My Games", "FINAL FANTASY VIII Remastered", "Steam");
  44. if (Directory.Exists(Steam2013Folder))
  45. {
  46. string[] dirs = Directory.GetDirectories(Steam2013Folder);
  47. if (dirs.Length > 0)
  48. {
  49. string[] SteamFolders = Directory.GetDirectories(Steam2013Folder);
  50. if (SteamFolders.Length > 0)
  51. {
  52. Steam2013Folder = SteamFolders[0];
  53. GetFiles(Steam2013Folder, @"slot(\d+)_save(\d+).ff8");
  54. }
  55. }
  56. }
  57. else if (Directory.Exists(CD2000Folder))
  58. {
  59. ProcessFiles(Directory.GetFiles(CD2000Folder, "*", SearchOption.AllDirectories), @"Slot(\d+)[\\/]save(\d+)");
  60. }
  61. else if (Directory.Exists(Steam2019Folder))
  62. {
  63. string[] dirs = Directory.GetDirectories(Steam2019Folder);
  64. if (dirs.Length > 0)
  65. {
  66. string[] SteamFolders = Directory.GetDirectories(Steam2019Folder);
  67. if (SteamFolders.Length > 0)
  68. {
  69. Steam2019Folder = Path.Combine(SteamFolders[0], "game_data", "user", "saves");
  70. GetFiles(Steam2019Folder, @"slot(\d+)_save(\d+).ff8");
  71. }
  72. }
  73. }
  74. }
  75. private static void GetFiles(string dir, string regex) => ProcessFiles(Directory.EnumerateFiles(dir), regex);
  76. private static void ProcessFiles(IEnumerable<string> files, string regex)
  77. {
  78. List<Task> tasks = new List<Task>();
  79. foreach (string file in files)
  80. {
  81. Match match = Regex.Match(file, regex, RegexOptions.IgnoreCase);
  82. if (match.Success && match.Groups.Count > 0)
  83. tasks.Add(Task.Run(() => Read(file, out FileList[int.Parse(match.Groups[1].Value) - 1, int.Parse(match.Groups[2].Value) - 1])));
  84. }
  85. Task.WaitAll(tasks.ToArray());
  86. }
  87. private static void Read(string file, out Data d)
  88. {
  89. Debug.WriteLine("Task={0}, Thread={1}, File={2}",
  90. Task.CurrentId, Thread.CurrentThread.ManagedThreadId, file);
  91. byte[] decmp;
  92. MemoryStream ms = null;
  93. FileStream fs = null;
  94. // fs is disposed by binaryreader.
  95. using (BinaryReader br = new BinaryReader(
  96. fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
  97. {
  98. int size = br.ReadInt32();
  99. byte[] tmp = br.ReadBytes((int)fs.Length - sizeof(uint));
  100. decmp = LZSS.DecompressAllNew(tmp);
  101. fs = null;
  102. }
  103. using (BinaryReader br = new BinaryReader(ms = new MemoryStream(decmp)))
  104. {
  105. ms.Seek(SteamOffset, SeekOrigin.Begin);
  106. d = new Data();
  107. d.Read(br);
  108. ms = null;
  109. }
  110. }
  111. }
  112. }