SaveManager.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using UnityEngine;
  2. using System;
  3. using System.Linq;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using System.IO;
  7. using Util;
  8. namespace CM3D2.MultipleMaids.Plugin
  9. {
  10. public partial class MultipleMaids
  11. {
  12. internal static readonly byte[] pngEnd = Encoding.ASCII.GetBytes("IEND");
  13. internal static readonly int[] border = { -1, 0, 0, 0, 0 };
  14. internal static readonly byte[] kankyoHeader = Encoding.ASCII.GetBytes("KANKYO");
  15. internal const string sceneDirectoryName = "< Scene Root >";
  16. internal const string kankyoDirectoryName = "< Kankyou Root >";
  17. internal readonly string saveScenePath = Path.Combine(Path.GetFullPath(".\\"), "Mod\\MultipleMaidsScene");
  18. private static string kankyoScenePath = Path.Combine(Path.GetFullPath(".\\"), "Mod\\MultipleMaidsKankyou");
  19. private static string sceneData;
  20. private static List<ScenePng> scenes = new List<ScenePng>(50);
  21. private GUIStyle selectedButtonStyle;
  22. private GUIContent infoString;
  23. private string[] directoryList;
  24. private Texture2D frame;
  25. private Texture2D infoHighlight;
  26. private Rect sceneManagerRect;
  27. private Rect sceneModalRect;
  28. private Rect resizeManagerRect;
  29. private Vector2 sceneManagerScrollPos = Vector2.zero;
  30. private Vector2 dirListScrollPos = Vector2.zero;
  31. private bool sceneManagerInitialize = false;
  32. private bool loadSceneFlag = false;
  33. private bool overwriteFlag = false;
  34. private bool createSceneFlag = false;
  35. private bool manageSceneFlag = false;
  36. private bool kankyoModeFlag = false;
  37. private bool kankyoToggle = false;
  38. private bool kankyoScene = false;
  39. private bool createDirectoryFlag = false;
  40. private bool deleteDirectoryFlag = false;
  41. private bool deleteSceneFlag = false;
  42. private bool resizeManager = false;
  43. private int selectedScene = 0;
  44. private int selectedDirectory = 0;
  45. private string currentDirectory = sceneDirectoryName;
  46. private string textFieldValue = "";
  47. public void InitializeSceneManager()
  48. {
  49. frame = MakeTex(2, 2, Color.white);
  50. infoHighlight = MakeTex(2, 2, new Color(0f, 0f, 0f, 0.8f));
  51. selectedButtonStyle = new GUIStyle("button");
  52. selectedButtonStyle.normal.background = MakeTex(1, 1, new Color(0.5f, 0.5f, 0.5f, 0.4f));
  53. selectedButtonStyle.normal.textColor = Color.white;
  54. currentDirectory = kankyoModeFlag ? kankyoDirectoryName : sceneDirectoryName;
  55. GetSceneDirectories();
  56. GetScenes(currentDirectory);
  57. sceneManagerInitialize = true;
  58. }
  59. private void RefreshSceneManager()
  60. {
  61. SwitchDirectory(currentDirectory);
  62. GetSceneDirectories();
  63. GetScenes(currentDirectory);
  64. }
  65. private void SwitchDirectory(string target)
  66. {
  67. string root = kankyoModeFlag ? kankyoScenePath : saveScenePath;
  68. string path = target.Equals(sceneDirectoryName) || target.Equals(kankyoDirectoryName)
  69. ? ""
  70. : target;
  71. string targetDirectory = Path.Combine(root, path);
  72. if (!Directory.Exists(targetDirectory))
  73. {
  74. currentDirectory = kankyoModeFlag ? kankyoDirectoryName : sceneDirectoryName;
  75. selectedDirectory = 0;
  76. GetSceneDirectories();
  77. }
  78. else
  79. {
  80. selectedDirectory = Array.FindIndex(directoryList, d => d.Equals(target, StringComparison.OrdinalIgnoreCase));
  81. selectedDirectory = selectedDirectory == -1 ? 0 : selectedDirectory;
  82. }
  83. if (target == currentDirectory) return;
  84. currentDirectory = target;
  85. GetScenes(target);
  86. }
  87. private void GetSceneDirectories()
  88. {
  89. if (!Directory.Exists(saveScenePath))
  90. {
  91. Directory.CreateDirectory(saveScenePath);
  92. }
  93. if (!Directory.Exists(kankyoScenePath))
  94. {
  95. Directory.CreateDirectory(kankyoScenePath);
  96. }
  97. string root = kankyoModeFlag ? kankyoScenePath : saveScenePath;
  98. DirectoryInfo[] directoryInfo = new DirectoryInfo(root).GetDirectories();
  99. directoryList = new string[directoryInfo.Length + 1];
  100. directoryList[0] = kankyoModeFlag ? kankyoDirectoryName : sceneDirectoryName;
  101. for (int i = 0; i < directoryInfo.Length; i++)
  102. {
  103. directoryList[i + 1] = directoryInfo[i].Name;
  104. }
  105. }
  106. private void GetScenes(string target)
  107. {
  108. string root = kankyoModeFlag ? kankyoScenePath : saveScenePath;
  109. scenes.Clear();
  110. if (target.Equals(sceneDirectoryName) || target.Equals(kankyoDirectoryName))
  111. {
  112. target = "";
  113. }
  114. string workingPath = Path.Combine(root, target);
  115. DirectoryInfo info = new DirectoryInfo(workingPath);
  116. foreach (var scene in info.GetFiles("*.png"))
  117. {
  118. Texture2D screenshot = new Texture2D(2, 2, TextureFormat.ARGB32, false);
  119. screenshot.LoadImage(File.ReadAllBytes(scene.FullName));
  120. scenes.Add(new ScenePng(scene, screenshot));
  121. }
  122. selectedScene = scenes.Count == 0 ? 0 : scenes.Count - 1;
  123. scenes.Sort((a, b) => a.info.LastWriteTime.CompareTo(b.info.LastWriteTime));
  124. sceneManagerScrollPos.y = 0;
  125. }
  126. private void CreateDirectory(string directoryName)
  127. {
  128. string root = kankyoModeFlag ? kankyoScenePath : saveScenePath;
  129. directoryName = string.Join("", directoryName.Split(Path.GetInvalidFileNameChars()));
  130. string newDirectory = Path.Combine(root, directoryName);
  131. if (!Directory.Exists(newDirectory))
  132. {
  133. Directory.CreateDirectory(newDirectory);
  134. }
  135. GetSceneDirectories();
  136. SwitchDirectory(directoryName);
  137. }
  138. private void DeleteDirectory()
  139. {
  140. string root = kankyoModeFlag ? kankyoScenePath : saveScenePath;
  141. string directory = Path.Combine(root, directoryList[selectedDirectory]);
  142. if (Directory.Exists(directory))
  143. {
  144. DirectoryInfo dirInfo = new DirectoryInfo(directory);
  145. foreach (var finfo in dirInfo.GetFiles())
  146. {
  147. finfo.Delete();
  148. }
  149. dirInfo.Delete();
  150. currentDirectory = sceneDirectoryName;
  151. }
  152. RefreshSceneManager();
  153. }
  154. private void DeleteScene()
  155. {
  156. string file = scenes[selectedScene].info.FullName;
  157. if (File.Exists(file))
  158. {
  159. scenes[selectedScene].info.Delete();
  160. scenes.RemoveAt(selectedScene);
  161. }
  162. RefreshSceneManager();
  163. }
  164. private void OverwriteScene()
  165. {
  166. string file = scenes[selectedScene].info.FullName;
  167. if (!File.Exists(file))
  168. {
  169. RefreshSceneManager();
  170. }
  171. else
  172. {
  173. File.Delete(file);
  174. scenes.RemoveAt(selectedScene);
  175. sceneManagerScrollPos.y = 0;
  176. }
  177. }
  178. private void SaveScene()
  179. {
  180. string target = currentDirectory;
  181. bool scene = target.Equals(sceneDirectoryName);
  182. bool kankyo = target.Equals(kankyoDirectoryName);
  183. if (scene || kankyo)
  184. target = "";
  185. string saveDirectory;
  186. saveDirectory = kankyoModeFlag
  187. ? Path.Combine(kankyoScenePath, target)
  188. : Path.Combine(saveScenePath, target);
  189. if (!Directory.Exists(saveDirectory))
  190. {
  191. thum_byte_to_base64_ = "";
  192. thum_file_path_ = "";
  193. RefreshSceneManager();
  194. return;
  195. }
  196. string sceneString = SerializeScene();
  197. #region MM screenshot processing stuff
  198. Texture2D screenshot = new Texture2D(1, 1, TextureFormat.ARGB32, false);
  199. screenshot.LoadImage(File.ReadAllBytes(thum_file_path_));
  200. float num2 = screenshot.width / (float)screenshot.height;
  201. Vector2 vector2 = new Vector2(480f, 270f);
  202. int newWidth = screenshot.width;
  203. int newHeight = screenshot.height;
  204. if (vector2.x < (double)screenshot.width && vector2.y < (double)screenshot.height)
  205. {
  206. newWidth = (int)vector2.x;
  207. newHeight = Mathf.RoundToInt(newWidth / num2);
  208. if (vector2.y < (double)newHeight)
  209. {
  210. newHeight = (int)vector2.y;
  211. newWidth = Mathf.RoundToInt(newHeight * num2);
  212. }
  213. }
  214. else if (vector2.x < (double)screenshot.width)
  215. {
  216. newWidth = (int)vector2.x;
  217. newHeight = Mathf.RoundToInt(newWidth / num2);
  218. }
  219. else if (vector2.y < (double)screenshot.height)
  220. {
  221. newHeight = (int)vector2.y;
  222. newWidth = Mathf.RoundToInt(newHeight * num2);
  223. }
  224. TextureScale.Bilinear(screenshot, newWidth, newHeight);
  225. #endregion
  226. thum_byte_to_base64_ = "";
  227. thum_file_path_ = "";
  228. string sceneType = kankyoModeFlag ? "mmkankyou" : "mmsave";
  229. string filePath;
  230. filePath = Path.Combine(saveDirectory, $"{sceneType}{DateTime.Now:yyyyMMddHHmmss}.png");
  231. using (FileStream fileStream = File.Create(filePath))
  232. using (MemoryStream sceneStream = new MemoryStream(Encoding.Unicode.GetBytes(sceneString)))
  233. {
  234. byte[] screenshotBuffer = screenshot.EncodeToPNG();
  235. byte[] sceneBuffer = LZMA.Compress(sceneStream);
  236. fileStream.Write(screenshotBuffer, 0, screenshotBuffer.Length);
  237. if (kankyoModeFlag) fileStream.Write(kankyoHeader, 0, kankyoHeader.Length);
  238. fileStream.Write(sceneBuffer, 0, sceneBuffer.Length);
  239. }
  240. scenes.Add(new ScenePng(new FileInfo(filePath), screenshot));
  241. selectedScene = scenes.Count - 1;
  242. }
  243. private void ReadScene()
  244. {
  245. string filePath = scenes[selectedScene].info.FullName;
  246. sceneData = null;
  247. if (!File.Exists(filePath))
  248. {
  249. RefreshSceneManager();
  250. return;
  251. }
  252. using (FileStream fileStream = File.OpenRead(filePath))
  253. {
  254. long pos = fileStream.Position = fileStream.Length - pngEnd.Length;
  255. byte[] buf = new byte[pngEnd.Length];
  256. while (true)
  257. {
  258. if (pos < 0)
  259. {
  260. Util.Logger.Log(LogLevel.Error, $"Could not read '{Path.GetFileName(filePath)}'");
  261. return;
  262. }
  263. fileStream.Position = pos;
  264. fileStream.Read(buf, 0, pngEnd.Length);
  265. if (BytesEqual(buf, pngEnd)) break;
  266. --pos;
  267. }
  268. fileStream.Position += 4;
  269. byte[] kankyo = new byte[kankyoHeader.Length];
  270. fileStream.Read(kankyo, 0, kankyo.Length);
  271. if (BytesEqual(kankyo, kankyoHeader))
  272. {
  273. kankyoScene = true;
  274. }
  275. else
  276. {
  277. kankyoScene = false;
  278. fileStream.Position -= kankyoHeader.Length;
  279. }
  280. try
  281. {
  282. using (var sceneStream = LZMA.Decompress(fileStream))
  283. {
  284. sceneData = Encoding.Unicode.GetString(sceneStream.ToArray());
  285. }
  286. }
  287. catch (Exception e)
  288. {
  289. Util.Logger.Log(LogLevel.Error, $"Failed to decompress scene data because '{e}'\n");
  290. }
  291. }
  292. }
  293. private static bool BytesEqual(byte[] a, byte[] b)
  294. {
  295. if (a.Length != b.Length) return false;
  296. for (long i = 0; i < a.Length; i++)
  297. {
  298. if (a[i] != b[i]) return false;
  299. }
  300. return true;
  301. }
  302. private class ScenePng
  303. {
  304. public FileInfo info { get; }
  305. public Texture2D screenshot { get; }
  306. public ScenePng(FileInfo info, Texture2D screenshot)
  307. {
  308. this.info = info;
  309. this.screenshot = screenshot;
  310. }
  311. }
  312. }
  313. }