Constants.cs 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Xml.Linq;
  6. using Newtonsoft.Json;
  7. using MyRoomCustom;
  8. using UnityEngine;
  9. using wf;
  10. namespace COM3D2.MeidoPhotoStudio.Plugin
  11. {
  12. using static MenuFileUtility;
  13. internal static class Constants
  14. {
  15. private static bool beginHandItemInit;
  16. private static bool beginMpnAttachInit;
  17. public const string customPoseDirectory = "Custom Poses";
  18. public const string customHandDirectory = "Hand Presets";
  19. public const string customFaceDirectory = "Face Presets";
  20. public const string sceneDirectory = "Scenes";
  21. public const string kankyoDirectory = "Environments";
  22. public const string configDirectory = "MeidoPhotoStudio";
  23. public const string translationDirectory = "Translations";
  24. public static readonly string customPosePath;
  25. public static readonly string customHandPath;
  26. public static readonly string customFacePath;
  27. public static readonly string scenesPath;
  28. public static readonly string kankyoPath;
  29. public static readonly string configPath;
  30. public static readonly int mainWindowID = 765;
  31. public static readonly int messageWindowID = 961;
  32. public static readonly int sceneManagerWindowID = 876;
  33. public static readonly int sceneManagerModalID = 283;
  34. public static readonly int dropdownWindowID = 777;
  35. public enum Window
  36. {
  37. Call, Pose, Face, BG, BG2, Main, Message, Save, SaveModal, Settings
  38. }
  39. public enum Scene
  40. {
  41. Daily = 3, Edit = 5
  42. }
  43. public static readonly List<string> PoseGroupList = new List<string>();
  44. public static readonly Dictionary<string, List<string>> PoseDict = new Dictionary<string, List<string>>();
  45. public static readonly List<string> CustomPoseGroupList = new List<string>();
  46. public static readonly Dictionary<string, List<string>> CustomPoseDict = new Dictionary<string, List<string>>();
  47. public static readonly List<string> CustomHandGroupList = new List<string>();
  48. public static readonly Dictionary<string, List<string>> CustomHandDict = new Dictionary<string, List<string>>();
  49. public static readonly List<string> FaceGroupList = new List<string>();
  50. public static readonly Dictionary<string, List<string>> FaceDict = new Dictionary<string, List<string>>();
  51. public static readonly List<string> CustomFaceGroupList = new List<string>();
  52. public static readonly Dictionary<string, List<string>> CustomFaceDict = new Dictionary<string, List<string>>();
  53. public static readonly List<string> BGList = new List<string>();
  54. public static readonly List<KeyValuePair<string, string>> MyRoomCustomBGList
  55. = new List<KeyValuePair<string, string>>();
  56. public static readonly List<string> DoguCategories = new List<string>();
  57. public static readonly Dictionary<string, List<string>> DoguDict = new Dictionary<string, List<string>>();
  58. public static readonly List<string> MyRoomPropCategories = new List<string>();
  59. public static readonly Dictionary<string, List<MyRoomItem>> MyRoomPropDict
  60. = new Dictionary<string, List<MyRoomItem>>();
  61. public static readonly Dictionary<string, List<ModItem>> ModPropDict
  62. = new Dictionary<string, List<ModItem>>(StringComparer.InvariantCultureIgnoreCase);
  63. public static readonly List<string> SceneDirectoryList = new List<string>();
  64. public static readonly List<string> KankyoDirectoryList = new List<string>();
  65. public static readonly List<MpnAttachProp> MpnAttachPropList = new List<MpnAttachProp>();
  66. public static int MyRoomCustomBGIndex { get; private set; } = -1;
  67. public static bool HandItemsInitialized { get; private set; }
  68. public static bool MpnAttachInitialized { get; private set; }
  69. public static bool MenuFilesInitialized { get; private set; }
  70. public static event EventHandler<MenuFilesEventArgs> MenuFilesChange;
  71. public static event EventHandler<PresetChangeEventArgs> CustomPoseChange;
  72. public static event EventHandler<PresetChangeEventArgs> CustomHandChange;
  73. public static event EventHandler<PresetChangeEventArgs> CustomFaceChange;
  74. public enum DoguCategory
  75. {
  76. Other, Mob, Desk, HandItem, BGSmall
  77. }
  78. public static readonly Dictionary<DoguCategory, string> customDoguCategories =
  79. new Dictionary<DoguCategory, string>()
  80. {
  81. [DoguCategory.Other] = "other",
  82. [DoguCategory.Mob] = "mob",
  83. [DoguCategory.Desk] = "desk",
  84. [DoguCategory.HandItem] = "handItem",
  85. [DoguCategory.BGSmall] = "bgSmall"
  86. };
  87. static Constants()
  88. {
  89. configPath = Path.Combine(BepInEx.Paths.ConfigPath, configDirectory);
  90. string presetPath = Path.Combine(configPath, "Presets");
  91. customPosePath = Path.Combine(presetPath, customPoseDirectory);
  92. customHandPath = Path.Combine(presetPath, customHandDirectory);
  93. customFacePath = Path.Combine(presetPath, customFaceDirectory);
  94. scenesPath = Path.Combine(configPath, sceneDirectory);
  95. kankyoPath = Path.Combine(configPath, kankyoDirectory);
  96. string[] directories = new[] {
  97. customPosePath, customHandPath, scenesPath, kankyoPath, configPath, customFacePath
  98. };
  99. foreach (string directory in directories)
  100. {
  101. if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
  102. }
  103. }
  104. public static void Initialize()
  105. {
  106. InitializeScenes();
  107. InitializePoses();
  108. InitializeHandPresets();
  109. InitializeFaceBlends();
  110. InitializeBGs();
  111. InitializeDogu();
  112. InitializeMyRoomProps();
  113. InitializeMpnAttachProps();
  114. }
  115. public static void AddFacePreset(Dictionary<string, float> faceData, string filename, string directory)
  116. {
  117. filename = Utility.SanitizePathPortion(filename);
  118. directory = Utility.SanitizePathPortion(directory);
  119. if (string.IsNullOrEmpty(filename)) filename = "face_preset";
  120. if (directory.Equals(customFaceDirectory, StringComparison.InvariantCultureIgnoreCase))
  121. {
  122. directory = string.Empty;
  123. }
  124. directory = Path.Combine(customFacePath, directory);
  125. if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
  126. string fullPath = Path.Combine(directory, filename);
  127. if (File.Exists($"{fullPath}.xml")) fullPath += $"_{DateTime.Now:yyyyMMddHHmmss}";
  128. fullPath = Path.GetFullPath($"{fullPath}.xml");
  129. if (!fullPath.StartsWith(customFacePath))
  130. {
  131. Utility.LogError($"Could not save face preset! Path is invalid: '{fullPath}'");
  132. return;
  133. }
  134. XElement rootElement = new XElement("FaceData");
  135. foreach (KeyValuePair<string, float> kvp in faceData)
  136. {
  137. rootElement.Add(new XElement("elm", kvp.Value.ToString("G9"), new XAttribute("name", kvp.Key)));
  138. }
  139. XDocument fullDocument = new XDocument(
  140. new XDeclaration("1.0", "utf-8", "true"),
  141. new XComment("MeidoPhotoStudio Face Preset"),
  142. rootElement
  143. );
  144. fullDocument.Save(fullPath);
  145. FileInfo fileInfo = new FileInfo(fullPath);
  146. string category = fileInfo.Directory.Name;
  147. string faceGroup = CustomFaceGroupList.Find(
  148. group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase)
  149. );
  150. if (string.IsNullOrEmpty(faceGroup))
  151. {
  152. CustomFaceGroupList.Add(category);
  153. CustomFaceDict[category] = new List<string>();
  154. }
  155. else category = faceGroup;
  156. CustomFaceDict[category].Add(fullPath);
  157. CustomFaceDict[category].Sort();
  158. CustomFaceChange?.Invoke(null, new PresetChangeEventArgs(fullPath, category));
  159. }
  160. public static void AddPose(byte[] anmBinary, string filename, string directory)
  161. {
  162. // TODO: Consider writing a file system monitor
  163. filename = Utility.SanitizePathPortion(filename);
  164. directory = Utility.SanitizePathPortion(directory);
  165. if (string.IsNullOrEmpty(filename)) filename = "custom_pose";
  166. if (directory.Equals(customPoseDirectory, StringComparison.InvariantCultureIgnoreCase))
  167. {
  168. directory = string.Empty;
  169. }
  170. directory = Path.Combine(customPosePath, directory);
  171. if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
  172. string fullPath = Path.Combine(directory, filename);
  173. if (File.Exists($"{fullPath}.anm")) fullPath += $"_{DateTime.Now:yyyyMMddHHmmss}";
  174. fullPath = Path.GetFullPath($"{fullPath}.anm");
  175. if (!fullPath.StartsWith(customPosePath))
  176. {
  177. Utility.LogError($"Could not save pose! Path is invalid: '{fullPath}'");
  178. return;
  179. }
  180. File.WriteAllBytes(fullPath, anmBinary);
  181. FileInfo fileInfo = new FileInfo(fullPath);
  182. string category = fileInfo.Directory.Name;
  183. string poseGroup = CustomPoseGroupList.Find(
  184. group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase)
  185. );
  186. if (string.IsNullOrEmpty(poseGroup))
  187. {
  188. CustomPoseGroupList.Add(category);
  189. CustomPoseDict[category] = new List<string>();
  190. }
  191. else category = poseGroup;
  192. CustomPoseDict[category].Add(fullPath);
  193. CustomPoseDict[category].Sort();
  194. CustomPoseChange?.Invoke(null, new PresetChangeEventArgs(fullPath, category));
  195. }
  196. public static void AddHand(byte[] handBinary, bool right, string filename, string directory)
  197. {
  198. filename = Utility.SanitizePathPortion(filename);
  199. directory = Utility.SanitizePathPortion(directory);
  200. if (string.IsNullOrEmpty(filename)) filename = "custom_hand";
  201. if (directory.Equals(customHandDirectory, StringComparison.InvariantCultureIgnoreCase))
  202. {
  203. directory = string.Empty;
  204. }
  205. directory = Path.Combine(customHandPath, directory);
  206. if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
  207. string fullPath = Path.Combine(directory, filename);
  208. if (File.Exists($"{fullPath}.xml")) fullPath += $"_{DateTime.Now:yyyyMMddHHmmss}";
  209. fullPath = Path.GetFullPath($"{fullPath}.xml");
  210. if (!fullPath.StartsWith(customHandPath))
  211. {
  212. Utility.LogError($"Could not save hand! Path is invalid: '{fullPath}'");
  213. return;
  214. }
  215. int gameVersion = Misc.GAME_VERSION; // get game version from user's Assembly-CSharp
  216. XDocument finalXml = new XDocument(new XDeclaration("1.0", "utf-8", "true"),
  217. new XComment("CM3D2 FingerData"),
  218. new XElement("FingerData",
  219. new XElement("GameVersion", gameVersion),
  220. new XElement("RightData", right),
  221. new XElement("BinaryData", Convert.ToBase64String(handBinary))
  222. )
  223. );
  224. finalXml.Save(fullPath);
  225. FileInfo fileInfo = new FileInfo(fullPath);
  226. string category = fileInfo.Directory.Name;
  227. string handGroup = CustomHandGroupList.Find(
  228. group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase)
  229. );
  230. if (string.IsNullOrEmpty(handGroup))
  231. {
  232. CustomHandGroupList.Add(category);
  233. CustomHandDict[category] = new List<string>();
  234. }
  235. else category = handGroup;
  236. CustomHandDict[category].Add(fullPath);
  237. CustomHandDict[category].Sort();
  238. CustomHandChange?.Invoke(null, new PresetChangeEventArgs(fullPath, category));
  239. }
  240. public static void InitializeScenes()
  241. {
  242. SceneDirectoryList.Clear();
  243. KankyoDirectoryList.Clear();
  244. SceneDirectoryList.Add(sceneDirectory);
  245. foreach (string directory in Directory.GetDirectories(scenesPath))
  246. {
  247. SceneDirectoryList.Add(new DirectoryInfo(directory).Name);
  248. }
  249. KankyoDirectoryList.Add(kankyoDirectory);
  250. foreach (string directory in Directory.GetDirectories(kankyoPath))
  251. {
  252. KankyoDirectoryList.Add(new DirectoryInfo(directory).Name);
  253. }
  254. }
  255. public static void InitializePoses()
  256. {
  257. // Load Poses
  258. string poseListPath = Path.Combine(configPath, "Database\\mm_pose_list.json");
  259. try
  260. {
  261. string poseListJson = File.ReadAllText(poseListPath);
  262. foreach (SerializePoseList poseList in JsonConvert.DeserializeObject<List<SerializePoseList>>(poseListJson))
  263. {
  264. PoseDict[poseList.UIName] = poseList.PoseList;
  265. PoseGroupList.Add(poseList.UIName);
  266. }
  267. }
  268. catch (IOException e)
  269. {
  270. Utility.LogError($"Could not open pose database because {e.Message}");
  271. Utility.LogMessage("Pose list will be serverely limited.");
  272. }
  273. catch (Exception e)
  274. {
  275. Utility.LogError($"Could not parse pose database because {e.Message}");
  276. }
  277. // Get Other poses that'll go into Normal 2 and Ero 2
  278. string[] com3d2MotionList = GameUty.FileSystem.GetList("motion", AFileSystemBase.ListType.AllFile);
  279. if (com3d2MotionList?.Length > 0)
  280. {
  281. HashSet<string> poseSet = new HashSet<string>();
  282. foreach (List<string> poses in PoseDict.Values) poseSet.UnionWith(poses);
  283. string[] newCategories = new[] { "normal", "normal2", "ero2" };
  284. foreach (string category in newCategories)
  285. {
  286. if (!PoseDict.ContainsKey(category)) PoseDict[category] = new List<string>();
  287. }
  288. foreach (string path in com3d2MotionList)
  289. {
  290. if (Path.GetExtension(path) == ".anm")
  291. {
  292. string file = Path.GetFileNameWithoutExtension(path);
  293. if (!poseSet.Contains(file))
  294. {
  295. if (file.StartsWith("edit_")) PoseDict["normal"].Add(file);
  296. else if (file != "dance_cm3d2_001_zoukin" && file != "dance_cm3d2_001_mop"
  297. && file != "aruki_1_idougo_f" && file != "sleep2" && file != "stand_akire2"
  298. && !file.EndsWith("_3_") && !file.EndsWith("_5_") && !file.StartsWith("vr_")
  299. && !file.StartsWith("dance_mc") && !file.Contains("_kubi_") && !file.Contains("a01_")
  300. && !file.Contains("b01_") && !file.Contains("b02_") && !file.EndsWith("_m2")
  301. && !file.EndsWith("_m2_once_") && !file.StartsWith("h_") && !file.StartsWith("event_")
  302. && !file.StartsWith("man_") && !file.EndsWith("_m") && !file.Contains("_m_")
  303. && !file.Contains("_man_")
  304. )
  305. {
  306. if (path.Contains(@"\sex\")) PoseDict["ero2"].Add(file);
  307. else PoseDict["normal2"].Add(file);
  308. }
  309. }
  310. }
  311. }
  312. foreach (string category in newCategories)
  313. {
  314. if (PoseDict[category].Count > 0) PoseGroupList.Add(category);
  315. else PoseDict.Remove(category);
  316. }
  317. }
  318. InitializeCustomPoses();
  319. }
  320. public static void InitializeCustomPoses()
  321. {
  322. void GetPoses(string directory)
  323. {
  324. List<string> poseList = Directory.GetFiles(directory)
  325. .Where(file => Path.GetExtension(file) == ".anm").ToList();
  326. if (poseList.Count > 0)
  327. {
  328. string poseGroupName = new DirectoryInfo(directory).Name;
  329. if (poseGroupName != customPoseDirectory) CustomPoseGroupList.Add(poseGroupName);
  330. CustomPoseDict[poseGroupName] = poseList;
  331. }
  332. }
  333. CustomPoseGroupList.Clear();
  334. CustomPoseDict.Clear();
  335. CustomPoseGroupList.Add(customPoseDirectory);
  336. CustomPoseDict[customPoseDirectory] = new List<string>();
  337. GetPoses(customPosePath);
  338. foreach (string directory in Directory.GetDirectories(customPosePath)) GetPoses(directory);
  339. CustomPoseChange?.Invoke(null, PresetChangeEventArgs.Empty);
  340. }
  341. public static void InitializeHandPresets()
  342. {
  343. void GetPresets(string directory)
  344. {
  345. IEnumerable<string> presetList = Directory.GetFiles(directory)
  346. .Where(file => Path.GetExtension(file) == ".xml");
  347. if (presetList.Any())
  348. {
  349. string presetCategory = new DirectoryInfo(directory).Name;
  350. if (presetCategory != customHandDirectory) CustomHandGroupList.Add(presetCategory);
  351. CustomHandDict[presetCategory] = new List<string>(presetList);
  352. }
  353. }
  354. CustomHandGroupList.Clear();
  355. CustomHandDict.Clear();
  356. CustomHandGroupList.Add(customHandDirectory);
  357. CustomHandDict[customHandDirectory] = new List<string>();
  358. GetPresets(customHandPath);
  359. foreach (string directory in Directory.GetDirectories(customHandPath)) GetPresets(directory);
  360. CustomHandChange?.Invoke(null, PresetChangeEventArgs.Empty);
  361. }
  362. public static void InitializeFaceBlends()
  363. {
  364. PhotoFaceData.Create();
  365. FaceGroupList.AddRange(PhotoFaceData.popup_category_list.Select(kvp => kvp.Key));
  366. foreach (KeyValuePair<string, List<PhotoFaceData>> kvp in PhotoFaceData.category_list)
  367. {
  368. FaceDict[kvp.Key] = kvp.Value.Select(data => data.setting_name).ToList();
  369. }
  370. InitializeCustomFaceBlends();
  371. }
  372. public static void InitializeCustomFaceBlends()
  373. {
  374. void GetFacePresets(string directory)
  375. {
  376. List<string> presetList = Directory.GetFiles(directory)
  377. .Where(file => Path.GetExtension(file) == ".xml").ToList();
  378. if (presetList.Count > 0)
  379. {
  380. string faceGroupName = new DirectoryInfo(directory).Name;
  381. if (faceGroupName != customFaceDirectory) CustomFaceGroupList.Add(faceGroupName);
  382. CustomFaceDict[faceGroupName] = presetList;
  383. }
  384. }
  385. CustomFaceGroupList.Clear();
  386. CustomFaceDict.Clear();
  387. CustomFaceGroupList.Add(customFaceDirectory);
  388. CustomFaceDict[customFaceDirectory] = new List<string>();
  389. GetFacePresets(customFacePath);
  390. foreach (string directory in Directory.GetDirectories(customFacePath)) GetFacePresets(directory);
  391. CustomFaceChange?.Invoke(null, PresetChangeEventArgs.Empty);
  392. }
  393. public static void InitializeBGs()
  394. {
  395. // Load BGs
  396. PhotoBGData.Create();
  397. // COM3D2 BGs
  398. foreach (PhotoBGData bgData in PhotoBGData.data)
  399. {
  400. if (!string.IsNullOrEmpty(bgData.create_prefab_name))
  401. {
  402. string bg = bgData.create_prefab_name;
  403. BGList.Add(bg);
  404. }
  405. }
  406. // CM3D2 BGs
  407. if (GameUty.IsEnabledCompatibilityMode)
  408. {
  409. using (CsvParser csvParser = OpenCsvParser("phot_bg_list.nei", GameUty.FileSystemOld))
  410. {
  411. for (int cell_y = 1; cell_y < csvParser.max_cell_y; cell_y++)
  412. {
  413. if (csvParser.IsCellToExistData(3, cell_y))
  414. {
  415. string bg = csvParser.GetCellAsString(3, cell_y);
  416. BGList.Add(bg);
  417. }
  418. }
  419. }
  420. }
  421. Dictionary<string, string> saveDataDict = CreativeRoomManager.GetSaveDataDic();
  422. if (saveDataDict != null)
  423. {
  424. MyRoomCustomBGIndex = BGList.Count;
  425. MyRoomCustomBGList.AddRange(saveDataDict);
  426. }
  427. }
  428. public static void InitializeDogu()
  429. {
  430. foreach (string customCategory in customDoguCategories.Values)
  431. {
  432. DoguDict[customCategory] = new List<string>();
  433. }
  434. InitializeDeskItems();
  435. InitializePhotoBGItems();
  436. InitializeOtherDogu();
  437. InitializeHandItems();
  438. foreach (string category in PhotoBGObjectData.popup_category_list.Select(kvp => kvp.Key))
  439. {
  440. if (category == "マイオブジェクト") continue;
  441. DoguCategories.Add(category);
  442. }
  443. foreach (DoguCategory category in Enum.GetValues(typeof(DoguCategory)))
  444. {
  445. DoguCategories.Add(customDoguCategories[category]);
  446. }
  447. }
  448. private static void InitializeOtherDogu()
  449. {
  450. DoguDict[customDoguCategories[DoguCategory.BGSmall]] = BGList;
  451. DoguDict[customDoguCategories[DoguCategory.Mob]].AddRange(new[] {
  452. "Mob_Man_Stand001", "Mob_Man_Stand002", "Mob_Man_Stand003", "Mob_Man_Sit001", "Mob_Man_Sit002",
  453. "Mob_Man_Sit003", "Mob_Girl_Stand001", "Mob_Girl_Stand002", "Mob_Girl_Stand003", "Mob_Girl_Sit001",
  454. "Mob_Girl_Sit002", "Mob_Girl_Sit003", "Salon:65", "Salon:63", "Salon:69"
  455. });
  456. List<string> DoguList = DoguDict[customDoguCategories[DoguCategory.Other]];
  457. // bg object extend
  458. HashSet<string> doguHashSet = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
  459. doguHashSet.UnionWith(BGList);
  460. try
  461. {
  462. string ignoreListPath = Path.Combine(configPath, "Database\\bg_ignore_list.json");
  463. string ignoreListJson = File.ReadAllText(ignoreListPath);
  464. string[] ignoreList = JsonConvert.DeserializeObject<string[]>(ignoreListJson);
  465. doguHashSet.UnionWith(ignoreList);
  466. }
  467. catch (IOException e)
  468. {
  469. Utility.LogWarning($"Could not open ignored BG database because {e.Message}");
  470. }
  471. catch { }
  472. foreach (List<string> doguList in DoguDict.Values)
  473. {
  474. doguHashSet.UnionWith(doguList);
  475. }
  476. foreach (string path in GameUty.FileSystem.GetList("bg", AFileSystemBase.ListType.AllFile))
  477. {
  478. if (Path.GetExtension(path) == ".asset_bg" && !path.Contains("myroomcustomize"))
  479. {
  480. string file = Path.GetFileNameWithoutExtension(path);
  481. if (!doguHashSet.Contains(file) && !file.EndsWith("_hit"))
  482. {
  483. DoguList.Add(file);
  484. doguHashSet.Add(file);
  485. }
  486. }
  487. }
  488. // Get cherry picked dogu that I can't find in the game files
  489. try
  490. {
  491. string doguExtendPath = Path.Combine(configPath, "Database\\extra_dogu.json");
  492. string doguExtendJson = File.ReadAllText(doguExtendPath);
  493. DoguList.AddRange(JsonConvert.DeserializeObject<IEnumerable<string>>(doguExtendJson));
  494. }
  495. catch (IOException e)
  496. {
  497. Utility.LogError($"Could not open extra prop database because {e.Message}");
  498. }
  499. catch (Exception e)
  500. {
  501. Utility.LogError($"Could not parse extra prop database because {e.Message}");
  502. }
  503. foreach (string path in GameUty.FileSystemOld.GetList("bg", AFileSystemBase.ListType.AllFile))
  504. {
  505. if (Path.GetExtension(path) == ".asset_bg")
  506. {
  507. string file = Path.GetFileNameWithoutExtension(path);
  508. if (!doguHashSet.Contains(file) && !file.EndsWith("_not_optimisation"))
  509. {
  510. DoguList.Add(file);
  511. }
  512. }
  513. }
  514. }
  515. private static void InitializeDeskItems()
  516. {
  517. HashSet<int> enabledIDs = new HashSet<int>();
  518. CsvCommonIdManager.ReadEnabledIdList(
  519. CsvCommonIdManager.FileSystemType.Normal, true, "desk_item_enabled_id", ref enabledIDs
  520. );
  521. CsvCommonIdManager.ReadEnabledIdList(
  522. CsvCommonIdManager.FileSystemType.Old, true, "desk_item_enabled_id", ref enabledIDs
  523. );
  524. List<string> com3d2DeskDogu = DoguDict[customDoguCategories[DoguCategory.Desk]];
  525. void GetDeskItems(AFileSystemBase fs)
  526. {
  527. using (CsvParser csvParser = OpenCsvParser("desk_item_detail.nei", fs))
  528. {
  529. for (int cell_y = 1; cell_y < csvParser.max_cell_y; cell_y++)
  530. {
  531. if (csvParser.IsCellToExistData(0, cell_y))
  532. {
  533. int cell = csvParser.GetCellAsInteger(0, cell_y);
  534. if (enabledIDs.Contains(cell))
  535. {
  536. string dogu = string.Empty;
  537. if (csvParser.IsCellToExistData(3, cell_y))
  538. {
  539. dogu = csvParser.GetCellAsString(3, cell_y);
  540. }
  541. else if (csvParser.IsCellToExistData(4, cell_y))
  542. {
  543. dogu = csvParser.GetCellAsString(4, cell_y);
  544. }
  545. if (!string.IsNullOrEmpty(dogu))
  546. {
  547. com3d2DeskDogu.Add(dogu);
  548. }
  549. }
  550. }
  551. }
  552. }
  553. }
  554. GetDeskItems(GameUty.FileSystem);
  555. }
  556. private static void InitializePhotoBGItems()
  557. {
  558. PhotoBGObjectData.Create();
  559. List<PhotoBGObjectData> photoBGObjectList = PhotoBGObjectData.data;
  560. List<string> doguCategories = new List<string>();
  561. HashSet<string> addedCategories = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
  562. foreach (PhotoBGObjectData photoBGObject in photoBGObjectList)
  563. {
  564. string category = photoBGObject.category;
  565. if (!addedCategories.Contains(category))
  566. {
  567. addedCategories.Add(category);
  568. doguCategories.Add(category);
  569. }
  570. if (!DoguDict.ContainsKey(category))
  571. {
  572. DoguDict[category] = new List<string>();
  573. }
  574. string dogu = string.Empty;
  575. if (!string.IsNullOrEmpty(photoBGObject.create_prefab_name))
  576. {
  577. dogu = photoBGObject.create_prefab_name;
  578. }
  579. else if (!string.IsNullOrEmpty(photoBGObject.create_asset_bundle_name))
  580. {
  581. dogu = photoBGObject.create_asset_bundle_name;
  582. }
  583. else if (!string.IsNullOrEmpty(photoBGObject.direct_file))
  584. {
  585. dogu = photoBGObject.direct_file;
  586. }
  587. if (!string.IsNullOrEmpty(dogu))
  588. {
  589. DoguDict[category].Add(dogu);
  590. }
  591. }
  592. DoguDict["パーティクル"].AddRange(new[] {
  593. "Particle/pLineY", "Particle/pLineP02", "Particle/pHeart01",
  594. "Particle/pLine_act2", "Particle/pstarY_act2"
  595. });
  596. }
  597. private static void InitializeHandItems()
  598. {
  599. if (HandItemsInitialized) return;
  600. if (!MenuFilesReady)
  601. {
  602. if (!beginHandItemInit) MenuFilesReadyChange += (s, a) => InitializeHandItems();
  603. beginHandItemInit = true;
  604. return;
  605. }
  606. MenuDataBase menuDataBase = GameMain.Instance.MenuDataBase;
  607. HashSet<string> doguHashSet = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
  608. doguHashSet.UnionWith(BGList);
  609. try
  610. {
  611. string ignoreListPath = Path.Combine(configPath, "Database\\bg_ignore_list.json");
  612. string ignoreListJson = File.ReadAllText(ignoreListPath);
  613. string[] ignoreList = JsonConvert.DeserializeObject<string[]>(ignoreListJson);
  614. doguHashSet.UnionWith(ignoreList);
  615. }
  616. catch (IOException e)
  617. {
  618. Utility.LogWarning($"Could not open ignored BG database because {e.Message}");
  619. }
  620. catch (Exception e)
  621. {
  622. Utility.LogError($"Could not parse ignored BG database because {e.Message}");
  623. }
  624. foreach (List<string> doguList in DoguDict.Values)
  625. {
  626. doguHashSet.UnionWith(doguList);
  627. }
  628. string category = customDoguCategories[DoguCategory.HandItem];
  629. for (int i = 0; i < menuDataBase.GetDataSize(); i++)
  630. {
  631. menuDataBase.SetIndex(i);
  632. if ((MPN)menuDataBase.GetCategoryMpn() == MPN.handitem)
  633. {
  634. string menuFileName = menuDataBase.GetMenuFileName();
  635. if (menuDataBase.GetBoDelOnly() || menuFileName.EndsWith("_del.menu")) continue;
  636. string handItemAsOdogu = Utility.HandItemToOdogu(menuFileName);
  637. string isolatedHandItem = menuFileName.Substring(menuFileName.IndexOf('_') + 1);
  638. if (!doguHashSet.Contains(handItemAsOdogu) && !doguHashSet.Contains(isolatedHandItem))
  639. {
  640. doguHashSet.Add(isolatedHandItem);
  641. DoguDict[category].Add(menuFileName);
  642. // Check for a half deck of cards to add the full deck as well
  643. if (menuFileName == "handitemd_cards_i_.menu")
  644. {
  645. DoguDict[category].Add("handiteml_cards_i_.menu");
  646. }
  647. }
  648. }
  649. }
  650. HandItemsInitialized = true;
  651. OnMenuFilesChange(MenuFilesEventArgs.EventType.HandItems);
  652. }
  653. private static void InitializeMpnAttachProps()
  654. {
  655. if (MpnAttachInitialized) return;
  656. if (!MenuFilesReady)
  657. {
  658. if (!beginMpnAttachInit) MenuFilesReadyChange += (s, a) => InitializeMpnAttachProps();
  659. beginMpnAttachInit = true;
  660. return;
  661. }
  662. MenuDataBase menuDataBase = GameMain.Instance.MenuDataBase;
  663. MPN[] attachMpn = { MPN.kousoku_lower, MPN.kousoku_upper };
  664. for (int i = 0; i < menuDataBase.GetDataSize(); i++)
  665. {
  666. menuDataBase.SetIndex(i);
  667. MPN itemMpn = (MPN)menuDataBase.GetCategoryMpn();
  668. if (attachMpn.Any(mpn => mpn == itemMpn))
  669. {
  670. string menuFileName = menuDataBase.GetMenuFileName();
  671. string mpnTag = menuDataBase.GetCategoryMpnText();
  672. if (menuDataBase.GetBoDelOnly() || menuFileName.EndsWith("_del.menu")) continue;
  673. MpnAttachPropList.Add(new MpnAttachProp(itemMpn, menuFileName));
  674. }
  675. }
  676. MpnAttachInitialized = true;
  677. OnMenuFilesChange(MenuFilesEventArgs.EventType.MpnAttach);
  678. }
  679. private static void InitializeMyRoomProps()
  680. {
  681. PlacementData.CreateData();
  682. List<PlacementData.Data> myRoomData = PlacementData.GetAllDatas(false);
  683. myRoomData.Sort((a, b) =>
  684. {
  685. int res = a.categoryID.CompareTo(b.categoryID);
  686. if (res == 0) res = a.ID.CompareTo(b.ID);
  687. return res;
  688. });
  689. foreach (PlacementData.Data data in myRoomData)
  690. {
  691. string category = PlacementData.GetCategoryName(data.categoryID);
  692. if (!MyRoomPropDict.ContainsKey(category))
  693. {
  694. MyRoomPropCategories.Add(category);
  695. MyRoomPropDict[category] = new List<MyRoomItem>();
  696. }
  697. string asset = !string.IsNullOrEmpty(data.resourceName) ? data.resourceName : data.assetName;
  698. MyRoomItem item = new MyRoomItem() { PrefabName = asset, ID = data.ID };
  699. MyRoomPropDict[category].Add(item);
  700. }
  701. }
  702. private static void InitializeModProps()
  703. {
  704. for (int i = 1; i < MenuCategories.Length; i++)
  705. {
  706. ModPropDict[MenuCategories[i]] = new List<ModItem>();
  707. }
  708. if (!PropManager.ModItemsOnly)
  709. {
  710. MenuDataBase menuDatabase = GameMain.Instance.MenuDataBase;
  711. for (int i = 0; i < menuDatabase.GetDataSize(); i++)
  712. {
  713. menuDatabase.SetIndex(i);
  714. ModItem modItem = new ModItem();
  715. if (ParseNativeMenuFile(i, modItem))
  716. {
  717. ModPropDict[modItem.Category].Add(modItem);
  718. }
  719. }
  720. }
  721. MenuFileCache cache = new MenuFileCache();
  722. foreach (string modMenuFile in GameUty.ModOnlysMenuFiles)
  723. {
  724. ModItem modItem;
  725. if (cache.Has(modMenuFile)) modItem = cache[modMenuFile];
  726. else
  727. {
  728. modItem = ModItem.Mod(modMenuFile);
  729. ParseMenuFile(modMenuFile, modItem);
  730. cache[modMenuFile] = modItem;
  731. }
  732. if (ValidBG2MenuFile(modItem)) ModPropDict[modItem.Category].Add(modItem);
  733. }
  734. cache.Serialize();
  735. foreach (string modFile in Menu.GetModFiles())
  736. {
  737. ModItem modItem = ModItem.OfficialMod(modFile);
  738. if (ParseModMenuFile(modFile, modItem))
  739. {
  740. ModPropDict[modItem.Category].Add(modItem);
  741. }
  742. }
  743. MenuFilesInitialized = true;
  744. }
  745. public static List<ModItem> GetModPropList(string category)
  746. {
  747. if (!PropManager.ModItemsOnly && !MenuFilesReady)
  748. {
  749. Utility.LogMessage("Menu files are not ready yet");
  750. return null;
  751. }
  752. if (!MenuFilesInitialized) InitializeModProps();
  753. if (!ModPropDict.ContainsKey(category)) return null;
  754. List<ModItem> selectedList = ModPropDict[category];
  755. if (selectedList[0].Icon == null)
  756. {
  757. selectedList.Sort((a, b) =>
  758. {
  759. int res = a.Priority.CompareTo(b.Priority);
  760. if (res == 0) res = string.Compare(a.Name, b.Name);
  761. return res;
  762. });
  763. string previousMenuFile = string.Empty;
  764. selectedList.RemoveAll(item =>
  765. {
  766. if (item.Icon == null)
  767. {
  768. Texture2D icon;
  769. string iconFile = item.IconFile;
  770. if (string.IsNullOrEmpty(iconFile) || !GameUty.FileSystem.IsExistentFile(iconFile))
  771. {
  772. Utility.LogWarning($"Could not find icon '{iconFile}' for menu '{item.MenuFile}");
  773. return true;
  774. }
  775. try
  776. {
  777. icon = ImportCM.CreateTexture(iconFile);
  778. }
  779. catch
  780. {
  781. try
  782. {
  783. icon = ImportCM.CreateTexture($"tex\\{iconFile}");
  784. }
  785. catch
  786. {
  787. Utility.LogWarning($"Could not load '{iconFile}' for menu '{item.MenuFile}");
  788. return true;
  789. }
  790. }
  791. item.Icon = icon;
  792. }
  793. return false;
  794. });
  795. }
  796. return selectedList;
  797. }
  798. private static CsvParser OpenCsvParser(string nei, AFileSystemBase fs)
  799. {
  800. try
  801. {
  802. if (fs.IsExistentFile(nei))
  803. {
  804. AFileBase file = fs.FileOpen(nei);
  805. CsvParser csvParser = new CsvParser();
  806. if (csvParser.Open(file)) return csvParser;
  807. file?.Dispose();
  808. }
  809. }
  810. catch { }
  811. return null;
  812. }
  813. private static void OnMenuFilesChange(MenuFilesEventArgs.EventType eventType)
  814. {
  815. MenuFilesChange?.Invoke(null, new MenuFilesEventArgs(eventType));
  816. }
  817. private class SerializePoseList
  818. {
  819. public string UIName { get; set; }
  820. public List<string> PoseList { get; set; }
  821. }
  822. }
  823. public class MenuFilesEventArgs : EventArgs
  824. {
  825. public EventType Type { get; }
  826. public enum EventType
  827. {
  828. HandItems, MenuFiles, MpnAttach
  829. }
  830. public MenuFilesEventArgs(EventType type) => Type = type;
  831. }
  832. public class PresetChangeEventArgs : EventArgs
  833. {
  834. public string Category { get; }
  835. public string Path { get; }
  836. public static new PresetChangeEventArgs Empty { get; } = new PresetChangeEventArgs(string.Empty, string.Empty);
  837. public PresetChangeEventArgs(string path, string category)
  838. {
  839. Path = path;
  840. Category = category;
  841. }
  842. }
  843. public struct MpnAttachProp
  844. {
  845. public MPN Tag { get; }
  846. public string MenuFile { get; }
  847. public MpnAttachProp(MPN tag, string menuFile)
  848. {
  849. Tag = tag;
  850. MenuFile = menuFile;
  851. }
  852. }
  853. }