Constants.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using Newtonsoft.Json;
  6. using MyRoomCustom;
  7. using UnityEngine;
  8. using wf;
  9. namespace COM3D2.MeidoPhotoStudio.Plugin
  10. {
  11. using static MenuFileUtility;
  12. internal class Constants
  13. {
  14. private static bool beginHandItemInit;
  15. public const string customPoseDirectory = "Custom Poses";
  16. public const string sceneDirectory = "Scenes";
  17. public const string kankyoDirectory = "Environments";
  18. public const string configDirectory = "MeidoPhotoStudio";
  19. public const string translationDirectory = "Translations";
  20. public static readonly string customPosePath;
  21. public static readonly string scenesPath;
  22. public static readonly string kankyoPath;
  23. public static readonly string configPath;
  24. public static readonly int mainWindowID = 765;
  25. public static readonly int messageWindowID = 961;
  26. public static readonly int sceneManagerWindowID = 876;
  27. public static readonly int sceneManagerModalID = 283;
  28. public static readonly int dropdownWindowID = 777;
  29. public enum Window
  30. {
  31. Call, Pose, Face, BG, BG2, Main, Message, Save, SaveModal
  32. }
  33. public enum Scene
  34. {
  35. Daily = 3, Edit = 5
  36. }
  37. public static readonly List<string> PoseGroupList = new List<string>();
  38. public static readonly Dictionary<string, List<string>> PoseDict = new Dictionary<string, List<string>>();
  39. public static readonly List<string> CustomPoseGroupList = new List<string>();
  40. public static readonly Dictionary<string, List<string>> CustomPoseDict = new Dictionary<string, List<string>>();
  41. public static readonly List<string> FaceBlendList = new List<string>();
  42. public static readonly List<string> BGList = new List<string>();
  43. public static readonly List<KeyValuePair<string, string>> MyRoomCustomBGList
  44. = new List<KeyValuePair<string, string>>();
  45. public static readonly List<string> DoguCategories = new List<string>();
  46. public static readonly Dictionary<string, List<string>> DoguDict = new Dictionary<string, List<string>>();
  47. public static readonly List<string> MyRoomPropCategories = new List<string>();
  48. public static readonly Dictionary<string, List<MyRoomItem>> MyRoomPropDict
  49. = new Dictionary<string, List<MyRoomItem>>();
  50. public static readonly Dictionary<string, List<ModItem>> ModPropDict
  51. = new Dictionary<string, List<ModItem>>(StringComparer.InvariantCultureIgnoreCase);
  52. public static int CustomPoseGroupsIndex { get; private set; } = -1;
  53. public static int MyRoomCustomBGIndex { get; private set; } = -1;
  54. public static bool HandItemsInitialized { get; private set; } = false;
  55. public static bool MenuFilesInitialized { get; private set; } = false;
  56. public static event EventHandler<MenuFilesEventArgs> MenuFilesChange;
  57. public static event EventHandler<CustomPoseEventArgs> customPoseChange;
  58. public enum DoguCategory
  59. {
  60. Other, Mob, Desk, HandItem, BGSmall
  61. }
  62. public static readonly Dictionary<DoguCategory, string> customDoguCategories =
  63. new Dictionary<DoguCategory, string>()
  64. {
  65. [DoguCategory.Other] = "other",
  66. [DoguCategory.Mob] = "mob",
  67. [DoguCategory.Desk] = "desk",
  68. [DoguCategory.HandItem] = "handItem",
  69. [DoguCategory.BGSmall] = "bgSmall"
  70. };
  71. static Constants()
  72. {
  73. string modsPath = Path.Combine(BepInEx.Paths.GameRootPath, @"Mod\MeidoPhotoStudio");
  74. customPosePath = Path.Combine(modsPath, customPoseDirectory);
  75. scenesPath = Path.Combine(modsPath, sceneDirectory);
  76. kankyoPath = Path.Combine(modsPath, kankyoDirectory);
  77. configPath = Path.Combine(BepInEx.Paths.ConfigPath, configDirectory);
  78. foreach (string directory in new[] { customPosePath, scenesPath, kankyoPath, configPath })
  79. {
  80. if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
  81. }
  82. }
  83. public static void Initialize()
  84. {
  85. InitializePoses();
  86. InitializeFaceBlends();
  87. InitializeBGs();
  88. InitializeDogu();
  89. InitializeMyRoomProps();
  90. }
  91. public static void AddPose(byte[] anmBinary, string filename, string directory)
  92. {
  93. // TODO: Consider writing a file system monitor
  94. filename = Utility.SanitizePathPortion(filename);
  95. directory = Utility.SanitizePathPortion(directory);
  96. if (string.IsNullOrEmpty(filename)) filename = "custom_pose";
  97. if (directory.Equals(Constants.customPoseDirectory, StringComparison.InvariantCultureIgnoreCase))
  98. {
  99. directory = String.Empty;
  100. }
  101. directory = Path.Combine(Constants.customPosePath, directory);
  102. if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
  103. string fullPath = Path.Combine(directory, filename);
  104. if (File.Exists($"{fullPath}.anm")) fullPath += $"_{DateTime.Now:yyyyMMddHHmmss}";
  105. fullPath += ".anm";
  106. if (!fullPath.StartsWith(Constants.customPosePath))
  107. {
  108. Utility.Logger.LogError($"Could not save pose! Path is invalid: '{fullPath}'");
  109. return;
  110. }
  111. File.WriteAllBytes(fullPath, anmBinary);
  112. FileInfo fileInfo = new FileInfo(fullPath);
  113. string category = fileInfo.Directory.Name;
  114. string poseGroup = Constants.CustomPoseGroupList.Find(
  115. group => string.Equals(category, group, StringComparison.InvariantCultureIgnoreCase)
  116. );
  117. if (string.IsNullOrEmpty(poseGroup))
  118. {
  119. Constants.CustomPoseGroupList.Add(category);
  120. Constants.CustomPoseDict[category] = new List<string>();
  121. }
  122. else category = poseGroup;
  123. Constants.CustomPoseDict[category].Add(fullPath);
  124. Constants.CustomPoseDict[category].Sort();
  125. customPoseChange?.Invoke(null, new CustomPoseEventArgs(fullPath, category));
  126. }
  127. public static void InitializePoses()
  128. {
  129. // Load Poses
  130. string poseListJson = File.ReadAllText(Path.Combine(configPath, "mm_pose_list.json"));
  131. List<SerializePoseList> poseLists = JsonConvert.DeserializeObject<List<SerializePoseList>>(poseListJson);
  132. foreach (SerializePoseList poseList in poseLists)
  133. {
  134. PoseDict[poseList.UIName] = poseList.PoseList;
  135. PoseGroupList.Add(poseList.UIName);
  136. }
  137. // Get Other poses that'll go into Normal 2 and Ero 2
  138. string[] com3d2MotionList = GameUty.FileSystem.GetList("motion", AFileSystemBase.ListType.AllFile);
  139. if (com3d2MotionList != null && com3d2MotionList.Length > 0)
  140. {
  141. HashSet<string> poseSet = new HashSet<string>();
  142. foreach (List<string> poses in PoseDict.Values)
  143. {
  144. poseSet.UnionWith(poses);
  145. }
  146. List<string> editPoseList = new List<string>();
  147. List<string> otherPoseList = new List<string>();
  148. List<string> eroPoseList = new List<string>();
  149. foreach (string path in com3d2MotionList)
  150. {
  151. if (Path.GetExtension(path) == ".anm")
  152. {
  153. string file = Path.GetFileNameWithoutExtension(path);
  154. if (!poseSet.Contains(file))
  155. {
  156. if (file.StartsWith("edit_"))
  157. {
  158. editPoseList.Add(file);
  159. }
  160. else if (file != "dance_cm3d2_001_zoukin" && file != "dance_cm3d2_001_mop"
  161. && file != "aruki_1_idougo_f" && file != "sleep2" && file != "stand_akire2"
  162. && !file.EndsWith("_3_") && !file.EndsWith("_5_") && !file.StartsWith("vr_")
  163. && !file.StartsWith("dance_mc") && !file.Contains("_kubi_") && !file.Contains("a01_")
  164. && !file.Contains("b01_") && !file.Contains("b02_") && !file.EndsWith("_m2")
  165. && !file.EndsWith("_m2_once_") && !file.StartsWith("h_") && !file.StartsWith("event_")
  166. && !file.StartsWith("man_") && !file.EndsWith("_m") && !file.Contains("_m_")
  167. && !file.Contains("_man_")
  168. )
  169. {
  170. if (path.Contains(@"\sex\")) eroPoseList.Add(file);
  171. else otherPoseList.Add(file);
  172. }
  173. }
  174. }
  175. }
  176. PoseDict["normal"].AddRange(editPoseList);
  177. PoseDict["normal2"] = otherPoseList;
  178. PoseDict["ero2"] = eroPoseList;
  179. PoseGroupList.AddRange(new[] { "normal2", "ero2" });
  180. }
  181. Action<string> GetPoses = directory =>
  182. {
  183. List<string> poseList = Directory.GetFiles(directory)
  184. .Where(file => Path.GetExtension(file) == ".anm").ToList();
  185. if (poseList.Count > 0)
  186. {
  187. string poseGroupName = new DirectoryInfo(directory).Name;
  188. if (poseGroupName != customPoseDirectory) CustomPoseGroupList.Add(poseGroupName);
  189. CustomPoseDict[poseGroupName] = poseList;
  190. }
  191. };
  192. CustomPoseGroupList.Add(customPoseDirectory);
  193. CustomPoseDict[customPoseDirectory] = new List<string>();
  194. GetPoses(customPosePath);
  195. foreach (string directory in Directory.GetDirectories(customPosePath))
  196. {
  197. GetPoses(directory);
  198. }
  199. }
  200. public static void InitializeFaceBlends()
  201. {
  202. using (CsvParser csvParser = OpenCsvParser("phot_face_list.nei"))
  203. {
  204. for (int cell_y = 1; cell_y < csvParser.max_cell_y; cell_y++)
  205. {
  206. if (csvParser.IsCellToExistData(3, cell_y))
  207. {
  208. string blendValue = csvParser.GetCellAsString(3, cell_y);
  209. FaceBlendList.Add(blendValue);
  210. }
  211. }
  212. }
  213. }
  214. public static void InitializeBGs()
  215. {
  216. // Load BGs
  217. PhotoBGData.Create();
  218. List<PhotoBGData> photList = PhotoBGData.data;
  219. // COM3D2 BGs
  220. foreach (PhotoBGData bgData in photList)
  221. {
  222. if (!string.IsNullOrEmpty(bgData.create_prefab_name))
  223. {
  224. string bg = bgData.create_prefab_name;
  225. BGList.Add(bg);
  226. }
  227. }
  228. // CM3D2 BGs
  229. if (GameUty.IsEnabledCompatibilityMode)
  230. {
  231. using (CsvParser csvParser = OpenCsvParser("phot_bg_list.nei", GameUty.FileSystemOld))
  232. {
  233. for (int cell_y = 1; cell_y < csvParser.max_cell_y; cell_y++)
  234. {
  235. if (csvParser.IsCellToExistData(3, cell_y))
  236. {
  237. string bg = csvParser.GetCellAsString(3, cell_y);
  238. BGList.Add(bg);
  239. }
  240. }
  241. }
  242. }
  243. Dictionary<string, string> saveDataDict = MyRoomCustom.CreativeRoomManager.GetSaveDataDic();
  244. if (saveDataDict != null)
  245. {
  246. MyRoomCustomBGIndex = BGList.Count;
  247. MyRoomCustomBGList.AddRange(saveDataDict);
  248. }
  249. }
  250. public static void InitializeDogu()
  251. {
  252. foreach (string customCategory in customDoguCategories.Values)
  253. {
  254. DoguDict[customCategory] = new List<string>();
  255. }
  256. InitializeDeskItems();
  257. InitializePhotoBGItems();
  258. InitializeOtherDogu();
  259. InitializeHandItems();
  260. foreach (string category in PhotoBGObjectData.popup_category_list.Select(kvp => kvp.Key))
  261. {
  262. if (category == "マイオブジェクト") continue;
  263. DoguCategories.Add(category);
  264. }
  265. foreach (DoguCategory category in Enum.GetValues(typeof(DoguCategory)))
  266. {
  267. DoguCategories.Add(customDoguCategories[category]);
  268. }
  269. }
  270. private static void InitializeOtherDogu()
  271. {
  272. DoguDict[customDoguCategories[DoguCategory.BGSmall]] = BGList;
  273. DoguDict[customDoguCategories[DoguCategory.Mob]].AddRange(new[] {
  274. "Mob_Man_Stand001", "Mob_Man_Stand002", "Mob_Man_Stand003", "Mob_Man_Sit001", "Mob_Man_Sit002",
  275. "Mob_Man_Sit003", "Mob_Girl_Stand001", "Mob_Girl_Stand002", "Mob_Girl_Stand003", "Mob_Girl_Sit001",
  276. "Mob_Girl_Sit002", "Mob_Girl_Sit003", "Salon:65", "Salon:63", "Salon:69"
  277. });
  278. List<string> DoguList = DoguDict[customDoguCategories[DoguCategory.Other]];
  279. string ignoreListPath = Path.Combine(configPath, "mm_ignore_list.json");
  280. string ignoreListJson = File.ReadAllText(ignoreListPath);
  281. string[] ignoreList = JsonConvert.DeserializeObject<IEnumerable<string>>(ignoreListJson).ToArray();
  282. // bg object extend
  283. HashSet<string> doguHashSet = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
  284. doguHashSet.UnionWith(BGList);
  285. doguHashSet.UnionWith(ignoreList);
  286. foreach (List<string> doguList in DoguDict.Values)
  287. {
  288. doguHashSet.UnionWith(doguList);
  289. }
  290. string[] com3d2BgList = GameUty.FileSystem.GetList("bg", AFileSystemBase.ListType.AllFile);
  291. foreach (string path in com3d2BgList)
  292. {
  293. if (Path.GetExtension(path) == ".asset_bg" && !path.Contains("myroomcustomize"))
  294. {
  295. string file = Path.GetFileNameWithoutExtension(path);
  296. if (!doguHashSet.Contains(file) && !file.EndsWith("_hit"))
  297. {
  298. DoguList.Add(file);
  299. doguHashSet.Add(file);
  300. }
  301. }
  302. }
  303. // Get cherry picked dogu that I can't find in the game files
  304. string doguExtendPath = Path.Combine(configPath, "mm_dogu_extend.json");
  305. string doguExtendJson = File.ReadAllText(doguExtendPath);
  306. DoguList.AddRange(JsonConvert.DeserializeObject<IEnumerable<string>>(doguExtendJson));
  307. string[] cm3d2BgList = GameUty.FileSystemOld.GetList("bg", AFileSystemBase.ListType.AllFile);
  308. foreach (string path in cm3d2BgList)
  309. {
  310. if (Path.GetExtension(path) == ".asset_bg")
  311. {
  312. string file = Path.GetFileNameWithoutExtension(path);
  313. if (!doguHashSet.Contains(file) && !file.EndsWith("_not_optimisation"))
  314. {
  315. DoguList.Add(file);
  316. }
  317. }
  318. }
  319. }
  320. private static void InitializeDeskItems()
  321. {
  322. HashSet<int> enabledIDs = new HashSet<int>();
  323. CsvCommonIdManager.ReadEnabledIdList(
  324. CsvCommonIdManager.FileSystemType.Normal, true, "desk_item_enabled_id", ref enabledIDs
  325. );
  326. CsvCommonIdManager.ReadEnabledIdList(
  327. CsvCommonIdManager.FileSystemType.Old, true, "desk_item_enabled_id", ref enabledIDs
  328. );
  329. List<string> com3d2DeskDogu = DoguDict[customDoguCategories[DoguCategory.Desk]];
  330. Action<AFileSystemBase> GetDeskItems = fs =>
  331. {
  332. using (CsvParser csvParser = OpenCsvParser("desk_item_detail.nei", fs))
  333. {
  334. for (int cell_y = 1; cell_y < csvParser.max_cell_y; cell_y++)
  335. {
  336. if (csvParser.IsCellToExistData(0, cell_y))
  337. {
  338. int cell = csvParser.GetCellAsInteger(0, cell_y);
  339. if (enabledIDs.Contains(cell))
  340. {
  341. string dogu = String.Empty;
  342. if (csvParser.IsCellToExistData(3, cell_y))
  343. {
  344. dogu = csvParser.GetCellAsString(3, cell_y);
  345. }
  346. else if (csvParser.IsCellToExistData(4, cell_y))
  347. {
  348. dogu = csvParser.GetCellAsString(4, cell_y);
  349. }
  350. if (!string.IsNullOrEmpty(dogu))
  351. {
  352. com3d2DeskDogu.Add(dogu);
  353. }
  354. }
  355. }
  356. }
  357. }
  358. };
  359. GetDeskItems(GameUty.FileSystem);
  360. }
  361. private static void InitializePhotoBGItems()
  362. {
  363. PhotoBGObjectData.Create();
  364. List<PhotoBGObjectData> photoBGObjectList = PhotoBGObjectData.data;
  365. List<string> doguCategories = new List<string>();
  366. HashSet<string> addedCategories = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
  367. foreach (PhotoBGObjectData photoBGObject in photoBGObjectList)
  368. {
  369. string category = photoBGObject.category;
  370. if (!addedCategories.Contains(category))
  371. {
  372. addedCategories.Add(category);
  373. doguCategories.Add(category);
  374. }
  375. if (!DoguDict.ContainsKey(category))
  376. {
  377. DoguDict[category] = new List<string>();
  378. }
  379. string dogu = String.Empty;
  380. if (!string.IsNullOrEmpty(photoBGObject.create_prefab_name))
  381. {
  382. dogu = photoBGObject.create_prefab_name;
  383. }
  384. else if (!string.IsNullOrEmpty(photoBGObject.create_asset_bundle_name))
  385. {
  386. dogu = photoBGObject.create_asset_bundle_name;
  387. }
  388. else if (!string.IsNullOrEmpty(photoBGObject.direct_file))
  389. {
  390. dogu = photoBGObject.direct_file;
  391. }
  392. if (!string.IsNullOrEmpty(dogu))
  393. {
  394. DoguDict[category].Add(dogu);
  395. }
  396. }
  397. DoguDict["パーティクル"].AddRange(new[] {
  398. "Particle/pLineY", "Particle/pLineP02", "Particle/pHeart01",
  399. "Particle/pLine_act2", "Particle/pstarY_act2"
  400. });
  401. }
  402. private static void InitializeHandItems()
  403. {
  404. if (HandItemsInitialized) return;
  405. if (!MenuFileUtility.MenuFilesReady)
  406. {
  407. if (!beginHandItemInit) MenuFileUtility.MenuFilesReadyChange += (s, a) => InitializeHandItems();
  408. beginHandItemInit = true;
  409. return;
  410. }
  411. MenuDataBase menuDataBase = GameMain.Instance.MenuDataBase;
  412. string ignoreListJson = File.ReadAllText(Path.Combine(configPath, "mm_ignore_list.json"));
  413. string[] ignoreList = JsonConvert.DeserializeObject<IEnumerable<string>>(ignoreListJson).ToArray();
  414. HashSet<string> doguHashSet = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
  415. doguHashSet.UnionWith(BGList);
  416. doguHashSet.UnionWith(ignoreList);
  417. foreach (List<string> doguList in DoguDict.Values)
  418. {
  419. doguHashSet.UnionWith(doguList);
  420. }
  421. string category = customDoguCategories[DoguCategory.HandItem];
  422. for (int i = 0; i < menuDataBase.GetDataSize(); i++)
  423. {
  424. menuDataBase.SetIndex(i);
  425. if ((MPN)menuDataBase.GetCategoryMpn() == MPN.handitem)
  426. {
  427. string menuFileName = menuDataBase.GetMenuFileName();
  428. if (menuDataBase.GetBoDelOnly() || menuFileName.EndsWith("_del.menu")) continue;
  429. string handItemAsOdogu = Utility.HandItemToOdogu(menuFileName);
  430. string isolatedHandItem = menuFileName.Substring(menuFileName.IndexOf('_') + 1);
  431. if (!doguHashSet.Contains(handItemAsOdogu) && !doguHashSet.Contains(isolatedHandItem))
  432. {
  433. doguHashSet.Add(isolatedHandItem);
  434. DoguDict[category].Add(menuFileName);
  435. // Check for a half deck of cards to add the full deck as well
  436. if (menuFileName == "handitemd_cards_i_.menu")
  437. {
  438. DoguDict[category].Add("handiteml_cards_i_.menu");
  439. }
  440. }
  441. }
  442. }
  443. OnMenuFilesChange(MenuFilesEventArgs.HandItems);
  444. HandItemsInitialized = true;
  445. }
  446. private static void InitializeMyRoomProps()
  447. {
  448. PlacementData.CreateData();
  449. List<PlacementData.Data> myRoomData = PlacementData.GetAllDatas(false);
  450. myRoomData.Sort((a, b) =>
  451. {
  452. int res = a.categoryID.CompareTo(b.categoryID);
  453. if (res == 0) res = a.ID.CompareTo(b.ID);
  454. return res;
  455. });
  456. foreach (PlacementData.Data data in myRoomData)
  457. {
  458. string category = PlacementData.GetCategoryName(data.categoryID);
  459. if (!MyRoomPropDict.ContainsKey(category))
  460. {
  461. MyRoomPropCategories.Add(category);
  462. MyRoomPropDict[category] = new List<MyRoomItem>();
  463. }
  464. string asset = !string.IsNullOrEmpty(data.resourceName) ? data.resourceName : data.assetName;
  465. MyRoomItem item = new MyRoomItem() { PrefabName = asset, ID = data.ID };
  466. MyRoomPropDict[category].Add(item);
  467. }
  468. }
  469. private static void InitializeModProps()
  470. {
  471. // TODO: cache menu files
  472. for (int i = 1; i < MenuFileUtility.MenuCategories.Length; i++)
  473. {
  474. ModPropDict[MenuCategories[i]] = new List<ModItem>();
  475. }
  476. if (!Configuration.ModItemsOnly)
  477. {
  478. MenuDataBase menuDatabase = GameMain.Instance.MenuDataBase;
  479. for (int i = 0; i < menuDatabase.GetDataSize(); i++)
  480. {
  481. menuDatabase.SetIndex(i);
  482. ModItem modItem = new ModItem();
  483. if (MenuFileUtility.ParseNativeMenuFile(i, modItem))
  484. {
  485. ModPropDict[modItem.Category].Add(modItem);
  486. }
  487. }
  488. }
  489. MenuFileCache cache = new MenuFileCache();
  490. foreach (string modMenuFile in GameUty.ModOnlysMenuFiles)
  491. {
  492. ModItem modItem;
  493. if (cache.Has(modMenuFile))
  494. {
  495. modItem = cache[modMenuFile];
  496. }
  497. else
  498. {
  499. modItem = new ModItem() { MenuFile = modMenuFile, IsMod = true };
  500. MenuFileUtility.ParseMenuFile(modMenuFile, modItem);
  501. cache[modMenuFile] = modItem;
  502. }
  503. if (MenuFileUtility.ValidBG2MenuFile(modItem)) ModPropDict[modItem.Category].Add(modItem);
  504. }
  505. cache.Serialize();
  506. foreach (string modFile in Menu.GetModFiles())
  507. {
  508. ModItem modItem = new ModItem()
  509. {
  510. MenuFile = modFile,
  511. IsMod = true,
  512. IsOfficialMod = true,
  513. Priority = 1000f
  514. };
  515. if (ParseModMenuFile(modFile, modItem))
  516. {
  517. ModPropDict[modItem.Category].Add(modItem);
  518. }
  519. }
  520. MenuFilesInitialized = true;
  521. }
  522. public static List<ModItem> GetModPropList(string category)
  523. {
  524. if (!Configuration.ModItemsOnly)
  525. {
  526. if (!MenuFileUtility.MenuFilesReady)
  527. {
  528. Utility.Logger.LogInfo("Menu files are not ready yet");
  529. return null;
  530. }
  531. }
  532. if (!MenuFilesInitialized) InitializeModProps();
  533. if (!ModPropDict.ContainsKey(category)) return null;
  534. List<ModItem> selectedList = ModPropDict[category];
  535. if (selectedList[0].Icon == null)
  536. {
  537. selectedList.Sort((a, b) =>
  538. {
  539. int res = a.Priority.CompareTo(b.Priority);
  540. if (res == 0) res = string.Compare(a.Name, b.Name);
  541. return res;
  542. });
  543. string previousMenuFile = String.Empty;
  544. selectedList.RemoveAll(item =>
  545. {
  546. if (item.Icon == null)
  547. {
  548. Texture2D icon;
  549. string iconFile = item.IconFile;
  550. if (string.IsNullOrEmpty(iconFile) || !GameUty.FileSystem.IsExistentFile(iconFile))
  551. {
  552. Utility.Logger.LogWarning($"Could not find icon '{iconFile}' for menu '{item.MenuFile}");
  553. return true;
  554. }
  555. else
  556. {
  557. try
  558. {
  559. icon = ImportCM.CreateTexture(iconFile);
  560. }
  561. catch
  562. {
  563. try
  564. {
  565. icon = ImportCM.CreateTexture($"tex\\{iconFile}");
  566. }
  567. catch
  568. {
  569. Utility.Logger.LogWarning($"Could not load '{iconFile}' for menu '{item.MenuFile}");
  570. return true;
  571. }
  572. }
  573. }
  574. item.Icon = icon;
  575. }
  576. return false;
  577. });
  578. }
  579. return selectedList;
  580. }
  581. private static CsvParser OpenCsvParser(string nei, AFileSystemBase fs)
  582. {
  583. try
  584. {
  585. if (fs.IsExistentFile(nei))
  586. {
  587. AFileBase file = fs.FileOpen(nei);
  588. CsvParser csvParser = new CsvParser();
  589. if (csvParser.Open(file)) return csvParser;
  590. else file?.Dispose();
  591. }
  592. }
  593. catch { }
  594. return null;
  595. }
  596. private static CsvParser OpenCsvParser(string nei)
  597. {
  598. return OpenCsvParser(nei, GameUty.FileSystem);
  599. }
  600. public static void WriteToFile(string name, IEnumerable<string> list)
  601. {
  602. if (Path.GetExtension(name) != ".txt") name += ".txt";
  603. File.WriteAllLines(Path.Combine(configPath, name), list.ToArray());
  604. }
  605. private static void OnMenuFilesChange(MenuFilesEventArgs args)
  606. {
  607. MenuFilesChange?.Invoke(null, args);
  608. }
  609. private class SerializePoseList
  610. {
  611. public string UIName { get; set; }
  612. public List<string> PoseList { get; set; }
  613. }
  614. }
  615. public class MenuFilesEventArgs : EventArgs
  616. {
  617. public EventType Type { get; }
  618. public enum EventType
  619. {
  620. HandItems, MenuFiles
  621. }
  622. public static MenuFilesEventArgs HandItems => new MenuFilesEventArgs(EventType.HandItems);
  623. public static MenuFilesEventArgs MenuFiles => new MenuFilesEventArgs(EventType.MenuFiles);
  624. public MenuFilesEventArgs(EventType type) => this.Type = type;
  625. }
  626. public class CustomPoseEventArgs : EventArgs
  627. {
  628. public string Category { get; }
  629. public string Path { get; }
  630. public CustomPoseEventArgs(string path, string category)
  631. {
  632. this.Path = path;
  633. this.Category = category;
  634. }
  635. }
  636. }