Constants.cs 40 KB

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