MenuFileUtility.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. using System;
  2. using System.Text;
  3. using System.IO;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using UnityEngine;
  8. using UnityEngine.Rendering;
  9. namespace COM3D2.MeidoPhotoStudio.Plugin
  10. {
  11. internal static class MenuFileUtility
  12. {
  13. private static byte[] menuFileBuffer;
  14. public const string noCategory = "noCategory";
  15. public static string[] MenuCategories = new[] {
  16. noCategory, "acchat", "headset", "wear", "skirt", "onepiece", "mizugi", "bra", "panz", "stkg", "shoes",
  17. "acckami", "megane", "acchead", "acchana", "accmimi", "glove", "acckubi", "acckubiwa", "acckamisub",
  18. "accnip", "accude", "accheso", "accashi", "accsenaka", "accshippo", "accxxx"
  19. };
  20. private static readonly HashSet<string> accMpn = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
  21. private enum IMode
  22. {
  23. None, ItemChange, TexChange
  24. }
  25. public static event EventHandler MenuFilesReadyChange;
  26. public static bool MenuFilesReady { get; private set; } = false;
  27. static MenuFileUtility()
  28. {
  29. accMpn.UnionWith(MenuCategories.Skip(1));
  30. GameMain.Instance.StartCoroutine(CheckMenuDataBaseJob());
  31. }
  32. private static IEnumerator CheckMenuDataBaseJob()
  33. {
  34. if (MenuFilesReady) yield break;
  35. while (!GameMain.Instance.MenuDataBase.JobFinished()) yield return null;
  36. MenuFilesReady = true;
  37. MenuFilesReadyChange?.Invoke(null, EventArgs.Empty);
  38. yield break;
  39. }
  40. private static bool ProcScriptBin(byte[] menuBuf, ModelInfo modelInfo)
  41. {
  42. using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(menuBuf), Encoding.UTF8))
  43. {
  44. string menuHeader = binaryReader.ReadString();
  45. NDebug.Assert(
  46. menuHeader == "CM3D2_MENU", "ProcScriptBin 例外 : ヘッダーファイルが不正です。" + menuHeader
  47. );
  48. binaryReader.ReadInt32(); // file version
  49. binaryReader.ReadString(); // txt path
  50. binaryReader.ReadString(); // name
  51. binaryReader.ReadString(); // category
  52. binaryReader.ReadString(); // description
  53. binaryReader.ReadInt32(); // idk (as long)
  54. string menuPropString = String.Empty;
  55. string menuPropStringTemp = String.Empty;
  56. // string tempString3 = String.Empty;
  57. // string slotName = String.Empty;
  58. try
  59. {
  60. while (true)
  61. {
  62. int numberOfProps = (int)binaryReader.ReadByte();
  63. menuPropStringTemp = menuPropString;
  64. menuPropString = String.Empty;
  65. if (numberOfProps != 0)
  66. {
  67. for (int i = 0; i < numberOfProps; i++)
  68. {
  69. menuPropString = $"{menuPropString}\"{binaryReader.ReadString()}\"";
  70. }
  71. if (menuPropString != string.Empty)
  72. {
  73. string header = UTY.GetStringCom(menuPropString);
  74. string[] menuProps = UTY.GetStringList(menuPropString);
  75. if (header == "end")
  76. {
  77. break;
  78. }
  79. else if (header == "マテリアル変更")
  80. {
  81. int matNo = int.Parse(menuProps[2]);
  82. string materialFile = menuProps[3];
  83. modelInfo.MaterialChanges.Add(new MaterialChange(matNo, materialFile));
  84. }
  85. else if (header == "additem")
  86. {
  87. modelInfo.ModelFile = menuProps[1];
  88. }
  89. }
  90. }
  91. else
  92. {
  93. break;
  94. }
  95. }
  96. }
  97. catch
  98. {
  99. return false;
  100. }
  101. }
  102. return true;
  103. }
  104. private static void ProcModScriptBin(byte[] cd, GameObject go)
  105. {
  106. BinaryReader binaryReader = new BinaryReader(new MemoryStream(cd), Encoding.UTF8);
  107. string str1 = binaryReader.ReadString();
  108. NDebug.Assert(str1 == "CM3D2_MOD", "ProcModScriptBin 例外 : ヘッダーファイルが不正です。" + str1);
  109. binaryReader.ReadInt32();
  110. binaryReader.ReadString();
  111. binaryReader.ReadString();
  112. binaryReader.ReadString();
  113. binaryReader.ReadString();
  114. binaryReader.ReadString();
  115. string mpnValue = binaryReader.ReadString();
  116. MPN mpn = MPN.null_mpn;
  117. try
  118. {
  119. mpn = (MPN)Enum.Parse(typeof(MPN), mpnValue);
  120. }
  121. catch { }
  122. if (mpn != MPN.null_mpn)
  123. {
  124. binaryReader.ReadString();
  125. }
  126. string s = binaryReader.ReadString();
  127. int num2 = binaryReader.ReadInt32();
  128. Dictionary<string, byte[]> dictionary = new Dictionary<string, byte[]>();
  129. for (int i = 0; i < num2; i++)
  130. {
  131. string key = binaryReader.ReadString();
  132. int count = binaryReader.ReadInt32();
  133. byte[] value = binaryReader.ReadBytes(count);
  134. dictionary.Add(key, value);
  135. }
  136. binaryReader.Close();
  137. using (StringReader stringReader = new StringReader(s))
  138. {
  139. IMode mode = IMode.None;
  140. string slotname = String.Empty;
  141. Material material = null;
  142. int num3 = 0;
  143. string line;
  144. bool change = false;
  145. while ((line = stringReader.ReadLine()) != null)
  146. {
  147. string[] array = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
  148. if (array[0] == "アイテム変更" || array[0] == "マテリアル変更")
  149. {
  150. mode = IMode.ItemChange;
  151. }
  152. else if (array[0] == "テクスチャ変更")
  153. {
  154. mode = IMode.TexChange;
  155. }
  156. if (mode == IMode.ItemChange)
  157. {
  158. if (array[0] == "スロット名")
  159. {
  160. slotname = array[1];
  161. change = true;
  162. }
  163. if (change)
  164. {
  165. if (array[0] == "マテリアル番号")
  166. {
  167. num3 = int.Parse(array[1]);
  168. foreach (Transform transform in go.GetComponentsInChildren<Transform>(true))
  169. {
  170. Renderer component = transform.GetComponent<Renderer>();
  171. if (component != null && component.materials != null)
  172. {
  173. Material[] materials = component.materials;
  174. for (int k = 0; k < materials.Length; k++)
  175. {
  176. if (k == num3)
  177. {
  178. material = materials[k];
  179. break;
  180. }
  181. }
  182. }
  183. }
  184. }
  185. if (material != null)
  186. {
  187. if (array[0] == "テクスチャ設定")
  188. {
  189. ChangeTex(num3, array[1], array[2].ToLower(), dictionary, go);
  190. }
  191. else if (array[0] == "色設定")
  192. {
  193. material.SetColor(array[1],
  194. new Color(
  195. float.Parse(array[2]) / 255f,
  196. float.Parse(array[3]) / 255f,
  197. float.Parse(array[4]) / 255f,
  198. float.Parse(array[5]) / 255f
  199. )
  200. );
  201. }
  202. else if (array[0] == "数値設定")
  203. {
  204. material.SetFloat(array[1], float.Parse(array[2]));
  205. }
  206. }
  207. }
  208. }
  209. else if (mode == IMode.TexChange)
  210. {
  211. int matno = int.Parse(array[2]);
  212. ChangeTex(matno, array[3], array[4].ToLower(), dictionary, go);
  213. }
  214. }
  215. }
  216. }
  217. private static void ChangeTex(
  218. int matno, string prop, string filename, Dictionary<string, byte[]> matDict, GameObject go
  219. )
  220. {
  221. TextureResource textureResource = null;
  222. byte[] buf = matDict[filename.ToLowerInvariant()];
  223. textureResource = new TextureResource(2, 2, TextureFormat.ARGB32, null, buf);
  224. List<Renderer> list = new List<Renderer>(3);
  225. go.transform.GetComponentsInChildren<Renderer>(true, list);
  226. foreach (Renderer r in list)
  227. {
  228. if (r != null && r.material != null)
  229. {
  230. if (matno < r.materials.Length)
  231. {
  232. r.materials[matno].SetTexture(prop, null);
  233. Texture2D texture2D = textureResource.CreateTexture2D();
  234. texture2D.name = filename;
  235. r.materials[matno].SetTexture(prop, texture2D);
  236. }
  237. }
  238. }
  239. }
  240. private static GameObject LoadSkinMesh_R(string modelFileName, int layer)
  241. {
  242. using (AFileBase afileBase = GameUty.FileOpen(modelFileName, null))
  243. {
  244. if (afileBase.IsValid() && afileBase.GetSize() != 0)
  245. {
  246. if (menuFileBuffer == null)
  247. {
  248. menuFileBuffer = new byte[System.Math.Max(500000, afileBase.GetSize())];
  249. }
  250. else if (menuFileBuffer.Length < afileBase.GetSize())
  251. {
  252. menuFileBuffer = new byte[afileBase.GetSize()];
  253. }
  254. afileBase.Read(ref menuFileBuffer, afileBase.GetSize());
  255. }
  256. else
  257. {
  258. Utility.LogError("invalid model");
  259. return null;
  260. }
  261. }
  262. using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(menuFileBuffer), Encoding.UTF8))
  263. {
  264. GameObject gameObject = UnityEngine.Object.Instantiate(Resources.Load("seed")) as GameObject;
  265. gameObject.layer = 1;
  266. GameObject gameObject2 = null;
  267. Hashtable hashtable = new Hashtable();
  268. string text = binaryReader.ReadString();
  269. if (text != "CM3D2_MESH")
  270. {
  271. NDebug.Assert("LoadSkinMesh_R 例外 : ヘッダーファイルが不正です。" + text, false);
  272. }
  273. int num = binaryReader.ReadInt32();
  274. string str = binaryReader.ReadString();
  275. gameObject.name = "_SM_" + str;
  276. string b = binaryReader.ReadString();
  277. int num2 = binaryReader.ReadInt32();
  278. List<GameObject> list = new List<GameObject>();
  279. for (int i = 0; i < num2; i++)
  280. {
  281. GameObject gameObject3 = UnityEngine.Object.Instantiate(Resources.Load("seed")) as GameObject;
  282. gameObject3.layer = layer;
  283. gameObject3.name = binaryReader.ReadString();
  284. list.Add(gameObject3);
  285. if (gameObject3.name == b)
  286. {
  287. gameObject2 = gameObject3;
  288. }
  289. hashtable[gameObject3.name] = gameObject3;
  290. bool flag = binaryReader.ReadByte() != 0;
  291. if (flag)
  292. {
  293. GameObject gameObject4 = UnityEngine.Object.Instantiate(Resources.Load("seed")) as GameObject;
  294. gameObject4.name = gameObject3.name + "_SCL_";
  295. gameObject4.transform.parent = gameObject3.transform;
  296. hashtable[gameObject3.name + "&_SCL_"] = gameObject4;
  297. }
  298. }
  299. for (int j = 0; j < num2; j++)
  300. {
  301. int num3 = binaryReader.ReadInt32();
  302. if (num3 >= 0)
  303. {
  304. list[j].transform.parent = list[num3].transform;
  305. }
  306. else
  307. {
  308. list[j].transform.parent = gameObject.transform;
  309. }
  310. }
  311. for (int k = 0; k < num2; k++)
  312. {
  313. Transform transform = list[k].transform;
  314. float x = binaryReader.ReadSingle();
  315. float y = binaryReader.ReadSingle();
  316. float z = binaryReader.ReadSingle();
  317. transform.localPosition = new Vector3(x, y, z);
  318. float x2 = binaryReader.ReadSingle();
  319. float y2 = binaryReader.ReadSingle();
  320. float z2 = binaryReader.ReadSingle();
  321. float w = binaryReader.ReadSingle();
  322. transform.localRotation = new Quaternion(x2, y2, z2, w);
  323. if (2001 <= num)
  324. {
  325. bool flag2 = binaryReader.ReadBoolean();
  326. if (flag2)
  327. {
  328. float x3 = binaryReader.ReadSingle();
  329. float y3 = binaryReader.ReadSingle();
  330. float z3 = binaryReader.ReadSingle();
  331. transform.localScale = new Vector3(x3, y3, z3);
  332. }
  333. }
  334. }
  335. int num4 = binaryReader.ReadInt32();
  336. int num5 = binaryReader.ReadInt32();
  337. int num6 = binaryReader.ReadInt32();
  338. SkinnedMeshRenderer skinnedMeshRenderer = gameObject2.AddComponent<SkinnedMeshRenderer>();
  339. skinnedMeshRenderer.updateWhenOffscreen = true;
  340. skinnedMeshRenderer.skinnedMotionVectors = false;
  341. skinnedMeshRenderer.lightProbeUsage = LightProbeUsage.Off;
  342. skinnedMeshRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off;
  343. skinnedMeshRenderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
  344. Transform[] array2 = new Transform[num6];
  345. for (int l = 0; l < num6; l++)
  346. {
  347. string text2 = binaryReader.ReadString();
  348. if (!hashtable.ContainsKey(text2))
  349. {
  350. Utility.LogError("nullbone= " + text2);
  351. }
  352. else
  353. {
  354. GameObject gameObject5;
  355. if (hashtable.ContainsKey(text2 + "&_SCL_"))
  356. {
  357. gameObject5 = (GameObject)hashtable[text2 + "&_SCL_"];
  358. }
  359. else
  360. {
  361. gameObject5 = (GameObject)hashtable[text2];
  362. }
  363. array2[l] = gameObject5.transform;
  364. }
  365. }
  366. skinnedMeshRenderer.bones = array2;
  367. Mesh mesh = new Mesh();
  368. skinnedMeshRenderer.sharedMesh = mesh;
  369. Mesh mesh2 = mesh;
  370. // bodyskin.listDEL.Add(mesh2);
  371. Matrix4x4[] array4 = new Matrix4x4[num6];
  372. for (int m = 0; m < num6; m++)
  373. {
  374. for (int n = 0; n < 16; n++)
  375. {
  376. array4[m][n] = binaryReader.ReadSingle();
  377. }
  378. }
  379. mesh2.bindposes = array4;
  380. Vector3[] array5 = new Vector3[num4];
  381. Vector3[] array6 = new Vector3[num4];
  382. Vector2[] array7 = new Vector2[num4];
  383. BoneWeight[] array8 = new BoneWeight[num4];
  384. for (int num8 = 0; num8 < num4; num8++)
  385. {
  386. float num9 = binaryReader.ReadSingle();
  387. float num10 = binaryReader.ReadSingle();
  388. float new_z = binaryReader.ReadSingle();
  389. array5[num8].Set(num9, num10, new_z);
  390. num9 = binaryReader.ReadSingle();
  391. num10 = binaryReader.ReadSingle();
  392. new_z = binaryReader.ReadSingle();
  393. array6[num8].Set(num9, num10, new_z);
  394. num9 = binaryReader.ReadSingle();
  395. num10 = binaryReader.ReadSingle();
  396. array7[num8].Set(num9, num10);
  397. }
  398. mesh2.vertices = array5;
  399. mesh2.normals = array6;
  400. mesh2.uv = array7;
  401. int num11 = binaryReader.ReadInt32();
  402. if (num11 > 0)
  403. {
  404. Vector4[] array9 = new Vector4[num11];
  405. for (int num12 = 0; num12 < num11; num12++)
  406. {
  407. float x4 = binaryReader.ReadSingle();
  408. float y4 = binaryReader.ReadSingle();
  409. float z4 = binaryReader.ReadSingle();
  410. float w2 = binaryReader.ReadSingle();
  411. array9[num12] = new Vector4(x4, y4, z4, w2);
  412. }
  413. mesh2.tangents = array9;
  414. }
  415. for (int num13 = 0; num13 < num4; num13++)
  416. {
  417. array8[num13].boneIndex0 = binaryReader.ReadUInt16();
  418. array8[num13].boneIndex1 = binaryReader.ReadUInt16();
  419. array8[num13].boneIndex2 = binaryReader.ReadUInt16();
  420. array8[num13].boneIndex3 = binaryReader.ReadUInt16();
  421. array8[num13].weight0 = binaryReader.ReadSingle();
  422. array8[num13].weight1 = binaryReader.ReadSingle();
  423. array8[num13].weight2 = binaryReader.ReadSingle();
  424. array8[num13].weight3 = binaryReader.ReadSingle();
  425. }
  426. mesh2.boneWeights = array8;
  427. mesh2.subMeshCount = num5;
  428. for (int num19 = 0; num19 < num5; num19++)
  429. {
  430. int num20 = binaryReader.ReadInt32();
  431. int[] array10 = new int[num20];
  432. for (int num21 = 0; num21 < num20; num21++)
  433. {
  434. array10[num21] = (int)binaryReader.ReadUInt16();
  435. }
  436. mesh2.SetTriangles(array10, num19);
  437. }
  438. int num22 = binaryReader.ReadInt32();
  439. Material[] array11 = new Material[num22];
  440. for (int num23 = 0; num23 < num22; num23++)
  441. {
  442. Material material = ImportCM.ReadMaterial(binaryReader);
  443. array11[num23] = material;
  444. }
  445. skinnedMeshRenderer.materials = array11;
  446. gameObject.AddComponent<Animation>();
  447. return gameObject;
  448. }
  449. }
  450. public static GameObject LoadModel(string menuFile) => LoadModel(new ModItem(menuFile));
  451. public static GameObject LoadModel(ModItem modItem)
  452. {
  453. byte[] menuBuffer = null;
  454. byte[] modMenuBuffer = null;
  455. if (modItem.IsOfficialMod)
  456. {
  457. using (FileStream fileStream = new FileStream(modItem.MenuFile, FileMode.Open))
  458. {
  459. if (fileStream == null || fileStream.Length == 0)
  460. {
  461. Utility.LogWarning($"Could not open mod menu file '{modItem.MenuFile}'");
  462. return null;
  463. }
  464. else
  465. {
  466. modMenuBuffer = new byte[fileStream.Length];
  467. fileStream.Read(modMenuBuffer, 0, (int)fileStream.Length);
  468. }
  469. }
  470. }
  471. string menu = modItem.IsOfficialMod ? modItem.BaseMenuFile : modItem.MenuFile;
  472. using (AFileBase afileBase = GameUty.FileOpen(menu))
  473. {
  474. if (afileBase == null || !afileBase.IsValid())
  475. {
  476. Utility.LogWarning($"Could not open menu file '{menu}'");
  477. return null;
  478. }
  479. else if (afileBase.GetSize() == 0)
  480. {
  481. Utility.LogWarning($"Mod menu is empty '{menu}'");
  482. return null;
  483. }
  484. else
  485. {
  486. menuBuffer = afileBase.ReadAll();
  487. }
  488. }
  489. ModelInfo modelInfo = new ModelInfo();
  490. if (ProcScriptBin(menuBuffer, modelInfo))
  491. {
  492. GameObject gameObject = null;
  493. try
  494. {
  495. gameObject = LoadSkinMesh_R(modelInfo.ModelFile, 1);
  496. }
  497. catch
  498. {
  499. Utility.LogWarning($"Could not load mesh for '{modItem.MenuFile}'");
  500. }
  501. if (gameObject != null)
  502. {
  503. foreach (MaterialChange matChange in modelInfo.MaterialChanges)
  504. {
  505. foreach (Transform transform in gameObject.transform.GetComponentsInChildren<Transform>(true))
  506. {
  507. Renderer renderer = transform.GetComponent<Renderer>();
  508. if (renderer != null && renderer.material != null
  509. && matChange.MaterialIndex < renderer.materials.Length
  510. )
  511. {
  512. renderer.materials[matChange.MaterialIndex] = ImportCM.LoadMaterial(
  513. matChange.MaterialFile, null, renderer.materials[matChange.MaterialIndex]
  514. );
  515. }
  516. }
  517. }
  518. if (modItem.IsOfficialMod)
  519. {
  520. ProcModScriptBin(modMenuBuffer, gameObject);
  521. }
  522. }
  523. return gameObject;
  524. }
  525. else
  526. {
  527. Utility.LogMessage($"Could not parse menu file '{modItem.MenuFile}'");
  528. return null;
  529. }
  530. }
  531. public static bool ParseNativeMenuFile(int menuIndex, ModItem modItem)
  532. {
  533. MenuDataBase menuDataBase = GameMain.Instance.MenuDataBase;
  534. menuDataBase.SetIndex(menuIndex);
  535. if (menuDataBase.GetBoDelOnly()) return false;
  536. modItem.Category = menuDataBase.GetCategoryMpnText();
  537. if (!accMpn.Contains(modItem.Category)) return false;
  538. modItem.MenuFile = menuDataBase.GetMenuFileName().ToLower();
  539. if (!ValidBG2MenuFile(modItem.MenuFile)) return false;
  540. modItem.Name = menuDataBase.GetMenuName();
  541. modItem.IconFile = menuDataBase.GetIconS();
  542. modItem.Priority = menuDataBase.GetPriority();
  543. return true;
  544. }
  545. public static bool ParseMenuFile(string menuFile, ModItem modItem)
  546. {
  547. if (!ValidBG2MenuFile(menuFile)) return false;
  548. try
  549. {
  550. using (AFileBase afileBase = GameUty.FileOpen(menuFile))
  551. {
  552. if (afileBase == null || !afileBase.IsValid() || afileBase.GetSize() == 0) return false;
  553. if (menuFileBuffer == null)
  554. {
  555. menuFileBuffer = new byte[System.Math.Max(500000, afileBase.GetSize())];
  556. }
  557. else if (menuFileBuffer.Length < afileBase.GetSize())
  558. {
  559. menuFileBuffer = new byte[afileBase.GetSize()];
  560. }
  561. afileBase.Read(ref menuFileBuffer, afileBase.GetSize());
  562. }
  563. }
  564. catch
  565. {
  566. Utility.LogError($"Could not read menu file '{menuFile}'");
  567. return false;
  568. }
  569. using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(menuFileBuffer), Encoding.UTF8))
  570. {
  571. string menuHeader = binaryReader.ReadString();
  572. NDebug.Assert(
  573. menuHeader == "CM3D2_MENU", "ProcScriptBin 例外 : ヘッダーファイルが不正です。" + menuHeader
  574. );
  575. binaryReader.ReadInt32(); // file version
  576. binaryReader.ReadString(); // txt path
  577. modItem.Name = binaryReader.ReadString(); // name
  578. binaryReader.ReadString(); // category
  579. binaryReader.ReadString(); // description
  580. binaryReader.ReadInt32(); // idk (as long)
  581. string menuPropString = String.Empty;
  582. string menuPropStringTemp = String.Empty;
  583. try
  584. {
  585. while (true)
  586. {
  587. int numberOfProps = (int)binaryReader.ReadByte();
  588. menuPropStringTemp = menuPropString;
  589. menuPropString = String.Empty;
  590. if (numberOfProps != 0)
  591. {
  592. for (int i = 0; i < numberOfProps; i++)
  593. {
  594. menuPropString = $"{menuPropString}\"{binaryReader.ReadString()}\"";
  595. }
  596. if (menuPropString != string.Empty)
  597. {
  598. string header = UTY.GetStringCom(menuPropString);
  599. string[] menuProps = UTY.GetStringList(menuPropString);
  600. if (header == "end")
  601. {
  602. break;
  603. }
  604. else if (header == "category")
  605. {
  606. modItem.Category = menuProps[1];
  607. if (!accMpn.Contains(modItem.Category)) return false;
  608. }
  609. else if (header == "icons" || header == "icon")
  610. {
  611. modItem.IconFile = menuProps[1];
  612. break;
  613. }
  614. else if (header == "priority")
  615. {
  616. modItem.Priority = float.Parse(menuProps[1]);
  617. }
  618. }
  619. }
  620. else
  621. {
  622. break;
  623. }
  624. }
  625. }
  626. catch (Exception e)
  627. {
  628. Utility.LogWarning($"Could not parse menu file '{menuFile}' because {e.Message}");
  629. return false;
  630. }
  631. }
  632. return true;
  633. }
  634. public static bool ParseModMenuFile(string modMenuFile, ModItem modItem)
  635. {
  636. if (!ValidBG2MenuFile(modMenuFile)) return false;
  637. byte[] buf = null;
  638. try
  639. {
  640. using (FileStream fileStream = new FileStream(modMenuFile, FileMode.Open))
  641. {
  642. if (fileStream == null) return false;
  643. if (fileStream.Length == 0L)
  644. {
  645. Utility.LogError($"Mod menu file '{modMenuFile}' is empty");
  646. return false;
  647. }
  648. buf = new byte[fileStream.Length];
  649. fileStream.Read(buf, 0, (int)fileStream.Length);
  650. }
  651. }
  652. catch (Exception e)
  653. {
  654. Utility.LogError($"Could not read mod menu file '{modMenuFile} because {e.Message}'");
  655. return false;
  656. }
  657. if (buf == null) return false;
  658. using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(buf), Encoding.UTF8))
  659. {
  660. try
  661. {
  662. if (binaryReader.ReadString() != "CM3D2_MOD") return false;
  663. else
  664. {
  665. binaryReader.ReadInt32();
  666. string iconName = binaryReader.ReadString();
  667. string baseItemPath = binaryReader.ReadString().Replace(":", " ");
  668. modItem.BaseMenuFile = Path.GetFileName(baseItemPath);
  669. modItem.Name = binaryReader.ReadString();
  670. modItem.Category = binaryReader.ReadString();
  671. if (!accMpn.Contains(modItem.Category)) return false;
  672. binaryReader.ReadString();
  673. string mpnValue = binaryReader.ReadString();
  674. MPN mpn = MPN.null_mpn;
  675. try
  676. {
  677. mpn = (MPN)Enum.Parse(typeof(MPN), mpnValue, true);
  678. }
  679. catch
  680. {
  681. return false;
  682. }
  683. if (mpn != MPN.null_mpn)
  684. {
  685. binaryReader.ReadString();
  686. }
  687. binaryReader.ReadString();
  688. int size = binaryReader.ReadInt32();
  689. for (int i = 0; i < size; i++)
  690. {
  691. string key = binaryReader.ReadString();
  692. int count = binaryReader.ReadInt32();
  693. byte[] data = binaryReader.ReadBytes(count);
  694. if (string.Equals(key, iconName, StringComparison.InvariantCultureIgnoreCase))
  695. {
  696. Texture2D tex = new Texture2D(1, 1, TextureFormat.RGBA32, false);
  697. tex.LoadImage(data);
  698. modItem.Icon = tex;
  699. break;
  700. }
  701. }
  702. }
  703. }
  704. catch (Exception e)
  705. {
  706. Utility.LogWarning($"Could not parse mod menu file '{modMenuFile}' because {e}");
  707. return false;
  708. }
  709. }
  710. return true;
  711. }
  712. public static bool ValidBG2MenuFile(ModItem modItem)
  713. {
  714. return accMpn.Contains(modItem.Category) && ValidBG2MenuFile(modItem.MenuFile);
  715. }
  716. public static bool ValidBG2MenuFile(string menu)
  717. {
  718. menu = Path.GetFileNameWithoutExtension(menu).ToLower();
  719. return !(menu.EndsWith("_del") || menu.Contains("zurashi") || menu.Contains("mekure")
  720. || menu.Contains("porori") || menu.Contains("moza") || menu.Contains("folder"));
  721. }
  722. public abstract class MenuItem
  723. {
  724. public string IconFile { get; set; }
  725. public Texture2D Icon { get; set; }
  726. }
  727. public class ModItem : MenuItem
  728. {
  729. public string MenuFile { get; set; }
  730. public string BaseMenuFile { get; set; }
  731. public string Name { get; set; }
  732. public string Category { get; set; }
  733. public float Priority { get; set; }
  734. public bool IsMod { get; set; }
  735. public bool IsOfficialMod { get; set; }
  736. public static ModItem OfficialMod(string menuFile) => new ModItem()
  737. {
  738. MenuFile = menuFile,
  739. IsMod = true,
  740. IsOfficialMod = true,
  741. Priority = 1000f
  742. };
  743. public static ModItem Mod(string menuFile) => new ModItem()
  744. {
  745. MenuFile = menuFile,
  746. IsMod = true
  747. };
  748. public ModItem() { }
  749. public ModItem(string menuFile) => MenuFile = menuFile;
  750. public override string ToString()
  751. {
  752. return IsOfficialMod ? $"{Path.GetFileName(MenuFile)}#{BaseMenuFile}" : MenuFile;
  753. }
  754. public static ModItem Deserialize(BinaryReader binaryReader)
  755. {
  756. return new ModItem()
  757. {
  758. MenuFile = binaryReader.ReadNullableString(),
  759. BaseMenuFile = binaryReader.ReadNullableString(),
  760. IconFile = binaryReader.ReadNullableString(),
  761. Name = binaryReader.ReadNullableString(),
  762. Category = binaryReader.ReadNullableString(),
  763. Priority = float.Parse(binaryReader.ReadNullableString()),
  764. IsMod = binaryReader.ReadBoolean(),
  765. IsOfficialMod = binaryReader.ReadBoolean()
  766. };
  767. }
  768. public void Serialize(BinaryWriter binaryWriter)
  769. {
  770. if (IsOfficialMod) return;
  771. binaryWriter.WriteNullableString(MenuFile);
  772. binaryWriter.WriteNullableString(BaseMenuFile);
  773. binaryWriter.WriteNullableString(IconFile);
  774. binaryWriter.WriteNullableString(Name);
  775. binaryWriter.WriteNullableString(Category);
  776. binaryWriter.WriteNullableString(Priority.ToString());
  777. binaryWriter.Write(IsMod);
  778. binaryWriter.Write(IsOfficialMod);
  779. }
  780. }
  781. public class MyRoomItem : MenuItem
  782. {
  783. public int ID { get; set; }
  784. public string PrefabName { get; set; }
  785. public override string ToString() => $"MYR_{ID}#{PrefabName}";
  786. }
  787. private class ModelInfo
  788. {
  789. public List<MaterialChange> MaterialChanges { get; set; } = new List<MaterialChange>();
  790. public string ModelFile { get; set; }
  791. }
  792. private struct MaterialChange
  793. {
  794. public int MaterialIndex { get; }
  795. public string MaterialFile { get; }
  796. public MaterialChange(int matno, string matf)
  797. {
  798. this.MaterialIndex = matno;
  799. this.MaterialFile = matf;
  800. }
  801. }
  802. }
  803. }