123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- using System;
- using System.Text;
- using System.IO;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- namespace COM3D2.MeidoPhotoStudio.Plugin
- {
- public static class MenuFileUtility
- {
- private static byte[] fileBuffer;
- public const string noCategory = "noCategory";
- public static readonly string[] MenuCategories =
- {
- noCategory, "acchat", "headset", "wear", "skirt", "onepiece", "mizugi", "bra", "panz", "stkg", "shoes",
- "acckami", "megane", "acchead", "acchana", "accmimi", "glove", "acckubi", "acckubiwa", "acckamisub",
- "accnip", "accude", "accheso", "accashi", "accsenaka", "accshippo", "accxxx"
- };
- private static readonly HashSet<string> accMpn = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
- public static event EventHandler MenuFilesReadyChange;
- public static bool MenuFilesReady { get; private set; }
- static MenuFileUtility()
- {
- accMpn.UnionWith(MenuCategories.Skip(1));
- GameMain.Instance.StartCoroutine(CheckMenuDataBaseJob());
- }
- private static IEnumerator CheckMenuDataBaseJob()
- {
- if (MenuFilesReady) yield break;
- while (!GameMain.Instance.MenuDataBase.JobFinished()) yield return null;
- MenuFilesReady = true;
- MenuFilesReadyChange?.Invoke(null, EventArgs.Empty);
- }
- private static ref byte[] GetFileBuffer(long size)
- {
- if (fileBuffer == null) fileBuffer = new byte[Math.Max(500000, size)];
- else if (fileBuffer.Length < size) fileBuffer = new byte[size];
- return ref fileBuffer;
- }
- public static byte[] ReadAFileBase(string filename)
- {
- using AFileBase aFileBase = GameUty.FileOpen(filename);
- if (aFileBase.IsValid() && aFileBase.GetSize() != 0)
- {
- ref byte[] buffer = ref GetFileBuffer(aFileBase.GetSize());
- aFileBase.Read(ref buffer, aFileBase.GetSize());
- return buffer;
- }
- Utility.LogError($"AFileBase '{filename}' is invalid");
- return null;
- }
- public static byte[] ReadOfficialMod(string filename)
- {
- using var fileStream = new FileStream(filename, FileMode.Open);
- if (fileStream.Length != 0)
- {
- ref byte[] buffer = ref GetFileBuffer(fileStream.Length);
- fileStream.Read(buffer, 0, (int) fileStream.Length);
- return buffer;
- }
- Utility.LogWarning($"Mod menu file '{filename}' is invalid");
- return null;
- }
- public static bool ParseNativeMenuFile(int menuIndex, ModItem modItem)
- {
- MenuDataBase menuDataBase = GameMain.Instance.MenuDataBase;
- menuDataBase.SetIndex(menuIndex);
- if (menuDataBase.GetBoDelOnly()) return false;
- modItem.Category = menuDataBase.GetCategoryMpnText();
- if (!accMpn.Contains(modItem.Category)) return false;
- modItem.MenuFile = menuDataBase.GetMenuFileName().ToLower();
- if (!ValidBG2MenuFile(modItem.MenuFile)) return false;
- modItem.Name = menuDataBase.GetMenuName();
- modItem.IconFile = menuDataBase.GetIconS();
- modItem.Priority = menuDataBase.GetPriority();
- return true;
- }
- public static void ParseMenuFile(string menuFile, ModItem modItem)
- {
- if (!ValidBG2MenuFile(menuFile)) return;
- byte[] buffer;
- try { buffer = ReadAFileBase(menuFile); }
- catch (Exception e)
- {
- Utility.LogError($"Could not read menu file '{menuFile}' because {e.Message}");
- return ;
- }
- try
- {
- using var binaryReader = new BinaryReader(new MemoryStream(buffer), Encoding.UTF8);
- if (binaryReader.ReadString() != "CM3D2_MENU") return;
- binaryReader.ReadInt32(); // file version
- binaryReader.ReadString(); // txt path
- modItem.Name = binaryReader.ReadString(); // name
- binaryReader.ReadString(); // category
- binaryReader.ReadString(); // description
- binaryReader.ReadInt32(); // idk (as long)
- while (true)
- {
- int numberOfProps = binaryReader.ReadByte();
- var menuPropString = string.Empty;
- if (numberOfProps == 0) break;
- for (var i = 0; i < numberOfProps; i++)
- {
- menuPropString = $"{menuPropString}\"{binaryReader.ReadString()}\"";
- }
- if (string.IsNullOrEmpty(menuPropString)) continue;
- var header = UTY.GetStringCom(menuPropString);
- string[] menuProps = UTY.GetStringList(menuPropString);
- if (header == "end") break;
- if (header == "category")
- {
- modItem.Category = menuProps[1];
- if (!accMpn.Contains(modItem.Category)) return;
- }
- else if (header == "icons" || header == "icon")
- {
- modItem.IconFile = menuProps[1];
- break;
- }
- else if (header == "priority") modItem.Priority = float.Parse(menuProps[1]);
- }
- }
- catch (Exception e)
- {
- Utility.LogWarning($"Could not parse menu file '{menuFile}' because {e.Message}");
- }
- }
- public static bool ParseModMenuFile(string modMenuFile, ModItem modItem)
- {
- if (!ValidBG2MenuFile(modMenuFile)) return false;
- byte[] modBuffer;
- try { modBuffer = ReadOfficialMod(modMenuFile); }
- catch (Exception e)
- {
- Utility.LogError($"Could not read mod menu file '{modMenuFile} because {e.Message}'");
- return false;
- }
- try
- {
- using var binaryReader = new BinaryReader(new MemoryStream(modBuffer), Encoding.UTF8);
- if (binaryReader.ReadString() != "CM3D2_MOD") return false;
- binaryReader.ReadInt32();
- var iconName = binaryReader.ReadString();
- var baseItemPath = binaryReader.ReadString().Replace(":", " ");
- modItem.BaseMenuFile = Path.GetFileName(baseItemPath);
- modItem.Name = binaryReader.ReadString();
- modItem.Category = binaryReader.ReadString();
- if (!accMpn.Contains(modItem.Category)) return false;
- binaryReader.ReadString();
- var mpnValue = binaryReader.ReadString();
- var mpn = MPN.null_mpn;
- try { mpn = (MPN) Enum.Parse(typeof(MPN), mpnValue, true); }
- catch { /* ignored */ }
- if (mpn != MPN.null_mpn) binaryReader.ReadString();
- binaryReader.ReadString();
- var entryCount = binaryReader.ReadInt32();
- for (var i = 0; i < entryCount; i++)
- {
- var key = binaryReader.ReadString();
- var count = binaryReader.ReadInt32();
- byte[] data = binaryReader.ReadBytes(count);
- if (!string.Equals(key, iconName, StringComparison.InvariantCultureIgnoreCase)) continue;
- var tex = new Texture2D(1, 1, TextureFormat.RGBA32, false);
- tex.LoadImage(data);
- modItem.Icon = tex;
- break;
- }
- }
- catch (Exception e)
- {
- Utility.LogWarning($"Could not parse mod menu file '{modMenuFile}' because {e}");
- return false;
- }
- return true;
- }
- public static bool ValidBG2MenuFile(ModItem modItem)
- => accMpn.Contains(modItem.Category) && ValidBG2MenuFile(modItem.MenuFile);
- public static bool ValidBG2MenuFile(string menu)
- {
- menu = Path.GetFileNameWithoutExtension(menu).ToLower();
- return !(menu.EndsWith("_del") || menu.Contains("zurashi") || menu.Contains("mekure")
- || menu.Contains("porori") || menu.Contains("moza") || menu.Contains("folder"));
- }
- }
- }
|