123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using UnityEngine;
- namespace MeidoPhotoStudio.Plugin;
- public static class MenuFileUtility
- {
- // TODO: What's this for?
- 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(StringComparer.InvariantCultureIgnoreCase);
- private static byte[] fileBuffer;
- static MenuFileUtility()
- {
- AccMpn.UnionWith(MenuCategories.Skip(1));
- GameMain.Instance.StartCoroutine(CheckMenuDataBaseJob());
- }
- public static event EventHandler MenuFilesReadyChange;
- public static bool MenuFilesReady { get; private set; }
- public static byte[] ReadAFileBase(string filename)
- {
- using var aFileBase = GameUty.FileOpen(filename);
- if (!aFileBase.IsValid() || aFileBase.GetSize() is 0)
- {
- Utility.LogError($"AFileBase '{filename}' is invalid");
- return null;
- }
- // INFO: Don't really understand what this does.
- ref var buffer = ref GetFileBuffer(aFileBase.GetSize());
- aFileBase.Read(ref buffer, aFileBase.GetSize());
- return buffer;
- }
- public static byte[] ReadOfficialMod(string filename)
- {
- using var fileStream = new FileStream(filename, FileMode.Open);
- if (fileStream.Length is 0)
- {
- Utility.LogWarning($"Mod menu file '{filename}' is invalid");
- return null;
- }
- ref var buffer = ref GetFileBuffer(fileStream.Length);
- fileStream.Read(buffer, 0, (int)fileStream.Length);
- return buffer;
- }
- public static bool ParseNativeMenuFile(int menuIndex, ModItem modItem)
- {
- var 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() is not "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)
- {
- var numberOfProps = binaryReader.ReadByte();
- var menuPropString = string.Empty;
- if (numberOfProps is 0)
- break;
- for (var i = 0; i < numberOfProps; i++)
- menuPropString = $"{menuPropString}\"{binaryReader.ReadString()}\"";
- if (string.IsNullOrEmpty(menuPropString))
- continue;
- var header = UTY.GetStringCom(menuPropString);
- var menuProps = UTY.GetStringList(menuPropString);
- if (header is "end")
- break;
- if (header is "category")
- {
- modItem.Category = menuProps[1];
- if (!AccMpn.Contains(modItem.Category))
- return;
- }
- else if (header is "icons" or "icon")
- {
- modItem.IconFile = menuProps[1];
- break;
- }
- else if (header is "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() is not "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 is not 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();
- var 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"));
- }
- 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 is null)
- fileBuffer = new byte[Math.Max(500000, size)];
- else if (fileBuffer.Length < size)
- fileBuffer = new byte[size];
- return ref fileBuffer;
- }
- }
|