// ****************************************************************************************
// 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