using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using GWYF_NewClothing.Patches;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace GWYF_NewClothing
{
internal static class AssetBundleLoader
{
public static GameObject LoadModel(string bundlePath, string assetName)
{
try
{
if (!File.Exists(bundlePath))
{
Debug.LogError((object)("[GWYF] Bundle file not found: " + bundlePath));
return null;
}
AssetBundle val = AssetBundle.LoadFromFile(bundlePath);
if ((Object)(object)val == (Object)null)
{
Debug.LogError((object)("[GWYF] Failed to load bundle: " + bundlePath));
return null;
}
GameObject val2 = val.LoadAsset<GameObject>(assetName);
val.Unload(false);
if ((Object)(object)val2 == (Object)null)
{
Debug.LogError((object)("[GWYF] Asset '" + assetName + "' not found in bundle: " + bundlePath));
return null;
}
GameObject obj = Object.Instantiate<GameObject>(val2);
((Object)obj).hideFlags = (HideFlags)61;
obj.SetActive(false);
Object.DontDestroyOnLoad((Object)(object)obj);
return obj;
}
catch (Exception ex)
{
Debug.LogError((object)("[GWYF] Bundle load failed: " + bundlePath + " — " + ex.Message));
return null;
}
}
}
public static class CosmeticRegistry
{
private static CosmeticData[] _allCosmetics = Array.Empty<CosmeticData>();
private static readonly List<CosmeticEntry> _pendingVanilla = new List<CosmeticEntry>();
private static int _nextId;
private static readonly List<string> _registeredDirs = new List<string>();
private static string _lastModelsDir = "";
private static string _lastTexDir = "";
private static string _lastBundlesDir = "";
public static CosmeticData[] AllCosmetics
{
get
{
ResolvePending();
return _allCosmetics;
}
}
public static void RegisterFrom(string pluginDir)
{
if (string.IsNullOrEmpty(pluginDir) || _registeredDirs.Contains(pluginDir))
{
return;
}
_registeredDirs.Add(pluginDir);
string path = Path.Combine(pluginDir, "cosmetics.json");
if (!File.Exists(path))
{
Debug.Log((object)("[MoreCosmetics] No cosmetics.json in " + pluginDir + ", skipping."));
return;
}
try
{
List<CosmeticEntry> list = ParseFromJson(File.ReadAllText(path));
string text = Path.Combine(pluginDir, "models");
string text2 = Path.Combine(pluginDir, "textures");
string text3 = Path.Combine(pluginDir, "bundles");
try
{
Directory.CreateDirectory(text);
}
catch
{
}
try
{
Directory.CreateDirectory(text2);
}
catch
{
}
try
{
Directory.CreateDirectory(text3);
}
catch
{
}
List<CosmeticData> list2 = new List<CosmeticData>();
foreach (CosmeticEntry item in list)
{
CosmeticData val = CreateCosmetic(item, list2.Count, text, text2, text3);
if ((Object)(object)val != (Object)null)
{
list2.Add(val);
}
else if (item != null && (item.model?.IsVanilla).GetValueOrDefault())
{
_pendingVanilla.Add(item);
}
}
if (_allCosmetics.Length == 0)
{
_allCosmetics = list2.ToArray();
}
else
{
List<CosmeticData> list3 = new List<CosmeticData>(_allCosmetics);
list3.AddRange(list2);
_allCosmetics = list3.ToArray();
}
Debug.Log((object)$"[MoreCosmetics] Registered {list2.Count} cosmetics from {Path.GetFileName(pluginDir)}");
if (_pendingVanilla.Count > 0)
{
Debug.Log((object)$"[MoreCosmetics] {_pendingVanilla.Count} vanilla-model entries deferred (not in cache yet)");
}
}
catch (Exception ex)
{
Debug.LogError((object)("[MoreCosmetics] Failed to register cosmetics from " + pluginDir + ": " + ex.Message));
}
}
internal static void ResolvePending()
{
if (_pendingVanilla.Count == 0 || !IsCacheReady())
{
return;
}
List<CosmeticData> list = new List<CosmeticData>();
List<CosmeticEntry> list2 = new List<CosmeticEntry>();
foreach (CosmeticEntry item in _pendingVanilla)
{
CosmeticData val = CreateCosmetic(item, list.Count, _lastModelsDir, _lastTexDir, _lastBundlesDir);
if ((Object)(object)val != (Object)null)
{
list.Add(val);
}
else
{
list2.Add(item);
}
}
if (list.Count > 0)
{
List<CosmeticData> list3 = new List<CosmeticData>(_allCosmetics);
list3.AddRange(list);
_allCosmetics = list3.ToArray();
}
_pendingVanilla.Clear();
_pendingVanilla.AddRange(list2);
}
private static bool IsCacheReady()
{
try
{
object obj = typeof(CosmeticDataManager).GetField("_isInitialized", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);
return obj is bool && (bool)obj;
}
catch
{
return false;
}
}
private static CosmeticData? CreateCosmetic(CosmeticEntry entry, int index, string modelsDir, string texturesDir, string bundlesDir)
{
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Expected O, but got Unknown
//IL_0140: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
//IL_011a: Unknown result type (might be due to invalid IL or missing references)
//IL_019b: Unknown result type (might be due to invalid IL or missing references)
//IL_019c: Unknown result type (might be due to invalid IL or missing references)
//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
_lastModelsDir = modelsDir;
_lastTexDir = texturesDir;
_lastBundlesDir = bundlesDir;
if (string.IsNullOrEmpty(entry.name) || string.IsNullOrEmpty(entry.type))
{
return null;
}
if (!Enum.TryParse<CosmeticType>(entry.type, ignoreCase: true, out CosmeticType result))
{
return null;
}
CosmeticRarity result2 = (CosmeticRarity)0;
if (!string.IsNullOrEmpty(entry.rarity))
{
Enum.TryParse<CosmeticRarity>(entry.rarity, ignoreCase: true, out result2);
}
if (entry.model == null)
{
return null;
}
GameObject val = ResolveModel(entry, modelsDir, bundlesDir);
if ((Object)(object)val == (Object)null)
{
return null;
}
Material val2 = new Material(Shader.Find((!string.IsNullOrEmpty(entry.shader)) ? entry.shader : "01_GWYF/GlobalShader") ?? Shader.Find("Standard") ?? Shader.Find("Diffuse"));
((Object)val2).name = entry.name + "_Mat";
if (!string.IsNullOrEmpty(entry.texture))
{
Texture2D val3 = TextureLoader.Load(Path.Combine(texturesDir, entry.texture));
if ((Object)(object)val3 != (Object)null)
{
val2.SetTexture("_MainTex", (Texture)(object)val3);
val2.SetTexture("_BaseMap", (Texture)(object)val3);
val2.color = Color.white;
}
else
{
val2.color = GetTintColor(entry.tint);
}
}
else
{
val2.color = GetTintColor(entry.tint);
}
CosmeticData obj = ScriptableObject.CreateInstance<CosmeticData>();
((Object)obj).name = entry.name;
obj.cosmeticId = PluginConfig.CosmeticIdStart.Value + _nextId++;
obj.cosmeticName = entry.name;
obj.description = entry.description ?? "";
obj.cosmeticType = result;
obj.rarity = result2;
obj.cosmeticModel = val;
obj.cosmeticMaterial = val2;
return obj;
}
private static GameObject? ResolveModel(CosmeticEntry entry, string modelsDir, string bundlesDir)
{
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Expected O, but got Unknown
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Expected O, but got Unknown
if (entry.model.IsVanilla)
{
return CloneVanillaModel(entry.model.vanilla);
}
if (entry.model.IsObj)
{
Mesh val = ObjImporter.Import(Path.Combine(modelsDir, entry.model.obj));
if ((Object)(object)val == (Object)null)
{
return null;
}
GameObject val2 = new GameObject(entry.name + "_Model")
{
hideFlags = (HideFlags)61
};
Object.DontDestroyOnLoad((Object)val2);
val2.AddComponent<MeshFilter>().sharedMesh = val;
val2.AddComponent<MeshRenderer>();
return val2;
}
if (entry.model.IsBundle)
{
return AssetBundleLoader.LoadModel(Path.Combine(bundlesDir, entry.model.bundle), entry.model.asset);
}
return null;
}
private static GameObject? CloneVanillaModel(string cosmeticName)
{
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Expected O, but got Unknown
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Expected O, but got Unknown
try
{
if (!(typeof(CosmeticDataManager).GetField("_cosmeticCache", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) is Dictionary<int, CosmeticData> dictionary))
{
return null;
}
foreach (KeyValuePair<int, CosmeticData> item in dictionary)
{
CosmeticData value = item.Value;
if (!((Object)(object)value == (Object)null) && !((Object)(object)value.cosmeticModel == (Object)null) && string.Equals(value.cosmeticName, cosmeticName, StringComparison.OrdinalIgnoreCase))
{
MeshFilter component = value.cosmeticModel.GetComponent<MeshFilter>();
if (!((Object)(object)component == (Object)null) && !((Object)(object)component.sharedMesh == (Object)null))
{
GameObject val = new GameObject(cosmeticName + "_Clone")
{
hideFlags = (HideFlags)61
};
Object.DontDestroyOnLoad((Object)val);
val.AddComponent<MeshFilter>().sharedMesh = Object.Instantiate<Mesh>(component.sharedMesh);
val.AddComponent<MeshRenderer>();
return val;
}
}
}
return null;
}
catch
{
return null;
}
}
private static Color GetTintColor(float[] tint)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (tint == null || tint.Length < 3)
{
return Color.white;
}
return new Color(Mathf.Clamp01(tint[0]), Mathf.Clamp01(tint[1]), Mathf.Clamp01(tint[2]));
}
private static List<CosmeticEntry> ParseFromJson(string json)
{
List<CosmeticEntry> list = new List<CosmeticEntry>();
try
{
json = json.Trim();
if (json.StartsWith("{"))
{
if (!TinyJson.ParseObject(json).TryGetValue("cosmetics", out object value) || !(value is string text))
{
return list;
}
json = text.Trim();
}
if (!json.StartsWith("["))
{
return list;
}
foreach (Dictionary<string, object> item in TinyJson.ParseArray(json))
{
CosmeticEntry cosmeticEntry = new CosmeticEntry();
if (item.TryGetValue("name", out var value2) && value2 is string name)
{
cosmeticEntry.name = name;
}
if (item.TryGetValue("type", out var value3) && value3 is string type)
{
cosmeticEntry.type = type;
}
if (item.TryGetValue("rarity", out var value4) && value4 is string rarity)
{
cosmeticEntry.rarity = rarity;
}
if (item.TryGetValue("description", out var value5) && value5 is string description)
{
cosmeticEntry.description = description;
}
if (item.TryGetValue("texture", out var value6) && value6 is string texture)
{
cosmeticEntry.texture = texture;
}
if (item.TryGetValue("shader", out var value7) && value7 is string shader)
{
cosmeticEntry.shader = shader;
}
if (item.TryGetValue("tint", out var value8))
{
if (value8 is float[] tint)
{
cosmeticEntry.tint = tint;
}
else if (value8 is List<float> list2)
{
cosmeticEntry.tint = list2.ToArray();
}
}
if (item.TryGetValue("model", out var value9))
{
cosmeticEntry.model = new ModelRef();
if (value9 is string vanilla)
{
cosmeticEntry.model.vanilla = vanilla;
}
else if (value9 is Dictionary<string, object> dictionary)
{
if (dictionary.TryGetValue("vanilla", out var value10) && value10 is string vanilla2)
{
cosmeticEntry.model.vanilla = vanilla2;
}
if (dictionary.TryGetValue("obj", out var value11) && value11 is string obj)
{
cosmeticEntry.model.obj = obj;
}
if (dictionary.TryGetValue("bundle", out var value12) && value12 is string bundle)
{
cosmeticEntry.model.bundle = bundle;
}
if (dictionary.TryGetValue("asset", out var value13) && value13 is string asset)
{
cosmeticEntry.model.asset = asset;
}
}
}
if (!string.IsNullOrEmpty(cosmeticEntry.name))
{
list.Add(cosmeticEntry);
}
}
}
catch (Exception ex)
{
Debug.LogError((object)("[MoreCosmetics] JSON parse error: " + ex.Message));
}
return list;
}
}
[Serializable]
public class CosmeticEntry
{
public string name;
public string type;
public string rarity;
public string description;
public ModelRef model;
public string texture;
public float[] tint;
public string shader;
}
[Serializable]
public class ModelRef
{
public string vanilla;
public string obj;
public string bundle;
public string asset;
public bool IsVanilla => !string.IsNullOrEmpty(vanilla);
public bool IsObj => !string.IsNullOrEmpty(obj);
public bool IsBundle => !string.IsNullOrEmpty(bundle);
}
internal static class ObjImporter
{
private readonly struct VertexTriplet
{
public readonly int posIndex;
public readonly int uvIndex;
public readonly int nrmIndex;
public VertexTriplet(int pos, int uv, int nrm)
{
posIndex = pos;
uvIndex = uv;
nrmIndex = nrm;
}
public override bool Equals(object obj)
{
if (obj is VertexTriplet vertexTriplet && posIndex == vertexTriplet.posIndex && uvIndex == vertexTriplet.uvIndex)
{
return nrmIndex == vertexTriplet.nrmIndex;
}
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(posIndex, uvIndex, nrmIndex);
}
}
public static Mesh Import(string path)
{
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
//IL_01f7: Expected O, but got Unknown
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
if (!File.Exists(path))
{
Debug.LogError((object)("[GWYF] OBJ file not found: " + path));
return null;
}
string[] array = File.ReadAllLines(path);
List<Vector3> list = new List<Vector3>();
List<Vector2> list2 = new List<Vector2>();
List<Vector3> list3 = new List<Vector3>();
List<VertexTriplet> list4 = new List<VertexTriplet>();
List<(int, int, int)> list5 = new List<(int, int, int)>();
string[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
string text = array2[i].Trim();
if (text.Length == 0 || text[0] == '#')
{
continue;
}
string[] array3 = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (array3.Length >= 2)
{
switch (array3[0])
{
case "v":
list.Add(ParseVector3(array3, 1));
break;
case "vt":
list2.Add(ParseVector2(array3, 1));
break;
case "vn":
list3.Add(ParseVector3(array3, 1));
break;
case "f":
ParseFace(array3, list4, list5);
break;
}
}
}
Dictionary<VertexTriplet, int> dict = new Dictionary<VertexTriplet, int>();
List<Vector3> list6 = new List<Vector3>();
List<Vector2> list7 = new List<Vector2>();
List<Vector3> list8 = new List<Vector3>();
List<int> list9 = new List<int>();
foreach (var item4 in list5)
{
int item = item4.Item1;
int item2 = item4.Item2;
int item3 = item4.Item3;
VertexTriplet vt = list4[item];
VertexTriplet vt2 = list4[item2];
VertexTriplet vt3 = list4[item3];
list9.Add(GetOrAdd(vt, dict, list6, list7, list8, list, list2, list3));
list9.Add(GetOrAdd(vt2, dict, list6, list7, list8, list, list2, list3));
list9.Add(GetOrAdd(vt3, dict, list6, list7, list8, list, list2, list3));
}
Mesh val = new Mesh();
((Object)val).name = Path.GetFileNameWithoutExtension(path);
val.indexFormat = (IndexFormat)(list6.Count > 65535);
val.SetVertices(list6);
val.SetTriangles(list9, 0);
if (list7.Count == list6.Count)
{
val.SetUVs(0, list7);
}
if (list8.Count == list6.Count)
{
val.SetNormals(list8);
}
else
{
val.RecalculateNormals();
}
val.RecalculateBounds();
val.RecalculateTangents();
Debug.Log((object)$"[GWYF] Imported OBJ '{Path.GetFileName(path)}': {list6.Count} verts, {list9.Count / 3} tris.");
return val;
}
private static int GetOrAdd(VertexTriplet vt, Dictionary<VertexTriplet, int> dict, List<Vector3> verts, List<Vector2> uvs, List<Vector3> norms, List<Vector3> posSrc, List<Vector2> uvSrc, List<Vector3> nrmSrc)
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
if (dict.TryGetValue(vt, out var value))
{
return value;
}
int result = (dict[vt] = verts.Count);
verts.Add((vt.posIndex > 0 && vt.posIndex <= posSrc.Count) ? posSrc[vt.posIndex - 1] : Vector3.zero);
if (vt.uvIndex > 0 && vt.uvIndex <= uvSrc.Count)
{
uvs.Add(uvSrc[vt.uvIndex - 1]);
}
if (vt.nrmIndex > 0 && vt.nrmIndex <= nrmSrc.Count)
{
norms.Add(nrmSrc[vt.nrmIndex - 1]);
}
return result;
}
private static Vector3 ParseVector3(string[] parts, int start)
{
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
float result = 0f;
float result2 = 0f;
float result3 = 0f;
if (parts.Length > start)
{
float.TryParse(parts[start], NumberStyles.Float, CultureInfo.InvariantCulture, out result);
}
if (parts.Length > start + 1)
{
float.TryParse(parts[start + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out result2);
}
if (parts.Length > start + 2)
{
float.TryParse(parts[start + 2], NumberStyles.Float, CultureInfo.InvariantCulture, out result3);
}
return new Vector3(result, result2, result3);
}
private static Vector2 ParseVector2(string[] parts, int start)
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
float result = 0f;
float result2 = 0f;
if (parts.Length > start)
{
float.TryParse(parts[start], NumberStyles.Float, CultureInfo.InvariantCulture, out result);
}
if (parts.Length > start + 1)
{
float.TryParse(parts[start + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out result2);
}
return new Vector2(result, result2);
}
private static void ParseFace(string[] parts, List<VertexTriplet> vertices, List<(int, int, int)> faces)
{
List<int> list = new List<int>();
for (int i = 1; i < parts.Length; i++)
{
list.Add(vertices.Count);
vertices.Add(ParseVertexTriplet(parts[i]));
}
for (int j = 1; j < list.Count - 1; j++)
{
faces.Add((list[0], list[j], list[j + 1]));
}
}
private static VertexTriplet ParseVertexTriplet(string part)
{
string[] array = part.Split('/');
int result = 0;
int result2 = 0;
int result3 = 0;
if (array.Length != 0)
{
int.TryParse(array[0], out result);
}
if (array.Length > 1 && !string.IsNullOrEmpty(array[1]))
{
int.TryParse(array[1], out result2);
}
if (array.Length > 2)
{
int.TryParse(array[2], out result3);
}
return new VertexTriplet(result, result2, result3);
}
}
[BepInPlugin("com.morecosmetics.injector", "More Cosmetics", "0.2.0")]
public class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "com.morecosmetics.injector";
public const string PluginName = "More Cosmetics";
public const string PluginVersion = "0.2.0";
internal static Plugin Instance { get; private set; }
private void Awake()
{
Instance = this;
try
{
PluginConfig.Bind(((BaseUnityPlugin)this).Config);
if (!PluginConfig.Enabled.Value)
{
return;
}
string item = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? Path.Combine(Paths.PluginPath, "More-Cosmetics");
HashSet<string> hashSet = new HashSet<string> { item };
if (PluginConfig.AutoDiscover.Value)
{
string pluginPath = Paths.PluginPath;
if (Directory.Exists(pluginPath))
{
string[] directories = Directory.GetDirectories(pluginPath);
foreach (string text in directories)
{
if (File.Exists(Path.Combine(text, "cosmetics.json")))
{
hashSet.Add(text);
}
}
}
}
Debug.Log((object)$"[MoreCosmetics] Scanning {hashSet.Count} directories for cosmetics...");
foreach (string item2 in hashSet)
{
if (File.Exists(Path.Combine(item2, "cosmetics.json")))
{
CosmeticRegistry.RegisterFrom(item2);
}
}
Harmony.CreateAndPatchAll(typeof(Plugin).Assembly, (string)null);
CosmeticDataManagerPatches.InjectCustomCosmeticsOnce();
((BaseUnityPlugin)this).Logger.LogInfo((object)"More Cosmetics 0.2.0 loaded.");
}
catch (Exception arg)
{
((BaseUnityPlugin)this).Logger.LogError((object)string.Format("{0} failed: {1}", "More Cosmetics", arg));
}
}
}
internal static class PluginConfig
{
public static ConfigEntry<bool> Enabled { get; private set; }
public static ConfigEntry<bool> AutoDiscover { get; private set; }
public static ConfigEntry<bool> AutoUnlockModCosmetics { get; private set; }
public static ConfigEntry<int> CosmeticIdStart { get; private set; }
public static void Bind(ConfigFile config)
{
Enabled = config.Bind<bool>("General", "Enabled", true, "Globally enables or disables the library and all loaded cosmetics.");
AutoDiscover = config.Bind<bool>("General", "AutoDiscover", true, "When true, automatically scans all plugin folders for cosmetics.json files.");
AutoUnlockModCosmetics = config.Bind<bool>("General", "AutoUnlockModCosmetics", true, "When true, all loaded cosmetics are automatically unlocked on game start.");
CosmeticIdStart = config.Bind<int>("General", "CosmeticIdStart", 10000, "Starting ID for auto-assigned cosmetic IDs.");
}
}
internal static class TextureLoader
{
public static Texture2D Load(string path)
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Expected O, but got Unknown
if (!File.Exists(path))
{
Debug.LogError((object)("[GWYF] Texture file not found: " + path));
return null;
}
byte[] array = File.ReadAllBytes(path);
Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
if (!ImageConversion.LoadImage(val, array))
{
Debug.LogError((object)("[GWYF] Failed to load texture: " + path));
Object.Destroy((Object)(object)val);
return null;
}
((Texture)val).filterMode = (FilterMode)0;
((Texture)val).wrapMode = (TextureWrapMode)1;
return val;
}
}
internal static class TinyJson
{
public static List<Dictionary<string, object>> ParseArray(string json)
{
List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
json = json.Trim();
if (!json.StartsWith("[") || !json.EndsWith("]"))
{
return list;
}
int num = 0;
int num2 = 0;
bool flag = false;
for (int i = 1; i < json.Length - 1; i++)
{
char c = json[i];
if (c == '"' && (i == 0 || json[i - 1] != '\\'))
{
flag = !flag;
}
if (flag)
{
continue;
}
switch (c)
{
case '{':
num++;
break;
case '}':
num--;
break;
case ',':
if (num == 0)
{
Dictionary<string, object> dictionary = ParseObject(json.Substring(num2 + 1, i - num2 - 1));
if (dictionary != null)
{
list.Add(dictionary);
}
num2 = i;
}
break;
}
}
if (num2 + 1 < json.Length - 1)
{
Dictionary<string, object> dictionary2 = ParseObject(json.Substring(num2 + 1, json.Length - num2 - 2));
if (dictionary2 != null)
{
list.Add(dictionary2);
}
}
return list;
}
public static Dictionary<string, object> ParseObject(string segment)
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
segment = segment.Trim();
if (!segment.StartsWith("{") || !segment.EndsWith("}"))
{
return dictionary;
}
int i = 1;
while (i < segment.Length - 1)
{
SkipWhitespace(segment, ref i);
if (i >= segment.Length - 1)
{
break;
}
string text = ReadString(segment, ref i);
SkipWhitespace(segment, ref i);
if (i >= segment.Length - 1 || segment[i] != ':')
{
break;
}
i++;
SkipWhitespace(segment, ref i);
object value = ReadValue(segment, ref i);
if (text != null)
{
dictionary[text] = value;
}
SkipWhitespace(segment, ref i);
if (i < segment.Length - 1 && segment[i] == ',')
{
i++;
}
}
return dictionary;
}
private static object ReadValue(string s, ref int i)
{
SkipWhitespace(s, ref i);
if (i >= s.Length)
{
return null;
}
if (s[i] == '"')
{
return ReadString(s, ref i);
}
if (s[i] == '{')
{
return ParseObject(ReadBraced(s, ref i));
}
if (s[i] == '[')
{
return ReadArray(s, ref i);
}
return ReadLiteral(s, ref i);
}
private static string ReadString(string s, ref int i)
{
if (i >= s.Length || s[i] != '"')
{
return "";
}
int num = ++i;
while (i < s.Length)
{
if (s[i] == '\\')
{
i += 2;
continue;
}
if (s[i] == '"')
{
break;
}
i++;
}
string result = s.Substring(num, i - num);
i++;
return result;
}
private static string ReadBraced(string s, ref int i)
{
if (i >= s.Length || s[i] != '{')
{
return "{}";
}
int num = i;
int num2 = 0;
bool flag = false;
while (i < s.Length)
{
char c = s[i];
if (c == '"' && (i == 0 || s[i - 1] != '\\'))
{
flag = !flag;
}
if (!flag)
{
if (c == '{')
{
num2++;
}
else if (c == '}')
{
num2--;
i++;
if (num2 == 0)
{
break;
}
continue;
}
}
i++;
}
return s.Substring(num, i - num);
}
private static object ReadArray(string s, ref int i)
{
if (i >= s.Length || s[i] != '[')
{
return Array.Empty<float>();
}
List<float> list = new List<float>();
i++;
while (i < s.Length && s[i] != ']')
{
SkipWhitespace(s, ref i);
if (i < s.Length && s[i] != ']' && ReadLiteral(s, ref i) is float item)
{
list.Add(item);
}
SkipWhitespace(s, ref i);
if (i < s.Length && s[i] == ',')
{
i++;
}
}
if (i < s.Length && s[i] == ']')
{
i++;
}
return list.ToArray();
}
private static object ReadLiteral(string s, ref int i)
{
int num = i;
while (i < s.Length && !IsDelimiter(s[i]))
{
i++;
}
string text = s.Substring(num, i - num).Trim().ToLowerInvariant();
switch (text)
{
case "null":
return null;
case "true":
return true;
case "false":
return false;
default:
{
if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
{
return result;
}
return text;
}
}
}
private static bool IsDelimiter(char c)
{
if (c != ',' && c != '}' && c != ']' && c != ':')
{
return char.IsWhiteSpace(c);
}
return true;
}
private static void SkipWhitespace(string s, ref int i)
{
while (i < s.Length && char.IsWhiteSpace(s[i]))
{
i++;
}
}
}
}
namespace GWYF_NewClothing.Patches
{
[HarmonyPatch]
internal static class CosmeticDataManagerPatches
{
private static bool _injected;
private static bool IsEnabled => PluginConfig.Enabled.Value;
[HarmonyPostfix]
[HarmonyPatch(typeof(CosmeticDataManager), "Initialize")]
private static void Initialize_Postfix()
{
if (IsEnabled)
{
CosmeticRegistry.ResolvePending();
InjectCustomCosmeticsOnce();
}
}
internal static void InjectCustomCosmeticsOnce()
{
if (_injected)
{
return;
}
if (!(AccessTools.Field(typeof(CosmeticDataManager), "_cosmeticCache").GetValue(null) is Dictionary<int, CosmeticData> dictionary))
{
Debug.LogWarning((object)"[MoreCosmetics] _cosmeticCache is null. Aborting injection.");
return;
}
CosmeticData[] allCosmetics = CosmeticRegistry.AllCosmetics;
if (allCosmetics.Length == 0)
{
return;
}
int num = 0;
CosmeticData[] array = allCosmetics;
foreach (CosmeticData val in array)
{
if (!dictionary.ContainsKey(val.cosmeticId))
{
dictionary[val.cosmeticId] = val;
num++;
}
}
if (num > 0)
{
AccessTools.Method(typeof(CosmeticDataManager), "RebuildSortedCosmeticIds", (Type[])null, (Type[])null).Invoke(null, null);
Debug.Log((object)$"[MoreCosmetics] Injected {num} cosmetics. Cache has {dictionary.Count} total.");
}
_injected = true;
}
}
[HarmonyPatch]
internal static class CosmeticsUnlockManagerPatches
{
private static bool IsEnabled => PluginConfig.Enabled.Value;
[HarmonyPostfix]
[HarmonyPatch(typeof(CosmeticsUnlockManager), "OnAwake")]
private static void OnAwake_Postfix(CosmeticsUnlockManager __instance)
{
if (!IsEnabled || !PluginConfig.AutoUnlockModCosmetics.Value)
{
return;
}
CosmeticData[] allCosmetics = CosmeticRegistry.AllCosmetics;
foreach (CosmeticData val in allCosmetics)
{
if (__instance.UnlockCosmetic(val.cosmeticId))
{
Debug.Log((object)$"[MoreCosmetics] Unlocked: {val.cosmeticName} (ID:{val.cosmeticId})");
}
}
}
}
[HarmonyPatch(typeof(PlayerCustomization), "ApplyCosmetic")]
internal static class PlayerCustomizationApplyPatch
{
private static bool IsEnabled => PluginConfig.Enabled.Value;
[HarmonyPostfix]
private static void Postfix(PlayerCustomization __instance, int cosmeticId)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
if (!IsEnabled)
{
return;
}
CosmeticData cosmeticById = CosmeticDataManager.GetCosmeticById(cosmeticId);
if ((Object)(object)cosmeticById == (Object)null || (Object)(object)cosmeticById.cosmeticMaterial == (Object)null)
{
return;
}
MeshFilter meshFilterForType = GetMeshFilterForType(__instance, cosmeticById.cosmeticType);
if (!((Object)(object)meshFilterForType == (Object)null))
{
MeshRenderer component = ((Component)meshFilterForType).GetComponent<MeshRenderer>();
if ((Object)(object)component != (Object)null)
{
((Renderer)component).sharedMaterial = cosmeticById.cosmeticMaterial;
}
}
}
private static MeshFilter GetMeshFilterForType(PlayerCustomization instance, CosmeticType type)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected I4, but got Unknown
string text = (int)type switch
{
0 => "hat",
1 => "hair",
2 => "mustache",
3 => "beard",
4 => "neckwear",
5 => "clothing",
6 => "facewear",
_ => null,
};
if (text == null)
{
return null;
}
object? obj = typeof(PlayerCustomization).GetField(text, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(instance);
return (MeshFilter)((obj is MeshFilter) ? obj : null);
}
}
}