// **************************************************************************************** // PresetPrepare Plugin by dynms1337 // Licensed under CC BY // **************************************************************************************** using System; using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; using SimpleJSON; using System.Linq; using System.Globalization; using MacGruber; using UnityEngine.Events; using MeshVR; using MVR.FileManagementSecure; namespace dynms1337 { public static class dUtil { // ********************************* PARAMETERS ****************************************** public const bool isDebug = false; public const string PLUGIN_NAME = "CharacterFusioner"; // *************************************************************************** public static void info(string mes) { SuperController.LogMessage($"I {PLUGIN_NAME} {mes}"); } public static void debug(string mes) { if (isDebug) SuperController.LogMessage($"D {PLUGIN_NAME} {mes}"); } public static void error(string mes) { SuperController.LogError($"E {PLUGIN_NAME} {mes}"); } public static UIDynamic CreateSpacer(MVRScript plugin, float height = 10f, bool rightSide = false) { UIDynamic spacer = plugin.CreateSpacer(rightSide); spacer.height = height; return spacer; } public static JSONStorableString CreateText(MVRScript plugin, string textName, string text, float height = 60f, bool rightSide = false) { JSONStorableString ret = new JSONStorableString(textName, text); UIDynamic helpTextfield = plugin.CreateTextField(ret, rightSide); helpTextfield.height = height; return ret; } public static JSONStorableBool CreateToggle(MVRScript plugin, string toggleName, bool defaultValue, bool rightSide = false, JSONStorableBool.SetBoolCallback callback = null) { if (callback == null) callback = (bool b) => { }; var ret = new JSONStorableBool(toggleName, defaultValue, callback); plugin.RegisterBool(ret); var toggle = plugin.CreateToggle(ret, rightSide); toggle.label = toggleName; return ret; } internal static string path2filename(string appearancePath, string prefix = "Preset_", string sufix = ".vap") { var ret = appearancePath.Split(new char[] { '\\', '/' }).Last(); if (prefix != "") ret = ret.Replace(prefix, ""); if (sufix != "") ret = ret.Replace(sufix, ""); return ret; } public static UIDynamicSlider CreateSlider(MVRScript plugin, string sliderName, float defaultValue, float minValue, float maxValue, bool constrain = true, bool interactable = true, bool rightSide = false, bool registerStorableIfCreate = true, JSONStorableFloat.SetFloatCallback callback = null, JSONStorableFloat jsf = null) { if (callback == null) callback = (float f) => { }; if (jsf == null) { jsf = new JSONStorableFloat(sliderName, defaultValue, callback, minValue, maxValue, constrain, interactable); if (registerStorableIfCreate) plugin.RegisterFloat(jsf); } UIDynamicSlider slider = plugin.CreateSlider(jsf, rightSide); return slider; } public static UIDynamicButton CreateButton(MVRScript plugin, string label, Color? normalColor = null, bool rightSide = false, UnityAction callback = null) { if (callback == null) callback = () => { }; var uiButton = Utils.SetupButton(plugin, label, callback: callback, rightSide: rightSide); var colors = uiButton.button.colors; colors.normalColor = normalColor ?? colors.normalColor; uiButton.button.colors = colors; return uiButton; } /// /// rightSide is on the same side as the previously created UI ? /// public static JSONStorableStringChooser CreateChooser(MVRScript plugin, string label, List choicesList, string startingValue = null, string paramName = null, float popupPanelHeight = 1100f, bool popupAlwaysOpen = false, bool rightSide = false, JSONStorableStringChooser.SetJSONStringCallback callback = null) { if (paramName == null) paramName = label; if (callback == null) callback = s => { }; JSONStorableStringChooser ret = new JSONStorableStringChooser(paramName, choicesList: choicesList, startingValue: startingValue, displayName: label, callback: callback); ret.storeType = JSONStorableParam.StoreType.Full; plugin.RegisterStringChooser(ret); UIDynamicPopup dp = plugin.CreateScrollablePopup(ret, rightSide: rightSide); dp.popupPanelHeight = popupPanelHeight; dp.popup.alwaysOpen = popupAlwaysOpen; //skinchooser.val = "hip"; return ret; } public const string DIR_APPEARANCES = @"Custom\Atom\Person\Appearance"; public static string create_appearancePresetPath(string charaName, string extention = "vap") { return $"{DIR_APPEARANCES}\\Preset_{charaName}.{extention}"; } /// /// Get the thumbnail path for a given appearance path, preserving subdirectory structure /// public static string get_thumbnailPath(string appearancePath, string extension = "jpg") { // Replace the .vap extension with the desired extension string path = appearancePath; if (path.EndsWith(".vap")) { path = path.Substring(0, path.Length - 4) + "." + extension; } else { // If no .vap extension, just append the new extension path = path + "." + extension; } return path; } /// /// Read values from ".vap" file. /// Although vap is JSON, it is not a strict format and cannot be parsed, so it is converted to JSON that can be interpreted by low-level processing and then parsed. /// /// /// lowest array key. ex. hair, morphs, ... /// public static JSONArray parseVapArray(string json, string tag, string filepath = "json") { var ret = new JSONArray(); if (json == "") { dUtil.error($"File is Empty : {filepath}"); return ret; } int start = json.IndexOf(tag) - 1; int end = Math.Max(json.IndexOf("]\r\n", start), json.IndexOf("]\n", start)); // b/c IndexOf return -1 if not found. if (!(0 <= start & start < end)) { dUtil.error($"parseVapArray > '{tag}' not found in '{filepath}'. start:{start.ToString()}/end:{end.ToString()}"); return ret; } json = json.Substring(start, end - start + 1); var _ret = (JSONArray)JSON.Parse(json); if (_ret == null) { dUtil.error($"parseVapArray > Cannot parse json :'{tag}' in '{filepath}'."); return ret; } return _ret; } /// /// Read values from ".vap" file. /// Although vap is JSON, it is not a strict format and cannot be parsed, so it is converted to JSON that can be interpreted by low-level processing and then parsed. /// /// /// lowest dictionary id. ex. rescaleObject, ... /// for error message /// public static JSONNode parseVapDictionary(string json, string tag, string filepath = "json") { //var json = readFile(filepath); int start = json.IndexOf($"\"id\" : \"{tag}") - 1; int end = json.IndexOf("}", start); json = "{" + json.Substring(start, end - start + 1); var ret = JSON.Parse(json); if (ret == null) { string mes = $"'${tag}' not found in '${filepath}'."; dUtil.error(mes); throw new Exception(mes); } return ret; } public static string parseVapLine(string json, string key, string filepath) { //var json = readFile(filepath); JSONNode ret = null; foreach (string line in json.Split('\n')) { if (!line.Contains($"\"{key}\" :")) continue; ret = line; break; } if (ret == null) { string mes = $"'${key}' not found in '${filepath}'."; dUtil.error(mes); throw new Exception(mes); } return ret; } /// /// Scan all packages for the file if filepath not exist on real directory. /// /// /// public static string findFilepath(string filepath) { if (FileManagerSecure.FileExists(filepath)) return filepath; // Scan all packages for the file var list = FileManagerSecure.GetShortCutsForDirectory(FileManagerSecure.GetDirectoryName(filepath)).ToList(); var filename = path2filename(filepath, prefix: "", sufix: ""); // DON'T inline. Unity crashes if call in where(). list = list.Where(sc => { return FileManagerSecure.FileExists($"{sc.path}\\{filename}"); }).ToList(); if (list.Count == 0) { string mes = $"findFilepath > path '{filepath} not found amoung all packages."; dUtil.error(mes); throw new Exception(mes); } return $"{list.First().path}\\{filename}"; } public static string readFile(string filepath) { filepath = findFilepath(filepath); string ret = ""; try { ret = SuperController.singleton.ReadFileIntoString(filepath); } catch (Exception e) { dUtil.error($"Error while reading`{filepath}`.\n{e.Message}"); } // not work b/c error squeeze&clashed. return ret; } public static void SaveJSON(string filepath, JSONClass jc) { SuperController.singleton.SaveJSON(jc, filepath); } public static void LoadTexture(string url, ImageLoaderThreaded.ImageLoaderCallback callback) { if (ImageLoaderThreaded.singleton == null) { error($"LoadTexture > ImageLoaderThreaded not ready."); return; } url = findFilepath(url); ImageLoaderThreaded.QueuedImage queuedImage = new ImageLoaderThreaded.QueuedImage(); queuedImage.imgPath = url; queuedImage.forceReload = true; queuedImage.skipCache = true; queuedImage.compress = true; queuedImage.createMipMaps = false; queuedImage.isNormalMap = false; queuedImage.linear = false; queuedImage.createAlphaFromGrayscale = false; queuedImage.createNormalFromBump = false; queuedImage.bumpStrength = 0f; queuedImage.isThumbnail = false; queuedImage.fillBackground = false; queuedImage.invert = false; queuedImage.callback = callback; Texture2D tex = ImageLoaderThreaded.singleton.GetCachedThumbnail(url); if (tex != null) { queuedImage.tex = tex; callback.Invoke(queuedImage); } else ImageLoaderThreaded.singleton.QueueThumbnail(queuedImage); } public static Texture2D ResizeTexture(Texture2D srcTexture, int newWidth, int newHeight) { var resizedTexture = new Texture2D(newWidth, newHeight); Graphics.ConvertTexture(srcTexture, resizedTexture); return resizedTexture; } // Call to remove a list of UI elements before rebuilding your UI. public static void RemoveUIElements(MVRScript script, List menuElements) { for (int i = 0; i < menuElements.Count; ++i) { if (menuElements[i] is GameObject) GameObject.Destroy(menuElements[i] as GameObject); // added else if (menuElements[i] is JSONStorableParam) { JSONStorableParam jsp = menuElements[i] as JSONStorableParam; if (jsp is JSONStorableFloat) script.RemoveSlider(jsp as JSONStorableFloat); else if (jsp is JSONStorableBool) script.RemoveToggle(jsp as JSONStorableBool); else if (jsp is JSONStorableColor) script.RemoveColorPicker(jsp as JSONStorableColor); else if (jsp is JSONStorableString) script.RemoveTextField(jsp as JSONStorableString); else if (jsp is JSONStorableStringChooser) { // Workaround for VaM not cleaning its panels properly. JSONStorableStringChooser jssc = jsp as JSONStorableStringChooser; RectTransform popupPanel = jssc.popup?.popupPanel; script.RemovePopup(jssc); if (popupPanel != null) UnityEngine.Object.Destroy(popupPanel.gameObject); } } else if (menuElements[i] is UIDynamic) { UIDynamic uid = menuElements[i] as UIDynamic; if (uid is UIDynamicButton) script.RemoveButton(uid as UIDynamicButton); else if (uid is UIDynamicUtils) script.RemoveSpacer(uid); else if (uid is UIDynamicSlider) script.RemoveSlider(uid as UIDynamicSlider); else if (uid is UIDynamicToggle) script.RemoveToggle(uid as UIDynamicToggle); else if (uid is UIDynamicColorPicker) script.RemoveColorPicker(uid as UIDynamicColorPicker); else if (uid is UIDynamicTextField) script.RemoveTextField(uid as UIDynamicTextField); else if (uid is UIDynamicPopup) { // Workaround for VaM not cleaning its panels properly. UIDynamicPopup uidp = uid as UIDynamicPopup; RectTransform popupPanel = uidp.popup?.popupPanel; script.RemovePopup(uidp); if (popupPanel != null) UnityEngine.Object.Destroy(popupPanel.gameObject); } else script.RemoveSpacer(uid); } } menuElements.Clear(); } } }