From e8080ded4425904c65e1a42dc8a4d1b261d6e056 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Sat, 7 Dec 2024 00:47:24 +0900 Subject: [PATCH 01/59] feat: set sync size for speed --- Editor/ShellProtectorEditor.cs | 41 +++++----- Runtime/Scripts/AnimatorManager.cs | 117 +++++++++------------------- Runtime/Scripts/ParameterManager.cs | 51 +++++------- Runtime/Scripts/ShellProtector.cs | 12 ++- 4 files changed, 90 insertions(+), 131 deletions(-) diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index ee8e176..a905ba8 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -29,6 +29,7 @@ public class ShellProtectorEditor : Editor SerializedProperty algorithm; SerializedProperty key_size; SerializedProperty key_size_idx; + SerializedProperty sync_size; SerializedProperty animation_speed; SerializedProperty delete_folders; SerializedProperty parameter_multiplexing; @@ -108,8 +109,9 @@ void OnEnable() algorithm = serializedObject.FindProperty("algorithm"); key_size = serializedObject.FindProperty("key_size"); key_size_idx = serializedObject.FindProperty("key_size_idx"); + sync_size = serializedObject.FindProperty("sync_size"); animation_speed = serializedObject.FindProperty("animation_speed"); - delete_folders = serializedObject.FindProperty("delete_folders"); + delete_folders = serializedObject.FindProperty("delete_folders"); parameter_multiplexing = serializedObject.FindProperty("parameter_multiplexing"); bUseSmallMipTexture = serializedObject.FindProperty("bUseSmallMipTexture"); bPreserveMMD = serializedObject.FindProperty("bPreserveMMD"); @@ -229,22 +231,10 @@ public override void OnInspectorGUI() int using_parameter = (key_size.intValue * 8); if(parameter_multiplexing.boolValue == true) { - int keys = key_size.intValue; - switch(keys) - { - case 4: - using_parameter = 8 + 3; - break; - case 8: - using_parameter = 8 + 4; - break; - case 12: - using_parameter = 8 + 5; - break; - case 16: - using_parameter = 8 + 5; - break; - } + int lock_size = 1; + int pkey_count = sync_size.intValue; + int switch_count = ShellProtector.GetRequiredSwitchCount(key_size.intValue, sync_size.intValue); + using_parameter = switch_count + lock_size + pkey_count * 8; } GUILayout.Label(Lang("Parameters to be used:") + using_parameter, EditorStyles.wordWrappedLabel); @@ -283,6 +273,21 @@ public override void OnInspectorGUI() break; } + var sync_size_value = sync_size.intValue; + int sync_size_index = 0; + int[] sync_size_caldidate = { 1, 2, 4}; + string[] selectable_values = { "1", "2", "4" }; + for (int i = 0; i < sync_size_caldidate.Length; i++) + if (sync_size_caldidate[i] == sync_size_value) + sync_size_index = i; + + if(key_size.intValue > 0) + { + GUILayout.Label(Lang("Sync speed"), EditorStyles.boldLabel); + sync_size_index = EditorGUILayout.Popup(sync_size_index, selectable_values, GUILayout.Width(100)); + sync_size.intValue = sync_size_caldidate[sync_size_index]; + } + GUILayout.Label(Lang("Encrytion algorithm"), EditorStyles.boldLabel); algorithm.intValue = EditorGUILayout.Popup(algorithm.intValue, enc_funcs, GUILayout.Width(120)); @@ -399,7 +404,7 @@ public override void OnInspectorGUI() if (game_object_list.count == 0 && material_list.count == 0) GUI.enabled = false; - + #if MODULAR if (GUILayout.Button(Lang("Manual Encrypt!"))) #else diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 19bcf40..1848e0b 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -217,56 +217,27 @@ private static BlendTree[] CreateKeyTree(string animation_dir, int key_length, f return tree; } - private static void AddTransition(AnimatorStateTransition transition, int key_length, int idx) + private static AnimatorConditionMode[] GetSwitchConditions(int switchCount, int index) { - transition.AddCondition(AnimatorConditionMode.IfNot, 0, "encrypt_lock"); - if (key_length == 4) - { - int n = 2; - AnimatorConditionMode[] mode = new AnimatorConditionMode[n]; - for (int i = 0; i < n; ++i) - mode[i] = AnimatorConditionMode.IfNot; - if ((idx & 0b0001) == 1) - mode[0] = AnimatorConditionMode.If; - if ((idx & 0b0010) == 2) - mode[1] = AnimatorConditionMode.If; - for (int i = 0; i < n; ++i) - transition.AddCondition(mode[i], 0, "encrypt_switch" + i); - } - else if (key_length == 8) - { - int n = 3; - AnimatorConditionMode[] mode = new AnimatorConditionMode[n]; - for (int i = 0; i < n; ++i) - mode[i] = AnimatorConditionMode.IfNot; - if ((idx & 0b0001) == 1) - mode[0] = AnimatorConditionMode.If; - if ((idx & 0b0010) == 2) - mode[1] = AnimatorConditionMode.If; - if ((idx & 0b0100) == 4) - mode[2] = AnimatorConditionMode.If; - for (int i = 0; i < n; ++i) - transition.AddCondition(mode[i], 0, "encrypt_switch" + i); - } - else + AnimatorConditionMode[] mode = new AnimatorConditionMode[switchCount]; + for (int i = 0; i < switchCount; ++i) + mode[i] = AnimatorConditionMode.IfNot; + for (int i = 0; i < switchCount; ++i) { - int n = 4; - AnimatorConditionMode[] mode = new AnimatorConditionMode[n]; - for (int i = 0; i < n; ++i) - mode[i] = AnimatorConditionMode.IfNot; - if ((idx & 0b0001) == 1) - mode[0] = AnimatorConditionMode.If; - if ((idx & 0b0010) == 2) - mode[1] = AnimatorConditionMode.If; - if ((idx & 0b0100) == 4) - mode[2] = AnimatorConditionMode.If; - if ((idx & 0b1000) == 8) - mode[3] = AnimatorConditionMode.If; - for (int i = 0; i < n; ++i) - transition.AddCondition(mode[i], 0, "encrypt_switch" + i); + if ((index & (1 << i)) != 0) + mode[i] = AnimatorConditionMode.If; } + return mode; + } + + private static void AddTransition(AnimatorStateTransition transition, int keyLength, int syncSize, int idx) + { + transition.AddCondition(AnimatorConditionMode.IfNot, 0, "encrypt_lock"); + AnimatorConditionMode[] switchConditions = GetSwitchConditions(ShellProtector.GetRequiredSwitchCount(keyLength, syncSize), idx); + for (int i = 0; i < switchConditions.Length; ++i) + transition.AddCondition(switchConditions[i], 0, "encrypt_switch" + i); } - public static void AddParameter(AnimatorController anim, int key_length, bool optimize) + public static void AddParameter(AnimatorController anim, int key_length, int sync_size, bool optimize) { var paramters = anim.parameters; for (int i = 0; i < paramters.Length; ++i) @@ -282,37 +253,21 @@ public static void AddParameter(AnimatorController anim, int key_length, bool op if (optimize) { - anim.AddParameter("pkey", AnimatorControllerParameterType.Float); + for(int i = 0; i < sync_size; i++) + anim.AddParameter("pkey_sync" + i, AnimatorControllerParameterType.Float); anim.AddParameter("encrypt_lock", AnimatorControllerParameterType.Bool); - int switch_count = 1; - switch (key_length) - { - case 4: - switch_count = 2; - break; - case 8: - switch_count = 3; - break; - case 12: - case 16: - switch_count = 4; - break; - default: - Debug.LogErrorFormat("AnimatorManager-AddParameter: key_length = {} is wrong!", key_length); - return; - } - + int switch_count = ShellProtector.GetRequiredSwitchCount(key_length, sync_size); for (int i = 0; i < switch_count; ++i) anim.AddParameter("encrypt_switch" + i, AnimatorControllerParameterType.Bool); } } - public static void AddKeyLayer(AnimatorController anim, string animation_dir, int key_length, float speed = 10.0f, bool optimize = false) + public static void AddKeyLayer(AnimatorController anim, string animation_dir, int key_length, int sync_size, float speed = 10.0f, bool optimize = false) { - AddParameter(anim, key_length, optimize); + AddParameter(anim, key_length, sync_size, optimize); if (optimize) { - AddKeyLayerMultiplexing(anim, animation_dir, key_length, speed); + AddKeyLayerMultiplexing(anim, animation_dir, key_length, sync_size, speed); return; } @@ -356,7 +311,8 @@ public static void AddKeyLayer(AnimatorController anim, string animation_dir, in tree_root.children = children; } - public static void AddKeyLayerMultiplexing(AnimatorController anim, string animation_dir, int key_length, float speed = 10.0f) + + public static void AddKeyLayerMultiplexing(AnimatorController anim, string animation_dir, int key_length, int sync_size, float speed = 10.0f) { var layers = anim.layers; foreach (var _layer in layers) @@ -383,28 +339,31 @@ public static void AddKeyLayerMultiplexing(AnimatorController anim, string anima transition.hasExitTime = false; transition.AddCondition(AnimatorConditionMode.If, 0, "encrypt_lock"); - for (int i = 0; i < key_length; ++i) + for (int i = 0; i < key_length / sync_size; ++i) { var key_state = layer.stateMachine.AddState("key" + i); - var behaviour = key_state.AddStateMachineBehaviour(); - var behaviour_param = new VRCAvatarParameterDriver.Parameter + for (var j = 0; j < sync_size; j++) { - type = VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Copy, - name = "pkey" + i, - source = "pkey", - }; - behaviour.parameters.Add(behaviour_param); + var behaviour = key_state.AddStateMachineBehaviour(); + var behaviour_param = new VRCAvatarParameterDriver.Parameter + { + type = VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Copy, + name = "pkey" + (i * sync_size + j), + source = "pkey_sync" + j + }; + behaviour.parameters.Add(behaviour_param); + } transition = layer.stateMachine.AddAnyStateTransition(key_state); transition.canTransitionToSelf = false; transition.exitTime = 0; transition.duration = 0; transition.hasExitTime = false; - AddTransition(transition, key_length, i); + AddTransition(transition, key_length, sync_size, i); } - AddKeyLayer(anim, animation_dir, key_length, speed, false); + AddKeyLayer(anim, animation_dir, key_length, sync_size, speed, false); } public static void AddFallbackLayer(AnimatorController anim, AnimationClip fallbackAnimation, float time = 3) diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index 6cc94d9..6ecaba6 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -1,6 +1,7 @@ #if UNITY_EDITOR using System.Collections; using System.Collections.Generic; +using Shell.Protector; using UnityEngine; using VRC.SDK3.Avatars; using VRC.SDK3.Avatars.ScriptableObjects; @@ -18,42 +19,25 @@ public static VRCExpressionParameters.Parameter CloneParameter(VRCExpressionPara return tmp; } - public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrc_parameters, int key_length, bool optimize = false) + public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrc_parameters, int key_length, int sync_size, bool use_multiplexing = false) { - VRCExpressionParameters result = new VRCExpressionParameters(); + VRCExpressionParameters result = ScriptableObject.CreateInstance(); result.name = vrc_parameters.name + "_encrypted"; var parameters = vrc_parameters.parameters; - int switch_size = 0; int etc = 0; - if (optimize == true) + if (use_multiplexing) { - etc = 2; //lock + pkey(sync) - switch(key_length) - { - case 4: - switch_size = 2; - break; - case 8: - switch_size = 3; - break; - case 12: - case 16: - switch_size = 4; - break; - default: - Debug.LogErrorFormat("ParameterManager: key_length = {} is wrong!", key_length); - return result; - } + etc = 1 + sync_size; // encrypt_lock + pkey_sync } - VRCExpressionParameters.Parameter[] tmp = new VRCExpressionParameters.Parameter[parameters.Length + switch_size + key_length + etc]; + VRCExpressionParameters.Parameter[] tmp = new VRCExpressionParameters.Parameter[parameters.Length + ShellProtector.GetRequiredSwitchCount(key_length, sync_size) + key_length + etc]; int idx; for(idx = 0; idx < parameters.Length; ++idx) tmp[idx] = CloneParameter(parameters[idx]); - if (optimize == false) + if (use_multiplexing == false) { for (int i = 0; i < key_length; ++i) { @@ -71,15 +55,18 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr } else { - var pkey = new VRCExpressionParameters.Parameter + for (int i = 0; i < sync_size; i++) { - name = "pkey", - saved = true, - networkSynced = true, - valueType = VRCExpressionParameters.ValueType.Float, - defaultValue = 0.0f - }; - tmp[idx++] = pkey; + var para = new VRCExpressionParameters.Parameter + { + name = $"pkey_sync{i}", + saved = true, + networkSynced = true, + valueType = VRCExpressionParameters.ValueType.Float, + defaultValue = 0.0f + }; + tmp[idx++] = para; + } for (int i = 0; i < key_length; ++i) { var para = new VRCExpressionParameters.Parameter @@ -102,7 +89,7 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr defaultValue = 0.0f }; tmp[idx++] = plock; - for (int i = 0; i < switch_size; ++i) + for (int i = 0; i < ShellProtector.GetRequiredSwitchCount(key_length, sync_size); ++i) { var para = new VRCExpressionParameters.Parameter { diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 9fc39d7..d149cd5 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -11,6 +11,7 @@ using System.Linq; using VRC.SDKBase; using UnityEditor.Animations; +using UnityEngine.Serialization; #if MODULAR using nadena.dev.modular_avatar.core; @@ -90,6 +91,7 @@ struct ProcessedTexture [SerializeField] int algorithm = 1; [SerializeField] int key_size_idx = 3; [SerializeField] int key_size = 12; + [SerializeField] int sync_size = 1; [SerializeField] float animation_speed = 5.0f; [SerializeField] bool delete_folders = true; [SerializeField] bool parameter_multiplexing = false; @@ -733,7 +735,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) ///////////////////////parameter//////////////////// var av3 = avatar.GetComponent(); - av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, key_size, parameter_multiplexing); + av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, key_size, sync_size, parameter_multiplexing); AssetDatabase.CreateAsset(av3.expressionParameters, Path.Combine(avatarDir, av3.expressionParameters.name + ".asset")); //////////////////////////////////////////////////// SetMaterialFallbackValue(avatar, true); @@ -783,7 +785,7 @@ public void SetAnimations(GameObject avatar, bool clone) meshes.CopyTo(mesh_array); AnimatorManager.CreateKeyAniamtions(Path.Combine(asset_dir, "Animations"), animation_dir, mesh_array); var fallbackAnim = AnimatorManager.CreateFallbackAniamtions(Path.Combine(asset_dir, "Animations", "FallbackOff.anim"), animation_dir, mesh_array); - AnimatorManager.AddKeyLayer(fx, animation_dir, key_size, animation_speed, parameter_multiplexing); + AnimatorManager.AddKeyLayer(fx, animation_dir, key_size, sync_size, animation_speed, parameter_multiplexing); AnimatorManager.AddFallbackLayer(fx, fallbackAnim, fallbackTime); AssetDatabase.SaveAssets(); @@ -1087,6 +1089,12 @@ public Shader IsEncryptedBefore(Shader shader) history.LoadData(); return history.IsEncryptedBefore(shader); } + + public static int GetRequiredSwitchCount(int key_length, int sync_size) + { + key_length /= sync_size; + return Mathf.CeilToInt(Mathf.Log(key_length, 2)); + } } } #endif \ No newline at end of file From 10da6db85462a579cd143111dea9302b74b3c59f Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Sat, 7 Dec 2024 01:05:43 +0900 Subject: [PATCH 02/59] fix: backward compatibility for modified pkey --- Runtime/Scripts/AnimatorManager.cs | 4 ++-- Runtime/Scripts/ParameterManager.cs | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 1848e0b..3f3dbb6 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -254,7 +254,7 @@ public static void AddParameter(AnimatorController anim, int key_length, int syn if (optimize) { for(int i = 0; i < sync_size; i++) - anim.AddParameter("pkey_sync" + i, AnimatorControllerParameterType.Float); + anim.AddParameter(ParameterManager.GetPKeySyncParameterName(i), AnimatorControllerParameterType.Float); anim.AddParameter("encrypt_lock", AnimatorControllerParameterType.Bool); int switch_count = ShellProtector.GetRequiredSwitchCount(key_length, sync_size); for (int i = 0; i < switch_count; ++i) @@ -350,7 +350,7 @@ public static void AddKeyLayerMultiplexing(AnimatorController anim, string anima { type = VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Copy, name = "pkey" + (i * sync_size + j), - source = "pkey_sync" + j + source = ParameterManager.GetPKeySyncParameterName(j) }; behaviour.parameters.Add(behaviour_param); } diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index 6ecaba6..01b458b 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -19,6 +19,12 @@ public static VRCExpressionParameters.Parameter CloneParameter(VRCExpressionPara return tmp; } + public static string GetPKeySyncParameterName(int index) + { + if (index == 0) return "pkey"; // For backward compatibility + return "pkey_sync" + index; + } + public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrc_parameters, int key_length, int sync_size, bool use_multiplexing = false) { VRCExpressionParameters result = ScriptableObject.CreateInstance(); @@ -59,7 +65,7 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr { var para = new VRCExpressionParameters.Parameter { - name = $"pkey_sync{i}", + name = GetPKeySyncParameterName(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Float, From cc8ecdf750ce3e825f4c4825018c03176565c830 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Sat, 7 Dec 2024 02:05:10 +0900 Subject: [PATCH 03/59] refactor: organize parameter names / simplify AddKeyParameter method --- Runtime/Scripts/AnimatorManager.cs | 18 ++--- Runtime/Scripts/ParameterManager.cs | 111 +++++++++++----------------- 2 files changed, 52 insertions(+), 77 deletions(-) diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 3f3dbb6..95eb574 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -195,10 +195,8 @@ private static BlendTree[] CreateKeyTree(string animation_dir, int key_length, f { BlendTree tree_key = new BlendTree(); tree_key.name = "key" + i; - tree_key.blendType = BlendTreeType.Direct; - tree_key.blendParameter = "key_weight"; tree_key.blendType = BlendTreeType.Simple1D; - tree_key.blendParameter = "pkey" + i; + tree_key.blendParameter = ParameterManager.GetPKeyParameterName(i); tree_key.useAutomaticThresholds = false; Motion motion0 = AssetDatabase.LoadAssetAtPath(Path.Combine(animation_dir, "key" + (i + offset) + ".anim"), typeof(AnimationClip)) as AnimationClip; @@ -232,10 +230,10 @@ private static AnimatorConditionMode[] GetSwitchConditions(int switchCount, int private static void AddTransition(AnimatorStateTransition transition, int keyLength, int syncSize, int idx) { - transition.AddCondition(AnimatorConditionMode.IfNot, 0, "encrypt_lock"); + transition.AddCondition(AnimatorConditionMode.IfNot, 0, ParameterManager.GetSyncLockParameterName()); AnimatorConditionMode[] switchConditions = GetSwitchConditions(ShellProtector.GetRequiredSwitchCount(keyLength, syncSize), idx); for (int i = 0; i < switchConditions.Length; ++i) - transition.AddCondition(switchConditions[i], 0, "encrypt_switch" + i); + transition.AddCondition(switchConditions[i], 0, ParameterManager.GetSyncSwitchParameterName(i)); } public static void AddParameter(AnimatorController anim, int key_length, int sync_size, bool optimize) { @@ -249,16 +247,16 @@ public static void AddParameter(AnimatorController anim, int key_length, int syn anim.AddParameter(new AnimatorControllerParameter() { defaultFloat = 1.0f, name = "key_weight", type = AnimatorControllerParameterType.Float }); for (int i = 0; i < key_length; ++i) - anim.AddParameter("pkey" + i, AnimatorControllerParameterType.Float); + anim.AddParameter(ParameterManager.GetPKeyParameterName(i), AnimatorControllerParameterType.Float); if (optimize) { for(int i = 0; i < sync_size; i++) anim.AddParameter(ParameterManager.GetPKeySyncParameterName(i), AnimatorControllerParameterType.Float); - anim.AddParameter("encrypt_lock", AnimatorControllerParameterType.Bool); + anim.AddParameter(ParameterManager.GetSyncLockParameterName(), AnimatorControllerParameterType.Bool); int switch_count = ShellProtector.GetRequiredSwitchCount(key_length, sync_size); for (int i = 0; i < switch_count; ++i) - anim.AddParameter("encrypt_switch" + i, AnimatorControllerParameterType.Bool); + anim.AddParameter(ParameterManager.GetSyncSwitchParameterName(i), AnimatorControllerParameterType.Bool); } } public static void AddKeyLayer(AnimatorController anim, string animation_dir, int key_length, int sync_size, float speed = 10.0f, bool optimize = false) @@ -337,7 +335,7 @@ public static void AddKeyLayerMultiplexing(AnimatorController anim, string anima transition.exitTime = 0; transition.duration = 0; transition.hasExitTime = false; - transition.AddCondition(AnimatorConditionMode.If, 0, "encrypt_lock"); + transition.AddCondition(AnimatorConditionMode.If, 0, ParameterManager.GetSyncLockParameterName()); for (int i = 0; i < key_length / sync_size; ++i) { @@ -349,7 +347,7 @@ public static void AddKeyLayerMultiplexing(AnimatorController anim, string anima var behaviour_param = new VRCAvatarParameterDriver.Parameter { type = VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Copy, - name = "pkey" + (i * sync_size + j), + name = ParameterManager.GetPKeyParameterName(i * sync_size + j), source = ParameterManager.GetPKeySyncParameterName(j) }; behaviour.parameters.Add(behaviour_param); diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index 01b458b..b38f8cc 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -1,114 +1,91 @@ #if UNITY_EDITOR -using System.Collections; using System.Collections.Generic; +using System.Linq; using Shell.Protector; using UnityEngine; -using VRC.SDK3.Avatars; using VRC.SDK3.Avatars.ScriptableObjects; public static class ParameterManager { - public static VRCExpressionParameters.Parameter CloneParameter(VRCExpressionParameters.Parameter parameter) - { - var tmp = new VRCExpressionParameters.Parameter(); - tmp.saved = parameter.saved; - tmp.name = parameter.name; - tmp.networkSynced = parameter.networkSynced; - tmp.valueType = parameter.valueType; - tmp.defaultValue = parameter.defaultValue; - return tmp; - } - public static string GetPKeySyncParameterName(int index) { if (index == 0) return "pkey"; // For backward compatibility return "pkey_sync" + index; } + public static string GetPKeyParameterName(int index) => "pkey" + index; + public static string GetSyncSwitchParameterName(int index) => "encrypt_switch" + index; + public static string GetSyncLockParameterName() => "encrypt_lock"; - public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrc_parameters, int key_length, int sync_size, bool use_multiplexing = false) - { - VRCExpressionParameters result = ScriptableObject.CreateInstance(); - result.name = vrc_parameters.name + "_encrypted"; - - var parameters = vrc_parameters.parameters; - int etc = 0; - if (use_multiplexing) - { - etc = 1 + sync_size; // encrypt_lock + pkey_sync - } - - VRCExpressionParameters.Parameter[] tmp = new VRCExpressionParameters.Parameter[parameters.Length + ShellProtector.GetRequiredSwitchCount(key_length, sync_size) + key_length + etc]; - int idx; - for(idx = 0; idx < parameters.Length; ++idx) - tmp[idx] = CloneParameter(parameters[idx]); + public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrcParameters, int keyLength, int syncSize, bool useMultiplexing) + { + var parameters = new List(); - if (use_multiplexing == false) + if (useMultiplexing == false) { - for (int i = 0; i < key_length; ++i) + for (var i = 0; i < keyLength; ++i) { - var para = new VRCExpressionParameters.Parameter + parameters.Add(new VRCExpressionParameters.Parameter { - name = "pkey" + i, + name = GetPKeyParameterName(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Float, defaultValue = 0.0f - }; - - tmp[idx++] = para; + }); } } else { - for (int i = 0; i < sync_size; i++) + parameters.Add(new VRCExpressionParameters.Parameter { - var para = new VRCExpressionParameters.Parameter + name = GetSyncLockParameterName(), + saved = true, + networkSynced = true, + valueType = VRCExpressionParameters.ValueType.Bool, + defaultValue = 0.0f + }); + + for (var i = 0; i < syncSize; i++) + { + parameters.Add(new VRCExpressionParameters.Parameter { name = GetPKeySyncParameterName(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Float, defaultValue = 0.0f - }; - tmp[idx++] = para; + }); } - for (int i = 0; i < key_length; ++i) - { - var para = new VRCExpressionParameters.Parameter - { - name = "pkey" + i, - saved = false, - networkSynced = false, - valueType = VRCExpressionParameters.ValueType.Float, - defaultValue = 0.0f - }; - tmp[idx++] = para; - } - var plock = new VRCExpressionParameters.Parameter - { - name = "encrypt_lock", - saved = true, - networkSynced = true, - valueType = VRCExpressionParameters.ValueType.Bool, - defaultValue = 0.0f - }; - tmp[idx++] = plock; - for (int i = 0; i < ShellProtector.GetRequiredSwitchCount(key_length, sync_size); ++i) + for (var i = 0; i < ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); ++i) { - var para = new VRCExpressionParameters.Parameter + parameters.Add(new VRCExpressionParameters.Parameter { - name = "encrypt_switch" + i, + name = GetSyncSwitchParameterName(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Bool, defaultValue = 0.0f - }; - tmp[idx++] = para; + }); + } + + for (var i = 0; i < keyLength; ++i) + { + parameters.Add(new VRCExpressionParameters.Parameter + { + name = GetPKeyParameterName(i), + saved = false, + networkSynced = false, + valueType = VRCExpressionParameters.ValueType.Float, + defaultValue = 0.0f + }); } } - result.parameters = tmp; + + var result = ScriptableObject.CreateInstance(); + result.name = vrcParameters.name + "_encrypted"; + result.parameters = vrcParameters.parameters.Concat(parameters).ToArray();; return result; } } From 4cbc21ac14f1dc5f88824acffbc4a6bdd5b98387 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Sat, 7 Dec 2024 03:50:54 +0900 Subject: [PATCH 04/59] feat: save on multiplexing --- Editor/ShellProtectorEditor.cs | 3 +- Runtime/Scripts/AnimatorManager.cs | 223 +++++++++++++++++++++------- Runtime/Scripts/ParameterManager.cs | 43 ++++-- 3 files changed, 200 insertions(+), 69 deletions(-) diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index a905ba8..be50a6c 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -232,9 +232,8 @@ public override void OnInspectorGUI() if(parameter_multiplexing.boolValue == true) { int lock_size = 1; - int pkey_count = sync_size.intValue; int switch_count = ShellProtector.GetRequiredSwitchCount(key_size.intValue, sync_size.intValue); - using_parameter = switch_count + lock_size + pkey_count * 8; + using_parameter = switch_count + lock_size + sync_size.intValue * 8; } GUILayout.Label(Lang("Parameters to be used:") + using_parameter, EditorStyles.wordWrappedLabel); diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 95eb574..db8c395 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -2,11 +2,13 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using UnityEditor; using UnityEditor.Animations; using UnityEngine; using VRC.SDK3.Avatars.Components; +using VRC.SDKBase; namespace Shell.Protector { @@ -118,7 +120,7 @@ public static void CreateKeyAniamtions(string animation_dir, string new_dir, Gam continue; if (filename.Contains("FallbackOff")) continue; - + string path = Path.Combine(new_dir, filename); AssetDatabase.CopyAsset(file, path); string anim = File.ReadAllText(path); @@ -196,7 +198,7 @@ private static BlendTree[] CreateKeyTree(string animation_dir, int key_length, f BlendTree tree_key = new BlendTree(); tree_key.name = "key" + i; tree_key.blendType = BlendTreeType.Simple1D; - tree_key.blendParameter = ParameterManager.GetPKeyParameterName(i); + tree_key.blendParameter = ParameterManager.GetKeyName(i); tree_key.useAutomaticThresholds = false; Motion motion0 = AssetDatabase.LoadAssetAtPath(Path.Combine(animation_dir, "key" + (i + offset) + ".anim"), typeof(AnimationClip)) as AnimationClip; @@ -230,50 +232,53 @@ private static AnimatorConditionMode[] GetSwitchConditions(int switchCount, int private static void AddTransition(AnimatorStateTransition transition, int keyLength, int syncSize, int idx) { - transition.AddCondition(AnimatorConditionMode.IfNot, 0, ParameterManager.GetSyncLockParameterName()); + transition.AddCondition(AnimatorConditionMode.IfNot, 0, ParameterManager.GetSyncLockName()); AnimatorConditionMode[] switchConditions = GetSwitchConditions(ShellProtector.GetRequiredSwitchCount(keyLength, syncSize), idx); for (int i = 0; i < switchConditions.Length; ++i) - transition.AddCondition(switchConditions[i], 0, ParameterManager.GetSyncSwitchParameterName(i)); + transition.AddCondition(switchConditions[i], 0, ParameterManager.GetSyncSwitchName(i)); } - public static void AddParameter(AnimatorController anim, int key_length, int sync_size, bool optimize) + + private static void AddParameters(AnimatorController anim, int keyLength, int syncSize, bool useMultiplexing) { - var paramters = anim.parameters; - for (int i = 0; i < paramters.Length; ++i) + anim.AddParameter(new AnimatorControllerParameter { - string name = paramters[i].name; - if (name == "key_weight") - return; - } - anim.AddParameter(new AnimatorControllerParameter() { defaultFloat = 1.0f, name = "key_weight", type = AnimatorControllerParameterType.Float }); + defaultFloat = 1.0f, + name = "key_weight", + type = AnimatorControllerParameterType.Float + }); - for (int i = 0; i < key_length; ++i) - anim.AddParameter(ParameterManager.GetPKeyParameterName(i), AnimatorControllerParameterType.Float); + anim.AddParameter(new AnimatorControllerParameter + { + defaultBool = true, + name = ParameterManager.GetSyncEnabledName(), + type = AnimatorControllerParameterType.Bool + }); - if (optimize) + for (var i = 0; i < keyLength; ++i) + anim.AddParameter(ParameterManager.GetKeyName(i), AnimatorControllerParameterType.Float); + + if (useMultiplexing) { - for(int i = 0; i < sync_size; i++) - anim.AddParameter(ParameterManager.GetPKeySyncParameterName(i), AnimatorControllerParameterType.Float); - anim.AddParameter(ParameterManager.GetSyncLockParameterName(), AnimatorControllerParameterType.Bool); - int switch_count = ShellProtector.GetRequiredSwitchCount(key_length, sync_size); - for (int i = 0; i < switch_count; ++i) - anim.AddParameter(ParameterManager.GetSyncSwitchParameterName(i), AnimatorControllerParameterType.Bool); + anim.AddParameter(ParameterManager.GetSyncLockName(), AnimatorControllerParameterType.Bool); + var switchCount = ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); + for (var i = 0; i < keyLength; ++i) + anim.AddParameter(ParameterManager.GetSavedKeyName(i), AnimatorControllerParameterType.Float); + for(var i = 0; i < syncSize; ++i) + anim.AddParameter(ParameterManager.GetSyncedKeyNAme(i), AnimatorControllerParameterType.Float); + for (var i = 0; i < switchCount; ++i) + anim.AddParameter(ParameterManager.GetSyncSwitchName(i), AnimatorControllerParameterType.Bool); } } - public static void AddKeyLayer(AnimatorController anim, string animation_dir, int key_length, int sync_size, float speed = 10.0f, bool optimize = false) + public static void AddKeyLayer(AnimatorController anim, string animationDir, int keyLength, int syncSize, float speed, bool useMultiplexing) { - AddParameter(anim, key_length, sync_size, optimize); + AddParameters(anim, keyLength, syncSize, useMultiplexing); - if (optimize) - { - AddKeyLayerMultiplexing(anim, animation_dir, key_length, sync_size, speed); - return; - } + if (anim.layers.Any(l => l.name == "ShellProtector")) return; - var layers = anim.layers; - foreach (var _layer in layers) + if (useMultiplexing) { - if (_layer.name == "ShellProtector") - return; + AddMuxLayer(anim, keyLength, syncSize, 0.15f, 0.1f, 1f); // 10hz + AddDemuxLayer(anim, keyLength, syncSize); } AnimatorStateMachine stateMachine = new AnimatorStateMachine @@ -296,8 +301,8 @@ public static void AddKeyLayer(AnimatorController anim, string animation_dir, in AssetDatabase.AddObjectToAsset(tree_root, anim); state.motion = tree_root; - var key_tree = CreateKeyTree(animation_dir, key_length, speed); - for (int i = 0; i < key_length; ++i) + var key_tree = CreateKeyTree(animationDir, keyLength, speed); + for (int i = 0; i < keyLength; ++i) { tree_root.AddChild(key_tree[i]); AssetDatabase.AddObjectToAsset(key_tree[i], anim); @@ -310,18 +315,127 @@ public static void AddKeyLayer(AnimatorController anim, string animation_dir, in tree_root.children = children; } - public static void AddKeyLayerMultiplexing(AnimatorController anim, string animation_dir, int key_length, int sync_size, float speed = 10.0f) + private static void AddSyncEnabledCondition(AnimatorStateTransition transition) { - var layers = anim.layers; - foreach (var _layer in layers) + transition.AddCondition(AnimatorConditionMode.If, 0, ParameterManager.GetSyncEnabledName()); + } + + private static void AddMuxLayer(AnimatorController anim, int keyLength, int syncSize, float unlockDelay, float interval, float delay) + { + if (anim.layers.Any(l => l.name == "ShellProtectorMux")) return; + + var stateMachine = new AnimatorStateMachine { - if (_layer.name == "ShellProtectorDriver") - return; + name = anim.MakeUniqueLayerName("ShellProtectorMux"), + hideFlags = HideFlags.HideInHierarchy + }; + + AssetDatabase.AddObjectToAsset(stateMachine, anim); + + anim.AddLayer(new AnimatorControllerLayer { name = stateMachine.name, defaultWeight = 1.0f, stateMachine = stateMachine }); + + var layer = anim.layers[anim.layers.Length - 1]; + var idle = layer.stateMachine.AddState("Idle", new Vector3(0, 0)); + layer.stateMachine.AddEntryTransition(idle); + + var steps = keyLength / syncSize; + var syncStates = new AnimatorState[steps]; + var lockStates = new AnimatorState[steps]; + var unlockStates = new AnimatorState[steps]; + const int x = 250; + const int y = 80; + layer.stateMachine.entryPosition = new Vector3(-x, 0); + layer.stateMachine.exitPosition = new Vector3(x * 4, y * (steps - 1)); + for (var step = 0; step < steps; step++) + { + var lockState = layer.stateMachine.AddState("mux" + step + "_lock", new Vector3(x * 1, y * step)); + var syncState = layer.stateMachine.AddState("mux" + step + "_sync", new Vector3(x * 2, y * step)); + var unlockState = layer.stateMachine.AddState("mux" + step + "_unlock", new Vector3(x * 3, y * step)); + + var lockToSync = lockState.AddTransition(syncState); + lockToSync.hasExitTime = false; + lockToSync.duration = 0; + AddSyncEnabledCondition(lockToSync); + + var syncToUnlock = syncState.AddTransition(unlockState); + syncToUnlock.hasExitTime = false; + syncToUnlock.duration = unlockDelay; + AddSyncEnabledCondition(syncToUnlock); + + if (step == 0) // first step + { + var transition = idle.AddTransition(lockState); + transition.hasExitTime = false; + transition.duration = delay; + AddSyncEnabledCondition(transition); + } + else + { + if (step == steps - 1) + { + var exit = unlockState.AddExitTransition(); // last step exit + exit.hasExitTime = false; + exit.duration = 0; + AddSyncEnabledCondition(exit); + } + var previousUnlock = unlockStates[step - 1]; + var transition = previousUnlock.AddTransition(lockState); + transition.hasExitTime = false; + transition.duration = interval; + AddSyncEnabledCondition(transition); + } + + var lockDriver = lockState.AddStateMachineBehaviour(); + var syncDriver = syncState.AddStateMachineBehaviour(); + var unlockDriver = unlockState.AddStateMachineBehaviour(); + + lockDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter + { + type = VRC_AvatarParameterDriver.ChangeType.Set, + name = ParameterManager.GetSyncLockName(), + value = 1 + }); + + for (var i = 0; i < syncSize; i++) + { + syncDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter + { + type = VRC_AvatarParameterDriver.ChangeType.Copy, + name = ParameterManager.GetSyncedKeyNAme(i), + source = ParameterManager.GetSavedKeyName(step * syncSize + i) + }); + } + + for (var i = 0; i < ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); i++) + { + syncDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter + { + type = VRC_AvatarParameterDriver.ChangeType.Set, + name = ParameterManager.GetSyncSwitchName(i), + value = (step & (1 << i)) != 0 ? 1 : 0 + }); + } + + unlockDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter + { + type = VRC_AvatarParameterDriver.ChangeType.Set, + name = ParameterManager.GetSyncLockName(), + value = 0 + }); + + syncStates[step] = syncState; + lockStates[step] = lockState; + unlockStates[step] = unlockState; } + } - AnimatorStateMachine stateMachine = new AnimatorStateMachine + private static void AddDemuxLayer(AnimatorController anim, int keyLength, int syncSize) + { + if (anim.layers.Any(l => l.name == "ShellProtectorDemux")) return; + + var stateMachine = new AnimatorStateMachine { - name = anim.MakeUniqueLayerName("ShellProtectorDriver"), + name = anim.MakeUniqueLayerName("ShellProtectorDemux"), hideFlags = HideFlags.HideInHierarchy }; AssetDatabase.AddObjectToAsset(stateMachine, anim); @@ -335,33 +449,30 @@ public static void AddKeyLayerMultiplexing(AnimatorController anim, string anima transition.exitTime = 0; transition.duration = 0; transition.hasExitTime = false; - transition.AddCondition(AnimatorConditionMode.If, 0, ParameterManager.GetSyncLockParameterName()); + transition.AddCondition(AnimatorConditionMode.If, 0, ParameterManager.GetSyncLockName()); - for (int i = 0; i < key_length / sync_size; ++i) + for (var i = 0; i < keyLength / syncSize; ++i) { - var key_state = layer.stateMachine.AddState("key" + i); + var keyState = layer.stateMachine.AddState("key" + i); - for (var j = 0; j < sync_size; j++) + for (var j = 0; j < syncSize; j++) { - var behaviour = key_state.AddStateMachineBehaviour(); - var behaviour_param = new VRCAvatarParameterDriver.Parameter + var behaviour = keyState.AddStateMachineBehaviour(); + behaviour.parameters.Add(new VRCAvatarParameterDriver.Parameter { - type = VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Copy, - name = ParameterManager.GetPKeyParameterName(i * sync_size + j), - source = ParameterManager.GetPKeySyncParameterName(j) - }; - behaviour.parameters.Add(behaviour_param); + type = VRC_AvatarParameterDriver.ChangeType.Copy, + name = ParameterManager.GetKeyName(i * syncSize + j), + source = ParameterManager.GetSyncedKeyNAme(j) + }); } - transition = layer.stateMachine.AddAnyStateTransition(key_state); + transition = layer.stateMachine.AddAnyStateTransition(keyState); transition.canTransitionToSelf = false; transition.exitTime = 0; transition.duration = 0; transition.hasExitTime = false; - AddTransition(transition, key_length, sync_size, i); + AddTransition(transition, keyLength, syncSize, i); } - - AddKeyLayer(anim, animation_dir, key_length, sync_size, speed, false); } public static void AddFallbackLayer(AnimatorController anim, AnimationClip fallbackAnimation, float time = 3) diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index b38f8cc..346c1cd 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -7,27 +7,39 @@ public static class ParameterManager { - public static string GetPKeySyncParameterName(int index) + private const string Prefix = "SHELL_PROTECTOR_"; + public static string GetSyncedKeyNAme(int index) { - if (index == 0) return "pkey"; // For backward compatibility - return "pkey_sync" + index; + if (index == 0) return "pkey"; // For backward compatibility (before commit e8080de) + return Prefix + "synced_key" + index; } - public static string GetPKeyParameterName(int index) => "pkey" + index; - public static string GetSyncSwitchParameterName(int index) => "encrypt_switch" + index; - public static string GetSyncLockParameterName() => "encrypt_lock"; + public static string GetKeyName(int index) => Prefix + "key" + index; + public static string GetSavedKeyName(int index) => Prefix + "saved_key" + index; + public static string GetSyncEnabledName() => Prefix + "sync_enabled"; + public static string GetSyncSwitchName(int index) => Prefix + "sync_switch" + index; + public static string GetSyncLockName() => Prefix + "sync_lock"; public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrcParameters, int keyLength, int syncSize, bool useMultiplexing) { var parameters = new List(); + parameters.Add(new VRCExpressionParameters.Parameter + { + name = GetSyncEnabledName(), + saved = false, + networkSynced = false, + valueType = VRCExpressionParameters.ValueType.Bool, + defaultValue = 1.0f + }); + if (useMultiplexing == false) { for (var i = 0; i < keyLength; ++i) { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetPKeyParameterName(i), + name = GetKeyName(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Float, @@ -39,7 +51,7 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetSyncLockParameterName(), + name = GetSyncLockName(), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Bool, @@ -50,7 +62,7 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetPKeySyncParameterName(i), + name = GetSyncedKeyNAme(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Float, @@ -62,7 +74,7 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetSyncSwitchParameterName(i), + name = GetSyncSwitchName(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Bool, @@ -74,12 +86,21 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetPKeyParameterName(i), + name = GetKeyName(i), saved = false, networkSynced = false, valueType = VRCExpressionParameters.ValueType.Float, defaultValue = 0.0f }); + + parameters.Add(new VRCExpressionParameters.Parameter + { + name = GetSavedKeyName(i), + saved = true, + networkSynced = false, + valueType = VRCExpressionParameters.ValueType.Float, + defaultValue = 0.0f + }); } } From d0b6f26f004ff289b45a4dc5f062511ceca4cef2 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Sat, 7 Dec 2024 04:05:45 +0900 Subject: [PATCH 05/59] fix: local only mux --- Runtime/Scripts/AnimatorManager.cs | 15 +++++++++------ Runtime/Scripts/ParameterManager.cs | 11 +---------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index db8c395..0867285 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -247,12 +247,15 @@ private static void AddParameters(AnimatorController anim, int keyLength, int sy type = AnimatorControllerParameterType.Float }); - anim.AddParameter(new AnimatorControllerParameter + if (anim.parameters.All(p => p.name != ParameterManager.GetIsLocalName())) { - defaultBool = true, - name = ParameterManager.GetSyncEnabledName(), - type = AnimatorControllerParameterType.Bool - }); + anim.AddParameter(new AnimatorControllerParameter + { + defaultBool = false, + name = ParameterManager.GetIsLocalName(), + type = AnimatorControllerParameterType.Bool + }); + } for (var i = 0; i < keyLength; ++i) anim.AddParameter(ParameterManager.GetKeyName(i), AnimatorControllerParameterType.Float); @@ -317,7 +320,7 @@ public static void AddKeyLayer(AnimatorController anim, string animationDir, int private static void AddSyncEnabledCondition(AnimatorStateTransition transition) { - transition.AddCondition(AnimatorConditionMode.If, 0, ParameterManager.GetSyncEnabledName()); + transition.AddCondition(AnimatorConditionMode.If, 0, ParameterManager.GetIsLocalName()); } private static void AddMuxLayer(AnimatorController anim, int keyLength, int syncSize, float unlockDelay, float interval, float delay) diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index 346c1cd..078f2c7 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -15,24 +15,15 @@ public static string GetSyncedKeyNAme(int index) } public static string GetKeyName(int index) => Prefix + "key" + index; public static string GetSavedKeyName(int index) => Prefix + "saved_key" + index; - public static string GetSyncEnabledName() => Prefix + "sync_enabled"; public static string GetSyncSwitchName(int index) => Prefix + "sync_switch" + index; public static string GetSyncLockName() => Prefix + "sync_lock"; + public static string GetIsLocalName() => "IsLocal"; public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrcParameters, int keyLength, int syncSize, bool useMultiplexing) { var parameters = new List(); - parameters.Add(new VRCExpressionParameters.Parameter - { - name = GetSyncEnabledName(), - saved = false, - networkSynced = false, - valueType = VRCExpressionParameters.ValueType.Bool, - defaultValue = 1.0f - }); - if (useMultiplexing == false) { for (var i = 0; i < keyLength; ++i) From 4b776010906f52a82805cb54c9619dc77d84d4d7 Mon Sep 17 00:00:00 2001 From: SnowyWalk <69317828+SnowyWalk@users.noreply.github.com> Date: Sun, 23 Mar 2025 20:07:45 +0900 Subject: [PATCH 06/59] =?UTF-8?q?Inspector=EC=97=90=EC=84=9C=20=EB=B3=B4?= =?UTF-8?q?=EC=97=AC=EC=A3=BC=EB=8A=94=20Key=EC=9D=98=20=EB=B2=94=EC=9C=84?= =?UTF-8?q?=EB=A5=BC=2015=20->=2016=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Key0~15까지 존재하나 Inspector에서는 Key0~14까지만 보여주고 있어서 수정 --- Editor/liltoonCustom/CustomInspector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Editor/liltoonCustom/CustomInspector.cs b/Editor/liltoonCustom/CustomInspector.cs index 9829dea..2ca6fba 100644 --- a/Editor/liltoonCustom/CustomInspector.cs +++ b/Editor/liltoonCustom/CustomInspector.cs @@ -11,7 +11,7 @@ public class ShellProtectorInspector : lilToonInspector MaterialProperty mip_tex; MaterialProperty encrypted_tex0; MaterialProperty encrypted_tex1; - MaterialProperty[] key = new MaterialProperty[15]; + MaterialProperty[] key = new MaterialProperty[16]; MaterialProperty fallback; private static bool isShowCustomProperties; @@ -165,4 +165,4 @@ private static void ConvertMaterialToCustomShaderMenu() } } #endif -#endif \ No newline at end of file +#endif From 70976384bd12217efd6ec987ace31315e43096b8 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Sun, 23 Mar 2025 20:23:46 +0900 Subject: [PATCH 07/59] Refactor: deduplicate shader/injector codes --- Runtime/Decrypt.cginc | 166 ----------------- Runtime/Decrypt.cginc.meta | 9 - Runtime/DecryptChacha.cginc | 168 ------------------ Runtime/Scripts/Algorithm/Chacha20.cs | 1 + Runtime/Scripts/Algorithm/IEncryptor.cs | 2 + Runtime/Scripts/Algorithm/XXTEA.cs | 1 + Runtime/Scripts/Injector/Injector.cs | 47 ++++- Runtime/Scripts/Injector/LilToonInjector.cs | 34 +--- Runtime/Scripts/Injector/PoiyomiInjector.cs | 20 +-- Runtime/Scripts/ShellProtector.cs | 24 +-- Runtime/Shader.meta | 8 + Runtime/{ => Shader}/Chacha.cginc | 4 +- Runtime/{ => Shader}/Chacha.cginc.meta | 0 Runtime/Shader/DXT.cginc | 62 +++++++ .../DXT.cginc.meta} | 2 +- Runtime/Shader/RGB.cginc | 40 +++++ .../RGB.cginc.meta} | 2 +- Runtime/Shader/RGBA.cginc | 34 ++++ .../RGBA.cginc.meta} | 2 +- Runtime/Shader/ShellProtector.cginc | 66 +++++++ .../ShellProtector.cginc.meta} | 2 +- Runtime/Shader/Utility.cginc | 21 +++ Runtime/Shader/Utility.cginc.meta | 7 + Runtime/{ => Shader}/XXTEA.cginc | 5 +- Runtime/{ => Shader}/XXTEA.cginc.meta | 0 Runtime/liltoonProtector/Shaders/Chacha.cginc | 107 ----------- .../liltoonProtector/Shaders/Decrypt.cginc | 166 ----------------- .../Shaders/DecryptChacha.cginc | 168 ------------------ Runtime/liltoonProtector/Shaders/custom.hlsl | 25 +-- .../Shaders/custom_insert.hlsl | 15 +- 30 files changed, 319 insertions(+), 889 deletions(-) delete mode 100644 Runtime/Decrypt.cginc delete mode 100644 Runtime/Decrypt.cginc.meta delete mode 100644 Runtime/DecryptChacha.cginc create mode 100644 Runtime/Shader.meta rename Runtime/{ => Shader}/Chacha.cginc (95%) rename Runtime/{ => Shader}/Chacha.cginc.meta (100%) create mode 100644 Runtime/Shader/DXT.cginc rename Runtime/{DecryptChacha.cginc.meta => Shader/DXT.cginc.meta} (75%) create mode 100644 Runtime/Shader/RGB.cginc rename Runtime/{liltoonProtector/Shaders/Chacha.cginc.meta => Shader/RGB.cginc.meta} (75%) create mode 100644 Runtime/Shader/RGBA.cginc rename Runtime/{liltoonProtector/Shaders/DecryptChacha.cginc.meta => Shader/RGBA.cginc.meta} (75%) create mode 100644 Runtime/Shader/ShellProtector.cginc rename Runtime/{liltoonProtector/Shaders/Decrypt.cginc.meta => Shader/ShellProtector.cginc.meta} (75%) create mode 100644 Runtime/Shader/Utility.cginc create mode 100644 Runtime/Shader/Utility.cginc.meta rename Runtime/{ => Shader}/XXTEA.cginc (90%) rename Runtime/{ => Shader}/XXTEA.cginc.meta (100%) delete mode 100644 Runtime/liltoonProtector/Shaders/Chacha.cginc delete mode 100644 Runtime/liltoonProtector/Shaders/Decrypt.cginc delete mode 100644 Runtime/liltoonProtector/Shaders/DecryptChacha.cginc diff --git a/Runtime/Decrypt.cginc b/Runtime/Decrypt.cginc deleted file mode 100644 index 7de41a0..0000000 --- a/Runtime/Decrypt.cginc +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once -#include "XXTEA.cginc" - -static const uint mw[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; -static const uint mh[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; - -float _Key0, _Key1, _Key2, _Key3, _Key4, _Key5, _Key6, _Key7, _Key8, _Key9, _Key10, _Key11, _Key12, _Key13, _Key14, _Key15; - -uint _Woffset; -uint _Hoffset; - -half3 InverseGammaCorrection(half3 rgb) -{ - half3 result = pow(rgb, 0.454545); - return result; -} -half3 GammaCorrection(half3 rgb) -{ - //half3 result = pow(rgb, 2.2); - half3 result = rgb * rgb * (rgb * (half)0.2 + (half)0.8); //fast pow - return result; -} - -half2 GetUV(int idx, int m, int woffset = 0, int hoffset = 0) -{ - int w = idx % mw[m + woffset]; - int h = idx / mw[m + woffset]; - return half2((half)w/mw[m + woffset], (half)h/mh[m + hoffset]); -} - -half4 DecryptTexture(half2 uv, int m) -{ - half x = frac(uv.x); - half y = frac(uv.y); - - int idx = (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 2) << 2) - }; - - half3 pixels[4]; - - const int pos[4] = { 0, -1, -2, -3 }; - int offset = pos[idx % 4]; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[2] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 2 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[3] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 3 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[3] = { 0, 0, 0 }; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[1].r * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].g * 255.0f) | ((uint)round(pixels[1].b * 255.0f) << 8) | ((uint)round(pixels[2].r * 255.0f) << 16) | ((uint)round(pixels[2].g * 255.0f) << 24)); - data[2] = ((uint)round(pixels[2].b * 255.0f) | ((uint)round(pixels[3].r * 255.0f) << 8) | ((uint)round(pixels[3].g * 255.0f) << 16) | ((uint)round(pixels[3].b * 255.0f) << 24)); - - XXTEADecrypt(data, key); - - half r[4] = { (data[0] & 0x000000FF)/255.0f, ((data[0] & 0xFF000000) >> 24)/255.0f, ((data[1] & 0x00FF0000) >> 16)/255.0f, ((data[2] & 0x0000FF00) >> 8)/255.0f }; - half g[4] = { ((data[0] & 0x0000FF00) >> 8)/255.0f, ((data[1] & 0x000000FF) >> 0)/255.0f, ((data[1] & 0xFF000000) >> 24)/255.0f, ((data[2] & 0x00FF0000) >> 16)/255.0f }; - half b[4] = { ((data[0] & 0x00FF0000) >> 16)/255.0f, ((data[1] & 0x0000FF00) >> 8)/255.0f, ((data[2] & 0x000000FF) >> 0)/255.0f, ((data[2] & 0xFF000000) >> 24)/255.0f }; - half3 decrypt = half3(r[idx % 4], g[idx % 4], b[idx % 4]); - - return half4(GammaCorrection(decrypt), 1.0); -} -half4 DecryptTextureRGBA(half2 uv, int m) -{ - half x = frac(uv.x); - half y = frac(uv.y); - - int idx = (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 1) << 1) - }; - - half4 pixels[2]; - - int offset = (idx & 1) == 0 ? 0 : -1; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[2] = { 0, 0 }; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); - - XXTEADecrypt(data, key); - - half r = ((data[idx & 1] & 0x000000FF) >> 0)/255.0f; - half g = ((data[idx & 1] & 0x0000FF00) >> 8)/255.0f; - half b = ((data[idx & 1] & 0x00FF0000) >> 16)/255.0f; - half a = ((data[idx & 1] & 0xFF000000) >> 24)/255.0f; - half4 decrypt = half4(r, g, b, a); - - return half4(GammaCorrection(decrypt.rgb), decrypt.a); -} -half4 DecryptTextureDXT(half2 uv, int m) -{ - half2 frac_uv = frac(uv.xy); - half x = frac_uv.x; - half y = frac_uv.y; - - half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); - - uint w = mw[m + (uint)_Woffset]; - uint h = mh[m + _Hoffset]; - - int idx = (w * floor(y * h)) + floor(x * w); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 1) << 1) - }; - - int offset = (idx & 1) == 0 ? 0 : -1; - - half4 pixels[2]; - pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[2]; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); - - XXTEADecrypt(data, key); - - uint r = (data[idx & 1] & 0x000000FF) >> 0; - uint g = (data[idx & 1] & 0x0000FF00) >> 8; - uint b = (data[idx & 1] & 0x00FF0000) >> 16; - uint a = (data[idx & 1] & 0xFF000000) >> 24; - - uint color1 = (r | g << 8); - uint color2 = (b | a << 8); - - uint color1_r = (color1 & 0xF800) >> 11; - color1_r = color1_r << 3 | color1_r >> 2; - uint color1_g = (color1 & 0x7E0) >> 5; - color1_g = color1_g << 2 | color1_g >> 4; - uint color1_b= color1 & 0x1F; - color1_b = color1_b << 3 | color1_b >> 2; - - uint color2_r = (color2 & 0xF800) >> 11; - color2_r = color2_r << 3 | color2_r >> 2; - uint color2_g = (color2 & 0x7E0) >> 5; - color2_g = color2_g << 2 | color2_g >> 4; - uint color2_b= color2 & 0x1F; - color2_b = color2_b << 3 | color2_b >> 2; - - half3 col1 = half3(color1_r / (half)255, color1_g / (half)255, color1_b / (half)255); - half3 col2 = half3(color2_r / (half)255, color2_g / (half)255, color2_b / (half)255); - - half3 result; - result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); - - return half4(GammaCorrection(result), col.a); -} \ No newline at end of file diff --git a/Runtime/Decrypt.cginc.meta b/Runtime/Decrypt.cginc.meta deleted file mode 100644 index 8a22f0d..0000000 --- a/Runtime/Decrypt.cginc.meta +++ /dev/null @@ -1,9 +0,0 @@ -fileFormatVersion: 2 -guid: 6ccf8c747e8388f42b93c42f1f2bd7ab -ShaderImporter: - externalObjects: {} - defaultTextures: [] - nonModifiableTextures: [] - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/DecryptChacha.cginc b/Runtime/DecryptChacha.cginc deleted file mode 100644 index 3264d5c..0000000 --- a/Runtime/DecryptChacha.cginc +++ /dev/null @@ -1,168 +0,0 @@ -#pragma once -#include "Chacha.cginc" - -static const uint mw[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; -static const uint mh[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; - -float _Key0, _Key1, _Key2, _Key3, _Key4, _Key5, _Key6, _Key7, _Key8, _Key9, _Key10, _Key11, _Key12, _Key13, _Key14, _Key15; - -uint _Woffset; -uint _Hoffset; - -half3 InverseGammaCorrection(half3 rgb) -{ - half3 result = pow(rgb, 0.454545); - return result; -} -half3 GammaCorrection(half3 rgb) -{ - //half3 result = pow(rgb, 2.2); - half3 result = rgb * rgb * (rgb * (half)0.2 + (half)0.8); //fast pow - return result; -} - -half2 GetUV(int idx, int m, int woffset = 0, int hoffset = 0) -{ - int w = idx % mw[m + woffset]; - int h = idx / mw[m + woffset]; - return half2((half)w/mw[m + woffset], (half)h/mh[m + hoffset]); -} - -half4 DecryptTexture(half2 uv, int m) -{ - half x = frac(uv.x); - half y = frac(uv.y); - - int idx = (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 2) << 2) - }; - - half3 pixels[4]; - - const int pos[4] = { 0, -1, -2, -3 }; - int offset = pos[idx % 4]; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[2] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 2 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[3] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 3 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[3] = { 0, 0, 0 }; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[1].r * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].g * 255.0f) | ((uint)round(pixels[1].b * 255.0f) << 8) | ((uint)round(pixels[2].r * 255.0f) << 16) | ((uint)round(pixels[2].g * 255.0f) << 24)); - data[2] = ((uint)round(pixels[2].b * 255.0f) | ((uint)round(pixels[3].r * 255.0f) << 8) | ((uint)round(pixels[3].g * 255.0f) << 16) | ((uint)round(pixels[3].b * 255.0f) << 24)); - - Chacha20XOR(data, key); - - half r[4] = { (data[0] & 0x000000FF)/255.0f, ((data[0] & 0xFF000000) >> 24)/255.0f, ((data[1] & 0x00FF0000) >> 16)/255.0f, ((data[2] & 0x0000FF00) >> 8)/255.0f }; - half g[4] = { ((data[0] & 0x0000FF00) >> 8)/255.0f, ((data[1] & 0x000000FF) >> 0)/255.0f, ((data[1] & 0xFF000000) >> 24)/255.0f, ((data[2] & 0x00FF0000) >> 16)/255.0f }; - half b[4] = { ((data[0] & 0x00FF0000) >> 16)/255.0f, ((data[1] & 0x0000FF00) >> 8)/255.0f, ((data[2] & 0x000000FF) >> 0)/255.0f, ((data[2] & 0xFF000000) >> 24)/255.0f }; - half3 decrypt = half3(r[idx % 4], g[idx % 4], b[idx % 4]); - - return half4(GammaCorrection(decrypt), 1.0); -} -half4 DecryptTextureRGBA(half2 uv, int m) -{ - half x = frac(uv.x); - half y = frac(uv.y); - - int idx = (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 1) << 1) - }; - - half4 pixels[2]; - - int offset = (idx & 1) == 0 ? 0 : -1; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[2] = { 0, 0 }; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); - - Chacha20XOR(data, key); - - half r = ((data[idx & 1] & 0x000000FF) >> 0)/255.0f; - half g = ((data[idx & 1] & 0x0000FF00) >> 8)/255.0f; - half b = ((data[idx & 1] & 0x00FF0000) >> 16)/255.0f; - half a = ((data[idx & 1] & 0xFF000000) >> 24)/255.0f; - half4 decrypt = half4(r, g, b, a); - - return half4(GammaCorrection(decrypt.rgb), decrypt.a); -} -half4 DecryptTextureDXT(half2 uv, int m) -{ - half2 frac_uv = frac(uv.xy); - half x = frac_uv.x; - half y = frac_uv.y; - - int miplv = 0; - - half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); - - uint w = mw[m + (uint)_Woffset]; - uint h = mh[m + _Hoffset]; - - int idx = (w * floor(y * h)) + floor(x * w); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 1) << 1) - }; - - int offset = (idx & 1) == 0 ? 0 : -1; - - half4 pixels[2]; - pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[2]; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); - - Chacha20XOR(data, key); - - uint r = (data[idx & 1] & 0x000000FF) >> 0; - uint g = (data[idx & 1] & 0x0000FF00) >> 8; - uint b = (data[idx & 1] & 0x00FF0000) >> 16; - uint a = (data[idx & 1] & 0xFF000000) >> 24; - - uint color1 = (r | g << 8); - uint color2 = (b | a << 8); - - uint color1_r = (color1 & 0xF800) >> 11; - color1_r = color1_r << 3 | color1_r >> 2; - uint color1_g = (color1 & 0x7E0) >> 5; - color1_g = color1_g << 2 | color1_g >> 4; - uint color1_b= color1 & 0x1F; - color1_b = color1_b << 3 | color1_b >> 2; - - uint color2_r = (color2 & 0xF800) >> 11; - color2_r = color2_r << 3 | color2_r >> 2; - uint color2_g = (color2 & 0x7E0) >> 5; - color2_g = color2_g << 2 | color2_g >> 4; - uint color2_b= color2 & 0x1F; - color2_b = color2_b << 3 | color2_b >> 2; - - half3 col1 = half3(color1_r / (half)255, color1_g / (half)255, color1_b / (half)255); - half3 col2 = half3(color2_r / (half)255, color2_g / (half)255, color2_b / (half)255); - - half3 result; - result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); - - return half4(GammaCorrection(result), col.a); -} \ No newline at end of file diff --git a/Runtime/Scripts/Algorithm/Chacha20.cs b/Runtime/Scripts/Algorithm/Chacha20.cs index 4e7185e..5e3c352 100644 --- a/Runtime/Scripts/Algorithm/Chacha20.cs +++ b/Runtime/Scripts/Algorithm/Chacha20.cs @@ -7,6 +7,7 @@ namespace Shell.Protector { public class Chacha20 : IEncryptor { + public string Keyword => "_SHELL_PROTECTOR_CHACHA"; public byte[] nonce = new byte[12]; [MethodImpl(MethodImplOptions.AggressiveInlining)] byte[] U32t8le(uint v) diff --git a/Runtime/Scripts/Algorithm/IEncryptor.cs b/Runtime/Scripts/Algorithm/IEncryptor.cs index c112ec7..8eb6918 100644 --- a/Runtime/Scripts/Algorithm/IEncryptor.cs +++ b/Runtime/Scripts/Algorithm/IEncryptor.cs @@ -2,6 +2,8 @@ namespace Shell.Protector { public interface IEncryptor { + public string Keyword { get; } + #if UNITY_2022 public uint[] Encrypt(uint[] data, uint[] key); public uint[] Decrypt(uint[] data, uint[] key); diff --git a/Runtime/Scripts/Algorithm/XXTEA.cs b/Runtime/Scripts/Algorithm/XXTEA.cs index fa91c22..acdf12e 100644 --- a/Runtime/Scripts/Algorithm/XXTEA.cs +++ b/Runtime/Scripts/Algorithm/XXTEA.cs @@ -5,6 +5,7 @@ namespace Shell.Protector { public class XXTEA : IEncryptor { + public string Keyword => "_SHELL_PROTECTOR_XXTEA"; const uint Delta = 0x9E3779B9; public uint m_rounds = 0; diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index 86345a7..e1677c3 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -117,7 +117,52 @@ public bool WasInjected(Shader shader) return false; } - abstract public Shader Inject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false); + private void SetKeywords(Material material, bool has_lim_texture = false) { + // Clear keywords prefixed with _SHELL_PROTECTOR_ + var keywords = material.shaderKeywords; + foreach (string keyword in keywords) + { + if (keyword.StartsWith("_SHELL_PROTECTOR_")) { + material.DisableKeyword(keyword); + } + } + + // Set format keywords + TextureFormat format = ((Texture2D)material.mainTexture).format; + if (format == TextureFormat.DXT1 || format == TextureFormat.DXT5) + { + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); + } + else if (format == TextureFormat.RGBA32) + { + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.EnableKeyword("_SHELL_PROTECTOR_FORMAT1"); + } + else if (format == TextureFormat.RGB24) + { + material.EnableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); + } + else + { + Debug.LogErrorFormat("{0} - main texture is unsupported format!", material.name); + return; + } + + // Set rimlight keyword + if (has_lim_texture) material.EnableKeyword("_SHELL_PROTECTOR_RIMLIGHT"); + + // Set encryptor keyword + material.EnableKeyword(encryptor.Keyword); + } + + public Shader Inject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) { + SetKeywords(mat, has_lim_texture); + return CustomInject(mat, decode_dir, output_dir, tex, has_lim_texture, has_lim_texture2, outline_tex); + } + + protected abstract Shader CustomInject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false); } } #endif \ No newline at end of file diff --git a/Runtime/Scripts/Injector/LilToonInjector.cs b/Runtime/Scripts/Injector/LilToonInjector.cs index 91f4441..bd34dd6 100644 --- a/Runtime/Scripts/Injector/LilToonInjector.cs +++ b/Runtime/Scripts/Injector/LilToonInjector.cs @@ -13,40 +13,8 @@ namespace Shell.Protector { public class LilToonInjector : Injector { - public override Shader Inject(Material mat, string decode_dir, string output_path, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) + protected override Shader CustomInject(Material mat, string decode_dir, string output_path, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) { - // Keyword setting - TextureFormat format = ((Texture2D)mat.mainTexture).format; - if (format == TextureFormat.DXT1 || format == TextureFormat.DXT5) - { - mat.DisableKeyword("_FORMAT0"); - mat.DisableKeyword("_FORMAT1"); - } - else if (format == TextureFormat.RGBA32) - { - mat.DisableKeyword("_FORMAT0"); - mat.EnableKeyword("_FORMAT1"); - } - else if (format == TextureFormat.RGB24) - { - mat.EnableKeyword("_FORMAT0"); - mat.DisableKeyword("_FORMAT1"); - } - else - { - Debug.LogErrorFormat("{0} - main texture is unsupported format!", mat.name); - return null; - } - if (has_lim_texture) - mat.EnableKeyword("_LIMLIGHT_ENCRYPTED"); - else - mat.DisableKeyword("_LIMLIGHT_ENCRYPTED"); - - if ((this.encryptor as XXTEA) != null) - mat.EnableKeyword("_XXTEA"); - else - mat.DisableKeyword("_XXTEA"); - string[] files = Directory.GetFiles(Path.Combine(asset_dir, "liltoonProtector", "Shaders")); // Find pass string shader_dir = AssetDatabase.GetAssetPath(mat.shader); diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index 3ad0818..048b327 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -10,7 +10,7 @@ namespace Shell.Protector { public class PoiyomiInjector : Injector { - public override Shader Inject(Material mat, string decode_dir, string output_path, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) + protected override Shader CustomInject(Material mat, string decode_dir, string output_path, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) { if (!File.Exists(decode_dir)) { @@ -28,7 +28,6 @@ public override Shader Inject(Material mat, string decode_dir, string output_pat string shader_path = AssetDatabase.GetAssetPath(shader); string shader_name = Path.GetFileName(shader_path); - string[] files = Directory.GetFiles(Path.GetDirectoryName(shader_path)); foreach (string file in files) @@ -75,14 +74,6 @@ public override Shader Inject(Material mat, string decode_dir, string output_pat frag = Regex.Replace(frag, "float4 mainTexture = .*?;", shader_code); frag = Regex.Replace(frag, "float4 mip_texture = _MipTex.Sample\\(sampler_MipTex, .*?\\);", "float4 mip_texture = _MipTex.Sample(sampler_MipTex, poiMesh.uv[0]);"); - if (tex.format == TextureFormat.DXT1) - { - frag = Regex.Replace(frag, "DecryptTexture", "DecryptTextureDXT"); - } - else if (EncryptTexture.HasAlpha(tex)) - { - frag = Regex.Replace(frag, "DecryptTexture", "DecryptTextureRGBA"); - } File.WriteAllText(path, frag); } else if(version >= 80) @@ -100,15 +91,6 @@ public override Shader Inject(Material mat, string decode_dir, string output_pat shader_code = shader_code_bilinear; shader_data = Regex.Replace(shader_data, "float4 mainTexture = .*?;", shader_code); - if (tex.format == TextureFormat.DXT1 || tex.format == TextureFormat.DXT5) - { - shader_data = Regex.Replace(shader_data, "DecryptTexture", "DecryptTextureDXT"); - } - else if (EncryptTexture.HasAlpha(tex)) - { - shader_data = Regex.Replace(shader_data, "DecryptTexture", "DecryptTextureRGBA"); - } - if (has_lim_texture) { if(version == 80) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index c4b58ca..4aaf030 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -18,6 +18,7 @@ #if POIYOMI using Thry; +using Thry.ThryEditor; #endif namespace Shell.Protector @@ -540,14 +541,15 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) { try { - string decodeDir = ""; - if (algorithm == (int)Algorithm.xxtea) - decodeDir = Path.Combine(asset_dir, "Decrypt.cginc"); - else if (algorithm == (int)Algorithm.chacha) - decodeDir = Path.Combine(asset_dir, "DecryptChacha.cginc"); - - encrypted_shader = injector.Inject(mat, decodeDir, encrypted_shader_path, encrypted_tex[0], - otherTex.limTexture != null, otherTex.limTexture2 != null, otherTex.outlineTexture != null); + encrypted_shader = injector.Inject( + mat, + Path.Combine(asset_dir, "Shader/ShellProtector.cginc"), + encrypted_shader_path, + encrypted_tex[0], + otherTex.limTexture != null, + otherTex.limTexture2 != null, + otherTex.outlineTexture != null + ); Selection.activeObject = encrypted_shader; EditorApplication.ExecuteMenuItem("Assets/Reimport"); @@ -732,10 +734,10 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) SetAnimations(avatar, true); ObfuscateBlendShape(avatar, true); ChangeMaterialsInAnims(avatar, true); - CleanComponent(avatar); + CleanComponent(avatar); } - + return avatar; } @@ -835,7 +837,7 @@ public void RemoveDuplicatedTextures(GameObject avatar) { Texture2D mainTexture = (Texture2D)mat.GetTexture(name); Texture2D encrypted0 = processedTextures[(Texture2D)mat.GetTexture(name)].encrypted0; - + int idx = processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.IndexOf(processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.Max()); Texture2D bigFallbackTexture = processedTextures[(Texture2D)mat.GetTexture(name)].fallbacks[idx]; diff --git a/Runtime/Shader.meta b/Runtime/Shader.meta new file mode 100644 index 0000000..2afdcbd --- /dev/null +++ b/Runtime/Shader.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 599f7af837cb58b41b622c0616b8fedf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Chacha.cginc b/Runtime/Shader/Chacha.cginc similarity index 95% rename from Runtime/Chacha.cginc rename to Runtime/Shader/Chacha.cginc index 3aba812..39ded16 100644 --- a/Runtime/Chacha.cginc +++ b/Runtime/Shader/Chacha.cginc @@ -17,7 +17,7 @@ void Chacha20QuarterRound(inout uint state[16], int a, int b, int c, int d) state[c] += state[d]; state[b] = Rotl32(state[b] ^ state[c], 7); } -void Chacha20XOR(inout uint data[2], const uint key[4]) +void Decrypt(inout uint data[2], const uint key[4]) { uint4 state[4]; state[0] = uint4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574); @@ -61,7 +61,7 @@ void Chacha20XOR(inout uint data[2], const uint key[4]) data[1] ^= block[1]; } -void Chacha20XOR(inout uint data[3], const uint key[4]) +void Decrypt(inout uint data[3], const uint key[4]) { uint4 state[4]; state[0] = uint4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574); diff --git a/Runtime/Chacha.cginc.meta b/Runtime/Shader/Chacha.cginc.meta similarity index 100% rename from Runtime/Chacha.cginc.meta rename to Runtime/Shader/Chacha.cginc.meta diff --git a/Runtime/Shader/DXT.cginc b/Runtime/Shader/DXT.cginc new file mode 100644 index 0000000..460420f --- /dev/null +++ b/Runtime/Shader/DXT.cginc @@ -0,0 +1,62 @@ +#pragma once + +#define _SHELL_PROTECTOR_DATA_LENGTH 2 +#define _SHELL_PROTECTOR_INDEX_ALIGNMENT 1 + +int GetIndex(half2 uv, int m) { + half x = frac(uv.x); + half y = frac(uv.y); + + uint w = mw[m + (uint)_Woffset]; + uint h = mh[m + _Hoffset]; + + return (w * floor(y * h)) + floor(x * w); +} + +void GetData(inout uint data[2], half2 uv, int m) { + int idx = GetIndex(uv, m); + int offset = (idx & 1) == 0 ? 0 : -1; + + half4 pixels[2]; + pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); + + data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); + data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); +} + +half4 GetPixel(inout uint data[2], half2 uv, int m) { + int idx = GetIndex(uv, m); + int offset = (idx & 1) == 0 ? 0 : -1; + + half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); + uint r = (data[idx & 1] & 0x000000FF) >> 0; + uint g = (data[idx & 1] & 0x0000FF00) >> 8; + uint b = (data[idx & 1] & 0x00FF0000) >> 16; + uint a = (data[idx & 1] & 0xFF000000) >> 24; + + uint color1 = (r | g << 8); + uint color2 = (b | a << 8); + + uint color1_r = (color1 & 0xF800) >> 11; + color1_r = color1_r << 3 | color1_r >> 2; + uint color1_g = (color1 & 0x7E0) >> 5; + color1_g = color1_g << 2 | color1_g >> 4; + uint color1_b= color1 & 0x1F; + color1_b = color1_b << 3 | color1_b >> 2; + + uint color2_r = (color2 & 0xF800) >> 11; + color2_r = color2_r << 3 | color2_r >> 2; + uint color2_g = (color2 & 0x7E0) >> 5; + color2_g = color2_g << 2 | color2_g >> 4; + uint color2_b= color2 & 0x1F; + color2_b = color2_b << 3 | color2_b >> 2; + + half3 col1 = half3(color1_r / (half)255, color1_g / (half)255, color1_b / (half)255); + half3 col2 = half3(color2_r / (half)255, color2_g / (half)255, color2_b / (half)255); + + half3 result; + result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); + + return half4(GammaCorrection(result), col.a); +} \ No newline at end of file diff --git a/Runtime/DecryptChacha.cginc.meta b/Runtime/Shader/DXT.cginc.meta similarity index 75% rename from Runtime/DecryptChacha.cginc.meta rename to Runtime/Shader/DXT.cginc.meta index 65c8587..f9a14de 100644 --- a/Runtime/DecryptChacha.cginc.meta +++ b/Runtime/Shader/DXT.cginc.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8415b59f27260de40b4bf81e1a40a220 +guid: a9274845e18784e469fcc9181c739619 ShaderIncludeImporter: externalObjects: {} userData: diff --git a/Runtime/Shader/RGB.cginc b/Runtime/Shader/RGB.cginc new file mode 100644 index 0000000..87d68de --- /dev/null +++ b/Runtime/Shader/RGB.cginc @@ -0,0 +1,40 @@ +#pragma once + +#define _SHELL_PROTECTOR_DATA_LENGTH 3 +#define _SHELL_PROTECTOR_INDEX_ALIGNMENT 2 + +int GetIndex(half2 uv, int m) { + half x = frac(uv.x); + half y = frac(uv.y); + + return (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); +} + +void GetData(inout uint data[3], half2 uv, int m) { + const int pos[4] = { 0, -1, -2, -3 }; + int idx = GetIndex(uv, m); + int offset = pos[idx % 4]; + + half3 pixels[4]; + pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[2] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 2 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[3] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 3 + offset, m, (uint)_Woffset, _Hoffset), m); + + data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[1].r * 255.0f) << 24)); + data[1] = ((uint)round(pixels[1].g * 255.0f) | ((uint)round(pixels[1].b * 255.0f) << 8) | ((uint)round(pixels[2].r * 255.0f) << 16) | ((uint)round(pixels[2].g * 255.0f) << 24)); + data[2] = ((uint)round(pixels[2].b * 255.0f) | ((uint)round(pixels[3].r * 255.0f) << 8) | ((uint)round(pixels[3].g * 255.0f) << 16) | ((uint)round(pixels[3].b * 255.0f) << 24)); +} + +half4 GetPixel(inout uint data[3], half2 uv, int m) { + const int pos[4] = { 0, -1, -2, -3 }; + int idx = GetIndex(uv, m); + int offset = pos[idx % 4]; + + half r[4] = { (data[0] & 0x000000FF)/255.0f, ((data[0] & 0xFF000000) >> 24)/255.0f, ((data[1] & 0x00FF0000) >> 16)/255.0f, ((data[2] & 0x0000FF00) >> 8)/255.0f }; + half g[4] = { ((data[0] & 0x0000FF00) >> 8)/255.0f, ((data[1] & 0x000000FF) >> 0)/255.0f, ((data[1] & 0xFF000000) >> 24)/255.0f, ((data[2] & 0x00FF0000) >> 16)/255.0f }; + half b[4] = { ((data[0] & 0x00FF0000) >> 16)/255.0f, ((data[1] & 0x0000FF00) >> 8)/255.0f, ((data[2] & 0x000000FF) >> 0)/255.0f, ((data[2] & 0xFF000000) >> 24)/255.0f }; + half3 decrypt = half3(r[idx % 4], g[idx % 4], b[idx % 4]); + + return half4(GammaCorrection(decrypt), 1.0); +} \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/Chacha.cginc.meta b/Runtime/Shader/RGB.cginc.meta similarity index 75% rename from Runtime/liltoonProtector/Shaders/Chacha.cginc.meta rename to Runtime/Shader/RGB.cginc.meta index 903c703..e418a56 100644 --- a/Runtime/liltoonProtector/Shaders/Chacha.cginc.meta +++ b/Runtime/Shader/RGB.cginc.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: bac1c2f2fa5382f4db5d3eb76b0bbde0 +guid: e28ddfbe363ef0c4da64b16b00ecd55d ShaderIncludeImporter: externalObjects: {} userData: diff --git a/Runtime/Shader/RGBA.cginc b/Runtime/Shader/RGBA.cginc new file mode 100644 index 0000000..ec96a53 --- /dev/null +++ b/Runtime/Shader/RGBA.cginc @@ -0,0 +1,34 @@ +#pragma once + +#define _SHELL_PROTECTOR_DATA_LENGTH 2 +#define _SHELL_PROTECTOR_INDEX_ALIGNMENT 1 + +int GetIndex(half2 uv, int m) { + half x = frac(uv.x); + half y = frac(uv.y); + + return (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); +} + +void GetData(inout uint data[2], half2 uv, int m) { + int idx = GetIndex(uv, m); + int offset = (idx & 1) == 0 ? 0 : -1; + + half4 pixels[2]; + pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); + + data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); + data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); +} + +half4 GetPixel(inout uint data[2], half2 uv, int m) { + int idx = GetIndex(uv, m); + half r = ((data[idx & 1] & 0x000000FF) >> 0)/255.0f; + half g = ((data[idx & 1] & 0x0000FF00) >> 8)/255.0f; + half b = ((data[idx & 1] & 0x00FF0000) >> 16)/255.0f; + half a = ((data[idx & 1] & 0xFF000000) >> 24)/255.0f; + half4 decrypt = half4(r, g, b, a); + + return half4(GammaCorrection(decrypt.rgb), decrypt.a); +} \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc.meta b/Runtime/Shader/RGBA.cginc.meta similarity index 75% rename from Runtime/liltoonProtector/Shaders/DecryptChacha.cginc.meta rename to Runtime/Shader/RGBA.cginc.meta index 0326a0e..1f34279 100644 --- a/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc.meta +++ b/Runtime/Shader/RGBA.cginc.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6d4b1b13948575f48ba55aebb4d099b1 +guid: 29d308e1129c5f447913ca44292e60cd ShaderIncludeImporter: externalObjects: {} userData: diff --git a/Runtime/Shader/ShellProtector.cginc b/Runtime/Shader/ShellProtector.cginc new file mode 100644 index 0000000..cdd5f20 --- /dev/null +++ b/Runtime/Shader/ShellProtector.cginc @@ -0,0 +1,66 @@ +#pragma once + +#pragma shader_feature_local _SHELL_PROTECTOR_XXTEA +#pragma shader_feature_local _SHELL_PROTECTOR_CHACHA +#pragma shader_feature_local _SHELL_PROTECTOR_FORMAT0 +#pragma shader_feature_local _SHELL_PROTECTOR_FORMAT1 +#pragma shader_feature_local _SHELL_PROTECTOR_RIMLIGHT + +// Format keywords +#if !_SHELL_PROTECTOR_FORMAT0 && !_SHELL_PROTECTOR_FORMAT1 + #define _SHELL_PROTECTOR_DXT +#elif !_SHELL_PROTECTOR_FORMAT0 && _SHELL_PROTECTOR_FORMAT1 + #define _SHELL_PROTECTOR_RGBA +#elif _SHELL_PROTECTOR_FORMAT0 && !_SHELL_PROTECTOR_FORMAT1 + #define _SHELL_PROTECTOR_RGB +#else + #error "Unsupported format" +#endif + +static const uint mw[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; +static const uint mh[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; + +float _Key0, _Key1, _Key2, _Key3, _Key4, _Key5, _Key6, _Key7, _Key8, _Key9, _Key10, _Key11, _Key12, _Key13, _Key14, _Key15; + +uint _Woffset; +uint _Hoffset; + +#include "Utility.cginc" + +// Ciphers +#ifdef _SHELL_PROTECTOR_XXTEA + #include "XXTEA.cginc" +#endif + +#ifdef _SHELL_PROTECTOR_CHACHA + #include "Chacha.cginc" +#endif + +#ifdef _SHELL_PROTECTOR_RGB + #include "RGB.cginc" +#endif + +#ifdef _SHELL_PROTECTOR_RGBA + #include "RGBA.cginc" +#endif + +#ifdef _SHELL_PROTECTOR_DXT + #include "DXT.cginc" +#endif + +half4 DecryptTexture(half2 uv, int m) +{ + int idx = GetIndex(uv, m); + const uint key[4] = + { + ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), + ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), + ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), + ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> _SHELL_PROTECTOR_INDEX_ALIGNMENT) << _SHELL_PROTECTOR_INDEX_ALIGNMENT) + }; + + uint data[_SHELL_PROTECTOR_DATA_LENGTH]; + GetData(data, uv, m); + Decrypt(data, key); + return GetPixel(data, uv, m); +} \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/Decrypt.cginc.meta b/Runtime/Shader/ShellProtector.cginc.meta similarity index 75% rename from Runtime/liltoonProtector/Shaders/Decrypt.cginc.meta rename to Runtime/Shader/ShellProtector.cginc.meta index 290c859..c108dad 100644 --- a/Runtime/liltoonProtector/Shaders/Decrypt.cginc.meta +++ b/Runtime/Shader/ShellProtector.cginc.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 93face655656f7a4d807abb62b9443c6 +guid: 49cf558e431d30f47bc14bde24eb9d49 ShaderIncludeImporter: externalObjects: {} userData: diff --git a/Runtime/Shader/Utility.cginc b/Runtime/Shader/Utility.cginc new file mode 100644 index 0000000..8f9b665 --- /dev/null +++ b/Runtime/Shader/Utility.cginc @@ -0,0 +1,21 @@ +#pragma once + +half3 InverseGammaCorrection(half3 rgb) +{ + half3 result = pow(rgb, 0.454545); + return result; +} + +half3 GammaCorrection(half3 rgb) +{ + //half3 result = pow(rgb, 2.2); + half3 result = rgb * rgb * (rgb * (half)0.2 + (half)0.8); //fast pow + return result; +} + +half2 GetUV(int idx, int m, int woffset = 0, int hoffset = 0) +{ + int w = idx % mw[m + woffset]; + int h = idx / mw[m + woffset]; + return half2((half)w/mw[m + woffset], (half)h/mh[m + hoffset]); +} diff --git a/Runtime/Shader/Utility.cginc.meta b/Runtime/Shader/Utility.cginc.meta new file mode 100644 index 0000000..02950d4 --- /dev/null +++ b/Runtime/Shader/Utility.cginc.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4f490184e4fcfe648ac53cb7f0f74bb3 +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/XXTEA.cginc b/Runtime/Shader/XXTEA.cginc similarity index 90% rename from Runtime/XXTEA.cginc rename to Runtime/Shader/XXTEA.cginc index 2395f12..cb6eb90 100644 --- a/Runtime/XXTEA.cginc +++ b/Runtime/Shader/XXTEA.cginc @@ -3,7 +3,7 @@ static const uint Delta = 0x9e3779b9; static const uint ROUNDS = 6; static const uint SUM = Delta * ROUNDS; -void XXTEADecrypt(inout uint data[3], const uint key[4]) +void Decrypt(inout uint data[3], const uint key[4]) { static const uint n = 3; uint v0, v1, sum; @@ -27,7 +27,8 @@ void XXTEADecrypt(inout uint data[3], const uint key[4]) sum -= Delta; } } -void XXTEADecrypt(inout uint data[2], const uint key[4]) + +void Decrypt(inout uint data[2], const uint key[4]) { static const uint n = 2; uint v0, v1, sum; diff --git a/Runtime/XXTEA.cginc.meta b/Runtime/Shader/XXTEA.cginc.meta similarity index 100% rename from Runtime/XXTEA.cginc.meta rename to Runtime/Shader/XXTEA.cginc.meta diff --git a/Runtime/liltoonProtector/Shaders/Chacha.cginc b/Runtime/liltoonProtector/Shaders/Chacha.cginc deleted file mode 100644 index 3aba812..0000000 --- a/Runtime/liltoonProtector/Shaders/Chacha.cginc +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once -static const uint CHACHA20_ROUNDS = 8; -uint _Nonce0; -uint _Nonce1; -uint _Nonce2; - -uint Rotl32(uint x, int n) -{ - return x << n | (x >> (32 - n)); -} - -void Chacha20QuarterRound(inout uint state[16], int a, int b, int c, int d) -{ - state[a] += state[b]; state[d] = Rotl32(state[d] ^ state[a], 16); - state[c] += state[d]; state[b] = Rotl32(state[b] ^ state[c], 12); - state[a] += state[b]; state[d] = Rotl32(state[d] ^ state[a], 8); - state[c] += state[d]; state[b] = Rotl32(state[b] ^ state[c], 7); -} - -void Chacha20XOR(inout uint data[2], const uint key[4]) -{ - uint4 state[4]; - state[0] = uint4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574); - state[1] = uint4(key[0], key[1], key[2], key[3]); - state[2] = state[1]; - state[3] = uint4(1, _Nonce0, _Nonce1, _Nonce2); - - uint block[16]; - //block - [unroll] - int i = 0; - for(i = 0; i < 4; ++i) - { - block[i * 4 + 0] = state[i].x; - block[i * 4 + 1] = state[i].y; - block[i * 4 + 2] = state[i].z; - block[i * 4 + 3] = state[i].w; - } - [unroll] - for (i = 0; i < CHACHA20_ROUNDS; i += 2) - { - Chacha20QuarterRound(block, 0, 4, 8, 12); - Chacha20QuarterRound(block, 1, 5, 9, 13); - Chacha20QuarterRound(block, 2, 6, 10, 14); - Chacha20QuarterRound(block, 3, 7, 11, 15); - Chacha20QuarterRound(block, 0, 5, 10, 15); - Chacha20QuarterRound(block, 1, 6, 11, 12); - Chacha20QuarterRound(block, 2, 7, 8, 13); - Chacha20QuarterRound(block, 3, 4, 9, 14); - } - [unroll] - for(i = 0; i < 4; ++i) - { - block[i * 4 + 0] += state[i].x; - block[i * 4 + 1] += state[i].y; - block[i * 4 + 2] += state[i].z; - block[i * 4 + 3] += state[i].w; - } - // - data[0] ^= block[0]; - data[1] ^= block[1]; -} - -void Chacha20XOR(inout uint data[3], const uint key[4]) -{ - uint4 state[4]; - state[0] = uint4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574); - state[1] = uint4(key[0], key[1], key[2], key[3]); - state[2] = state[1]; - state[3] = uint4(1, _Nonce0, _Nonce1, _Nonce2); - - uint block[16]; - //block - int i = 0; - [unroll] - for(i = 0; i < 4; ++i) - { - block[i * 4 + 0] = state[i].x; - block[i * 4 + 1] = state[i].y; - block[i * 4 + 2] = state[i].z; - block[i * 4 + 3] = state[i].w; - } - [unroll] - for (i = 0; i < CHACHA20_ROUNDS; i += 2) - { - Chacha20QuarterRound(block, 0, 4, 8, 12); - Chacha20QuarterRound(block, 1, 5, 9, 13); - Chacha20QuarterRound(block, 2, 6, 10, 14); - Chacha20QuarterRound(block, 3, 7, 11, 15); - Chacha20QuarterRound(block, 0, 5, 10, 15); - Chacha20QuarterRound(block, 1, 6, 11, 12); - Chacha20QuarterRound(block, 2, 7, 8, 13); - Chacha20QuarterRound(block, 3, 4, 9, 14); - } - [unroll] - for(i = 0; i < 4; ++i) - { - block[i * 4 + 0] += state[i].x; - block[i * 4 + 1] += state[i].y; - block[i * 4 + 2] += state[i].z; - block[i * 4 + 3] += state[i].w; - } - // - data[0] ^= block[0]; - data[1] ^= block[1]; - data[2] ^= block[2]; -} \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/Decrypt.cginc b/Runtime/liltoonProtector/Shaders/Decrypt.cginc deleted file mode 100644 index 7de41a0..0000000 --- a/Runtime/liltoonProtector/Shaders/Decrypt.cginc +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once -#include "XXTEA.cginc" - -static const uint mw[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; -static const uint mh[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; - -float _Key0, _Key1, _Key2, _Key3, _Key4, _Key5, _Key6, _Key7, _Key8, _Key9, _Key10, _Key11, _Key12, _Key13, _Key14, _Key15; - -uint _Woffset; -uint _Hoffset; - -half3 InverseGammaCorrection(half3 rgb) -{ - half3 result = pow(rgb, 0.454545); - return result; -} -half3 GammaCorrection(half3 rgb) -{ - //half3 result = pow(rgb, 2.2); - half3 result = rgb * rgb * (rgb * (half)0.2 + (half)0.8); //fast pow - return result; -} - -half2 GetUV(int idx, int m, int woffset = 0, int hoffset = 0) -{ - int w = idx % mw[m + woffset]; - int h = idx / mw[m + woffset]; - return half2((half)w/mw[m + woffset], (half)h/mh[m + hoffset]); -} - -half4 DecryptTexture(half2 uv, int m) -{ - half x = frac(uv.x); - half y = frac(uv.y); - - int idx = (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 2) << 2) - }; - - half3 pixels[4]; - - const int pos[4] = { 0, -1, -2, -3 }; - int offset = pos[idx % 4]; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[2] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 2 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[3] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 3 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[3] = { 0, 0, 0 }; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[1].r * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].g * 255.0f) | ((uint)round(pixels[1].b * 255.0f) << 8) | ((uint)round(pixels[2].r * 255.0f) << 16) | ((uint)round(pixels[2].g * 255.0f) << 24)); - data[2] = ((uint)round(pixels[2].b * 255.0f) | ((uint)round(pixels[3].r * 255.0f) << 8) | ((uint)round(pixels[3].g * 255.0f) << 16) | ((uint)round(pixels[3].b * 255.0f) << 24)); - - XXTEADecrypt(data, key); - - half r[4] = { (data[0] & 0x000000FF)/255.0f, ((data[0] & 0xFF000000) >> 24)/255.0f, ((data[1] & 0x00FF0000) >> 16)/255.0f, ((data[2] & 0x0000FF00) >> 8)/255.0f }; - half g[4] = { ((data[0] & 0x0000FF00) >> 8)/255.0f, ((data[1] & 0x000000FF) >> 0)/255.0f, ((data[1] & 0xFF000000) >> 24)/255.0f, ((data[2] & 0x00FF0000) >> 16)/255.0f }; - half b[4] = { ((data[0] & 0x00FF0000) >> 16)/255.0f, ((data[1] & 0x0000FF00) >> 8)/255.0f, ((data[2] & 0x000000FF) >> 0)/255.0f, ((data[2] & 0xFF000000) >> 24)/255.0f }; - half3 decrypt = half3(r[idx % 4], g[idx % 4], b[idx % 4]); - - return half4(GammaCorrection(decrypt), 1.0); -} -half4 DecryptTextureRGBA(half2 uv, int m) -{ - half x = frac(uv.x); - half y = frac(uv.y); - - int idx = (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 1) << 1) - }; - - half4 pixels[2]; - - int offset = (idx & 1) == 0 ? 0 : -1; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[2] = { 0, 0 }; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); - - XXTEADecrypt(data, key); - - half r = ((data[idx & 1] & 0x000000FF) >> 0)/255.0f; - half g = ((data[idx & 1] & 0x0000FF00) >> 8)/255.0f; - half b = ((data[idx & 1] & 0x00FF0000) >> 16)/255.0f; - half a = ((data[idx & 1] & 0xFF000000) >> 24)/255.0f; - half4 decrypt = half4(r, g, b, a); - - return half4(GammaCorrection(decrypt.rgb), decrypt.a); -} -half4 DecryptTextureDXT(half2 uv, int m) -{ - half2 frac_uv = frac(uv.xy); - half x = frac_uv.x; - half y = frac_uv.y; - - half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); - - uint w = mw[m + (uint)_Woffset]; - uint h = mh[m + _Hoffset]; - - int idx = (w * floor(y * h)) + floor(x * w); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 1) << 1) - }; - - int offset = (idx & 1) == 0 ? 0 : -1; - - half4 pixels[2]; - pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[2]; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); - - XXTEADecrypt(data, key); - - uint r = (data[idx & 1] & 0x000000FF) >> 0; - uint g = (data[idx & 1] & 0x0000FF00) >> 8; - uint b = (data[idx & 1] & 0x00FF0000) >> 16; - uint a = (data[idx & 1] & 0xFF000000) >> 24; - - uint color1 = (r | g << 8); - uint color2 = (b | a << 8); - - uint color1_r = (color1 & 0xF800) >> 11; - color1_r = color1_r << 3 | color1_r >> 2; - uint color1_g = (color1 & 0x7E0) >> 5; - color1_g = color1_g << 2 | color1_g >> 4; - uint color1_b= color1 & 0x1F; - color1_b = color1_b << 3 | color1_b >> 2; - - uint color2_r = (color2 & 0xF800) >> 11; - color2_r = color2_r << 3 | color2_r >> 2; - uint color2_g = (color2 & 0x7E0) >> 5; - color2_g = color2_g << 2 | color2_g >> 4; - uint color2_b= color2 & 0x1F; - color2_b = color2_b << 3 | color2_b >> 2; - - half3 col1 = half3(color1_r / (half)255, color1_g / (half)255, color1_b / (half)255); - half3 col2 = half3(color2_r / (half)255, color2_g / (half)255, color2_b / (half)255); - - half3 result; - result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); - - return half4(GammaCorrection(result), col.a); -} \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc b/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc deleted file mode 100644 index 3264d5c..0000000 --- a/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc +++ /dev/null @@ -1,168 +0,0 @@ -#pragma once -#include "Chacha.cginc" - -static const uint mw[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; -static const uint mh[13] = { 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; - -float _Key0, _Key1, _Key2, _Key3, _Key4, _Key5, _Key6, _Key7, _Key8, _Key9, _Key10, _Key11, _Key12, _Key13, _Key14, _Key15; - -uint _Woffset; -uint _Hoffset; - -half3 InverseGammaCorrection(half3 rgb) -{ - half3 result = pow(rgb, 0.454545); - return result; -} -half3 GammaCorrection(half3 rgb) -{ - //half3 result = pow(rgb, 2.2); - half3 result = rgb * rgb * (rgb * (half)0.2 + (half)0.8); //fast pow - return result; -} - -half2 GetUV(int idx, int m, int woffset = 0, int hoffset = 0) -{ - int w = idx % mw[m + woffset]; - int h = idx / mw[m + woffset]; - return half2((half)w/mw[m + woffset], (half)h/mh[m + hoffset]); -} - -half4 DecryptTexture(half2 uv, int m) -{ - half x = frac(uv.x); - half y = frac(uv.y); - - int idx = (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 2) << 2) - }; - - half3 pixels[4]; - - const int pos[4] = { 0, -1, -2, -3 }; - int offset = pos[idx % 4]; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[2] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 2 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[3] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 3 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[3] = { 0, 0, 0 }; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[1].r * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].g * 255.0f) | ((uint)round(pixels[1].b * 255.0f) << 8) | ((uint)round(pixels[2].r * 255.0f) << 16) | ((uint)round(pixels[2].g * 255.0f) << 24)); - data[2] = ((uint)round(pixels[2].b * 255.0f) | ((uint)round(pixels[3].r * 255.0f) << 8) | ((uint)round(pixels[3].g * 255.0f) << 16) | ((uint)round(pixels[3].b * 255.0f) << 24)); - - Chacha20XOR(data, key); - - half r[4] = { (data[0] & 0x000000FF)/255.0f, ((data[0] & 0xFF000000) >> 24)/255.0f, ((data[1] & 0x00FF0000) >> 16)/255.0f, ((data[2] & 0x0000FF00) >> 8)/255.0f }; - half g[4] = { ((data[0] & 0x0000FF00) >> 8)/255.0f, ((data[1] & 0x000000FF) >> 0)/255.0f, ((data[1] & 0xFF000000) >> 24)/255.0f, ((data[2] & 0x00FF0000) >> 16)/255.0f }; - half b[4] = { ((data[0] & 0x00FF0000) >> 16)/255.0f, ((data[1] & 0x0000FF00) >> 8)/255.0f, ((data[2] & 0x000000FF) >> 0)/255.0f, ((data[2] & 0xFF000000) >> 24)/255.0f }; - half3 decrypt = half3(r[idx % 4], g[idx % 4], b[idx % 4]); - - return half4(GammaCorrection(decrypt), 1.0); -} -half4 DecryptTextureRGBA(half2 uv, int m) -{ - half x = frac(uv.x); - half y = frac(uv.y); - - int idx = (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 1) << 1) - }; - - half4 pixels[2]; - - int offset = (idx & 1) == 0 ? 0 : -1; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[2] = { 0, 0 }; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); - - Chacha20XOR(data, key); - - half r = ((data[idx & 1] & 0x000000FF) >> 0)/255.0f; - half g = ((data[idx & 1] & 0x0000FF00) >> 8)/255.0f; - half b = ((data[idx & 1] & 0x00FF0000) >> 16)/255.0f; - half a = ((data[idx & 1] & 0xFF000000) >> 24)/255.0f; - half4 decrypt = half4(r, g, b, a); - - return half4(GammaCorrection(decrypt.rgb), decrypt.a); -} -half4 DecryptTextureDXT(half2 uv, int m) -{ - half2 frac_uv = frac(uv.xy); - half x = frac_uv.x; - half y = frac_uv.y; - - int miplv = 0; - - half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); - - uint w = mw[m + (uint)_Woffset]; - uint h = mh[m + _Hoffset]; - - int idx = (w * floor(y * h)) + floor(x * w); - - const uint key[4] = - { - ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), - ((uint)round(_Key4) | ((uint)round(_Key5) << 8) | ((uint)round(_Key6) << 16) | ((uint)round(_Key7) << 24)), - ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), - ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> 1) << 1) - }; - - int offset = (idx & 1) == 0 ? 0 : -1; - - half4 pixels[2]; - pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - uint data[2]; - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); - - Chacha20XOR(data, key); - - uint r = (data[idx & 1] & 0x000000FF) >> 0; - uint g = (data[idx & 1] & 0x0000FF00) >> 8; - uint b = (data[idx & 1] & 0x00FF0000) >> 16; - uint a = (data[idx & 1] & 0xFF000000) >> 24; - - uint color1 = (r | g << 8); - uint color2 = (b | a << 8); - - uint color1_r = (color1 & 0xF800) >> 11; - color1_r = color1_r << 3 | color1_r >> 2; - uint color1_g = (color1 & 0x7E0) >> 5; - color1_g = color1_g << 2 | color1_g >> 4; - uint color1_b= color1 & 0x1F; - color1_b = color1_b << 3 | color1_b >> 2; - - uint color2_r = (color2 & 0xF800) >> 11; - color2_r = color2_r << 3 | color2_r >> 2; - uint color2_g = (color2 & 0x7E0) >> 5; - color2_g = color2_g << 2 | color2_g >> 4; - uint color2_b= color2 & 0x1F; - color2_b = color2_b << 3 | color2_b >> 2; - - half3 col1 = half3(color1_r / (half)255, color1_g / (half)255, color1_b / (half)255); - half3 col2 = half3(color2_r / (half)255, color2_g / (half)255, color2_b / (half)255); - - half3 result; - result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); - - return half4(GammaCorrection(result), col.a); -} \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/custom.hlsl b/Runtime/liltoonProtector/Shaders/custom.hlsl index 921fd50..72c02a5 100644 --- a/Runtime/liltoonProtector/Shaders/custom.hlsl +++ b/Runtime/liltoonProtector/Shaders/custom.hlsl @@ -11,26 +11,13 @@ TEXTURE2D(_EncryptTex0); \ SAMPLER(sampler_EncryptTex0); \ TEXTURE2D(_EncryptTex1); - -#ifndef _FORMAT0 - #ifndef _FORMAT1 - #define DECRYPT DecryptTextureDXT - #else - #define DECRYPT DecryptTextureRGBA - #endif -#else - #ifndef _FORMAT1 - #define DECRYPT DecryptTexture - #endif -#endif - #ifdef _POINT #define CODE\ half4 mip_texture = tex2D(_MipTex, fd.uvMain);\ int mip = round(mip_texture.r * 255 / 10);\ int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 };\ \ - half4 c00 = DECRYPT(fd.uvMain, m[mip]);\ + half4 c00 = DecryptTexture(fd.uvMain, m[mip]);\ \ fd.col = c00; #else @@ -42,10 +29,10 @@ int mip = round(mip_texture.r * 255 / 10);\ static const int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 };\ \ - half4 c00 = DECRYPT(uv_bilinear + half2(uv_unit.x * 0, uv_unit.y * 0), m[mip]);\ - half4 c10 = DECRYPT(uv_bilinear + half2(uv_unit.x * 1, uv_unit.y * 0), m[mip]);\ - half4 c01 = DECRYPT(uv_bilinear + half2(uv_unit.x * 0, uv_unit.y * 1), m[mip]);\ - half4 c11 = DECRYPT(uv_bilinear + half2(uv_unit.x * 1, uv_unit.y * 1), m[mip]);\ + half4 c00 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 0, uv_unit.y * 0), m[mip]);\ + half4 c10 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 1, uv_unit.y * 0), m[mip]);\ + half4 c01 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 0, uv_unit.y * 1), m[mip]);\ + half4 c11 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 1, uv_unit.y * 1), m[mip]);\ \ half2 f = frac(uv_bilinear * _EncryptTex0_TexelSize.zw);\ \ @@ -75,7 +62,7 @@ #define OVERRIDE_MATCAP \ lilGetMatCap(fd, _MipTex); -#if defined(_LIMLIGHT_ENCRYPTED) +#if defined(_SHELL_PROTECTOR_RIMLIGHT) #if defined(LIL_LITE) #define OVERRIDE_RIMLIGHT \ lilGetRim(fd); diff --git a/Runtime/liltoonProtector/Shaders/custom_insert.hlsl b/Runtime/liltoonProtector/Shaders/custom_insert.hlsl index dd93694..1fd566c 100644 --- a/Runtime/liltoonProtector/Shaders/custom_insert.hlsl +++ b/Runtime/liltoonProtector/Shaders/custom_insert.hlsl @@ -1,15 +1,2 @@ -#pragma shader_feature_local _XXTEA -#pragma shader_feature_local _FORMAT0 -#pragma shader_feature_local _FORMAT1 -#pragma shader_feature_local _POINT -#pragma shader_feature_local _LIMLIGHT_ENCRYPTED - -//format = format1 | format0 << 1 -//format 00:DXT, 01:RGB, 10:RGBA - -#ifdef _XXTEA - #include "Decrypt.cginc" -#else - #include "DecryptChacha.cginc" -#endif +#include "../../Shader/ShellProtector.cginc" #include "UnityCG.cginc" \ No newline at end of file From 15797af9b9e71e410e59d29c28df9936a54108cb Mon Sep 17 00:00:00 2001 From: SnowyWalk Date: Sun, 23 Mar 2025 23:53:48 +0900 Subject: [PATCH 08/59] =?UTF-8?q?=EB=B3=B5=ED=98=B8=ED=99=94=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=EC=97=90=20=EB=8C=80=ED=95=9C=20=ED=8C=90=EB=8B=A8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Editor/liltoonCustom/CustomInspector.cs | 7 ++- Runtime/Decrypt.cginc | 7 +++ Runtime/DecryptChacha.cginc | 7 +++ .../liltoonProtector/Shaders/Decrypt.cginc | 7 +++ .../Shaders/DecryptChacha.cginc | 7 +++ Runtime/liltoonProtector/Shaders/custom.hlsl | 15 ++++- .../lilCustomShaderProperties.lilblock | 3 +- SnowyWalkCustom.meta | 8 +++ SnowyWalkCustom/Editor.meta | 8 +++ SnowyWalkCustom/Editor/HashFloat4Editor.cs | 62 +++++++++++++++++++ .../Editor/HashFloat4Editor.cs.meta | 11 ++++ 11 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 SnowyWalkCustom.meta create mode 100644 SnowyWalkCustom/Editor.meta create mode 100644 SnowyWalkCustom/Editor/HashFloat4Editor.cs create mode 100644 SnowyWalkCustom/Editor/HashFloat4Editor.cs.meta diff --git a/Editor/liltoonCustom/CustomInspector.cs b/Editor/liltoonCustom/CustomInspector.cs index 9829dea..8459a5c 100644 --- a/Editor/liltoonCustom/CustomInspector.cs +++ b/Editor/liltoonCustom/CustomInspector.cs @@ -11,8 +11,9 @@ public class ShellProtectorInspector : lilToonInspector MaterialProperty mip_tex; MaterialProperty encrypted_tex0; MaterialProperty encrypted_tex1; - MaterialProperty[] key = new MaterialProperty[15]; + MaterialProperty[] key = new MaterialProperty[16]; MaterialProperty fallback; + MaterialProperty decrypt_complete_key; private static bool isShowCustomProperties; private static bool show_pwd = false; @@ -34,6 +35,7 @@ protected override void LoadCustomProperties(MaterialProperty[] props, Material encrypted_tex0 = FindProperty("_EncryptTex0", props); encrypted_tex1 = FindProperty("_EncryptTex1", props); fallback = FindProperty("_fallback", props); + decrypt_complete_key = FindProperty("_Decrypt_complete_key", props); for (int i = 0; i < key.Length; ++i) key[i] = FindProperty("_Key" + i, props); @@ -68,6 +70,9 @@ protected override void DrawCustomProperties(Material material) EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical(); + if (decrypt_complete_key != null) + m_MaterialEditor.ShaderProperty(decrypt_complete_key, "Decrypt complete key"); + show_pwd = Foldout("Keys", "Keys", show_pwd); if(show_pwd) { diff --git a/Runtime/Decrypt.cginc b/Runtime/Decrypt.cginc index 7de41a0..a676f12 100644 --- a/Runtime/Decrypt.cginc +++ b/Runtime/Decrypt.cginc @@ -163,4 +163,11 @@ half4 DecryptTextureDXT(half2 uv, int m) result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); return half4(GammaCorrection(result), col.a); +} + +float4 HashFloat4(float4 v0, float4 v1, float4 v2, float4 v3) +{ + float4 seed = v0 * 0.1031 + v1 * 0.11369 + v2 * 0.13787 + v3 * 0.09997; + seed = frac(sin(seed) * 43758.5453); + return seed; } \ No newline at end of file diff --git a/Runtime/DecryptChacha.cginc b/Runtime/DecryptChacha.cginc index 3264d5c..f596361 100644 --- a/Runtime/DecryptChacha.cginc +++ b/Runtime/DecryptChacha.cginc @@ -165,4 +165,11 @@ half4 DecryptTextureDXT(half2 uv, int m) result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); return half4(GammaCorrection(result), col.a); +} + +float4 HashFloat4(float4 v0, float4 v1, float4 v2, float4 v3) +{ + float4 seed = v0 * 0.1031 + v1 * 0.11369 + v2 * 0.13787 + v3 * 0.09997; + seed = frac(sin(seed) * 43758.5453); + return seed; } \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/Decrypt.cginc b/Runtime/liltoonProtector/Shaders/Decrypt.cginc index 7de41a0..a676f12 100644 --- a/Runtime/liltoonProtector/Shaders/Decrypt.cginc +++ b/Runtime/liltoonProtector/Shaders/Decrypt.cginc @@ -163,4 +163,11 @@ half4 DecryptTextureDXT(half2 uv, int m) result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); return half4(GammaCorrection(result), col.a); +} + +float4 HashFloat4(float4 v0, float4 v1, float4 v2, float4 v3) +{ + float4 seed = v0 * 0.1031 + v1 * 0.11369 + v2 * 0.13787 + v3 * 0.09997; + seed = frac(sin(seed) * 43758.5453); + return seed; } \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc b/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc index 3264d5c..f596361 100644 --- a/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc +++ b/Runtime/liltoonProtector/Shaders/DecryptChacha.cginc @@ -165,4 +165,11 @@ half4 DecryptTextureDXT(half2 uv, int m) result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); return half4(GammaCorrection(result), col.a); +} + +float4 HashFloat4(float4 v0, float4 v1, float4 v2, float4 v3) +{ + float4 seed = v0 * 0.1031 + v1 * 0.11369 + v2 * 0.13787 + v3 * 0.09997; + seed = frac(sin(seed) * 43758.5453); + return seed; } \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/custom.hlsl b/Runtime/liltoonProtector/Shaders/custom.hlsl index 921fd50..73827fa 100644 --- a/Runtime/liltoonProtector/Shaders/custom.hlsl +++ b/Runtime/liltoonProtector/Shaders/custom.hlsl @@ -2,8 +2,9 @@ // Macro #define LIL_CUSTOM_PROPERTIES\ - half4 _EncryptTex0_TexelSize;\ - fixed _fallback; + half4 _EncryptTex0_TexelSize; \ + fixed _fallback; \ + float4 _Decrypt_complete_key; // Custom textures #define LIL_CUSTOM_TEXTURES\ @@ -59,10 +60,18 @@ fd.col *= _Color; #endif +#define DECRYPT_CHECK_FUNC \ + (all(abs(_Decrypt_complete_key - HashFloat4(\ + float4(_Key0, _Key1, _Key2, _Key3), \ + float4(_Key4, _Key5, _Key6, _Key7), \ + float4(_Key8, _Key9, _Key10, _Key11), \ + float4(_Key12, _Key13, _Key14, _Key15) \ + )) < 1e-1)) + #define OVERRIDE_MAIN\ LIL_GET_MAIN_TEX\ UNITY_BRANCH\ - if(_fallback == 1)\ + if(DECRYPT_CHECK_FUNC == false)\ {\ LIL_APPLY_MAIN_TONECORRECTION\ fd.col *= _Color;\ diff --git a/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock b/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock index 3fdb2a1..ca25171 100644 --- a/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock +++ b/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock @@ -22,4 +22,5 @@ _Hoffset ("Hoffset", integer) = 0 _Nonce0 ("Nonce", integer) = 0 _Nonce1 ("Nonce", integer) = 0 - _Nonce2 ("Nonce", integer) = 0 \ No newline at end of file + _Nonce2 ("Nonce", integer) = 0 + _Decrypt_complete_key ("Decrypt Complete Key", Vector) = (0, 0, 0, 0) \ No newline at end of file diff --git a/SnowyWalkCustom.meta b/SnowyWalkCustom.meta new file mode 100644 index 0000000..bf96995 --- /dev/null +++ b/SnowyWalkCustom.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f93e766cd672ee6478d527420e02ccdb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SnowyWalkCustom/Editor.meta b/SnowyWalkCustom/Editor.meta new file mode 100644 index 0000000..869bbe4 --- /dev/null +++ b/SnowyWalkCustom/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b2c169cd0e6511446b745a2ac88f7c67 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/SnowyWalkCustom/Editor/HashFloat4Editor.cs b/SnowyWalkCustom/Editor/HashFloat4Editor.cs new file mode 100644 index 0000000..2982cbe --- /dev/null +++ b/SnowyWalkCustom/Editor/HashFloat4Editor.cs @@ -0,0 +1,62 @@ +using UnityEngine; +using UnityEditor; + +public class HashFloat4Editor : EditorWindow +{ + Vector4 v0 = new Vector4(112f, 97f, 115f, 115f); + Vector4 v1 = Vector4.zero; + Vector4 v2 = Vector4.zero; + Vector4 v3 = Vector4.zero; + + Vector4 result; + + [MenuItem("SnowyWalk/HashFloat4 Editor")] + public static void ShowWindow() + { + GetWindow("HashFloat4 Editor"); + } + + void OnGUI() + { + // 벡터 입력 + v0 = EditorGUILayout.Vector4Field("v0", v0); + v1 = EditorGUILayout.Vector4Field("v1", v1); + v2 = EditorGUILayout.Vector4Field("v2", v2); + v3 = EditorGUILayout.Vector4Field("v3", v3); + + if (GUILayout.Button("Calculate")) + { + result = HashFloat4(v0, v1, v2, v3); + } + + EditorGUILayout.LabelField("Result", result.ToString("F5")); + } + + Vector4 HashFloat4(Vector4 a, Vector4 b, Vector4 c, Vector4 d) + { + // (1) seed 계산 + // float4 seed = v0*0.1031 + v1*0.11369 + v2*0.13787 + v3*0.09997; + Vector4 seed = a * 0.1031f + b * 0.11369f + c * 0.13787f + d * 0.09997f; + + // (2) sin(seed) * 43758.5453 => frac(...) + // sin()과 floor()는 C#에서 Mathf.Sin, Mathf.Floor 사용 + Vector4 sinVal = new Vector4( + Mathf.Sin(seed.x), + Mathf.Sin(seed.y), + Mathf.Sin(seed.z), + Mathf.Sin(seed.w) + ); + + sinVal *= 43758.5453f; + + // frac(x) = x - floor(x) + Vector4 fracVal = new Vector4( + sinVal.x - Mathf.Floor(sinVal.x), + sinVal.y - Mathf.Floor(sinVal.y), + sinVal.z - Mathf.Floor(sinVal.z), + sinVal.w - Mathf.Floor(sinVal.w) + ); + + return fracVal; + } +} diff --git a/SnowyWalkCustom/Editor/HashFloat4Editor.cs.meta b/SnowyWalkCustom/Editor/HashFloat4Editor.cs.meta new file mode 100644 index 0000000..c42ea0e --- /dev/null +++ b/SnowyWalkCustom/Editor/HashFloat4Editor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0618600a6526c23498f578069f4e01fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From cd1dc83825852c5ec176667e423430ef44882631 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Mon, 24 Mar 2025 11:27:07 +0900 Subject: [PATCH 09/59] =?UTF-8?q?fix:=20fallback=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Editor/ShellProtectorEditor.cs | 16 --- Editor/TesterEditor.cs | 15 --- Editor/liltoonCustom/CustomInspector.cs | 4 - Runtime/Scripts/AnimatorManager.cs | 113 ------------------ Runtime/Scripts/Injector/PoiyomiInjector.cs | 1 - .../Scripts/NDMF/ShellProtectorNDMFPlugin.cs | 1 - Runtime/Scripts/ShellProtector.cs | 52 -------- Runtime/Scripts/ShellProtectorTester.cs | 4 - Runtime/liltoonProtector/Shaders/custom.hlsl | 1 - .../lilCustomShaderProperties.lilblock | 1 - 10 files changed, 208 deletions(-) diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index f71c889..78c1704 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -36,7 +36,6 @@ public class ShellProtectorEditor : Editor SerializedProperty parameter_multiplexing; SerializedProperty bUseSmallMipTexture; SerializedProperty bPreserveMMD; - SerializedProperty fallbackTime; SerializedProperty turnOnAllSafetyFallback; bool debug = false; bool option = true; @@ -117,7 +116,6 @@ void OnEnable() parameter_multiplexing = serializedObject.FindProperty("parameter_multiplexing"); bUseSmallMipTexture = serializedObject.FindProperty("bUseSmallMipTexture"); bPreserveMMD = serializedObject.FindProperty("bPreserveMMD"); - fallbackTime = serializedObject.FindProperty("fallbackTime"); turnOnAllSafetyFallback = serializedObject.FindProperty("turnOnAllSafetyFallback"); #endregion @@ -372,20 +370,6 @@ public override void OnInspectorGUI() { GUILayout.Label(Lang("Opponents with Safety option turned on will see degraded textures instead of noise.")); - GUILayout.Label(Lang("Fallback wait time"), EditorStyles.boldLabel); - GUILayout.BeginHorizontal(); - fallbackTime.floatValue = GUILayout.HorizontalSlider(fallbackTime.floatValue, 0.0f, 10.0f, GUILayout.Width(100)); - fallbackTime.floatValue = EditorGUILayout.FloatField("", fallbackTime.floatValue, GUILayout.Width(50)); - fallbackTime.floatValue = Mathf.Clamp(fallbackTime.floatValue, 0.0f, 10.0f); -#if UNITY_2022 - fallbackTime.floatValue = MathF.Round(fallbackTime.floatValue, 1); -#endif - GUILayout.FlexibleSpace(); - GUILayout.Label(Lang("After this time, the fallback is turned off. (Only who is Safety OFF)"), EditorStyles.wordWrappedLabel); - GUILayout.EndHorizontal(); - - GUILayout.Space(10); - GUILayout.BeginHorizontal(); GUILayout.Label(Lang("Change all Safety Fallback settings of shader to Unlit."), EditorStyles.boldLabel); turnOnAllSafetyFallback.boolValue = EditorGUILayout.Toggle(turnOnAllSafetyFallback.boolValue); diff --git a/Editor/TesterEditor.cs b/Editor/TesterEditor.cs index 972931e..692cb3e 100644 --- a/Editor/TesterEditor.cs +++ b/Editor/TesterEditor.cs @@ -64,21 +64,6 @@ public override void OnInspectorGUI() GUILayout.Space(10); - GUILayout.BeginHorizontal(); - if (GUILayout.Button(Lang("Off fallback"))) - { - root.ResetEncryption(); - root.protector.SetMaterialFallbackValue(root.transform.root.gameObject, false); - } - if (GUILayout.Button(Lang("On fallback"))) - { - root.ResetEncryption(); - root.protector.SetMaterialFallbackValue(root.transform.root.gameObject, true); - } - GUILayout.EndHorizontal(); - - GUILayout.Space(10); - if (root.userKeyLength == 0) { GUILayout.Label(Lang("It's okay for the 0-digit password to be the same as the original.")); diff --git a/Editor/liltoonCustom/CustomInspector.cs b/Editor/liltoonCustom/CustomInspector.cs index 0eff76e..a2fa340 100644 --- a/Editor/liltoonCustom/CustomInspector.cs +++ b/Editor/liltoonCustom/CustomInspector.cs @@ -14,7 +14,6 @@ public class ShellProtectorInspector : lilToonInspector MaterialProperty encrypted_tex0; MaterialProperty encrypted_tex1; MaterialProperty[] key = new MaterialProperty[16]; - MaterialProperty fallback; MaterialProperty password_hash; private static bool isShowCustomProperties; @@ -36,7 +35,6 @@ protected override void LoadCustomProperties(MaterialProperty[] props, Material mip_tex = FindProperty("_MipTex", props); encrypted_tex0 = FindProperty("_EncryptTex0", props); encrypted_tex1 = FindProperty("_EncryptTex1", props); - fallback = FindProperty("_fallback", props); password_hash = FindProperty("_PasswordHash", props); for (int i = 0; i < key.Length; ++i) @@ -56,8 +54,6 @@ protected override void DrawCustomProperties(Material material) isShowCustomProperties = Foldout("ShellProtector", "ShellProtector", isShowCustomProperties); if(isShowCustomProperties) { - m_MaterialEditor.ShaderProperty(fallback, "fallback"); - EditorGUILayout.BeginVertical(boxOuter); EditorGUILayout.LabelField(GetLoc("ShellProtector"), customToggleFont); EditorGUILayout.BeginVertical(boxInnerHalf); diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index e09608f..af64adc 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -75,50 +75,6 @@ public class AnimatorManager : ScriptableObject path: Body classID: 137 script: {fileID: 0}"; - static string fallbackOffCurve = @" - - serializedVersion: 2 - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 0 - inSlope: 0 - outSlope: 0 - tangentMode: 136 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - attribute: material._fallback - path: Body - classID: 137 - script: {fileID: 0} - flags: 16"; - static string fallbackOnCurve = @" - - serializedVersion: 2 - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 1 - inSlope: 0 - outSlope: 0 - tangentMode: 136 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - attribute: material._fallback - path: Body - classID: 137 - script: {fileID: 0} - flags: 16"; public static AnimatorController DuplicateAnimator(RuntimeAnimatorController anim, string new_dir) { string dir = AssetDatabase.GetAssetPath(anim); @@ -140,8 +96,6 @@ public static void CreateKeyAniamtions(string animation_dir, string new_dir, Gam continue; if (filename.Contains("dummy")) continue; - if (filename.Contains("FallbackOff")) - continue; string path = Path.Combine(new_dir, filename); AssetDatabase.CopyAsset(file, path); @@ -178,40 +132,6 @@ public static void CreateKeyAniamtions(string animation_dir, string new_dir, Gam AssetDatabase.Refresh(); } - public static AnimationClip CreateFallbackAniamtions(string animationDir, string newDir, GameObject[] objs, bool bOff = true) - { - string animName = Path.GetFileName(animationDir); - string newPath = Path.Combine(newDir, animName); - - AssetDatabase.CopyAsset(animationDir, newPath); - - string anim = File.ReadAllText(newPath); - - foreach (var obj in objs) - { - if (obj.name == "Body") - continue; - - string hrPath = obj.transform.GetHierarchyPath(); - hrPath = Regex.Replace(hrPath, ".*?/(.*)", "'$1'"); - - string curve = Regex.Replace(bOff ? fallbackOffCurve : fallbackOnCurve, "path: Body", "path: " + hrPath); - //SkinnedMeshRender classID:137 - //MeshRenderer classID:23 - if (obj.GetComponent() == null) - curve = Regex.Replace(curve, "classID: 137", "classID: 23"); - - anim = Regex.Replace(anim, "m_FloatCurves:", "m_FloatCurves:" + curve); - anim = Regex.Replace(anim, "m_EditorCurves:", "m_EditorCurves:" + curve); - } - File.WriteAllText(newPath, anim); - - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - - return AssetDatabase.LoadAssetAtPath(newPath); - } - private static BlendTree[] CreateKeyTree(string animation_dir, int key_length, float speed) { BlendTree[] tree = new BlendTree[key_length]; @@ -501,39 +421,6 @@ private static void AddDemuxLayer(AnimatorController anim, int keyLength, int sy } } - public static void AddFallbackLayer(AnimatorController anim, AnimationClip fallbackOnAnimation, AnimationClip fallbackOffAnimation, float time = 3) - { - var layers = anim.layers; - foreach (var _layer in layers) - { - if (_layer.name == "ShellProtectorFallback") - return; - } - - AnimatorStateMachine stateMachine = new AnimatorStateMachine - { - name = anim.MakeUniqueLayerName("ShellProtectorFallback"), - hideFlags = HideFlags.HideInHierarchy - }; - AssetDatabase.AddObjectToAsset(stateMachine, anim); - anim.AddLayer(new AnimatorControllerLayer { name = stateMachine.name, defaultWeight = 1.0f, stateMachine = stateMachine }); - - var layer = anim.layers[anim.layers.Length - 1]; - var defaultState = layer.stateMachine.AddState("default"); - defaultState.writeDefaultValues = true; - defaultState.motion = fallbackOnAnimation; - - var fallbackState = layer.stateMachine.AddState("fallbackState"); - - var transition = defaultState.AddTransition(fallbackState); - transition.canTransitionToSelf = false; - transition.exitTime = time; - transition.duration = 0; - transition.hasExitTime = true; - - fallbackState.motion = fallbackOffAnimation; - } - public static bool IsMaterialInClip(AnimationClip clip, Material originalMaterial) { EditorCurveBinding[] bindings = AnimationUtility.GetObjectReferenceCurveBindings(clip); diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index 048b327..242c462 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -135,7 +135,6 @@ private void InsertProperties(ref string data) _MipTex (""MipReference"", 2D) = ""white"" { } _EncryptTex0 (""Encrypted0"", 2D) = ""white"" { } _EncryptTex1 (""Encrypted1"", 2D) = ""white"" { } - [MaterialToggle] _fallback(""Fallback"", float) = 0 _Woffset (""Woffset"", integer) = 0 _Hoffset (""Hoffset"", integer) = 0 _Nonce0 (""Nonce"", integer) = 0 diff --git a/Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs b/Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs index ded8572..a3ad366 100644 --- a/Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs +++ b/Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs @@ -21,7 +21,6 @@ protected override void Configure() shellProtector.Encrypt(isModular: true); shellProtector.ReplaceMaterials(ctx.AvatarRootObject); shellProtector.RemoveDuplicatedTextures(ctx.AvatarRootObject); - shellProtector.SetMaterialFallbackValue(ctx.AvatarRootObject, true); shellProtector.SetAnimations(ctx.AvatarRootObject, false); shellProtector.ObfuscateBlendShape(ctx.AvatarRootObject, false); diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 41cee38..31239cf 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -751,7 +751,6 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) { ReplaceMaterials(avatar); RemoveDuplicatedTextures(avatar); - SetMaterialFallbackValue(avatar, true); descriptor.gameObject.SetActive(false); @@ -1042,10 +1041,7 @@ public void SetAnimations(GameObject avatar, bool clone) GameObject[] meshArray = new GameObject[meshes.Count]; meshes.CopyTo(meshArray); AnimatorManager.CreateKeyAniamtions(Path.Combine(asset_dir, "Animations"), animationDir, meshArray); - var fallbackOnAnim = AnimatorManager.CreateFallbackAniamtions(Path.Combine(asset_dir, "Animations", "FallbackOn.anim"), animationDir, meshArray, false); - var fallbackOffAnim = AnimatorManager.CreateFallbackAniamtions(Path.Combine(asset_dir, "Animations", "FallbackOff.anim"), animationDir, meshArray, true); AnimatorManager.AddKeyLayer(fx, animationDir, key_size, sync_size, animation_speed, parameter_multiplexing); - AnimatorManager.AddFallbackLayer(fx, fallbackOnAnim, fallbackOffAnim, fallbackTime); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); @@ -1215,54 +1211,6 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) } } - public void SetMaterialFallbackValue(GameObject avatar, bool fallback) - { - var renderers = avatar.GetComponentsInChildren(true); - if (renderers != null) - { - foreach (var r in renderers) - { - var mats = r.sharedMaterials; - if (mats == null) - { - Debug.LogWarning(r.gameObject.name + ": can't find sharedMaterials"); - continue; - } - foreach (var mat in mats) - { - if (mat == null) - continue; - if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) - { - mat.SetFloat("_fallback", fallback == true ? 1.0f : 0.0f); - } - } - } - } - var skinned_renderers = avatar.GetComponentsInChildren(true); - if (skinned_renderers != null) - { - foreach (var r in skinned_renderers) - { - var mats = r.sharedMaterials; - if (mats == null) - { - Debug.LogWarning(r.gameObject.name + ": can't find sharedMaterials"); - continue; - } - foreach (var mat in mats) - { - if (mat == null) - continue; - if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) - { - mat.SetFloat("_fallback", fallback == true ? 1.0f : 0.0f); - } - } - } - } - } - public int GetEncyryptedFoldersCount() { if (!Directory.Exists(asset_dir)) diff --git a/Runtime/Scripts/ShellProtectorTester.cs b/Runtime/Scripts/ShellProtectorTester.cs index e986b57..9c32e86 100644 --- a/Runtime/Scripts/ShellProtectorTester.cs +++ b/Runtime/Scripts/ShellProtectorTester.cs @@ -23,8 +23,6 @@ public void CheckEncryption() return; } - protector.SetMaterialFallbackValue(transform.root.gameObject, false); - byte[] pwd_bytes = protector.GetKeyBytes(); var renderers = transform.root.GetComponentsInChildren(true); @@ -76,8 +74,6 @@ public void CheckEncryption() } public void ResetEncryption() { - protector.SetMaterialFallbackValue(transform.root.gameObject, true); - var renderers = transform.root.GetComponentsInChildren(true); foreach (var r in renderers) { diff --git a/Runtime/liltoonProtector/Shaders/custom.hlsl b/Runtime/liltoonProtector/Shaders/custom.hlsl index 2994d2d..e25fea0 100644 --- a/Runtime/liltoonProtector/Shaders/custom.hlsl +++ b/Runtime/liltoonProtector/Shaders/custom.hlsl @@ -3,7 +3,6 @@ #define LIL_CUSTOM_PROPERTIES\ half4 _EncryptTex0_TexelSize; \ - fixed _fallback; \ int _PasswordHash; // Custom textures diff --git a/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock b/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock index 66e3e6b..3f4ec9c 100644 --- a/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock +++ b/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock @@ -17,7 +17,6 @@ _Key13 ("key13", float) = 0 _Key14 ("key14", float) = 0 _Key15 ("key15", float) = 0 - [MaterialToggle] _fallback("Fallback", float) = 0 _Woffset ("Woffset", integer) = 0 _Hoffset ("Hoffset", integer) = 0 _Nonce0 ("Nonce", integer) = 0 From 15043e6c2ba42a884d24f118df08141f0fb8a42c Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Mon, 24 Mar 2025 11:33:47 +0900 Subject: [PATCH 10/59] =?UTF-8?q?fix:=20Poiyomi=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Runtime/Scripts/Injector/Injector.cs | 4 ++-- Runtime/Scripts/Injector/PoiyomiInjector.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index e1677c3..3a8fdd6 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -21,7 +21,7 @@ abstract public class Injector half4 mainTexture; UNITY_BRANCH - if(_fallback == 0) + if(IsDecrypted()) { half4 mip_texture = _MipTex.Sample(sampler_MipTex, mainUV); @@ -41,7 +41,7 @@ abstract public class Injector half4 mainTexture; UNITY_BRANCH - if(_fallback == 0) + if(IsDecrypted()) { half4 mip_texture = _MipTex.Sample(sampler_MipTex, mainUV); diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index 242c462..904dd3d 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -52,7 +52,7 @@ protected override Shader CustomInject(Material mat, string decode_dir, string o Texture2D _EncryptTex1; float4 _EncryptTex0_TexelSize; - fixed _fallback; + int _PasswordHash; "; int version = AssetManager.GetInstance().GetShaderType(shader); if(version == 73) @@ -140,6 +140,7 @@ private void InsertProperties(ref string data) _Nonce0 (""Nonce"", integer) = 0 _Nonce1 (""Nonce"", integer) = 0 _Nonce2 (""Nonce"", integer) = 0 + _PasswordHash (""PasswordHash"", integer) = 0 "; for (int i = 0; i < 16; ++i) From f220300ea6d7120d14c484182998f4493e1646db Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Mon, 24 Mar 2025 11:34:20 +0900 Subject: [PATCH 11/59] =?UTF-8?q?refactor:=20=ED=95=98=EC=9C=84=ED=98=B8?= =?UTF-8?q?=ED=99=98=EC=84=B1=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Runtime/Scripts/AnimatorManager.cs | 6 +++--- Runtime/Scripts/ParameterManager.cs | 8 ++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index af64adc..776a835 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -210,7 +210,7 @@ private static void AddParameters(AnimatorController anim, int keyLength, int sy for (var i = 0; i < keyLength; ++i) anim.AddParameter(ParameterManager.GetSavedKeyName(i), AnimatorControllerParameterType.Float); for(var i = 0; i < syncSize; ++i) - anim.AddParameter(ParameterManager.GetSyncedKeyNAme(i), AnimatorControllerParameterType.Float); + anim.AddParameter(ParameterManager.GetSyncedKeyName(i), AnimatorControllerParameterType.Float); for (var i = 0; i < switchCount; ++i) anim.AddParameter(ParameterManager.GetSyncSwitchName(i), AnimatorControllerParameterType.Bool); } @@ -347,7 +347,7 @@ private static void AddMuxLayer(AnimatorController anim, int keyLength, int sync syncDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Copy, - name = ParameterManager.GetSyncedKeyNAme(i), + name = ParameterManager.GetSyncedKeyName(i), source = ParameterManager.GetSavedKeyName(step * syncSize + i) }); } @@ -408,7 +408,7 @@ private static void AddDemuxLayer(AnimatorController anim, int keyLength, int sy { type = VRC_AvatarParameterDriver.ChangeType.Copy, name = ParameterManager.GetKeyName(i * syncSize + j), - source = ParameterManager.GetSyncedKeyNAme(j) + source = ParameterManager.GetSyncedKeyName(j) }); } diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index 078f2c7..616819c 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -8,11 +8,7 @@ public static class ParameterManager { private const string Prefix = "SHELL_PROTECTOR_"; - public static string GetSyncedKeyNAme(int index) - { - if (index == 0) return "pkey"; // For backward compatibility (before commit e8080de) - return Prefix + "synced_key" + index; - } + public static string GetSyncedKeyName(int index) => Prefix + "synced_key" + index; public static string GetKeyName(int index) => Prefix + "key" + index; public static string GetSavedKeyName(int index) => Prefix + "saved_key" + index; public static string GetSyncSwitchName(int index) => Prefix + "sync_switch" + index; @@ -53,7 +49,7 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetSyncedKeyNAme(i), + name = GetSyncedKeyName(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Float, From d2577fd65b8565d4570b0e02fb7f8d7e65349fb0 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Mon, 24 Mar 2025 11:41:05 +0900 Subject: [PATCH 12/59] =?UTF-8?q?fix:=20multiplexing=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0=20(=ED=95=AD=EC=83=81=20=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Editor/ShellProtectorEditor.cs | 21 +------ Runtime/Scripts/AnimatorManager.cs | 33 +++++----- Runtime/Scripts/ParameterManager.cs | 93 ++++++++++++----------------- Runtime/Scripts/ShellProtector.cs | 5 +- 4 files changed, 57 insertions(+), 95 deletions(-) diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index 78c1704..8d383ab 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -33,7 +33,6 @@ public class ShellProtectorEditor : Editor SerializedProperty sync_size; SerializedProperty animation_speed; SerializedProperty delete_folders; - SerializedProperty parameter_multiplexing; SerializedProperty bUseSmallMipTexture; SerializedProperty bPreserveMMD; SerializedProperty turnOnAllSafetyFallback; @@ -113,7 +112,6 @@ void OnEnable() sync_size = serializedObject.FindProperty("sync_size"); animation_speed = serializedObject.FindProperty("animation_speed"); delete_folders = serializedObject.FindProperty("delete_folders"); - parameter_multiplexing = serializedObject.FindProperty("parameter_multiplexing"); bUseSmallMipTexture = serializedObject.FindProperty("bUseSmallMipTexture"); bPreserveMMD = serializedObject.FindProperty("bPreserveMMD"); turnOnAllSafetyFallback = serializedObject.FindProperty("turnOnAllSafetyFallback"); @@ -228,13 +226,9 @@ public override void OnInspectorGUI() free_parameter = 256 - parameters.CalcTotalCost(); GUILayout.Label(Lang("Free parameter:") + free_parameter, EditorStyles.wordWrappedLabel); } - int using_parameter = (key_size.intValue * 8); - if(parameter_multiplexing.boolValue == true) - { - int lock_size = 1; - int switch_count = ShellProtector.GetRequiredSwitchCount(key_size.intValue, sync_size.intValue); - using_parameter = switch_count + lock_size + sync_size.intValue * 8; - } + int lock_size = 1; + int switch_count = ShellProtector.GetRequiredSwitchCount(key_size.intValue, sync_size.intValue); + int using_parameter = switch_count + lock_size + sync_size.intValue * 8; GUILayout.Label(Lang("Parameters to be used:") + using_parameter, EditorStyles.wordWrappedLabel); serializedObject.Update(); @@ -332,15 +326,6 @@ public override void OnInspectorGUI() GUILayout.Space(10); - GUILayout.BeginHorizontal(); - GUILayout.Label(Lang("parameter-multiplexing"), EditorStyles.boldLabel); - parameter_multiplexing.boolValue = EditorGUILayout.Toggle(parameter_multiplexing.boolValue); - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); - GUILayout.Label(Lang("The OSC program must always be on, but it consumes fewer parameters."), EditorStyles.wordWrappedLabel); - - GUILayout.Space(10); - GUILayout.BeginHorizontal(); GUILayout.Label(Lang("Small mip texture"), EditorStyles.boldLabel); bUseSmallMipTexture.boolValue = EditorGUILayout.Toggle(bUseSmallMipTexture.boolValue); diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 776a835..7b1a3ce 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -181,7 +181,7 @@ private static void AddTransition(AnimatorStateTransition transition, int keyLen transition.AddCondition(switchConditions[i], 0, ParameterManager.GetSyncSwitchName(i)); } - private static void AddParameters(AnimatorController anim, int keyLength, int syncSize, bool useMultiplexing) + private static void AddParameters(AnimatorController anim, int keyLength, int syncSize) { anim.AddParameter(new AnimatorControllerParameter { @@ -203,29 +203,24 @@ private static void AddParameters(AnimatorController anim, int keyLength, int sy for (var i = 0; i < keyLength; ++i) anim.AddParameter(ParameterManager.GetKeyName(i), AnimatorControllerParameterType.Float); - if (useMultiplexing) - { - anim.AddParameter(ParameterManager.GetSyncLockName(), AnimatorControllerParameterType.Bool); - var switchCount = ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); - for (var i = 0; i < keyLength; ++i) - anim.AddParameter(ParameterManager.GetSavedKeyName(i), AnimatorControllerParameterType.Float); - for(var i = 0; i < syncSize; ++i) - anim.AddParameter(ParameterManager.GetSyncedKeyName(i), AnimatorControllerParameterType.Float); - for (var i = 0; i < switchCount; ++i) - anim.AddParameter(ParameterManager.GetSyncSwitchName(i), AnimatorControllerParameterType.Bool); - } + anim.AddParameter(ParameterManager.GetSyncLockName(), AnimatorControllerParameterType.Bool); + var switchCount = ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); + for (var i = 0; i < keyLength; ++i) + anim.AddParameter(ParameterManager.GetSavedKeyName(i), AnimatorControllerParameterType.Float); + for (var i = 0; i < syncSize; ++i) + anim.AddParameter(ParameterManager.GetSyncedKeyName(i), AnimatorControllerParameterType.Float); + for (var i = 0; i < switchCount; ++i) + anim.AddParameter(ParameterManager.GetSyncSwitchName(i), AnimatorControllerParameterType.Bool); } - public static void AddKeyLayer(AnimatorController anim, string animationDir, int keyLength, int syncSize, float speed, bool useMultiplexing) + + public static void AddKeyLayer(AnimatorController anim, string animationDir, int keyLength, int syncSize, float speed) { - AddParameters(anim, keyLength, syncSize, useMultiplexing); + AddParameters(anim, keyLength, syncSize); if (anim.layers.Any(l => l.name == "ShellProtector")) return; - if (useMultiplexing) - { - AddMuxLayer(anim, keyLength, syncSize, 0.15f, 0.1f, 1f); // 10hz - AddDemuxLayer(anim, keyLength, syncSize); - } + AddMuxLayer(anim, keyLength, syncSize, 0.15f, 0.1f, 1f); // 10hz + AddDemuxLayer(anim, keyLength, syncSize); AnimatorStateMachine stateMachine = new AnimatorStateMachine { diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index 616819c..59939f6 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -16,79 +16,62 @@ public static class ParameterManager public static string GetIsLocalName() => "IsLocal"; - public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrcParameters, int keyLength, int syncSize, bool useMultiplexing) + public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrcParameters, int keyLength, int syncSize) { var parameters = new List(); - if (useMultiplexing == false) + parameters.Add(new VRCExpressionParameters.Parameter { - for (var i = 0; i < keyLength; ++i) + name = GetSyncLockName(), + saved = true, + networkSynced = true, + valueType = VRCExpressionParameters.ValueType.Bool, + defaultValue = 0.0f + }); + + for (var i = 0; i < syncSize; i++) + { + parameters.Add(new VRCExpressionParameters.Parameter { - parameters.Add(new VRCExpressionParameters.Parameter - { - name = GetKeyName(i), - saved = true, - networkSynced = true, - valueType = VRCExpressionParameters.ValueType.Float, - defaultValue = 0.0f - }); - } + name = GetSyncedKeyName(i), + saved = true, + networkSynced = true, + valueType = VRCExpressionParameters.ValueType.Float, + defaultValue = 0.0f + }); } - else + + for (var i = 0; i < ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); ++i) { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetSyncLockName(), + name = GetSyncSwitchName(i), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Bool, defaultValue = 0.0f }); + } - for (var i = 0; i < syncSize; i++) - { - parameters.Add(new VRCExpressionParameters.Parameter - { - name = GetSyncedKeyName(i), - saved = true, - networkSynced = true, - valueType = VRCExpressionParameters.ValueType.Float, - defaultValue = 0.0f - }); - } - - for (var i = 0; i < ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); ++i) + for (var i = 0; i < keyLength; ++i) + { + parameters.Add(new VRCExpressionParameters.Parameter { - parameters.Add(new VRCExpressionParameters.Parameter - { - name = GetSyncSwitchName(i), - saved = true, - networkSynced = true, - valueType = VRCExpressionParameters.ValueType.Bool, - defaultValue = 0.0f - }); - } + name = GetKeyName(i), + saved = false, + networkSynced = false, + valueType = VRCExpressionParameters.ValueType.Float, + defaultValue = 0.0f + }); - for (var i = 0; i < keyLength; ++i) + parameters.Add(new VRCExpressionParameters.Parameter { - parameters.Add(new VRCExpressionParameters.Parameter - { - name = GetKeyName(i), - saved = false, - networkSynced = false, - valueType = VRCExpressionParameters.ValueType.Float, - defaultValue = 0.0f - }); - - parameters.Add(new VRCExpressionParameters.Parameter - { - name = GetSavedKeyName(i), - saved = true, - networkSynced = false, - valueType = VRCExpressionParameters.ValueType.Float, - defaultValue = 0.0f - }); - } + name = GetSavedKeyName(i), + saved = true, + networkSynced = false, + valueType = VRCExpressionParameters.ValueType.Float, + defaultValue = 0.0f + }); } var result = ScriptableObject.CreateInstance(); diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 31239cf..0b99192 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -104,7 +104,6 @@ struct OtherTextures [SerializeField] int sync_size = 1; [SerializeField] float animation_speed = 5.0f; [SerializeField] bool delete_folders = true; - [SerializeField] bool parameter_multiplexing = false; [SerializeField] bool bUseSmallMipTexture = true; [SerializeField] bool bPreserveMMD = true; @@ -744,7 +743,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) ///////////////////////parameter//////////////////// var av3 = avatar.GetComponent(); - av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, key_size, sync_size, parameter_multiplexing); + av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, key_size, sync_size); AssetDatabase.CreateAsset(av3.expressionParameters, Path.Combine(avatarDir, av3.expressionParameters.name + ".asset")); //////////////////////////////////////////////////// if (!isModular) @@ -1041,7 +1040,7 @@ public void SetAnimations(GameObject avatar, bool clone) GameObject[] meshArray = new GameObject[meshes.Count]; meshes.CopyTo(meshArray); AnimatorManager.CreateKeyAniamtions(Path.Combine(asset_dir, "Animations"), animationDir, meshArray); - AnimatorManager.AddKeyLayer(fx, animationDir, key_size, sync_size, animation_speed, parameter_multiplexing); + AnimatorManager.AddKeyLayer(fx, animationDir, key_size, sync_size, animation_speed); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); From 9b1addf9fad31c9032806b12d8f37b569cfe3760 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Tue, 25 Mar 2025 12:30:39 +0900 Subject: [PATCH 13/59] Refactor: extract format interface --- Editor/MaterialAdvancedSettings.cs | 19 +- Editor/ShellProtectorEditor.cs | 41 +- Runtime/Scripts/EncryptTexture.cs | 538 ------------------ Runtime/Scripts/Format.cs | 37 ++ ...{EncryptTexture.cs.meta => Format.cs.meta} | 2 +- Runtime/Scripts/Formats.meta | 8 + Runtime/Scripts/Formats/DXTFormat.cs | 202 +++++++ Runtime/Scripts/Formats/DXTFormat.cs.meta | 11 + Runtime/Scripts/Formats/RGBFormat.cs | 119 ++++ Runtime/Scripts/Formats/RGBFormat.cs.meta | 11 + Runtime/Scripts/Injector/Injector.cs | 22 +- Runtime/Scripts/ShellProtector.cs | 84 +-- Runtime/Scripts/TextureEncryptManager.cs | 164 ++++++ Runtime/Scripts/TextureEncryptManager.cs.meta | 11 + Runtime/Shader/BC7.cginc | 70 +++ Runtime/Shader/BC7.cginc.meta | 7 + Runtime/Shader/ShellProtector.cginc | 6 + 17 files changed, 692 insertions(+), 660 deletions(-) delete mode 100644 Runtime/Scripts/EncryptTexture.cs create mode 100644 Runtime/Scripts/Format.cs rename Runtime/Scripts/{EncryptTexture.cs.meta => Format.cs.meta} (83%) create mode 100644 Runtime/Scripts/Formats.meta create mode 100644 Runtime/Scripts/Formats/DXTFormat.cs create mode 100644 Runtime/Scripts/Formats/DXTFormat.cs.meta create mode 100644 Runtime/Scripts/Formats/RGBFormat.cs create mode 100644 Runtime/Scripts/Formats/RGBFormat.cs.meta create mode 100644 Runtime/Scripts/TextureEncryptManager.cs create mode 100644 Runtime/Scripts/TextureEncryptManager.cs.meta create mode 100644 Runtime/Shader/BC7.cginc create mode 100644 Runtime/Shader/BC7.cginc.meta diff --git a/Editor/MaterialAdvancedSettings.cs b/Editor/MaterialAdvancedSettings.cs index 6c0076d..dc2a34c 100644 --- a/Editor/MaterialAdvancedSettings.cs +++ b/Editor/MaterialAdvancedSettings.cs @@ -84,17 +84,16 @@ private void OnGUI() { GUILayout.Label(Lang("The main texture is empty."), redStyle); } - else if ((mainTex is Texture2D) == false) + else if (!TextureEncryptManager.IsSupportedTexture(mainTex)) { - GUILayout.Label(Lang("The main texture is not Texture2D."), redStyle); - } - else if ( - mainTex.format != TextureFormat.DXT1 && - mainTex.format != TextureFormat.DXT5 && - mainTex.format != TextureFormat.RGB24 && - mainTex.format != TextureFormat.RGBA32) - { - GUILayout.Label(Lang("The main texture is not supported format."), redStyle); + if (!(mainTex is Texture2D)) + { + GUILayout.Label(Lang("The main texture is not Texture2D."), redStyle); + } + else + { + GUILayout.Label(Lang("The main texture is not supported format."), redStyle); + } } } } diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index 8d383ab..34bc3d1 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -422,19 +422,19 @@ public override void OnInspectorGUI() TextureSettings.SetRWEnableTexture(texture); - Texture2D[] encrypted_texture = root.GetEncryptTexture().TextureEncrypt(texture, KeyGenerator.MakeKeyBytes(root.pwd, root.pwd2, key_size.intValue), new XXTEA()); + var result = TextureEncryptManager.EncryptTexture(texture, KeyGenerator.MakeKeyBytes(root.pwd, root.pwd2, key_size.intValue), new XXTEA()); - last = encrypted_texture[0]; + last = result.Texture1; if (!AssetDatabase.IsValidFolder(root.asset_dir + '/' + root.descriptor.gameObject.name)) AssetDatabase.CreateFolder(root.asset_dir, root.descriptor.gameObject.name); if (!AssetDatabase.IsValidFolder(root.asset_dir + '/' + root.descriptor.gameObject.name + "/mat")) AssetDatabase.CreateFolder(root.asset_dir + '/' + root.descriptor.gameObject.name, "mat"); - AssetDatabase.CreateAsset(encrypted_texture[0], root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.asset"); - File.WriteAllBytes(root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.png", encrypted_texture[1].EncodeToPNG()); - if (encrypted_texture[1] != null) - AssetDatabase.CreateAsset(encrypted_texture[1], root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt2.asset"); + AssetDatabase.CreateAsset(result.Texture1, root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.asset"); + File.WriteAllBytes(root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.png", result.Texture2.EncodeToPNG()); + if (result.Texture2 != null) + AssetDatabase.CreateAsset(result.Texture2, root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt2.asset"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); @@ -443,35 +443,6 @@ public override void OnInspectorGUI() Selection.activeObject = last; } - /*if (GUILayout.Button("Decrypt")) - { - Texture2D last = null; - for (int i = 0; i < texture_list.count; i++) - { - SerializedProperty textureProperty = texture_list.serializedProperty.GetArrayElementAtIndex(i); - Texture2D texture = textureProperty.objectReferenceValue as Texture2D; - - root.SetRWEnableTexture(texture); - - Texture2D tmp = root.GetEncryptTexture().TextureDecryptXXTEA(texture, root.MakeKeyBytes(root.pwd)); - - if (root.asset_dir[root.asset_dir.Length - 1] == '/') - root.asset_dir = root.asset_dir.Remove(root.asset_dir.Length - 1); - - if (!AssetDatabase.IsValidFolder(root.asset_dir + '/' + root.gameObject.name)) - AssetDatabase.CreateFolder(root.asset_dir, root.gameObject.name); - if (!AssetDatabase.IsValidFolder(root.asset_dir + '/' + root.gameObject.name + "/mat")) - AssetDatabase.CreateFolder(root.asset_dir + '/' + root.gameObject.name, "mat"); - - System.IO.File.WriteAllBytes(root.asset_dir + '/' + root.gameObject.name + '/' + texture.name + "_decrypt.png", tmp.EncodeToPNG()); - last = (Texture2D)AssetDatabase.LoadAssetAtPath(root.asset_dir + '/' + root.gameObject.name + '/' + texture.name + "_decrypt.png", typeof(Texture2D)); - - AssetDatabase.Refresh(); - } - if (last != null) - Selection.activeObject = last; - }*/ - GUILayout.EndHorizontal(); } serializedObject.ApplyModifiedProperties(); diff --git a/Runtime/Scripts/EncryptTexture.cs b/Runtime/Scripts/EncryptTexture.cs deleted file mode 100644 index 88251de..0000000 --- a/Runtime/Scripts/EncryptTexture.cs +++ /dev/null @@ -1,538 +0,0 @@ -#if UNITY_EDITOR -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using System; -using System.Linq; -using UnityEditor; - -namespace Shell.Protector -{ - public class EncryptTexture - { - public static int GetCanMipmapLevel(int w, int h) - { - int w_level, h_level; - if (w < 1 || h <= 1) - return 0; - w_level = (int)Mathf.Log(w, 2); - h_level = (int)Mathf.Log(h, 2); - return Math.Max(w_level, h_level); - } - public int GetDXT1Length(int w, int h, int m) - { - int len = 0; - for (int i = 0; i < m + 1; ++i) - { - len += w * h / 16 * 8; - w /= 2; - h /= 2; - } - return len; - } - public static bool HasAlpha(Texture2D texture) - { - Color32[] pixels = texture.GetPixels32(); - for (int i = 0; i < pixels.Length; i++) - { - if (pixels[i].a != 255) - { - return true; - } - } - return false; - } - private int GetArrayIdxInBlock(int block, int n, int w) - { - int wblock = w / 4; - int x = (block % wblock); - int y = block / wblock; - - int pivot = x * 4 + y * w * 4; - - return pivot + n / 4 * w + n % 4; - } - /// - /// 밉 레벨에 해당하는 DXT데이터 배열을 가져오는 함수 - /// - private byte[] GetArrayDXT(byte[] data, int texture_width, int texture_height, bool dxt5, int miplv) - { - int start = 0; - int end = 0; - - for (int i = 0; i <= miplv; ++i) - { - start += end; - int w = texture_width / (int)(Mathf.Pow(2, i)); - int h = texture_height / (int)(Mathf.Pow(2, i)); - int block_count = (w / 4) * (h / 4); - int len = block_count * 8; - if (dxt5) - len *= 2; - end = len; - } - - var segment = new ArraySegment(data, start, end); - return segment.ToArray(); - } - public Texture2D GenerateRefMipmap(int width, int height, bool small = false) - { - int mip_lv = GetCanMipmapLevel(width, height); - Debug.LogFormat("mip {0}, {1} : {2}", width, height, mip_lv); - - Texture2D mip = new Texture2D(width, (small == false) ? height : 1, TextureFormat.RGB24, mip_lv, true); - mip.filterMode = FilterMode.Bilinear; - mip.anisoLevel = (small == false) ? 1 : 0; - - for (int m = 0; m < mip.mipmapCount; ++m) - { - Color32[] pixels_mip = mip.GetPixels32(m); - for (int i = 0; i < pixels_mip.Length; ++i) - { - pixels_mip[i].r = (byte)(m * 10); - pixels_mip[i].g = 0; - pixels_mip[i].b = 0; - } - mip.SetPixels32(pixels_mip, m); - } - if (small == false) - mip.Compress(false); - return mip; - } - - public Texture2D GenerateFallback(Texture2D original, int size = 32) - { - if(original.width < 128 || original.height < 128) - { - return null; - } - TextureFormat format = TextureFormat.RGB24; - bool hasAlpha = HasAlpha(original); - if (hasAlpha) - format = TextureFormat.RGBA32; - - RenderTexture renderTexture = new RenderTexture(size, size, 0); - RenderTexture.active = renderTexture; - - Graphics.Blit(original, renderTexture); - - Texture2D resizedTexture = new Texture2D(size, size, format, true); - resizedTexture.ReadPixels(new Rect(0, 0, size, size), 0, 0); - resizedTexture.Apply(); - - resizedTexture.filterMode = FilterMode.Point; - resizedTexture.anisoLevel = 0; - resizedTexture.Compress(false); - if(hasAlpha) - resizedTexture.alphaIsTransparency = true; - - RenderTexture.active = null; - - return resizedTexture; - } - - private Texture2D[] EncryptDXT1(Texture2D texture, byte[] key, IEncryptor encryptor) - { - Texture2D[] result = new Texture2D[2]; - uint[] key_uint = new uint[4]; - key_uint[0] = (uint)(key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24)); - key_uint[1] = (uint)(key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24)); - key_uint[2] = (uint)(key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24)); - key_uint[3] = 0; - - int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); - Texture2D dxt1 = texture; - if (texture.format == TextureFormat.DXT1Crunched) - { - Debug.LogWarningFormat("{0} is the crunch compression format. There may be degradation in image quality.", texture.name); - dxt1 = new Texture2D(texture.width, texture.height, TextureFormat.RGB24, mip_lv, true); - for (int m = 0; m <= mip_lv; ++m) - { - if (m != 0 && m == mip_lv) - break; - dxt1.SetPixels32(texture.GetPixels32(m), m); - dxt1.Apply(); - } - dxt1.Compress(false); - } - if (mip_lv != 0) - { - result[0] = new Texture2D(dxt1.width, dxt1.height, TextureFormat.DXT1, mip_lv, true); - result[1] = new Texture2D(dxt1.width / 4, dxt1.height / 4, TextureFormat.RGBA32, mip_lv, true); - } - else - { - result[0] = new Texture2D(dxt1.width, dxt1.height, TextureFormat.DXT1, false, true); - result[1] = new Texture2D(dxt1.width / 4, dxt1.height / 4, TextureFormat.RGBA32, false, true); - } - result[1].filterMode = FilterMode.Point; - result[1].anisoLevel = 0; - //Note: DXT1 per 4x4 block is 64bit - var raw_data = dxt1.GetRawTextureData(); - int lenidx = 0; - - for (int m = 0; m <= mip_lv; ++m) - { - if (m != 0 && m == mip_lv) - break; - var tex_data = GetArrayDXT(raw_data, dxt1.width, dxt1.height, false, m); - var pixel = result[1].GetPixels32(m); - - for (int i = 0; i < tex_data.Length; i += 16) //reference color texture - { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)(i / 8); //8 bytes(1block) are same id. - - uint[] data = new uint[2]; - data[0] = (uint)(tex_data[i + 0] + (tex_data[i + 1] << 8) + (tex_data[i + 2] << 16) + (tex_data[i + 3] << 24)); - data[1] = (uint)(tex_data[(i + 8) + 0] + (tex_data[(i + 8) + 1] << 8) + (tex_data[(i + 8) + 2] << 16) + (tex_data[(i + 8) + 3] << 24)); - - uint[] data_enc = encryptor.Encrypt(data, key_uint); - - for (int j = 0; j < 2; ++j) - { - pixel[i / 8 + j].r = (byte)((data_enc[j] & 0x000000FF) >> 0); - pixel[i / 8 + j].g = (byte)((data_enc[j] & 0x0000FF00) >> 8); - pixel[i / 8 + j].b = (byte)((data_enc[j] & 0x00FF0000) >> 16); - pixel[i / 8 + j].a = (byte)((data_enc[j] & 0xFF000000) >> 24); - } - } - for (int i = 0; i < tex_data.Length; i += 8) - { - tex_data[i + 0] = 255; - tex_data[i + 1] = 255; - tex_data[i + 2] = 0; - tex_data[i + 3] = 0; - } - for (int i = 0; i < tex_data.Length; ++i) - { - raw_data[i + lenidx] = tex_data[i]; - } - lenidx += tex_data.Length; - result[1].SetPixels32(pixel, m); - } - result[0].LoadRawTextureData(raw_data); - - return result; - } - private Texture2D[] EncryptDXT5(Texture2D tex, byte[] key, IEncryptor encryptor) - { - Texture2D[] result = new Texture2D[2]; - uint[] key_uint = new uint[4]; - key_uint[0] = (uint)(key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24)); - key_uint[1] = (uint)(key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24)); - key_uint[2] = (uint)(key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24)); - key_uint[3] = 0; - - int mip_lv = GetCanMipmapLevel(tex.width / 4, tex.height / 4); - Texture2D dxt5 = tex; - if (tex.format == TextureFormat.DXT5Crunched) - { - Debug.LogWarningFormat("{0} is the crunch compression format. There may be degradation in image quality.", tex.name); - dxt5 = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, mip_lv, true); - for (int m = 0; m < mip_lv; ++m) - { - if (m != 0 && m == mip_lv) - break; - dxt5.SetPixels32(tex.GetPixels32(m), m); - dxt5.Apply(); - } - - dxt5.Compress(true); - } - - if (mip_lv != 0) - { - result[0] = new Texture2D(tex.width, tex.height, TextureFormat.DXT5, mip_lv, true); - result[1] = new Texture2D(tex.width / 4, tex.height / 4, TextureFormat.RGBA32, mip_lv, true); - } - else - { - result[0] = new Texture2D(tex.width, tex.height, TextureFormat.DXT5, false, true); - result[1] = new Texture2D(tex.width / 4, tex.height / 4, TextureFormat.RGBA32, false, true); - } - result[0].alphaIsTransparency = true; - result[1].filterMode = FilterMode.Point; - result[1].anisoLevel = 0; - var raw_data = dxt5.GetRawTextureData(); - int lenidx = 0; - for (int m = 0; m <= mip_lv; ++m) - { - if (m != 0 && m == mip_lv) - break; - var tex_data = GetArrayDXT(raw_data, tex.width, tex.height, true, m); - var pixel = result[1].GetPixels32(m); - - for (int i = 0; i < tex_data.Length; i += 32) //reference color texture - { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)(i / 16); - - uint[] data = new uint[2]; - data[0] = (uint)(tex_data[i + 8] + (tex_data[i + 9] << 8) + (tex_data[i + 10] << 16) + (tex_data[i + 11] << 24)); - data[1] = (uint)(tex_data[i + 16 + 8] + (tex_data[i + 16 + 9] << 8) + (tex_data[i + 16 + 10] << 16) + (tex_data[i + 16 + 11] << 24)); - - uint[] data_enc = encryptor.Encrypt(data, key_uint); - - for (int j = 0; j < 2; ++j) - { - pixel[i / 16 + j].r = (byte)((data_enc[j] & 0x000000FF) >> 0); - pixel[i / 16 + j].g = (byte)((data_enc[j] & 0x0000FF00) >> 8); - pixel[i / 16 + j].b = (byte)((data_enc[j] & 0x00FF0000) >> 16); - pixel[i / 16 + j].a = (byte)((data_enc[j] & 0xFF000000) >> 24); - } - } - for (int i = 0; i < tex_data.Length; i += 16) - { - tex_data[i + 8] = 255; - tex_data[i + 9] = 255; - tex_data[i + 10] = 0; - tex_data[i + 11] = 0; - } - for (int i = 0; i < tex_data.Length; ++i) - { - raw_data[i + lenidx] = tex_data[i]; - } - lenidx += tex_data.Length; - result[1].SetPixels32(pixel, m); - } - result[0].LoadRawTextureData(raw_data); - - return result; - } - private Texture2D[] EncryptRGB24(Texture2D tex, byte[] key, IEncryptor encryptor) - { - Texture2D[] result = new Texture2D[2]; - uint[] key_uint = new uint[4]; - key_uint[0] = (uint)(key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24)); - key_uint[1] = (uint)(key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24)); - key_uint[2] = (uint)(key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24)); - key_uint[3] = 0; - - int mip_lv = GetCanMipmapLevel(tex.width, tex.height); - result[0] = new Texture2D(tex.width, tex.height, TextureFormat.RGB24, mip_lv - 2, true); //mip_lv-2 is blur trick (like the box filter) - for (int m = 0; m < result[0].mipmapCount; ++m) - { - Color32[] pixels = tex.GetPixels32(m); - - for (int i = 0; i < pixels.Length; i += 4) - { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)i; - - uint[] data = new uint[3]; - data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 1].r << 24)); - data[1] = (uint)(pixels[i + 1].g + (pixels[i + 1].b << 8) + (pixels[i + 2].r << 16) + (pixels[i + 2].g << 24)); - data[2] = (uint)(pixels[i + 2].b + (pixels[i + 3].r << 8) + (pixels[i + 3].g << 16) + (pixels[i + 3].b << 24)); - - uint[] data_enc = encryptor.Encrypt(data, key_uint); - - pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); - pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); - pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); - pixels[i + 1].r = (byte)((data_enc[0] & 0xFF000000) >> 24); - pixels[i + 1].g = (byte)((data_enc[1] & 0x000000FF) >> 0); - pixels[i + 1].b = (byte)((data_enc[1] & 0x0000FF00) >> 8); - pixels[i + 2].r = (byte)((data_enc[1] & 0x00FF0000) >> 16); - pixels[i + 2].g = (byte)((data_enc[1] & 0xFF000000) >> 24); - pixels[i + 2].b = (byte)((data_enc[2] & 0x000000FF) >> 0); - pixels[i + 3].r = (byte)((data_enc[2] & 0x0000FF00) >> 8); - pixels[i + 3].g = (byte)((data_enc[2] & 0x00FF0000) >> 16); - pixels[i + 3].b = (byte)((data_enc[2] & 0xFF000000) >> 24); - } - result[0].SetPixels32(pixels, m); - } - return result; - } - - private Texture2D[] EncryptRGBA32(Texture2D tex, byte[] key, IEncryptor encryptor) - { - Texture2D[] result = new Texture2D[2]; - uint[] key_uint = new uint[4]; - key_uint[0] = (uint)(key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24)); - key_uint[1] = (uint)(key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24)); - key_uint[2] = (uint)(key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24)); - key_uint[3] = 0; - - int mip_lv = GetCanMipmapLevel(tex.width, tex.height); - result[0] = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, mip_lv - 2, true); //mip_lv-2 is blur trick (like the box filter) - for (int m = 0; m < result[0].mipmapCount; ++m) - { - Color32[] pixels = tex.GetPixels32(m); - - for (int i = 0; i < pixels.Length; i += 2) - { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)i; - - uint[] data = new uint[2]; - data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 0].a << 24)); - data[1] = (uint)(pixels[i + 1].r + (pixels[i + 1].g << 8) + (pixels[i + 1].b << 16) + (pixels[i + 1].a << 24)); - - uint[] data_enc = encryptor.Encrypt(data, key_uint); - - pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); - pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); - pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); - pixels[i + 0].a = (byte)((data_enc[0] & 0xFF000000) >> 24); - pixels[i + 1].r = (byte)((data_enc[1] & 0x000000FF) >> 0); - pixels[i + 1].g = (byte)((data_enc[1] & 0x0000FF00) >> 8); - pixels[i + 1].b = (byte)((data_enc[1] & 0x00FF0000) >> 16); - pixels[i + 1].a = (byte)((data_enc[1] & 0xFF000000) >> 24); - } - result[0].SetPixels32(pixels, m); - } - - return result; - } - - public Texture2D[] TextureEncrypt(Texture2D texture, byte[] key, IEncryptor encryptor) - { - Texture2D tex = texture; - if (tex.width % 2 != 0 && tex.height % 2 != 0) - { - Debug.LogErrorFormat("{0} : The texture size must be a multiple of 2!", texture.name); - return null; - } - - Texture2D[] result = new Texture2D[2]; - - if (texture.format == TextureFormat.DXT1 || - texture.format == TextureFormat.DXT1Crunched || - texture.format == TextureFormat.DXT5 || - texture.format == TextureFormat.DXT5Crunched) - { - if (tex.width < 8) - { - Debug.LogErrorFormat("{0} : The texture width must be >= 8px", texture.name); - return null; - } - if (tex.height < 4) - { - Debug.LogErrorFormat("{0} : The texture height must be >= 4px", texture.name); - return null; - } - } - - if (texture.format == TextureFormat.DXT1 || texture.format == TextureFormat.DXT1Crunched) - { - result = EncryptDXT1(texture, key, encryptor); - } - else if (texture.format == TextureFormat.DXT5 || texture.format == TextureFormat.DXT5Crunched) - { - result = EncryptDXT5(texture, key, encryptor); - } - else if (tex.format == TextureFormat.RGB24) - { - result = EncryptRGB24(tex, key, encryptor); - } - else if (tex.format == TextureFormat.RGBA32) - { - result = EncryptRGBA32(tex, key, encryptor); - } - else - { - Debug.LogErrorFormat("{0} is not supported texture format! supported type:DXT1, DXT5, RGB, RGBA", tex.name); - result[0] = null; - } - if (result[0]) - { - result[0].filterMode = FilterMode.Point; - result[0].anisoLevel = 0; - } - return result; - } - /*public Texture2D TextureDecryptXXTEA(Texture2D texture, byte[] key) - { - Texture2D tex = texture; - - if (tex.width % 2 != 0 && tex.height % 2 != 0) - { - Debug.LogErrorFormat("{0} : The texture size must be a multiple of 2!", texture.name); - return null; - } - - int mip_lv = GetCanMipmapLevel(tex.width, tex.height); - - Texture2D tmp; - - XXTEA xxtea = new XXTEA(); - - uint[] key_uint = new uint[4]; - key_uint[0] = (uint)(key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24)); - key_uint[1] = (uint)(key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24)); - key_uint[2] = (uint)(key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24)); - key_uint[3] = 0; - if (HasAlpha(tex)) - { - tmp = new Texture2D(tex.width, tex.height, TextureFormat.RGB24, mip_lv - 2, true); //mip_lv-2 is blur trick (like the box filter) - for (int m = 0; m < tmp.mipmapCount; ++m) - { - Color32[] pixels = tex.GetPixels32(m); - - for (int i = 0; i < pixels.Length; i += 4) - { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)i; - - uint[] data = new uint[2]; - data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 0].a << 24)); - data[1] = (uint)(pixels[i + 1].r + (pixels[i + 1].g << 8) + (pixels[i + 1].b << 16) + (pixels[i + 1].a << 24)); - - uint[] data_enc = xxtea.Decrypt(data, key_uint); - - pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); - pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); - pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); - pixels[i + 0].a = (byte)((data_enc[0] & 0xFF000000) >> 24); - pixels[i + 1].r = (byte)((data_enc[1] & 0x000000FF) >> 0); - pixels[i + 1].g = (byte)((data_enc[1] & 0x0000FF00) >> 8); - pixels[i + 1].b = (byte)((data_enc[1] & 0x00FF0000) >> 16); - pixels[i + 1].a = (byte)((data_enc[1] & 0xFF000000) >> 24); - } - tmp.SetPixels32(pixels, m); - } - } - else - { - tmp = new Texture2D(tex.width, tex.height, TextureFormat.RGB24, mip_lv - 2, true); //mip_lv-2 is blur trick (like the box filter) - for (int m = 0; m < tmp.mipmapCount; ++m) - { - Color32[] pixels = tex.GetPixels32(m); - - for (int i = 0; i < pixels.Length; i += 4) - { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)i; - - uint[] data = new uint[3]; - data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 1].r << 24)); - data[1] = (uint)(pixels[i + 1].g + (pixels[i + 1].b << 8) + (pixels[i + 2].r << 16) + (pixels[i + 2].g << 24)); - data[2] = (uint)(pixels[i + 2].b + (pixels[i + 3].r << 8) + (pixels[i + 3].g << 16) + (pixels[i + 3].b << 24)); - - uint[] data_enc = xxtea.Decrypt(data, key_uint); - - pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); - pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); - pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); - pixels[i + 1].r = (byte)((data_enc[0] & 0xFF000000) >> 24); - pixels[i + 1].g = (byte)((data_enc[1] & 0x000000FF) >> 0); - pixels[i + 1].b = (byte)((data_enc[1] & 0x0000FF00) >> 8); - pixels[i + 2].r = (byte)((data_enc[1] & 0x00FF0000) >> 16); - pixels[i + 2].g = (byte)((data_enc[1] & 0xFF000000) >> 24); - pixels[i + 2].b = (byte)((data_enc[2] & 0x000000FF) >> 0); - pixels[i + 3].r = (byte)((data_enc[2] & 0x0000FF00) >> 8); - pixels[i + 3].g = (byte)((data_enc[2] & 0x00FF0000) >> 16); - pixels[i + 3].b = (byte)((data_enc[2] & 0xFF000000) >> 24); - } - tmp.SetPixels32(pixels, m); - } - } - - tmp.filterMode = FilterMode.Point; - tmp.anisoLevel = 0; - return tmp; - }*/ - } -} -#endif \ No newline at end of file diff --git a/Runtime/Scripts/Format.cs b/Runtime/Scripts/Format.cs new file mode 100644 index 0000000..8d6e330 --- /dev/null +++ b/Runtime/Scripts/Format.cs @@ -0,0 +1,37 @@ +using Shell.Protector; +using UnityEngine; + +public struct EncryptResult { + public Texture2D Texture1; + public Texture2D Texture2; +} + +public interface ITextureFormat { + bool CanHandle(TextureFormat format); + EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm); + void SetFormatKeywords(Material material); + (int, int) CalculateOffsets(Texture2D texture); +} + +public abstract class BaseTextureFormat : ITextureFormat { + protected uint[] ConvertKeyToUInt(byte[] key) { + uint[] key_uint = new uint[4]; + key_uint[0] = (uint)(key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24)); + key_uint[1] = (uint)(key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24)); + key_uint[2] = (uint)(key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24)); + key_uint[3] = 0; + return key_uint; + } + + protected int GetCanMipmapLevel(int w, int h) { + if (w < 1 || h <= 1) return 0; + int w_level = (int)Mathf.Log(w, 2); + int h_level = (int)Mathf.Log(h, 2); + return Mathf.Max(w_level, h_level); + } + + public abstract bool CanHandle(TextureFormat format); + public abstract EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm); + public abstract void SetFormatKeywords(Material material); + public abstract (int, int) CalculateOffsets(Texture2D texture); +} \ No newline at end of file diff --git a/Runtime/Scripts/EncryptTexture.cs.meta b/Runtime/Scripts/Format.cs.meta similarity index 83% rename from Runtime/Scripts/EncryptTexture.cs.meta rename to Runtime/Scripts/Format.cs.meta index bc6fe90..aba9c45 100644 --- a/Runtime/Scripts/EncryptTexture.cs.meta +++ b/Runtime/Scripts/Format.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 66bc90ffbdb30c34694f898f5f1a2842 +guid: 28a232c48b14ddb4594ae2600ebedd44 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Scripts/Formats.meta b/Runtime/Scripts/Formats.meta new file mode 100644 index 0000000..42f816d --- /dev/null +++ b/Runtime/Scripts/Formats.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 30147c802ecd3944ba60a816157bea4c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Formats/DXTFormat.cs b/Runtime/Scripts/Formats/DXTFormat.cs new file mode 100644 index 0000000..076368e --- /dev/null +++ b/Runtime/Scripts/Formats/DXTFormat.cs @@ -0,0 +1,202 @@ +using UnityEngine; +using Shell.Protector; +using System; + +public abstract class DXTFormat : BaseTextureFormat { + protected byte[] GetArrayDXT(byte[] data, int texture_width, int texture_height, bool dxt5, int miplv) { + int start = 0; + int end = 0; + + for (int i = 0; i <= miplv; ++i) { + start += end; + int w = texture_width / (int)(Mathf.Pow(2, i)); + int h = texture_height / (int)(Mathf.Pow(2, i)); + int block_count = (w / 4) * (h / 4); + int len = block_count * 8; + if (dxt5) len *= 2; + end = len; + } + + var segment = new ArraySegment(data, start, end); + return segment.ToArray(); + } + + protected Texture2D HandleCrunchedFormat(Texture2D texture, int mip_lv, bool isDXT5) { + if (!texture.name.Contains("Crunched")) return texture; + + Debug.LogWarningFormat("{0} is the crunch compression format. There may be degradation in image quality.", texture.name); + var format = isDXT5 ? TextureFormat.RGBA32 : TextureFormat.RGB24; + var result = new Texture2D(texture.width, texture.height, format, mip_lv, true); + + for (int m = 0; m <= mip_lv; ++m) { + if (m != 0 && m == mip_lv) break; + result.SetPixels32(texture.GetPixels32(m), m); + result.Apply(); + } + + result.Compress(isDXT5); + return result; + } + + public override void SetFormatKeywords(Material material) { + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); + } + + public override (int, int) CalculateOffsets(Texture2D texture) { + int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1 + 2; + int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1 + 2; + return (woffset, hoffset); + } +} + +public class DXT1Format : DXTFormat { + public override bool CanHandle(TextureFormat format) { + return format == TextureFormat.DXT1 || format == TextureFormat.DXT1Crunched; + } + + public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { + if (texture.width < 8) { + Debug.LogErrorFormat("{0} : The texture width must be >= 8px", texture.name); + return new EncryptResult(); + } + if (texture.height < 4) { + Debug.LogErrorFormat("{0} : The texture height must be >= 4px", texture.name); + return new EncryptResult(); + } + + int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); + Texture2D dxt1 = HandleCrunchedFormat(texture, mip_lv, false); + + var result = new EncryptResult(); + if (mip_lv != 0) { + result.Texture1 = new Texture2D(dxt1.width, dxt1.height, TextureFormat.DXT1, mip_lv, true); + result.Texture2 = new Texture2D(dxt1.width / 4, dxt1.height / 4, TextureFormat.RGBA32, mip_lv, true); + } else { + result.Texture1 = new Texture2D(dxt1.width, dxt1.height, TextureFormat.DXT1, false, true); + result.Texture2 = new Texture2D(dxt1.width / 4, dxt1.height / 4, TextureFormat.RGBA32, false, true); + } + result.Texture2.filterMode = FilterMode.Point; + result.Texture2.anisoLevel = 0; + + var raw_data = dxt1.GetRawTextureData(); + int lenidx = 0; + var key_uint = ConvertKeyToUInt(key); + + for (int m = 0; m <= mip_lv; ++m) { + if (m != 0 && m == mip_lv) break; + var tex_data = GetArrayDXT(raw_data, dxt1.width, dxt1.height, false, m); + var pixel = result.Texture2.GetPixels32(m); + + for (int i = 0; i < tex_data.Length; i += 16) { + key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); + key_uint[3] ^= (uint)(i / 8); + + uint[] data = new uint[2]; + data[0] = (uint)(tex_data[i + 0] + (tex_data[i + 1] << 8) + (tex_data[i + 2] << 16) + (tex_data[i + 3] << 24)); + data[1] = (uint)(tex_data[(i + 8) + 0] + (tex_data[(i + 8) + 1] << 8) + (tex_data[(i + 8) + 2] << 16) + (tex_data[(i + 8) + 3] << 24)); + + uint[] data_enc = algorithm.Encrypt(data, key_uint); + + for (int j = 0; j < 2; ++j) { + pixel[i / 8 + j].r = (byte)((data_enc[j] & 0x000000FF) >> 0); + pixel[i / 8 + j].g = (byte)((data_enc[j] & 0x0000FF00) >> 8); + pixel[i / 8 + j].b = (byte)((data_enc[j] & 0x00FF0000) >> 16); + pixel[i / 8 + j].a = (byte)((data_enc[j] & 0xFF000000) >> 24); + } + } + for (int i = 0; i < tex_data.Length; i += 8) { + tex_data[i + 0] = 255; + tex_data[i + 1] = 255; + tex_data[i + 2] = 0; + tex_data[i + 3] = 0; + } + for (int i = 0; i < tex_data.Length; ++i) { + raw_data[i + lenidx] = tex_data[i]; + } + lenidx += tex_data.Length; + result.Texture2.SetPixels32(pixel, m); + } + result.Texture1.LoadRawTextureData(raw_data); + result.Texture1.filterMode = FilterMode.Point; + result.Texture1.anisoLevel = 0; + + return result; + } +} + +public class DXT5Format : DXTFormat { + public override bool CanHandle(TextureFormat format) { + return format == TextureFormat.DXT5 || format == TextureFormat.DXT5Crunched; + } + + public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { + if (texture.width < 8) { + Debug.LogErrorFormat("{0} : The texture width must be >= 8px", texture.name); + return new EncryptResult(); + } + if (texture.height < 4) { + Debug.LogErrorFormat("{0} : The texture height must be >= 4px", texture.name); + return new EncryptResult(); + } + + int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); + Texture2D dxt5 = HandleCrunchedFormat(texture, mip_lv, true); + + var result = new EncryptResult(); + if (mip_lv != 0) { + result.Texture1 = new Texture2D(texture.width, texture.height, TextureFormat.DXT5, mip_lv, true); + result.Texture2 = new Texture2D(texture.width / 4, texture.height / 4, TextureFormat.RGBA32, mip_lv, true); + } else { + result.Texture1 = new Texture2D(texture.width, texture.height, TextureFormat.DXT5, false, true); + result.Texture2 = new Texture2D(texture.width / 4, texture.height / 4, TextureFormat.RGBA32, false, true); + } + result.Texture1.alphaIsTransparency = true; + result.Texture2.filterMode = FilterMode.Point; + result.Texture2.anisoLevel = 0; + + var raw_data = dxt5.GetRawTextureData(); + int lenidx = 0; + var key_uint = ConvertKeyToUInt(key); + + for (int m = 0; m <= mip_lv; ++m) { + if (m != 0 && m == mip_lv) break; + var tex_data = GetArrayDXT(raw_data, texture.width, texture.height, true, m); + var pixel = result.Texture2.GetPixels32(m); + + for (int i = 0; i < tex_data.Length; i += 32) { + key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); + key_uint[3] ^= (uint)(i / 16); + + uint[] data = new uint[2]; + data[0] = (uint)(tex_data[i + 8] + (tex_data[i + 9] << 8) + (tex_data[i + 10] << 16) + (tex_data[i + 11] << 24)); + data[1] = (uint)(tex_data[i + 16 + 8] + (tex_data[i + 16 + 9] << 8) + (tex_data[i + 16 + 10] << 16) + (tex_data[i + 16 + 11] << 24)); + + uint[] data_enc = algorithm.Encrypt(data, key_uint); + + for (int j = 0; j < 2; ++j) { + pixel[i / 16 + j].r = (byte)((data_enc[j] & 0x000000FF) >> 0); + pixel[i / 16 + j].g = (byte)((data_enc[j] & 0x0000FF00) >> 8); + pixel[i / 16 + j].b = (byte)((data_enc[j] & 0x00FF0000) >> 16); + pixel[i / 16 + j].a = (byte)((data_enc[j] & 0xFF000000) >> 24); + } + } + for (int i = 0; i < tex_data.Length; i += 16) { + tex_data[i + 8] = 255; + tex_data[i + 9] = 255; + tex_data[i + 10] = 0; + tex_data[i + 11] = 0; + } + for (int i = 0; i < tex_data.Length; ++i) { + raw_data[i + lenidx] = tex_data[i]; + } + lenidx += tex_data.Length; + result.Texture2.SetPixels32(pixel, m); + } + result.Texture1.LoadRawTextureData(raw_data); + result.Texture1.filterMode = FilterMode.Point; + result.Texture1.anisoLevel = 0; + + return result; + } +} \ No newline at end of file diff --git a/Runtime/Scripts/Formats/DXTFormat.cs.meta b/Runtime/Scripts/Formats/DXTFormat.cs.meta new file mode 100644 index 0000000..e91f11f --- /dev/null +++ b/Runtime/Scripts/Formats/DXTFormat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 54fcef2bb0fb0b243b8a33682d209c41 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Formats/RGBFormat.cs b/Runtime/Scripts/Formats/RGBFormat.cs new file mode 100644 index 0000000..094a648 --- /dev/null +++ b/Runtime/Scripts/Formats/RGBFormat.cs @@ -0,0 +1,119 @@ +using UnityEngine; +using Shell.Protector; + +public abstract class RGBFormat : BaseTextureFormat { + protected Texture2D CreateResultTexture(Texture2D texture, TextureFormat format) { + int mip_lv = GetCanMipmapLevel(texture.width, texture.height); + var result = new Texture2D(texture.width, texture.height, format, mip_lv - 2, true); + result.filterMode = FilterMode.Point; + result.anisoLevel = 0; + return result; + } +} + +public class RGB24Format : RGBFormat { + public override bool CanHandle(TextureFormat format) { + return format == TextureFormat.RGB24; + } + + public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { + var result = new EncryptResult(); + result.Texture1 = CreateResultTexture(texture, TextureFormat.RGB24); + + var key_uint = ConvertKeyToUInt(key); + + for (int m = 0; m < result.Texture1.mipmapCount; ++m) { + Color32[] pixels = texture.GetPixels32(m); + + for (int i = 0; i < pixels.Length; i += 4) { + key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); + key_uint[3] ^= (uint)i; + + uint[] data = new uint[3]; + data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 1].r << 24)); + data[1] = (uint)(pixels[i + 1].g + (pixels[i + 1].b << 8) + (pixels[i + 2].r << 16) + (pixels[i + 2].g << 24)); + data[2] = (uint)(pixels[i + 2].b + (pixels[i + 3].r << 8) + (pixels[i + 3].g << 16) + (pixels[i + 3].b << 24)); + + uint[] data_enc = algorithm.Encrypt(data, key_uint); + + pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); + pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); + pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); + pixels[i + 1].r = (byte)((data_enc[0] & 0xFF000000) >> 24); + pixels[i + 1].g = (byte)((data_enc[1] & 0x000000FF) >> 0); + pixels[i + 1].b = (byte)((data_enc[1] & 0x0000FF00) >> 8); + pixels[i + 2].r = (byte)((data_enc[1] & 0x00FF0000) >> 16); + pixels[i + 2].g = (byte)((data_enc[1] & 0xFF000000) >> 24); + pixels[i + 2].b = (byte)((data_enc[2] & 0x000000FF) >> 0); + pixels[i + 3].r = (byte)((data_enc[2] & 0x0000FF00) >> 8); + pixels[i + 3].g = (byte)((data_enc[2] & 0x00FF0000) >> 16); + pixels[i + 3].b = (byte)((data_enc[2] & 0xFF000000) >> 24); + } + result.Texture1.SetPixels32(pixels, m); + } + + return result; + } + + public override void SetFormatKeywords(Material material) { + material.EnableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); + } + + public override (int, int) CalculateOffsets(Texture2D texture) { + int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1; + int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1; + return (woffset, hoffset); + } +} + +public class RGBA32Format : RGBFormat { + public override bool CanHandle(TextureFormat format) { + return format == TextureFormat.RGBA32; + } + + public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { + var result = new EncryptResult(); + result.Texture1 = CreateResultTexture(texture, TextureFormat.RGBA32); + + var key_uint = ConvertKeyToUInt(key); + + for (int m = 0; m < result.Texture1.mipmapCount; ++m) { + Color32[] pixels = texture.GetPixels32(m); + + for (int i = 0; i < pixels.Length; i += 2) { + key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); + key_uint[3] ^= (uint)i; + + uint[] data = new uint[2]; + data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 0].a << 24)); + data[1] = (uint)(pixels[i + 1].r + (pixels[i + 1].g << 8) + (pixels[i + 1].b << 16) + (pixels[i + 1].a << 24)); + + uint[] data_enc = algorithm.Encrypt(data, key_uint); + + pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); + pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); + pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); + pixels[i + 0].a = (byte)((data_enc[0] & 0xFF000000) >> 24); + pixels[i + 1].r = (byte)((data_enc[1] & 0x000000FF) >> 0); + pixels[i + 1].g = (byte)((data_enc[1] & 0x0000FF00) >> 8); + pixels[i + 1].b = (byte)((data_enc[1] & 0x00FF0000) >> 16); + pixels[i + 1].a = (byte)((data_enc[1] & 0xFF000000) >> 24); + } + result.Texture1.SetPixels32(pixels, m); + } + + return result; + } + + public override void SetFormatKeywords(Material material) { + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.EnableKeyword("_SHELL_PROTECTOR_FORMAT1"); + } + + public override (int, int) CalculateOffsets(Texture2D texture) { + int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1; + int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1; + return (woffset, hoffset); + } +} \ No newline at end of file diff --git a/Runtime/Scripts/Formats/RGBFormat.cs.meta b/Runtime/Scripts/Formats/RGBFormat.cs.meta new file mode 100644 index 0000000..38d3beb --- /dev/null +++ b/Runtime/Scripts/Formats/RGBFormat.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d80e9a000061de4b8709ef3f1eef4b5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index 3a8fdd6..b84f95c 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -128,27 +128,7 @@ private void SetKeywords(Material material, bool has_lim_texture = false) { } // Set format keywords - TextureFormat format = ((Texture2D)material.mainTexture).format; - if (format == TextureFormat.DXT1 || format == TextureFormat.DXT5) - { - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); - } - else if (format == TextureFormat.RGBA32) - { - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.EnableKeyword("_SHELL_PROTECTOR_FORMAT1"); - } - else if (format == TextureFormat.RGB24) - { - material.EnableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); - } - else - { - Debug.LogErrorFormat("{0} - main texture is unsupported format!", material.name); - return; - } + TextureEncryptManager.SetFormatKeywords(material); // Set rimlight keyword if (has_lim_texture) material.EnableKeyword("_SHELL_PROTECTOR_RIMLIGHT"); diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 0b99192..ab369b1 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -35,7 +35,6 @@ public class ShellProtector : MonoBehaviour, IEditorOnly [SerializeField] List obfuscationRenderers = new List(); - EncryptTexture encrypt = new EncryptTexture(); Injector injector; AssetManager shader_manager = AssetManager.GetInstance(); bool init = false; @@ -164,10 +163,7 @@ public byte[] GetKeyBytes() { return KeyGenerator.MakeKeyBytes(pwd, pwd2, key_size); } - public EncryptTexture GetEncryptTexture() - { - return encrypt; - } + public GameObject DuplicateAvatar(GameObject avatar) { GameObject cpy = Instantiate(avatar); @@ -221,21 +217,19 @@ bool ConditionCheck(Material mat) return true; } - bool CheckTextureFormat(Material mat) + bool CheckIsSupportedFormat(Material mat) { - if (mat == null) - return false; - if (mat.mainTexture == null) - return false; - var textureFormat = ((Texture2D)mat.mainTexture).format; - if (textureFormat != TextureFormat.DXT1 && textureFormat != TextureFormat.DXT5 && - textureFormat != TextureFormat.RGB24 && textureFormat != TextureFormat.RGBA32) + if (!TextureEncryptManager.IsSupportedFormat(mat)) { - Debug.LogWarningFormat("{0} : is unsupported format", mat.mainTexture.name); + if (mat.mainTexture != null) + { + Debug.LogWarningFormat("{0} : is unsupported format", mat.mainTexture.name); + } return false; } return true; } + public void CreateFolders() { if (!AssetDatabase.IsValidFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString()))) @@ -371,7 +365,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) var materials = new List(); foreach (var mat in GetMaterials()) { - if (CheckTextureFormat(mat)) + if (CheckIsSupportedFormat(mat)) { materials.Add(mat); } @@ -472,7 +466,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) int size = Math.Max(mat.mainTexture.width, mat.mainTexture.height); if (!mips.ContainsKey(size)) { - var mip = encrypt.GenerateRefMipmap(size, size, bUseSmallMip); + var mip = TextureEncryptManager.GenerateRefMipmap(size, size, bUseSmallMip); if (mip == null) Debug.LogErrorFormat("{0} : Can't generate mip tex{1}.", mat.name, size); else @@ -494,7 +488,6 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) string encrypted_shader_path = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); #region Make encrypted textures - Texture2D[] encrypted_tex = new Texture2D[2] { null, null }; bool processed = processedTextures.ContainsKey(main_texture); ProcessedTexture processedTexture; if (processed) @@ -527,41 +520,33 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) } } + EncryptResult result; if (processed == false) { try { - encrypted_tex = encrypt.TextureEncrypt(main_texture, key_bytes, encryptor); + result = TextureEncryptManager.EncryptTexture(main_texture, key_bytes, encryptor); } catch(ArgumentException e) { Debug.LogErrorFormat("{0} : ArgumentException - {1}", main_texture.name, e.Message); continue; } - if (encrypted_tex == null) - { - Debug.LogErrorFormat("{0} : encrypt failed0.", main_texture.name); - continue; - } - if (encrypted_tex[0] == null) - { - Debug.LogErrorFormat("{0} : encrypt failed1.", main_texture.name); - continue; - } - AssetDatabase.CreateAsset(encrypted_tex[0], encrypt_tex_path); - Debug.Log(encrypted_tex[0].name + ": " + AssetDatabase.GetAssetPath(encrypted_tex[0])); - processedTexture.encrypted0 = encrypted_tex[0]; + + AssetDatabase.CreateAsset(result.Texture1, encrypt_tex_path); + Debug.Log(result.Texture1.name + ": " + AssetDatabase.GetAssetPath(result.Texture1)); + processedTexture.encrypted0 = result.Texture1; - if (encrypted_tex[1] != null) + if (result.Texture2 != null) { - AssetDatabase.CreateAsset(encrypted_tex[1], encrypt_tex2_path); - processedTexture.encrypted1 = encrypted_tex[1]; + AssetDatabase.CreateAsset(result.Texture2, encrypt_tex2_path); + processedTexture.encrypted1 = result.Texture2; } } else { - encrypted_tex[0] = processedTexture.encrypted0; - encrypted_tex[1] = processedTexture.encrypted1; + result.Texture1 = processedTexture.encrypted0; + result.Texture2 = processedTexture.encrypted1; } #endregion @@ -576,7 +561,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) mat, Path.Combine(asset_dir, "Shader/ShellProtector.cginc"), encrypted_shader_path, - encrypted_tex[0], + result.Texture1, otherTex.limTexture != null, otherTex.limTexture2 != null, otherTex.outlineTexture != null @@ -638,7 +623,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) } if (fallbackSize > 1) { - fallback = encrypt.GenerateFallback(main_texture, fallbackSize); + fallback = TextureEncryptManager.GenerateFallback(main_texture, fallbackSize); if (fallback != null) { processedTexture.fallbacks.Add(fallback); @@ -678,34 +663,23 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) var original_tex = new_mat.mainTexture; new_mat.mainTexture = fallback; - int max = Math.Max(encrypted_tex[0].width, encrypted_tex[0].height); + int max = Math.Max(result.Texture1.width, result.Texture1.height); var mip_tex = mips[max]; if(mip_tex == null) Debug.LogWarningFormat("mip_{0} is not exsist", max); new_mat.SetTexture("_MipTex", mip_tex); - if (encrypted_tex[0] != null) - new_mat.SetTexture("_EncryptTex0", encrypted_tex[0]); - if (encrypted_tex[1] != null) - new_mat.SetTexture("_EncryptTex1", encrypted_tex[1]); + if (result.Texture1 != null) + new_mat.SetTexture("_EncryptTex0", result.Texture1); + if (result.Texture2 != null) + new_mat.SetTexture("_EncryptTex1", result.Texture2); new_mat.renderQueue = mat.renderQueue; if (turnOnAllSafetyFallback) new_mat.SetOverrideTag("VRCFallback", "Unlit"); - int woffset = 0; - int hoffset = 0; - if (main_texture.format == TextureFormat.DXT1 || main_texture.format == TextureFormat.DXT5) - { - woffset = 13 - (int)Mathf.Log(main_texture.width, 2) - 1 + 2; - hoffset = 13 - (int)Mathf.Log(main_texture.height, 2) - 1 + 2; - } - else - { - woffset = 13 - (int)Mathf.Log(main_texture.width, 2) - 1; - hoffset = 13 - (int)Mathf.Log(main_texture.height, 2) - 1; - } + var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(main_texture); new_mat.SetInteger("_Woffset", woffset); new_mat.SetInteger("_Hoffset", hoffset); for (int i = 0; i < 16 - key_size; ++i) diff --git a/Runtime/Scripts/TextureEncryptManager.cs b/Runtime/Scripts/TextureEncryptManager.cs new file mode 100644 index 0000000..3d32147 --- /dev/null +++ b/Runtime/Scripts/TextureEncryptManager.cs @@ -0,0 +1,164 @@ +using UnityEngine; +using System.Collections.Generic; +using System.Linq; + +namespace Shell.Protector +{ + public class TextureEncryptManager + { + private static readonly Dictionary _formats = new Dictionary { + { TextureFormat.DXT1, new DXT1Format() }, + { TextureFormat.DXT1Crunched, new DXT1Format() }, + { TextureFormat.DXT5, new DXT5Format() }, + { TextureFormat.DXT5Crunched, new DXT5Format() }, + { TextureFormat.RGB24, new RGB24Format() }, + { TextureFormat.RGBA32, new RGBA32Format() } + }; + + public static bool HasAlpha(Texture2D texture) + { + Color32[] pixels = texture.GetPixels32(); + for (int i = 0; i < pixels.Length; i++) + { + if (pixels[i].a != 255) return true; + } + return false; + } + + public static Texture2D GenerateRefMipmap(int width, int height, bool small = false) + { + int mip_lv = GetCanMipmapLevel(width, height); + Debug.LogFormat("mip {0}, {1} : {2}", width, height, mip_lv); + + Texture2D mip = new Texture2D(width, (small == false) ? height : 1, TextureFormat.RGB24, mip_lv, true); + mip.filterMode = FilterMode.Bilinear; + mip.anisoLevel = (small == false) ? 1 : 0; + + for (int m = 0; m < mip.mipmapCount; ++m) + { + Color32[] pixels_mip = mip.GetPixels32(m); + for (int i = 0; i < pixels_mip.Length; ++i) + { + pixels_mip[i].r = (byte)(m * 10); + pixels_mip[i].g = 0; + pixels_mip[i].b = 0; + } + mip.SetPixels32(pixels_mip, m); + } + if (small == false) + mip.Compress(false); + return mip; + } + + public static Texture2D GenerateFallback(Texture2D original, int size = 32) + { + if (original.width < 128 || original.height < 128) + { + return null; + } + TextureFormat format = TextureFormat.RGB24; + bool hasAlpha = HasAlpha(original); + if (hasAlpha) + format = TextureFormat.RGBA32; + + RenderTexture renderTexture = new RenderTexture(size, size, 0); + RenderTexture.active = renderTexture; + + Graphics.Blit(original, renderTexture); + + Texture2D resizedTexture = new Texture2D(size, size, format, true); + resizedTexture.ReadPixels(new Rect(0, 0, size, size), 0, 0); + resizedTexture.Apply(); + + resizedTexture.filterMode = FilterMode.Point; + resizedTexture.anisoLevel = 0; + resizedTexture.Compress(false); + if (hasAlpha) + resizedTexture.alphaIsTransparency = true; + + RenderTexture.active = null; + + return resizedTexture; + } + + private static int GetCanMipmapLevel(int w, int h) + { + if (w < 1 || h <= 1) return 0; + int w_level = (int)Mathf.Log(w, 2); + int h_level = (int)Mathf.Log(h, 2); + return Mathf.Max(w_level, h_level); + } + + private static ITextureFormat GetFormat(Texture texture) + { + if (texture == null) + { + return null; + } + + if (texture is not Texture2D texture2D) + { + return null; + } + + return _formats.FirstOrDefault(f => f.Value.CanHandle(texture2D.format)).Value; + } + + private static ITextureFormat GetFormat(Material material) + { + if (material == null || material.mainTexture == null) + { + return null; + } + + return GetFormat(material.mainTexture as Texture2D); + } + + public static EncryptResult EncryptTexture(Texture2D texture, byte[] key, IEncryptor encryptor) + { + if (texture.width % 2 != 0 && texture.height % 2 != 0) + { + Debug.LogErrorFormat("{0} : The texture size must be a multiple of 2!", texture.name); + return new EncryptResult(); + } + + var format = GetFormat(texture); + if (format == null) + { + Debug.LogErrorFormat("{0} is not supported texture format! supported type:DXT1, DXT5, RGB, RGBA", texture.name); + return new EncryptResult(); + } + + return format.Encrypt(texture, key, encryptor); + } + + public static bool IsSupportedFormat(Material material) + { + return GetFormat(material) != null; + } + + public static bool IsSupportedTexture(Texture texture) + { + return GetFormat(texture) != null; + } + + public static bool IsDXTFormat(TextureFormat format) + { + return format == TextureFormat.DXT1 || format == TextureFormat.DXT5; + } + + public static void SetFormatKeywords(Material material) + { + var format = GetFormat(material); + if (format == null) return; + format.SetFormatKeywords(material); + } + + public static (int, int) CalculateOffsets(Texture2D texture) + { + var format = GetFormat(texture); + if (format == null) return (0, 0); + return format.CalculateOffsets(texture); + } + } +} \ No newline at end of file diff --git a/Runtime/Scripts/TextureEncryptManager.cs.meta b/Runtime/Scripts/TextureEncryptManager.cs.meta new file mode 100644 index 0000000..a8291da --- /dev/null +++ b/Runtime/Scripts/TextureEncryptManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 05268f803de0f0c47a4f086f06cdb2e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Shader/BC7.cginc b/Runtime/Shader/BC7.cginc new file mode 100644 index 0000000..633d1c9 --- /dev/null +++ b/Runtime/Shader/BC7.cginc @@ -0,0 +1,70 @@ +#pragma once + +#define _SHELL_PROTECTOR_DATA_LENGTH 2 +#define _SHELL_PROTECTOR_INDEX_ALIGNMENT 1 + +int GetIndex(half2 uv, int m) { + half x = frac(uv.x); + half y = frac(uv.y); + + uint w = mw[m + (uint)_Woffset]; + uint h = mh[m + _Hoffset]; + + return (w * floor(y * h)) + floor(x * w); +} + +void GetData(inout uint data[2], half2 uv, int m) { + int idx = GetIndex(uv, m); + int offset = (idx & 1) == 0 ? 0 : -1; + + half4 pixels[2]; + pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); + + data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); + data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); +} + +// BC7 블록 모드별 색상 보간 함수 +half3 InterpolateBC7Colors(half3 endpoint0, half3 endpoint1, uint weight, uint totalWeights) { + if (totalWeights == 4) { + switch(weight) { + case 0: return endpoint0; + case 1: return lerp(endpoint0, endpoint1, 0.333333); + case 2: return lerp(endpoint0, endpoint1, 0.666667); + case 3: return endpoint1; + } + } + else if (totalWeights == 8) { + return lerp(endpoint0, endpoint1, weight / 7.0); + } + return endpoint0; +} + +half4 GetPixel(inout uint data[2], half2 uv, int m) { + int idx = GetIndex(uv, m); + half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); + + // BC7 데이터 디코딩 + uint endpoint0 = data[0]; + uint endpoint1 = data[1]; + + half3 color0, color1; + color0.r = (endpoint0 & 0xFF) / 255.0; + color0.g = ((endpoint0 >> 8) & 0xFF) / 255.0; + color0.b = ((endpoint0 >> 16) & 0xFF) / 255.0; + + color1.r = (endpoint1 & 0xFF) / 255.0; + color1.g = ((endpoint1 >> 8) & 0xFF) / 255.0; + color1.b = ((endpoint1 >> 16) & 0xFF) / 255.0; + + half alpha0 = ((endpoint0 >> 24) & 0xFF) / 255.0; + half alpha1 = ((endpoint1 >> 24) & 0xFF) / 255.0; + + // BC7 보간 가중치 계산 + half weight = col.r; + half3 finalColor = lerp(color0, color1, weight); + half finalAlpha = lerp(alpha0, alpha1, col.a); + + return half4(GammaCorrection(finalColor), finalAlpha); +} \ No newline at end of file diff --git a/Runtime/Shader/BC7.cginc.meta b/Runtime/Shader/BC7.cginc.meta new file mode 100644 index 0000000..55c9b06 --- /dev/null +++ b/Runtime/Shader/BC7.cginc.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2b26cd4d52f556e4aaab45ff7cf5da8a +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Shader/ShellProtector.cginc b/Runtime/Shader/ShellProtector.cginc index 14758d8..50fa03b 100644 --- a/Runtime/Shader/ShellProtector.cginc +++ b/Runtime/Shader/ShellProtector.cginc @@ -13,6 +13,8 @@ #define _SHELL_PROTECTOR_RGBA #elif _SHELL_PROTECTOR_FORMAT0 && !_SHELL_PROTECTOR_FORMAT1 #define _SHELL_PROTECTOR_RGB +#elif _SHELL_PROTECTOR_FORMAT0 && _SHELL_PROTECTOR_FORMAT1 + #define _SHELL_PROTECTOR_BC7 #else #error "Unsupported format" #endif @@ -48,6 +50,10 @@ uint _Hoffset; #include "DXT.cginc" #endif +#ifdef _SHELL_PROTECTOR_BC7 + #include "BC7.cginc" +#endif + half4 DecryptTexture(half2 uv, int m) { int idx = GetIndex(uv, m); From 194b1ee6bd0da337ad0a53513868b5e56c992f88 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Tue, 25 Mar 2025 12:32:58 +0900 Subject: [PATCH 14/59] fix: change log error to throw error in DXTFormat --- Runtime/Scripts/Formats/DXTFormat.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Runtime/Scripts/Formats/DXTFormat.cs b/Runtime/Scripts/Formats/DXTFormat.cs index 076368e..ad7aae3 100644 --- a/Runtime/Scripts/Formats/DXTFormat.cs +++ b/Runtime/Scripts/Formats/DXTFormat.cs @@ -57,12 +57,11 @@ public override bool CanHandle(TextureFormat format) { public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { if (texture.width < 8) { - Debug.LogErrorFormat("{0} : The texture width must be >= 8px", texture.name); - return new EncryptResult(); + throw new Exception($"{texture.name} : The texture width must be >= 8px"); } + if (texture.height < 4) { - Debug.LogErrorFormat("{0} : The texture height must be >= 4px", texture.name); - return new EncryptResult(); + throw new Exception($"{texture.name} : The texture height must be >= 4px"); } int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); @@ -132,12 +131,11 @@ public override bool CanHandle(TextureFormat format) { public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { if (texture.width < 8) { - Debug.LogErrorFormat("{0} : The texture width must be >= 8px", texture.name); - return new EncryptResult(); + throw new Exception($"{texture.name} : The texture width must be >= 8px"); } + if (texture.height < 4) { - Debug.LogErrorFormat("{0} : The texture height must be >= 4px", texture.name); - return new EncryptResult(); + throw new Exception($"{texture.name} : The texture height must be >= 4px"); } int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); From 34d1fa2c366a6cc383f70d95f3cc63d2c9447a79 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Tue, 25 Mar 2025 12:57:37 +0900 Subject: [PATCH 15/59] fix: remove bc7.cginc --- Runtime/Shader/BC7.cginc | 70 ----------------------------------- Runtime/Shader/BC7.cginc.meta | 7 ---- 2 files changed, 77 deletions(-) delete mode 100644 Runtime/Shader/BC7.cginc delete mode 100644 Runtime/Shader/BC7.cginc.meta diff --git a/Runtime/Shader/BC7.cginc b/Runtime/Shader/BC7.cginc deleted file mode 100644 index 633d1c9..0000000 --- a/Runtime/Shader/BC7.cginc +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#define _SHELL_PROTECTOR_DATA_LENGTH 2 -#define _SHELL_PROTECTOR_INDEX_ALIGNMENT 1 - -int GetIndex(half2 uv, int m) { - half x = frac(uv.x); - half y = frac(uv.y); - - uint w = mw[m + (uint)_Woffset]; - uint h = mh[m + _Hoffset]; - - return (w * floor(y * h)) + floor(x * w); -} - -void GetData(inout uint data[2], half2 uv, int m) { - int idx = GetIndex(uv, m); - int offset = (idx & 1) == 0 ? 0 : -1; - - half4 pixels[2]; - pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); -} - -// BC7 블록 모드별 색상 보간 함수 -half3 InterpolateBC7Colors(half3 endpoint0, half3 endpoint1, uint weight, uint totalWeights) { - if (totalWeights == 4) { - switch(weight) { - case 0: return endpoint0; - case 1: return lerp(endpoint0, endpoint1, 0.333333); - case 2: return lerp(endpoint0, endpoint1, 0.666667); - case 3: return endpoint1; - } - } - else if (totalWeights == 8) { - return lerp(endpoint0, endpoint1, weight / 7.0); - } - return endpoint0; -} - -half4 GetPixel(inout uint data[2], half2 uv, int m) { - int idx = GetIndex(uv, m); - half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); - - // BC7 데이터 디코딩 - uint endpoint0 = data[0]; - uint endpoint1 = data[1]; - - half3 color0, color1; - color0.r = (endpoint0 & 0xFF) / 255.0; - color0.g = ((endpoint0 >> 8) & 0xFF) / 255.0; - color0.b = ((endpoint0 >> 16) & 0xFF) / 255.0; - - color1.r = (endpoint1 & 0xFF) / 255.0; - color1.g = ((endpoint1 >> 8) & 0xFF) / 255.0; - color1.b = ((endpoint1 >> 16) & 0xFF) / 255.0; - - half alpha0 = ((endpoint0 >> 24) & 0xFF) / 255.0; - half alpha1 = ((endpoint1 >> 24) & 0xFF) / 255.0; - - // BC7 보간 가중치 계산 - half weight = col.r; - half3 finalColor = lerp(color0, color1, weight); - half finalAlpha = lerp(alpha0, alpha1, col.a); - - return half4(GammaCorrection(finalColor), finalAlpha); -} \ No newline at end of file diff --git a/Runtime/Shader/BC7.cginc.meta b/Runtime/Shader/BC7.cginc.meta deleted file mode 100644 index 55c9b06..0000000 --- a/Runtime/Shader/BC7.cginc.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 2b26cd4d52f556e4aaab45ff7cf5da8a -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From 22b0cd5fefc58e19a3088fad104746ae6c0b3dc8 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Tue, 25 Mar 2025 12:57:37 +0900 Subject: [PATCH 16/59] fix: remove bc7.cginc --- Runtime/Shader/BC7.cginc | 70 ----------------------------- Runtime/Shader/BC7.cginc.meta | 7 --- Runtime/Shader/ShellProtector.cginc | 6 --- 3 files changed, 83 deletions(-) delete mode 100644 Runtime/Shader/BC7.cginc delete mode 100644 Runtime/Shader/BC7.cginc.meta diff --git a/Runtime/Shader/BC7.cginc b/Runtime/Shader/BC7.cginc deleted file mode 100644 index 633d1c9..0000000 --- a/Runtime/Shader/BC7.cginc +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#define _SHELL_PROTECTOR_DATA_LENGTH 2 -#define _SHELL_PROTECTOR_INDEX_ALIGNMENT 1 - -int GetIndex(half2 uv, int m) { - half x = frac(uv.x); - half y = frac(uv.y); - - uint w = mw[m + (uint)_Woffset]; - uint h = mh[m + _Hoffset]; - - return (w * floor(y * h)) + floor(x * w); -} - -void GetData(inout uint data[2], half2 uv, int m) { - int idx = GetIndex(uv, m); - int offset = (idx & 1) == 0 ? 0 : -1; - - half4 pixels[2]; - pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); -} - -// BC7 블록 모드별 색상 보간 함수 -half3 InterpolateBC7Colors(half3 endpoint0, half3 endpoint1, uint weight, uint totalWeights) { - if (totalWeights == 4) { - switch(weight) { - case 0: return endpoint0; - case 1: return lerp(endpoint0, endpoint1, 0.333333); - case 2: return lerp(endpoint0, endpoint1, 0.666667); - case 3: return endpoint1; - } - } - else if (totalWeights == 8) { - return lerp(endpoint0, endpoint1, weight / 7.0); - } - return endpoint0; -} - -half4 GetPixel(inout uint data[2], half2 uv, int m) { - int idx = GetIndex(uv, m); - half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); - - // BC7 데이터 디코딩 - uint endpoint0 = data[0]; - uint endpoint1 = data[1]; - - half3 color0, color1; - color0.r = (endpoint0 & 0xFF) / 255.0; - color0.g = ((endpoint0 >> 8) & 0xFF) / 255.0; - color0.b = ((endpoint0 >> 16) & 0xFF) / 255.0; - - color1.r = (endpoint1 & 0xFF) / 255.0; - color1.g = ((endpoint1 >> 8) & 0xFF) / 255.0; - color1.b = ((endpoint1 >> 16) & 0xFF) / 255.0; - - half alpha0 = ((endpoint0 >> 24) & 0xFF) / 255.0; - half alpha1 = ((endpoint1 >> 24) & 0xFF) / 255.0; - - // BC7 보간 가중치 계산 - half weight = col.r; - half3 finalColor = lerp(color0, color1, weight); - half finalAlpha = lerp(alpha0, alpha1, col.a); - - return half4(GammaCorrection(finalColor), finalAlpha); -} \ No newline at end of file diff --git a/Runtime/Shader/BC7.cginc.meta b/Runtime/Shader/BC7.cginc.meta deleted file mode 100644 index 55c9b06..0000000 --- a/Runtime/Shader/BC7.cginc.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 2b26cd4d52f556e4aaab45ff7cf5da8a -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/Shader/ShellProtector.cginc b/Runtime/Shader/ShellProtector.cginc index 50fa03b..14758d8 100644 --- a/Runtime/Shader/ShellProtector.cginc +++ b/Runtime/Shader/ShellProtector.cginc @@ -13,8 +13,6 @@ #define _SHELL_PROTECTOR_RGBA #elif _SHELL_PROTECTOR_FORMAT0 && !_SHELL_PROTECTOR_FORMAT1 #define _SHELL_PROTECTOR_RGB -#elif _SHELL_PROTECTOR_FORMAT0 && _SHELL_PROTECTOR_FORMAT1 - #define _SHELL_PROTECTOR_BC7 #else #error "Unsupported format" #endif @@ -50,10 +48,6 @@ uint _Hoffset; #include "DXT.cginc" #endif -#ifdef _SHELL_PROTECTOR_BC7 - #include "BC7.cginc" -#endif - half4 DecryptTexture(half2 uv, int m) { int idx = GetIndex(uv, m); From 018d6d8cec0f6fce19635a07da73f85fefa6e7c3 Mon Sep 17 00:00:00 2001 From: cstria0106 Date: Wed, 26 Mar 2025 23:36:34 +0900 Subject: [PATCH 17/59] fix: fix upload error --- Runtime/Scripts/Formats/DXTFormat.cs | 350 ++++++++++++----------- Runtime/Scripts/Formats/RGBFormat.cs | 198 ++++++------- Runtime/Scripts/TextureEncryptManager.cs | 6 +- 3 files changed, 285 insertions(+), 269 deletions(-) diff --git a/Runtime/Scripts/Formats/DXTFormat.cs b/Runtime/Scripts/Formats/DXTFormat.cs index ad7aae3..f6c8da9 100644 --- a/Runtime/Scripts/Formats/DXTFormat.cs +++ b/Runtime/Scripts/Formats/DXTFormat.cs @@ -1,200 +1,206 @@ using UnityEngine; -using Shell.Protector; using System; -public abstract class DXTFormat : BaseTextureFormat { - protected byte[] GetArrayDXT(byte[] data, int texture_width, int texture_height, bool dxt5, int miplv) { - int start = 0; - int end = 0; - - for (int i = 0; i <= miplv; ++i) { - start += end; - int w = texture_width / (int)(Mathf.Pow(2, i)); - int h = texture_height / (int)(Mathf.Pow(2, i)); - int block_count = (w / 4) * (h / 4); - int len = block_count * 8; - if (dxt5) len *= 2; - end = len; - } - - var segment = new ArraySegment(data, start, end); - return segment.ToArray(); - } +#if UNITY_EDITOR + +namespace Shell.Protector +{ + public abstract class DXTFormat : BaseTextureFormat { + protected byte[] GetArrayDXT(byte[] data, int texture_width, int texture_height, bool dxt5, int miplv) { + int start = 0; + int end = 0; + + for (int i = 0; i <= miplv; ++i) { + start += end; + int w = texture_width / (int)(Mathf.Pow(2, i)); + int h = texture_height / (int)(Mathf.Pow(2, i)); + int block_count = (w / 4) * (h / 4); + int len = block_count * 8; + if (dxt5) len *= 2; + end = len; + } - protected Texture2D HandleCrunchedFormat(Texture2D texture, int mip_lv, bool isDXT5) { - if (!texture.name.Contains("Crunched")) return texture; - - Debug.LogWarningFormat("{0} is the crunch compression format. There may be degradation in image quality.", texture.name); - var format = isDXT5 ? TextureFormat.RGBA32 : TextureFormat.RGB24; - var result = new Texture2D(texture.width, texture.height, format, mip_lv, true); - - for (int m = 0; m <= mip_lv; ++m) { - if (m != 0 && m == mip_lv) break; - result.SetPixels32(texture.GetPixels32(m), m); - result.Apply(); + var segment = new ArraySegment(data, start, end); + return segment.ToArray(); } - - result.Compress(isDXT5); - return result; - } - public override void SetFormatKeywords(Material material) { - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); - } - - public override (int, int) CalculateOffsets(Texture2D texture) { - int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1 + 2; - int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1 + 2; - return (woffset, hoffset); - } -} - -public class DXT1Format : DXTFormat { - public override bool CanHandle(TextureFormat format) { - return format == TextureFormat.DXT1 || format == TextureFormat.DXT1Crunched; - } - - public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { - if (texture.width < 8) { - throw new Exception($"{texture.name} : The texture width must be >= 8px"); + protected Texture2D HandleCrunchedFormat(Texture2D texture, int mip_lv, bool isDXT5) { + if (!texture.name.Contains("Crunched")) return texture; + + Debug.LogWarningFormat("{0} is the crunch compression format. There may be degradation in image quality.", texture.name); + var format = isDXT5 ? TextureFormat.RGBA32 : TextureFormat.RGB24; + var result = new Texture2D(texture.width, texture.height, format, mip_lv, true); + + for (int m = 0; m <= mip_lv; ++m) { + if (m != 0 && m == mip_lv) break; + result.SetPixels32(texture.GetPixels32(m), m); + result.Apply(); + } + + result.Compress(isDXT5); + return result; } - if (texture.height < 4) { - throw new Exception($"{texture.name} : The texture height must be >= 4px"); + public override void SetFormatKeywords(Material material) { + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); } - int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); - Texture2D dxt1 = HandleCrunchedFormat(texture, mip_lv, false); - - var result = new EncryptResult(); - if (mip_lv != 0) { - result.Texture1 = new Texture2D(dxt1.width, dxt1.height, TextureFormat.DXT1, mip_lv, true); - result.Texture2 = new Texture2D(dxt1.width / 4, dxt1.height / 4, TextureFormat.RGBA32, mip_lv, true); - } else { - result.Texture1 = new Texture2D(dxt1.width, dxt1.height, TextureFormat.DXT1, false, true); - result.Texture2 = new Texture2D(dxt1.width / 4, dxt1.height / 4, TextureFormat.RGBA32, false, true); + public override (int, int) CalculateOffsets(Texture2D texture) { + int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1 + 2; + int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1 + 2; + return (woffset, hoffset); } - result.Texture2.filterMode = FilterMode.Point; - result.Texture2.anisoLevel = 0; - - var raw_data = dxt1.GetRawTextureData(); - int lenidx = 0; - var key_uint = ConvertKeyToUInt(key); - - for (int m = 0; m <= mip_lv; ++m) { - if (m != 0 && m == mip_lv) break; - var tex_data = GetArrayDXT(raw_data, dxt1.width, dxt1.height, false, m); - var pixel = result.Texture2.GetPixels32(m); - - for (int i = 0; i < tex_data.Length; i += 16) { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)(i / 8); + } - uint[] data = new uint[2]; - data[0] = (uint)(tex_data[i + 0] + (tex_data[i + 1] << 8) + (tex_data[i + 2] << 16) + (tex_data[i + 3] << 24)); - data[1] = (uint)(tex_data[(i + 8) + 0] + (tex_data[(i + 8) + 1] << 8) + (tex_data[(i + 8) + 2] << 16) + (tex_data[(i + 8) + 3] << 24)); + public class DXT1Format : DXTFormat { + public override bool CanHandle(TextureFormat format) { + return format == TextureFormat.DXT1 || format == TextureFormat.DXT1Crunched; + } - uint[] data_enc = algorithm.Encrypt(data, key_uint); + public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { + if (texture.width < 8) { + throw new Exception($"{texture.name} : The texture width must be >= 8px"); + } - for (int j = 0; j < 2; ++j) { - pixel[i / 8 + j].r = (byte)((data_enc[j] & 0x000000FF) >> 0); - pixel[i / 8 + j].g = (byte)((data_enc[j] & 0x0000FF00) >> 8); - pixel[i / 8 + j].b = (byte)((data_enc[j] & 0x00FF0000) >> 16); - pixel[i / 8 + j].a = (byte)((data_enc[j] & 0xFF000000) >> 24); - } + if (texture.height < 4) { + throw new Exception($"{texture.name} : The texture height must be >= 4px"); } - for (int i = 0; i < tex_data.Length; i += 8) { - tex_data[i + 0] = 255; - tex_data[i + 1] = 255; - tex_data[i + 2] = 0; - tex_data[i + 3] = 0; + + int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); + Texture2D dxt1 = HandleCrunchedFormat(texture, mip_lv, false); + + var result = new EncryptResult(); + if (mip_lv != 0) { + result.Texture1 = new Texture2D(dxt1.width, dxt1.height, TextureFormat.DXT1, mip_lv, true); + result.Texture2 = new Texture2D(dxt1.width / 4, dxt1.height / 4, TextureFormat.RGBA32, mip_lv, true); + } else { + result.Texture1 = new Texture2D(dxt1.width, dxt1.height, TextureFormat.DXT1, false, true); + result.Texture2 = new Texture2D(dxt1.width / 4, dxt1.height / 4, TextureFormat.RGBA32, false, true); } - for (int i = 0; i < tex_data.Length; ++i) { - raw_data[i + lenidx] = tex_data[i]; + result.Texture2.filterMode = FilterMode.Point; + result.Texture2.anisoLevel = 0; + + var raw_data = dxt1.GetRawTextureData(); + int lenidx = 0; + var key_uint = ConvertKeyToUInt(key); + + for (int m = 0; m <= mip_lv; ++m) { + if (m != 0 && m == mip_lv) break; + var tex_data = GetArrayDXT(raw_data, dxt1.width, dxt1.height, false, m); + var pixel = result.Texture2.GetPixels32(m); + + for (int i = 0; i < tex_data.Length; i += 16) { + key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); + key_uint[3] ^= (uint)(i / 8); + + uint[] data = new uint[2]; + data[0] = (uint)(tex_data[i + 0] + (tex_data[i + 1] << 8) + (tex_data[i + 2] << 16) + (tex_data[i + 3] << 24)); + data[1] = (uint)(tex_data[(i + 8) + 0] + (tex_data[(i + 8) + 1] << 8) + (tex_data[(i + 8) + 2] << 16) + (tex_data[(i + 8) + 3] << 24)); + + uint[] data_enc = algorithm.Encrypt(data, key_uint); + + for (int j = 0; j < 2; ++j) { + pixel[i / 8 + j].r = (byte)((data_enc[j] & 0x000000FF) >> 0); + pixel[i / 8 + j].g = (byte)((data_enc[j] & 0x0000FF00) >> 8); + pixel[i / 8 + j].b = (byte)((data_enc[j] & 0x00FF0000) >> 16); + pixel[i / 8 + j].a = (byte)((data_enc[j] & 0xFF000000) >> 24); + } + } + for (int i = 0; i < tex_data.Length; i += 8) { + tex_data[i + 0] = 255; + tex_data[i + 1] = 255; + tex_data[i + 2] = 0; + tex_data[i + 3] = 0; + } + for (int i = 0; i < tex_data.Length; ++i) { + raw_data[i + lenidx] = tex_data[i]; + } + lenidx += tex_data.Length; + result.Texture2.SetPixels32(pixel, m); } - lenidx += tex_data.Length; - result.Texture2.SetPixels32(pixel, m); - } - result.Texture1.LoadRawTextureData(raw_data); - result.Texture1.filterMode = FilterMode.Point; - result.Texture1.anisoLevel = 0; - - return result; - } -} + result.Texture1.LoadRawTextureData(raw_data); + result.Texture1.filterMode = FilterMode.Point; + result.Texture1.anisoLevel = 0; -public class DXT5Format : DXTFormat { - public override bool CanHandle(TextureFormat format) { - return format == TextureFormat.DXT5 || format == TextureFormat.DXT5Crunched; + return result; + } } - public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { - if (texture.width < 8) { - throw new Exception($"{texture.name} : The texture width must be >= 8px"); + public class DXT5Format : DXTFormat { + public override bool CanHandle(TextureFormat format) { + return format == TextureFormat.DXT5 || format == TextureFormat.DXT5Crunched; } - if (texture.height < 4) { - throw new Exception($"{texture.name} : The texture height must be >= 4px"); - } + public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { + if (texture.width < 8) { + throw new Exception($"{texture.name} : The texture width must be >= 8px"); + } - int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); - Texture2D dxt5 = HandleCrunchedFormat(texture, mip_lv, true); - - var result = new EncryptResult(); - if (mip_lv != 0) { - result.Texture1 = new Texture2D(texture.width, texture.height, TextureFormat.DXT5, mip_lv, true); - result.Texture2 = new Texture2D(texture.width / 4, texture.height / 4, TextureFormat.RGBA32, mip_lv, true); - } else { - result.Texture1 = new Texture2D(texture.width, texture.height, TextureFormat.DXT5, false, true); - result.Texture2 = new Texture2D(texture.width / 4, texture.height / 4, TextureFormat.RGBA32, false, true); - } - result.Texture1.alphaIsTransparency = true; - result.Texture2.filterMode = FilterMode.Point; - result.Texture2.anisoLevel = 0; - - var raw_data = dxt5.GetRawTextureData(); - int lenidx = 0; - var key_uint = ConvertKeyToUInt(key); - - for (int m = 0; m <= mip_lv; ++m) { - if (m != 0 && m == mip_lv) break; - var tex_data = GetArrayDXT(raw_data, texture.width, texture.height, true, m); - var pixel = result.Texture2.GetPixels32(m); - - for (int i = 0; i < tex_data.Length; i += 32) { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)(i / 16); - - uint[] data = new uint[2]; - data[0] = (uint)(tex_data[i + 8] + (tex_data[i + 9] << 8) + (tex_data[i + 10] << 16) + (tex_data[i + 11] << 24)); - data[1] = (uint)(tex_data[i + 16 + 8] + (tex_data[i + 16 + 9] << 8) + (tex_data[i + 16 + 10] << 16) + (tex_data[i + 16 + 11] << 24)); - - uint[] data_enc = algorithm.Encrypt(data, key_uint); - - for (int j = 0; j < 2; ++j) { - pixel[i / 16 + j].r = (byte)((data_enc[j] & 0x000000FF) >> 0); - pixel[i / 16 + j].g = (byte)((data_enc[j] & 0x0000FF00) >> 8); - pixel[i / 16 + j].b = (byte)((data_enc[j] & 0x00FF0000) >> 16); - pixel[i / 16 + j].a = (byte)((data_enc[j] & 0xFF000000) >> 24); - } + if (texture.height < 4) { + throw new Exception($"{texture.name} : The texture height must be >= 4px"); } - for (int i = 0; i < tex_data.Length; i += 16) { - tex_data[i + 8] = 255; - tex_data[i + 9] = 255; - tex_data[i + 10] = 0; - tex_data[i + 11] = 0; + + int mip_lv = GetCanMipmapLevel(texture.width / 4, texture.height / 4); + Texture2D dxt5 = HandleCrunchedFormat(texture, mip_lv, true); + + var result = new EncryptResult(); + if (mip_lv != 0) { + result.Texture1 = new Texture2D(texture.width, texture.height, TextureFormat.DXT5, mip_lv, true); + result.Texture2 = new Texture2D(texture.width / 4, texture.height / 4, TextureFormat.RGBA32, mip_lv, true); + } else { + result.Texture1 = new Texture2D(texture.width, texture.height, TextureFormat.DXT5, false, true); + result.Texture2 = new Texture2D(texture.width / 4, texture.height / 4, TextureFormat.RGBA32, false, true); } - for (int i = 0; i < tex_data.Length; ++i) { - raw_data[i + lenidx] = tex_data[i]; + result.Texture1.alphaIsTransparency = true; + result.Texture2.filterMode = FilterMode.Point; + result.Texture2.anisoLevel = 0; + + var raw_data = dxt5.GetRawTextureData(); + int lenidx = 0; + var key_uint = ConvertKeyToUInt(key); + + for (int m = 0; m <= mip_lv; ++m) { + if (m != 0 && m == mip_lv) break; + var tex_data = GetArrayDXT(raw_data, texture.width, texture.height, true, m); + var pixel = result.Texture2.GetPixels32(m); + + for (int i = 0; i < tex_data.Length; i += 32) { + key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); + key_uint[3] ^= (uint)(i / 16); + + uint[] data = new uint[2]; + data[0] = (uint)(tex_data[i + 8] + (tex_data[i + 9] << 8) + (tex_data[i + 10] << 16) + (tex_data[i + 11] << 24)); + data[1] = (uint)(tex_data[i + 16 + 8] + (tex_data[i + 16 + 9] << 8) + (tex_data[i + 16 + 10] << 16) + (tex_data[i + 16 + 11] << 24)); + + uint[] data_enc = algorithm.Encrypt(data, key_uint); + + for (int j = 0; j < 2; ++j) { + pixel[i / 16 + j].r = (byte)((data_enc[j] & 0x000000FF) >> 0); + pixel[i / 16 + j].g = (byte)((data_enc[j] & 0x0000FF00) >> 8); + pixel[i / 16 + j].b = (byte)((data_enc[j] & 0x00FF0000) >> 16); + pixel[i / 16 + j].a = (byte)((data_enc[j] & 0xFF000000) >> 24); + } + } + for (int i = 0; i < tex_data.Length; i += 16) { + tex_data[i + 8] = 255; + tex_data[i + 9] = 255; + tex_data[i + 10] = 0; + tex_data[i + 11] = 0; + } + for (int i = 0; i < tex_data.Length; ++i) { + raw_data[i + lenidx] = tex_data[i]; + } + lenidx += tex_data.Length; + result.Texture2.SetPixels32(pixel, m); } - lenidx += tex_data.Length; - result.Texture2.SetPixels32(pixel, m); - } - result.Texture1.LoadRawTextureData(raw_data); - result.Texture1.filterMode = FilterMode.Point; - result.Texture1.anisoLevel = 0; + result.Texture1.LoadRawTextureData(raw_data); + result.Texture1.filterMode = FilterMode.Point; + result.Texture1.anisoLevel = 0; - return result; + return result; + } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/Runtime/Scripts/Formats/RGBFormat.cs b/Runtime/Scripts/Formats/RGBFormat.cs index 094a648..4a0b3d4 100644 --- a/Runtime/Scripts/Formats/RGBFormat.cs +++ b/Runtime/Scripts/Formats/RGBFormat.cs @@ -1,119 +1,125 @@ using UnityEngine; -using Shell.Protector; - -public abstract class RGBFormat : BaseTextureFormat { - protected Texture2D CreateResultTexture(Texture2D texture, TextureFormat format) { - int mip_lv = GetCanMipmapLevel(texture.width, texture.height); - var result = new Texture2D(texture.width, texture.height, format, mip_lv - 2, true); - result.filterMode = FilterMode.Point; - result.anisoLevel = 0; - return result; - } -} -public class RGB24Format : RGBFormat { - public override bool CanHandle(TextureFormat format) { - return format == TextureFormat.RGB24; +#if UNITY_EDITOR + +namespace Shell.Protector +{ + public abstract class RGBFormat : BaseTextureFormat { + protected Texture2D CreateResultTexture(Texture2D texture, TextureFormat format) { + int mip_lv = GetCanMipmapLevel(texture.width, texture.height); + var result = new Texture2D(texture.width, texture.height, format, mip_lv - 2, true); + result.filterMode = FilterMode.Point; + result.anisoLevel = 0; + return result; + } } - public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { - var result = new EncryptResult(); - result.Texture1 = CreateResultTexture(texture, TextureFormat.RGB24); - - var key_uint = ConvertKeyToUInt(key); - - for (int m = 0; m < result.Texture1.mipmapCount; ++m) { - Color32[] pixels = texture.GetPixels32(m); - - for (int i = 0; i < pixels.Length; i += 4) { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)i; - - uint[] data = new uint[3]; - data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 1].r << 24)); - data[1] = (uint)(pixels[i + 1].g + (pixels[i + 1].b << 8) + (pixels[i + 2].r << 16) + (pixels[i + 2].g << 24)); - data[2] = (uint)(pixels[i + 2].b + (pixels[i + 3].r << 8) + (pixels[i + 3].g << 16) + (pixels[i + 3].b << 24)); - - uint[] data_enc = algorithm.Encrypt(data, key_uint); - - pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); - pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); - pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); - pixels[i + 1].r = (byte)((data_enc[0] & 0xFF000000) >> 24); - pixels[i + 1].g = (byte)((data_enc[1] & 0x000000FF) >> 0); - pixels[i + 1].b = (byte)((data_enc[1] & 0x0000FF00) >> 8); - pixels[i + 2].r = (byte)((data_enc[1] & 0x00FF0000) >> 16); - pixels[i + 2].g = (byte)((data_enc[1] & 0xFF000000) >> 24); - pixels[i + 2].b = (byte)((data_enc[2] & 0x000000FF) >> 0); - pixels[i + 3].r = (byte)((data_enc[2] & 0x0000FF00) >> 8); - pixels[i + 3].g = (byte)((data_enc[2] & 0x00FF0000) >> 16); - pixels[i + 3].b = (byte)((data_enc[2] & 0xFF000000) >> 24); - } - result.Texture1.SetPixels32(pixels, m); + public class RGB24Format : RGBFormat { + public override bool CanHandle(TextureFormat format) { + return format == TextureFormat.RGB24; } - return result; - } + public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { + var result = new EncryptResult(); + result.Texture1 = CreateResultTexture(texture, TextureFormat.RGB24); + + var key_uint = ConvertKeyToUInt(key); + + for (int m = 0; m < result.Texture1.mipmapCount; ++m) { + Color32[] pixels = texture.GetPixels32(m); + + for (int i = 0; i < pixels.Length; i += 4) { + key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); + key_uint[3] ^= (uint)i; + + uint[] data = new uint[3]; + data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 1].r << 24)); + data[1] = (uint)(pixels[i + 1].g + (pixels[i + 1].b << 8) + (pixels[i + 2].r << 16) + (pixels[i + 2].g << 24)); + data[2] = (uint)(pixels[i + 2].b + (pixels[i + 3].r << 8) + (pixels[i + 3].g << 16) + (pixels[i + 3].b << 24)); + + uint[] data_enc = algorithm.Encrypt(data, key_uint); + + pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); + pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); + pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); + pixels[i + 1].r = (byte)((data_enc[0] & 0xFF000000) >> 24); + pixels[i + 1].g = (byte)((data_enc[1] & 0x000000FF) >> 0); + pixels[i + 1].b = (byte)((data_enc[1] & 0x0000FF00) >> 8); + pixels[i + 2].r = (byte)((data_enc[1] & 0x00FF0000) >> 16); + pixels[i + 2].g = (byte)((data_enc[1] & 0xFF000000) >> 24); + pixels[i + 2].b = (byte)((data_enc[2] & 0x000000FF) >> 0); + pixels[i + 3].r = (byte)((data_enc[2] & 0x0000FF00) >> 8); + pixels[i + 3].g = (byte)((data_enc[2] & 0x00FF0000) >> 16); + pixels[i + 3].b = (byte)((data_enc[2] & 0xFF000000) >> 24); + } + result.Texture1.SetPixels32(pixels, m); + } - public override void SetFormatKeywords(Material material) { - material.EnableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); - } + return result; + } - public override (int, int) CalculateOffsets(Texture2D texture) { - int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1; - int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1; - return (woffset, hoffset); - } -} + public override void SetFormatKeywords(Material material) { + material.EnableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); + } -public class RGBA32Format : RGBFormat { - public override bool CanHandle(TextureFormat format) { - return format == TextureFormat.RGBA32; + public override (int, int) CalculateOffsets(Texture2D texture) { + int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1; + int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1; + return (woffset, hoffset); + } } - public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { - var result = new EncryptResult(); - result.Texture1 = CreateResultTexture(texture, TextureFormat.RGBA32); + public class RGBA32Format : RGBFormat { + public override bool CanHandle(TextureFormat format) { + return format == TextureFormat.RGBA32; + } - var key_uint = ConvertKeyToUInt(key); + public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm) { + var result = new EncryptResult(); + result.Texture1 = CreateResultTexture(texture, TextureFormat.RGBA32); - for (int m = 0; m < result.Texture1.mipmapCount; ++m) { - Color32[] pixels = texture.GetPixels32(m); + var key_uint = ConvertKeyToUInt(key); - for (int i = 0; i < pixels.Length; i += 2) { - key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); - key_uint[3] ^= (uint)i; + for (int m = 0; m < result.Texture1.mipmapCount; ++m) { + Color32[] pixels = texture.GetPixels32(m); - uint[] data = new uint[2]; - data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 0].a << 24)); - data[1] = (uint)(pixels[i + 1].r + (pixels[i + 1].g << 8) + (pixels[i + 1].b << 16) + (pixels[i + 1].a << 24)); + for (int i = 0; i < pixels.Length; i += 2) { + key_uint[3] = (uint)(key[12] | (key[13] << 8) | (key[14] << 16) | (key[15] << 24)); + key_uint[3] ^= (uint)i; - uint[] data_enc = algorithm.Encrypt(data, key_uint); + uint[] data = new uint[2]; + data[0] = (uint)(pixels[i + 0].r + (pixels[i + 0].g << 8) + (pixels[i + 0].b << 16) + (pixels[i + 0].a << 24)); + data[1] = (uint)(pixels[i + 1].r + (pixels[i + 1].g << 8) + (pixels[i + 1].b << 16) + (pixels[i + 1].a << 24)); - pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); - pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); - pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); - pixels[i + 0].a = (byte)((data_enc[0] & 0xFF000000) >> 24); - pixels[i + 1].r = (byte)((data_enc[1] & 0x000000FF) >> 0); - pixels[i + 1].g = (byte)((data_enc[1] & 0x0000FF00) >> 8); - pixels[i + 1].b = (byte)((data_enc[1] & 0x00FF0000) >> 16); - pixels[i + 1].a = (byte)((data_enc[1] & 0xFF000000) >> 24); + uint[] data_enc = algorithm.Encrypt(data, key_uint); + + pixels[i + 0].r = (byte)((data_enc[0] & 0x000000FF) >> 0); + pixels[i + 0].g = (byte)((data_enc[0] & 0x0000FF00) >> 8); + pixels[i + 0].b = (byte)((data_enc[0] & 0x00FF0000) >> 16); + pixels[i + 0].a = (byte)((data_enc[0] & 0xFF000000) >> 24); + pixels[i + 1].r = (byte)((data_enc[1] & 0x000000FF) >> 0); + pixels[i + 1].g = (byte)((data_enc[1] & 0x0000FF00) >> 8); + pixels[i + 1].b = (byte)((data_enc[1] & 0x00FF0000) >> 16); + pixels[i + 1].a = (byte)((data_enc[1] & 0xFF000000) >> 24); + } + result.Texture1.SetPixels32(pixels, m); } - result.Texture1.SetPixels32(pixels, m); + + return result; } - return result; - } + public override void SetFormatKeywords(Material material) { + material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); + material.EnableKeyword("_SHELL_PROTECTOR_FORMAT1"); + } - public override void SetFormatKeywords(Material material) { - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.EnableKeyword("_SHELL_PROTECTOR_FORMAT1"); + public override (int, int) CalculateOffsets(Texture2D texture) { + int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1; + int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1; + return (woffset, hoffset); + } } +} - public override (int, int) CalculateOffsets(Texture2D texture) { - int woffset = 13 - (int)Mathf.Log(texture.width, 2) - 1; - int hoffset = 13 - (int)Mathf.Log(texture.height, 2) - 1; - return (woffset, hoffset); - } -} \ No newline at end of file +#endif \ No newline at end of file diff --git a/Runtime/Scripts/TextureEncryptManager.cs b/Runtime/Scripts/TextureEncryptManager.cs index 3d32147..26f0f04 100644 --- a/Runtime/Scripts/TextureEncryptManager.cs +++ b/Runtime/Scripts/TextureEncryptManager.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; +#if UNITY_EDITOR + namespace Shell.Protector { public class TextureEncryptManager @@ -161,4 +163,6 @@ public static (int, int) CalculateOffsets(Texture2D texture) return format.CalculateOffsets(texture); } } -} \ No newline at end of file +} + +#endif \ No newline at end of file From e7c2d83996dd6759c23c83cac1af90bdac31551a Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Wed, 4 Jun 2025 10:09:13 +0900 Subject: [PATCH 18/59] fix: Problem with missing keyword insertion --- Runtime/Scripts/Injector/Injector.cs | 9 +++++---- Runtime/Scripts/ShellProtector.cs | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index b84f95c..72b5e75 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -117,7 +117,7 @@ public bool WasInjected(Shader shader) return false; } - private void SetKeywords(Material material, bool has_lim_texture = false) { + public void SetKeywords(Material material, bool has_lim_texture = false) { // Clear keywords prefixed with _SHELL_PROTECTOR_ var keywords = material.shaderKeywords; foreach (string keyword in keywords) @@ -131,14 +131,15 @@ private void SetKeywords(Material material, bool has_lim_texture = false) { TextureEncryptManager.SetFormatKeywords(material); // Set rimlight keyword - if (has_lim_texture) material.EnableKeyword("_SHELL_PROTECTOR_RIMLIGHT"); + if (has_lim_texture) + material.EnableKeyword("_SHELL_PROTECTOR_RIMLIGHT"); // Set encryptor keyword material.EnableKeyword(encryptor.Keyword); } - public Shader Inject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) { - SetKeywords(mat, has_lim_texture); + public Shader Inject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) + { return CustomInject(mat, decode_dir, output_dir, tex, has_lim_texture, has_lim_texture2, outline_tex); } diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 171e568..97d2b6d 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -704,6 +704,8 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) var hash = SimpleHash(key); new_mat.SetInteger("_PasswordHash", (int)hash); + injector.SetKeywords(new_mat, otherTex.limTexture != null); + AssetDatabase.CreateAsset(new_mat, encrypted_mat_path); Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(new_mat)); AssetDatabase.SaveAssets(); From ce18d3837de830955b906457e9639a0b9229a493 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Tue, 8 Jul 2025 23:01:23 +0900 Subject: [PATCH 19/59] feat: Support poiyomi 9.2, 9.3 --- Runtime/Scripts/AssetManager.cs | 17 ++++++++++------- Runtime/Scripts/CompileErrorListener.cs | 2 ++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Runtime/Scripts/AssetManager.cs b/Runtime/Scripts/AssetManager.cs index c08a1d8..9ed6671 100644 --- a/Runtime/Scripts/AssetManager.cs +++ b/Runtime/Scripts/AssetManager.cs @@ -27,6 +27,7 @@ static public AssetManager GetInstance() support_version.Add("Poiyomi 9.0", 90); support_version.Add("Poiyomi 9.1", 91); support_version.Add("Poiyomi 9.2", 92); + support_version.Add("Poiyomi 9.3", 93); support_version.Add("lilToon", 0); } public bool IsPoiyomi(Shader shader) @@ -60,7 +61,9 @@ public int GetShaderType(Shader shader) if (poiyomiLabel != -1) { var str = shader.GetPropertyDescription(poiyomiLabel); - if (str.Contains("Poiyomi 9.2")) + if (str.Contains("Poiyomi 9.3")) + return support_version["Poiyomi 9.3"]; + else if (str.Contains("Poiyomi 9.2")) return support_version["Poiyomi 9.2"]; else if (str.Contains("Poiyomi 9.1")) return support_version["Poiyomi 9.1"]; @@ -85,11 +88,13 @@ public bool IsSupportShader(Shader shader) public List CheckShader() { + Debug.Log("Checking Shader..."); string[] guids = AssetDatabase.FindAssets("lilConstants"); string symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); string symbols_original = string.Copy(symbols); symbols = symbols.Replace(";LILTOON", ""); + symbols = symbols.Replace(";POIYOMI91", ""); symbols = symbols.Replace(";POIYOMI", ""); List return_shader = new List(); if(guids.Length > 0) @@ -103,7 +108,7 @@ public List CheckShader() if (ClassExists("Thry.ThryEditor.ShaderOptimizer")) { symbols += ";POIYOMI91"; - return_shader.Add("Poiyomi9.1"); + return_shader.Add("Poiyomi9.1>"); } else // < 9.1 { @@ -112,12 +117,10 @@ public List CheckShader() } } - if (symbols_original.Contains(";LILTOON") != symbols.Contains(";LILTOON")) + if (symbols_original.Contains(";LILTOON") != symbols.Contains(";LILTOON") || + symbols_original.Contains(";POIYOMI") != symbols.Contains(";POIYOMI")) { - if (symbols_original.Contains(";POIYOMI") != symbols.Contains(";POIYOMI")) - { - PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, symbols); - } + PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, symbols); } return return_shader; diff --git a/Runtime/Scripts/CompileErrorListener.cs b/Runtime/Scripts/CompileErrorListener.cs index b20bc34..3fda27c 100644 --- a/Runtime/Scripts/CompileErrorListener.cs +++ b/Runtime/Scripts/CompileErrorListener.cs @@ -22,6 +22,8 @@ static CompileErrorListener() continue; if (!m.message.Contains("The type or namespace name 'Thry' could not be found")) continue; + if (!m.message.Contains("The name 'ShaderOptimizer' does not exist in the current context")) + continue; AssetManager.GetInstance().ResetDefine(); break; } From 6f135808f4298fca6c9eaa4d9b4736374df6c10c Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Tue, 8 Jul 2025 23:03:05 +0900 Subject: [PATCH 20/59] feat: Legacy supporting, Refactoring code --- Editor/ShellProtectorEditor.cs | 21 +- Editor/liltoonCustom/CustomInspector.cs | 2 +- Runtime/Scripts/AnimatorManager.cs | 48 +- Runtime/Scripts/{ => Formats}/Format.cs | 0 Runtime/Scripts/{ => Formats}/Format.cs.meta | 0 Runtime/Scripts/Injector/Injector.cs | 6 +- Runtime/Scripts/KeyGenerator.cs | 30 ++ Runtime/Scripts/ParameterManager.cs | 45 +- Runtime/Scripts/ShellProtector.cs | 471 +++++++++---------- 9 files changed, 332 insertions(+), 291 deletions(-) rename Runtime/Scripts/{ => Formats}/Format.cs (100%) rename Runtime/Scripts/{ => Formats}/Format.cs.meta (100%) diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index 4f94e6c..4483d81 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -31,7 +31,6 @@ public class ShellProtectorEditor : Editor SerializedProperty key_size; SerializedProperty key_size_idx; SerializedProperty sync_size; - SerializedProperty animation_speed; SerializedProperty delete_folders; SerializedProperty bUseSmallMipTexture; SerializedProperty bPreserveMMD; @@ -110,7 +109,6 @@ void OnEnable() key_size = serializedObject.FindProperty("key_size"); key_size_idx = serializedObject.FindProperty("key_size_idx"); sync_size = serializedObject.FindProperty("sync_size"); - animation_speed = serializedObject.FindProperty("animation_speed"); delete_folders = serializedObject.FindProperty("delete_folders"); bUseSmallMipTexture = serializedObject.FindProperty("bUseSmallMipTexture"); bPreserveMMD = serializedObject.FindProperty("bPreserveMMD"); @@ -246,6 +244,7 @@ public override void OnInspectorGUI() { GUILayout.Label(Lang("Max password length"), EditorStyles.boldLabel); key_size_idx.intValue = EditorGUILayout.Popup(key_size_idx.intValue, key_lengths, GUILayout.Width(150)); + GUILayout.Space(10); switch (key_size_idx.intValue) { @@ -279,6 +278,8 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("Sync speed"), EditorStyles.boldLabel); sync_size_index = EditorGUILayout.Popup(sync_size_index, selectable_values, GUILayout.Width(100)); sync_size.intValue = sync_size_caldidate[sync_size_index]; + GUILayout.Label(Lang("When the Sync speed is 2 or higher, OSC1.7 or higher must be used."), EditorStyles.boldLabel); + GUILayout.Space(10); } GUILayout.Label(Lang("Encrytion algorithm"), EditorStyles.boldLabel); @@ -307,14 +308,14 @@ public override void OnInspectorGUI() filter.intValue = EditorGUILayout.Popup(filter.intValue, ShellProtector.filterStrings, GUILayout.Width(100)); GUILayout.Label(Lang("Setting it to 'Point' may result in aliasing, but performance is better."), EditorStyles.wordWrappedLabel); - GUILayout.Label(Lang("Initial animation speed"), EditorStyles.boldLabel); - GUILayout.BeginHorizontal(); - animation_speed.floatValue = GUILayout.HorizontalSlider(animation_speed.floatValue, 2.0f, 5.0f, GUILayout.Width(100)); - animation_speed.floatValue = EditorGUILayout.FloatField("", animation_speed.floatValue, GUILayout.Width(50)); - animation_speed.floatValue = Math.Clamp(animation_speed.floatValue, 2.0f, 5.0f); - GUILayout.FlexibleSpace(); - GUILayout.Label(Lang("Avatar first load animation speed"), EditorStyles.wordWrappedLabel); - GUILayout.EndHorizontal(); + //GUILayout.Label(Lang("Initial animation speed"), EditorStyles.boldLabel); + //GUILayout.BeginHorizontal(); + //animation_speed.floatValue = GUILayout.HorizontalSlider(animation_speed.floatValue, 2.0f, 5.0f, GUILayout.Width(100)); + //animation_speed.floatValue = EditorGUILayout.FloatField("", animation_speed.floatValue, GUILayout.Width(50)); + //animation_speed.floatValue = Math.Clamp(animation_speed.floatValue, 2.0f, 5.0f); + //GUILayout.FlexibleSpace(); + //GUILayout.Label(Lang("Avatar first load animation speed"), EditorStyles.wordWrappedLabel); + //GUILayout.EndHorizontal(); GUILayout.Space(10); diff --git a/Editor/liltoonCustom/CustomInspector.cs b/Editor/liltoonCustom/CustomInspector.cs index a2fa340..8061a21 100644 --- a/Editor/liltoonCustom/CustomInspector.cs +++ b/Editor/liltoonCustom/CustomInspector.cs @@ -81,7 +81,7 @@ protected override void DrawCustomProperties(Material material) } } - var hash = ShellProtector.SimpleHash(key.Select(k => (byte)Mathf.RoundToInt(k.floatValue)).ToArray()); + var hash = KeyGenerator.SimpleHash(key.Select(k => (byte)Mathf.RoundToInt(k.floatValue)).ToArray()); EditorGUILayout.LabelField("Password hash", hash.ToString()); } } diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index e11429a..8ef2182 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -178,14 +178,16 @@ private static AnimatorConditionMode[] GetSwitchConditions(int switchCount, int private static void AddTransition(AnimatorStateTransition transition, int keyLength, int syncSize, int idx) { - transition.AddCondition(AnimatorConditionMode.IfNot, 0, ParameterManager.GetSyncLockName()); + bool bLegacy = syncSize == 1; + transition.AddCondition(AnimatorConditionMode.IfNot, 0, ParameterManager.GetSyncLockName(bLegacy)); AnimatorConditionMode[] switchConditions = GetSwitchConditions(ShellProtector.GetRequiredSwitchCount(keyLength, syncSize), idx); for (int i = 0; i < switchConditions.Length; ++i) - transition.AddCondition(switchConditions[i], 0, ParameterManager.GetSyncSwitchName(i)); + transition.AddCondition(switchConditions[i], 0, ParameterManager.GetSyncSwitchName(i, bLegacy)); } private static void AddParameters(AnimatorController anim, int keyLength, int syncSize) { + bool bLegacy = syncSize == 1; anim.AddParameter(new AnimatorControllerParameter { defaultFloat = 1.0f, @@ -206,18 +208,23 @@ private static void AddParameters(AnimatorController anim, int keyLength, int sy for (var i = 0; i < keyLength; ++i) anim.AddParameter(ParameterManager.GetKeyName(i), AnimatorControllerParameterType.Float); - anim.AddParameter(ParameterManager.GetSyncLockName(), AnimatorControllerParameterType.Bool); + anim.AddParameter(ParameterManager.GetSyncLockName(bLegacy), AnimatorControllerParameterType.Bool); var switchCount = ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); - for (var i = 0; i < keyLength; ++i) - anim.AddParameter(ParameterManager.GetSavedKeyName(i), AnimatorControllerParameterType.Float); + + if (!bLegacy) + { + for (var i = 0; i < keyLength; ++i) + anim.AddParameter(ParameterManager.GetSavedKeyName(i), AnimatorControllerParameterType.Float); + } for (var i = 0; i < syncSize; ++i) - anim.AddParameter(ParameterManager.GetSyncedKeyName(i), AnimatorControllerParameterType.Float); + anim.AddParameter(ParameterManager.GetSyncedKeyName(i, bLegacy), AnimatorControllerParameterType.Float); for (var i = 0; i < switchCount; ++i) - anim.AddParameter(ParameterManager.GetSyncSwitchName(i), AnimatorControllerParameterType.Bool); + anim.AddParameter(ParameterManager.GetSyncSwitchName(i, bLegacy), AnimatorControllerParameterType.Bool); } public static void AddKeyLayer(AnimatorController anim, string animationDir, int keyLength, int syncSize, float speed) { + bool bLegacy = syncSize == 1; AddParameters(anim, keyLength, syncSize); if (anim.layers.Any(l => l.name == "ShellProtector")) return; @@ -266,21 +273,23 @@ private static void AddSyncEnabledCondition(AnimatorStateTransition transition) private static void AddMuxLayer(AnimatorController anim, int keyLength, int syncSize, float unlockDelay, float interval, float delay) { - if (anim.layers.Any(l => l.name == "ShellProtectorMux")) return; + if (anim.layers.Any(l => l.name == "ShellProtectorMux")) + return; + + bool bLegacy = syncSize == 1; + + if (bLegacy) + return; var stateMachine = new AnimatorStateMachine { name = anim.MakeUniqueLayerName("ShellProtectorMux"), hideFlags = HideFlags.HideInHierarchy }; - AssetDatabase.AddObjectToAsset(stateMachine, anim); - anim.AddLayer(new AnimatorControllerLayer { name = stateMachine.name, defaultWeight = 1.0f, stateMachine = stateMachine }); - var layer = anim.layers[anim.layers.Length - 1]; var idle = layer.stateMachine.AddState("Idle", new Vector3(0, 0)); - layer.stateMachine.AddEntryTransition(idle); var steps = keyLength / syncSize; var syncStates = new AnimatorState[steps]; @@ -336,7 +345,7 @@ private static void AddMuxLayer(AnimatorController anim, int keyLength, int sync lockDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Set, - name = ParameterManager.GetSyncLockName(), + name = ParameterManager.GetSyncLockName(bLegacy), value = 1 }); @@ -345,7 +354,7 @@ private static void AddMuxLayer(AnimatorController anim, int keyLength, int sync syncDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Copy, - name = ParameterManager.GetSyncedKeyName(i), + name = ParameterManager.GetKeyName(step * syncSize + i), source = ParameterManager.GetSavedKeyName(step * syncSize + i) }); } @@ -355,7 +364,7 @@ private static void AddMuxLayer(AnimatorController anim, int keyLength, int sync syncDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Set, - name = ParameterManager.GetSyncSwitchName(i), + name = ParameterManager.GetSyncSwitchName(i, bLegacy), value = (step & (1 << i)) != 0 ? 1 : 0 }); } @@ -363,7 +372,7 @@ private static void AddMuxLayer(AnimatorController anim, int keyLength, int sync unlockDriver.parameters.Add(new VRC_AvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Set, - name = ParameterManager.GetSyncLockName(), + name = ParameterManager.GetSyncLockName(bLegacy), value = 0 }); @@ -375,6 +384,7 @@ private static void AddMuxLayer(AnimatorController anim, int keyLength, int sync private static void AddDemuxLayer(AnimatorController anim, int keyLength, int syncSize) { + bool bLegacy = syncSize == 1; if (anim.layers.Any(l => l.name == "ShellProtectorDemux")) return; var stateMachine = new AnimatorStateMachine @@ -393,7 +403,7 @@ private static void AddDemuxLayer(AnimatorController anim, int keyLength, int sy transition.exitTime = 0; transition.duration = 0; transition.hasExitTime = false; - transition.AddCondition(AnimatorConditionMode.If, 0, ParameterManager.GetSyncLockName()); + transition.AddCondition(AnimatorConditionMode.If, 0, ParameterManager.GetSyncLockName(bLegacy)); for (var i = 0; i < keyLength / syncSize; ++i) { @@ -405,8 +415,8 @@ private static void AddDemuxLayer(AnimatorController anim, int keyLength, int sy behaviour.parameters.Add(new VRCAvatarParameterDriver.Parameter { type = VRC_AvatarParameterDriver.ChangeType.Copy, - name = ParameterManager.GetKeyName(i * syncSize + j), - source = ParameterManager.GetSyncedKeyName(j) + source = ParameterManager.GetSyncedKeyName(j, bLegacy), + name = ParameterManager.GetKeyName(i * syncSize + j) }); } diff --git a/Runtime/Scripts/Format.cs b/Runtime/Scripts/Formats/Format.cs similarity index 100% rename from Runtime/Scripts/Format.cs rename to Runtime/Scripts/Formats/Format.cs diff --git a/Runtime/Scripts/Format.cs.meta b/Runtime/Scripts/Formats/Format.cs.meta similarity index 100% rename from Runtime/Scripts/Format.cs.meta rename to Runtime/Scripts/Formats/Format.cs.meta diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index 72b5e75..d7aaf51 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -1,10 +1,6 @@ -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Text.RegularExpressions; +using System.IO; using UnityEditor; using UnityEngine; -using static VRC.SDK3.Dynamics.PhysBone.PhysBoneMigration.DynamicBoneColliderData; #if UNITY_EDITOR namespace Shell.Protector diff --git a/Runtime/Scripts/KeyGenerator.cs b/Runtime/Scripts/KeyGenerator.cs index 294deb3..a75b080 100644 --- a/Runtime/Scripts/KeyGenerator.cs +++ b/Runtime/Scripts/KeyGenerator.cs @@ -68,4 +68,34 @@ public static string GenerateRandomString(int length) return builder.ToString(); } + + public static uint SimpleHash(byte[] data) + { + if (data.Length != 16) + throw new ArgumentException("Input must be exactly 16 bytes."); + + uint hash = 0x811C9DC5u; + + for (int i = 0; i < 16; i++) + { + uint k = data[i]; + + k *= 0xcc9e2d51u; + k = (k << 15) | (k >> 17); + k *= 0x1b873593u; + + hash ^= k; + hash = (hash << 13) | (hash >> 19); + hash = hash * 5u + 0xe6546b64u; + } + + hash ^= 16u; + hash ^= (hash >> 16); + hash *= 0x85ebca6bu; + hash ^= (hash >> 13); + hash *= 0xc2b2ae35u; + hash ^= (hash >> 16); + + return hash; + } } diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index 59939f6..54e9743 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -8,21 +8,37 @@ public static class ParameterManager { private const string Prefix = "SHELL_PROTECTOR_"; - public static string GetSyncedKeyName(int index) => Prefix + "synced_key" + index; + public static string GetSyncedKeyName(int index, bool bLegacy = false) + { + if (bLegacy) + return "pkey"; + return Prefix + "synced_key" + index; + } public static string GetKeyName(int index) => Prefix + "key" + index; public static string GetSavedKeyName(int index) => Prefix + "saved_key" + index; - public static string GetSyncSwitchName(int index) => Prefix + "sync_switch" + index; - public static string GetSyncLockName() => Prefix + "sync_lock"; + public static string GetSyncSwitchName(int index, bool bLegacy = false) + { + if (bLegacy) + return "encrypt_switch" + index; + return Prefix + "sync_switch" + index; + } + public static string GetSyncLockName(bool bLegacy = false) + { + if (bLegacy) + return "encrypt_lock"; + return Prefix + "sync_lock"; + } public static string GetIsLocalName() => "IsLocal"; public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vrcParameters, int keyLength, int syncSize) { + bool bLegacy = syncSize == 1; var parameters = new List(); parameters.Add(new VRCExpressionParameters.Parameter { - name = GetSyncLockName(), + name = GetSyncLockName(bLegacy), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Bool, @@ -33,7 +49,7 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetSyncedKeyName(i), + name = GetSyncedKeyName(i, bLegacy), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Float, @@ -45,7 +61,7 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr { parameters.Add(new VRCExpressionParameters.Parameter { - name = GetSyncSwitchName(i), + name = GetSyncSwitchName(i, bLegacy), saved = true, networkSynced = true, valueType = VRCExpressionParameters.ValueType.Bool, @@ -64,14 +80,17 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr defaultValue = 0.0f }); - parameters.Add(new VRCExpressionParameters.Parameter + if (!bLegacy) { - name = GetSavedKeyName(i), - saved = true, - networkSynced = false, - valueType = VRCExpressionParameters.ValueType.Float, - defaultValue = 0.0f - }); + parameters.Add(new VRCExpressionParameters.Parameter + { + name = GetSavedKeyName(i), + saved = true, + networkSynced = false, + valueType = VRCExpressionParameters.ValueType.Float, + defaultValue = 0.0f + }); + } } var result = ScriptableObject.CreateInstance(); diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 97d2b6d..8bbe5ed 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -1,6 +1,5 @@ #if UNITY_EDITOR using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; @@ -11,7 +10,6 @@ using System.Linq; using VRC.SDKBase; using UnityEditor.Animations; -using UnityEngine.Serialization; #if MODULAR using nadena.dev.modular_avatar.core; @@ -75,8 +73,7 @@ public class MaterialOptionPair struct ProcessedTexture { - public Texture2D encrypted0; - public Texture2D encrypted1; + public EncryptResult encrypted; public List fallbacks; public List fallbackOptions; public byte[] nonce; @@ -102,7 +99,6 @@ struct OtherTextures [SerializeField] int key_size_idx = 3; [SerializeField] int key_size = 12; [SerializeField] int sync_size = 1; - [SerializeField] float animation_speed = 5.0f; [SerializeField] bool delete_folders = true; [SerializeField] bool bUseSmallMipTexture = true; @@ -146,7 +142,7 @@ public void Init() public void SyncMatOption() { - foreach(var pair in matOptionSaved) + foreach (var pair in matOptionSaved) { if (pair.material != null) matOptions[pair.material] = pair.option; @@ -154,7 +150,7 @@ public void SyncMatOption() } public void SaveMatOption() { - foreach(var pair in matOptions) + foreach (var pair in matOptions) { matOptionSaved.Add(new MaterialOptionPair { material = pair.Key, option = pair.Value }); } @@ -168,7 +164,7 @@ public byte[] GetKeyBytes() public GameObject DuplicateAvatar(GameObject avatar) { GameObject cpy = Instantiate(avatar); - if(!avatar.name.Contains("_encrypted")) + if (!avatar.name.Contains("_encrypted")) cpy.name = avatar.name + "_encrypted"; return cpy; } @@ -300,35 +296,211 @@ public List GetMaterials() return materials.Concat(material_list).Distinct().ToList(); } + Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) + { + var mip = TextureEncryptManager.GenerateRefMipmap(size, size, bUseSmallMip); + if (mip == null) + Debug.LogErrorFormat("{0} : Can't generate mip tex{1}.", outputDir, size); + else + { + AssetDatabase.CreateAsset(mip, outputDir); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + return mip; + } - public static uint SimpleHash(byte[] data) + ProcessedTexture? GenerateEncryptedTexture(string avatarDir, Material mat, IEncryptor encryptor, byte[] keyBytes) { - if (data.Length != 16) - throw new ArgumentException("Input must be exactly 16 bytes."); + Texture2D mainTexture = (Texture2D)mat.mainTexture; - uint hash = 0x811C9DC5u; + string texPath1 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt.asset"); + string texPath2 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt2.asset"); - for (int i = 0; i < 16; i++) + bool processed = processedTextures.ContainsKey(mainTexture); + ProcessedTexture processedTexture; + if (processed) + processedTexture = processedTextures[mainTexture]; + else + { + processedTexture = new ProcessedTexture + { + encrypted = new EncryptResult(), + fallbacks = new List(), + fallbackOptions = new List(), + nonce = new byte[12] + }; + } + + //Set chacha nonce + if (algorithm == (int)Algorithm.chacha) + { + Chacha20 chacha = encryptor as Chacha20; + if (!processed) + { + byte[] hashMat = KeyGenerator.GetHash(mat.GetInstanceID()); + for (int i = 0; i < chacha.nonce.Length; ++i) + chacha.nonce[i] ^= hashMat[i]; + Array.Copy(chacha.nonce, 0, processedTexture.nonce, 0, processedTexture.nonce.Length); + } + else + { + byte[] nonce = processedTextures[mainTexture].nonce; + Array.Copy(nonce, 0, chacha.nonce, 0, chacha.nonce.Length); + } + } + + if (!processed) { - uint k = data[i]; + EncryptResult encryptResult; + try + { + encryptResult = TextureEncryptManager.EncryptTexture(mainTexture, keyBytes, encryptor); + } + catch (ArgumentException e) + { + Debug.LogErrorFormat("{0} : ArgumentException - {1}", mainTexture.name, e.Message); + return null; + } + AssetDatabase.CreateAsset(encryptResult.Texture1, texPath1); + if (encryptResult.Texture2 != null) + AssetDatabase.CreateAsset(encryptResult.Texture2, texPath2); - k *= 0xcc9e2d51u; - k = (k << 15) | (k >> 17); - k *= 0x1b873593u; + processedTexture.encrypted = encryptResult; - hash ^= k; - hash = (hash << 13) | (hash >> 19); - hash = hash * 5u + 0xe6546b64u; + processedTextures.Add(mainTexture, processedTexture); } - hash ^= 16u; - hash ^= (hash >> 16); - hash *= 0x85ebca6bu; - hash ^= (hash >> 13); - hash *= 0xc2b2ae35u; - hash ^= (hash >> 16); + return processedTexture; + } + + Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D mainTexture, ref ProcessedTexture processedTexture) + { + int fallbackOption = this.fallback; + if (option != null) + fallbackOption = option.fallback; - return hash; + int idx = processedTexture.fallbackOptions.FindIndex(option => option == fallbackOption); + Texture2D fallback = null; + if (idx == -1) + { + int fallbackSize = 32; + switch (fallbackOption) + { + case 0: // white + fallbackSize = 0; + break; + case 1: // black + fallbackSize = 1; + break; + case 2: + fallbackSize = 4; + break; + case 3: + fallbackSize = 8; + break; + case 4: + fallbackSize = 16; + break; + case 5: + fallbackSize = 32; + break; + case 6: + fallbackSize = 64; + break; + case 7: + fallbackSize = 128; + break; + } + if (fallbackSize > 1) + { + fallback = TextureEncryptManager.GenerateFallback(mainTexture, fallbackSize); + if (fallback != null) + { + processedTexture.fallbacks.Add(fallback); + processedTexture.fallbackOptions.Add(fallbackOption); + AssetDatabase.CreateAsset(fallback, outputDir); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + } + else + { + switch (fallbackSize) + { + case 0: + processedTexture.fallbacks.Add(fallbackWhite); + processedTexture.fallbackOptions.Add(fallbackOption); + fallback = fallbackWhite; + break; + case 1: + processedTexture.fallbacks.Add(fallbackBlack); + processedTexture.fallbackOptions.Add(fallbackOption); + fallback = fallbackBlack; + break; + } + } + } + else + fallback = processedTexture.fallbacks[idx]; + + return fallback; + } + + Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, OtherTextures otherTex , ProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) + { + Material newMat = new Material(mat.shader); + newMat.CopyPropertiesFromMaterial(mat); + newMat.shader = encryptedShader; + var originalTex = (Texture2D)newMat.mainTexture; + newMat.mainTexture = fallback; + + Texture2D encryptedTex0 = processedTexture.encrypted.Texture1; + Texture2D encryptedTex1 = processedTexture.encrypted.Texture2; + + newMat.SetTexture("_MipTex", mip); + + if (encryptedTex0 != null) + newMat.SetTexture("_EncryptTex0", encryptedTex0); + if (encryptedTex1 != null) + newMat.SetTexture("_EncryptTex1", encryptedTex1); + + newMat.renderQueue = mat.renderQueue; + if (turnOnAllSafetyFallback) + newMat.SetOverrideTag("VRCFallback", "Unlit"); + + var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(originalTex); + newMat.SetInteger("_Woffset", woffset); + newMat.SetInteger("_Hoffset", hoffset); + for (int i = 0; i < 16 - key_size; ++i) + newMat.SetFloat("_Key" + i, keyBytes[i]); + + if (algorithm == (int)Algorithm.chacha) + { + Chacha20 chacha = encryptor as Chacha20; + newMat.SetInteger("_Nonce0", (int)chacha.GetNonceUint3()[0]); + newMat.SetInteger("_Nonce1", (int)chacha.GetNonceUint3()[1]); + newMat.SetInteger("_Nonce2", (int)chacha.GetNonceUint3()[2]); + } + + var key = new byte[16]; + for (int i = 0; i < 16; i++) + key[i] = keyBytes[i]; + + var hash = KeyGenerator.SimpleHash(key); + newMat.SetInteger("_PasswordHash", (int)hash); + + injector.SetKeywords(newMat, otherTex.limTexture != null); + + AssetDatabase.CreateAsset(newMat, outputDir); + Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(newMat)); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + if (!encryptedMaterials.ContainsKey(mat)) + encryptedMaterials.Add(mat, newMat); + + return newMat; } public GameObject Encrypt(bool isModular = true) @@ -390,9 +562,6 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) Debug.LogError("Cannot create duplicated avatar!"); return null; } - - var mips = new Dictionary(); - byte[] key_bytes = GetKeyBytes(); CreateFolders(); @@ -427,6 +596,8 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) int progress = 0; int maxprogress = materials.Count; + + var mips = new Dictionary(); foreach (var mat in materials) { int filter = this.filter; @@ -457,101 +628,34 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) continue; } if (!ConditionCheck(mat)) - { continue; - } + Debug.LogFormat("{0} : Start encrypt...", mat.name); Texture2D main_texture = (Texture2D)mat.mainTexture; injector.Init(descriptor.gameObject, main_texture, key_bytes, key_size, filter, asset_dir, encryptor); - #region Generate mip_tex - int size = Math.Max(mat.mainTexture.width, mat.mainTexture.height); - if (!mips.ContainsKey(size)) + int mipRefSize = Math.Max(mat.mainTexture.width, mat.mainTexture.height); + if (!mips.ContainsKey(mipRefSize)) { - var mip = TextureEncryptManager.GenerateRefMipmap(size, size, bUseSmallMip); - if (mip == null) - Debug.LogErrorFormat("{0} : Can't generate mip tex{1}.", mat.name, size); - else - { - mips.Add(size, mip); - AssetDatabase.CreateAsset(mip, Path.Combine(avatarDir, "tex", "mip_" + size + ".asset")); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } + Texture2D mipRef = GenerateMipRefTexture(Path.Combine(avatarDir, "tex", "mip_" + mipRefSize + ".asset"), mipRefSize, bUseSmallMip); + if (mipRef != null) + mips.Add(mipRefSize, mipRef); } - #endregion + TextureSettings.SetRWEnableTexture(main_texture); TextureSettings.SetCrunchCompression(main_texture, false); TextureSettings.SetGenerateMipmap(main_texture, true); - string encrypt_tex_path = Path.Combine(avatarDir, "tex", main_texture.GetInstanceID() + "_encrypt.asset"); - string encrypt_tex2_path = Path.Combine(avatarDir, "tex", main_texture.GetInstanceID() + "_encrypt2.asset"); - string encrypted_mat_path = Path.Combine(avatarDir, "mat", mat.GetInstanceID() + "_encrypted.mat"); string encrypted_shader_path = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); - #region Make encrypted textures - bool processed = processedTextures.ContainsKey(main_texture); - ProcessedTexture processedTexture; - if (processed) - processedTexture = processedTextures[main_texture]; - else - processedTexture = new ProcessedTexture - { - encrypted0 = null, - encrypted1 = null, - fallbacks = new List(), - fallbackOptions = new List(), - nonce = new byte[12] - }; - - //Set chacha nonce - if (algorithm == (int)Algorithm.chacha) - { - Chacha20 chacha = encryptor as Chacha20; - if (!processed) - { - byte[] hashMat = KeyGenerator.GetHash(mat.GetInstanceID()); - for (int i = 0; i < chacha.nonce.Length; ++i) - chacha.nonce[i] ^= hashMat[i]; - Array.Copy(chacha.nonce, 0, processedTexture.nonce, 0, processedTexture.nonce.Length); - } - else - { - byte[] nonce = processedTextures[main_texture].nonce; - Array.Copy(nonce, 0, chacha.nonce, 0, chacha.nonce.Length); - } - } - - EncryptResult result; - if (processed == false) - { - try - { - result = TextureEncryptManager.EncryptTexture(main_texture, key_bytes, encryptor); - } - catch(ArgumentException e) - { - Debug.LogErrorFormat("{0} : ArgumentException - {1}", main_texture.name, e.Message); - continue; - } - - AssetDatabase.CreateAsset(result.Texture1, encrypt_tex_path); - Debug.Log(result.Texture1.name + ": " + AssetDatabase.GetAssetPath(result.Texture1)); - processedTexture.encrypted0 = result.Texture1; + var processedTextureResult = GenerateEncryptedTexture(avatarDir, mat, encryptor, key_bytes); + if (!processedTextureResult.HasValue) + continue; + ProcessedTexture processedTexture = processedTextureResult.Value; - if (result.Texture2 != null) - { - AssetDatabase.CreateAsset(result.Texture2, encrypt_tex2_path); - processedTexture.encrypted1 = result.Texture2; - } - } - else - { - result.Texture1 = processedTexture.encrypted0; - result.Texture2 = processedTexture.encrypted1; - } - #endregion + Texture2D encryptedTex1 = processedTexture.encrypted.Texture1; + Texture2D encryptedTex2 = processedTexture.encrypted.Texture2; //////////////////////Inject shader/////////////////////// OtherTextures otherTex = GetLimOutlineTextures(mat); @@ -563,8 +667,8 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) encrypted_shader = injector.Inject( mat, Path.Combine(asset_dir, "Shader/ShellProtector.cginc"), - encrypted_shader_path, - result.Texture1, + encrypted_shader_path, + encryptedTex1, otherTex.limTexture != null, otherTex.limTexture2 != null, otherTex.outlineTexture != null @@ -585,138 +689,19 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) continue; } } - #region FallbackTexture - /////////////////Generate fallback///////////////////// - string fallbackDir = Path.Combine(avatarDir, "tex", main_texture.GetInstanceID() + "_fallback.asset"); - int fallbackOption = this.fallback; - if (option != null) - fallbackOption = option.fallback; - int idx = processedTexture.fallbackOptions.FindIndex(option => option == fallbackOption); - Texture2D fallback = null; - if (idx == -1) - { - int fallbackSize = 32; - switch (fallbackOption) - { - case 0: // white - fallbackSize = 0; - break; - case 1: // black - fallbackSize = 1; - break; - case 2: - fallbackSize = 4; - break; - case 3: - fallbackSize = 8; - break; - case 4: - fallbackSize = 16; - break; - case 5: - fallbackSize = 32; - break; - case 6: - fallbackSize = 64; - break; - case 7: - fallbackSize = 128; - break; - } - if (fallbackSize > 1) - { - fallback = TextureEncryptManager.GenerateFallback(main_texture, fallbackSize); - if (fallback != null) - { - processedTexture.fallbacks.Add(fallback); - processedTexture.fallbackOptions.Add(fallbackOption); - AssetDatabase.CreateAsset(fallback, fallbackDir); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } - } - else - { - switch (fallbackSize) - { - case 0: - processedTexture.fallbacks.Add(fallbackWhite); - processedTexture.fallbackOptions.Add(fallbackOption); - fallback = fallbackWhite; - break; - case 1: - processedTexture.fallbacks.Add(fallbackBlack); - processedTexture.fallbackOptions.Add(fallbackOption); - fallback = fallbackBlack; - break; - } - } - } - else - fallback = processedTexture.fallbacks[idx]; - //////////////////////////////////////////////////////// - #endregion - - #region Material - //////////////////Create Material//////////////////////// - Material new_mat = new Material(mat.shader); - new_mat.CopyPropertiesFromMaterial(mat); - new_mat.shader = encrypted_shader; - var original_tex = new_mat.mainTexture; - new_mat.mainTexture = fallback; - - int max = Math.Max(result.Texture1.width, result.Texture1.height); - var mip_tex = mips[max]; - if(mip_tex == null) - Debug.LogWarningFormat("mip_{0} is not exsist", max); - - new_mat.SetTexture("_MipTex", mip_tex); - - if (result.Texture1 != null) - new_mat.SetTexture("_EncryptTex0", result.Texture1); - if (result.Texture2 != null) - new_mat.SetTexture("_EncryptTex1", result.Texture2); - - new_mat.renderQueue = mat.renderQueue; - if (turnOnAllSafetyFallback) - new_mat.SetOverrideTag("VRCFallback", "Unlit"); - - var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(main_texture); - new_mat.SetInteger("_Woffset", woffset); - new_mat.SetInteger("_Hoffset", hoffset); - for (int i = 0; i < 16 - key_size; ++i) - new_mat.SetFloat("_Key" + i, key_bytes[i]); - - if (algorithm == (int)Algorithm.chacha) - { - Chacha20 chacha = encryptor as Chacha20; - new_mat.SetInteger("_Nonce0", (int)chacha.GetNonceUint3()[0]); - new_mat.SetInteger("_Nonce1", (int)chacha.GetNonceUint3()[1]); - new_mat.SetInteger("_Nonce2", (int)chacha.GetNonceUint3()[2]); - } - - var key = new byte[16]; - for (int i = 0; i < 16; i++) - { - key[i] = key_bytes[i]; - } - var hash = SimpleHash(key); - new_mat.SetInteger("_PasswordHash", (int)hash); - - injector.SetKeywords(new_mat, otherTex.limTexture != null); + string fallbackDir = Path.Combine(avatarDir, "tex", main_texture.GetInstanceID() + "_fallback.asset"); + Texture2D fallback = GenerateFallbackTexture(fallbackDir, option, main_texture, ref processedTexture); + if (fallback == null) + Debug.LogErrorFormat("Failed to generate fallback texture: {0}", main_texture.name); - AssetDatabase.CreateAsset(new_mat, encrypted_mat_path); - Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(new_mat)); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - ////////////////////////////////////////////////////// - #endregion + int maxSize = Math.Max(main_texture.width, main_texture.height); + Texture2D mipTex = mips[maxSize]; + if (mipTex == null) + Debug.LogWarningFormat("mip_{0} is not exsist", maxSize); - if (!processed) - processedTextures.Add(main_texture, processedTexture); - if (!encryptedMaterials.ContainsKey(mat)) - encryptedMaterials.Add(mat, new_mat); + string encryptedMatPath = Path.Combine(avatarDir, "mat", mat.GetInstanceID() + "_encrypted.mat"); + GenerateEncryptedMaterial(encryptedMatPath, mat, encrypted_shader, fallback, mipTex, otherTex, processedTexture, key_bytes, encryptor); } // Material loop EditorUtility.ClearProgressBar(); @@ -853,7 +838,7 @@ public void RemoveDuplicatedTextures(GameObject avatar) if (processedTextures.ContainsKey((Texture2D)mat.GetTexture(name))) { Texture2D mainTexture = (Texture2D)mat.GetTexture(name); - Texture2D encrypted0 = processedTextures[(Texture2D)mat.GetTexture(name)].encrypted0; + Texture2D encrypted0 = processedTextures[(Texture2D)mat.GetTexture(name)].encrypted.Texture1; int idx = processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.IndexOf(processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.Max()); Texture2D bigFallbackTexture = processedTextures[(Texture2D)mat.GetTexture(name)].fallbacks[idx]; @@ -1019,7 +1004,7 @@ public void SetAnimations(GameObject avatar, bool clone) GameObject[] meshArray = new GameObject[meshes.Count]; meshes.CopyTo(meshArray); AnimatorManager.CreateKeyAniamtions(Path.Combine(asset_dir, "Animations"), animationDir, meshArray); - AnimatorManager.AddKeyLayer(fx, animationDir, key_size, sync_size, animation_speed); + AnimatorManager.AddKeyLayer(fx, animationDir, key_size, sync_size, 3.0f); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); From b8dc805711a3ebc6e0d82bc427b4acf52870f4ac Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:07:52 +0900 Subject: [PATCH 21/59] fix(Obfuscator): Add hash code to the generated asset name to prevent duplication --- Runtime/Scripts/Obfuscator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/Obfuscator.cs b/Runtime/Scripts/Obfuscator.cs index a5e1d5c..808242f 100644 --- a/Runtime/Scripts/Obfuscator.cs +++ b/Runtime/Scripts/Obfuscator.cs @@ -109,7 +109,7 @@ public Mesh ObfuscateBlendShapeMesh(Mesh mesh, string newPath) } Debug.LogFormat("Obfuscator blendshapes : {0}", string.Join(", ", obfuscatedBlendShapeNames.Select(kv => $"{kv.Key}: {kv.Value}"))); - AssetDatabase.CreateAsset(obfuscatedMesh, Path.Combine(newPath, obfuscatedMesh.name + ".asset")); + AssetDatabase.CreateAsset(obfuscatedMesh, Path.Combine(newPath, obfuscatedMesh.name + obfuscatedMesh.GetHashCode() + ".asset")); AssetDatabase.Refresh(); return obfuscatedMesh; } @@ -237,7 +237,7 @@ public AnimationClip ChangeBlendShapeInClip(AnimationClip clip, GameObject obj) else { newClip = Instantiate(clip); - AssetDatabase.CreateAsset(newClip, Path.Combine(animDir, clip.name + "_obfuscated.anim")); + AssetDatabase.CreateAsset(newClip, Path.Combine(animDir, clip.name + clip.GetHashCode() + "_obfuscated.anim")); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); obfuscatedClip.Add(clip, newClip); From 74f4975ab38baccd189a66ef6b41224f76031a4a Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:33:46 +0900 Subject: [PATCH 22/59] feat: Add a hash salt --- Editor/MaterialAdvancedSettings.cs | 1 + Runtime/Scripts/Injector/PoiyomiInjector.cs | 1 + Runtime/Scripts/KeyGenerator.cs | 3 ++- Runtime/Scripts/ShellProtector.cs | 8 ++++++-- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Editor/MaterialAdvancedSettings.cs b/Editor/MaterialAdvancedSettings.cs index dc2a34c..1cddfaa 100644 --- a/Editor/MaterialAdvancedSettings.cs +++ b/Editor/MaterialAdvancedSettings.cs @@ -71,6 +71,7 @@ private void OnGUI() else GUILayout.Label(Lang("New shader")); } + //option.Value.emissionEnc = GUILayout.Toggle(option.Value.emissionEnc, "Encrypt emission()"); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index 904dd3d..14b4965 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -141,6 +141,7 @@ private void InsertProperties(ref string data) _Nonce1 (""Nonce"", integer) = 0 _Nonce2 (""Nonce"", integer) = 0 _PasswordHash (""PasswordHash"", integer) = 0 + _HashMagic (""HashMagic"", integer) = 0 "; for (int i = 0; i < 16; ++i) diff --git a/Runtime/Scripts/KeyGenerator.cs b/Runtime/Scripts/KeyGenerator.cs index a75b080..c22740e 100644 --- a/Runtime/Scripts/KeyGenerator.cs +++ b/Runtime/Scripts/KeyGenerator.cs @@ -69,12 +69,13 @@ public static string GenerateRandomString(int length) return builder.ToString(); } - public static uint SimpleHash(byte[] data) + public static uint SimpleHash(byte[] data, uint hashMagic) { if (data.Length != 16) throw new ArgumentException("Input must be exactly 16 bytes."); uint hash = 0x811C9DC5u; + hash *= hashMagic; for (int i = 0; i < 16; i++) { diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 8bbe5ed..9519dfd 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -57,6 +57,7 @@ public class MatOption public bool active = true; public int filter = -1; public int fallback = -1; + public bool emissionEnc = false; } [Serializable] public class MaterialOptionPair @@ -487,7 +488,10 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp for (int i = 0; i < 16; i++) key[i] = keyBytes[i]; - var hash = KeyGenerator.SimpleHash(key); + uint hashMagic = (uint)mat.GetInstanceID(); + + var hash = KeyGenerator.SimpleHash(key, hashMagic); + newMat.SetInteger("_HashMagic", (int)hashMagic); newMat.SetInteger("_PasswordHash", (int)hash); injector.SetKeywords(newMat, otherTex.limTexture != null); @@ -624,7 +628,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) injector = InjectorFactory.GetInjector(mat.shader); if (injector == null) { - Debug.LogWarning(mat.shader + " is a unsupported shader! supported type:lilToon, poiyomi"); + Debug.LogError(mat.shader + " is a unsupported shader! supported type:lilToon, poiyomi"); continue; } if (!ConditionCheck(mat)) From fbf7e9500cf6aa80ee52a3af55257a17637afea7 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:34:00 +0900 Subject: [PATCH 23/59] feat: Separation of decryption function --- Runtime/Scripts/Injector/Injector.cs | 31 ++------------------ Runtime/Shader/DXT.cginc | 15 ++++++---- Runtime/Shader/ShellProtector.cginc | 44 ++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 38 deletions(-) diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index d7aaf51..d07e9b7 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -19,14 +19,7 @@ abstract public class Injector UNITY_BRANCH if(IsDecrypted()) { - half4 mip_texture = _MipTex.Sample(sampler_MipTex, mainUV); - - int mip = round(mip_texture.r * 255 / 10); //fucking precision problems - const int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 }; // max size 4k - - half4 c00 = DecryptTexture(mainUV, m[mip]); - - mainTexture = c00; + mainTexture = DecryptTextureBox(_EncryptTex0, _EncryptTex1, sampler_EncryptTex0, _EncryptTex0_TexelSize, _MipTex, sampler_MipTex, mainUV); } else { @@ -39,27 +32,7 @@ abstract public class Injector UNITY_BRANCH if(IsDecrypted()) { - half4 mip_texture = _MipTex.Sample(sampler_MipTex, mainUV); - - half2 uv_unit = _EncryptTex0_TexelSize.xy; - //bilinear interpolation - half2 uv_bilinear = poiMesh.uv[0] - 0.5 * uv_unit; - int mip = round(mip_texture.r * 255 / 10); //fucking precision problems - const int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 }; // max size 4k - - half4 c00 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 0, uv_unit.y * 0), m[mip]); - half4 c10 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 1, uv_unit.y * 0), m[mip]); - half4 c01 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 0, uv_unit.y * 1), m[mip]); - half4 c11 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 1, uv_unit.y * 1), m[mip]); - - half2 f = frac(uv_bilinear * _EncryptTex0_TexelSize.zw); - - half4 c0 = lerp(c00, c10, f.x); - half4 c1 = lerp(c01, c11, f.x); - - half4 bilinear = lerp(c0, c1, f.y); - - mainTexture = bilinear; + mainTexture = DecryptTextureBilinear(_EncryptTex0, _EncryptTex1, sampler_EncryptTex0, _EncryptTex0_TexelSize, _MipTex, sampler_MipTex, mainUV); } else { diff --git a/Runtime/Shader/DXT.cginc b/Runtime/Shader/DXT.cginc index 460420f..aecd0ee 100644 --- a/Runtime/Shader/DXT.cginc +++ b/Runtime/Shader/DXT.cginc @@ -3,7 +3,8 @@ #define _SHELL_PROTECTOR_DATA_LENGTH 2 #define _SHELL_PROTECTOR_INDEX_ALIGNMENT 1 -int GetIndex(half2 uv, int m) { +int GetIndex(half2 uv, int m) +{ half x = frac(uv.x); half y = frac(uv.y); @@ -13,23 +14,25 @@ int GetIndex(half2 uv, int m) { return (w * floor(y * h)) + floor(x * w); } -void GetData(inout uint data[2], half2 uv, int m) { +void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[2], half2 uv, int m) +{ int idx = GetIndex(uv, m); int offset = (idx & 1) == 0 ? 0 : -1; half4 pixels[2]; - pixels[0] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex1.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[0] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 0 + offset, m, (uint) _Woffset, _Hoffset), m); + pixels[1] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 1 + offset, m, (uint) _Woffset, _Hoffset), m); data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); } -half4 GetPixel(inout uint data[2], half2 uv, int m) { +half4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, inout uint data[2], half2 uv, int m) +{ int idx = GetIndex(uv, m); int offset = (idx & 1) == 0 ? 0 : -1; - half4 col = _EncryptTex0.SampleLevel(sampler_EncryptTex0, uv, m); + half4 col = tex0.SampleLevel(tex0Sampler, uv, m); uint r = (data[idx & 1] & 0x000000FF) >> 0; uint g = (data[idx & 1] & 0x0000FF00) >> 8; uint b = (data[idx & 1] & 0x00FF0000) >> 16; diff --git a/Runtime/Shader/ShellProtector.cginc b/Runtime/Shader/ShellProtector.cginc index 14758d8..e04c49a 100644 --- a/Runtime/Shader/ShellProtector.cginc +++ b/Runtime/Shader/ShellProtector.cginc @@ -24,6 +24,7 @@ float _Key0, _Key1, _Key2, _Key3, _Key4, _Key5, _Key6, _Key7, _Key8, _Key9, _Key uint _Woffset; uint _Hoffset; +uint _HashMagic; #include "Utility.cginc" @@ -48,7 +49,7 @@ uint _Hoffset; #include "DXT.cginc" #endif -half4 DecryptTexture(half2 uv, int m) +half4 DecryptTexture(Texture2D tex0, Texture2D tex1, SamplerState tex0Sampler, half2 uv, int m) { int idx = GetIndex(uv, m); const uint key[4] = @@ -60,14 +61,51 @@ half4 DecryptTexture(half2 uv, int m) }; uint data[_SHELL_PROTECTOR_DATA_LENGTH]; - GetData(data, uv, m); + GetData(tex1, tex0Sampler, data, uv, m); Decrypt(data, key); - return GetPixel(data, uv, m); + return GetPixel(tex0, tex0Sampler, data, uv, m); +} + +half4 DecryptTextureBox(Texture2D tex0, Texture2D tex1, SamplerState texSampler, half4 texSize, Texture2D mipTex, SamplerState mipSamp, half2 uv) +{ + half4 mipPixel = mipTex.Sample(mipSamp, uv); + + int mip = round(mipPixel.r * 255 / 10); //fucking precision problems + const int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 }; // max size 4k + + half4 c00 = DecryptTexture(tex0, tex1, texSampler, uv, m[mip]); + + return c00; +} + +half4 DecryptTextureBilinear(Texture2D tex0, Texture2D tex1, SamplerState texSampler, half4 texSize, Texture2D mipTex, SamplerState mipSamp, half2 uv) +{ + const half4 mipPixel = mipTex.Sample(mipSamp, uv); + const half2 uvUnit = texSize.xy; + //bilinear interpolation + const half2 uvBilinear = uv - 0.5 * uvUnit; + const int mip = round(mipPixel.r * 255 / 10); //fucking precision problems + const int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 }; // max size 4k + + half4 c00 = DecryptTexture(tex0, tex1, texSampler, uvBilinear + half2(uvUnit.x * 0, uvUnit.y * 0), m[mip]); + half4 c10 = DecryptTexture(tex0, tex1, texSampler, uvBilinear + half2(uvUnit.x * 1, uvUnit.y * 0), m[mip]); + half4 c01 = DecryptTexture(tex0, tex1, texSampler, uvBilinear + half2(uvUnit.x * 0, uvUnit.y * 1), m[mip]); + half4 c11 = DecryptTexture(tex0, tex1, texSampler, uvBilinear + half2(uvUnit.x * 1, uvUnit.y * 1), m[mip]); + + half2 f = frac(uvBilinear * texSize.zw); + + half4 c0 = lerp(c00, c10, f.x); + half4 c1 = lerp(c01, c11, f.x); + + half4 bilinear = lerp(c0, c1, f.y); + + return bilinear; } inline uint SimpleHash(int data[16]) { uint hash = 0x811C9DC5u; + hash *= _HashMagic; [unroll] for (int i = 0; i < 16; i++) From abde5f4b049e2061b2f8dc02fc7fbff5ffa866db Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:34:32 +0900 Subject: [PATCH 24/59] feat: Support liltoon --- Editor/ShellProtectorEditor.cs | 11 ++-- Editor/liltoonCustom/CustomInspector.cs | 9 ++-- Runtime/liltoonProtector/Shaders/XXTEA.cginc | 53 ------------------- .../liltoonProtector/Shaders/XXTEA.cginc.meta | 7 --- .../lilCustomShaderProperties.lilblock | 3 +- 5 files changed, 15 insertions(+), 68 deletions(-) delete mode 100644 Runtime/liltoonProtector/Shaders/XXTEA.cginc delete mode 100644 Runtime/liltoonProtector/Shaders/XXTEA.cginc.meta diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index 4483d81..5cb3cf4 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -267,8 +267,10 @@ public override void OnInspectorGUI() var sync_size_value = sync_size.intValue; int sync_size_index = 0; - int[] sync_size_caldidate = { 1, 2, 4}; - string[] selectable_values = { "1", "2", "4" }; + //int[] sync_size_caldidate = { 1, 2, 4}; + //string[] selectable_values = { "1", "2", "4" }; + int[] sync_size_caldidate = { 1 }; + string[] selectable_values = { "1" }; for (int i = 0; i < sync_size_caldidate.Length; i++) if (sync_size_caldidate[i] == sync_size_value) sync_size_index = i; @@ -278,7 +280,8 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("Sync speed"), EditorStyles.boldLabel); sync_size_index = EditorGUILayout.Popup(sync_size_index, selectable_values, GUILayout.Width(100)); sync_size.intValue = sync_size_caldidate[sync_size_index]; - GUILayout.Label(Lang("When the Sync speed is 2 or higher, OSC1.7 or higher must be used."), EditorStyles.boldLabel); + GUILayout.Label(Lang("Under development."), EditorStyles.boldLabel); + //GUILayout.Label(Lang("When the Sync speed is 2 or higher, OSC1.7 or higher must be used."), EditorStyles.boldLabel); GUILayout.Space(10); } @@ -382,7 +385,7 @@ public override void OnInspectorGUI() #if MODULAR - if (GUILayout.Button(Lang("Manual Encrypt!"))) + if (GUILayout.Button(Lang("Manual Encrypt! (for testing)"))) #else if (GUILayout.Button(Lang("Encrypt!"))) #endif diff --git a/Editor/liltoonCustom/CustomInspector.cs b/Editor/liltoonCustom/CustomInspector.cs index 8061a21..4c6933c 100644 --- a/Editor/liltoonCustom/CustomInspector.cs +++ b/Editor/liltoonCustom/CustomInspector.cs @@ -15,6 +15,7 @@ public class ShellProtectorInspector : lilToonInspector MaterialProperty encrypted_tex1; MaterialProperty[] key = new MaterialProperty[16]; MaterialProperty password_hash; + MaterialProperty hashMagic; private static bool isShowCustomProperties; private static bool show_pwd = false; @@ -36,6 +37,7 @@ protected override void LoadCustomProperties(MaterialProperty[] props, Material encrypted_tex0 = FindProperty("_EncryptTex0", props); encrypted_tex1 = FindProperty("_EncryptTex1", props); password_hash = FindProperty("_PasswordHash", props); + hashMagic = FindProperty("_HashMagic", props); for (int i = 0; i < key.Length; ++i) key[i] = FindProperty("_Key" + i, props); @@ -69,8 +71,9 @@ protected override void DrawCustomProperties(Material material) EditorGUILayout.EndVertical(); m_MaterialEditor.ShaderProperty(password_hash, "Password hash"); - - show_pwd = Foldout("Keys", "Keys", show_pwd); + m_MaterialEditor.ShaderProperty(hashMagic, "Hash Salt"); + + show_pwd = Foldout("Keys", "Keys", show_pwd); if(show_pwd) { for(int i = 0; i < key.Length; ++i) @@ -81,7 +84,7 @@ protected override void DrawCustomProperties(Material material) } } - var hash = KeyGenerator.SimpleHash(key.Select(k => (byte)Mathf.RoundToInt(k.floatValue)).ToArray()); + var hash = KeyGenerator.SimpleHash(key.Select(k => (byte)Mathf.RoundToInt(k.floatValue)).ToArray(), (uint)hashMagic.intValue); EditorGUILayout.LabelField("Password hash", hash.ToString()); } } diff --git a/Runtime/liltoonProtector/Shaders/XXTEA.cginc b/Runtime/liltoonProtector/Shaders/XXTEA.cginc deleted file mode 100644 index 2395f12..0000000 --- a/Runtime/liltoonProtector/Shaders/XXTEA.cginc +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -static const uint Delta = 0x9e3779b9; -static const uint ROUNDS = 6; -static const uint SUM = Delta * ROUNDS; - -void XXTEADecrypt(inout uint data[3], const uint key[4]) -{ - static const uint n = 3; - uint v0, v1, sum; - uint p, e; - - sum = SUM; - - v0 = data[0]; - for(int i = 0; i < ROUNDS; ++i) - { - e = (sum >> 2) & 3; - for (p = n-1; p > 0; p--) - { - v1 = data[p - 1]; - data[p] -= (((v1 >> 5 ^ v0 << 2) + (v0 >> 3 ^ v1 << 4)) ^ ((sum ^ v0) + (key[(p & 3) ^ e] ^ v1))); - v0 = data[p]; - } - v1 = data[n - 1]; - data[0] -= (((v1 >> 5 ^ v0 << 2) + (v0 >> 3 ^ v1 << 4)) ^ ((sum ^ v0) + (key[(p & 3) ^ e] ^ v1))); - v0 = data[0]; - sum -= Delta; - } -} -void XXTEADecrypt(inout uint data[2], const uint key[4]) -{ - static const uint n = 2; - uint v0, v1, sum; - uint p, e; - - sum = SUM; - - v0 = data[0]; - for(int i = 0; i < ROUNDS; ++i) - { - e = (sum >> 2) & 3; - for (p = n-1; p > 0; p--) - { - v1 = data[p - 1]; - data[p] -= (((v1 >> 5 ^ v0 << 2) + (v0 >> 3 ^ v1 << 4)) ^ ((sum ^ v0) + (key[(p & 3) ^ e] ^ v1))); - v0 = data[p]; - } - v1 = data[n - 1]; - data[0] -= (((v1 >> 5 ^ v0 << 2) + (v0 >> 3 ^ v1 << 4)) ^ ((sum ^ v0) + (key[(p & 3) ^ e] ^ v1))); - v0 = data[0]; - sum -= Delta; - } -} \ No newline at end of file diff --git a/Runtime/liltoonProtector/Shaders/XXTEA.cginc.meta b/Runtime/liltoonProtector/Shaders/XXTEA.cginc.meta deleted file mode 100644 index fc960bc..0000000 --- a/Runtime/liltoonProtector/Shaders/XXTEA.cginc.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 0b885182c32e5244a819063762959709 -ShaderIncludeImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock b/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock index 3f4ec9c..9f01063 100644 --- a/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock +++ b/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock @@ -22,4 +22,5 @@ _Nonce0 ("Nonce", integer) = 0 _Nonce1 ("Nonce", integer) = 0 _Nonce2 ("Nonce", integer) = 0 - _PasswordHash ("PasswordHash", integer) = 0 \ No newline at end of file + _PasswordHash ("PasswordHash", integer) = 0 + _HashMagic ("HashMagic", integer) = 0 \ No newline at end of file From 40c4afb88fb191423f6fe838ab5981857230c9c9 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:44:11 +0900 Subject: [PATCH 25/59] Update README --- README.ENG.md | 11 +++++++---- README.JP.md | 11 +++++++---- README.md | 11 +++++++---- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/README.ENG.md b/README.ENG.md index c1dbbe4..39e451c 100644 --- a/README.ENG.md +++ b/README.ENG.md @@ -19,8 +19,8 @@ You can easily enter the password with the OSC program. Source code of OSC: https://github.com/Shell4026/ShellProtectorOSC ## Supported shaders -- Poiyomi 7.3(Unstable), 8.0, 8.1, 8.2, 9.0, 9.1, 9.2(pro) PCSS(Need to more testing) -- lilToon (1.3.8 ~ 1.7.3)(VCC) +- Poiyomi 8.0, 8.1, 8.2, 9.0, 9.1, 9.2, 9.3(pro), PCSS(Need to more testing) +- lilToon (1.3.8 ~ 2.3.2)(VCC) ## Supported texture formats - RGB24, RGBA32 @@ -31,7 +31,7 @@ Source code of OSC: https://github.com/Shell4026/ShellProtectorOSC - Texture Encryption - OSC programs for descryption - Blendshape obfuscation -- Fallback: the ability to make non-friends see a 16x16 texture instead of encryption noise +- Fallback: the ability to make non-friends see a small texture instead of encryption noise ## Usage @@ -64,7 +64,8 @@ When using parameter multiplexing, depending on the server or network conditions In this case, try increasing the refresh rate slightly, which was added in OSC 1.5.0. ### Avatar fallback -A feature that allows anyone with Safety On when encryption is in place to appear as a degraded version of themselves when viewing your avatar. +A feature that allows anyone with Safety On when encryption is in place to appear as a degraded version of themselves when viewing your avatar.
+It appears as a fallback texture even when the encryption hasn't been decrypted. ![fallback](https://github.com/user-attachments/assets/d3ca69b0-ff08-4793-a4e4-73269bc8efd3) ## Troubleshooting @@ -130,6 +131,8 @@ Bilinear filtering: 0.35ms This may not seem like a huge difference, but for performance reasons, I recommend only encrypting textures that are absolutely necessary. +Encrypting too many parts may cause lag during the decryption process. + ## How secure is it? By default, it has 16 bytes of keys, split between keys stored inside the shader and keys that the user can enter using VRC parameters. (I'll call these user keys.) diff --git a/README.JP.md b/README.JP.md index 2bb2a18..09aa3d4 100644 --- a/README.JP.md +++ b/README.JP.md @@ -21,8 +21,8 @@ OSCプログラムで簡単にパスワードを入力することができま OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC ## 対応シェーダー -- Poiyomi 8.0, 8.1, 8.2, 9.0, 9.1, 9.2(pro), PCSS(より多くのテストが必要) -- lilToon (1.3.8 ~ 1.7.3)(VCC) +- Poiyomi 8.0, 8.1, 8.2, 9.0, 9.1, 9.2, 9.3(pro), PCSS(より多くのテストが必要) +- lilToon (1.3.8 ~ 2.3.2)(VCC) ## 対応テクスチャ形式 - RGB24, RGBA32 @@ -33,7 +33,7 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC - テクスチャ暗号化 - 暗号を解読するためのOSCプログラム - Blendshape難読化 -- フォールバック: 友達以外のユーザーには、暗号化ノイズの代わりに16x16テクスチャで見えるようにする機能。 +- フォールバック: 友達以外のユーザーには、暗号化ノイズの代わりに小さなテクスチャで見えるようにする機能。 ## 使用方法 1. アバターを右クリックして「Shell Protector」を押してコンポーネントを追加します。 @@ -65,7 +65,8 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC この場合、OSC 1.5.0で追加されたRefresh rateを少し上げてみてください。 ### アバターフォールバック -暗号化がかかっているときにSafetyがオンになっている人は、アバターを見るときに劣化したバージョンに見えるようにする機能です。 +暗号化がかかっているときにSafetyがオンになっている人は、アバターを見るときに劣化したバージョンに見えるようにする機能です。
+暗号化が解除されていない場合でもフォールバックテクスチャとして表示されます。 ![fallback](https://github.com/user-attachments/assets/d3ca69b0-ff08-4793-a4e4-73269bc8efd3) ## 問題解決 @@ -132,6 +133,8 @@ Bilinear:0.35ms それほど大きな差はないようですが、パフォーマンスを考えると、必要なテクスチャだけを暗号化することをお勧めします。 +暗号化する部分が多すぎると、復号処理中に遅延が発生する可能性があります。 + ## どの程度安全ですか 基本的に16バイトのキーを持ち、シェーダー内部に保存されるキーとユーザーがVRCパラメータを利用して入力できるキーに分かれています(ユーザーキーと呼びます)。 diff --git a/README.md b/README.md index 75a4843..dd370bb 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ OSC 프로그램으로 간편하게 비밀번호를 입력할 수 있습니다. OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC ## 지원 셰이더 -- Poiyomi 7.3(불안정), 8.0, 8.1, 8.2, 9.0, 9.1, 9.2(pro), PCSS(테스트 더 필요) -- lilToon (1.3.8 ~ 1.7.3)(VCC) +- Poiyomi 8.0, 8.1, 8.2, 9.0, 9.1, 9.2, 9.3(pro), PCSS(테스트 더 필요) +- lilToon (1.3.8 ~ 2.3.2)(VCC) ## 지원 텍스쳐 형식 - RGB24, RGBA32 @@ -31,7 +31,7 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC - 텍스쳐 암호화 - 암호화를 풀기 위한 OSC 프로그램 - 쉐이프키 난독화 -- 폴백: 친구가 아닌 유저에게는 암호화 노이즈 대신 16x16 텍스쳐로 보이게 하는 기능 +- 폴백: 친구가 아닌 유저에게는 암호화 노이즈 대신 작은 텍스쳐로 보이게 하는 기능 ## 사용법 1. 아바타를 우클릭해 'Shell Protector'를 눌러 컴포넌트를 추가합니다. @@ -63,7 +63,8 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC 이 경우 OSC 1.5.0에 추가된 Refresh rate를 조금 올려보시길 바랍니다. ### 아바타 폴백 -암호화가 걸려있을 때 세이프티가 켜져있는 사람은 아바타를 볼 때 열화된 버전으로 보이게 하는 기능입니다. +암호화가 걸려있을 때 세이프티가 켜져있는 사람은 아바타를 볼 때 열화된 버전으로 보이게 하는 기능입니다.
+암호화가 풀리지 않았을 때도 폴백 텍스쳐로 보입니다. ![fallback](https://github.com/user-attachments/assets/d3ca69b0-ff08-4793-a4e4-73269bc8efd3) ## 문제해결 @@ -131,6 +132,8 @@ Bilinear 필터링: 0.35ms 그렇게 큰 차이는 아닌 것으로 보이나, 성능을 생각하면 꼭 필요한 텍스쳐만 암호화 하는 것을 추천합니다. +너무 많은 부위를 암호화 하면 복호화 과정에서 렉이 걸리는 현상이 있을 수 있습니다. + ## 얼마나 안전한가요? 기본적으로 16바이트의 키를 가지며, 셰이더 내부에 저장되는 키와 사용자가 VRC 파라미터를 이용하여 입력할 수 있는 키로 나누어져 있습니다. (사용자 키라고 부르겠습니다.) From 073021eefb78d767258722f4d7118d5517c4f61f Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 16 Nov 2025 23:34:39 +0900 Subject: [PATCH 26/59] feat: 2.5.0 beta --- Runtime/Scripts/lang/LanguageManager.cs | 10 +++-- Runtime/liltoonProtector/Shaders/custom.hlsl | 47 +++++--------------- package.json | 2 +- version.json | 2 +- 4 files changed, 20 insertions(+), 41 deletions(-) diff --git a/Runtime/Scripts/lang/LanguageManager.cs b/Runtime/Scripts/lang/LanguageManager.cs index 9057a38..cb95e4f 100644 --- a/Runtime/Scripts/lang/LanguageManager.cs +++ b/Runtime/Scripts/lang/LanguageManager.cs @@ -66,7 +66,7 @@ private LanguageManager() { "Small mip texture", "작은 밉 텍스쳐" }, { "It uses a smaller mipTexture to reduce memory usage and improve performance. It may look slightly different from the original when viewed from the side.", "작은 밉 텍스쳐를 사용하여 메모리 사용량을 줄이고 성능을 개선합니다. 옆에서 봤을 때 원본과 약간 다르게 보일 수 있습니다."}, { "Object list", "오브젝트 목록" }, - { "Manual Encrypt!", "수동 암호화 시작!" }, + { "Manual Encrypt! (for testing)", "수동 암호화 시작! (테스트용)" }, { "Modular avatars exist. It is automatically encrypted on upload.", "모듈러 아바타가 존재합니다. 업로드 시 자동으로 암호화됩니다." }, { "Force progress", "강제 진행" }, { "Obfustactor Options", "난독화 옵션" }, @@ -89,7 +89,8 @@ private LanguageManager() { "New shader", "새로운 셰이더" }, { "The main texture is not Texture2D.", "메인 텍스쳐가 Texture2D가 아닙니다." }, { "Opponents with Safety option turned on will see degraded textures instead of noise.", "세이프티를 켜둔 상대방은 노이즈 대신 저하된 텍스처를 보게 됩니다."}, - { "Default fallback texture", "기본 폴백 텍스쳐" } + { "Default fallback texture", "기본 폴백 텍스쳐" }, + { "Under development.", "개발 중입니다." } }; var jpStrings = new Dictionary() @@ -138,7 +139,7 @@ private LanguageManager() { "Small mip texture", "小さなミップテクスチャ" }, { "It uses a smaller mipTexture to reduce memory usage and improve performance. It may look slightly different from the original when viewed from the side.", "小さなミップテクスチャを使用して、メモリ使用量を減らし、パフォーマンスを向上させます。 横から見ると、オリジナルと少し違って見えるかもしれません。"}, { "Object list", "オブジェクト一覧"}, - { "Manual Encrypt!", "手動暗号化開始!" }, + { "Manual Encrypt! (for testing)", "手動暗号化開始!(テスト用)" }, { "Modular avatars exist. it is automatically encrypted on upload.", "Modular Avatarが存在します。アップロード時に自動的に暗号化されます。" }, { "Force progress", "強制的に進行" }, { "Obfustactor Options", "難読化オプション" }, @@ -161,7 +162,8 @@ private LanguageManager() { "New shader", "新しいシェーダー" }, {"The main texture is not Texture2D.", "メインテクスチャがTexture2Dではありません。" }, { "Opponents with Safety option turned on will see degraded textures instead of noise.", "Safetyオプションをオンにした相手には、ノイズの代わりに劣化したテクスチャが表示されます。"}, - { "Default fallback texture", "デフォルトのフォールバックテクスチャ" } + { "Default fallback texture", "デフォルトのフォールバックテクスチャ" }, + { "Under development.", "開発中です。" } }; languageMap.Add("kor", koreanStrings); diff --git a/Runtime/liltoonProtector/Shaders/custom.hlsl b/Runtime/liltoonProtector/Shaders/custom.hlsl index e25fea0..e59464d 100644 --- a/Runtime/liltoonProtector/Shaders/custom.hlsl +++ b/Runtime/liltoonProtector/Shaders/custom.hlsl @@ -2,48 +2,23 @@ // Macro #define LIL_CUSTOM_PROPERTIES\ - half4 _EncryptTex0_TexelSize; \ + half4 _EncryptTex0_TexelSize;\ int _PasswordHash; // Custom textures #define LIL_CUSTOM_TEXTURES\ - SAMPLER(_MipTex); \ - TEXTURE2D(_EncryptTex0); \ - SAMPLER(sampler_EncryptTex0); \ - TEXTURE2D(_EncryptTex1); + TEXTURE2D(_MipTex);\ + TEXTURE2D(_EncryptTex0);\ + TEXTURE2D(_EncryptTex1);\ + SAMPLER(sampler_MipTex);\ + SAMPLER(sampler_EncryptTex0); + #ifdef _POINT #define CODE\ - half4 mip_texture = tex2D(_MipTex, fd.uvMain);\ - int mip = round(mip_texture.r * 255 / 10);\ - int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 };\ - \ - half4 c00 = DecryptTexture(fd.uvMain, m[mip]);\ - \ - fd.col = c00; + fd.col = DecryptTextureBox(_EncryptTex0, _EncryptTex1, sampler_EncryptTex0, _EncryptTex0_TexelSize, _MipTex, sampler_MipTex, fd.uvMain); #else #define CODE\ - half4 mip_texture = tex2D(_MipTex, fd.uvMain);\ - \ - half2 uv_unit = _EncryptTex0_TexelSize.xy;\ - half2 uv_bilinear = fd.uvMain - 0.5 * uv_unit;\ - int mip = round(mip_texture.r * 255 / 10);\ - static const int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 };\ - \ - half4 c00 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 0, uv_unit.y * 0), m[mip]);\ - half4 c10 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 1, uv_unit.y * 0), m[mip]);\ - half4 c01 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 0, uv_unit.y * 1), m[mip]);\ - half4 c11 = DecryptTexture(uv_bilinear + half2(uv_unit.x * 1, uv_unit.y * 1), m[mip]);\ - \ - half2 f = frac(uv_bilinear * _EncryptTex0_TexelSize.zw);\ - \ - half4 c0 = lerp(c00, c10, f.x);\ - half4 c1 = lerp(c01, c11, f.x);\ - \ - half4 bilinear = lerp(c0, c1, f.y);\ - \ - fd.col = bilinear;\ - LIL_APPLY_MAIN_TONECORRECTION\ - fd.col *= _Color; + fd.col = DecryptTextureBilinear(_EncryptTex0, _EncryptTex1, sampler_EncryptTex0, _EncryptTex0_TexelSize, _MipTex, sampler_MipTex, fd.uvMain); #endif #define OVERRIDE_MAIN\ @@ -57,10 +32,12 @@ else\ {\ CODE\ + LIL_APPLY_MAIN_TONECORRECTION\ + fd.col *= _Color;\ } #define OVERRIDE_MATCAP \ - lilGetMatCap(fd, _MipTex); + lilGetMatCap(fd, sampler_MipTex); #if defined(_SHELL_PROTECTOR_RIMLIGHT) #if defined(LIL_LITE) diff --git a/package.json b/package.json index 8dfe9d2..9f8ba2e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shell.protector", "displayName": "Shell Protector", - "version": "2.4.5", + "version": "2.5.0-beta", "unity": "2022.3", "description": "Texture encryption for VRChat. Save avatars from ripping", "author": { diff --git a/version.json b/version.json index 6b15fc5..cfb21f6 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "latestVersion": "2.4.5", "downloadPage": "/releases/tag/2.4.5" } \ No newline at end of file +{ "latestVersion": "2.5.0-beta", "downloadPage": "/releases/tag/2.5.0b" } \ No newline at end of file From 2a612840a5f284482708d8589643448ee6e960c2 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Mon, 17 Nov 2025 00:54:15 +0900 Subject: [PATCH 27/59] fix: Modify shader check logic --- Runtime/Scripts/CompileErrorListener.cs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Runtime/Scripts/CompileErrorListener.cs b/Runtime/Scripts/CompileErrorListener.cs index 3fda27c..482a4dc 100644 --- a/Runtime/Scripts/CompileErrorListener.cs +++ b/Runtime/Scripts/CompileErrorListener.cs @@ -14,21 +14,24 @@ static CompileErrorListener() { CompilationPipeline.assemblyCompilationFinished += (string s, CompilerMessage[] message) => { - foreach(var m in message) + foreach (var m in message) { - if(m.type == CompilerMessageType.Error) + if (m.type == CompilerMessageType.Error) { - if(!m.message.Contains("The type or namespace name 'lilToonInspector' could not be found")) - continue; - if (!m.message.Contains("The type or namespace name 'Thry' could not be found")) - continue; - if (!m.message.Contains("The name 'ShaderOptimizer' does not exist in the current context")) - continue; - AssetManager.GetInstance().ResetDefine(); - break; + if + ( + m.message.Contains("The type or namespace name 'lilToonInspector' could not be found") || + m.message.Contains("The type or namespace name 'Thry' could not be found") || + m.message.Contains("The name 'ShaderOptimizer' does not exist in the current context") + ) + { + AssetManager.GetInstance().ResetDefine(); + } + else + break; } } - + }; } } From a26108e5de5c918c0721f3fc048f026c664373c7 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Mon, 17 Nov 2025 01:03:22 +0900 Subject: [PATCH 28/59] feat: Check the retry count when the shader changed --- Runtime/Scripts/CompileErrorListener.cs | 61 +++++++++++++++++-------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/Runtime/Scripts/CompileErrorListener.cs b/Runtime/Scripts/CompileErrorListener.cs index 482a4dc..e52a585 100644 --- a/Runtime/Scripts/CompileErrorListener.cs +++ b/Runtime/Scripts/CompileErrorListener.cs @@ -1,6 +1,4 @@ #if UNITY_EDITOR -using System.Collections; -using System.Collections.Generic; using UnityEditor; using UnityEditor.Compilation; using UnityEngine; @@ -10,30 +8,53 @@ namespace Shell.Protector [InitializeOnLoad] public class CompileErrorListener { + private static int retryLimit = 3; + private static int retryCount = 0; + static CompileErrorListener() { - CompilationPipeline.assemblyCompilationFinished += (string s, CompilerMessage[] message) => + CompilationPipeline.assemblyCompilationFinished += OnAssemblyCompilationFinished; + } + + private static void OnAssemblyCompilationFinished(string assembly, CompilerMessage[] messages) + { + bool foundTargetError = false; + + foreach (var m in messages) { - foreach (var m in message) + if (m.type != CompilerMessageType.Error) + continue; + + if ( + m.message.Contains("The type or namespace name 'lilToonInspector' could not be found") || + m.message.Contains("The type or namespace name 'Thry' could not be found") || + m.message.Contains("The name 'ShaderOptimizer' does not exist in the current context") + ) { - if (m.type == CompilerMessageType.Error) - { - if - ( - m.message.Contains("The type or namespace name 'lilToonInspector' could not be found") || - m.message.Contains("The type or namespace name 'Thry' could not be found") || - m.message.Contains("The name 'ShaderOptimizer' does not exist in the current context") - ) - { - AssetManager.GetInstance().ResetDefine(); - } - else - break; - } + foundTargetError = true; + break; } + } - }; + if (foundTargetError) + { + retryCount++; + + if (retryCount <= retryLimit) + { + Debug.LogErrorFormat("Shader check error -> ResetDefine {0}/{0}", retryCount, retryLimit); + AssetManager.GetInstance().ResetDefine(); + } + else + { + Debug.LogError("Failed ResetDefine"); + } + } + else + { + retryCount = 0; + } } } } -#endif \ No newline at end of file +#endif From d6895a3e1ebb0e551700e74a63a1d67bec246f4e Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:57:50 +0900 Subject: [PATCH 29/59] refact: Change variable names from snake_case to camelCase --- Editor/ShellProtectorEditor.cs | 132 ++++++++-------- Editor/TesterEditor.cs | 4 +- Runtime/Scripts/ShellProtector.cs | 190 ++++++++++++------------ Runtime/Scripts/ShellProtectorTester.cs | 2 +- 4 files changed, 164 insertions(+), 164 deletions(-) diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index 5cb3cf4..db1b7f0 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -19,19 +19,19 @@ public class ShellProtectorEditor : Editor ShellProtector root = null; readonly LanguageManager lang = LanguageManager.GetInstance(); - ReorderableList game_object_list; - ReorderableList material_list; - ReorderableList texture_list; + ReorderableList gameobjectList; + ReorderableList materialList; + ReorderableList textureList; ReorderableList obfuscationList; SerializedProperty rounds; SerializedProperty filter; SerializedProperty fallback; SerializedProperty algorithm; - SerializedProperty key_size; - SerializedProperty key_size_idx; - SerializedProperty sync_size; - SerializedProperty delete_folders; + SerializedProperty keySize; + SerializedProperty keySizeIdx; + SerializedProperty syncSize; + SerializedProperty deleteFolders; SerializedProperty bUseSmallMipTexture; SerializedProperty bPreserveMMD; SerializedProperty turnOnAllSafetyFallback; @@ -67,29 +67,29 @@ void OnEnable() MonoScript monoScript = MonoScript.FromMonoBehaviour(root); string script_path = AssetDatabase.GetAssetPath(monoScript); - root.asset_dir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); + root.assetDir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); - game_object_list = new ReorderableList(serializedObject, serializedObject.FindProperty("gameobject_list"), true, true, true, true); - game_object_list.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Object list")); - game_object_list.drawElementCallback = (rect, index, is_active, is_focused) => + gameobjectList = new ReorderableList(serializedObject, serializedObject.FindProperty("gameobjectList"), true, true, true, true); + gameobjectList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Object list")); + gameobjectList.drawElementCallback = (rect, index, is_active, is_focused) => { - SerializedProperty element = game_object_list.serializedProperty.GetArrayElementAtIndex(index); + SerializedProperty element = gameobjectList.serializedProperty.GetArrayElementAtIndex(index); EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none); }; - material_list = new ReorderableList(serializedObject, serializedObject.FindProperty("material_list"), true, true, true, true); - material_list.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Material List")); - material_list.drawElementCallback = (rect, index, is_active, is_focused) => + materialList = new ReorderableList(serializedObject, serializedObject.FindProperty("materialList"), true, true, true, true); + materialList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Material List")); + materialList.drawElementCallback = (rect, index, is_active, is_focused) => { - SerializedProperty element = material_list.serializedProperty.GetArrayElementAtIndex(index); + SerializedProperty element = materialList.serializedProperty.GetArrayElementAtIndex(index); EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none); }; - texture_list = new ReorderableList(serializedObject, serializedObject.FindProperty("texture_list"), true, true, true, true); - texture_list.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Texture List")); - texture_list.drawElementCallback = (rect, index, is_active, is_focused) => + textureList = new ReorderableList(serializedObject, serializedObject.FindProperty("textureList"), true, true, true, true); + textureList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Texture List")); + textureList.drawElementCallback = (rect, index, is_active, is_focused) => { - SerializedProperty element = texture_list.serializedProperty.GetArrayElementAtIndex(index); + SerializedProperty element = textureList.serializedProperty.GetArrayElementAtIndex(index); EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none); }; @@ -106,10 +106,10 @@ void OnEnable() filter = serializedObject.FindProperty("filter"); fallback = serializedObject.FindProperty("fallback"); algorithm = serializedObject.FindProperty("algorithm"); - key_size = serializedObject.FindProperty("key_size"); - key_size_idx = serializedObject.FindProperty("key_size_idx"); - sync_size = serializedObject.FindProperty("sync_size"); - delete_folders = serializedObject.FindProperty("delete_folders"); + keySize = serializedObject.FindProperty("keySize"); + keySizeIdx = serializedObject.FindProperty("keySizeIdx"); + syncSize = serializedObject.FindProperty("syncSize"); + deleteFolders = serializedObject.FindProperty("deleteFolders"); bUseSmallMipTexture = serializedObject.FindProperty("bUseSmallMipTexture"); bPreserveMMD = serializedObject.FindProperty("bPreserveMMD"); turnOnAllSafetyFallback = serializedObject.FindProperty("turnOnAllSafetyFallback"); @@ -150,7 +150,7 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("Languages: ")); GUILayout.FlexibleSpace(); - root.lang_idx = EditorGUILayout.Popup(root.lang_idx, languages, GUILayout.Width(100)); + root.langIdx = EditorGUILayout.Popup(root.langIdx, languages, GUILayout.Width(100)); key_lengths[0] = Lang("0 (Minimal security)"); key_lengths[1] = Lang("4 (Low security)"); @@ -158,7 +158,7 @@ public override void OnInspectorGUI() key_lengths[3] = Lang("12 (Hight security)"); key_lengths[4] = Lang("16 (Unbreakable security)"); - switch (root.lang_idx) + switch (root.langIdx) { case 0: root.lang = "eng"; @@ -186,9 +186,9 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("Password"), EditorStyles.boldLabel); - if (key_size.intValue < 16) + if (keySize.intValue < 16) { - int length = 16 - key_size.intValue; + int length = 16 - keySize.intValue; GUILayout.BeginHorizontal(); root.pwd = GUILayout.TextField(root.pwd, length, GUILayout.Width(100)); if (GUILayout.Button(Lang("Generate"))) @@ -197,17 +197,17 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("A password that you don't need to memorize. (max:") + length + ")", EditorStyles.wordWrappedLabel); GUILayout.EndHorizontal(); } - if (key_size.intValue > 0) + if (keySize.intValue > 0) { GUILayout.BeginHorizontal(); if(!show_pwd) - root.pwd2 = GUILayout.PasswordField(root.pwd2, '*', key_size.intValue, GUILayout.Width(100)); + root.pwd2 = GUILayout.PasswordField(root.pwd2, '*', keySize.intValue, GUILayout.Width(100)); else - root.pwd2 = GUILayout.TextField(root.pwd2, key_size.intValue, GUILayout.Width(100)); + root.pwd2 = GUILayout.TextField(root.pwd2, keySize.intValue, GUILayout.Width(100)); if (GUILayout.Button(Lang("Show"))) show_pwd = !show_pwd; GUILayout.FlexibleSpace(); - GUILayout.Label(Lang("This password should be memorized. (max:") + key_size.intValue + ")", EditorStyles.wordWrappedLabel); + GUILayout.Label(Lang("This password should be memorized. (max:") + keySize.intValue + ")", EditorStyles.wordWrappedLabel); GUILayout.EndHorizontal(); } int free_parameter = -1; @@ -225,13 +225,13 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("Free parameter:") + free_parameter, EditorStyles.wordWrappedLabel); } int lock_size = 1; - int switch_count = ShellProtector.GetRequiredSwitchCount(key_size.intValue, sync_size.intValue); - int using_parameter = switch_count + lock_size + sync_size.intValue * 8; + int switch_count = ShellProtector.GetRequiredSwitchCount(keySize.intValue, syncSize.intValue); + int using_parameter = switch_count + lock_size + syncSize.intValue * 8; GUILayout.Label(Lang("Parameters to be used:") + using_parameter, EditorStyles.wordWrappedLabel); serializedObject.Update(); - game_object_list.DoLayoutList(); - material_list.DoLayoutList(); + gameobjectList.DoLayoutList(); + materialList.DoLayoutList(); GUILayout.Label(Lang("Encrypting too many objects can cause lag when loading avatars in-game.")); if(GUILayout.Button(Lang("Material advanced settings"))) { @@ -243,43 +243,43 @@ public override void OnInspectorGUI() if(option) { GUILayout.Label(Lang("Max password length"), EditorStyles.boldLabel); - key_size_idx.intValue = EditorGUILayout.Popup(key_size_idx.intValue, key_lengths, GUILayout.Width(150)); + keySizeIdx.intValue = EditorGUILayout.Popup(keySizeIdx.intValue, key_lengths, GUILayout.Width(150)); GUILayout.Space(10); - switch (key_size_idx.intValue) + switch (keySizeIdx.intValue) { case 0: - key_size.intValue = 0; + keySize.intValue = 0; break; case 1: - key_size.intValue = 4; + keySize.intValue = 4; break; case 2: - key_size.intValue = 8; + keySize.intValue = 8; break; case 3: - key_size.intValue = 12; + keySize.intValue = 12; break; case 4: - key_size.intValue = 16; + keySize.intValue = 16; break; } - var sync_size_value = sync_size.intValue; - int sync_size_index = 0; - //int[] sync_size_caldidate = { 1, 2, 4}; + var syncSize_value = syncSize.intValue; + int syncSize_index = 0; + //int[] syncSize_caldidate = { 1, 2, 4}; //string[] selectable_values = { "1", "2", "4" }; - int[] sync_size_caldidate = { 1 }; + int[] syncSize_caldidate = { 1 }; string[] selectable_values = { "1" }; - for (int i = 0; i < sync_size_caldidate.Length; i++) - if (sync_size_caldidate[i] == sync_size_value) - sync_size_index = i; + for (int i = 0; i < syncSize_caldidate.Length; i++) + if (syncSize_caldidate[i] == syncSize_value) + syncSize_index = i; - if(key_size.intValue > 0) + if(keySize.intValue > 0) { GUILayout.Label(Lang("Sync speed"), EditorStyles.boldLabel); - sync_size_index = EditorGUILayout.Popup(sync_size_index, selectable_values, GUILayout.Width(100)); - sync_size.intValue = sync_size_caldidate[sync_size_index]; + syncSize_index = EditorGUILayout.Popup(syncSize_index, selectable_values, GUILayout.Width(100)); + syncSize.intValue = syncSize_caldidate[syncSize_index]; GUILayout.Label(Lang("Under development."), EditorStyles.boldLabel); //GUILayout.Label(Lang("When the Sync speed is 2 or higher, OSC1.7 or higher must be used."), EditorStyles.boldLabel); GUILayout.Space(10); @@ -324,7 +324,7 @@ public override void OnInspectorGUI() GUILayout.BeginHorizontal(); GUILayout.Label(Lang("Delete folders that already exists when at creation time"), EditorStyles.boldLabel); - delete_folders.boolValue = EditorGUILayout.Toggle(delete_folders.boolValue); + deleteFolders.boolValue = EditorGUILayout.Toggle(deleteFolders.boolValue); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); @@ -380,7 +380,7 @@ public override void OnInspectorGUI() GUILayout.EndHorizontal(); GUI.enabled = forceProgress; } - if (game_object_list.count == 0 && material_list.count == 0) + if (gameobjectList.count == 0 && materialList.count == 0) GUI.enabled = false; @@ -414,31 +414,31 @@ public override void OnInspectorGUI() Test.ChachaTest(root.pwd, root.pwd2, root.GetKeySize()); GUILayout.Space(10); - texture_list.DoLayoutList(); + textureList.DoLayoutList(); GUILayout.BeginHorizontal(); if (GUILayout.Button(Lang("Encrypt"))) { Texture2D last = null; - for (int i = 0; i < texture_list.count; i++) + for (int i = 0; i < textureList.count; i++) { - SerializedProperty element = texture_list.serializedProperty.GetArrayElementAtIndex(i); + SerializedProperty element = textureList.serializedProperty.GetArrayElementAtIndex(i); Texture2D texture = element.objectReferenceValue as Texture2D; TextureSettings.SetRWEnableTexture(texture); - var result = TextureEncryptManager.EncryptTexture(texture, KeyGenerator.MakeKeyBytes(root.pwd, root.pwd2, key_size.intValue), new XXTEA()); + var result = TextureEncryptManager.EncryptTexture(texture, KeyGenerator.MakeKeyBytes(root.pwd, root.pwd2, keySize.intValue), new XXTEA()); last = result.Texture1; - if (!AssetDatabase.IsValidFolder(root.asset_dir + '/' + root.descriptor.gameObject.name)) - AssetDatabase.CreateFolder(root.asset_dir, root.descriptor.gameObject.name); - if (!AssetDatabase.IsValidFolder(root.asset_dir + '/' + root.descriptor.gameObject.name + "/mat")) - AssetDatabase.CreateFolder(root.asset_dir + '/' + root.descriptor.gameObject.name, "mat"); + if (!AssetDatabase.IsValidFolder(root.assetDir + '/' + root.descriptor.gameObject.name)) + AssetDatabase.CreateFolder(root.assetDir, root.descriptor.gameObject.name); + if (!AssetDatabase.IsValidFolder(root.assetDir + '/' + root.descriptor.gameObject.name + "/mat")) + AssetDatabase.CreateFolder(root.assetDir + '/' + root.descriptor.gameObject.name, "mat"); - AssetDatabase.CreateAsset(result.Texture1, root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.asset"); - File.WriteAllBytes(root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.png", result.Texture2.EncodeToPNG()); + AssetDatabase.CreateAsset(result.Texture1, root.assetDir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.asset"); + File.WriteAllBytes(root.assetDir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.png", result.Texture2.EncodeToPNG()); if (result.Texture2 != null) - AssetDatabase.CreateAsset(result.Texture2, root.asset_dir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt2.asset"); + AssetDatabase.CreateAsset(result.Texture2, root.assetDir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt2.asset"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); diff --git a/Editor/TesterEditor.cs b/Editor/TesterEditor.cs index 692cb3e..26aa355 100644 --- a/Editor/TesterEditor.cs +++ b/Editor/TesterEditor.cs @@ -47,8 +47,8 @@ public override void OnInspectorGUI() GUILayout.Space(10); - root.lang_idx = EditorGUILayout.Popup(root.lang_idx, languages, GUILayout.Width(100)); - switch (root.lang_idx) + root.langIdx = EditorGUILayout.Popup(root.langIdx, languages, GUILayout.Width(100)); + switch (root.langIdx) { case 0: root.lang = "eng"; diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 9519dfd..dc50a07 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -26,16 +26,16 @@ namespace Shell.Protector public class ShellProtector : MonoBehaviour, IEditorOnly { [SerializeField] - List gameobject_list = new List(); + List gameobjectList = new List(); [SerializeField] - List material_list = new List(); + List materialList = new List(); [SerializeField] - List texture_list = new List(); + List textureList = new List(); [SerializeField] List obfuscationRenderers = new List(); Injector injector; - AssetManager shader_manager = AssetManager.GetInstance(); + AssetManager shaderManager = AssetManager.GetInstance(); bool init = false; enum Algorithm @@ -44,10 +44,10 @@ enum Algorithm chacha = 1 } - public string asset_dir = "Assets/ShellProtect"; + public string assetDir = "Assets/ShellProtect"; public string pwd = "password"; // fixed password public string pwd2 = "pass"; // user password - public int lang_idx = 0; + public int langIdx = 0; public string lang = "kor"; public VRCAvatarDescriptor descriptor; @@ -97,10 +97,10 @@ struct OtherTextures [SerializeField] int filter = 1; [SerializeField] int fallback = 5; [SerializeField] int algorithm = 1; - [SerializeField] int key_size_idx = 3; - [SerializeField] int key_size = 12; - [SerializeField] int sync_size = 1; - [SerializeField] bool delete_folders = true; + [SerializeField] int keySizeIdx = 3; + [SerializeField] int keySize = 12; + [SerializeField] int syncSize = 1; + [SerializeField] bool deleteFolders = true; [SerializeField] bool bUseSmallMipTexture = true; [SerializeField] bool bPreserveMMD = true; @@ -159,7 +159,7 @@ public void SaveMatOption() public byte[] GetKeyBytes() { - return KeyGenerator.MakeKeyBytes(pwd, pwd2, key_size); + return KeyGenerator.MakeKeyBytes(pwd, pwd2, keySize); } public GameObject DuplicateAvatar(GameObject avatar) @@ -172,9 +172,9 @@ public GameObject DuplicateAvatar(GameObject avatar) bool ConditionCheck(Material mat) { - if (shader_manager.IsPoiyomi(mat.shader)) + if (shaderManager.IsPoiyomi(mat.shader)) { - if (!shader_manager.IsLockPoiyomi(mat.shader)) + if (!shaderManager.IsLockPoiyomi(mat.shader)) { #if POIYOMI ShaderOptimizer.SetLockedForAllMaterials(new[] { mat }, 1, true); @@ -232,31 +232,31 @@ bool CheckIsSupportedFormat(Material mat) public void CreateFolders() { - if (!AssetDatabase.IsValidFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString()))) + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()))) { - AssetDatabase.CreateFolder(asset_dir, descriptor.gameObject.GetInstanceID().ToString()); + AssetDatabase.CreateFolder(assetDir, descriptor.gameObject.GetInstanceID().ToString()); } else { - if (delete_folders) + if (deleteFolders) { - AssetDatabase.DeleteAsset(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "animations")); - AssetDatabase.DeleteAsset(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "mat")); - AssetDatabase.DeleteAsset(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "shader")); - AssetDatabase.DeleteAsset(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "tex")); + AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations")); + AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "mat")); + AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "shader")); + AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "tex")); } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); - if (!AssetDatabase.IsValidFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "tex"))) - AssetDatabase.CreateFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString()), "tex"); - if (!AssetDatabase.IsValidFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "mat"))) - AssetDatabase.CreateFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString()), "mat"); - if (!AssetDatabase.IsValidFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "shader"))) - AssetDatabase.CreateFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString()), "shader"); - if (!AssetDatabase.IsValidFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "animations"))) - AssetDatabase.CreateFolder(Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString()), "animations"); + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "tex"))) + AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "tex"); + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "mat"))) + AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "mat"); + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "shader"))) + AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "shader"); + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"))) + AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "animations"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); @@ -265,7 +265,7 @@ public void CreateFolders() public List GetMaterials() { List materials = new List(); - foreach (GameObject g in gameobject_list) + foreach (GameObject g in gameobjectList) { if (g == null) continue; @@ -295,7 +295,7 @@ public List GetMaterials() } } - return materials.Concat(material_list).Distinct().ToList(); + return materials.Concat(materialList).Distinct().ToList(); } Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) { @@ -473,7 +473,7 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(originalTex); newMat.SetInteger("_Woffset", woffset); newMat.SetInteger("_Hoffset", hoffset); - for (int i = 0; i < 16 - key_size; ++i) + for (int i = 0; i < 16 - keySize; ++i) newMat.SetFloat("_Key" + i, keyBytes[i]); if (algorithm == (int)Algorithm.chacha) @@ -522,15 +522,15 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) MonoScript monoScript = MonoScript.FromMonoBehaviour(this); string script_path = AssetDatabase.GetAssetPath(monoScript); - asset_dir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); - string avatarDir = Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString()); + assetDir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); + string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); - Debug.Log("AssetDir: " + asset_dir); + Debug.Log("AssetDir: " + assetDir); if (fallbackWhite == null) - fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(asset_dir, "white.png"), typeof(Texture2D)) as Texture2D; + fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "white.png"), typeof(Texture2D)) as Texture2D; if (fallbackBlack == null) - fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(asset_dir, "black.png"), typeof(Texture2D)) as Texture2D; + fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "black.png"), typeof(Texture2D)) as Texture2D; if (descriptor == null) { @@ -566,7 +566,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) Debug.LogError("Cannot create duplicated avatar!"); return null; } - byte[] key_bytes = GetKeyBytes(); + byte[] keyBytes = GetKeyBytes(); CreateFolders(); @@ -581,7 +581,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) else if(algorithm == (int)Algorithm.chacha) { Chacha20 chacha = new Chacha20(); - byte[] hash1 = KeyGenerator.GetKeyHash(key_bytes, KeyGenerator.GenerateRandomString(chacha.nonce.Length)); + byte[] hash1 = KeyGenerator.GetKeyHash(keyBytes, KeyGenerator.GenerateRandomString(chacha.nonce.Length)); Array.Copy(hash1, 0, chacha.nonce, 0, chacha.nonce.Length); encryptor = chacha; } @@ -589,11 +589,11 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) if (history == null) { - history = AssetDatabase.LoadAssetAtPath(Path.Combine(asset_dir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; + history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; if (history == null) { history = new EncryptedHistory(); - AssetDatabase.CreateAsset(history, Path.Combine(asset_dir, "EncryptedHistory.asset")); + AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); } } history.LoadData(); @@ -636,8 +636,8 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) Debug.LogFormat("{0} : Start encrypt...", mat.name); - Texture2D main_texture = (Texture2D)mat.mainTexture; - injector.Init(descriptor.gameObject, main_texture, key_bytes, key_size, filter, asset_dir, encryptor); + Texture2D mainTexture = (Texture2D)mat.mainTexture; + injector.Init(descriptor.gameObject, mainTexture, keyBytes, keySize, filter, assetDir, encryptor); int mipRefSize = Math.Max(mat.mainTexture.width, mat.mainTexture.height); if (!mips.ContainsKey(mipRefSize)) @@ -647,13 +647,13 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) mips.Add(mipRefSize, mipRef); } - TextureSettings.SetRWEnableTexture(main_texture); - TextureSettings.SetCrunchCompression(main_texture, false); - TextureSettings.SetGenerateMipmap(main_texture, true); + TextureSettings.SetRWEnableTexture(mainTexture); + TextureSettings.SetCrunchCompression(mainTexture, false); + TextureSettings.SetGenerateMipmap(mainTexture, true); - string encrypted_shader_path = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); + string encryptedShader_path = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); - var processedTextureResult = GenerateEncryptedTexture(avatarDir, mat, encryptor, key_bytes); + var processedTextureResult = GenerateEncryptedTexture(avatarDir, mat, encryptor, keyBytes); if (!processedTextureResult.HasValue) continue; ProcessedTexture processedTexture = processedTextureResult.Value; @@ -663,24 +663,24 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) //////////////////////Inject shader/////////////////////// OtherTextures otherTex = GetLimOutlineTextures(mat); - Shader encrypted_shader = IsEncryptedBefore(mat.shader); - if (encrypted_shader == null) + Shader encryptedShader = IsEncryptedBefore(mat.shader); + if (encryptedShader == null) { try { - encrypted_shader = injector.Inject( + encryptedShader = injector.Inject( mat, - Path.Combine(asset_dir, "Shader/ShellProtector.cginc"), - encrypted_shader_path, + Path.Combine(assetDir, "Shader/ShellProtector.cginc"), + encryptedShader_path, encryptedTex1, otherTex.limTexture != null, otherTex.limTexture2 != null, otherTex.outlineTexture != null ); - Selection.activeObject = encrypted_shader; + Selection.activeObject = encryptedShader; EditorApplication.ExecuteMenuItem("Assets/Reimport"); - if (encrypted_shader == null) + if (encryptedShader == null) { Debug.LogErrorFormat("{0}: Injection failed", mat.name); continue; @@ -694,24 +694,24 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) } } - string fallbackDir = Path.Combine(avatarDir, "tex", main_texture.GetInstanceID() + "_fallback.asset"); - Texture2D fallback = GenerateFallbackTexture(fallbackDir, option, main_texture, ref processedTexture); + string fallbackDir = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_fallback.asset"); + Texture2D fallback = GenerateFallbackTexture(fallbackDir, option, mainTexture, ref processedTexture); if (fallback == null) - Debug.LogErrorFormat("Failed to generate fallback texture: {0}", main_texture.name); + Debug.LogErrorFormat("Failed to generate fallback texture: {0}", mainTexture.name); - int maxSize = Math.Max(main_texture.width, main_texture.height); + int maxSize = Math.Max(mainTexture.width, mainTexture.height); Texture2D mipTex = mips[maxSize]; if (mipTex == null) Debug.LogWarningFormat("mip_{0} is not exsist", maxSize); string encryptedMatPath = Path.Combine(avatarDir, "mat", mat.GetInstanceID() + "_encrypted.mat"); - GenerateEncryptedMaterial(encryptedMatPath, mat, encrypted_shader, fallback, mipTex, otherTex, processedTexture, key_bytes, encryptor); + GenerateEncryptedMaterial(encryptedMatPath, mat, encryptedShader, fallback, mipTex, otherTex, processedTexture, keyBytes, encryptor); } // Material loop EditorUtility.ClearProgressBar(); ///////////////////////parameter//////////////////// var av3 = avatar.GetComponent(); - av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, key_size, sync_size); + av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, keySize, syncSize); AssetDatabase.CreateAsset(av3.expressionParameters, Path.Combine(avatarDir, av3.expressionParameters.name + ".asset")); //////////////////////////////////////////////////// if (!isModular) @@ -724,16 +724,16 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) var newDesriptor = avatar.transform.GetComponentInChildren(true).gameObject; var tester = newDesriptor.AddComponent(); tester.lang = lang; - tester.lang_idx = lang_idx; + tester.langIdx = langIdx; tester.protector = this; - tester.userKeyLength = key_size; + tester.userKeyLength = keySize; Selection.activeObject = tester; #if MODULAR var maMergeAnims = avatar.GetComponentsInChildren(true); foreach (var maMergeAnim in maMergeAnims) { - AnimatorController newAnim = AnimatorManager.DuplicateAnimator(maMergeAnim.animator, Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString())); + AnimatorController newAnim = AnimatorManager.DuplicateAnimator(maMergeAnim.animator, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); maMergeAnim.animator = newAnim; } #endif @@ -797,7 +797,7 @@ public void ReplaceMaterials(GameObject avatar) OtherTextures GetLimOutlineTextures(Material mat) { OtherTextures others = new OtherTextures(); - if (shader_manager.IsPoiyomi(mat.shader)) + if (shaderManager.IsPoiyomi(mat.shader)) { var tex_properties = mat.GetTexturePropertyNames(); foreach (var t in tex_properties) @@ -810,7 +810,7 @@ OtherTextures GetLimOutlineTextures(Material mat) others.outlineTexture = (Texture2D)mat.GetTexture(t); } } - else if (shader_manager.IslilToon(mat.shader)) + else if (shaderManager.IslilToon(mat.shader)) { var tex_properties = mat.GetTexturePropertyNames(); foreach (var t in tex_properties) @@ -827,7 +827,7 @@ OtherTextures GetLimOutlineTextures(Material mat) } public void RemoveDuplicatedTextures(GameObject avatar) { - string avatarDir = Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString()); + string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); foreach (var mat in encryptedMaterials.Values) { OtherTextures otherTex = GetLimOutlineTextures(mat); @@ -850,9 +850,9 @@ public void RemoveDuplicatedTextures(GameObject avatar) if (otherTex.limTexture != null) { string texName = ""; - if (shader_manager.IsPoiyomi(mat.shader)) + if (shaderManager.IsPoiyomi(mat.shader)) texName = "_RimTex"; - else if (shader_manager.IslilToon(mat.shader)) + else if (shaderManager.IslilToon(mat.shader)) texName = "_RimColorTex"; if (mainTexture == otherTex.limTexture) @@ -864,7 +864,7 @@ public void RemoveDuplicatedTextures(GameObject avatar) if (otherTex.limTexture2 != null) //only poiyomi { string texName = ""; - if (shader_manager.IsPoiyomi(mat.shader)) + if (shaderManager.IsPoiyomi(mat.shader)) texName = "_Rim2Tex"; if (mainTexture == otherTex.limTexture2) @@ -875,9 +875,9 @@ public void RemoveDuplicatedTextures(GameObject avatar) if (otherTex.outlineTexture != null) { string texName = ""; - if (shader_manager.IsPoiyomi(mat.shader)) + if (shaderManager.IsPoiyomi(mat.shader)) texName = "_OutlineTexture"; - else if (shader_manager.IslilToon(mat.shader)) + else if (shaderManager.IslilToon(mat.shader)) texName = "_OutlineTex"; if (mainTexture == otherTex.outlineTexture) @@ -944,12 +944,12 @@ public void RemoveDuplicatedTextures(GameObject avatar) renderers[i].sharedMaterials = mats; } } - var skinned_renderers = avatar.GetComponentsInChildren(true); - if (skinned_renderers != null) + var skinnedRenderers = avatar.GetComponentsInChildren(true); + if (skinnedRenderers != null) { - for (int i = 0; i < skinned_renderers.Length; ++i) + for (int i = 0; i < skinnedRenderers.Length; ++i) { - var mats = skinned_renderers[i].sharedMaterials; + var mats = skinnedRenderers[i].sharedMaterials; if (mats == null) continue; for (int j = 0; j < mats.Length; ++j) @@ -989,7 +989,7 @@ public void RemoveDuplicatedTextures(GameObject avatar) if (tmp != null) mats[j] = tmp; } - skinned_renderers[i].sharedMaterials = mats; + skinnedRenderers[i].sharedMaterials = mats; } } } @@ -998,17 +998,17 @@ public void SetAnimations(GameObject avatar, bool clone) var av3 = avatar.GetComponent(); AnimatorController fx; if (clone) - fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString())); + fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); else fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; av3.baseAnimationLayers[4].animatorController = fx; - string animationDir = Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); + string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); GameObject[] meshArray = new GameObject[meshes.Count]; meshes.CopyTo(meshArray); - AnimatorManager.CreateKeyAniamtions(Path.Combine(asset_dir, "Animations"), animationDir, meshArray); - AnimatorManager.AddKeyLayer(fx, animationDir, key_size, sync_size, 3.0f); + AnimatorManager.CreateKeyAniamtions(Path.Combine(assetDir, "Animations"), animationDir, meshArray); + AnimatorManager.AddKeyLayer(fx, animationDir, keySize, syncSize, 3.0f); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); @@ -1023,7 +1023,7 @@ public void ChangeMaterialsInAnims(GameObject avatar, bool clone) { var av3 = avatar.GetComponent(); var fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; - string animationDir = Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); + string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); AnimatorManager animManager = new AnimatorManager(); foreach (var pair in encryptedMaterials) @@ -1070,7 +1070,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) // bClone true = Manual encrypt var av3 = avatar.GetComponent(); AnimatorController fx = Getfx(avatar); - string animDir = Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); + string animDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); Obfuscator obfuscator = new Obfuscator(); obfuscator.clone = bClone; @@ -1113,7 +1113,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) Debug.LogErrorFormat("{0} haven't mesh", renderer.transform.name); continue; } - Mesh newMesh = obfuscator.ObfuscateBlendShapeMesh(mesh, Path.Combine(asset_dir, descriptor.gameObject.GetInstanceID().ToString())); + Mesh newMesh = obfuscator.ObfuscateBlendShapeMesh(mesh, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); selectRenderer.sharedMesh = newMesh; ////////Change renderer component shape keys//////// @@ -1180,14 +1180,14 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) public int GetEncyryptedFoldersCount() { - if (!Directory.Exists(asset_dir)) + if (!Directory.Exists(assetDir)) { - Debug.LogError($"The specified path does not exist: {asset_dir}"); + Debug.LogError($"The specified path does not exist: {assetDir}"); return 0; } else { - string[] directories = Directory.GetDirectories(asset_dir); + string[] directories = Directory.GetDirectories(assetDir); int deletedCount = 0; foreach (string dir in directories) @@ -1203,15 +1203,15 @@ public int GetEncyryptedFoldersCount() } public void CleanEncrypted() { - AssetDatabase.DeleteAsset(Path.Combine(asset_dir, "EncryptedHistory.asset")); + AssetDatabase.DeleteAsset(Path.Combine(assetDir, "EncryptedHistory.asset")); - if (!Directory.Exists(asset_dir)) + if (!Directory.Exists(assetDir)) { - Debug.LogError($"The specified path does not exist: {asset_dir}"); + Debug.LogError($"The specified path does not exist: {assetDir}"); } else { - string[] directories = Directory.GetDirectories(asset_dir); + string[] directories = Directory.GetDirectories(assetDir); int deletedCount = 0; foreach (string dir in directories) @@ -1247,7 +1247,7 @@ public int GetDefaultFallback() } public int GetKeySize() { - return key_size; + return keySize; } public void ResetMaterialOptions() @@ -1260,20 +1260,20 @@ public Shader IsEncryptedBefore(Shader shader) { if (history == null) { - history = AssetDatabase.LoadAssetAtPath(Path.Combine(asset_dir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; + history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; if (history == null) { history = new EncryptedHistory(); - AssetDatabase.CreateAsset(history, Path.Combine(asset_dir, "EncryptedHistory.asset")); + AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); } } history.LoadData(); return history.IsEncryptedBefore(shader); } - public static int GetRequiredSwitchCount(int key_length, int sync_size) + public static int GetRequiredSwitchCount(int key_length, int syncSize) { - key_length /= sync_size; + key_length /= syncSize; return Mathf.CeilToInt(Mathf.Log(key_length, 2)); } } diff --git a/Runtime/Scripts/ShellProtectorTester.cs b/Runtime/Scripts/ShellProtectorTester.cs index 9c32e86..d503621 100644 --- a/Runtime/Scripts/ShellProtectorTester.cs +++ b/Runtime/Scripts/ShellProtectorTester.cs @@ -10,7 +10,7 @@ namespace Shell.Protector public class ShellProtectorTester : MonoBehaviour, IEditorOnly { public string lang = "eng"; - public int lang_idx = 0; + public int langIdx = 0; public int userKeyLength = 4; public ShellProtector protector; From a1d1538b58cf659312d9c8d5434faab005d3a7e7 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:15:26 +0900 Subject: [PATCH 30/59] refact: Move private functions below public functions --- Runtime/Scripts/ShellProtector.cs | 1761 ++++++++++++++--------------- 1 file changed, 877 insertions(+), 884 deletions(-) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index dc50a07..358530d 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -119,7 +119,7 @@ public void Init() if (init) return; - HashSet rednererSet = new HashSet(); + HashSet rendererSet = new HashSet(); Transform child = descriptor.transform.Find("Body"); if (child != null) { @@ -129,12 +129,12 @@ public void Init() Mesh mesh = renderer.sharedMesh; if (mesh != null) { - rednererSet.Add(renderer); + rendererSet.Add(renderer); } } } - foreach (var renderer in rednererSet) + foreach (var renderer in rendererSet) { obfuscationRenderers.Add(renderer); } @@ -170,1111 +170,1104 @@ public GameObject DuplicateAvatar(GameObject avatar) return cpy; } - bool ConditionCheck(Material mat) + public GameObject Encrypt(bool isModular = true) { - if (shaderManager.IsPoiyomi(mat.shader)) + return Encrypt(bUseSmallMipTexture, isModular); + } + + public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) + { + meshes.Clear(); + encryptedMaterials.Clear(); + processedTextures.Clear(); + + SyncMatOption(); + + MonoScript monoScript = MonoScript.FromMonoBehaviour(this); + string script_path = AssetDatabase.GetAssetPath(monoScript); + assetDir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); + string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); + + Debug.Log("AssetDir: " + assetDir); + + if (fallbackWhite == null) + fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "white.png"), typeof(Texture2D)) as Texture2D; + if (fallbackBlack == null) + fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "black.png"), typeof(Texture2D)) as Texture2D; + + if (descriptor == null) { - if (!shaderManager.IsLockPoiyomi(mat.shader)) - { -#if POIYOMI - ShaderOptimizer.SetLockedForAllMaterials(new[] { mat }, 1, true); -#elif POIYOMI91 - ShaderOptimizer.LockMaterials(new[] { mat }); -#endif - } + Debug.LogError("Can't find avatar descriptor!"); + return null; } - if (mat.mainTexture == null) + + descriptor.gameObject.SetActive(true); + Debug.Log("Key bytes: " + string.Join(", ", GetKeyBytes())); + + var materials = new List(); + foreach (var mat in GetMaterials()) { - Debug.LogWarningFormat("{0} : The mainTexture is empty. it will be skip.", mat.name); - return false; + if (CheckIsSupportedFormat(mat)) + { + materials.Add(mat); + } } - if ((mat.mainTexture is Texture2D) == false) + + GameObject avatar; + if (!isModular) { - Debug.LogErrorFormat("MainTexture in {0} is not texture2D", mat.name); - return false; + avatar = DuplicateAvatar(descriptor.gameObject); + Debug.Log("Duplicate avatar success."); } - if (mat.mainTexture.width % 2 != 0 && mat.mainTexture.height % 2 != 0) + else { - Debug.LogErrorFormat("{0} : The texture size must be a multiple of 2!", mat.mainTexture.name); - return false; + avatar = descriptor.gameObject; } - if (injector.WasInjected(mat.shader)) + + if (avatar == null) { - Debug.LogWarning(mat.name + ": The shader is already encrypted."); - return false; + Debug.LogError("Cannot create duplicated avatar!"); + return null; } - var av3 = descriptor.gameObject.GetComponent(); - if (av3 == null) + byte[] keyBytes = GetKeyBytes(); + + CreateFolders(); + + ///////////////////Select crypto algorithm///////////////////// + IEncryptor encryptor = new XXTEA(); + if (algorithm == (int)Algorithm.xxtea) { - Debug.LogError(descriptor.gameObject.name + ": can't find VRCAvatarDescriptor!"); - return false; + XXTEA xxtea = new XXTEA(); + xxtea.m_rounds = rounds; + encryptor = xxtea; } - if (av3.expressionParameters == null) + else if(algorithm == (int)Algorithm.chacha) { - Debug.LogError(descriptor.gameObject.name + ": can't find expressionParmeters!"); - return false; + Chacha20 chacha = new Chacha20(); + byte[] hash1 = KeyGenerator.GetKeyHash(keyBytes, KeyGenerator.GenerateRandomString(chacha.nonce.Length)); + Array.Copy(hash1, 0, chacha.nonce, 0, chacha.nonce.Length); + encryptor = chacha; } - return true; - } + /////////////////////////////////////////////////////////////// - bool CheckIsSupportedFormat(Material mat) - { - if (!TextureEncryptManager.IsSupportedFormat(mat)) + if (history == null) { - if (mat.mainTexture != null) + history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; + if (history == null) { - Debug.LogWarningFormat("{0} : is unsupported format", mat.mainTexture.name); + history = new EncryptedHistory(); + AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); } - return false; } - return true; - } + history.LoadData(); - public void CreateFolders() - { - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()))) - { - AssetDatabase.CreateFolder(assetDir, descriptor.gameObject.GetInstanceID().ToString()); - } - else + int progress = 0; + int maxprogress = materials.Count; + + var mips = new Dictionary(); + foreach (var mat in materials) { - if (deleteFolders) + int filter = this.filter; +#if UNITY_2022 + MatOption option = matOptions.GetValueOrDefault(mat, null); +#else + MatOption option = null; + if (matOptions.ContainsKey(mat)) + option = matOptions[mat]; +#endif + if (option != null) { - AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations")); - AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "mat")); - AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "shader")); - AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "tex")); + if (option.active == false) + { + Debug.LogFormat("{0} : Skip", mat.name); + continue; + } + filter = option.filter; } - } - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); + if (mat == null) + continue; - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "tex"))) - AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "tex"); - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "mat"))) - AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "mat"); - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "shader"))) - AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "shader"); - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"))) - AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "animations"); + EditorUtility.DisplayProgressBar("Encrypt...", "Encrypt Progress " + ++progress + " of " + maxprogress, (float)progress / (float)maxprogress); + injector = InjectorFactory.GetInjector(mat.shader); + if (injector == null) + { + Debug.LogError(mat.shader + " is a unsupported shader! supported type:lilToon, poiyomi"); + continue; + } + if (!ConditionCheck(mat)) + continue; - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } + Debug.LogFormat("{0} : Start encrypt...", mat.name); - public List GetMaterials() - { - List materials = new List(); - foreach (GameObject g in gameobjectList) - { - if (g == null) - continue; + Texture2D mainTexture = (Texture2D)mat.mainTexture; + injector.Init(descriptor.gameObject, mainTexture, keyBytes, keySize, filter, assetDir, encryptor); - var meshRenderers = g.GetComponentsInChildren(true); - foreach (var meshRenderer in meshRenderers) + int mipRefSize = Math.Max(mat.mainTexture.width, mat.mainTexture.height); + if (!mips.ContainsKey(mipRefSize)) { - foreach (var material in meshRenderer.sharedMaterials) - { - if (material != null) - { - materials.Add(material); - } - } + Texture2D mipRef = GenerateMipRefTexture(Path.Combine(avatarDir, "tex", "mip_" + mipRefSize + ".asset"), mipRefSize, bUseSmallMip); + if (mipRef != null) + mips.Add(mipRefSize, mipRef); } - var skinnedMeshRenderers = g.GetComponentsInChildren(true); - foreach (var skinnedMeshRenderer in skinnedMeshRenderers) + TextureSettings.SetRWEnableTexture(mainTexture); + TextureSettings.SetCrunchCompression(mainTexture, false); + TextureSettings.SetGenerateMipmap(mainTexture, true); + + string encryptedShader_path = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); + + var processedTextureResult = GenerateEncryptedTexture(avatarDir, mat, encryptor, keyBytes); + if (!processedTextureResult.HasValue) + continue; + ProcessedTexture processedTexture = processedTextureResult.Value; + + Texture2D encryptedTex1 = processedTexture.encrypted.Texture1; + Texture2D encryptedTex2 = processedTexture.encrypted.Texture2; + + //////////////////////Inject shader/////////////////////// + OtherTextures otherTex = GetLimOutlineTextures(mat); + Shader encryptedShader = IsEncryptedBefore(mat.shader); + if (encryptedShader == null) { - foreach (var material in skinnedMeshRenderer.sharedMaterials) + try { - if (material != null) + encryptedShader = injector.Inject( + mat, + Path.Combine(assetDir, "Shader/ShellProtector.cginc"), + encryptedShader_path, + encryptedTex1, + otherTex.limTexture != null, + otherTex.limTexture2 != null, + otherTex.outlineTexture != null + ); + + Selection.activeObject = encryptedShader; + EditorApplication.ExecuteMenuItem("Assets/Reimport"); + if (encryptedShader == null) { - materials.Add(material); + Debug.LogErrorFormat("{0}: Injection failed", mat.name); + continue; } + history.Save(mat.shader); + } + catch (UnityException e) + { + Debug.LogError(e.Message); + continue; } } - } - return materials.Concat(materialList).Distinct().ToList(); - } - Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) - { - var mip = TextureEncryptManager.GenerateRefMipmap(size, size, bUseSmallMip); - if (mip == null) - Debug.LogErrorFormat("{0} : Can't generate mip tex{1}.", outputDir, size); - else - { - AssetDatabase.CreateAsset(mip, outputDir); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } - return mip; - } + string fallbackDir = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_fallback.asset"); + Texture2D fallback = GenerateFallbackTexture(fallbackDir, option, mainTexture, ref processedTexture); + if (fallback == null) + Debug.LogErrorFormat("Failed to generate fallback texture: {0}", mainTexture.name); - ProcessedTexture? GenerateEncryptedTexture(string avatarDir, Material mat, IEncryptor encryptor, byte[] keyBytes) - { - Texture2D mainTexture = (Texture2D)mat.mainTexture; + int maxSize = Math.Max(mainTexture.width, mainTexture.height); + Texture2D mipTex = mips[maxSize]; + if (mipTex == null) + Debug.LogWarningFormat("mip_{0} is not exsist", maxSize); - string texPath1 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt.asset"); - string texPath2 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt2.asset"); + string encryptedMatPath = Path.Combine(avatarDir, "mat", mat.GetInstanceID() + "_encrypted.mat"); + GenerateEncryptedMaterial(encryptedMatPath, mat, encryptedShader, fallback, mipTex, otherTex, processedTexture, keyBytes, encryptor); + } // Material loop + EditorUtility.ClearProgressBar(); - bool processed = processedTextures.ContainsKey(mainTexture); - ProcessedTexture processedTexture; - if (processed) - processedTexture = processedTextures[mainTexture]; - else + ///////////////////////parameter//////////////////// + var av3 = avatar.GetComponent(); + av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, keySize, syncSize); + AssetDatabase.CreateAsset(av3.expressionParameters, Path.Combine(avatarDir, av3.expressionParameters.name + ".asset")); + //////////////////////////////////////////////////// + if (!isModular) { - processedTexture = new ProcessedTexture + ReplaceMaterials(avatar); + RemoveDuplicatedTextures(avatar); + + descriptor.gameObject.SetActive(false); + + var newDesriptor = avatar.transform.GetComponentInChildren(true).gameObject; + var tester = newDesriptor.AddComponent(); + tester.lang = lang; + tester.langIdx = langIdx; + tester.protector = this; + tester.userKeyLength = keySize; + Selection.activeObject = tester; + +#if MODULAR + var maMergeAnims = avatar.GetComponentsInChildren(true); + foreach (var maMergeAnim in maMergeAnims) { - encrypted = new EncryptResult(), - fallbacks = new List(), - fallbackOptions = new List(), - nonce = new byte[12] - }; + AnimatorController newAnim = AnimatorManager.DuplicateAnimator(maMergeAnim.animator, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); + maMergeAnim.animator = newAnim; + } +#endif + SetAnimations(avatar, true); + ObfuscateBlendShape(avatar, true); + ChangeMaterialsInAnims(avatar, true); + CleanComponent(avatar); } - //Set chacha nonce - if (algorithm == (int)Algorithm.chacha) + + return avatar; + } + + public void ReplaceMaterials(GameObject avatar) + { + var renderers = avatar.GetComponentsInChildren(true); + if (renderers != null) { - Chacha20 chacha = encryptor as Chacha20; - if (!processed) + for (int i = 0; i < renderers.Length; ++i) { - byte[] hashMat = KeyGenerator.GetHash(mat.GetInstanceID()); - for (int i = 0; i < chacha.nonce.Length; ++i) - chacha.nonce[i] ^= hashMat[i]; - Array.Copy(chacha.nonce, 0, processedTexture.nonce, 0, processedTexture.nonce.Length); + var mats = renderers[i].sharedMaterials; + if (mats == null) + continue; + for (int j = 0; j < mats.Length; ++j) + { + if (mats[j] == null) + continue; + + if (encryptedMaterials.ContainsKey(mats[j])) + { + mats[j] = encryptedMaterials[mats[j]]; + meshes.Add(renderers[i].gameObject); + } + } + renderers[i].sharedMaterials = mats; } - else + } + var skinnedRenderers = avatar.GetComponentsInChildren(true); + if (skinnedRenderers != null) + { + for (int i = 0; i < skinnedRenderers.Length; ++i) { - byte[] nonce = processedTextures[mainTexture].nonce; - Array.Copy(nonce, 0, chacha.nonce, 0, chacha.nonce.Length); + var mats = skinnedRenderers[i].sharedMaterials; + if (mats == null) + continue; + for (int j = 0; j < mats.Length; ++j) + { + if (mats[j] == null) + continue; + + if (encryptedMaterials.ContainsKey(mats[j])) + { + mats[j] = encryptedMaterials[mats[j]]; + meshes.Add(skinnedRenderers[i].gameObject); + } + } + skinnedRenderers[i].sharedMaterials = mats; } } - - if (!processed) + } + OtherTextures GetLimOutlineTextures(Material mat) + { + OtherTextures others = new OtherTextures(); + if (shaderManager.IsPoiyomi(mat.shader)) { - EncryptResult encryptResult; - try + var tex_properties = mat.GetTexturePropertyNames(); + foreach (var t in tex_properties) { - encryptResult = TextureEncryptManager.EncryptTexture(mainTexture, keyBytes, encryptor); + if (t == "_RimTex") + others.limTexture = (Texture2D)mat.GetTexture(t); + else if (t == "_Rim2Tex") + others.limTexture2 = (Texture2D)mat.GetTexture(t); + else if (t == "_OutlineTexture") + others.outlineTexture = (Texture2D)mat.GetTexture(t); } - catch (ArgumentException e) + } + else if (shaderManager.IslilToon(mat.shader)) + { + var tex_properties = mat.GetTexturePropertyNames(); + foreach (var t in tex_properties) { - Debug.LogErrorFormat("{0} : ArgumentException - {1}", mainTexture.name, e.Message); - return null; + if (t == "_RimColorTex") + others.limTexture = (Texture2D)mat.GetTexture(t); + else if (t == "_OutlineTex") + others.outlineTexture = (Texture2D)mat.GetTexture(t); + else if (t == "_RimShadeMask") + others.limShadeTexture = (Texture2D)mat.GetTexture(t); } - AssetDatabase.CreateAsset(encryptResult.Texture1, texPath1); - if (encryptResult.Texture2 != null) - AssetDatabase.CreateAsset(encryptResult.Texture2, texPath2); - - processedTexture.encrypted = encryptResult; - - processedTextures.Add(mainTexture, processedTexture); } - - return processedTexture; + return others; } - - Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D mainTexture, ref ProcessedTexture processedTexture) + public void RemoveDuplicatedTextures(GameObject avatar) { - int fallbackOption = this.fallback; - if (option != null) - fallbackOption = option.fallback; - - int idx = processedTexture.fallbackOptions.FindIndex(option => option == fallbackOption); - Texture2D fallback = null; - if (idx == -1) + string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); + foreach (var mat in encryptedMaterials.Values) { - int fallbackSize = 32; - switch (fallbackOption) + OtherTextures otherTex = GetLimOutlineTextures(mat); + + foreach (var name in mat.GetTexturePropertyNames()) { - case 0: // white - fallbackSize = 0; - break; - case 1: // black - fallbackSize = 1; - break; - case 2: - fallbackSize = 4; - break; - case 3: - fallbackSize = 8; - break; - case 4: - fallbackSize = 16; - break; - case 5: - fallbackSize = 32; - break; - case 6: - fallbackSize = 64; - break; - case 7: - fallbackSize = 128; - break; + if (mat.GetTexture(name) == null) + continue; + if (!(mat.GetTexture(name) is Texture2D)) + continue; + + if (processedTextures.ContainsKey((Texture2D)mat.GetTexture(name))) + { + Texture2D mainTexture = (Texture2D)mat.GetTexture(name); + Texture2D encrypted0 = processedTextures[(Texture2D)mat.GetTexture(name)].encrypted.Texture1; + + int idx = processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.IndexOf(processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.Max()); + Texture2D bigFallbackTexture = processedTextures[(Texture2D)mat.GetTexture(name)].fallbacks[idx]; + + if (otherTex.limTexture != null) + { + string texName = ""; + if (shaderManager.IsPoiyomi(mat.shader)) + texName = "_RimTex"; + else if (shaderManager.IslilToon(mat.shader)) + texName = "_RimColorTex"; + + if (mainTexture == otherTex.limTexture) + mat.SetTexture(texName, encrypted0); + else if (processedTextures.ContainsKey(otherTex.limTexture)) + mat.SetTexture(texName, null); + + } + if (otherTex.limTexture2 != null) //only poiyomi + { + string texName = ""; + if (shaderManager.IsPoiyomi(mat.shader)) + texName = "_Rim2Tex"; + + if (mainTexture == otherTex.limTexture2) + mat.SetTexture(texName, encrypted0); + else if (processedTextures.ContainsKey(otherTex.limTexture2)) + mat.SetTexture(texName, null); + } + if (otherTex.outlineTexture != null) + { + string texName = ""; + if (shaderManager.IsPoiyomi(mat.shader)) + texName = "_OutlineTexture"; + else if (shaderManager.IslilToon(mat.shader)) + texName = "_OutlineTex"; + + if (mainTexture == otherTex.outlineTexture) + mat.SetTexture(texName, bigFallbackTexture); + else if (processedTextures.ContainsKey(otherTex.outlineTexture)) + mat.SetTexture(texName, processedTextures[otherTex.outlineTexture].fallbacks[0]); + } + if (otherTex.limShadeTexture != null) //only liltoon + { + string texName = "_RimShadeMask"; + if (mainTexture == otherTex.limShadeTexture) + mat.SetTexture(texName, bigFallbackTexture); + else if (processedTextures.ContainsKey(otherTex.limShadeTexture)) + mat.SetTexture(texName, null); + } + } } - if (fallbackSize > 1) + } // Encrypted materials loop + + var renderers = avatar.GetComponentsInChildren(true); + if (renderers != null) + { + for (int i = 0; i < renderers.Length; ++i) { - fallback = TextureEncryptManager.GenerateFallback(mainTexture, fallbackSize); - if (fallback != null) + var mats = renderers[i].sharedMaterials; + if (mats == null) + continue; + for (int j = 0; j < mats.Length; ++j) { - processedTexture.fallbacks.Add(fallback); - processedTexture.fallbackOptions.Add(fallbackOption); - AssetDatabase.CreateAsset(fallback, outputDir); - AssetDatabase.SaveAssets(); + if (mats[j] == null) + continue; + + Material tmp = null; + foreach (var name in mats[j].GetTexturePropertyNames()) + { + if (mats[j].GetTexture(name) == null) + continue; + if (!(mats[j].GetTexture(name) is Texture2D)) + continue; + Texture2D tex = (Texture2D)mats[j].GetTexture(name); + if (processedTextures.ContainsKey(tex)) + { + int idx = processedTextures[tex].fallbackOptions.IndexOf(processedTextures[tex].fallbackOptions.Max()); + Texture2D bigFallbackTexture = processedTextures[tex].fallbacks[idx]; + if (tmp == null) + { + Material mat = AssetDatabase.LoadAssetAtPath(Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); + if (mat == null) + { + tmp = Instantiate(mats[j]); + AssetDatabase.CreateAsset(tmp, Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); + AssetDatabase.SaveAssets(); + } + else + tmp = mat; + } + tmp.SetTexture(name, bigFallbackTexture); + } + } AssetDatabase.Refresh(); + if (tmp != null) + mats[j] = tmp; } + renderers[i].sharedMaterials = mats; } - else + } + var skinnedRenderers = avatar.GetComponentsInChildren(true); + if (skinnedRenderers != null) + { + for (int i = 0; i < skinnedRenderers.Length; ++i) { - switch (fallbackSize) + var mats = skinnedRenderers[i].sharedMaterials; + if (mats == null) + continue; + for (int j = 0; j < mats.Length; ++j) { - case 0: - processedTexture.fallbacks.Add(fallbackWhite); - processedTexture.fallbackOptions.Add(fallbackOption); - fallback = fallbackWhite; - break; - case 1: - processedTexture.fallbacks.Add(fallbackBlack); - processedTexture.fallbackOptions.Add(fallbackOption); - fallback = fallbackBlack; - break; + if (mats[j] == null) + continue; + + Material tmp = null; + foreach (var name in mats[j].GetTexturePropertyNames()) + { + if (mats[j].GetTexture(name) == null) + continue; + if (!(mats[j].GetTexture(name) is Texture2D)) + continue; + + if (processedTextures.ContainsKey((Texture2D)mats[j].GetTexture(name))) + { + Texture2D mainTex = (Texture2D)mats[j].GetTexture(name); + int idx = processedTextures[mainTex].fallbackOptions.IndexOf(processedTextures[mainTex].fallbackOptions.Max()); + Texture2D bigFallbackTexture = processedTextures[mainTex].fallbacks[idx]; + if (tmp == null) + { + Material mat = AssetDatabase.LoadAssetAtPath(Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); + if (mat == null) + { + tmp = Instantiate(mats[j]); + AssetDatabase.CreateAsset(tmp, Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); + AssetDatabase.SaveAssets(); + } + else + tmp = mat; + } + tmp.SetTexture(name, bigFallbackTexture); + } + } + AssetDatabase.Refresh(); + if (tmp != null) + mats[j] = tmp; } + skinnedRenderers[i].sharedMaterials = mats; } } - else - fallback = processedTexture.fallbacks[idx]; - - return fallback; } - - Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, OtherTextures otherTex , ProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) + public void SetAnimations(GameObject avatar, bool clone) { - Material newMat = new Material(mat.shader); - newMat.CopyPropertiesFromMaterial(mat); - newMat.shader = encryptedShader; - var originalTex = (Texture2D)newMat.mainTexture; - newMat.mainTexture = fallback; - - Texture2D encryptedTex0 = processedTexture.encrypted.Texture1; - Texture2D encryptedTex1 = processedTexture.encrypted.Texture2; - - newMat.SetTexture("_MipTex", mip); - - if (encryptedTex0 != null) - newMat.SetTexture("_EncryptTex0", encryptedTex0); - if (encryptedTex1 != null) - newMat.SetTexture("_EncryptTex1", encryptedTex1); - - newMat.renderQueue = mat.renderQueue; - if (turnOnAllSafetyFallback) - newMat.SetOverrideTag("VRCFallback", "Unlit"); - - var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(originalTex); - newMat.SetInteger("_Woffset", woffset); - newMat.SetInteger("_Hoffset", hoffset); - for (int i = 0; i < 16 - keySize; ++i) - newMat.SetFloat("_Key" + i, keyBytes[i]); - - if (algorithm == (int)Algorithm.chacha) - { - Chacha20 chacha = encryptor as Chacha20; - newMat.SetInteger("_Nonce0", (int)chacha.GetNonceUint3()[0]); - newMat.SetInteger("_Nonce1", (int)chacha.GetNonceUint3()[1]); - newMat.SetInteger("_Nonce2", (int)chacha.GetNonceUint3()[2]); - } - - var key = new byte[16]; - for (int i = 0; i < 16; i++) - key[i] = keyBytes[i]; + var av3 = avatar.GetComponent(); + AnimatorController fx; + if (clone) + fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); + else + fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; - uint hashMagic = (uint)mat.GetInstanceID(); - - var hash = KeyGenerator.SimpleHash(key, hashMagic); - newMat.SetInteger("_HashMagic", (int)hashMagic); - newMat.SetInteger("_PasswordHash", (int)hash); + av3.baseAnimationLayers[4].animatorController = fx; + string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); - injector.SetKeywords(newMat, otherTex.limTexture != null); + GameObject[] meshArray = new GameObject[meshes.Count]; + meshes.CopyTo(meshArray); + AnimatorManager.CreateKeyAniamtions(Path.Combine(assetDir, "Animations"), animationDir, meshArray); + AnimatorManager.AddKeyLayer(fx, animationDir, keySize, syncSize, 3.0f); - AssetDatabase.CreateAsset(newMat, outputDir); - Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(newMat)); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); - - if (!encryptedMaterials.ContainsKey(mat)) - encryptedMaterials.Add(mat, newMat); - - return newMat; } - public GameObject Encrypt(bool isModular = true) + public void CleanComponent(GameObject avatar) { - return Encrypt(bUseSmallMipTexture, isModular); + DestroyImmediate(avatar.GetComponentInChildren(true)); } - public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) + public void ChangeMaterialsInAnims(GameObject avatar, bool clone) { - meshes.Clear(); - encryptedMaterials.Clear(); - processedTextures.Clear(); - - SyncMatOption(); - - MonoScript monoScript = MonoScript.FromMonoBehaviour(this); - string script_path = AssetDatabase.GetAssetPath(monoScript); - assetDir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); - string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); - - Debug.Log("AssetDir: " + assetDir); - - if (fallbackWhite == null) - fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "white.png"), typeof(Texture2D)) as Texture2D; - if (fallbackBlack == null) - fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "black.png"), typeof(Texture2D)) as Texture2D; + var av3 = avatar.GetComponent(); + var fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; + string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); - if (descriptor == null) + AnimatorManager animManager = new AnimatorManager(); + foreach (var pair in encryptedMaterials) { - Debug.LogError("Can't find avatar descriptor!"); - return null; + Debug.LogFormat("{0}, {1}", pair.Key.name, pair.Value.name); + animManager.ChangeAnimationMaterial(fx, pair.Key, pair.Value, clone, animationDir); } - descriptor.gameObject.SetActive(true); - Debug.Log("Key bytes: " + string.Join(", ", GetKeyBytes())); - - var materials = new List(); - foreach (var mat in GetMaterials()) +#if MODULAR + if (clone) { - if (CheckIsSupportedFormat(mat)) + var maMergeAnims = avatar.GetComponentsInChildren(true); + foreach (var maMergeAnim in maMergeAnims) { - materials.Add(mat); + if (maMergeAnim.animator == null) + continue; + foreach (var pair in encryptedMaterials) + { + animManager.ChangeAnimationMaterial(maMergeAnim.animator as AnimatorController, pair.Key, pair.Value, clone, animationDir); + } } } +#endif + } - GameObject avatar; - if (!isModular) - { - avatar = DuplicateAvatar(descriptor.gameObject); - Debug.Log("Duplicate avatar success."); - } - else - { - avatar = descriptor.gameObject; - } + public VRCExpressionParameters GetParameter() + { + var av3 = descriptor; + if (av3 == null) + return null; + return av3.expressionParameters; + } - if (avatar == null) - { - Debug.LogError("Cannot create duplicated avatar!"); + public static AnimatorController Getfx(GameObject avatar) + { + var av3 = avatar.GetComponent(); + if (av3 == null) return null; - } - byte[] keyBytes = GetKeyBytes(); + return av3.baseAnimationLayers[4].animatorController as AnimatorController; + } - CreateFolders(); + public void ObfuscateBlendShape(GameObject avatar, bool bClone) + { + // bClone true = Manual encrypt + var av3 = avatar.GetComponent(); + AnimatorController fx = Getfx(avatar); + string animDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); - ///////////////////Select crypto algorithm///////////////////// - IEncryptor encryptor = new XXTEA(); - if (algorithm == (int)Algorithm.xxtea) - { - XXTEA xxtea = new XXTEA(); - xxtea.m_rounds = rounds; - encryptor = xxtea; - } - else if(algorithm == (int)Algorithm.chacha) - { - Chacha20 chacha = new Chacha20(); - byte[] hash1 = KeyGenerator.GetKeyHash(keyBytes, KeyGenerator.GenerateRandomString(chacha.nonce.Length)); - Array.Copy(hash1, 0, chacha.nonce, 0, chacha.nonce.Length); - encryptor = chacha; - } - /////////////////////////////////////////////////////////////// + Obfuscator obfuscator = new Obfuscator(); + obfuscator.clone = bClone; + obfuscator.bPreserveMMD = bPreserveMMD; - if (history == null) + var childRenderers = avatar.GetComponentsInChildren(); + +#if MODULAR + //Check localblendshape is empty in MA Blendshape Sync + var maBlendshapeSyncs = avatar.GetComponentsInChildren(true); + foreach (var maBlendshapeSync in maBlendshapeSyncs) { - history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; - if (history == null) + for (int i = 0; i < maBlendshapeSync.Bindings.Count; ++i) { - history = new EncryptedHistory(); - AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); + var binding = maBlendshapeSync.Bindings[i]; + if (binding.LocalBlendshape == null || binding.LocalBlendshape == "") + binding.LocalBlendshape = string.Copy(binding.Blendshape); + + maBlendshapeSync.Bindings[i] = binding; } } - history.LoadData(); - - int progress = 0; - int maxprogress = materials.Count; - - var mips = new Dictionary(); - foreach (var mat in materials) - { - int filter = this.filter; -#if UNITY_2022 - MatOption option = matOptions.GetValueOrDefault(mat, null); -#else - MatOption option = null; - if (matOptions.ContainsKey(mat)) - option = matOptions[mat]; #endif - if (option != null) + foreach (var renderer in obfuscationRenderers) + { + SkinnedMeshRenderer selectRenderer = null; + foreach (var childRenderer in childRenderers) { - if (option.active == false) + if(childRenderer.sharedMesh == renderer.sharedMesh) { - Debug.LogFormat("{0} : Skip", mat.name); - continue; + selectRenderer = childRenderer; + break; } - filter = option.filter; } - if (mat == null) + if (selectRenderer == null) continue; - EditorUtility.DisplayProgressBar("Encrypt...", "Encrypt Progress " + ++progress + " of " + maxprogress, (float)progress / (float)maxprogress); - injector = InjectorFactory.GetInjector(mat.shader); - if (injector == null) + Mesh mesh = selectRenderer.sharedMesh; + if (mesh == null) { - Debug.LogError(mat.shader + " is a unsupported shader! supported type:lilToon, poiyomi"); + Debug.LogErrorFormat("{0} haven't mesh", renderer.transform.name); continue; } - if (!ConditionCheck(mat)) - continue; - - Debug.LogFormat("{0} : Start encrypt...", mat.name); - - Texture2D mainTexture = (Texture2D)mat.mainTexture; - injector.Init(descriptor.gameObject, mainTexture, keyBytes, keySize, filter, assetDir, encryptor); + Mesh newMesh = obfuscator.ObfuscateBlendShapeMesh(mesh, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); + selectRenderer.sharedMesh = newMesh; - int mipRefSize = Math.Max(mat.mainTexture.width, mat.mainTexture.height); - if (!mips.ContainsKey(mipRefSize)) + ////////Change renderer component shape keys//////// + List weights = new List(); + for (int i = 0; i < newMesh.blendShapeCount; ++i) { - Texture2D mipRef = GenerateMipRefTexture(Path.Combine(avatarDir, "tex", "mip_" + mipRefSize + ".asset"), mipRefSize, bUseSmallMip); - if (mipRef != null) - mips.Add(mipRefSize, mipRef); + weights.Add(selectRenderer.GetBlendShapeWeight(i)); + selectRenderer.SetBlendShapeWeight(i, 0.0f); } - - TextureSettings.SetRWEnableTexture(mainTexture); - TextureSettings.SetCrunchCompression(mainTexture, false); - TextureSettings.SetGenerateMipmap(mainTexture, true); - - string encryptedShader_path = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); - - var processedTextureResult = GenerateEncryptedTexture(avatarDir, mat, encryptor, keyBytes); - if (!processedTextureResult.HasValue) - continue; - ProcessedTexture processedTexture = processedTextureResult.Value; - - Texture2D encryptedTex1 = processedTexture.encrypted.Texture1; - Texture2D encryptedTex2 = processedTexture.encrypted.Texture2; - - //////////////////////Inject shader/////////////////////// - OtherTextures otherTex = GetLimOutlineTextures(mat); - Shader encryptedShader = IsEncryptedBefore(mat.shader); - if (encryptedShader == null) + var obList = obfuscator.GetObfuscatedBlendShapeIndex(); + for (int i = 0; i < newMesh.blendShapeCount; ++i) { - try - { - encryptedShader = injector.Inject( - mat, - Path.Combine(assetDir, "Shader/ShellProtector.cginc"), - encryptedShader_path, - encryptedTex1, - otherTex.limTexture != null, - otherTex.limTexture2 != null, - otherTex.outlineTexture != null - ); - - Selection.activeObject = encryptedShader; - EditorApplication.ExecuteMenuItem("Assets/Reimport"); - if (encryptedShader == null) - { - Debug.LogErrorFormat("{0}: Injection failed", mat.name); - continue; - } - history.Save(mat.shader); - } - catch (UnityException e) - { - Debug.LogError(e.Message); - continue; - } + selectRenderer.SetBlendShapeWeight(i, weights[obList[i]]); } - - string fallbackDir = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_fallback.asset"); - Texture2D fallback = GenerateFallbackTexture(fallbackDir, option, mainTexture, ref processedTexture); - if (fallback == null) - Debug.LogErrorFormat("Failed to generate fallback texture: {0}", mainTexture.name); - - int maxSize = Math.Max(mainTexture.width, mainTexture.height); - Texture2D mipTex = mips[maxSize]; - if (mipTex == null) - Debug.LogWarningFormat("mip_{0} is not exsist", maxSize); - - string encryptedMatPath = Path.Combine(avatarDir, "mat", mat.GetInstanceID() + "_encrypted.mat"); - GenerateEncryptedMaterial(encryptedMatPath, mat, encryptedShader, fallback, mipTex, otherTex, processedTexture, keyBytes, encryptor); - } // Material loop - EditorUtility.ClearProgressBar(); - - ///////////////////////parameter//////////////////// - var av3 = avatar.GetComponent(); - av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, keySize, syncSize); - AssetDatabase.CreateAsset(av3.expressionParameters, Path.Combine(avatarDir, av3.expressionParameters.name + ".asset")); - //////////////////////////////////////////////////// - if (!isModular) - { - ReplaceMaterials(avatar); - RemoveDuplicatedTextures(avatar); - - descriptor.gameObject.SetActive(false); - - var newDesriptor = avatar.transform.GetComponentInChildren(true).gameObject; - var tester = newDesriptor.AddComponent(); - tester.lang = lang; - tester.langIdx = langIdx; - tester.protector = this; - tester.userKeyLength = keySize; - Selection.activeObject = tester; - + ///////////////////////////////// #if MODULAR - var maMergeAnims = avatar.GetComponentsInChildren(true); - foreach (var maMergeAnim in maMergeAnims) + //Change MA Blendshape Sync component + foreach (var maBlendshapeSync in maBlendshapeSyncs) { - AnimatorController newAnim = AnimatorManager.DuplicateAnimator(maMergeAnim.animator, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); - maMergeAnim.animator = newAnim; - } -#endif - SetAnimations(avatar, true); - ObfuscateBlendShape(avatar, true); - ChangeMaterialsInAnims(avatar, true); - CleanComponent(avatar); - } - + for (int i = 0; i < maBlendshapeSync.Bindings.Count; ++i) + { + var binding = maBlendshapeSync.Bindings[i]; - return avatar; - } + GameObject targetObject = binding.ReferenceMesh.Get(maBlendshapeSync); + SkinnedMeshRenderer targetRenderer = targetObject.GetComponent(); + SkinnedMeshRenderer syncRenderer = maBlendshapeSync.GetComponent(); - public void ReplaceMaterials(GameObject avatar) - { - var renderers = avatar.GetComponentsInChildren(true); - if (renderers != null) - { - for (int i = 0; i < renderers.Length; ++i) - { - var mats = renderers[i].sharedMaterials; - if (mats == null) - continue; - for (int j = 0; j < mats.Length; ++j) - { - if (mats[j] == null) + if (targetRenderer == null) continue; + if (targetRenderer == selectRenderer) + { + string obfuscatedShape = obfuscator.GetOriginalBlendShapeName(binding.Blendshape); + if (obfuscatedShape != null) + binding.Blendshape = obfuscatedShape; + } - if (encryptedMaterials.ContainsKey(mats[j])) + if (syncRenderer == null) + continue; + if (syncRenderer == selectRenderer) { - mats[j] = encryptedMaterials[mats[j]]; - meshes.Add(renderers[i].gameObject); + string obfuscatedShape = obfuscator.GetOriginalBlendShapeName(binding.LocalBlendshape); + if (obfuscatedShape != null) + binding.LocalBlendshape = obfuscator.GetOriginalBlendShapeName(binding.LocalBlendshape); } + + maBlendshapeSync.Bindings[i] = binding; } - renderers[i].sharedMaterials = mats; } - } - var skinnedRenderers = avatar.GetComponentsInChildren(true); - if (skinnedRenderers != null) - { - for (int i = 0; i < skinnedRenderers.Length; ++i) + + if(bClone) { - var mats = skinnedRenderers[i].sharedMaterials; - if (mats == null) - continue; - for (int j = 0; j < mats.Length; ++j) + var maMergeAnims = avatar.GetComponentsInChildren(true); + foreach (var maMergeAnim in maMergeAnims) { - if (mats[j] == null) - continue; - - if (encryptedMaterials.ContainsKey(mats[j])) - { - mats[j] = encryptedMaterials[mats[j]]; - meshes.Add(skinnedRenderers[i].gameObject); - } + obfuscator.ObfuscateBlendshapeInAnim(maMergeAnim.animator as AnimatorController, selectRenderer.gameObject, animDir); } - skinnedRenderers[i].sharedMaterials = mats; } +#endif + obfuscator.ObfuscateBlendshapeInAnim(fx, selectRenderer.gameObject, animDir); + obfuscator.ChangeObfuscatedBlendShapeInDescriptor(av3); + obfuscator.Clean(); } } - OtherTextures GetLimOutlineTextures(Material mat) + + public int GetEncyryptedFoldersCount() { - OtherTextures others = new OtherTextures(); - if (shaderManager.IsPoiyomi(mat.shader)) + if (!Directory.Exists(assetDir)) { - var tex_properties = mat.GetTexturePropertyNames(); - foreach (var t in tex_properties) - { - if (t == "_RimTex") - others.limTexture = (Texture2D)mat.GetTexture(t); - else if (t == "_Rim2Tex") - others.limTexture2 = (Texture2D)mat.GetTexture(t); - else if (t == "_OutlineTexture") - others.outlineTexture = (Texture2D)mat.GetTexture(t); - } + Debug.LogError($"The specified path does not exist: {assetDir}"); + return 0; } - else if (shaderManager.IslilToon(mat.shader)) + else { - var tex_properties = mat.GetTexturePropertyNames(); - foreach (var t in tex_properties) + string[] directories = Directory.GetDirectories(assetDir); + int deletedCount = 0; + + foreach (string dir in directories) { - if (t == "_RimColorTex") - others.limTexture = (Texture2D)mat.GetTexture(t); - else if (t == "_OutlineTex") - others.outlineTexture = (Texture2D)mat.GetTexture(t); - else if (t == "_RimShadeMask") - others.limShadeTexture = (Texture2D)mat.GetTexture(t); + string folderName = Path.GetFileName(dir); + if (Regex.IsMatch(folderName, @"^-*\d+$")) + { + deletedCount++; + } } + return deletedCount; } - return others; } - public void RemoveDuplicatedTextures(GameObject avatar) + public void CleanEncrypted() { - string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); - foreach (var mat in encryptedMaterials.Values) + AssetDatabase.DeleteAsset(Path.Combine(assetDir, "EncryptedHistory.asset")); + + if (!Directory.Exists(assetDir)) { - OtherTextures otherTex = GetLimOutlineTextures(mat); + Debug.LogError($"The specified path does not exist: {assetDir}"); + } + else + { + string[] directories = Directory.GetDirectories(assetDir); + int deletedCount = 0; - foreach (var name in mat.GetTexturePropertyNames()) + foreach (string dir in directories) { - if (mat.GetTexture(name) == null) - continue; - if (!(mat.GetTexture(name) is Texture2D)) - continue; - - if (processedTextures.ContainsKey((Texture2D)mat.GetTexture(name))) + string folderName = Path.GetFileName(dir); + if (Regex.IsMatch(folderName, @"^-*\d+$")) { - Texture2D mainTexture = (Texture2D)mat.GetTexture(name); - Texture2D encrypted0 = processedTextures[(Texture2D)mat.GetTexture(name)].encrypted.Texture1; - - int idx = processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.IndexOf(processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.Max()); - Texture2D bigFallbackTexture = processedTextures[(Texture2D)mat.GetTexture(name)].fallbacks[idx]; - - if (otherTex.limTexture != null) - { - string texName = ""; - if (shaderManager.IsPoiyomi(mat.shader)) - texName = "_RimTex"; - else if (shaderManager.IslilToon(mat.shader)) - texName = "_RimColorTex"; - - if (mainTexture == otherTex.limTexture) - mat.SetTexture(texName, encrypted0); - else if (processedTextures.ContainsKey(otherTex.limTexture)) - mat.SetTexture(texName, null); - - } - if (otherTex.limTexture2 != null) //only poiyomi - { - string texName = ""; - if (shaderManager.IsPoiyomi(mat.shader)) - texName = "_Rim2Tex"; - - if (mainTexture == otherTex.limTexture2) - mat.SetTexture(texName, encrypted0); - else if (processedTextures.ContainsKey(otherTex.limTexture2)) - mat.SetTexture(texName, null); - } - if (otherTex.outlineTexture != null) + try { - string texName = ""; - if (shaderManager.IsPoiyomi(mat.shader)) - texName = "_OutlineTexture"; - else if (shaderManager.IslilToon(mat.shader)) - texName = "_OutlineTex"; - - if (mainTexture == otherTex.outlineTexture) - mat.SetTexture(texName, bigFallbackTexture); - else if (processedTextures.ContainsKey(otherTex.outlineTexture)) - mat.SetTexture(texName, processedTextures[otherTex.outlineTexture].fallbacks[0]); + AssetDatabase.DeleteAsset(dir); + deletedCount++; + Debug.Log($"Deleted folder: {dir}"); } - if (otherTex.limShadeTexture != null) //only liltoon + catch (System.Exception e) { - string texName = "_RimShadeMask"; - if (mainTexture == otherTex.limShadeTexture) - mat.SetTexture(texName, bigFallbackTexture); - else if (processedTextures.ContainsKey(otherTex.limShadeTexture)) - mat.SetTexture(texName, null); + Debug.LogError($"Failed to delete folder {dir}: {e.Message}"); } } } - } // Encrypted materials loop - var renderers = avatar.GetComponentsInChildren(true); - if (renderers != null) + Debug.Log($"Deletion complete. {deletedCount} folders were deleted."); + AssetDatabase.Refresh(); + } + } + + public int GetDefaultFilter() + { + return filter; + } + public int GetDefaultFallback() + { + return fallback; + } + public int GetKeySize() + { + return keySize; + } + + public void ResetMaterialOptions() + { + matOptionSaved.Clear(); + matOptions.Clear(); + } + + public Shader IsEncryptedBefore(Shader shader) + { + if (history == null) { - for (int i = 0; i < renderers.Length; ++i) + history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; + if (history == null) + { + history = new EncryptedHistory(); + AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); + } + } + history.LoadData(); + return history.IsEncryptedBefore(shader); + } + + public static int GetRequiredSwitchCount(int key_length, int syncSize) + { + key_length /= syncSize; + return Mathf.CeilToInt(Mathf.Log(key_length, 2)); + } + bool ConditionCheck(Material mat) + { + if (shaderManager.IsPoiyomi(mat.shader)) + { + if (!shaderManager.IsLockPoiyomi(mat.shader)) + { +#if POIYOMI + ShaderOptimizer.SetLockedForAllMaterials(new[] { mat }, 1, true); +#elif POIYOMI91 + ShaderOptimizer.LockMaterials(new[] { mat }); +#endif + } + } + if (mat.mainTexture == null) + { + Debug.LogWarningFormat("{0} : The mainTexture is empty. it will be skip.", mat.name); + return false; + } + if ((mat.mainTexture is Texture2D) == false) + { + Debug.LogErrorFormat("MainTexture in {0} is not texture2D", mat.name); + return false; + } + if (mat.mainTexture.width % 2 != 0 && mat.mainTexture.height % 2 != 0) + { + Debug.LogErrorFormat("{0} : The texture size must be a multiple of 2!", mat.mainTexture.name); + return false; + } + if (injector.WasInjected(mat.shader)) + { + Debug.LogWarning(mat.name + ": The shader is already encrypted."); + return false; + } + var av3 = descriptor.gameObject.GetComponent(); + if (av3 == null) + { + Debug.LogError(descriptor.gameObject.name + ": can't find VRCAvatarDescriptor!"); + return false; + } + if (av3.expressionParameters == null) + { + Debug.LogError(descriptor.gameObject.name + ": can't find expressionParmeters!"); + return false; + } + return true; + } + public List GetMaterials() + { + List materials = new List(); + foreach (GameObject g in gameobjectList) + { + if (g == null) + continue; + + var meshRenderers = g.GetComponentsInChildren(true); + foreach (var meshRenderer in meshRenderers) { - var mats = renderers[i].sharedMaterials; - if (mats == null) - continue; - for (int j = 0; j < mats.Length; ++j) + foreach (var material in meshRenderer.sharedMaterials) { - if (mats[j] == null) - continue; - - Material tmp = null; - foreach (var name in mats[j].GetTexturePropertyNames()) + if (material != null) { - if (mats[j].GetTexture(name) == null) - continue; - if (!(mats[j].GetTexture(name) is Texture2D)) - continue; - Texture2D tex = (Texture2D)mats[j].GetTexture(name); - if (processedTextures.ContainsKey(tex)) - { - int idx = processedTextures[tex].fallbackOptions.IndexOf(processedTextures[tex].fallbackOptions.Max()); - Texture2D bigFallbackTexture = processedTextures[tex].fallbacks[idx]; - if (tmp == null) - { - Material mat = AssetDatabase.LoadAssetAtPath(Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); - if (mat == null) - { - tmp = Instantiate(mats[j]); - AssetDatabase.CreateAsset(tmp, Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); - AssetDatabase.SaveAssets(); - } - else - tmp = mat; - } - tmp.SetTexture(name, bigFallbackTexture); - } + materials.Add(material); } - AssetDatabase.Refresh(); - if (tmp != null) - mats[j] = tmp; } - renderers[i].sharedMaterials = mats; } - } - var skinnedRenderers = avatar.GetComponentsInChildren(true); - if (skinnedRenderers != null) - { - for (int i = 0; i < skinnedRenderers.Length; ++i) + + var skinnedMeshRenderers = g.GetComponentsInChildren(true); + foreach (var skinnedMeshRenderer in skinnedMeshRenderers) { - var mats = skinnedRenderers[i].sharedMaterials; - if (mats == null) - continue; - for (int j = 0; j < mats.Length; ++j) + foreach (var material in skinnedMeshRenderer.sharedMaterials) { - if (mats[j] == null) - continue; - - Material tmp = null; - foreach (var name in mats[j].GetTexturePropertyNames()) + if (material != null) { - if (mats[j].GetTexture(name) == null) - continue; - if (!(mats[j].GetTexture(name) is Texture2D)) - continue; - - if (processedTextures.ContainsKey((Texture2D)mats[j].GetTexture(name))) - { - Texture2D mainTex = (Texture2D)mats[j].GetTexture(name); - int idx = processedTextures[mainTex].fallbackOptions.IndexOf(processedTextures[mainTex].fallbackOptions.Max()); - Texture2D bigFallbackTexture = processedTextures[mainTex].fallbacks[idx]; - if (tmp == null) - { - Material mat = AssetDatabase.LoadAssetAtPath(Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); - if (mat == null) - { - tmp = Instantiate(mats[j]); - AssetDatabase.CreateAsset(tmp, Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); - AssetDatabase.SaveAssets(); - } - else - tmp = mat; - } - tmp.SetTexture(name, bigFallbackTexture); - } + materials.Add(material); } - AssetDatabase.Refresh(); - if (tmp != null) - mats[j] = tmp; } - skinnedRenderers[i].sharedMaterials = mats; } } - } - public void SetAnimations(GameObject avatar, bool clone) - { - var av3 = avatar.GetComponent(); - AnimatorController fx; - if (clone) - fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); - else - fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; - - av3.baseAnimationLayers[4].animatorController = fx; - string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); - GameObject[] meshArray = new GameObject[meshes.Count]; - meshes.CopyTo(meshArray); - AnimatorManager.CreateKeyAniamtions(Path.Combine(assetDir, "Animations"), animationDir, meshArray); - AnimatorManager.AddKeyLayer(fx, animationDir, keySize, syncSize, 3.0f); - - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); + return materials.Concat(materialList).Distinct().ToList(); } - - public void CleanComponent(GameObject avatar) + bool CheckIsSupportedFormat(Material mat) { - DestroyImmediate(avatar.GetComponentInChildren(true)); + if (!TextureEncryptManager.IsSupportedFormat(mat)) + { + if (mat.mainTexture != null) + { + Debug.LogWarningFormat("{0} : is unsupported format", mat.mainTexture.name); + } + return false; + } + return true; } - - public void ChangeMaterialsInAnims(GameObject avatar, bool clone) + void CreateFolders() { - var av3 = avatar.GetComponent(); - var fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; - string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); - - AnimatorManager animManager = new AnimatorManager(); - foreach (var pair in encryptedMaterials) + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()))) { - Debug.LogFormat("{0}, {1}", pair.Key.name, pair.Value.name); - animManager.ChangeAnimationMaterial(fx, pair.Key, pair.Value, clone, animationDir); + AssetDatabase.CreateFolder(assetDir, descriptor.gameObject.GetInstanceID().ToString()); } - -#if MODULAR - if (clone) + else { - var maMergeAnims = avatar.GetComponentsInChildren(true); - foreach (var maMergeAnim in maMergeAnims) + if (deleteFolders) { - if (maMergeAnim.animator == null) - continue; - foreach (var pair in encryptedMaterials) - { - animManager.ChangeAnimationMaterial(maMergeAnim.animator as AnimatorController, pair.Key, pair.Value, clone, animationDir); - } + AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations")); + AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "mat")); + AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "shader")); + AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "tex")); } } -#endif - } + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); - public VRCExpressionParameters GetParameter() - { - var av3 = descriptor; - if (av3 == null) - return null; - return av3.expressionParameters; - } + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "tex"))) + AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "tex"); + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "mat"))) + AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "mat"); + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "shader"))) + AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "shader"); + if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"))) + AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "animations"); - public static AnimatorController Getfx(GameObject avatar) + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) { - var av3 = avatar.GetComponent(); - if (av3 == null) - return null; - return av3.baseAnimationLayers[4].animatorController as AnimatorController; + var mip = TextureEncryptManager.GenerateRefMipmap(size, size, bUseSmallMip); + if (mip == null) + Debug.LogErrorFormat("{0} : Can't generate mip tex{1}.", outputDir, size); + else + { + AssetDatabase.CreateAsset(mip, outputDir); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + } + return mip; } - - public void ObfuscateBlendShape(GameObject avatar, bool bClone) + ProcessedTexture? GenerateEncryptedTexture(string avatarDir, Material mat, IEncryptor encryptor, byte[] keyBytes) { - // bClone true = Manual encrypt - var av3 = avatar.GetComponent(); - AnimatorController fx = Getfx(avatar); - string animDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); - - Obfuscator obfuscator = new Obfuscator(); - obfuscator.clone = bClone; - obfuscator.bPreserveMMD = bPreserveMMD; + Texture2D mainTexture = (Texture2D)mat.mainTexture; - var childRenderers = avatar.GetComponentsInChildren(); + string texPath1 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt.asset"); + string texPath2 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt2.asset"); -#if MODULAR - //Check localblendshape is empty in MA Blendshape Sync - var maBlendshapeSyncs = avatar.GetComponentsInChildren(true); - foreach (var maBlendshapeSync in maBlendshapeSyncs) + bool processed = processedTextures.ContainsKey(mainTexture); + ProcessedTexture processedTexture; + if (processed) + processedTexture = processedTextures[mainTexture]; + else { - for (int i = 0; i < maBlendshapeSync.Bindings.Count; ++i) + processedTexture = new ProcessedTexture { - var binding = maBlendshapeSync.Bindings[i]; - if (binding.LocalBlendshape == null || binding.LocalBlendshape == "") - binding.LocalBlendshape = string.Copy(binding.Blendshape); - - maBlendshapeSync.Bindings[i] = binding; - } + encrypted = new EncryptResult(), + fallbacks = new List(), + fallbackOptions = new List(), + nonce = new byte[12] + }; } -#endif - foreach (var renderer in obfuscationRenderers) + + //Set chacha nonce + if (algorithm == (int)Algorithm.chacha) { - SkinnedMeshRenderer selectRenderer = null; - foreach (var childRenderer in childRenderers) + Chacha20 chacha = encryptor as Chacha20; + if (!processed) { - if(childRenderer.sharedMesh == renderer.sharedMesh) - { - selectRenderer = childRenderer; - break; - } + byte[] hashMat = KeyGenerator.GetHash(mat.GetInstanceID()); + for (int i = 0; i < chacha.nonce.Length; ++i) + chacha.nonce[i] ^= hashMat[i]; + Array.Copy(chacha.nonce, 0, processedTexture.nonce, 0, processedTexture.nonce.Length); } - if (selectRenderer == null) - continue; - - Mesh mesh = selectRenderer.sharedMesh; - if (mesh == null) + else { - Debug.LogErrorFormat("{0} haven't mesh", renderer.transform.name); - continue; + byte[] nonce = processedTextures[mainTexture].nonce; + Array.Copy(nonce, 0, chacha.nonce, 0, chacha.nonce.Length); } - Mesh newMesh = obfuscator.ObfuscateBlendShapeMesh(mesh, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); - selectRenderer.sharedMesh = newMesh; + } - ////////Change renderer component shape keys//////// - List weights = new List(); - for (int i = 0; i < newMesh.blendShapeCount; ++i) + if (!processed) + { + EncryptResult encryptResult; + try { - weights.Add(selectRenderer.GetBlendShapeWeight(i)); - selectRenderer.SetBlendShapeWeight(i, 0.0f); + encryptResult = TextureEncryptManager.EncryptTexture(mainTexture, keyBytes, encryptor); } - var obList = obfuscator.GetObfuscatedBlendShapeIndex(); - for (int i = 0; i < newMesh.blendShapeCount; ++i) + catch (ArgumentException e) { - selectRenderer.SetBlendShapeWeight(i, weights[obList[i]]); + Debug.LogErrorFormat("{0} : ArgumentException - {1}", mainTexture.name, e.Message); + return null; } - ///////////////////////////////// -#if MODULAR - //Change MA Blendshape Sync component - foreach (var maBlendshapeSync in maBlendshapeSyncs) - { - for (int i = 0; i < maBlendshapeSync.Bindings.Count; ++i) - { - var binding = maBlendshapeSync.Bindings[i]; + AssetDatabase.CreateAsset(encryptResult.Texture1, texPath1); + if (encryptResult.Texture2 != null) + AssetDatabase.CreateAsset(encryptResult.Texture2, texPath2); - GameObject targetObject = binding.ReferenceMesh.Get(maBlendshapeSync); - SkinnedMeshRenderer targetRenderer = targetObject.GetComponent(); - SkinnedMeshRenderer syncRenderer = maBlendshapeSync.GetComponent(); + processedTexture.encrypted = encryptResult; - if (targetRenderer == null) - continue; - if (targetRenderer == selectRenderer) - { - string obfuscatedShape = obfuscator.GetOriginalBlendShapeName(binding.Blendshape); - if (obfuscatedShape != null) - binding.Blendshape = obfuscatedShape; - } + processedTextures.Add(mainTexture, processedTexture); + } - if (syncRenderer == null) - continue; - if (syncRenderer == selectRenderer) - { - string obfuscatedShape = obfuscator.GetOriginalBlendShapeName(binding.LocalBlendshape); - if (obfuscatedShape != null) - binding.LocalBlendshape = obfuscator.GetOriginalBlendShapeName(binding.LocalBlendshape); - } + return processedTexture; + } + Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D mainTexture, ref ProcessedTexture processedTexture) + { + int fallbackOption = this.fallback; + if (option != null) + fallbackOption = option.fallback; - maBlendshapeSync.Bindings[i] = binding; - } + int idx = processedTexture.fallbackOptions.FindIndex(option => option == fallbackOption); + Texture2D fallback = null; + if (idx == -1) + { + int fallbackSize = 32; + switch (fallbackOption) + { + case 0: // white + fallbackSize = 0; + break; + case 1: // black + fallbackSize = 1; + break; + case 2: + fallbackSize = 4; + break; + case 3: + fallbackSize = 8; + break; + case 4: + fallbackSize = 16; + break; + case 5: + fallbackSize = 32; + break; + case 6: + fallbackSize = 64; + break; + case 7: + fallbackSize = 128; + break; } - - if(bClone) + if (fallbackSize > 1) { - var maMergeAnims = avatar.GetComponentsInChildren(true); - foreach (var maMergeAnim in maMergeAnims) + fallback = TextureEncryptManager.GenerateFallback(mainTexture, fallbackSize); + if (fallback != null) { - obfuscator.ObfuscateBlendshapeInAnim(maMergeAnim.animator as AnimatorController, selectRenderer.gameObject, animDir); + processedTexture.fallbacks.Add(fallback); + processedTexture.fallbackOptions.Add(fallbackOption); + AssetDatabase.CreateAsset(fallback, outputDir); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); } } -#endif - obfuscator.ObfuscateBlendshapeInAnim(fx, selectRenderer.gameObject, animDir); - obfuscator.ChangeObfuscatedBlendShapeInDescriptor(av3); - obfuscator.Clean(); - } - } - - public int GetEncyryptedFoldersCount() - { - if (!Directory.Exists(assetDir)) - { - Debug.LogError($"The specified path does not exist: {assetDir}"); - return 0; - } - else - { - string[] directories = Directory.GetDirectories(assetDir); - int deletedCount = 0; - - foreach (string dir in directories) + else { - string folderName = Path.GetFileName(dir); - if (Regex.IsMatch(folderName, @"^-*\d+$")) + switch (fallbackSize) { - deletedCount++; + case 0: + processedTexture.fallbacks.Add(fallbackWhite); + processedTexture.fallbackOptions.Add(fallbackOption); + fallback = fallbackWhite; + break; + case 1: + processedTexture.fallbacks.Add(fallbackBlack); + processedTexture.fallbackOptions.Add(fallbackOption); + fallback = fallbackBlack; + break; } } - return deletedCount; } + else + fallback = processedTexture.fallbacks[idx]; + + return fallback; } - public void CleanEncrypted() + Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, OtherTextures otherTex, ProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) { - AssetDatabase.DeleteAsset(Path.Combine(assetDir, "EncryptedHistory.asset")); + Material newMat = new Material(mat.shader); + newMat.CopyPropertiesFromMaterial(mat); + newMat.shader = encryptedShader; + var originalTex = (Texture2D)newMat.mainTexture; + newMat.mainTexture = fallback; - if (!Directory.Exists(assetDir)) - { - Debug.LogError($"The specified path does not exist: {assetDir}"); - } - else - { - string[] directories = Directory.GetDirectories(assetDir); - int deletedCount = 0; + Texture2D encryptedTex0 = processedTexture.encrypted.Texture1; + Texture2D encryptedTex1 = processedTexture.encrypted.Texture2; - foreach (string dir in directories) - { - string folderName = Path.GetFileName(dir); - if (Regex.IsMatch(folderName, @"^-*\d+$")) - { - try - { - AssetDatabase.DeleteAsset(dir); - deletedCount++; - Debug.Log($"Deleted folder: {dir}"); - } - catch (System.Exception e) - { - Debug.LogError($"Failed to delete folder {dir}: {e.Message}"); - } - } - } + newMat.SetTexture("_MipTex", mip); - Debug.Log($"Deletion complete. {deletedCount} folders were deleted."); - AssetDatabase.Refresh(); - } - } + if (encryptedTex0 != null) + newMat.SetTexture("_EncryptTex0", encryptedTex0); + if (encryptedTex1 != null) + newMat.SetTexture("_EncryptTex1", encryptedTex1); - public int GetDefaultFilter() - { - return filter; - } - public int GetDefaultFallback() - { - return fallback; - } - public int GetKeySize() - { - return keySize; - } + newMat.renderQueue = mat.renderQueue; + if (turnOnAllSafetyFallback) + newMat.SetOverrideTag("VRCFallback", "Unlit"); - public void ResetMaterialOptions() - { - matOptionSaved.Clear(); - matOptions.Clear(); - } + var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(originalTex); + newMat.SetInteger("_Woffset", woffset); + newMat.SetInteger("_Hoffset", hoffset); + for (int i = 0; i < 16 - keySize; ++i) + newMat.SetFloat("_Key" + i, keyBytes[i]); - public Shader IsEncryptedBefore(Shader shader) - { - if (history == null) + if (algorithm == (int)Algorithm.chacha) { - history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; - if (history == null) - { - history = new EncryptedHistory(); - AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); - } + Chacha20 chacha = encryptor as Chacha20; + newMat.SetInteger("_Nonce0", (int)chacha.GetNonceUint3()[0]); + newMat.SetInteger("_Nonce1", (int)chacha.GetNonceUint3()[1]); + newMat.SetInteger("_Nonce2", (int)chacha.GetNonceUint3()[2]); } - history.LoadData(); - return history.IsEncryptedBefore(shader); - } - public static int GetRequiredSwitchCount(int key_length, int syncSize) - { - key_length /= syncSize; - return Mathf.CeilToInt(Mathf.Log(key_length, 2)); + var key = new byte[16]; + for (int i = 0; i < 16; i++) + key[i] = keyBytes[i]; + + uint hashMagic = (uint)mat.GetInstanceID(); + + var hash = KeyGenerator.SimpleHash(key, hashMagic); + newMat.SetInteger("_HashMagic", (int)hashMagic); + newMat.SetInteger("_PasswordHash", (int)hash); + + injector.SetKeywords(newMat, otherTex.limTexture != null); + + AssetDatabase.CreateAsset(newMat, outputDir); + Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(newMat)); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + if (!encryptedMaterials.ContainsKey(mat)) + encryptedMaterials.Add(mat, newMat); + + return newMat; } } } From b261fd29f5de8c10529b46e0b60a713b8b1a0e99 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Wed, 19 Nov 2025 03:01:20 +0900 Subject: [PATCH 31/59] feat: Use reflection when locking shader --- Runtime/Scripts/AssetManager.cs | 57 ++++++++++++++++++++++++++++--- Runtime/Scripts/ShellProtector.cs | 34 +++++++----------- 2 files changed, 65 insertions(+), 26 deletions(-) diff --git a/Runtime/Scripts/AssetManager.cs b/Runtime/Scripts/AssetManager.cs index 9ed6671..86f119f 100644 --- a/Runtime/Scripts/AssetManager.cs +++ b/Runtime/Scripts/AssetManager.cs @@ -4,6 +4,7 @@ using UnityEditor; using UnityEngine; using System.Linq; +using System.Reflection; namespace Shell.Protector { @@ -44,11 +45,9 @@ public bool IslilToon(Shader shader) return true; return false; } - public bool IsLockPoiyomi(Shader shader) + public bool IsLockPoiyomi(Material mat) { - if (shader.name.Contains("Locked")) - return true; - return false; + return mat.shader.name.StartsWith("Hidden/") && mat.GetTag("OriginalShader", false, "") != ""; } public int GetShaderType(Shader shader) { @@ -171,6 +170,56 @@ public void ResetDefine() PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, symbols); } + + public bool LockShader(Material mat) + { + if (IsLockPoiyomi(mat)) + return true; + + bool bOldOptimizer = false; + + Type optimizer = Type.GetType("Thry.ThryEditor.ShaderOptimizer, ThryAssemblyDefinition"); + if (optimizer == null) + { + bOldOptimizer = true; + optimizer = Type.GetType("Thry.ShaderOptimizer, ThryAssemblyDefinition"); + } + + if (optimizer == null) + { + Debug.LogError("Not found the ShaderOptimizer!"); + return false; + } + if (!bOldOptimizer) + { + MethodInfo lockFn = optimizer.GetMethod("LockMaterials"); + if (lockFn == null) + { + Debug.LogError("Not found LockMaterials()"); + return false; + } + object[] param = + { + new[] { mat }, 0 + }; + lockFn.Invoke(null, param); + } + else + { + MethodInfo lockFn = optimizer.GetMethod("SetLockedForAllMaterials"); + if (lockFn == null) + { + Debug.LogError("Not found SetLockedForAllMaterials()"); + return false; + } + object[] param = + { + new[] { mat }, 1, true, false, true, null + }; + lockFn.Invoke(null, param); + } + return true; + } } } #endif \ No newline at end of file diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 358530d..8a5c956 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -15,12 +15,6 @@ using nadena.dev.modular_avatar.core; #endif -#if POIYOMI -using Thry; -#elif POIYOMI91 -using Thry.ThryEditor; -#endif - namespace Shell.Protector { public class ShellProtector : MonoBehaviour, IEditorOnly @@ -30,8 +24,6 @@ public class ShellProtector : MonoBehaviour, IEditorOnly [SerializeField] List materialList = new List(); [SerializeField] - List textureList = new List(); - [SerializeField] List obfuscationRenderers = new List(); Injector injector; @@ -267,6 +259,8 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) var mips = new Dictionary(); foreach (var mat in materials) { + if (mat == null) + continue; int filter = this.filter; #if UNITY_2022 MatOption option = matOptions.GetValueOrDefault(mat, null); @@ -284,8 +278,6 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) } filter = option.filter; } - if (mat == null) - continue; EditorUtility.DisplayProgressBar("Encrypt...", "Encrypt Progress " + ++progress + " of " + maxprogress, (float)progress / (float)maxprogress); injector = InjectorFactory.GetInjector(mat.shader); @@ -297,6 +289,15 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) if (!ConditionCheck(mat)) continue; + if (shaderManager.IsPoiyomi(mat.shader)) + { + if (!shaderManager.IsLockPoiyomi(mat)) + { + shaderManager.LockShader(mat); + Debug.LogFormat("Lock: {0} - {1}", mat.name, AssetDatabase.GetAssetPath(mat.shader)); + } + } + Debug.LogFormat("{0} : Start encrypt...", mat.name); Texture2D mainTexture = (Texture2D)mat.mainTexture; @@ -356,7 +357,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) continue; } } - + ///////////////////////////////////////////////////////// string fallbackDir = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_fallback.asset"); Texture2D fallback = GenerateFallbackTexture(fallbackDir, option, mainTexture, ref processedTexture); if (fallback == null) @@ -941,17 +942,6 @@ public static int GetRequiredSwitchCount(int key_length, int syncSize) } bool ConditionCheck(Material mat) { - if (shaderManager.IsPoiyomi(mat.shader)) - { - if (!shaderManager.IsLockPoiyomi(mat.shader)) - { -#if POIYOMI - ShaderOptimizer.SetLockedForAllMaterials(new[] { mat }, 1, true); -#elif POIYOMI91 - ShaderOptimizer.LockMaterials(new[] { mat }); -#endif - } - } if (mat.mainTexture == null) { Debug.LogWarningFormat("{0} : The mainTexture is empty. it will be skip.", mat.name); From 7e1305d37b88ccdb935b270d577f5818057b33f7 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Wed, 19 Nov 2025 03:02:06 +0900 Subject: [PATCH 32/59] fix: Check texture size correctly --- Runtime/Scripts/ShellProtector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 8a5c956..ca2517e 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -952,7 +952,7 @@ bool ConditionCheck(Material mat) Debug.LogErrorFormat("MainTexture in {0} is not texture2D", mat.name); return false; } - if (mat.mainTexture.width % 2 != 0 && mat.mainTexture.height % 2 != 0) + if (mat.mainTexture.width % 2 != 0 || mat.mainTexture.height % 2 != 0) { Debug.LogErrorFormat("{0} : The texture size must be a multiple of 2!", mat.mainTexture.name); return false; From 0f14a60feaac911239e34351bac8d4ec534730d4 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Wed, 19 Nov 2025 03:04:53 +0900 Subject: [PATCH 33/59] build: 2.5.1 beta --- package.json | 2 +- version.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9f8ba2e..d6e8549 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shell.protector", "displayName": "Shell Protector", - "version": "2.5.0-beta", + "version": "2.5.1-beta", "unity": "2022.3", "description": "Texture encryption for VRChat. Save avatars from ripping", "author": { diff --git a/version.json b/version.json index cfb21f6..f18a908 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "latestVersion": "2.5.0-beta", "downloadPage": "/releases/tag/2.5.0b" } \ No newline at end of file +{ "latestVersion": "2.5.1-beta", "downloadPage": "/releases/tag/2.5.1-beta" } \ No newline at end of file From 6d1a8a1e0150b0d83c8294d722b4890c3332b3ed Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sat, 14 Feb 2026 04:24:33 +0900 Subject: [PATCH 34/59] Update README.ENG.md --- README.ENG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.ENG.md b/README.ENG.md index c1dbbe4..1dddcea 100644 --- a/README.ENG.md +++ b/README.ENG.md @@ -53,7 +53,7 @@ Source code of OSC: https://github.com/Shell4026/ShellProtectorOSC ### Parameter-multiplexing Detailed principle:https://github.com/seanedwards/vrc-worldobject/blob/main/docs/parameter-multiplexing.md -This is a parameter-saving technique. After checking, OSC must always be turned on and Parameter-multiplexing must be checked in the OSC program. +This is a parameter-saving technique. After checking, OSC must always be **turned on and Parameter-multiplexing** must be checked in the OSC program. This will slightly increase the time it takes to get back to your original avatar appearance in-game. @@ -63,6 +63,8 @@ When using parameter multiplexing, depending on the server or network conditions In this case, try increasing the refresh rate slightly, which was added in OSC 1.5.0. +It is applied by default from version 2.5.0beta. + ### Avatar fallback A feature that allows anyone with Safety On when encryption is in place to appear as a degraded version of themselves when viewing your avatar. ![fallback](https://github.com/user-attachments/assets/d3ca69b0-ff08-4793-a4e4-73269bc8efd3) From c380a6434be336a806fde07acd7089b18f1494d5 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sat, 14 Feb 2026 04:30:01 +0900 Subject: [PATCH 35/59] Update README.JP.md --- README.JP.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.JP.md b/README.JP.md index 2bb2a18..f3b15ab 100644 --- a/README.JP.md +++ b/README.JP.md @@ -54,7 +54,7 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC ### パラメータマルチプレックス 詳細原理:https://github.com/seanedwards/vrc-worldobject/blob/main/docs/parameter-multiplexing.md -パラメータ節約技術です。チェック後、OSCを常にオンにしておく必要があり、OSCプログラムにもParameter-multiplexingをチェックする必要があります。 +パラメータ節約技術です。チェック後、OSCを常にオンにしておく必要があり、OSCプログラムにも**Parameter-multiplexingをチェック**する必要があります。 ゲーム内で元の姿に戻るまでの時間が若干増加します。 @@ -64,6 +64,8 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC この場合、OSC 1.5.0で追加されたRefresh rateを少し上げてみてください。 +2.5.0betaからデフォルトで適用されます。 + ### アバターフォールバック 暗号化がかかっているときにSafetyがオンになっている人は、アバターを見るときに劣化したバージョンに見えるようにする機能です。 ![fallback](https://github.com/user-attachments/assets/d3ca69b0-ff08-4793-a4e4-73269bc8efd3) From 250cddd1881e84b532e9a6b1bfcee0c61b952555 Mon Sep 17 00:00:00 2001 From: Lee Sangyeop Date: Fri, 3 Jul 2026 22:17:42 +0900 Subject: [PATCH 36/59] test: add shell protector coverage --- Runtime/Scripts/Injector/PoiyomiInjector.cs | 3 +- Runtime/Scripts/ShellProtector.cs | 52 +++- Runtime/Shader/RGB.cginc | 14 +- Runtime/Shader/RGBA.cginc | 10 +- Runtime/Shader/ShellProtector.cginc | 11 +- Runtime/Shader/XXTEA.cginc | 20 +- .../lilCustomShaderProperties.lilblock | 3 +- Tests.meta | 8 + Tests/Editor.meta | 8 + Tests/Editor/Gpu.meta | 8 + Tests/Editor/Gpu/GpuTextureDecryptionTests.cs | 187 ++++++++++++ .../Gpu/GpuTextureDecryptionTests.cs.meta | 11 + Tests/Editor/Gpu/Shaders.meta | 8 + Tests/Editor/Gpu/Shaders/Hidden.meta | 8 + .../ShellProtectorGpuDecryptTest.shader | 120 ++++++++ .../ShellProtectorGpuDecryptTest.shader.meta | 9 + Tests/Editor/Integration.meta | 8 + .../ShellProtectorPipelineTests.cs | 276 ++++++++++++++++++ .../ShellProtectorPipelineTests.cs.meta | 11 + Tests/Editor/Shared.meta | 8 + Tests/Editor/Shared/TestAssetScope.cs | 172 +++++++++++ Tests/Editor/Shared/TestAssetScope.cs.meta | 11 + .../Shared/VrcExpressionParametersTestUtil.cs | 220 ++++++++++++++ .../VrcExpressionParametersTestUtil.cs.meta | 11 + .../Shell.Protector.Editor.Tests.asmdef | 28 ++ .../Shell.Protector.Editor.Tests.asmdef.meta | 7 + Tests/Editor/Unit.meta | 8 + Tests/Editor/Unit/CryptoTests.cs | 83 ++++++ Tests/Editor/Unit/CryptoTests.cs.meta | 11 + Tests/Editor/Unit/ParameterManagerTests.cs | 70 +++++ .../Editor/Unit/ParameterManagerTests.cs.meta | 11 + .../Editor/Unit/TextureEncryptManagerTests.cs | 83 ++++++ .../Unit/TextureEncryptManagerTests.cs.meta | 11 + 33 files changed, 1473 insertions(+), 36 deletions(-) create mode 100644 Tests.meta create mode 100644 Tests/Editor.meta create mode 100644 Tests/Editor/Gpu.meta create mode 100644 Tests/Editor/Gpu/GpuTextureDecryptionTests.cs create mode 100644 Tests/Editor/Gpu/GpuTextureDecryptionTests.cs.meta create mode 100644 Tests/Editor/Gpu/Shaders.meta create mode 100644 Tests/Editor/Gpu/Shaders/Hidden.meta create mode 100644 Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader create mode 100644 Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader.meta create mode 100644 Tests/Editor/Integration.meta create mode 100644 Tests/Editor/Integration/ShellProtectorPipelineTests.cs create mode 100644 Tests/Editor/Integration/ShellProtectorPipelineTests.cs.meta create mode 100644 Tests/Editor/Shared.meta create mode 100644 Tests/Editor/Shared/TestAssetScope.cs create mode 100644 Tests/Editor/Shared/TestAssetScope.cs.meta create mode 100644 Tests/Editor/Shared/VrcExpressionParametersTestUtil.cs create mode 100644 Tests/Editor/Shared/VrcExpressionParametersTestUtil.cs.meta create mode 100644 Tests/Editor/Shell.Protector.Editor.Tests.asmdef create mode 100644 Tests/Editor/Shell.Protector.Editor.Tests.asmdef.meta create mode 100644 Tests/Editor/Unit.meta create mode 100644 Tests/Editor/Unit/CryptoTests.cs create mode 100644 Tests/Editor/Unit/CryptoTests.cs.meta create mode 100644 Tests/Editor/Unit/ParameterManagerTests.cs create mode 100644 Tests/Editor/Unit/ParameterManagerTests.cs.meta create mode 100644 Tests/Editor/Unit/TextureEncryptManagerTests.cs create mode 100644 Tests/Editor/Unit/TextureEncryptManagerTests.cs.meta diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index 14b4965..aa386d2 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -140,6 +140,7 @@ private void InsertProperties(ref string data) _Nonce0 (""Nonce"", integer) = 0 _Nonce1 (""Nonce"", integer) = 0 _Nonce2 (""Nonce"", integer) = 0 + _Rounds (""Rounds"", integer) = 0 _PasswordHash (""PasswordHash"", integer) = 0 _HashMagic (""HashMagic"", integer) = 0 "; @@ -157,4 +158,4 @@ private void InsertProperties(ref string data) } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index ca2517e..a24d3fb 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -29,6 +29,7 @@ public class ShellProtector : MonoBehaviour, IEditorOnly Injector injector; AssetManager shaderManager = AssetManager.GetInstance(); bool init = false; + string packageAssetDir = null; enum Algorithm { @@ -106,6 +107,25 @@ struct OtherTextures Texture2D fallbackWhite = null; Texture2D fallbackBlack = null; + string GetPackageAssetDir() + { + if (!string.IsNullOrEmpty(packageAssetDir)) + return packageAssetDir; + + MonoScript monoScript = MonoScript.FromMonoBehaviour(this); + string script_path = AssetDatabase.GetAssetPath(monoScript); + packageAssetDir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); + return packageAssetDir; + } + + string ResolveOutputAssetDir() + { + string packageDir = GetPackageAssetDir(); + if (string.IsNullOrEmpty(assetDir) || assetDir == "Assets/ShellProtect") + assetDir = packageDir; + return assetDir; + } + public void Init() { if (init) @@ -175,17 +195,16 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) SyncMatOption(); - MonoScript monoScript = MonoScript.FromMonoBehaviour(this); - string script_path = AssetDatabase.GetAssetPath(monoScript); - assetDir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); + string resourceDir = GetPackageAssetDir(); + assetDir = ResolveOutputAssetDir(); string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); Debug.Log("AssetDir: " + assetDir); if (fallbackWhite == null) - fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "white.png"), typeof(Texture2D)) as Texture2D; + fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(resourceDir, "white.png"), typeof(Texture2D)) as Texture2D; if (fallbackBlack == null) - fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "black.png"), typeof(Texture2D)) as Texture2D; + fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(resourceDir, "black.png"), typeof(Texture2D)) as Texture2D; if (descriptor == null) { @@ -247,7 +266,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; if (history == null) { - history = new EncryptedHistory(); + history = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); } } @@ -301,7 +320,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) Debug.LogFormat("{0} : Start encrypt...", mat.name); Texture2D mainTexture = (Texture2D)mat.mainTexture; - injector.Init(descriptor.gameObject, mainTexture, keyBytes, keySize, filter, assetDir, encryptor); + injector.Init(descriptor.gameObject, mainTexture, keyBytes, keySize, filter, resourceDir, encryptor); int mipRefSize = Math.Max(mat.mainTexture.width, mat.mainTexture.height); if (!mips.ContainsKey(mipRefSize)) @@ -334,7 +353,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) { encryptedShader = injector.Inject( mat, - Path.Combine(assetDir, "Shader/ShellProtector.cginc"), + Path.Combine(resourceDir, "Shader/ShellProtector.cginc"), encryptedShader_path, encryptedTex1, otherTex.limTexture != null, @@ -657,6 +676,7 @@ public void RemoveDuplicatedTextures(GameObject avatar) } } } + public void SetAnimations(GameObject avatar, bool clone) { var av3 = avatar.GetComponent(); @@ -671,7 +691,7 @@ public void SetAnimations(GameObject avatar, bool clone) GameObject[] meshArray = new GameObject[meshes.Count]; meshes.CopyTo(meshArray); - AnimatorManager.CreateKeyAniamtions(Path.Combine(assetDir, "Animations"), animationDir, meshArray); + AnimatorManager.CreateKeyAniamtions(Path.Combine(GetPackageAssetDir(), "Animations"), animationDir, meshArray); AnimatorManager.AddKeyLayer(fx, animationDir, keySize, syncSize, 3.0f); AssetDatabase.SaveAssets(); @@ -689,7 +709,7 @@ public void ChangeMaterialsInAnims(GameObject avatar, bool clone) var fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); - AnimatorManager animManager = new AnimatorManager(); + AnimatorManager animManager = ScriptableObject.CreateInstance(); foreach (var pair in encryptedMaterials) { Debug.LogFormat("{0}, {1}", pair.Key.name, pair.Value.name); @@ -736,7 +756,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) AnimatorController fx = Getfx(avatar); string animDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); - Obfuscator obfuscator = new Obfuscator(); + Obfuscator obfuscator = ScriptableObject.CreateInstance(); obfuscator.clone = bClone; obfuscator.bPreserveMMD = bPreserveMMD; @@ -927,7 +947,7 @@ public Shader IsEncryptedBefore(Shader shader) history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; if (history == null) { - history = new EncryptedHistory(); + history = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); } } @@ -1226,7 +1246,7 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(originalTex); newMat.SetInteger("_Woffset", woffset); newMat.SetInteger("_Hoffset", hoffset); - for (int i = 0; i < 16 - keySize; ++i) + for (int i = 0; i < keyBytes.Length; ++i) newMat.SetFloat("_Key" + i, keyBytes[i]); if (algorithm == (int)Algorithm.chacha) @@ -1236,6 +1256,10 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp newMat.SetInteger("_Nonce1", (int)chacha.GetNonceUint3()[1]); newMat.SetInteger("_Nonce2", (int)chacha.GetNonceUint3()[2]); } + else if (algorithm == (int)Algorithm.xxtea) + { + newMat.SetInteger("_Rounds", (int)rounds); + } var key = new byte[16]; for (int i = 0; i < 16; i++) @@ -1261,4 +1285,4 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Shader/RGB.cginc b/Runtime/Shader/RGB.cginc index 87d68de..933f23b 100644 --- a/Runtime/Shader/RGB.cginc +++ b/Runtime/Shader/RGB.cginc @@ -10,23 +10,23 @@ int GetIndex(half2 uv, int m) { return (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); } -void GetData(inout uint data[3], half2 uv, int m) { +void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[3], half2 uv, int m) { const int pos[4] = { 0, -1, -2, -3 }; int idx = GetIndex(uv, m); int offset = pos[idx % 4]; half3 pixels[4]; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[2] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 2 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[3] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 3 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[0] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[1] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[2] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 2 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[3] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 3 + offset, m, (uint)_Woffset, _Hoffset), m); data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[1].r * 255.0f) << 24)); data[1] = ((uint)round(pixels[1].g * 255.0f) | ((uint)round(pixels[1].b * 255.0f) << 8) | ((uint)round(pixels[2].r * 255.0f) << 16) | ((uint)round(pixels[2].g * 255.0f) << 24)); data[2] = ((uint)round(pixels[2].b * 255.0f) | ((uint)round(pixels[3].r * 255.0f) << 8) | ((uint)round(pixels[3].g * 255.0f) << 16) | ((uint)round(pixels[3].b * 255.0f) << 24)); } -half4 GetPixel(inout uint data[3], half2 uv, int m) { +half4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, inout uint data[3], half2 uv, int m) { const int pos[4] = { 0, -1, -2, -3 }; int idx = GetIndex(uv, m); int offset = pos[idx % 4]; @@ -37,4 +37,4 @@ half4 GetPixel(inout uint data[3], half2 uv, int m) { half3 decrypt = half3(r[idx % 4], g[idx % 4], b[idx % 4]); return half4(GammaCorrection(decrypt), 1.0); -} \ No newline at end of file +} diff --git a/Runtime/Shader/RGBA.cginc b/Runtime/Shader/RGBA.cginc index ec96a53..8004fe7 100644 --- a/Runtime/Shader/RGBA.cginc +++ b/Runtime/Shader/RGBA.cginc @@ -10,19 +10,19 @@ int GetIndex(half2 uv, int m) { return (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); } -void GetData(inout uint data[2], half2 uv, int m) { +void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[2], half2 uv, int m) { int idx = GetIndex(uv, m); int offset = (idx & 1) == 0 ? 0 : -1; half4 pixels[2]; - pixels[0] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); - pixels[1] = _EncryptTex0.SampleLevel(sampler_EncryptTex0, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[0] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); + pixels[1] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); } -half4 GetPixel(inout uint data[2], half2 uv, int m) { +half4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, inout uint data[2], half2 uv, int m) { int idx = GetIndex(uv, m); half r = ((data[idx & 1] & 0x000000FF) >> 0)/255.0f; half g = ((data[idx & 1] & 0x0000FF00) >> 8)/255.0f; @@ -31,4 +31,4 @@ half4 GetPixel(inout uint data[2], half2 uv, int m) { half4 decrypt = half4(r, g, b, a); return half4(GammaCorrection(decrypt.rgb), decrypt.a); -} \ No newline at end of file +} diff --git a/Runtime/Shader/ShellProtector.cginc b/Runtime/Shader/ShellProtector.cginc index e04c49a..e660657 100644 --- a/Runtime/Shader/ShellProtector.cginc +++ b/Runtime/Shader/ShellProtector.cginc @@ -6,6 +6,11 @@ #pragma shader_feature_local _SHELL_PROTECTOR_FORMAT1 #pragma shader_feature_local _SHELL_PROTECTOR_RIMLIGHT +// Unity also compiles a no-keyword variant while importing shaders. +#if !_SHELL_PROTECTOR_XXTEA && !_SHELL_PROTECTOR_CHACHA + #define _SHELL_PROTECTOR_CHACHA +#endif + // Format keywords #if !_SHELL_PROTECTOR_FORMAT0 && !_SHELL_PROTECTOR_FORMAT1 #define _SHELL_PROTECTOR_DXT @@ -61,7 +66,11 @@ half4 DecryptTexture(Texture2D tex0, Texture2D tex1, SamplerState tex0Sampler, h }; uint data[_SHELL_PROTECTOR_DATA_LENGTH]; +#ifdef _SHELL_PROTECTOR_DXT GetData(tex1, tex0Sampler, data, uv, m); +#else + GetData(tex0, tex0Sampler, data, uv, m); +#endif Decrypt(data, key); return GetPixel(tex0, tex0Sampler, data, uv, m); } @@ -140,4 +149,4 @@ inline bool IsDecrypted() { (int)round(_Key12), (int)round(_Key13), (int)round(_Key14), (int)round(_Key15) }; return SimpleHash(key) == _PasswordHash; -} \ No newline at end of file +} diff --git a/Runtime/Shader/XXTEA.cginc b/Runtime/Shader/XXTEA.cginc index cb6eb90..a4fbd99 100644 --- a/Runtime/Shader/XXTEA.cginc +++ b/Runtime/Shader/XXTEA.cginc @@ -1,7 +1,11 @@ #pragma once static const uint Delta = 0x9e3779b9; -static const uint ROUNDS = 6; -static const uint SUM = Delta * ROUNDS; +uint _Rounds; + +uint GetXXTEARounds(uint n) +{ + return _Rounds == 0 ? 6 + 52 / n : _Rounds; +} void Decrypt(inout uint data[3], const uint key[4]) { @@ -9,10 +13,11 @@ void Decrypt(inout uint data[3], const uint key[4]) uint v0, v1, sum; uint p, e; - sum = SUM; + uint rounds = GetXXTEARounds(n); + sum = Delta * rounds; v0 = data[0]; - for(int i = 0; i < ROUNDS; ++i) + for(uint i = 0; i < rounds; ++i) { e = (sum >> 2) & 3; for (p = n-1; p > 0; p--) @@ -34,10 +39,11 @@ void Decrypt(inout uint data[2], const uint key[4]) uint v0, v1, sum; uint p, e; - sum = SUM; + uint rounds = GetXXTEARounds(n); + sum = Delta * rounds; v0 = data[0]; - for(int i = 0; i < ROUNDS; ++i) + for(uint i = 0; i < rounds; ++i) { e = (sum >> 2) & 3; for (p = n-1; p > 0; p--) @@ -51,4 +57,4 @@ void Decrypt(inout uint data[2], const uint key[4]) v0 = data[0]; sum -= Delta; } -} \ No newline at end of file +} diff --git a/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock b/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock index 9f01063..82ab85e 100644 --- a/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock +++ b/Runtime/liltoonProtector/Shaders/lilCustomShaderProperties.lilblock @@ -22,5 +22,6 @@ _Nonce0 ("Nonce", integer) = 0 _Nonce1 ("Nonce", integer) = 0 _Nonce2 ("Nonce", integer) = 0 + _Rounds ("Rounds", integer) = 0 _PasswordHash ("PasswordHash", integer) = 0 - _HashMagic ("HashMagic", integer) = 0 \ No newline at end of file + _HashMagic ("HashMagic", integer) = 0 diff --git a/Tests.meta b/Tests.meta new file mode 100644 index 0000000..bd006a6 --- /dev/null +++ b/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: da102c537c144ecbb10c5c7edb29f733 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor.meta b/Tests/Editor.meta new file mode 100644 index 0000000..c3359d6 --- /dev/null +++ b/Tests/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 956601edd67e49dc84b71d3072016f1f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Gpu.meta b/Tests/Editor/Gpu.meta new file mode 100644 index 0000000..f24d1d0 --- /dev/null +++ b/Tests/Editor/Gpu.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f33a865bba384e6dab3f2c5d95587d3b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs b/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs new file mode 100644 index 0000000..4105f38 --- /dev/null +++ b/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs @@ -0,0 +1,187 @@ +#if UNITY_EDITOR +using System; +using NUnit.Framework; +using UnityEngine; + +namespace Shell.Protector.Tests.Gpu +{ + public class GpuTextureDecryptionTests + { + private const int Size = 16; + private static readonly byte[] KeyBytes = KeyGenerator.MakeKeyBytes("password", "pass", 12); + private Material referenceMaterial; + private Material decryptMaterial; + private Texture2D mipTexture; + + [SetUp] + public void SetUp() + { + Shader shader = Shader.Find("Hidden/ShellProtectorGpuDecryptTest"); + Assert.That(shader, Is.Not.Null, "GPU decrypt test shader was not imported."); + Assert.That(shader.isSupported, Is.True, "GPU decrypt test shader is not supported or failed to compile."); + + referenceMaterial = new Material(shader); + decryptMaterial = new Material(shader); + mipTexture = new Texture2D(1, 1, TextureFormat.RGB24, false, true); + mipTexture.SetPixel(0, 0, Color.black); + mipTexture.filterMode = FilterMode.Point; + mipTexture.Apply(false, false); + } + + [TearDown] + public void TearDown() + { + UnityEngine.Object.DestroyImmediate(referenceMaterial); + UnityEngine.Object.DestroyImmediate(decryptMaterial); + UnityEngine.Object.DestroyImmediate(mipTexture); + } + + [TestCase(TextureFormat.RGB24, false, false)] + [TestCase(TextureFormat.RGB24, false, true)] + [TestCase(TextureFormat.RGBA32, true, false)] + [TestCase(TextureFormat.RGBA32, true, true)] + [TestCase(TextureFormat.DXT1, false, false)] + [TestCase(TextureFormat.DXT1, false, true)] + [TestCase(TextureFormat.DXT5, true, false)] + [TestCase(TextureFormat.DXT5, true, true)] + public void XxteaEncryptedTexture_DecryptsToOriginalGpuSample(TextureFormat format, bool alpha, bool bilinear) + { + XXTEA xxtea = new XXTEA { m_rounds = 20 }; + + AssertDecryptsToOriginalGpuSample(format, alpha, bilinear, xxtea, material => + { + material.SetInteger("_Rounds", 20); + }); + } + + [TestCase(TextureFormat.RGB24, false, false)] + [TestCase(TextureFormat.RGB24, false, true)] + [TestCase(TextureFormat.RGBA32, true, false)] + [TestCase(TextureFormat.RGBA32, true, true)] + [TestCase(TextureFormat.DXT1, false, false)] + [TestCase(TextureFormat.DXT1, false, true)] + [TestCase(TextureFormat.DXT5, true, false)] + [TestCase(TextureFormat.DXT5, true, true)] + public void ChachaEncryptedTexture_DecryptsToOriginalGpuSample(TextureFormat format, bool alpha, bool bilinear) + { + Chacha20 chacha = new Chacha20(); + for (int i = 0; i < chacha.nonce.Length; i++) + chacha.nonce[i] = (byte)i; + + AssertDecryptsToOriginalGpuSample(format, alpha, bilinear, chacha, material => + { + uint[] nonce = chacha.GetNonceUint3(); + material.SetInteger("_Nonce0", unchecked((int)nonce[0])); + material.SetInteger("_Nonce1", unchecked((int)nonce[1])); + material.SetInteger("_Nonce2", unchecked((int)nonce[2])); + }); + } + + private void AssertDecryptsToOriginalGpuSample(TextureFormat format, bool alpha, bool bilinear, IEncryptor encryptor, Action configureCipher) + { + Texture2D original = TestAssetScope.CreatePatternTexture(Size, Size, format, alpha); + original.filterMode = FilterMode.Point; + original.wrapMode = TextureWrapMode.Repeat; + + EncryptResult encrypted = TextureEncryptManager.EncryptTexture(original, KeyBytes, encryptor); + FinalizeTexture(encrypted.Texture1); + FinalizeTexture(encrypted.Texture2); + + ConfigureReferenceMaterial(original); + ConfigureDecryptMaterial(original, encrypted, encryptor, configureCipher); + + Color32[] reference = Render(referenceMaterial, original, 0, Size, Size); + Color32[] decrypted = Render(decryptMaterial, Texture2D.blackTexture, bilinear ? 2 : 1, Size, Size); + + AssertPixelsEqual(reference, decrypted, format, encryptor.Keyword, bilinear); + } + + private void ConfigureReferenceMaterial(Texture2D original) + { + referenceMaterial.SetTexture("_MainTex", original); + } + + private void ConfigureDecryptMaterial(Texture2D original, EncryptResult encrypted, IEncryptor encryptor, Action configureCipher) + { + decryptMaterial.shaderKeywords = Array.Empty(); + decryptMaterial.mainTexture = original; + decryptMaterial.SetTexture("_EncryptTex0", encrypted.Texture1); + decryptMaterial.SetTexture("_EncryptTex1", encrypted.Texture2 != null ? encrypted.Texture2 : Texture2D.blackTexture); + decryptMaterial.SetTexture("_MipTex", mipTexture); + + for (int i = 0; i < KeyBytes.Length; i++) + decryptMaterial.SetFloat("_Key" + i, KeyBytes[i]); + + var offsets = TextureEncryptManager.CalculateOffsets(original); + decryptMaterial.SetInteger("_Woffset", offsets.Item1); + decryptMaterial.SetInteger("_Hoffset", offsets.Item2); + decryptMaterial.SetInteger("_HashMagic", unchecked((int)0x12345678u)); + decryptMaterial.SetInteger("_PasswordHash", unchecked((int)KeyGenerator.SimpleHash(KeyBytes, 0x12345678u))); + + TextureEncryptManager.SetFormatKeywords(decryptMaterial); + decryptMaterial.EnableKeyword(encryptor.Keyword); + configureCipher(decryptMaterial); + } + + private static void FinalizeTexture(Texture2D texture) + { + if (texture == null) + return; + + texture.filterMode = FilterMode.Point; + texture.wrapMode = TextureWrapMode.Repeat; + texture.Apply(false, false); + } + + private static Color32[] Render(Material material, Texture source, int pass, int width, int height) + { + RenderTexture target = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); + target.filterMode = FilterMode.Point; + target.Create(); + + RenderTexture previous = RenderTexture.active; + Texture2D readback = new Texture2D(width, height, TextureFormat.RGBA32, false, true); + try + { + Graphics.Blit(source, target, material, pass); + RenderTexture.active = target; + readback.ReadPixels(new Rect(0, 0, width, height), 0, 0); + readback.Apply(false, false); + return readback.GetPixels32(); + } + finally + { + RenderTexture.active = previous; + target.Release(); + UnityEngine.Object.DestroyImmediate(target); + UnityEngine.Object.DestroyImmediate(readback); + } + } + + private static void AssertPixelsEqual(Color32[] expected, Color32[] actual, TextureFormat format, string keyword, bool bilinear) + { + Assert.That(actual.Length, Is.EqualTo(expected.Length)); + for (int i = 0; i < expected.Length; i++) + { + if (!expected[i].Equals(actual[i])) + { + Assert.Fail( + "{0} {1} {2} pixel {3} differs. Expected RGBA({4},{5},{6},{7}), Actual RGBA({8},{9},{10},{11})", + format, + keyword, + bilinear ? "bilinear" : "box", + i, + expected[i].r, + expected[i].g, + expected[i].b, + expected[i].a, + actual[i].r, + actual[i].g, + actual[i].b, + actual[i].a); + } + } + } + } +} +#endif diff --git a/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs.meta b/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs.meta new file mode 100644 index 0000000..8d51d52 --- /dev/null +++ b/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e68ab58df2a4a15802eecb0ccd45afb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Gpu/Shaders.meta b/Tests/Editor/Gpu/Shaders.meta new file mode 100644 index 0000000..3f4444f --- /dev/null +++ b/Tests/Editor/Gpu/Shaders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 95fb310bf78a4821867f6d7b494e3b1c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Gpu/Shaders/Hidden.meta b/Tests/Editor/Gpu/Shaders/Hidden.meta new file mode 100644 index 0000000..1b00bf5 --- /dev/null +++ b/Tests/Editor/Gpu/Shaders/Hidden.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b41acc594bfe4585ad33ad4fc6c6b9c9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader b/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader new file mode 100644 index 0000000..aa9e01f --- /dev/null +++ b/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader @@ -0,0 +1,120 @@ +Shader "Hidden/ShellProtectorGpuDecryptTest" +{ + Properties + { + _MainTex ("Main", 2D) = "white" {} + _MipTex ("Mip", 2D) = "black" {} + _EncryptTex0 ("Encrypted0", 2D) = "white" {} + _EncryptTex1 ("Encrypted1", 2D) = "white" {} + _Key0 ("key0", Float) = 0 + _Key1 ("key1", Float) = 0 + _Key2 ("key2", Float) = 0 + _Key3 ("key3", Float) = 0 + _Key4 ("key4", Float) = 0 + _Key5 ("key5", Float) = 0 + _Key6 ("key6", Float) = 0 + _Key7 ("key7", Float) = 0 + _Key8 ("key8", Float) = 0 + _Key9 ("key9", Float) = 0 + _Key10 ("key10", Float) = 0 + _Key11 ("key11", Float) = 0 + _Key12 ("key12", Float) = 0 + _Key13 ("key13", Float) = 0 + _Key14 ("key14", Float) = 0 + _Key15 ("key15", Float) = 0 + _Woffset ("Woffset", Integer) = 0 + _Hoffset ("Hoffset", Integer) = 0 + _HashMagic ("HashMagic", Integer) = 0 + _PasswordHash ("PasswordHash", Integer) = 0 + _Nonce0 ("Nonce0", Integer) = 0 + _Nonce1 ("Nonce1", Integer) = 0 + _Nonce2 ("Nonce2", Integer) = 0 + _Rounds ("Rounds", Integer) = 0 + } + + SubShader + { + Cull Off ZWrite Off ZTest Always + + Pass + { + Name "Reference" + CGPROGRAM + #pragma vertex vert_img + #pragma fragment frag + #include "UnityCG.cginc" + + sampler2D _MainTex; + + half3 ShellProtectorGammaCorrection(half3 rgb) + { + return rgb * rgb * (rgb * (half)0.2 + (half)0.8); + } + + half4 frag(v2f_img i) : SV_Target + { + half4 color = tex2D(_MainTex, i.uv); + return half4(ShellProtectorGammaCorrection(color.rgb), color.a); + } + ENDCG + } + + Pass + { + Name "DecryptBox" + CGPROGRAM + #pragma target 5.0 + #pragma vertex vert_img + #pragma fragment frag + #pragma shader_feature_local _SHELL_PROTECTOR_XXTEA + #pragma shader_feature_local _SHELL_PROTECTOR_CHACHA + #pragma shader_feature_local _SHELL_PROTECTOR_FORMAT0 + #pragma shader_feature_local _SHELL_PROTECTOR_FORMAT1 + #include "UnityCG.cginc" + + Texture2D _EncryptTex0; + Texture2D _EncryptTex1; + Texture2D _MipTex; + SamplerState point_repeat_sampler; + half4 _EncryptTex0_TexelSize; + int _PasswordHash; + + #include "../../../../../Runtime/Shader/ShellProtector.cginc" + + half4 frag(v2f_img i) : SV_Target + { + return DecryptTextureBox(_EncryptTex0, _EncryptTex1, point_repeat_sampler, _EncryptTex0_TexelSize, _MipTex, point_repeat_sampler, i.uv); + } + ENDCG + } + + Pass + { + Name "DecryptBilinear" + CGPROGRAM + #pragma target 5.0 + #pragma vertex vert_img + #pragma fragment frag + #pragma shader_feature_local _SHELL_PROTECTOR_XXTEA + #pragma shader_feature_local _SHELL_PROTECTOR_CHACHA + #pragma shader_feature_local _SHELL_PROTECTOR_FORMAT0 + #pragma shader_feature_local _SHELL_PROTECTOR_FORMAT1 + #include "UnityCG.cginc" + + Texture2D _EncryptTex0; + Texture2D _EncryptTex1; + Texture2D _MipTex; + SamplerState point_repeat_sampler; + half4 _EncryptTex0_TexelSize; + int _PasswordHash; + + #include "../../../../../Runtime/Shader/ShellProtector.cginc" + + half4 frag(v2f_img i) : SV_Target + { + return DecryptTextureBilinear(_EncryptTex0, _EncryptTex1, point_repeat_sampler, _EncryptTex0_TexelSize, _MipTex, point_repeat_sampler, i.uv); + } + ENDCG + } + } +} diff --git a/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader.meta b/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader.meta new file mode 100644 index 0000000..a22a4b8 --- /dev/null +++ b/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 0b3b4eb99cd246df801a92489f283798 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Integration.meta b/Tests/Editor/Integration.meta new file mode 100644 index 0000000..86b4b3f --- /dev/null +++ b/Tests/Editor/Integration.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 40d63da9abf2483bade6abd9464d8691 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Integration/ShellProtectorPipelineTests.cs b/Tests/Editor/Integration/ShellProtectorPipelineTests.cs new file mode 100644 index 0000000..e02b77b --- /dev/null +++ b/Tests/Editor/Integration/ShellProtectorPipelineTests.cs @@ -0,0 +1,276 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using NUnit.Framework; +using Shell.Protector; +using UnityEditor; +using UnityEditor.Animations; +using UnityEngine; +using VRC.SDK3.Avatars.Components; +using VRC.SDKBase; + +namespace Shell.Protector.Tests.Integration +{ + public class ShellProtectorPipelineTests + { + private readonly List sceneObjects = new List(); + + [SetUp] + public void SetUp() + { + TestAssetScope.Reset(); + } + + [TearDown] + public void TearDown() + { + TestAssetScope.DestroyObjects(sceneObjects); + TestAssetScope.DeleteGeneratedRoot(); + } + + [Test] + public void ManualEncrypt_CreatesEncryptedAvatarAndRewritesProtectionAssets() + { + Fixture fixture = CreateFixture("Manual"); + + GameObject encryptedAvatar = fixture.Protector.Encrypt(false); + sceneObjects.Add(encryptedAvatar); + + Assert.That(encryptedAvatar, Is.Not.Null); + Assert.That(encryptedAvatar, Is.Not.SameAs(fixture.Avatar)); + Assert.That(encryptedAvatar.name, Does.Contain("_encrypted")); + Assert.That(fixture.Avatar.activeSelf, Is.False); + Assert.That(encryptedAvatar.GetComponentInChildren(true), Is.Null); + Assert.That(encryptedAvatar.GetComponentInChildren(true), Is.Not.Null); + + AssertEncryptedRenderer(encryptedAvatar, fixture.Material); + AssertFxController(encryptedAvatar); + AssertExpressionParameters(encryptedAvatar); + AssertBlendShapeWasObfuscated(encryptedAvatar); + AssertAnimationMaterialWasRewritten(encryptedAvatar, fixture.Material); + } + + [Test] + public void InPlaceEncrypt_RewritesOriginalAvatarWhenNdmfStyleStepsRun() + { + Fixture fixture = CreateFixture("InPlace"); + + GameObject avatar = fixture.Protector.Encrypt(true); + fixture.Protector.ReplaceMaterials(avatar); + fixture.Protector.RemoveDuplicatedTextures(avatar); + fixture.Protector.SetAnimations(avatar, false); + fixture.Protector.ObfuscateBlendShape(avatar, false); + fixture.Protector.ChangeMaterialsInAnims(avatar, false); + fixture.Protector.CleanComponent(avatar); + + Assert.That(avatar, Is.SameAs(fixture.Avatar)); + Assert.That(avatar.activeSelf, Is.True); + Assert.That(avatar.GetComponentInChildren(true), Is.Null); + Assert.That(avatar.GetComponentInChildren(true), Is.Null); + + AssertEncryptedRenderer(avatar, fixture.Material); + AssertFxController(avatar); + AssertExpressionParameters(avatar); + AssertBlendShapeWasObfuscated(avatar); + AssertAnimationMaterialWasRewritten(avatar, fixture.Material); + } + + private Fixture CreateFixture(string name) + { + Texture2D texture = TestAssetScope.CreatePatternTexture(128, 128, TextureFormat.RGBA32, true); + texture.name = name + "Texture"; + string texturePath = TestAssetScope.CreateAsset(texture, name + "/texture.asset"); + texture = AssetDatabase.LoadAssetAtPath(texturePath); + + Shader lilToon = AssetDatabase.LoadAssetAtPath("Packages/jp.lilxyzw.liltoon/Shader/lts.shader"); + Assert.That(lilToon, Is.Not.Null, "lilToon lts.shader is required for the integration fixture."); + + Material material = new Material(lilToon); + material.name = name + "Material"; + material.mainTexture = texture; + string materialPath = TestAssetScope.CreateAsset(material, name + "/material.mat"); + material = AssetDatabase.LoadAssetAtPath(materialPath); + + Mesh mesh = TestAssetScope.CreateBlendShapeQuadMesh(); + string meshPath = TestAssetScope.CreateAsset(mesh, name + "/mesh.asset"); + mesh = AssetDatabase.LoadAssetAtPath(meshPath); + + AnimationClip materialClip = CreateMaterialClip(name, material); + AnimatorController fx = CreateFxController(name, materialClip); + ScriptableObject parameters = CreateExpressionParameters(name); + + GameObject avatar = new GameObject(name + "Avatar"); + sceneObjects.Add(avatar); + + GameObject body = new GameObject("Body"); + body.transform.SetParent(avatar.transform, false); + SkinnedMeshRenderer renderer = body.AddComponent(); + renderer.sharedMesh = mesh; + renderer.sharedMaterial = material; + + VRCAvatarDescriptor descriptor = avatar.AddComponent(); + VrcExpressionParametersTestUtil.SetDescriptorParameters(descriptor, parameters); + descriptor.VisemeBlendShapes = Enumerable.Repeat(string.Empty, (int)VRC_AvatarDescriptor.Viseme.Count).ToArray(); + var eyeSettings = descriptor.customEyeLookSettings; + eyeSettings.eyelidsBlendshapes = new int[0]; + descriptor.customEyeLookSettings = eyeSettings; + descriptor.baseAnimationLayers = new VRCAvatarDescriptor.CustomAnimLayer[5]; + descriptor.baseAnimationLayers[4] = new VRCAvatarDescriptor.CustomAnimLayer + { + type = VRCAvatarDescriptor.AnimLayerType.FX, + isDefault = false, + animatorController = fx + }; + + ShellProtector protector = avatar.AddComponent(); + protector.descriptor = descriptor; + protector.assetDir = TestAssetScope.GeneratedRoot; + SetSerializedField(protector, "gameobjectList", new List { avatar }); + SetSerializedField(protector, "algorithm", 1); + SetSerializedField(protector, "filter", 0); + SetSerializedField(protector, "fallback", 5); + SetSerializedField(protector, "keySize", 12); + SetSerializedField(protector, "syncSize", 1); + protector.Init(); + + return new Fixture + { + Avatar = avatar, + Protector = protector, + Material = material + }; + } + + private static AnimationClip CreateMaterialClip(string name, Material material) + { + AnimationClip clip = new AnimationClip(); + clip.name = name + "MaterialSwap"; + EditorCurveBinding binding = EditorCurveBinding.PPtrCurve("Body", typeof(SkinnedMeshRenderer), "m_Materials.Array.data[0]"); + AnimationUtility.SetObjectReferenceCurve(clip, binding, new[] + { + new ObjectReferenceKeyframe { time = 0f, value = material } + }); + string path = TestAssetScope.CreateAsset(clip, name + "/materialSwap.anim"); + return AssetDatabase.LoadAssetAtPath(path); + } + + private static AnimatorController CreateFxController(string name, AnimationClip materialClip) + { + string path = TestAssetScope.GeneratedRoot + "/" + name + "/fx.controller"; + TestAssetScope.EnsureFolder(TestAssetScope.GeneratedRoot + "/" + name); + AnimatorController controller = AnimatorController.CreateAnimatorControllerAtPath(path); + AnimatorControllerLayer layer = controller.layers[0]; + AnimatorState state = layer.stateMachine.AddState("MaterialSwap"); + state.motion = materialClip; + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + return AssetDatabase.LoadAssetAtPath(path); + } + + private static ScriptableObject CreateExpressionParameters(string name) + { + ScriptableObject parameters = VrcExpressionParametersTestUtil.Create( + name + "Params", + new VrcExpressionParametersTestUtil.ParameterSpec + { + Name = "existing", + Saved = false, + NetworkSynced = false, + ValueType = "Bool", + DefaultValue = 0f + }); + string path = TestAssetScope.CreateAsset(parameters, name + "/params.asset"); + return AssetDatabase.LoadAssetAtPath(path); + } + + private static void AssertEncryptedRenderer(GameObject avatar, Material originalMaterial) + { + SkinnedMeshRenderer renderer = avatar.transform.Find("Body").GetComponent(); + Material encryptedMaterial = renderer.sharedMaterial; + + Assert.That(encryptedMaterial, Is.Not.Null); + Assert.That(encryptedMaterial, Is.Not.SameAs(originalMaterial)); + Assert.That(encryptedMaterial.name, Does.Contain("_encrypted")); + Assert.That(encryptedMaterial.mainTexture, Is.Not.SameAs(originalMaterial.mainTexture)); + Assert.That(encryptedMaterial.GetTexture("_EncryptTex0"), Is.Not.Null); + Assert.That(encryptedMaterial.GetTexture("_MipTex"), Is.Not.Null); + Assert.That(encryptedMaterial.GetTag("VRCFallback", false), Is.EqualTo("Unlit")); + Assert.That(encryptedMaterial.IsKeywordEnabled("_SHELL_PROTECTOR_CHACHA"), Is.True); + Assert.That(AssetDatabase.GetAssetPath(encryptedMaterial), Does.StartWith(TestAssetScope.GeneratedRoot)); + } + + private static void AssertFxController(GameObject avatar) + { + AnimatorController fx = ShellProtector.Getfx(avatar); + + Assert.That(fx, Is.Not.Null); + Assert.That(fx.layers.Select(l => l.name), Does.Contain("ShellProtector")); + Assert.That(fx.layers.Select(l => l.name), Does.Contain("ShellProtectorDemux")); + Assert.That(fx.parameters.Select(p => p.name), Does.Contain("SHELL_PROTECTOR_key0")); + Assert.That(fx.parameters.Select(p => p.name), Does.Contain("encrypt_lock")); + } + + private static void AssertExpressionParameters(GameObject avatar) + { + VRCAvatarDescriptor descriptor = avatar.GetComponent(); + ScriptableObject parameters = VrcExpressionParametersTestUtil.GetDescriptorParameters(descriptor); + VrcExpressionParametersTestUtil.ParameterSnapshot[] snapshots = VrcExpressionParametersTestUtil.Read(parameters).ToArray(); + + Assert.That(parameters, Is.Not.Null); + Assert.That(snapshots.Select(p => p.Name), Does.Contain("SHELL_PROTECTOR_key11")); + Assert.That(snapshots.Select(p => p.Name), Does.Contain("encrypt_lock")); + Assert.That(AssetDatabase.GetAssetPath(parameters), Does.StartWith(TestAssetScope.GeneratedRoot)); + } + + private static void AssertBlendShapeWasObfuscated(GameObject avatar) + { + SkinnedMeshRenderer renderer = avatar.transform.Find("Body").GetComponent(); + + Assert.That(renderer.sharedMesh.blendShapeCount, Is.EqualTo(1)); + Assert.That(renderer.sharedMesh.GetBlendShapeName(0), Is.Not.EqualTo("Smile")); + Assert.That(AssetDatabase.GetAssetPath(renderer.sharedMesh), Does.StartWith(TestAssetScope.GeneratedRoot)); + } + + private static void AssertAnimationMaterialWasRewritten(GameObject avatar, Material originalMaterial) + { + AnimatorController fx = ShellProtector.Getfx(avatar); + Material encryptedMaterial = avatar.transform.Find("Body").GetComponent().sharedMaterial; + List clips = new List(); + + foreach (AnimatorControllerLayer layer in fx.layers) + CollectClips(layer.stateMachine, clips); + + Assert.That(clips.Count, Is.GreaterThan(0)); + Assert.That(clips.Any(clip => AnimatorManager.IsMaterialInClip(clip, originalMaterial)), Is.False); + Assert.That(clips.Any(clip => AnimatorManager.IsMaterialInClip(clip, encryptedMaterial)), Is.True); + } + + private static void CollectClips(AnimatorStateMachine stateMachine, List clips) + { + foreach (ChildAnimatorState state in stateMachine.states) + { + if (state.state.motion is AnimationClip clip) + clips.Add(clip); + } + + foreach (ChildAnimatorStateMachine child in stateMachine.stateMachines) + CollectClips(child.stateMachine, clips); + } + + private static void SetSerializedField(ShellProtector protector, string fieldName, T value) + { + FieldInfo field = typeof(ShellProtector).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + Assert.That(field, Is.Not.Null, fieldName); + field.SetValue(protector, value); + } + + private class Fixture + { + public GameObject Avatar; + public ShellProtector Protector; + public Material Material; + } + } +} +#endif diff --git a/Tests/Editor/Integration/ShellProtectorPipelineTests.cs.meta b/Tests/Editor/Integration/ShellProtectorPipelineTests.cs.meta new file mode 100644 index 0000000..31ebd24 --- /dev/null +++ b/Tests/Editor/Integration/ShellProtectorPipelineTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 717ea5bafb884615a36c02079d25342f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Shared.meta b/Tests/Editor/Shared.meta new file mode 100644 index 0000000..de2c6bc --- /dev/null +++ b/Tests/Editor/Shared.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dedafe08f345437d8ba765b4997456a5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Shared/TestAssetScope.cs b/Tests/Editor/Shared/TestAssetScope.cs new file mode 100644 index 0000000..0c653a9 --- /dev/null +++ b/Tests/Editor/Shared/TestAssetScope.cs @@ -0,0 +1,172 @@ +#if UNITY_EDITOR +using System; +using System.Collections.Generic; +using System.IO; +using UnityEditor; +using UnityEngine; + +namespace Shell.Protector.Tests +{ + internal static class TestAssetScope + { + public const string GeneratedRoot = "Assets/ShellProtector/Tests/__Generated"; + + public static void Reset() + { + DeleteGeneratedRoot(); + EnsureFolder(GeneratedRoot); + } + + public static void DeleteGeneratedRoot() + { + if (AssetDatabase.IsValidFolder(GeneratedRoot)) + { + AssetDatabase.DeleteAsset(GeneratedRoot); + AssetDatabase.Refresh(); + } + } + + public static void EnsureFolder(string assetPath) + { + string normalized = assetPath.Replace('\\', '/').TrimEnd('/'); + string[] parts = normalized.Split('/'); + if (parts.Length == 0 || parts[0] != "Assets") + throw new ArgumentException("Generated test assets must live under Assets.", nameof(assetPath)); + + string current = "Assets"; + for (int i = 1; i < parts.Length; i++) + { + string next = current + "/" + parts[i]; + if (!AssetDatabase.IsValidFolder(next)) + AssetDatabase.CreateFolder(current, parts[i]); + current = next; + } + } + + public static string CreateAsset(T asset, string relativePath) where T : UnityEngine.Object + { + EnsureFolder(GeneratedRoot); + string path = GeneratedRoot + "/" + relativePath.Replace('\\', '/'); + EnsureFolder(Path.GetDirectoryName(path).Replace('\\', '/')); + AssetDatabase.CreateAsset(asset, path); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + return path; + } + + public static Texture2D CreatePatternTexture(int width, int height, TextureFormat format, bool alpha) + { + Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, true, true); + Color32[] pixels = new Color32[width * height]; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + byte r = (byte)((x * 17 + y * 3) & 0xFF); + byte g = (byte)((x * 5 + y * 29) & 0xFF); + byte b = (byte)((x * 11 + y * 7 + 31) & 0xFF); + byte a = alpha ? (byte)(64 + ((x * 13 + y * 19) & 0x7F)) : (byte)255; + pixels[y * width + x] = new Color32(r, g, b, a); + } + } + if (format == TextureFormat.DXT1 || format == TextureFormat.DXT5) + FillDxtStablePattern(pixels, width, height, alpha); + + texture.SetPixels32(pixels); + texture.Apply(true, false); + + if (format == TextureFormat.RGB24) + { + Texture2D rgb = new Texture2D(width, height, TextureFormat.RGB24, true, true); + rgb.SetPixels32(texture.GetPixels32()); + rgb.Apply(true, false); + UnityEngine.Object.DestroyImmediate(texture); + return rgb; + } + + if (format == TextureFormat.RGBA32) + return texture; + + TextureFormat uncompressedFormat = format == TextureFormat.DXT1 ? TextureFormat.RGB24 : TextureFormat.RGBA32; + Texture2D dxt = new Texture2D(width, height, uncompressedFormat, true, true); + dxt.SetPixels32(texture.GetPixels32()); + dxt.Apply(true, false); + dxt.Compress(format == TextureFormat.DXT5); + dxt.Apply(true, false); + UnityEngine.Object.DestroyImmediate(texture); + return dxt; + } + + private static void FillDxtStablePattern(Color32[] pixels, int width, int height, bool alpha) + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int block = (x / 4) + (y / 4) * (width / 4); + Color32 c0 = new Color32( + Expand5((block * 3 + 4) & 31), + Expand6((block * 5 + 9) & 63), + Expand5((block * 7 + 13) & 31), + alpha ? (byte)96 : (byte)255); + Color32 c1 = new Color32( + Expand5((block * 11 + 17) & 31), + Expand6((block * 13 + 21) & 63), + Expand5((block * 15 + 25) & 31), + alpha ? (byte)192 : (byte)255); + pixels[y * width + x] = ((x + y) & 1) == 0 ? c0 : c1; + } + } + } + + private static byte Expand5(int value) + { + return (byte)((value * 255 + 15) / 31); + } + + private static byte Expand6(int value) + { + return (byte)((value * 255 + 31) / 63); + } + + public static Mesh CreateBlendShapeQuadMesh() + { + Mesh mesh = new Mesh(); + mesh.name = "ShellProtectorTestMesh"; + mesh.vertices = new[] + { + new Vector3(-0.5f, -0.5f, 0f), + new Vector3(0.5f, -0.5f, 0f), + new Vector3(-0.5f, 0.5f, 0f), + new Vector3(0.5f, 0.5f, 0f) + }; + mesh.uv = new[] + { + new Vector2(0f, 0f), + new Vector2(1f, 0f), + new Vector2(0f, 1f), + new Vector2(1f, 1f) + }; + mesh.triangles = new[] { 0, 2, 1, 2, 3, 1 }; + mesh.RecalculateNormals(); + + Vector3[] deltaVertices = new Vector3[mesh.vertexCount]; + Vector3[] deltaNormals = new Vector3[mesh.vertexCount]; + Vector3[] deltaTangents = new Vector3[mesh.vertexCount]; + for (int i = 0; i < deltaVertices.Length; i++) + deltaVertices[i] = new Vector3(0f, 0.01f * (i + 1), 0f); + mesh.AddBlendShapeFrame("Smile", 100f, deltaVertices, deltaNormals, deltaTangents); + return mesh; + } + + public static void DestroyObjects(IEnumerable objects) + { + foreach (UnityEngine.Object obj in objects) + { + if (obj != null) + UnityEngine.Object.DestroyImmediate(obj); + } + } + } +} +#endif diff --git a/Tests/Editor/Shared/TestAssetScope.cs.meta b/Tests/Editor/Shared/TestAssetScope.cs.meta new file mode 100644 index 0000000..236dbb9 --- /dev/null +++ b/Tests/Editor/Shared/TestAssetScope.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 714444c3456449f9b92ad7aa96653347 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Shared/VrcExpressionParametersTestUtil.cs b/Tests/Editor/Shared/VrcExpressionParametersTestUtil.cs new file mode 100644 index 0000000..419b91c --- /dev/null +++ b/Tests/Editor/Shared/VrcExpressionParametersTestUtil.cs @@ -0,0 +1,220 @@ +#if UNITY_EDITOR +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Shell.Protector; +using UnityEditor; +using UnityEngine; +using VRC.SDK3.Avatars.Components; + +namespace Shell.Protector.Tests +{ + internal static class VrcExpressionParametersTestUtil + { + public struct ParameterSpec + { + public string Name; + public bool Saved; + public bool NetworkSynced; + public string ValueType; + public float DefaultValue; + } + + public struct ParameterSnapshot + { + public string Name; + public bool Saved; + public bool NetworkSynced; + public string ValueType; + public float DefaultValue; + } + + private static Type expressionParametersType; + private static Type parameterType; + private static Type valueTypeEnum; + + public static ScriptableObject Create(string name, params ParameterSpec[] specs) + { + ScriptableObject parameters = ScriptableObject.CreateInstance(ExpressionParametersType); + parameters.name = name; + SetParameters(parameters, specs); + return parameters; + } + + public static ScriptableObject AddKeyParameter(ScriptableObject parameters, int keyLength, int syncSize) + { + MethodInfo method = typeof(ParameterManager).GetMethod( + "AddKeyParameter", + BindingFlags.Public | BindingFlags.Static); + AssertMethod(method, "ParameterManager.AddKeyParameter"); + return (ScriptableObject)method.Invoke(null, new object[] { parameters, keyLength, syncSize }); + } + + public static IReadOnlyList Read(ScriptableObject parameters) + { + object[] values = GetParameterArray(parameters); + return values.Select(ReadParameter).ToArray(); + } + + public static void SetDescriptorParameters(VRCAvatarDescriptor descriptor, ScriptableObject parameters) + { + FieldInfo field = typeof(VRCAvatarDescriptor).GetField("expressionParameters"); + if (field != null) + { + field.SetValue(descriptor, parameters); + return; + } + + PropertyInfo property = typeof(VRCAvatarDescriptor).GetProperty("expressionParameters"); + if (property != null) + { + property.SetValue(descriptor, parameters); + return; + } + + SerializedObject serializedDescriptor = new SerializedObject(descriptor); + SerializedProperty serializedProperty = serializedDescriptor.FindProperty("expressionParameters"); + if (serializedProperty == null) + throw new MissingMemberException(typeof(VRCAvatarDescriptor).FullName, "expressionParameters"); + + serializedProperty.objectReferenceValue = parameters; + serializedDescriptor.ApplyModifiedPropertiesWithoutUndo(); + } + + public static ScriptableObject GetDescriptorParameters(VRCAvatarDescriptor descriptor) + { + FieldInfo field = typeof(VRCAvatarDescriptor).GetField("expressionParameters"); + if (field != null) + return (ScriptableObject)field.GetValue(descriptor); + + PropertyInfo property = typeof(VRCAvatarDescriptor).GetProperty("expressionParameters"); + if (property != null) + return (ScriptableObject)property.GetValue(descriptor); + + SerializedObject serializedDescriptor = new SerializedObject(descriptor); + SerializedProperty serializedProperty = serializedDescriptor.FindProperty("expressionParameters"); + return serializedProperty == null ? null : serializedProperty.objectReferenceValue as ScriptableObject; + } + + private static Type ExpressionParametersType + { + get + { + expressionParametersType ??= FindType("VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionParameters"); + return expressionParametersType; + } + } + + private static Type ParameterType + { + get + { + parameterType ??= ExpressionParametersType.GetNestedType("Parameter"); + return parameterType; + } + } + + private static Type ValueTypeEnum + { + get + { + valueTypeEnum ??= ExpressionParametersType.GetNestedType("ValueType"); + return valueTypeEnum; + } + } + + private static void SetParameters(ScriptableObject parameters, ParameterSpec[] specs) + { + Array values = Array.CreateInstance(ParameterType, specs.Length); + for (int i = 0; i < specs.Length; i++) + { + object parameter = Activator.CreateInstance(ParameterType); + SetMember(parameter, "name", specs[i].Name); + SetMember(parameter, "saved", specs[i].Saved); + SetMember(parameter, "networkSynced", specs[i].NetworkSynced); + SetMember(parameter, "valueType", Enum.Parse(ValueTypeEnum, specs[i].ValueType)); + SetMember(parameter, "defaultValue", specs[i].DefaultValue); + values.SetValue(parameter, i); + } + + SetMember(parameters, "parameters", values); + } + + private static object[] GetParameterArray(ScriptableObject parameters) + { + object value = GetMember(parameters, "parameters"); + if (value is Array array) + { + object[] values = new object[array.Length]; + array.CopyTo(values, 0); + return values; + } + + return Array.Empty(); + } + + private static ParameterSnapshot ReadParameter(object parameter) + { + return new ParameterSnapshot + { + Name = (string)GetMember(parameter, "name"), + Saved = (bool)GetMember(parameter, "saved"), + NetworkSynced = (bool)GetMember(parameter, "networkSynced"), + ValueType = GetMember(parameter, "valueType").ToString(), + DefaultValue = (float)GetMember(parameter, "defaultValue") + }; + } + + private static Type FindType(string fullName) + { + Type type = AppDomain.CurrentDomain.GetAssemblies() + .Select(assembly => assembly.GetType(fullName, false)) + .FirstOrDefault(candidate => candidate != null); + + if (type == null) + throw new TypeLoadException(fullName); + + return type; + } + + private static object GetMember(object target, string name) + { + FieldInfo field = target.GetType().GetField(name); + if (field != null) + return field.GetValue(target); + + PropertyInfo property = target.GetType().GetProperty(name); + if (property != null) + return property.GetValue(target); + + throw new MissingMemberException(target.GetType().FullName, name); + } + + private static void SetMember(object target, string name, object value) + { + FieldInfo field = target.GetType().GetField(name); + if (field != null) + { + field.SetValue(target, value); + return; + } + + PropertyInfo property = target.GetType().GetProperty(name); + if (property != null) + { + property.SetValue(target, value); + return; + } + + throw new MissingMemberException(target.GetType().FullName, name); + } + + private static void AssertMethod(MethodInfo method, string name) + { + if (method == null) + throw new MissingMethodException(name); + } + } +} +#endif diff --git a/Tests/Editor/Shared/VrcExpressionParametersTestUtil.cs.meta b/Tests/Editor/Shared/VrcExpressionParametersTestUtil.cs.meta new file mode 100644 index 0000000..a0b638b --- /dev/null +++ b/Tests/Editor/Shared/VrcExpressionParametersTestUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8f6bb62a6034c6d95f74464e80520b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Shell.Protector.Editor.Tests.asmdef b/Tests/Editor/Shell.Protector.Editor.Tests.asmdef new file mode 100644 index 0000000..3e551a9 --- /dev/null +++ b/Tests/Editor/Shell.Protector.Editor.Tests.asmdef @@ -0,0 +1,28 @@ +{ + "name": "shell.protector.Editor.Tests", + "rootNamespace": "Shell.Protector.Tests", + "references": [ + "shell.protector", + "VRC.SDK3A", + "VRC.SDK3A.Editor", + "VRC.SDKBase.Editor", + "VRC.SDKBase" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [ + "VRCSDK3A.dll", + "VRCSDKBase.dll" + ], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "optionalUnityReferences": [ + "TestAssemblies" + ], + "noEngineReferences": false +} diff --git a/Tests/Editor/Shell.Protector.Editor.Tests.asmdef.meta b/Tests/Editor/Shell.Protector.Editor.Tests.asmdef.meta new file mode 100644 index 0000000..8afbb34 --- /dev/null +++ b/Tests/Editor/Shell.Protector.Editor.Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ea291350a3b04158a0d418640566cfdb +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Unit.meta b/Tests/Editor/Unit.meta new file mode 100644 index 0000000..b6c1717 --- /dev/null +++ b/Tests/Editor/Unit.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e05b0aeb969149a2b286ea2bbc0f5660 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Unit/CryptoTests.cs b/Tests/Editor/Unit/CryptoTests.cs new file mode 100644 index 0000000..0f84857 --- /dev/null +++ b/Tests/Editor/Unit/CryptoTests.cs @@ -0,0 +1,83 @@ +#if UNITY_EDITOR +using System.Linq; +using NUnit.Framework; + +namespace Shell.Protector.Tests.Unit +{ + public class CryptoTests + { + [Test] + public void MakeKeyBytes_ReturnsStablePasswordKey() + { + byte[] key = KeyGenerator.MakeKeyBytes("password", "pass", 12); + + Assert.That(ToHex(key), Is.EqualTo("70617373a72e839d8da3b9806b18c877")); + } + + [Test] + public void MakeKeyBytes_UsesFixedPasswordPrefix() + { + byte[] key = KeyGenerator.MakeKeyBytes("fixed", "user", 12); + + Assert.That(key, Has.Length.EqualTo(16)); + Assert.That(key[0], Is.EqualTo((byte)'f')); + Assert.That(key[1], Is.EqualTo((byte)'i')); + } + + [Test] + public void SimpleHash_ReturnsStableHash() + { + byte[] key = KeyGenerator.MakeKeyBytes("password", "pass", 12); + + Assert.That(KeyGenerator.SimpleHash(key, 0x12345678u), Is.EqualTo(0x94f301a9u)); + } + + [Test] + public void Xxtea_EncryptsStableVector_AndRoundTrips() + { + XXTEA xxtea = new XXTEA(); + uint[] data = { 0x01234567u, 0x89abcdefu, 0xfedcba98u }; + uint[] key = { 0x00112233u, 0x44556677u, 0x8899aabbu, 0xccddeeffu }; + + uint[] encrypted = xxtea.Encrypt(data, key); + + Assert.That(encrypted, Is.EqualTo(new[] { 0x6d601d05u, 0x17b6001eu, 0x9547b398u })); + Assert.That(xxtea.Decrypt(encrypted, key), Is.EqualTo(data)); + } + + [Test] + public void Xxtea_WithExplicitShellProtectorRounds_RoundTrips() + { + XXTEA xxtea = new XXTEA { m_rounds = 20 }; + uint[] data = { 0x10203040u, 0x50607080u }; + uint[] key = { 0x00112233u, 0x44556677u, 0x8899aabbu, 0xccddeeffu }; + + uint[] encrypted = xxtea.Encrypt(data, key); + + Assert.That(encrypted, Is.Not.EqualTo(data)); + Assert.That(xxtea.Decrypt(encrypted, key), Is.EqualTo(data)); + } + + [Test] + public void Chacha20_EncryptsStableVector_AndRoundTrips() + { + Chacha20 chacha = new Chacha20(); + for (int i = 0; i < chacha.nonce.Length; i++) + chacha.nonce[i] = (byte)i; + + uint[] data = { 0x00010203u, 0x04050607u, 0x08090a0bu, 0x0c0d0e0fu }; + uint[] key = { 0x00112233u, 0x44556677u, 0x8899aabbu, 0xccddeeffu }; + + uint[] encrypted = chacha.Encrypt(data, key); + + Assert.That(encrypted, Is.EqualTo(new[] { 0xed4198d8u, 0x7ca6601fu, 0xde134dedu, 0x76b88629u })); + Assert.That(chacha.Decrypt(encrypted, key), Is.EqualTo(data)); + } + + private static string ToHex(byte[] bytes) + { + return string.Concat(bytes.Select(b => b.ToString("x2"))); + } + } +} +#endif diff --git a/Tests/Editor/Unit/CryptoTests.cs.meta b/Tests/Editor/Unit/CryptoTests.cs.meta new file mode 100644 index 0000000..044be9c --- /dev/null +++ b/Tests/Editor/Unit/CryptoTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec88e66595dc43e685a88fba8239c55e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Unit/ParameterManagerTests.cs b/Tests/Editor/Unit/ParameterManagerTests.cs new file mode 100644 index 0000000..2a4b776 --- /dev/null +++ b/Tests/Editor/Unit/ParameterManagerTests.cs @@ -0,0 +1,70 @@ +#if UNITY_EDITOR +using System.Linq; +using NUnit.Framework; +using UnityEngine; + +namespace Shell.Protector.Tests.Unit +{ + public class ParameterManagerTests + { + [Test] + public void AddKeyParameter_UsesLegacyNamesForSyncSizeOne() + { + ScriptableObject original = CreateBaseParameters(); + + ScriptableObject result = VrcExpressionParametersTestUtil.AddKeyParameter(original, 12, 1); + VrcExpressionParametersTestUtil.ParameterSnapshot[] parameters = VrcExpressionParametersTestUtil.Read(result).ToArray(); + + Assert.That(result.name, Is.EqualTo("BaseParams_encrypted")); + Assert.That(parameters.Length, Is.EqualTo(19)); + AssertParameter(parameters, "existing", false, false, "Bool"); + AssertParameter(parameters, "encrypt_lock", true, true, "Bool"); + AssertParameter(parameters, "pkey", true, true, "Float"); + AssertParameter(parameters, "encrypt_switch0", true, true, "Bool"); + AssertParameter(parameters, "encrypt_switch3", true, true, "Bool"); + AssertParameter(parameters, "SHELL_PROTECTOR_key11", false, false, "Float"); + Assert.That(parameters.Any(p => p.Name == "SHELL_PROTECTOR_saved_key0"), Is.False); + } + + [Test] + public void AddKeyParameter_UsesPrefixedNamesForMultiSync() + { + ScriptableObject original = CreateBaseParameters(); + + ScriptableObject result = VrcExpressionParametersTestUtil.AddKeyParameter(original, 12, 3); + VrcExpressionParametersTestUtil.ParameterSnapshot[] parameters = VrcExpressionParametersTestUtil.Read(result).ToArray(); + + Assert.That(parameters.Length, Is.EqualTo(31)); + AssertParameter(parameters, "SHELL_PROTECTOR_sync_lock", true, true, "Bool"); + AssertParameter(parameters, "SHELL_PROTECTOR_synced_key0", true, true, "Float"); + AssertParameter(parameters, "SHELL_PROTECTOR_synced_key2", true, true, "Float"); + AssertParameter(parameters, "SHELL_PROTECTOR_sync_switch1", true, true, "Bool"); + AssertParameter(parameters, "SHELL_PROTECTOR_key11", false, false, "Float"); + AssertParameter(parameters, "SHELL_PROTECTOR_saved_key11", true, false, "Float"); + } + + private static ScriptableObject CreateBaseParameters() + { + return VrcExpressionParametersTestUtil.Create( + "BaseParams", + new VrcExpressionParametersTestUtil.ParameterSpec + { + Name = "existing", + Saved = false, + NetworkSynced = false, + ValueType = "Bool", + DefaultValue = 0f + }); + } + + private static void AssertParameter(VrcExpressionParametersTestUtil.ParameterSnapshot[] parameters, string name, bool saved, bool networkSynced, string valueType) + { + VrcExpressionParametersTestUtil.ParameterSnapshot parameter = parameters.Single(p => p.Name == name); + Assert.That(parameter.Saved, Is.EqualTo(saved), name); + Assert.That(parameter.NetworkSynced, Is.EqualTo(networkSynced), name); + Assert.That(parameter.ValueType, Is.EqualTo(valueType), name); + Assert.That(parameter.DefaultValue, Is.EqualTo(0f), name); + } + } +} +#endif diff --git a/Tests/Editor/Unit/ParameterManagerTests.cs.meta b/Tests/Editor/Unit/ParameterManagerTests.cs.meta new file mode 100644 index 0000000..52bdfc8 --- /dev/null +++ b/Tests/Editor/Unit/ParameterManagerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96bd17070d9a4ca1a8140a1f0715d419 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Unit/TextureEncryptManagerTests.cs b/Tests/Editor/Unit/TextureEncryptManagerTests.cs new file mode 100644 index 0000000..d233f3d --- /dev/null +++ b/Tests/Editor/Unit/TextureEncryptManagerTests.cs @@ -0,0 +1,83 @@ +#if UNITY_EDITOR +using NUnit.Framework; +using UnityEngine; + +namespace Shell.Protector.Tests.Unit +{ + public class TextureEncryptManagerTests + { + [TestCase(TextureFormat.RGB24, true)] + [TestCase(TextureFormat.RGBA32, true)] + [TestCase(TextureFormat.DXT1, false)] + [TestCase(TextureFormat.DXT5, true)] + public void SupportsExpectedTextureFormats(TextureFormat format, bool alpha) + { + Texture2D texture = TestAssetScope.CreatePatternTexture(16, 16, format, alpha); + + Assert.That(TextureEncryptManager.IsSupportedTexture(texture), Is.True); + } + + [Test] + public void CalculatesOffsetsPerFormat() + { + Texture2D rgb = TestAssetScope.CreatePatternTexture(16, 16, TextureFormat.RGB24, false); + Texture2D rgba = TestAssetScope.CreatePatternTexture(16, 16, TextureFormat.RGBA32, true); + Texture2D dxt = TestAssetScope.CreatePatternTexture(16, 16, TextureFormat.DXT1, false); + + Assert.That(TextureEncryptManager.CalculateOffsets(rgb), Is.EqualTo((8, 8))); + Assert.That(TextureEncryptManager.CalculateOffsets(rgba), Is.EqualTo((8, 8))); + Assert.That(TextureEncryptManager.CalculateOffsets(dxt), Is.EqualTo((10, 10))); + } + + [Test] + public void GeneratesMipReferenceTexture() + { + Texture2D mip = TextureEncryptManager.GenerateRefMipmap(16, 16, false); + + Assert.That(mip.width, Is.EqualTo(16)); + Assert.That(mip.height, Is.EqualTo(16)); + Assert.That(mip.GetPixels32(0)[0].r, Is.EqualTo(0)); + Assert.That(mip.GetPixels32(1)[0].r, Is.EqualTo(10)); + } + + [Test] + public void GeneratesFallbackTexture() + { + Texture2D original = TestAssetScope.CreatePatternTexture(128, 128, TextureFormat.RGBA32, true); + + Texture2D fallback = TextureEncryptManager.GenerateFallback(original, 32); + + Assert.That(fallback, Is.Not.Null); + Assert.That(fallback.width, Is.EqualTo(32)); + Assert.That(fallback.height, Is.EqualTo(32)); + Assert.That(fallback.format, Is.EqualTo(TextureFormat.DXT5)); + Assert.That(fallback.filterMode, Is.EqualTo(FilterMode.Point)); + } + + [TestCase(TextureFormat.RGB24, true, TextureFormat.RGB24)] + [TestCase(TextureFormat.RGBA32, true, TextureFormat.RGBA32)] + [TestCase(TextureFormat.DXT1, false, TextureFormat.DXT1)] + [TestCase(TextureFormat.DXT5, true, TextureFormat.DXT5)] + public void EncryptTextureCreatesExpectedResultTextures(TextureFormat sourceFormat, bool alpha, TextureFormat encryptedFormat) + { + Texture2D texture = TestAssetScope.CreatePatternTexture(16, 16, sourceFormat, alpha); + XXTEA xxtea = new XXTEA { m_rounds = 20 }; + byte[] key = KeyGenerator.MakeKeyBytes("password", "pass", 12); + + EncryptResult result = TextureEncryptManager.EncryptTexture(texture, key, xxtea); + + Assert.That(result.Texture1, Is.Not.Null); + Assert.That(result.Texture1.format, Is.EqualTo(encryptedFormat)); + if (TextureEncryptManager.IsDXTFormat(sourceFormat)) + { + Assert.That(result.Texture2, Is.Not.Null); + Assert.That(result.Texture2.format, Is.EqualTo(TextureFormat.RGBA32)); + } + else + { + Assert.That(result.Texture2, Is.Null); + } + } + } +} +#endif diff --git a/Tests/Editor/Unit/TextureEncryptManagerTests.cs.meta b/Tests/Editor/Unit/TextureEncryptManagerTests.cs.meta new file mode 100644 index 0000000..08dc32c --- /dev/null +++ b/Tests/Editor/Unit/TextureEncryptManagerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0da572332c494b3a98efcf6ed43a0540 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 122436e334d3023680d64196436f9f1afd95fd15 Mon Sep 17 00:00:00 2001 From: Lee Sangyeop Date: Fri, 3 Jul 2026 20:42:21 +0900 Subject: [PATCH 37/59] refactor: restructure shell protector pipeline --- Editor/ShellProtectorEditor.cs | 26 ++-- Editor/ShellProtectorEditorViewModel.cs | 48 ++++++ Editor/ShellProtectorEditorViewModel.cs.meta | 11 ++ Editor/TesterEditor.cs | 4 +- Editor/liltoonCustom/CustomInspector.cs | 12 +- Runtime/Scripts/Algorithm/Chacha20.cs | 4 +- Runtime/Scripts/Algorithm/IEncryptor.cs | 8 +- Runtime/Scripts/Algorithm/XXTEA.cs | 4 +- Runtime/Scripts/AnimatorManager.cs | 125 ++++++---------- Runtime/Scripts/EncryptedHistory.cs | 140 +++++++++--------- Runtime/Scripts/Formats/DXTFormat.cs | 6 +- Runtime/Scripts/Formats/Format.cs | 63 ++++---- Runtime/Scripts/Formats/RGBFormat.cs | 10 +- Runtime/Scripts/Injector/IShaderAdapter.cs | 14 ++ .../Scripts/Injector/IShaderAdapter.cs.meta | 11 ++ Runtime/Scripts/Injector/Injector.cs | 23 ++- Runtime/Scripts/Injector/InjectorFactory.cs | 22 ++- Runtime/Scripts/Injector/LilToonInjector.cs | 7 +- Runtime/Scripts/Injector/PoiyomiInjector.cs | 29 ++-- Runtime/Scripts/KeyGenerator.cs | 4 + Runtime/Scripts/Obfuscator.cs | 27 ++-- Runtime/Scripts/ParameterManager.cs | 6 +- Runtime/Scripts/Pipeline.meta | 8 + Runtime/Scripts/Pipeline/IAssetWriter.cs | 38 +++++ Runtime/Scripts/Pipeline/IAssetWriter.cs.meta | 11 ++ .../Pipeline/ShellProtectorBuildContext.cs | 41 +++++ .../ShellProtectorBuildContext.cs.meta | 11 ++ .../Pipeline/ShellProtectorBuildRequest.cs | 22 +++ .../ShellProtectorBuildRequest.cs.meta | 11 ++ .../Pipeline/ShellProtectorBuildResult.cs | 27 ++++ .../ShellProtectorBuildResult.cs.meta | 11 ++ .../Pipeline/ShellProtectorPipeline.cs | 22 +++ .../Pipeline/ShellProtectorPipeline.cs.meta | 11 ++ .../Pipeline/ShellProtectorSettings.cs | 23 +++ .../Pipeline/ShellProtectorSettings.cs.meta | 11 ++ Runtime/Scripts/ShellProtector.cs | 130 +++++++++++----- Runtime/Scripts/ShellProtectorConstants.cs | 49 ++++++ .../Scripts/ShellProtectorConstants.cs.meta | 11 ++ Runtime/Scripts/ShellProtectorTester.cs | 26 ++-- Runtime/Scripts/Test.cs | 3 + Runtime/Scripts/TextureSettings.cs | 95 +++++++----- Runtime/Scripts/VersionManager.cs | 135 +++++++---------- 42 files changed, 874 insertions(+), 426 deletions(-) create mode 100644 Editor/ShellProtectorEditorViewModel.cs create mode 100644 Editor/ShellProtectorEditorViewModel.cs.meta create mode 100644 Runtime/Scripts/Injector/IShaderAdapter.cs create mode 100644 Runtime/Scripts/Injector/IShaderAdapter.cs.meta create mode 100644 Runtime/Scripts/Pipeline.meta create mode 100644 Runtime/Scripts/Pipeline/IAssetWriter.cs create mode 100644 Runtime/Scripts/Pipeline/IAssetWriter.cs.meta create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs.meta create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs.meta create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs.meta create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs.meta create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorSettings.cs create mode 100644 Runtime/Scripts/Pipeline/ShellProtectorSettings.cs.meta create mode 100644 Runtime/Scripts/ShellProtectorConstants.cs create mode 100644 Runtime/Scripts/ShellProtectorConstants.cs.meta diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index db1b7f0..f7015e6 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -35,6 +35,7 @@ public class ShellProtectorEditor : Editor SerializedProperty bUseSmallMipTexture; SerializedProperty bPreserveMMD; SerializedProperty turnOnAllSafetyFallback; + ShellProtectorEditorViewModel viewModel; bool debug = false; bool option = true; bool ObfuscatorOption = true; @@ -114,6 +115,7 @@ void OnEnable() bPreserveMMD = serializedObject.FindProperty("bPreserveMMD"); turnOnAllSafetyFallback = serializedObject.FindProperty("turnOnAllSafetyFallback"); #endregion + viewModel = new ShellProtectorEditorViewModel(root, keySize, syncSize, gameobjectList, materialList); enc_funcs[0] = "XXTEA"; enc_funcs[1] = "Chacha8"; @@ -138,7 +140,7 @@ public override void OnInspectorGUI() { root = target as ShellProtector; - root.descriptor = EditorGUILayout.ObjectField(root.descriptor, typeof(VRCAvatarDescriptor)) as VRCAvatarDescriptor; + root.descriptor = EditorGUILayout.ObjectField(root.descriptor, typeof(VRCAvatarDescriptor), true) as VRCAvatarDescriptor; GUILayout.BeginHorizontal(); GUILayout.Label(Lang("Current version: ") + VersionManager.GetInstance().GetVersion()); GUILayout.FlexibleSpace(); @@ -210,26 +212,21 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("This password should be memorized. (max:") + keySize.intValue + ")", EditorStyles.wordWrappedLabel); GUILayout.EndHorizontal(); } - int free_parameter = -1; + serializedObject.Update(); + viewModel.Refresh(); GUIStyle red_style = new GUIStyle(GUI.skin.label); red_style.normal.textColor = Color.red; red_style.wordWrap = true; - var parameters = root.GetParameter(); - if (parameters == null) + if (!viewModel.HasParameterAsset) GUILayout.Label(Lang("Cannot find VRCExpressionParameters in your avatar!"), red_style); else { - free_parameter = 256 - parameters.CalcTotalCost(); - GUILayout.Label(Lang("Free parameter:") + free_parameter, EditorStyles.wordWrappedLabel); + GUILayout.Label(Lang("Free parameter:") + viewModel.FreeParameter, EditorStyles.wordWrappedLabel); } - int lock_size = 1; - int switch_count = ShellProtector.GetRequiredSwitchCount(keySize.intValue, syncSize.intValue); - int using_parameter = switch_count + lock_size + syncSize.intValue * 8; - GUILayout.Label(Lang("Parameters to be used:") + using_parameter, EditorStyles.wordWrappedLabel); + GUILayout.Label(Lang("Parameters to be used:") + viewModel.UsedParameter, EditorStyles.wordWrappedLabel); - serializedObject.Update(); gameobjectList.DoLayoutList(); materialList.DoLayoutList(); GUILayout.Label(Lang("Encrypting too many objects can cause lag when loading avatars in-game.")); @@ -370,7 +367,8 @@ public override void OnInspectorGUI() } #endregion - if (free_parameter - using_parameter < 0) + viewModel.Refresh(); + if (!viewModel.HasEnoughParameterSpace) { GUILayout.Label(Lang("Not enough parameter space!"), red_style); GUILayout.BeginHorizontal(); @@ -380,7 +378,7 @@ public override void OnInspectorGUI() GUILayout.EndHorizontal(); GUI.enabled = forceProgress; } - if (gameobjectList.count == 0 && materialList.count == 0) + if (!viewModel.HasTargets) GUI.enabled = false; @@ -506,4 +504,4 @@ private void OnGUI() } } } -#endif \ No newline at end of file +#endif diff --git a/Editor/ShellProtectorEditorViewModel.cs b/Editor/ShellProtectorEditorViewModel.cs new file mode 100644 index 0000000..00a6206 --- /dev/null +++ b/Editor/ShellProtectorEditorViewModel.cs @@ -0,0 +1,48 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEditorInternal; +using VRC.SDK3.Avatars.ScriptableObjects; + +namespace Shell.Protector +{ + internal sealed class ShellProtectorEditorViewModel + { + readonly ShellProtector protector; + readonly SerializedProperty keySize; + readonly SerializedProperty syncSize; + readonly ReorderableList gameobjectList; + readonly ReorderableList materialList; + + public ShellProtectorEditorViewModel( + ShellProtector protector, + SerializedProperty keySize, + SerializedProperty syncSize, + ReorderableList gameobjectList, + ReorderableList materialList) + { + this.protector = protector; + this.keySize = keySize; + this.syncSize = syncSize; + this.gameobjectList = gameobjectList; + this.materialList = materialList; + } + + public bool HasParameterAsset { get; private set; } + public int FreeParameter { get; private set; } + public int UsedParameter { get; private set; } + public bool HasEnoughParameterSpace => HasParameterAsset && FreeParameter >= UsedParameter; + public bool HasTargets => gameobjectList.count > 0 || materialList.count > 0; + + public void Refresh() + { + VRCExpressionParameters parameters = protector.GetParameter(); + HasParameterAsset = parameters != null; + FreeParameter = HasParameterAsset ? 256 - parameters.CalcTotalCost() : -1; + + int lockSize = 1; + int switchCount = ShellProtector.GetRequiredSwitchCount(keySize.intValue, syncSize.intValue); + UsedParameter = switchCount + lockSize + syncSize.intValue * 8; + } + } +} +#endif diff --git a/Editor/ShellProtectorEditorViewModel.cs.meta b/Editor/ShellProtectorEditorViewModel.cs.meta new file mode 100644 index 0000000..2dbe6ad --- /dev/null +++ b/Editor/ShellProtectorEditorViewModel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22ea150d6dc34805a3c1fb82273a77a0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/TesterEditor.cs b/Editor/TesterEditor.cs index 26aa355..45d2008 100644 --- a/Editor/TesterEditor.cs +++ b/Editor/TesterEditor.cs @@ -29,7 +29,7 @@ public void OnEnable() } public override void OnInspectorGUI() { - root.protector = EditorGUILayout.ObjectField(root.protector, typeof(ShellProtector), GUILayout.Width(200)) as ShellProtector; + root.protector = EditorGUILayout.ObjectField(root.protector, typeof(ShellProtector), true, GUILayout.Width(200)) as ShellProtector; GUILayout.Space(10); @@ -84,4 +84,4 @@ public override void OnInspectorGUI() } } } -#endif \ No newline at end of file +#endif diff --git a/Editor/liltoonCustom/CustomInspector.cs b/Editor/liltoonCustom/CustomInspector.cs index 4c6933c..e0f016d 100644 --- a/Editor/liltoonCustom/CustomInspector.cs +++ b/Editor/liltoonCustom/CustomInspector.cs @@ -33,14 +33,14 @@ protected override void LoadCustomProperties(MaterialProperty[] props, Material //isShowRenderMode = false; //LoadCustomLanguage(""); - mip_tex = FindProperty("_MipTex", props); - encrypted_tex0 = FindProperty("_EncryptTex0", props); - encrypted_tex1 = FindProperty("_EncryptTex1", props); - password_hash = FindProperty("_PasswordHash", props); - hashMagic = FindProperty("_HashMagic", props); + mip_tex = FindProperty(ShellProtectorShaderProperties.MipTexture, props); + encrypted_tex0 = FindProperty(ShellProtectorShaderProperties.EncryptTexture0, props); + encrypted_tex1 = FindProperty(ShellProtectorShaderProperties.EncryptTexture1, props); + password_hash = FindProperty(ShellProtectorShaderProperties.PasswordHash, props); + hashMagic = FindProperty(ShellProtectorShaderProperties.HashMagic, props); for (int i = 0; i < key.Length; ++i) - key[i] = FindProperty("_Key" + i, props); + key[i] = FindProperty(ShellProtectorShaderProperties.KeyPrefix + i, props); } protected override void DrawCustomProperties(Material material) diff --git a/Runtime/Scripts/Algorithm/Chacha20.cs b/Runtime/Scripts/Algorithm/Chacha20.cs index 5e3c352..087acbf 100644 --- a/Runtime/Scripts/Algorithm/Chacha20.cs +++ b/Runtime/Scripts/Algorithm/Chacha20.cs @@ -7,7 +7,7 @@ namespace Shell.Protector { public class Chacha20 : IEncryptor { - public string Keyword => "_SHELL_PROTECTOR_CHACHA"; + public string Keyword => ShellProtectorShaderProperties.ChachaKeyword; public byte[] nonce = new byte[12]; [MethodImpl(MethodImplOptions.AggressiveInlining)] byte[] U32t8le(uint v) @@ -181,4 +181,4 @@ public uint[] GetNonceUint3() return result; } } -} \ No newline at end of file +} diff --git a/Runtime/Scripts/Algorithm/IEncryptor.cs b/Runtime/Scripts/Algorithm/IEncryptor.cs index 8eb6918..d6b82d9 100644 --- a/Runtime/Scripts/Algorithm/IEncryptor.cs +++ b/Runtime/Scripts/Algorithm/IEncryptor.cs @@ -1,6 +1,6 @@ namespace Shell.Protector { - public interface IEncryptor + public interface ITextureEncryptor { public string Keyword { get; } @@ -12,4 +12,8 @@ public interface IEncryptor uint[] Decrypt(uint[] data, uint[] key); #endif } -} \ No newline at end of file + + public interface IEncryptor : ITextureEncryptor + { + } +} diff --git a/Runtime/Scripts/Algorithm/XXTEA.cs b/Runtime/Scripts/Algorithm/XXTEA.cs index acdf12e..397fde4 100644 --- a/Runtime/Scripts/Algorithm/XXTEA.cs +++ b/Runtime/Scripts/Algorithm/XXTEA.cs @@ -5,7 +5,7 @@ namespace Shell.Protector { public class XXTEA : IEncryptor { - public string Keyword => "_SHELL_PROTECTOR_XXTEA"; + public string Keyword => ShellProtectorShaderProperties.XXTEAKeyword; const uint Delta = 0x9E3779B9; public uint m_rounds = 0; @@ -85,4 +85,4 @@ public uint[] Decrypt(uint[] data, uint[] key) return result; } } -} \ No newline at end of file +} diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 8ef2182..2b64cf6 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -16,65 +16,6 @@ public class AnimatorManager : ScriptableObject { Dictionary encryptedClip = new Dictionary(); - static string curve1 = @" - - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: -128 - inSlope: -1919.995 - outSlope: 59.999996 - tangentMode: 69 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 2.1333334 - value: 0 - inSlope: 59.999996 - outSlope: -1919.995 - tangentMode: 69 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - attribute: material._Key0 - path: Body - classID: 137 - script: {fileID: 0}"; - - static string curve2 = @" - - curve: - serializedVersion: 2 - m_Curve: - - serializedVersion: 3 - time: 0 - value: 128 - inSlope: 423.3333 - outSlope: 60.000004 - tangentMode: 69 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - - serializedVersion: 3 - time: 2.1333334 - value: 256 - inSlope: 59.999996 - outSlope: 60 - tangentMode: 69 - weightedMode: 0 - inWeight: 0.33333334 - outWeight: 0.33333334 - m_PreInfinity: 2 - m_PostInfinity: 2 - m_RotationOrder: 4 - attribute: material._Key0 - path: Body - classID: 137 - script: {fileID: 0}"; public static AnimatorController DuplicateAnimator(RuntimeAnimatorController anim, string new_dir) { string dir = AssetDatabase.GetAssetPath(anim); @@ -102,7 +43,9 @@ public static void CreateKeyAniamtions(string animation_dir, string new_dir, Gam string path = Path.Combine(new_dir, filename); AssetDatabase.CopyAsset(file, path); - string anim = File.ReadAllText(path); + AnimationClip clip = AssetDatabase.LoadAssetAtPath(path); + if (clip == null) + continue; Match match = Regex.Match(filename, "key(\\d+).*?\\.anim"); int n = 0; @@ -111,30 +54,54 @@ public static void CreateKeyAniamtions(string animation_dir, string new_dir, Gam foreach (var obj in objs) { - string hr_path = obj.transform.GetHierarchyPath(); - hr_path = Regex.Replace(hr_path, ".*?/(.*)", "'$1'"); - - string curve; - - if (!filename.Contains("_")) - curve = Regex.Replace(curve1, "attribute: material._Key\\d+", "attribute: material._Key" + n); - else - curve = Regex.Replace(curve2, "attribute: material._Key\\d+", "attribute: material._Key" + n); - curve = Regex.Replace(curve, "path: Body", "path: " + hr_path); - //SkinnedMeshRender classID:137 - //MeshRenderer classID:23 - if (obj.GetComponent() == null) - curve = Regex.Replace(curve, "classID: 137", "classID: 23"); - - anim = Regex.Replace(anim, "m_FloatCurves:", "m_FloatCurves:" + curve); - anim = Regex.Replace(anim, "m_EditorCurves:", "m_EditorCurves:" + curve); + AddKeyCurve(clip, obj, n, filename.Contains("_")); } - File.WriteAllText(path, anim); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } + static void AddKeyCurve(AnimationClip clip, GameObject obj, int keyIndex, bool secondKeyClip) + { + var binding = new EditorCurveBinding + { + path = GetAnimationPath(obj.transform), + propertyName = "material." + ShellProtectorShaderProperties.KeyPrefix + keyIndex, + type = obj.GetComponent() == null ? typeof(MeshRenderer) : typeof(SkinnedMeshRenderer) + }; + + AnimationUtility.SetEditorCurve(clip, binding, CreateKeyCurve(secondKeyClip)); + } + + static string GetAnimationPath(Transform transform) + { + var names = new List(); + Transform current = transform; + while (current != null && current.parent != null) + { + names.Add(current.name); + current = current.parent; + } + names.Reverse(); + return string.Join("/", names); + } + + static AnimationCurve CreateKeyCurve(bool secondKeyClip) + { + if (!secondKeyClip) + { + return new AnimationCurve( + new Keyframe(0, -128, -1919.995f, 59.999996f), + new Keyframe(2.1333334f, 0, 59.999996f, -1919.995f) + ); + } + + return new AnimationCurve( + new Keyframe(0, 128, 423.3333f, 60.000004f), + new Keyframe(2.1333334f, 256, 59.999996f, 60) + ); + } + private static BlendTree[] CreateKeyTree(string animation_dir, int key_length, float speed) { BlendTree[] tree = new BlendTree[key_length]; @@ -575,4 +542,4 @@ public void ChangeAnimationMaterial(AnimatorController anim, Material original, } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/EncryptedHistory.cs b/Runtime/Scripts/EncryptedHistory.cs index 46f701b..e90a65b 100644 --- a/Runtime/Scripts/EncryptedHistory.cs +++ b/Runtime/Scripts/EncryptedHistory.cs @@ -1,4 +1,4 @@ -using Shell.Protector; +#if UNITY_EDITOR using System; using System.Collections.Generic; using System.IO; @@ -6,97 +6,101 @@ using UnityEditor; using UnityEngine; -#if UNITY_EDITOR - -[CreateAssetMenu(fileName = "EncryptedHistory", menuName = "ShellProtector/EncryptedHistory", order = 1)] -public class EncryptedHistory : ScriptableObject +namespace Shell.Protector { - [Serializable] - class ShaderInfo + [CreateAssetMenu(fileName = "EncryptedHistory", menuName = "ShellProtector/EncryptedHistory", order = 1)] + public class EncryptedHistory : ScriptableObject { - public string shader; - public long size; - public string hash; - } - - [SerializeField] - List shaderHistory = new List(); - Dictionary shaderHistoryDic = new Dictionary(); + [Serializable] + class ShaderInfo + { + public string shader; + public long size; + public string hash; + } - public void LoadData() - { - if (shaderHistoryDic.Count != 0) - return; + [SerializeField] + List shaderHistory = new List(); + Dictionary shaderHistoryDic = new Dictionary(); - foreach (var info in shaderHistory) + public void LoadData() { - Shader shader = Shader.Find(info.shader); - if (shader) + if (shaderHistoryDic.Count != 0) + return; + + foreach (var info in shaderHistory) { - shaderHistoryDic.TryAdd(shader, info); + Shader shader = Shader.Find(info.shader); + if (shader) + { + shaderHistoryDic.TryAdd(shader, info); + } } } - } - public void Save(Shader shader) - { - if (AssetManager.GetInstance().IslilToon(shader)) - return; - string dir = AssetDatabase.GetAssetPath(shader); - FileInfo fileInfo = new FileInfo(dir); - long size = fileInfo.Length; - string hash = CalculateMD5(dir); - if (shaderHistoryDic.ContainsKey(shader)) - { - shaderHistoryDic[shader].size = size; - shaderHistoryDic[shader].hash = hash; - } - else + public void Save(Shader shader) { - var shaderInfo = new ShaderInfo { shader = shader.name, size = size, hash = hash }; - shaderHistoryDic.Add(shader, shaderInfo); - shaderHistory.Add(shaderInfo); - } + if (AssetManager.GetInstance().IslilToon(shader)) + return; - EditorUtility.SetDirty(this); - AssetDatabase.SaveAssets(); - } - public Shader IsEncryptedBefore(Shader originalShader) - { - if (shaderHistoryDic.ContainsKey(originalShader)) - { - string dir = AssetDatabase.GetAssetPath(originalShader); + string dir = AssetDatabase.GetAssetPath(shader); FileInfo fileInfo = new FileInfo(dir); long size = fileInfo.Length; - long oldSize = shaderHistoryDic[originalShader].size; - string oldHash = shaderHistoryDic[originalShader].hash; + string hash = CalculateMD5(dir); - if (size == oldSize) + if (shaderHistoryDic.ContainsKey(shader)) { - if(oldHash == CalculateMD5(dir)) - return Shader.Find(originalShader.name + "_encrypted"); - return null; + shaderHistoryDic[shader].size = size; + shaderHistoryDic[shader].hash = hash; } else - return null; + { + var shaderInfo = new ShaderInfo { shader = shader.name, size = size, hash = hash }; + shaderHistoryDic.Add(shader, shaderInfo); + shaderHistory.Add(shaderInfo); + } + + EditorUtility.SetDirty(this); + AssetDatabase.SaveAssets(); } - return null; - } - string CalculateMD5(string filePath) - { - if (!File.Exists(filePath)) + public Shader IsEncryptedBefore(Shader originalShader) { - Debug.LogError($"File not found: {filePath}"); + if (shaderHistoryDic.ContainsKey(originalShader)) + { + string dir = AssetDatabase.GetAssetPath(originalShader); + FileInfo fileInfo = new FileInfo(dir); + long size = fileInfo.Length; + long oldSize = shaderHistoryDic[originalShader].size; + string oldHash = shaderHistoryDic[originalShader].hash; + + if (size == oldSize) + { + if (oldHash == CalculateMD5(dir)) + return Shader.Find(originalShader.name + "_encrypted"); + return null; + } + else + return null; + } return null; } - using (FileStream stream = File.OpenRead(filePath)) + string CalculateMD5(string filePath) { - MD5 md5 = MD5.Create(); - byte[] hashBytes = md5.ComputeHash(stream); - return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); + if (!File.Exists(filePath)) + { + Debug.LogError($"File not found: {filePath}"); + return null; + } + + using (FileStream stream = File.OpenRead(filePath)) + { + MD5 md5 = MD5.Create(); + byte[] hashBytes = md5.ComputeHash(stream); + return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); + } } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/Formats/DXTFormat.cs b/Runtime/Scripts/Formats/DXTFormat.cs index f6c8da9..e3e83fe 100644 --- a/Runtime/Scripts/Formats/DXTFormat.cs +++ b/Runtime/Scripts/Formats/DXTFormat.cs @@ -42,8 +42,8 @@ protected Texture2D HandleCrunchedFormat(Texture2D texture, int mip_lv, bool isD } public override void SetFormatKeywords(Material material) { - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); + material.DisableKeyword(ShellProtectorShaderProperties.Format0Keyword); + material.DisableKeyword(ShellProtectorShaderProperties.Format1Keyword); } public override (int, int) CalculateOffsets(Texture2D texture) { @@ -203,4 +203,4 @@ public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/Formats/Format.cs b/Runtime/Scripts/Formats/Format.cs index 8d6e330..9dbff3b 100644 --- a/Runtime/Scripts/Formats/Format.cs +++ b/Runtime/Scripts/Formats/Format.cs @@ -1,37 +1,40 @@ using Shell.Protector; using UnityEngine; -public struct EncryptResult { - public Texture2D Texture1; - public Texture2D Texture2; -} - -public interface ITextureFormat { - bool CanHandle(TextureFormat format); - EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm); - void SetFormatKeywords(Material material); - (int, int) CalculateOffsets(Texture2D texture); -} - -public abstract class BaseTextureFormat : ITextureFormat { - protected uint[] ConvertKeyToUInt(byte[] key) { - uint[] key_uint = new uint[4]; - key_uint[0] = (uint)(key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24)); - key_uint[1] = (uint)(key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24)); - key_uint[2] = (uint)(key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24)); - key_uint[3] = 0; - return key_uint; +namespace Shell.Protector +{ + public struct EncryptResult { + public Texture2D Texture1; + public Texture2D Texture2; } - protected int GetCanMipmapLevel(int w, int h) { - if (w < 1 || h <= 1) return 0; - int w_level = (int)Mathf.Log(w, 2); - int h_level = (int)Mathf.Log(h, 2); - return Mathf.Max(w_level, h_level); + public interface ITextureFormat { + bool CanHandle(TextureFormat format); + EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm); + void SetFormatKeywords(Material material); + (int, int) CalculateOffsets(Texture2D texture); } - public abstract bool CanHandle(TextureFormat format); - public abstract EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm); - public abstract void SetFormatKeywords(Material material); - public abstract (int, int) CalculateOffsets(Texture2D texture); -} \ No newline at end of file + public abstract class BaseTextureFormat : ITextureFormat { + protected uint[] ConvertKeyToUInt(byte[] key) { + uint[] key_uint = new uint[4]; + key_uint[0] = (uint)(key[0] | (key[1] << 8) | (key[2] << 16) | (key[3] << 24)); + key_uint[1] = (uint)(key[4] | (key[5] << 8) | (key[6] << 16) | (key[7] << 24)); + key_uint[2] = (uint)(key[8] | (key[9] << 8) | (key[10] << 16) | (key[11] << 24)); + key_uint[3] = 0; + return key_uint; + } + + protected int GetCanMipmapLevel(int w, int h) { + if (w < 1 || h <= 1) return 0; + int w_level = (int)Mathf.Log(w, 2); + int h_level = (int)Mathf.Log(h, 2); + return Mathf.Max(w_level, h_level); + } + + public abstract bool CanHandle(TextureFormat format); + public abstract EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor algorithm); + public abstract void SetFormatKeywords(Material material); + public abstract (int, int) CalculateOffsets(Texture2D texture); + } +} diff --git a/Runtime/Scripts/Formats/RGBFormat.cs b/Runtime/Scripts/Formats/RGBFormat.cs index 4a0b3d4..9f2723d 100644 --- a/Runtime/Scripts/Formats/RGBFormat.cs +++ b/Runtime/Scripts/Formats/RGBFormat.cs @@ -59,8 +59,8 @@ public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor } public override void SetFormatKeywords(Material material) { - material.EnableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT1"); + material.EnableKeyword(ShellProtectorShaderProperties.Format0Keyword); + material.DisableKeyword(ShellProtectorShaderProperties.Format1Keyword); } public override (int, int) CalculateOffsets(Texture2D texture) { @@ -110,8 +110,8 @@ public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor } public override void SetFormatKeywords(Material material) { - material.DisableKeyword("_SHELL_PROTECTOR_FORMAT0"); - material.EnableKeyword("_SHELL_PROTECTOR_FORMAT1"); + material.DisableKeyword(ShellProtectorShaderProperties.Format0Keyword); + material.EnableKeyword(ShellProtectorShaderProperties.Format1Keyword); } public override (int, int) CalculateOffsets(Texture2D texture) { @@ -122,4 +122,4 @@ public override (int, int) CalculateOffsets(Texture2D texture) { } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/Injector/IShaderAdapter.cs b/Runtime/Scripts/Injector/IShaderAdapter.cs new file mode 100644 index 0000000..b4a7bc7 --- /dev/null +++ b/Runtime/Scripts/Injector/IShaderAdapter.cs @@ -0,0 +1,14 @@ +#if UNITY_EDITOR +using UnityEngine; + +namespace Shell.Protector +{ + public interface IShaderAdapter + { + bool CanHandle(Shader shader); + bool WasInjected(Shader shader); + void SetKeywords(Material material, bool hasLimTexture = false); + Shader Inject(Material material, string decoderPath, string outputPath, Texture2D mainTexture, ShellProtectorAuxiliaryTextures auxiliaryTextures); + } +} +#endif diff --git a/Runtime/Scripts/Injector/IShaderAdapter.cs.meta b/Runtime/Scripts/Injector/IShaderAdapter.cs.meta new file mode 100644 index 0000000..784a0fc --- /dev/null +++ b/Runtime/Scripts/Injector/IShaderAdapter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6fcb5c843134bc3bb07144b57fbce16 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index d07e9b7..77824da 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -5,7 +5,7 @@ #if UNITY_EDITOR namespace Shell.Protector { - abstract public class Injector + abstract public class Injector : IShaderAdapter { protected ushort[] keys = new ushort[8]; //16byte protected AssetManager shader_manager = AssetManager.GetInstance(); @@ -76,6 +76,8 @@ public void Init(GameObject target, Texture2D main_tex, byte[] key, int user_key this.encryptor = encryptor; } + public abstract bool CanHandle(Shader shader); + public bool WasInjected(Shader shader) { string shader_path = AssetDatabase.GetAssetPath(shader); @@ -91,7 +93,7 @@ public void SetKeywords(Material material, bool has_lim_texture = false) { var keywords = material.shaderKeywords; foreach (string keyword in keywords) { - if (keyword.StartsWith("_SHELL_PROTECTOR_")) { + if (keyword.StartsWith(ShellProtectorShaderProperties.KeywordPrefix)) { material.DisableKeyword(keyword); } } @@ -101,12 +103,25 @@ public void SetKeywords(Material material, bool has_lim_texture = false) { // Set rimlight keyword if (has_lim_texture) - material.EnableKeyword("_SHELL_PROTECTOR_RIMLIGHT"); + material.EnableKeyword(ShellProtectorShaderProperties.RimLightKeyword); // Set encryptor keyword material.EnableKeyword(encryptor.Keyword); } + public Shader Inject(Material material, string decoderPath, string outputPath, Texture2D mainTexture, ShellProtectorAuxiliaryTextures auxiliaryTextures) + { + return Inject( + material, + decoderPath, + outputPath, + mainTexture, + auxiliaryTextures.limTexture != null, + auxiliaryTextures.limTexture2 != null, + auxiliaryTextures.outlineTexture != null + ); + } + public Shader Inject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) { return CustomInject(mat, decode_dir, output_dir, tex, has_lim_texture, has_lim_texture2, outline_tex); @@ -115,4 +130,4 @@ public Shader Inject(Material mat, string decode_dir, string output_dir, Texture protected abstract Shader CustomInject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false); } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/Injector/InjectorFactory.cs b/Runtime/Scripts/Injector/InjectorFactory.cs index cc86f7e..89082bb 100644 --- a/Runtime/Scripts/Injector/InjectorFactory.cs +++ b/Runtime/Scripts/Injector/InjectorFactory.cs @@ -10,14 +10,20 @@ public class InjectorFactory { public static Injector GetInjector(Shader shader) { - AssetManager shader_manager = AssetManager.GetInstance(); - if (shader_manager.IsPoiyomi(shader)) - return new PoiyomiInjector(); - else if (shader_manager.IslilToon(shader)) - return new LilToonInjector(); - else - return null; + Injector[] adapters = + { + new PoiyomiInjector(), + new LilToonInjector() + }; + + foreach (Injector adapter in adapters) + { + if (adapter.CanHandle(shader)) + return adapter; + } + + return null; } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/Injector/LilToonInjector.cs b/Runtime/Scripts/Injector/LilToonInjector.cs index bd34dd6..b3a194e 100644 --- a/Runtime/Scripts/Injector/LilToonInjector.cs +++ b/Runtime/Scripts/Injector/LilToonInjector.cs @@ -13,6 +13,11 @@ namespace Shell.Protector { public class LilToonInjector : Injector { + public override bool CanHandle(Shader shader) + { + return shader_manager.IslilToon(shader); + } + protected override Shader CustomInject(Material mat, string decode_dir, string output_path, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) { string[] files = Directory.GetFiles(Path.Combine(asset_dir, "liltoonProtector", "Shaders")); @@ -49,4 +54,4 @@ protected override Shader CustomInject(Material mat, string decode_dir, string o } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index aa386d2..c947326 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -10,6 +10,11 @@ namespace Shell.Protector { public class PoiyomiInjector : Injector { + public override bool CanHandle(Shader shader) + { + return shader_manager.IsPoiyomi(shader); + } + protected override Shader CustomInject(Material mat, string decode_dir, string output_path, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) { if (!File.Exists(decode_dir)) @@ -132,21 +137,21 @@ private void InsertProperties(ref string data) { int suffix_idx = match.Index + match.Length; string properties = @" - _MipTex (""MipReference"", 2D) = ""white"" { } - _EncryptTex0 (""Encrypted0"", 2D) = ""white"" { } - _EncryptTex1 (""Encrypted1"", 2D) = ""white"" { } - _Woffset (""Woffset"", integer) = 0 - _Hoffset (""Hoffset"", integer) = 0 - _Nonce0 (""Nonce"", integer) = 0 - _Nonce1 (""Nonce"", integer) = 0 - _Nonce2 (""Nonce"", integer) = 0 - _Rounds (""Rounds"", integer) = 0 - _PasswordHash (""PasswordHash"", integer) = 0 - _HashMagic (""HashMagic"", integer) = 0 +" + ShellProtectorShaderProperties.MipTexture + @" (""MipReference"", 2D) = ""white"" { } +" + ShellProtectorShaderProperties.EncryptTexture0 + @" (""Encrypted0"", 2D) = ""white"" { } +" + ShellProtectorShaderProperties.EncryptTexture1 + @" (""Encrypted1"", 2D) = ""white"" { } +" + ShellProtectorShaderProperties.WidthOffset + @" (""Woffset"", integer) = 0 +" + ShellProtectorShaderProperties.HeightOffset + @" (""Hoffset"", integer) = 0 +" + ShellProtectorShaderProperties.Nonce0 + @" (""Nonce"", integer) = 0 +" + ShellProtectorShaderProperties.Nonce1 + @" (""Nonce"", integer) = 0 +" + ShellProtectorShaderProperties.Nonce2 + @" (""Nonce"", integer) = 0 +" + ShellProtectorShaderProperties.Rounds + @" (""Rounds"", integer) = 0 +" + ShellProtectorShaderProperties.PasswordHash + @" (""PasswordHash"", integer) = 0 +" + ShellProtectorShaderProperties.HashMagic + @" (""HashMagic"", integer) = 0 "; for (int i = 0; i < 16; ++i) - properties += "_Key" + i + " (\"key" + i + "\", float) = 0\n"; + properties += ShellProtectorShaderProperties.KeyPrefix + i + " (\"key" + i + "\", float) = 0\n"; data = data.Insert(suffix_idx, properties); } diff --git a/Runtime/Scripts/KeyGenerator.cs b/Runtime/Scripts/KeyGenerator.cs index c22740e..08772df 100644 --- a/Runtime/Scripts/KeyGenerator.cs +++ b/Runtime/Scripts/KeyGenerator.cs @@ -2,6 +2,9 @@ using System.Security.Cryptography; using System; using UnityEngine; + +namespace Shell.Protector +{ public class KeyGenerator { //key1 is fixed key @@ -100,3 +103,4 @@ public static uint SimpleHash(byte[] data, uint hashMagic) return hash; } } +} diff --git a/Runtime/Scripts/Obfuscator.cs b/Runtime/Scripts/Obfuscator.cs index 808242f..12cdd78 100644 --- a/Runtime/Scripts/Obfuscator.cs +++ b/Runtime/Scripts/Obfuscator.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.RegularExpressions; using UnityEditor; using UnityEditor.Animations; using UnityEngine; @@ -19,8 +18,6 @@ public class Obfuscator : ScriptableObject Dictionary obfuscatedClip = new Dictionary(); // before, after HashSet mmdShapes = new HashSet(); - readonly Regex re = new Regex(".*?/(.*)"); - public bool clone = true; public bool bPreserveMMD = true; @@ -49,6 +46,20 @@ public void Clean() animDir = ""; obfuscatedBlendShapeNames.Clear(); obfuscatedBlendShapeIndex.Clear(); + obfuscatedClip.Clear(); + } + + static string GetAnimationPath(Transform transform) + { + var names = new List(); + Transform current = transform; + while (current != null && current.parent != null) + { + names.Add(current.name); + current = current.parent; + } + names.Reverse(); + return string.Join("/", names); } public Mesh ObfuscateBlendShapeMesh(Mesh mesh, string newPath) @@ -124,7 +135,7 @@ public void ChangeObfuscatedBlendShapeInDescriptor(VRCAvatarDescriptor descripto for (int i = 0; i < descriptor.customEyeLookSettings.eyelidsBlendshapes.Length; i++) { int idx = descriptor.customEyeLookSettings.eyelidsBlendshapes[i]; - descriptor.customEyeLookSettings.eyelidsBlendshapes[i] = obfuscatedBlendShapeIndex.FindIndex(0, obfuscatedBlendShapeIndex.Count - 1, + descriptor.customEyeLookSettings.eyelidsBlendshapes[i] = obfuscatedBlendShapeIndex.FindIndex(0, obfuscatedBlendShapeIndex.Count, x => { return x == idx; @@ -210,8 +221,7 @@ public AnimationClip ChangeBlendShapeInClip(AnimationClip clip, GameObject obj) { if (binding.type == typeof(SkinnedMeshRenderer) && binding.propertyName.StartsWith("blendShape.")) { - Match m = re.Match(obj.transform.GetHierarchyPath()); - string hierarchyPath = m.Groups[1].Value; + string hierarchyPath = GetAnimationPath(obj.transform); if (binding.path != hierarchyPath) continue; @@ -249,8 +259,7 @@ public AnimationClip ChangeBlendShapeInClip(AnimationClip clip, GameObject obj) { if (binding.type == typeof(SkinnedMeshRenderer) && binding.propertyName.StartsWith("blendShape.")) { - Match m = re.Match(obj.transform.GetHierarchyPath()); - string hierarchyPath = m.Groups[1].Value; + string hierarchyPath = GetAnimationPath(obj.transform); string blendShapeName = binding.propertyName.Substring("blendShape.".Length); @@ -287,4 +296,4 @@ public string GetOriginalBlendShapeName(string obfuscatedBlendShape) } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/ParameterManager.cs b/Runtime/Scripts/ParameterManager.cs index 54e9743..2004f37 100644 --- a/Runtime/Scripts/ParameterManager.cs +++ b/Runtime/Scripts/ParameterManager.cs @@ -1,10 +1,11 @@ #if UNITY_EDITOR using System.Collections.Generic; using System.Linq; -using Shell.Protector; using UnityEngine; using VRC.SDK3.Avatars.ScriptableObjects; +namespace Shell.Protector +{ public static class ParameterManager { private const string Prefix = "SHELL_PROTECTOR_"; @@ -99,4 +100,5 @@ public static VRCExpressionParameters AddKeyParameter(VRCExpressionParameters vr return result; } } -#endif \ No newline at end of file +} +#endif diff --git a/Runtime/Scripts/Pipeline.meta b/Runtime/Scripts/Pipeline.meta new file mode 100644 index 0000000..7e02195 --- /dev/null +++ b/Runtime/Scripts/Pipeline.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a7c3a9f0f2e44e2ba4762f4929f4c221 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/IAssetWriter.cs b/Runtime/Scripts/Pipeline/IAssetWriter.cs new file mode 100644 index 0000000..6a0e509 --- /dev/null +++ b/Runtime/Scripts/Pipeline/IAssetWriter.cs @@ -0,0 +1,38 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace Shell.Protector +{ + public interface IAssetWriter + { + void CreateAsset(Object asset, string path); + bool CopyAsset(string sourcePath, string targetPath); + T LoadAssetAtPath(string path) where T : Object; + bool IsValidFolder(string path); + void CreateFolder(string parentFolder, string newFolderName); + void DeleteAsset(string path); + void SaveAssets(); + void Refresh(); + void SaveAndRefresh(); + } + + public sealed class UnityAssetWriter : IAssetWriter + { + public void CreateAsset(Object asset, string path) => AssetDatabase.CreateAsset(asset, path); + public bool CopyAsset(string sourcePath, string targetPath) => AssetDatabase.CopyAsset(sourcePath, targetPath); + public T LoadAssetAtPath(string path) where T : Object => AssetDatabase.LoadAssetAtPath(path); + public bool IsValidFolder(string path) => AssetDatabase.IsValidFolder(path); + public void CreateFolder(string parentFolder, string newFolderName) => AssetDatabase.CreateFolder(parentFolder, newFolderName); + public void DeleteAsset(string path) => AssetDatabase.DeleteAsset(path); + public void SaveAssets() => AssetDatabase.SaveAssets(); + public void Refresh() => AssetDatabase.Refresh(); + + public void SaveAndRefresh() + { + SaveAssets(); + Refresh(); + } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/IAssetWriter.cs.meta b/Runtime/Scripts/Pipeline/IAssetWriter.cs.meta new file mode 100644 index 0000000..13fbd3e --- /dev/null +++ b/Runtime/Scripts/Pipeline/IAssetWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 032df40621b84d208e71e152d75ac1e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs b/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs new file mode 100644 index 0000000..045e8c7 --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs @@ -0,0 +1,41 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using UnityEngine; + +namespace Shell.Protector +{ + public struct ShellProtectorProcessedTexture + { + public EncryptResult encrypted; + public List fallbacks; + public List fallbackOptions; + public byte[] nonce; + } + + public struct ShellProtectorAuxiliaryTextures + { + public Texture2D limTexture; + public Texture2D limTexture2; + public Texture2D outlineTexture; + public Texture2D limShadeTexture; + } + + public sealed class ShellProtectorBuildContext + { + public ShellProtectorBuildContext(ShellProtectorBuildRequest request, ShellProtectorSettings settings) + { + Request = request; + Settings = settings; + Result = new ShellProtectorBuildResult(); + } + + public ShellProtectorBuildRequest Request { get; } + public ShellProtectorSettings Settings { get; } + public ShellProtectorBuildResult Result { get; } + public IEncryptor Encryptor { get; set; } + public EncryptedHistory History { get; set; } + public Texture2D FallbackWhite { get; set; } + public Texture2D FallbackBlack { get; set; } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs.meta b/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs.meta new file mode 100644 index 0000000..10cc8e3 --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0967a630bc624e6499905c432cf46547 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs b/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs new file mode 100644 index 0000000..5924408 --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs @@ -0,0 +1,22 @@ +#if UNITY_EDITOR +using VRC.SDK3.Avatars.Components; + +namespace Shell.Protector +{ + public sealed class ShellProtectorBuildRequest + { + public ShellProtectorBuildRequest(ShellProtector owner, VRCAvatarDescriptor descriptor, bool useSmallMipTexture, bool isModular) + { + Owner = owner; + Descriptor = descriptor; + UseSmallMipTexture = useSmallMipTexture; + IsModular = isModular; + } + + public ShellProtector Owner { get; } + public VRCAvatarDescriptor Descriptor { get; } + public bool UseSmallMipTexture { get; } + public bool IsModular { get; } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs.meta b/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs.meta new file mode 100644 index 0000000..9e17c37 --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b2750924c1964b17afaf3457d6e4f56a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs b/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs new file mode 100644 index 0000000..0ed5cb4 --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs @@ -0,0 +1,27 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using UnityEngine; + +namespace Shell.Protector +{ + public sealed class ShellProtectorBuildResult + { + public GameObject Avatar { get; set; } + public string AvatarDir { get; set; } + public byte[] KeyBytes { get; set; } + public HashSet Meshes { get; } = new HashSet(); + public Dictionary EncryptedMaterials { get; } = new Dictionary(); + public Dictionary ProcessedTextures { get; } = new Dictionary(); + + public void Clear() + { + Avatar = null; + AvatarDir = null; + KeyBytes = null; + Meshes.Clear(); + EncryptedMaterials.Clear(); + ProcessedTextures.Clear(); + } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs.meta b/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs.meta new file mode 100644 index 0000000..0ae4c26 --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ee7d88ba50648469ab6ad1df9c25381 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs b/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs new file mode 100644 index 0000000..859087e --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs @@ -0,0 +1,22 @@ +#if UNITY_EDITOR +using UnityEngine; + +namespace Shell.Protector +{ + public sealed class ShellProtectorPipeline + { + public ShellProtectorBuildResult Encrypt(ShellProtectorBuildRequest request, ShellProtectorSettings settings) + { + if (request == null || request.Owner == null) + return new ShellProtectorBuildResult(); + + request.Owner.ApplySettings(settings); + GameObject avatar = request.Owner.EncryptLegacy(request.UseSmallMipTexture, request.IsModular); + + ShellProtectorBuildResult result = request.Owner.CurrentBuildResult; + result.Avatar = avatar; + return result; + } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs.meta b/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs.meta new file mode 100644 index 0000000..f594feb --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c42b6cf9a14f42429cdf2548e56669d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs b/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs new file mode 100644 index 0000000..a657300 --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs @@ -0,0 +1,23 @@ +#if UNITY_EDITOR +namespace Shell.Protector +{ + public sealed class ShellProtectorSettings + { + public string AssetDir { get; set; } + public string FixedPassword { get; set; } + public string UserPassword { get; set; } + public string Language { get; set; } + public int LanguageIndex { get; set; } + public uint Rounds { get; set; } + public int Filter { get; set; } + public int Fallback { get; set; } + public int Algorithm { get; set; } + public int KeySize { get; set; } + public int SyncSize { get; set; } + public bool DeleteFolders { get; set; } + public bool UseSmallMipTexture { get; set; } + public bool PreserveMMD { get; set; } + public bool TurnOnAllSafetyFallback { get; set; } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs.meta b/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs.meta new file mode 100644 index 0000000..b6b8b41 --- /dev/null +++ b/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1aef04344d846ce8aa6f936291351af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index a24d3fb..4baec13 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -65,32 +65,20 @@ public class MaterialOptionPair EncryptedHistory history; - struct ProcessedTexture - { - public EncryptResult encrypted; - public List fallbacks; - public List fallbackOptions; - public byte[] nonce; - } - struct OtherTextures - { - public Texture2D limTexture; - public Texture2D limTexture2; - public Texture2D outlineTexture; - public Texture2D limShadeTexture; - } - //Must clear them before start encrypting// - HashSet meshes = new HashSet(); - Dictionary encryptedMaterials = new Dictionary(); // original, encrypted - Dictionary processedTextures = new Dictionary(); + ShellProtectorBuildResult buildResult = new ShellProtectorBuildResult(); + HashSet meshes => buildResult.Meshes; + Dictionary encryptedMaterials => buildResult.EncryptedMaterials; // original, encrypted + Dictionary processedTextures => buildResult.ProcessedTextures; ////////////////////////////////// [SerializeField] uint rounds = 20; [SerializeField] int filter = 1; [SerializeField] int fallback = 5; [SerializeField] int algorithm = 1; +#pragma warning disable CS0414 [SerializeField] int keySizeIdx = 3; +#pragma warning restore CS0414 [SerializeField] int keySize = 12; [SerializeField] int syncSize = 1; [SerializeField] bool deleteFolders = true; @@ -98,7 +86,6 @@ struct OtherTextures [SerializeField] bool bPreserveMMD = true; - [SerializeField] float fallbackTime = 5.0f; [SerializeField] bool turnOnAllSafetyFallback = true; public static readonly string[] filterStrings = new string[2] { "Point", "Bilinear" }; @@ -188,6 +175,65 @@ public GameObject Encrypt(bool isModular = true) } public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) + { + var request = new ShellProtectorBuildRequest(this, descriptor, bUseSmallMip, isModular); + var result = new ShellProtectorPipeline().Encrypt(request, CreateSettings()); + ApplyBuildResult(result); + return result.Avatar; + } + + internal ShellProtectorBuildResult CurrentBuildResult => buildResult; + + internal void ApplyBuildResult(ShellProtectorBuildResult result) + { + buildResult = result ?? new ShellProtectorBuildResult(); + } + + internal ShellProtectorSettings CreateSettings() + { + return new ShellProtectorSettings + { + AssetDir = assetDir, + FixedPassword = pwd, + UserPassword = pwd2, + Language = lang, + LanguageIndex = langIdx, + Rounds = rounds, + Filter = filter, + Fallback = fallback, + Algorithm = algorithm, + KeySize = keySize, + SyncSize = syncSize, + DeleteFolders = deleteFolders, + UseSmallMipTexture = bUseSmallMipTexture, + PreserveMMD = bPreserveMMD, + TurnOnAllSafetyFallback = turnOnAllSafetyFallback + }; + } + + internal void ApplySettings(ShellProtectorSettings settings) + { + if (settings == null) + return; + + assetDir = settings.AssetDir; + pwd = settings.FixedPassword; + pwd2 = settings.UserPassword; + lang = settings.Language; + langIdx = settings.LanguageIndex; + rounds = settings.Rounds; + filter = settings.Filter; + fallback = settings.Fallback; + algorithm = settings.Algorithm; + keySize = settings.KeySize; + syncSize = settings.SyncSize; + deleteFolders = settings.DeleteFolders; + bUseSmallMipTexture = settings.UseSmallMipTexture; + bPreserveMMD = settings.PreserveMMD; + turnOnAllSafetyFallback = settings.TurnOnAllSafetyFallback; + } + + internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) { meshes.Clear(); encryptedMaterials.Clear(); @@ -198,6 +244,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) string resourceDir = GetPackageAssetDir(); assetDir = ResolveOutputAssetDir(); string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); + buildResult.AvatarDir = avatarDir; Debug.Log("AssetDir: " + assetDir); @@ -241,6 +288,7 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) return null; } byte[] keyBytes = GetKeyBytes(); + buildResult.KeyBytes = keyBytes; CreateFolders(); @@ -339,13 +387,13 @@ public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) var processedTextureResult = GenerateEncryptedTexture(avatarDir, mat, encryptor, keyBytes); if (!processedTextureResult.HasValue) continue; - ProcessedTexture processedTexture = processedTextureResult.Value; + ShellProtectorProcessedTexture processedTexture = processedTextureResult.Value; Texture2D encryptedTex1 = processedTexture.encrypted.Texture1; Texture2D encryptedTex2 = processedTexture.encrypted.Texture2; //////////////////////Inject shader/////////////////////// - OtherTextures otherTex = GetLimOutlineTextures(mat); + ShellProtectorAuxiliaryTextures otherTex = GetLimOutlineTextures(mat); Shader encryptedShader = IsEncryptedBefore(mat.shader); if (encryptedShader == null) { @@ -477,9 +525,9 @@ public void ReplaceMaterials(GameObject avatar) } } } - OtherTextures GetLimOutlineTextures(Material mat) + ShellProtectorAuxiliaryTextures GetLimOutlineTextures(Material mat) { - OtherTextures others = new OtherTextures(); + ShellProtectorAuxiliaryTextures others = new ShellProtectorAuxiliaryTextures(); if (shaderManager.IsPoiyomi(mat.shader)) { var tex_properties = mat.GetTexturePropertyNames(); @@ -513,7 +561,7 @@ public void RemoveDuplicatedTextures(GameObject avatar) string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); foreach (var mat in encryptedMaterials.Values) { - OtherTextures otherTex = GetLimOutlineTextures(mat); + ShellProtectorAuxiliaryTextures otherTex = GetLimOutlineTextures(mat); foreach (var name in mat.GetTexturePropertyNames()) { @@ -1086,7 +1134,7 @@ Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) } return mip; } - ProcessedTexture? GenerateEncryptedTexture(string avatarDir, Material mat, IEncryptor encryptor, byte[] keyBytes) + ShellProtectorProcessedTexture? GenerateEncryptedTexture(string avatarDir, Material mat, IEncryptor encryptor, byte[] keyBytes) { Texture2D mainTexture = (Texture2D)mat.mainTexture; @@ -1094,12 +1142,12 @@ Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) string texPath2 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt2.asset"); bool processed = processedTextures.ContainsKey(mainTexture); - ProcessedTexture processedTexture; + ShellProtectorProcessedTexture processedTexture; if (processed) processedTexture = processedTextures[mainTexture]; else { - processedTexture = new ProcessedTexture + processedTexture = new ShellProtectorProcessedTexture { encrypted = new EncryptResult(), fallbacks = new List(), @@ -1149,7 +1197,7 @@ Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) return processedTexture; } - Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D mainTexture, ref ProcessedTexture processedTexture) + Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D mainTexture, ref ShellProtectorProcessedTexture processedTexture) { int fallbackOption = this.fallback; if (option != null) @@ -1221,7 +1269,7 @@ Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D return fallback; } - Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, OtherTextures otherTex, ProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) + Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, ShellProtectorAuxiliaryTextures otherTex, ShellProtectorProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) { Material newMat = new Material(mat.shader); newMat.CopyPropertiesFromMaterial(mat); @@ -1232,33 +1280,33 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp Texture2D encryptedTex0 = processedTexture.encrypted.Texture1; Texture2D encryptedTex1 = processedTexture.encrypted.Texture2; - newMat.SetTexture("_MipTex", mip); + newMat.SetTexture(ShellProtectorShaderProperties.MipTexture, mip); if (encryptedTex0 != null) - newMat.SetTexture("_EncryptTex0", encryptedTex0); + newMat.SetTexture(ShellProtectorShaderProperties.EncryptTexture0, encryptedTex0); if (encryptedTex1 != null) - newMat.SetTexture("_EncryptTex1", encryptedTex1); + newMat.SetTexture(ShellProtectorShaderProperties.EncryptTexture1, encryptedTex1); newMat.renderQueue = mat.renderQueue; if (turnOnAllSafetyFallback) newMat.SetOverrideTag("VRCFallback", "Unlit"); var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(originalTex); - newMat.SetInteger("_Woffset", woffset); - newMat.SetInteger("_Hoffset", hoffset); + newMat.SetInteger(ShellProtectorShaderProperties.WidthOffset, woffset); + newMat.SetInteger(ShellProtectorShaderProperties.HeightOffset, hoffset); for (int i = 0; i < keyBytes.Length; ++i) - newMat.SetFloat("_Key" + i, keyBytes[i]); + newMat.SetFloat(ShellProtectorShaderProperties.KeyPrefix + i, keyBytes[i]); if (algorithm == (int)Algorithm.chacha) { Chacha20 chacha = encryptor as Chacha20; - newMat.SetInteger("_Nonce0", (int)chacha.GetNonceUint3()[0]); - newMat.SetInteger("_Nonce1", (int)chacha.GetNonceUint3()[1]); - newMat.SetInteger("_Nonce2", (int)chacha.GetNonceUint3()[2]); + newMat.SetInteger(ShellProtectorShaderProperties.Nonce0, (int)chacha.GetNonceUint3()[0]); + newMat.SetInteger(ShellProtectorShaderProperties.Nonce1, (int)chacha.GetNonceUint3()[1]); + newMat.SetInteger(ShellProtectorShaderProperties.Nonce2, (int)chacha.GetNonceUint3()[2]); } else if (algorithm == (int)Algorithm.xxtea) { - newMat.SetInteger("_Rounds", (int)rounds); + newMat.SetInteger(ShellProtectorShaderProperties.Rounds, (int)rounds); } var key = new byte[16]; @@ -1268,8 +1316,8 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp uint hashMagic = (uint)mat.GetInstanceID(); var hash = KeyGenerator.SimpleHash(key, hashMagic); - newMat.SetInteger("_HashMagic", (int)hashMagic); - newMat.SetInteger("_PasswordHash", (int)hash); + newMat.SetInteger(ShellProtectorShaderProperties.HashMagic, (int)hashMagic); + newMat.SetInteger(ShellProtectorShaderProperties.PasswordHash, (int)hash); injector.SetKeywords(newMat, otherTex.limTexture != null); diff --git a/Runtime/Scripts/ShellProtectorConstants.cs b/Runtime/Scripts/ShellProtectorConstants.cs new file mode 100644 index 0000000..5a7baa2 --- /dev/null +++ b/Runtime/Scripts/ShellProtectorConstants.cs @@ -0,0 +1,49 @@ +namespace Shell.Protector +{ + public enum ShellProtectorAlgorithm + { + XXTEA = 0, + Chacha = 1 + } + + public enum ShellProtectorTextureFilter + { + Point = 0, + Bilinear = 1 + } + + public enum ShellProtectorFallback + { + White = 0, + Black = 1, + Size4 = 2, + Size8 = 3, + Size16 = 4, + Size32 = 5, + Size64 = 6, + Size128 = 7 + } + + public static class ShellProtectorShaderProperties + { + public const string KeywordPrefix = "_SHELL_PROTECTOR_"; + public const string XXTEAKeyword = "_SHELL_PROTECTOR_XXTEA"; + public const string ChachaKeyword = "_SHELL_PROTECTOR_CHACHA"; + public const string Format0Keyword = "_SHELL_PROTECTOR_FORMAT0"; + public const string Format1Keyword = "_SHELL_PROTECTOR_FORMAT1"; + public const string RimLightKeyword = "_SHELL_PROTECTOR_RIMLIGHT"; + + public const string MipTexture = "_MipTex"; + public const string EncryptTexture0 = "_EncryptTex0"; + public const string EncryptTexture1 = "_EncryptTex1"; + public const string WidthOffset = "_Woffset"; + public const string HeightOffset = "_Hoffset"; + public const string Nonce0 = "_Nonce0"; + public const string Nonce1 = "_Nonce1"; + public const string Nonce2 = "_Nonce2"; + public const string Rounds = "_Rounds"; + public const string PasswordHash = "_PasswordHash"; + public const string HashMagic = "_HashMagic"; + public const string KeyPrefix = "_Key"; + } +} diff --git a/Runtime/Scripts/ShellProtectorConstants.cs.meta b/Runtime/Scripts/ShellProtectorConstants.cs.meta new file mode 100644 index 0000000..6088b1f --- /dev/null +++ b/Runtime/Scripts/ShellProtectorConstants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8020f6b777ad47f6a2bc684c00a1c208 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/ShellProtectorTester.cs b/Runtime/Scripts/ShellProtectorTester.cs index d503621..2318946 100644 --- a/Runtime/Scripts/ShellProtectorTester.cs +++ b/Runtime/Scripts/ShellProtectorTester.cs @@ -43,7 +43,7 @@ public void CheckEncryption() if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { for (int i = 0; i < 16; ++i) - mat.SetInt("_Key" + i, pwd_bytes[i]); + mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, pwd_bytes[i]); } } } @@ -66,7 +66,7 @@ public void CheckEncryption() if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { for (int i = 0; i < 16; ++i) - mat.SetInt("_Key" + i, pwd_bytes[i]); + mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, pwd_bytes[i]); } } } @@ -87,11 +87,11 @@ public void ResetEncryption() { if (mat == null) continue; - if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) - { - for (int i = 16 - userKeyLength; i < 16; ++i) - mat.SetInt("_Key" + i, 0); - } + if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) + { + for (int i = 16 - userKeyLength; i < 16; ++i) + mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, 0); + } } } var skinned_renderers = transform.root.GetComponentsInChildren(true); @@ -107,14 +107,14 @@ public void ResetEncryption() { if (mat == null) continue; - if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) - { - for (int i = 16 - userKeyLength; i < 16; ++i) - mat.SetInt("_Key" + i, 0); - } + if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) + { + for (int i = 16 - userKeyLength; i < 16; ++i) + mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, 0); + } } } } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/Test.cs b/Runtime/Scripts/Test.cs index a68cb76..9416717 100644 --- a/Runtime/Scripts/Test.cs +++ b/Runtime/Scripts/Test.cs @@ -1,6 +1,8 @@ using Shell.Protector; using UnityEngine; +namespace Shell.Protector +{ public class Test { public static void XXTEATest(string fixedKey, string userKey, int userKeySize) @@ -56,3 +58,4 @@ public static void ChachaTest(string fixedKey, string userKey, int userKeySize) Debug.Log("Decrypted data: " + string.Join(", ", result)); } } +} diff --git a/Runtime/Scripts/TextureSettings.cs b/Runtime/Scripts/TextureSettings.cs index cba2108..0076dd2 100644 --- a/Runtime/Scripts/TextureSettings.cs +++ b/Runtime/Scripts/TextureSettings.cs @@ -1,59 +1,72 @@ -using System.IO; -using System.Text.RegularExpressions; +#if UNITY_EDITOR using UnityEditor; using UnityEngine; -#if UNITY_EDITOR - -public class TextureSettings +namespace Shell.Protector { - public static void SetRWEnableTexture(Texture2D texture) + public struct TextureImportChange { - if (texture.isReadable) - return; - string path = AssetDatabase.GetAssetPath(texture); - string meta = File.ReadAllText(path + ".meta"); - - meta = Regex.Replace(meta, "isReadable: 0", "isReadable: 1"); - File.WriteAllText(path + ".meta", meta); - - AssetDatabase.Refresh(); + public bool changed; + public bool readWriteChanged; + public bool crunchChanged; + public bool mipmapChanged; } - public static void SetCrunchCompression(Texture2D texture, bool crunch) + + public class TextureSettings { - string path = AssetDatabase.GetAssetPath(texture); - string meta = File.ReadAllText(path + ".meta"); + public static TextureImportChange SetRWEnableTexture(Texture2D texture) + { + return Apply(texture, readable: true, crunch: null, generateMipmaps: null); + } + + public static TextureImportChange SetCrunchCompression(Texture2D texture, bool crunch) + { + return Apply(texture, readable: null, crunch: crunch, generateMipmaps: null); + } + + public static TextureImportChange SetGenerateMipmap(Texture2D texture, bool generate) + { + return Apply(texture, readable: null, crunch: null, generateMipmaps: generate); + } - if (crunch == false) + static TextureImportChange Apply(Texture2D texture, bool? readable, bool? crunch, bool? generateMipmaps) { - if (texture.format == TextureFormat.DXT1Crunched) + TextureImportChange result = new TextureImportChange(); + if (texture == null) + return result; + + string path = AssetDatabase.GetAssetPath(texture); + if (string.IsNullOrEmpty(path)) + return result; + + TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; + if (importer == null) + return result; + + if (readable.HasValue && importer.isReadable != readable.Value) { - int format = 10; - meta = Regex.Replace(meta, "textureFormat: \\d+", "textureFormat: " + format); + importer.isReadable = readable.Value; + result.readWriteChanged = true; } - else if (texture.format == TextureFormat.DXT5Crunched) + + if (crunch.HasValue && importer.crunchedCompression != crunch.Value) { - int format = 12; - meta = Regex.Replace(meta, "textureFormat: \\d+", "textureFormat: " + format); + importer.crunchedCompression = crunch.Value; + result.crunchChanged = true; } - } - int enable = crunch ? 1 : 0; - meta = Regex.Replace(meta, "crunchedCompression: \\d+", "crunchedCompression: " + enable); - File.WriteAllText(path + ".meta", meta); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } - public static void SetGenerateMipmap(Texture2D texture, bool generate) - { - string path = AssetDatabase.GetAssetPath(texture); - string meta = File.ReadAllText(path + ".meta"); + if (generateMipmaps.HasValue && importer.mipmapEnabled != generateMipmaps.Value) + { + importer.mipmapEnabled = generateMipmaps.Value; + result.mipmapChanged = true; + } - int enable = generate ? 1 : 0; - meta = Regex.Replace(meta, "enableMipMap: \\d+", "enableMipMap: " + enable); - File.WriteAllText(path + ".meta", meta); + result.changed = result.readWriteChanged || result.crunchChanged || result.mipmapChanged; + if (result.changed) + importer.SaveAndReimport(); - AssetDatabase.Refresh(); + return result; + } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/VersionManager.cs b/Runtime/Scripts/VersionManager.cs index bbd6819..7cb41ae 100644 --- a/Runtime/Scripts/VersionManager.cs +++ b/Runtime/Scripts/VersionManager.cs @@ -1,113 +1,84 @@ -#if UNITY_EDITOR +#if UNITY_EDITOR using System.Collections; -using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; using UnityEngine.Networking; -public class VersionManager : MonoBehaviour +namespace Shell.Protector { - string version = ""; - string download_uri = ""; - string github_version = ""; - - static VersionManager instance; - const string github_url = "https://github.com/Shell4026/ShellProtector"; - const string version_uri = github_url + "/raw/main/version.json"; - string raw_data; - - public static VersionManager GetInstance() + public class VersionManager : MonoBehaviour { - if(instance == null) + string githubVersion = ""; + + static VersionManager instance; + const string githubUrl = "https://github.com/Shell4026/ShellProtector"; + const string versionUri = githubUrl + "/raw/main/version.json"; + + public static VersionManager GetInstance() { - GameObject obj = new GameObject("VersionManager"); - obj.hideFlags = HideFlags.HideAndDontSave; - instance = obj.AddComponent(); + if (instance == null) + { + GameObject obj = new GameObject("VersionManager"); + obj.hideFlags = HideFlags.HideAndDontSave; + instance = obj.AddComponent(); + } + return instance; } - return instance; - } - private string[] ParseVersionJson(string data) - { - Match match = Regex.Match(data, "{ \"latestVersion\": \"(.*?)\", \"downloadPage\": \"(.*?)\" }"); - string version = "", download_uri = ""; - if (match.Success) + private string[] ParseVersionJson(string data) { - version = match.Groups[1].Value; - download_uri = match.Groups[2].Value; + Match match = Regex.Match(data, "{ \"latestVersion\": \"(.*?)\", \"downloadPage\": \"(.*?)\" }"); + string version = "", downloadUri = ""; + if (match.Success) + { + version = match.Groups[1].Value; + downloadUri = match.Groups[2].Value; + } + return new[] { version, downloadUri }; } - return new[]{ version, download_uri }; - } - public string GetVersion() - { - MonoScript monoScript = MonoScript.FromMonoBehaviour(this); - string script_path = Directory.GetParent(AssetDatabase.GetAssetPath(monoScript)).ToString(); - string dir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); - string data = File.ReadAllText(Path.Combine(dir, "version.json")); - string[] parse = ParseVersionJson(data); - return parse[0]; - } - public string GetGithubVersion() - { - return github_version; - } - public void Refresh() - { - StartCoroutine(LoadData()); - } - private IEnumerator LoadData() - { - using (UnityWebRequest www = UnityWebRequest.Get(version_uri)) + public string GetVersion() { - yield return www.SendWebRequest(); + MonoScript monoScript = MonoScript.FromMonoBehaviour(this); + string scriptPath = Directory.GetParent(AssetDatabase.GetAssetPath(monoScript)).ToString(); + string dir = Path.GetDirectoryName(Path.GetDirectoryName(scriptPath)); - if (!www.isNetworkError) - { - string json_data = www.downloadHandler.text; - string[] parse = ParseVersionJson(json_data); + string data = File.ReadAllText(Path.Combine(dir, "version.json")); + string[] parse = ParseVersionJson(data); + return parse[0]; + } - github_version = parse[0]; - download_uri = parse[1]; - } - else - { - Debug.Log("[ShellProtector]version checking error: " + www.error); - } + public string GetGithubVersion() + { + return githubVersion; } - } - private IEnumerator DownloadNewVersion() - { - if (download_uri == null) + + public void Refresh() { - yield return StartCoroutine(LoadData()); + StartCoroutine(LoadData()); } - while (true) + private IEnumerator LoadData() { - if (download_uri != null) + using (UnityWebRequest www = UnityWebRequest.Get(versionUri)) { - using (UnityWebRequest www = UnityWebRequest.Get(version_uri)) - { - yield return www.SendWebRequest(); + yield return www.SendWebRequest(); - if (!www.isNetworkError) - { - string json_data = www.downloadHandler.text; - string[] parse = ParseVersionJson(json_data); + if (www.result == UnityWebRequest.Result.Success) + { + string jsonData = www.downloadHandler.text; + string[] parse = ParseVersionJson(jsonData); - github_version = parse[0]; - download_uri = parse[1]; - } - else - { - Debug.Log("[ShellProtector]version checking error: " + www.error); - } + githubVersion = parse[0]; + } + else + { + Debug.Log("[ShellProtector]version checking error: " + www.error); } } } } } -#endif \ No newline at end of file +#endif From e2f7ba238daab59afcb3f408d4ef3694ddb0951a Mon Sep 17 00:00:00 2001 From: Lee Sangyeop Date: Fri, 3 Jul 2026 22:53:39 +0900 Subject: [PATCH 38/59] refactor: align C# naming conventions --- Editor/MaterialAdvancedSettings.cs | 28 +- Editor/ShellProtectorEditor.cs | 156 ++--- Editor/TesterEditor.cs | 20 +- Editor/liltoonCustom/CustomInspector.cs | 36 +- Runtime/Scripts/Algorithm/Chacha20.cs | 12 +- Runtime/Scripts/Algorithm/XXTEA.cs | 12 +- Runtime/Scripts/AnimatorManager.cs | 60 +- Runtime/Scripts/AssetManager.cs | 82 +-- Runtime/Scripts/EncryptedHistory.cs | 2 +- Runtime/Scripts/Injector/Injector.cs | 73 +- Runtime/Scripts/Injector/LilToonInjector.cs | 18 +- Runtime/Scripts/Injector/PoiyomiInjector.cs | 98 +-- Runtime/Scripts/Obfuscator.cs | 15 +- .../Pipeline/ShellProtectorBuildContext.cs | 16 +- Runtime/Scripts/ShellProtector.cs | 649 +++++++++--------- Runtime/Scripts/ShellProtectorTester.cs | 40 +- Runtime/Scripts/Test.cs | 38 +- Tests/Editor/Gpu/GpuTextureDecryptionTests.cs | 6 +- .../ShellProtectorPipelineTests.cs | 20 +- Tests/Editor/Unit/CryptoTests.cs | 6 +- .../Editor/Unit/TextureEncryptManagerTests.cs | 2 +- 21 files changed, 714 insertions(+), 675 deletions(-) diff --git a/Editor/MaterialAdvancedSettings.cs b/Editor/MaterialAdvancedSettings.cs index 1cddfaa..89ee7b8 100644 --- a/Editor/MaterialAdvancedSettings.cs +++ b/Editor/MaterialAdvancedSettings.cs @@ -38,7 +38,7 @@ private string Lang(string word) { if (protector == null) return ""; - return lang.GetLang(protector.lang, word); + return lang.GetLang(protector.Language, word); } private void OnGUI() @@ -48,21 +48,21 @@ private void OnGUI() redStyle.wordWrap = true; scroll = GUILayout.BeginScrollView(scroll); - foreach (var option in protector.matOptions) + foreach (var option in protector.MaterialOptions) { Texture2D mainTex = option.Key.mainTexture as Texture2D; GUILayout.BeginHorizontal(); - option.Value.active = GUILayout.Toggle(option.Value.active, ""); + option.Value.Active = GUILayout.Toggle(option.Value.Active, ""); EditorGUILayout.ObjectField(option.Key, typeof(Material), true); EditorGUILayout.ObjectField(mainTex, typeof(Texture2D), true); - if (option.Value.filter == -1) - option.Value.filter = protector.GetDefaultFilter(); - option.Value.filter = EditorGUILayout.Popup(option.Value.filter, ShellProtector.filterStrings, GUILayout.Width(100)); + if (option.Value.Filter == -1) + option.Value.Filter = protector.GetDefaultFilter(); + option.Value.Filter = EditorGUILayout.Popup(option.Value.Filter, ShellProtector.FilterStrings, GUILayout.Width(100)); - if (option.Value.fallback == -1) - option.Value.fallback = protector.GetDefaultFallback(); - option.Value.fallback = EditorGUILayout.Popup(option.Value.fallback, ShellProtector.fallbackStrings, GUILayout.Width(100)); + if (option.Value.Fallback == -1) + option.Value.Fallback = protector.GetDefaultFallback(); + option.Value.Fallback = EditorGUILayout.Popup(option.Value.Fallback, ShellProtector.FallbackStrings, GUILayout.Width(100)); if (AssetManager.GetInstance().IsPoiyomi(option.Key.shader)) { @@ -118,16 +118,16 @@ private void Init() foreach (var mat in mats) { matSets.Add(mat); - if (!protector.matOptions.ContainsKey(mat)) + if (!protector.MaterialOptions.ContainsKey(mat)) { var option = new ShellProtector.MatOption(); - option.active = true; - protector.matOptions.Add(mat, option); + option.Active = true; + protector.MaterialOptions.Add(mat, option); } } List removed = new List(); - foreach (var pair in protector.matOptions) + foreach (var pair in protector.MaterialOptions) { if (!matSets.Contains(pair.Key)) { @@ -135,7 +135,7 @@ private void Init() } } foreach (var mat in removed) - protector.matOptions.Remove(mat); + protector.MaterialOptions.Remove(mat); } } } diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index f7015e6..4154ae3 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -38,27 +38,27 @@ public class ShellProtectorEditor : Editor ShellProtectorEditorViewModel viewModel; bool debug = false; bool option = true; - bool ObfuscatorOption = true; + bool obfuscatorOption = true; bool forceProgress = false; bool fallbackOption = true; readonly string[] languages = new string[3]; - readonly string[] enc_funcs = new string[2]; - readonly string[] key_lengths = new string[5]; + readonly string[] encryptFunctions = new string[2]; + readonly string[] keyLengthLabels = new string[5]; List shaders = new List(); - bool show_pwd = false; + bool showPassword = false; Texture2D tex; - string github_version; + string githubVersion; private string Lang(string word) { if (root == null) return ""; - return lang.GetLang(root.lang, word); + return lang.GetLang(root.Language, word); } void OnEnable() @@ -66,11 +66,11 @@ void OnEnable() root = target as ShellProtector; MonoScript monoScript = MonoScript.FromMonoBehaviour(root); - string script_path = AssetDatabase.GetAssetPath(monoScript); + string scriptPath = AssetDatabase.GetAssetPath(monoScript); - root.assetDir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); + root.AssetDir = Path.GetDirectoryName(Path.GetDirectoryName(scriptPath)); - gameobjectList = new ReorderableList(serializedObject, serializedObject.FindProperty("gameobjectList"), true, true, true, true); + gameobjectList = new ReorderableList(serializedObject, serializedObject.FindProperty("_gameObjectList"), true, true, true, true); gameobjectList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Object list")); gameobjectList.drawElementCallback = (rect, index, is_active, is_focused) => { @@ -78,7 +78,7 @@ void OnEnable() EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none); }; - materialList = new ReorderableList(serializedObject, serializedObject.FindProperty("materialList"), true, true, true, true); + materialList = new ReorderableList(serializedObject, serializedObject.FindProperty("_materialList"), true, true, true, true); materialList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Material List")); materialList.drawElementCallback = (rect, index, is_active, is_focused) => { @@ -94,7 +94,7 @@ void OnEnable() EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none); }; - obfuscationList = new ReorderableList(serializedObject, serializedObject.FindProperty("obfuscationRenderers"), true, true, true, true); + obfuscationList = new ReorderableList(serializedObject, serializedObject.FindProperty("_obfuscationRenderers"), true, true, true, true); obfuscationList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("BlendShape obfuscation")); obfuscationList.drawElementCallback = (rect, index, is_active, is_focused) => { @@ -103,32 +103,32 @@ void OnEnable() }; #region SerializedObject - rounds = serializedObject.FindProperty("rounds"); - filter = serializedObject.FindProperty("filter"); - fallback = serializedObject.FindProperty("fallback"); - algorithm = serializedObject.FindProperty("algorithm"); - keySize = serializedObject.FindProperty("keySize"); - keySizeIdx = serializedObject.FindProperty("keySizeIdx"); - syncSize = serializedObject.FindProperty("syncSize"); - deleteFolders = serializedObject.FindProperty("deleteFolders"); - bUseSmallMipTexture = serializedObject.FindProperty("bUseSmallMipTexture"); - bPreserveMMD = serializedObject.FindProperty("bPreserveMMD"); - turnOnAllSafetyFallback = serializedObject.FindProperty("turnOnAllSafetyFallback"); + rounds = serializedObject.FindProperty("_rounds"); + filter = serializedObject.FindProperty("_filter"); + fallback = serializedObject.FindProperty("_fallback"); + algorithm = serializedObject.FindProperty("_algorithm"); + keySize = serializedObject.FindProperty("_keySize"); + keySizeIdx = serializedObject.FindProperty("_keySizeIndex"); + syncSize = serializedObject.FindProperty("_syncSize"); + deleteFolders = serializedObject.FindProperty("_deleteFolders"); + bUseSmallMipTexture = serializedObject.FindProperty("_useSmallMipTexture"); + bPreserveMMD = serializedObject.FindProperty("_preserveMmd"); + turnOnAllSafetyFallback = serializedObject.FindProperty("_turnOnAllSafetyFallback"); #endregion viewModel = new ShellProtectorEditorViewModel(root, keySize, syncSize, gameobjectList, materialList); - enc_funcs[0] = "XXTEA"; - enc_funcs[1] = "Chacha8"; + encryptFunctions[0] = "XXTEA"; + encryptFunctions[1] = "Chacha8"; languages[0] = "English"; languages[1] = "한국어"; languages[2] = "日本語"; - key_lengths[0] = Lang("0 (Minimal security)"); - key_lengths[1] = Lang("4 (Low security)"); - key_lengths[2] = Lang("8 (Middle security)"); - key_lengths[3] = Lang("12 (Hight security)"); - key_lengths[4] = Lang("16 (Unbreakable security)"); + keyLengthLabels[0] = Lang("0 (Minimal security)"); + keyLengthLabels[1] = Lang("4 (Low security)"); + keyLengthLabels[2] = Lang("8 (Middle security)"); + keyLengthLabels[3] = Lang("12 (Hight security)"); + keyLengthLabels[4] = Lang("16 (Unbreakable security)"); VersionManager.GetInstance().Refresh(); @@ -140,7 +140,7 @@ public override void OnInspectorGUI() { root = target as ShellProtector; - root.descriptor = EditorGUILayout.ObjectField(root.descriptor, typeof(VRCAvatarDescriptor), true) as VRCAvatarDescriptor; + root.Descriptor = EditorGUILayout.ObjectField(root.Descriptor, typeof(VRCAvatarDescriptor), true) as VRCAvatarDescriptor; GUILayout.BeginHorizontal(); GUILayout.Label(Lang("Current version: ") + VersionManager.GetInstance().GetVersion()); GUILayout.FlexibleSpace(); @@ -152,27 +152,27 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("Languages: ")); GUILayout.FlexibleSpace(); - root.langIdx = EditorGUILayout.Popup(root.langIdx, languages, GUILayout.Width(100)); + root.LanguageIndex = EditorGUILayout.Popup(root.LanguageIndex, languages, GUILayout.Width(100)); - key_lengths[0] = Lang("0 (Minimal security)"); - key_lengths[1] = Lang("4 (Low security)"); - key_lengths[2] = Lang("8 (Middle security)"); - key_lengths[3] = Lang("12 (Hight security)"); - key_lengths[4] = Lang("16 (Unbreakable security)"); + keyLengthLabels[0] = Lang("0 (Minimal security)"); + keyLengthLabels[1] = Lang("4 (Low security)"); + keyLengthLabels[2] = Lang("8 (Middle security)"); + keyLengthLabels[3] = Lang("12 (Hight security)"); + keyLengthLabels[4] = Lang("16 (Unbreakable security)"); - switch (root.langIdx) + switch (root.LanguageIndex) { case 0: - root.lang = "eng"; + root.Language = "eng"; break; case 1: - root.lang = "kor"; + root.Language = "kor"; break; case 2: - root.lang = "jp"; + root.Language = "jp"; break; default: - root.lang = "eng"; + root.Language = "eng"; break; } @@ -192,9 +192,9 @@ public override void OnInspectorGUI() { int length = 16 - keySize.intValue; GUILayout.BeginHorizontal(); - root.pwd = GUILayout.TextField(root.pwd, length, GUILayout.Width(100)); + root.FixedPassword = GUILayout.TextField(root.FixedPassword, length, GUILayout.Width(100)); if (GUILayout.Button(Lang("Generate"))) - root.pwd = KeyGenerator.GenerateRandomString(length); + root.FixedPassword = KeyGenerator.GenerateRandomString(length); GUILayout.FlexibleSpace(); GUILayout.Label(Lang("A password that you don't need to memorize. (max:") + length + ")", EditorStyles.wordWrappedLabel); GUILayout.EndHorizontal(); @@ -202,12 +202,12 @@ public override void OnInspectorGUI() if (keySize.intValue > 0) { GUILayout.BeginHorizontal(); - if(!show_pwd) - root.pwd2 = GUILayout.PasswordField(root.pwd2, '*', keySize.intValue, GUILayout.Width(100)); + if(!showPassword) + root.UserPassword = GUILayout.PasswordField(root.UserPassword, '*', keySize.intValue, GUILayout.Width(100)); else - root.pwd2 = GUILayout.TextField(root.pwd2, keySize.intValue, GUILayout.Width(100)); + root.UserPassword = GUILayout.TextField(root.UserPassword, keySize.intValue, GUILayout.Width(100)); if (GUILayout.Button(Lang("Show"))) - show_pwd = !show_pwd; + showPassword = !showPassword; GUILayout.FlexibleSpace(); GUILayout.Label(Lang("This password should be memorized. (max:") + keySize.intValue + ")", EditorStyles.wordWrappedLabel); GUILayout.EndHorizontal(); @@ -215,12 +215,12 @@ public override void OnInspectorGUI() serializedObject.Update(); viewModel.Refresh(); - GUIStyle red_style = new GUIStyle(GUI.skin.label); - red_style.normal.textColor = Color.red; - red_style.wordWrap = true; + GUIStyle redStyle = new GUIStyle(GUI.skin.label); + redStyle.normal.textColor = Color.red; + redStyle.wordWrap = true; if (!viewModel.HasParameterAsset) - GUILayout.Label(Lang("Cannot find VRCExpressionParameters in your avatar!"), red_style); + GUILayout.Label(Lang("Cannot find VRCExpressionParameters in your avatar!"), redStyle); else { GUILayout.Label(Lang("Free parameter:") + viewModel.FreeParameter, EditorStyles.wordWrappedLabel); @@ -240,7 +240,7 @@ public override void OnInspectorGUI() if(option) { GUILayout.Label(Lang("Max password length"), EditorStyles.boldLabel); - keySizeIdx.intValue = EditorGUILayout.Popup(keySizeIdx.intValue, key_lengths, GUILayout.Width(150)); + keySizeIdx.intValue = EditorGUILayout.Popup(keySizeIdx.intValue, keyLengthLabels, GUILayout.Width(150)); GUILayout.Space(10); switch (keySizeIdx.intValue) @@ -264,26 +264,26 @@ public override void OnInspectorGUI() var syncSize_value = syncSize.intValue; int syncSize_index = 0; - //int[] syncSize_caldidate = { 1, 2, 4}; - //string[] selectable_values = { "1", "2", "4" }; - int[] syncSize_caldidate = { 1 }; - string[] selectable_values = { "1" }; - for (int i = 0; i < syncSize_caldidate.Length; i++) - if (syncSize_caldidate[i] == syncSize_value) + //int[] syncSizeCandidates = { 1, 2, 4}; + //string[] selectableValues = { "1", "2", "4" }; + int[] syncSizeCandidates = { 1 }; + string[] selectableValues = { "1" }; + for (int i = 0; i < syncSizeCandidates.Length; i++) + if (syncSizeCandidates[i] == syncSize_value) syncSize_index = i; if(keySize.intValue > 0) { GUILayout.Label(Lang("Sync speed"), EditorStyles.boldLabel); - syncSize_index = EditorGUILayout.Popup(syncSize_index, selectable_values, GUILayout.Width(100)); - syncSize.intValue = syncSize_caldidate[syncSize_index]; + syncSize_index = EditorGUILayout.Popup(syncSize_index, selectableValues, GUILayout.Width(100)); + syncSize.intValue = syncSizeCandidates[syncSize_index]; GUILayout.Label(Lang("Under development."), EditorStyles.boldLabel); //GUILayout.Label(Lang("When the Sync speed is 2 or higher, OSC1.7 or higher must be used."), EditorStyles.boldLabel); GUILayout.Space(10); } GUILayout.Label(Lang("Encrytion algorithm"), EditorStyles.boldLabel); - algorithm.intValue = EditorGUILayout.Popup(algorithm.intValue, enc_funcs, GUILayout.Width(120)); + algorithm.intValue = EditorGUILayout.Popup(algorithm.intValue, encryptFunctions, GUILayout.Width(120)); if (algorithm.intValue == 0) { @@ -305,7 +305,7 @@ public override void OnInspectorGUI() GUILayout.Space(10); GUILayout.Label(Lang("Default texture filter"), EditorStyles.boldLabel); - filter.intValue = EditorGUILayout.Popup(filter.intValue, ShellProtector.filterStrings, GUILayout.Width(100)); + filter.intValue = EditorGUILayout.Popup(filter.intValue, ShellProtector.FilterStrings, GUILayout.Width(100)); GUILayout.Label(Lang("Setting it to 'Point' may result in aliasing, but performance is better."), EditorStyles.wordWrappedLabel); //GUILayout.Label(Lang("Initial animation speed"), EditorStyles.boldLabel); @@ -337,8 +337,8 @@ public override void OnInspectorGUI() GUILayout.Space(10); } - ObfuscatorOption = EditorGUILayout.Foldout(ObfuscatorOption, Lang("Obfustactor Options")); - if(ObfuscatorOption) + obfuscatorOption = EditorGUILayout.Foldout(obfuscatorOption, Lang("Obfustactor Options")); + if(obfuscatorOption) { obfuscationList.DoLayoutList(); @@ -363,14 +363,14 @@ public override void OnInspectorGUI() GUILayout.EndHorizontal(); GUILayout.Label(Lang("Default fallback texture"), EditorStyles.boldLabel); - fallback.intValue = EditorGUILayout.Popup(fallback.intValue, ShellProtector.fallbackStrings, GUILayout.Width(100)); + fallback.intValue = EditorGUILayout.Popup(fallback.intValue, ShellProtector.FallbackStrings, GUILayout.Width(100)); } #endregion viewModel.Refresh(); if (!viewModel.HasEnoughParameterSpace) { - GUILayout.Label(Lang("Not enough parameter space!"), red_style); + GUILayout.Label(Lang("Not enough parameter space!"), redStyle); GUILayout.BeginHorizontal(); GUILayout.Label(Lang("Force progress")); forceProgress = EditorGUILayout.Toggle(forceProgress); @@ -397,7 +397,7 @@ public override void OnInspectorGUI() GUILayout.Label(Lang("Modular avatars exist. It is automatically encrypted on upload."), modularStyle); #endif - if (GUILayout.Button(Lang("Delete previously encrypted files") + String.Format("({0})", root.GetEncyryptedFoldersCount()))) + if (GUILayout.Button(Lang("Delete previously encrypted files") + String.Format("({0})", root.GetEncryptedFoldersCount()))) { root.CleanEncrypted(); } @@ -407,9 +407,9 @@ public override void OnInspectorGUI() { GUILayout.Space(10); if (GUILayout.Button(Lang("XXTEA test"))) - Test.XXTEATest(root.pwd, root.pwd2, root.GetKeySize()); + Test.XXTEATest(root.FixedPassword, root.UserPassword, root.GetKeySize()); if (GUILayout.Button(Lang("Chacha8 test"))) - Test.ChachaTest(root.pwd, root.pwd2, root.GetKeySize()); + Test.ChachaTest(root.FixedPassword, root.UserPassword, root.GetKeySize()); GUILayout.Space(10); textureList.DoLayoutList(); @@ -424,19 +424,19 @@ public override void OnInspectorGUI() TextureSettings.SetRWEnableTexture(texture); - var result = TextureEncryptManager.EncryptTexture(texture, KeyGenerator.MakeKeyBytes(root.pwd, root.pwd2, keySize.intValue), new XXTEA()); + var result = TextureEncryptManager.EncryptTexture(texture, KeyGenerator.MakeKeyBytes(root.FixedPassword, root.UserPassword, keySize.intValue), new XXTEA()); last = result.Texture1; - if (!AssetDatabase.IsValidFolder(root.assetDir + '/' + root.descriptor.gameObject.name)) - AssetDatabase.CreateFolder(root.assetDir, root.descriptor.gameObject.name); - if (!AssetDatabase.IsValidFolder(root.assetDir + '/' + root.descriptor.gameObject.name + "/mat")) - AssetDatabase.CreateFolder(root.assetDir + '/' + root.descriptor.gameObject.name, "mat"); + if (!AssetDatabase.IsValidFolder(root.AssetDir + '/' + root.Descriptor.gameObject.name)) + AssetDatabase.CreateFolder(root.AssetDir, root.Descriptor.gameObject.name); + if (!AssetDatabase.IsValidFolder(root.AssetDir + '/' + root.Descriptor.gameObject.name + "/mat")) + AssetDatabase.CreateFolder(root.AssetDir + '/' + root.Descriptor.gameObject.name, "mat"); - AssetDatabase.CreateAsset(result.Texture1, root.assetDir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.asset"); - File.WriteAllBytes(root.assetDir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt.png", result.Texture2.EncodeToPNG()); + AssetDatabase.CreateAsset(result.Texture1, root.AssetDir + '/' + root.Descriptor.gameObject.name + '/' + texture.name + "_encrypt.asset"); + File.WriteAllBytes(root.AssetDir + '/' + root.Descriptor.gameObject.name + '/' + texture.name + "_encrypt.png", result.Texture2.EncodeToPNG()); if (result.Texture2 != null) - AssetDatabase.CreateAsset(result.Texture2, root.assetDir + '/' + root.descriptor.gameObject.name + '/' + texture.name + "_encrypt2.asset"); + AssetDatabase.CreateAsset(result.Texture2, root.AssetDir + '/' + root.Descriptor.gameObject.name + '/' + texture.name + "_encrypt2.asset"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); @@ -468,7 +468,7 @@ static void AddShellProtector() obj.transform.parent = gameobject.transform; var shellProtector = obj.AddComponent(); - shellProtector.descriptor = av3; + shellProtector.Descriptor = av3; shellProtector.Init(); Selection.activeObject = obj; diff --git a/Editor/TesterEditor.cs b/Editor/TesterEditor.cs index 45d2008..81766b0 100644 --- a/Editor/TesterEditor.cs +++ b/Editor/TesterEditor.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR +#if UNITY_EDITOR using System.Collections; using System.Collections.Generic; using UnityEditor; @@ -18,7 +18,7 @@ private string Lang(string word) { if (root == null) return ""; - return lang.GetLang(root.lang, word); + return lang.GetLang(root.Language, word); } public void OnEnable() { @@ -29,14 +29,14 @@ public void OnEnable() } public override void OnInspectorGUI() { - root.protector = EditorGUILayout.ObjectField(root.protector, typeof(ShellProtector), true, GUILayout.Width(200)) as ShellProtector; + root.Protector = EditorGUILayout.ObjectField(root.Protector, typeof(ShellProtector), true, GUILayout.Width(200)) as ShellProtector; GUILayout.Space(10); GUILayout.BeginHorizontal(); GUILayout.Label(Lang("Max password length")); GUILayout.FlexibleSpace(); - root.userKeyLength = EditorGUILayout.IntField(root.userKeyLength, GUILayout.Width(50)); + root.UserKeyLength = EditorGUILayout.IntField(root.UserKeyLength, GUILayout.Width(50)); GUILayout.EndHorizontal(); GUILayout.Space(10); @@ -47,24 +47,24 @@ public override void OnInspectorGUI() GUILayout.Space(10); - root.langIdx = EditorGUILayout.Popup(root.langIdx, languages, GUILayout.Width(100)); - switch (root.langIdx) + root.LanguageIndex = EditorGUILayout.Popup(root.LanguageIndex, languages, GUILayout.Width(100)); + switch (root.LanguageIndex) { case 0: - root.lang = "eng"; + root.Language = "eng"; break; case 1: - root.lang = "kor"; + root.Language = "kor"; break; default: - root.lang = "eng"; + root.Language = "eng"; break; } GUILayout.EndHorizontal(); GUILayout.Space(10); - if (root.userKeyLength == 0) + if (root.UserKeyLength == 0) { GUILayout.Label(Lang("It's okay for the 0-digit password to be the same as the original.")); } diff --git a/Editor/liltoonCustom/CustomInspector.cs b/Editor/liltoonCustom/CustomInspector.cs index e0f016d..722c919 100644 --- a/Editor/liltoonCustom/CustomInspector.cs +++ b/Editor/liltoonCustom/CustomInspector.cs @@ -10,15 +10,15 @@ namespace lilToon public class ShellProtectorInspector : lilToonInspector { // Custom properties - MaterialProperty mip_tex; - MaterialProperty encrypted_tex0; - MaterialProperty encrypted_tex1; + MaterialProperty mipTexture; + MaterialProperty encryptedTexture0; + MaterialProperty encryptedTexture1; MaterialProperty[] key = new MaterialProperty[16]; - MaterialProperty password_hash; + MaterialProperty passwordHash; MaterialProperty hashMagic; private static bool isShowCustomProperties; - private static bool show_pwd = false; + private static bool showPassword = false; private const string shaderName = "ShellProtector"; protected override void LoadCustomProperties(MaterialProperty[] props, Material material) @@ -33,10 +33,10 @@ protected override void LoadCustomProperties(MaterialProperty[] props, Material //isShowRenderMode = false; //LoadCustomLanguage(""); - mip_tex = FindProperty(ShellProtectorShaderProperties.MipTexture, props); - encrypted_tex0 = FindProperty(ShellProtectorShaderProperties.EncryptTexture0, props); - encrypted_tex1 = FindProperty(ShellProtectorShaderProperties.EncryptTexture1, props); - password_hash = FindProperty(ShellProtectorShaderProperties.PasswordHash, props); + mipTexture = FindProperty(ShellProtectorShaderProperties.MipTexture, props); + encryptedTexture0 = FindProperty(ShellProtectorShaderProperties.EncryptTexture0, props); + encryptedTexture1 = FindProperty(ShellProtectorShaderProperties.EncryptTexture1, props); + passwordHash = FindProperty(ShellProtectorShaderProperties.PasswordHash, props); hashMagic = FindProperty(ShellProtectorShaderProperties.HashMagic, props); for (int i = 0; i < key.Length; ++i) @@ -60,21 +60,21 @@ protected override void DrawCustomProperties(Material material) EditorGUILayout.LabelField(GetLoc("ShellProtector"), customToggleFont); EditorGUILayout.BeginVertical(boxInnerHalf); - if (mip_tex != null) - m_MaterialEditor.ShaderProperty(mip_tex, "Mip reference texture"); - if(encrypted_tex0 != null) - m_MaterialEditor.ShaderProperty(encrypted_tex0, "Encrypted texture0"); - if (encrypted_tex1 != null) - m_MaterialEditor.ShaderProperty(encrypted_tex1, "Encrypted texture1"); + if (mipTexture != null) + m_MaterialEditor.ShaderProperty(mipTexture, "Mip reference texture"); + if(encryptedTexture0 != null) + m_MaterialEditor.ShaderProperty(encryptedTexture0, "Encrypted texture0"); + if (encryptedTexture1 != null) + m_MaterialEditor.ShaderProperty(encryptedTexture1, "Encrypted texture1"); EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical(); - m_MaterialEditor.ShaderProperty(password_hash, "Password hash"); + m_MaterialEditor.ShaderProperty(passwordHash, "Password hash"); m_MaterialEditor.ShaderProperty(hashMagic, "Hash Salt"); - show_pwd = Foldout("Keys", "Keys", show_pwd); - if(show_pwd) + showPassword = Foldout("Keys", "Keys", showPassword); + if(showPassword) { for(int i = 0; i < key.Length; ++i) { diff --git a/Runtime/Scripts/Algorithm/Chacha20.cs b/Runtime/Scripts/Algorithm/Chacha20.cs index 087acbf..4bb9766 100644 --- a/Runtime/Scripts/Algorithm/Chacha20.cs +++ b/Runtime/Scripts/Algorithm/Chacha20.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Runtime.CompilerServices; using UnityEngine; @@ -8,7 +8,7 @@ namespace Shell.Protector public class Chacha20 : IEncryptor { public string Keyword => ShellProtectorShaderProperties.ChachaKeyword; - public byte[] nonce = new byte[12]; + public byte[] Nonce { get; } = new byte[12]; [MethodImpl(MethodImplOptions.AggressiveInlining)] byte[] U32t8le(uint v) { @@ -56,12 +56,12 @@ byte[] Chacha20Serialize(uint[] input) } return output; } - byte[] Chacha20Block(uint[] input, int num_rounds) + byte[] Chacha20Block(uint[] input, int numRounds) { byte[] output = new byte[64]; uint[] x = new uint[16]; Array.Copy(input, 0, x, 0, input.Length); - for (int i = num_rounds; i > 0; i -= 2) + for (int i = numRounds; i > 0; i -= 2) { Chacha20QuarterRound(x, 0, 4, 8, 12); Chacha20QuarterRound(x, 1, 5, 9, 13); @@ -155,7 +155,7 @@ public uint[] Encrypt(uint[] data, uint[] key) dataBytes[i + 3] = (byte)(data[i / 4] >> 24 & 0xFF); } - byte[] resultBytes = ChaCha20XOR(keyBytes, 1, nonce, dataBytes); + byte[] resultBytes = ChaCha20XOR(keyBytes, 1, Nonce, dataBytes); uint[] result = new uint[data.Length]; for (int i = 0; i < data.Length; ++i) { @@ -174,7 +174,7 @@ public uint[] GetNonceUint3() byte[] nonceTmp = new byte[4]; for (int i = 0; i < 3; i++) { - Array.Copy(nonce, i * 4, nonceTmp, 0, nonceTmp.Length); + Array.Copy(Nonce, i * 4, nonceTmp, 0, nonceTmp.Length); result[i] = U8t32le(nonceTmp); } diff --git a/Runtime/Scripts/Algorithm/XXTEA.cs b/Runtime/Scripts/Algorithm/XXTEA.cs index 397fde4..191594c 100644 --- a/Runtime/Scripts/Algorithm/XXTEA.cs +++ b/Runtime/Scripts/Algorithm/XXTEA.cs @@ -1,4 +1,4 @@ -using System; +using System; using UnityEngine; namespace Shell.Protector @@ -8,7 +8,7 @@ public class XXTEA : IEncryptor public string Keyword => ShellProtectorShaderProperties.XXTEAKeyword; const uint Delta = 0x9E3779B9; - public uint m_rounds = 0; + public uint Rounds { get; set; } public uint[] Encrypt(uint[] data, uint[] key) { uint n = (uint)data.Length; @@ -28,10 +28,10 @@ public uint[] Encrypt(uint[] data, uint[] key) uint y, z, sum; uint p, rounds, e; - if (m_rounds == 0) + if (Rounds == 0) rounds = 6 + 52 / n; else - rounds = m_rounds; + rounds = Rounds; sum = 0; z = result[n - 1]; do @@ -63,10 +63,10 @@ public uint[] Decrypt(uint[] data, uint[] key) uint y, z, sum; uint p, rounds, e; - if (m_rounds == 0) + if (Rounds == 0) rounds = 6 + 52 / n; else - rounds = m_rounds; + rounds = Rounds; sum = rounds * Delta; y = result[0]; diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 2b64cf6..3ed08a6 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR +#if UNITY_EDITOR using System.Collections; using System.Collections.Generic; using System.IO; @@ -16,10 +16,10 @@ public class AnimatorManager : ScriptableObject { Dictionary encryptedClip = new Dictionary(); - public static AnimatorController DuplicateAnimator(RuntimeAnimatorController anim, string new_dir) + public static AnimatorController DuplicateAnimator(RuntimeAnimatorController anim, string newDir) { string dir = AssetDatabase.GetAssetPath(anim); - string output = Path.Combine(new_dir, anim.name + anim.GetInstanceID().ToString() + "_encrypted.anim"); + string output = Path.Combine(newDir, anim.name + anim.GetInstanceID().ToString() + "_encrypted.anim"); if (!AssetDatabase.CopyAsset(dir, output)) { Debug.LogErrorFormat("Failed to copy a animator: {0}", anim.name); @@ -30,9 +30,9 @@ public static AnimatorController DuplicateAnimator(RuntimeAnimatorController ani return AssetDatabase.LoadAssetAtPath(output, typeof(RuntimeAnimatorController)) as AnimatorController; } - public static void CreateKeyAniamtions(string animation_dir, string new_dir, GameObject[] objs) + public static void CreateKeyAnimations(string animationDir, string newDir, GameObject[] objs) { - string[] files = Directory.GetFiles(animation_dir); + string[] files = Directory.GetFiles(animationDir); foreach (string file in files) { string filename = Path.GetFileName(file); @@ -41,7 +41,7 @@ public static void CreateKeyAniamtions(string animation_dir, string new_dir, Gam if (filename.Contains("dummy")) continue; - string path = Path.Combine(new_dir, filename); + string path = Path.Combine(newDir, filename); AssetDatabase.CopyAsset(file, path); AnimationClip clip = AssetDatabase.LoadAssetAtPath(path); if (clip == null) @@ -102,30 +102,30 @@ static AnimationCurve CreateKeyCurve(bool secondKeyClip) ); } - private static BlendTree[] CreateKeyTree(string animation_dir, int key_length, float speed) + private static BlendTree[] CreateKeyTree(string animationDir, int keyLength, float speed) { - BlendTree[] tree = new BlendTree[key_length]; - int offset = 16 - key_length; - for (int i = 0; i < key_length; ++i) + BlendTree[] tree = new BlendTree[keyLength]; + int offset = 16 - keyLength; + for (int i = 0; i < keyLength; ++i) { - BlendTree tree_key = new BlendTree(); - tree_key.name = "key" + i; - tree_key.blendType = BlendTreeType.Simple1D; - tree_key.blendParameter = ParameterManager.GetKeyName(i); - tree_key.useAutomaticThresholds = false; + BlendTree keyTree = new BlendTree(); + keyTree.name = "key" + i; + keyTree.blendType = BlendTreeType.Simple1D; + keyTree.blendParameter = ParameterManager.GetKeyName(i); + keyTree.useAutomaticThresholds = false; - Motion motion0 = AssetDatabase.LoadAssetAtPath(Path.Combine(animation_dir, "key" + (i + offset) + ".anim"), typeof(AnimationClip)) as AnimationClip; - Motion motion1 = AssetDatabase.LoadAssetAtPath(Path.Combine(animation_dir, "key" + (i + offset) + "_2.anim"), typeof(AnimationClip)) as AnimationClip; + Motion motion0 = AssetDatabase.LoadAssetAtPath(Path.Combine(animationDir, "key" + (i + offset) + ".anim"), typeof(AnimationClip)) as AnimationClip; + Motion motion1 = AssetDatabase.LoadAssetAtPath(Path.Combine(animationDir, "key" + (i + offset) + "_2.anim"), typeof(AnimationClip)) as AnimationClip; - tree_key.AddChild(motion0, -1); - tree_key.AddChild(motion1, 1); + keyTree.AddChild(motion0, -1); + keyTree.AddChild(motion1, 1); - ChildMotion[] motions = tree_key.children; + ChildMotion[] motions = keyTree.children; for (int j = 0; j < motions.Length; ++j) motions[j].timeScale = speed; - tree_key.children = motions; + keyTree.children = motions; - tree[i] = tree_key; + tree[i] = keyTree; } return tree; } @@ -210,27 +210,27 @@ public static void AddKeyLayer(AnimatorController anim, string animationDir, int var layer = anim.layers[anim.layers.Length - 1]; var state = layer.stateMachine.AddState("keys"); - BlendTree tree_root = new BlendTree + BlendTree rootTree = new BlendTree { name = "key_root", blendType = BlendTreeType.Direct, blendParameter = "key_weight" }; - AssetDatabase.AddObjectToAsset(tree_root, anim); - state.motion = tree_root; + AssetDatabase.AddObjectToAsset(rootTree, anim); + state.motion = rootTree; - var key_tree = CreateKeyTree(animationDir, keyLength, speed); + var keyTrees = CreateKeyTree(animationDir, keyLength, speed); for (int i = 0; i < keyLength; ++i) { - tree_root.AddChild(key_tree[i]); - AssetDatabase.AddObjectToAsset(key_tree[i], anim); + rootTree.AddChild(keyTrees[i]); + AssetDatabase.AddObjectToAsset(keyTrees[i], anim); } - ChildMotion[] children = tree_root.children; + ChildMotion[] children = rootTree.children; for (int i = 0; i < children.Length; ++i) children[i].directBlendParameter = "key_weight"; - tree_root.children = children; + rootTree.children = children; } private static void AddSyncEnabledCondition(AnimatorStateTransition transition) diff --git a/Runtime/Scripts/AssetManager.cs b/Runtime/Scripts/AssetManager.cs index 86f119f..0a686b9 100644 --- a/Runtime/Scripts/AssetManager.cs +++ b/Runtime/Scripts/AssetManager.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR +#if UNITY_EDITOR using System; using System.Collections.Generic; using UnityEditor; @@ -10,26 +10,26 @@ namespace Shell.Protector { public class AssetManager { - static AssetManager instance; - readonly Dictionary support_version = new Dictionary(); + static AssetManager _instance; + readonly Dictionary _supportedVersions = new Dictionary(); - static public AssetManager GetInstance() + public static AssetManager GetInstance() { - if(instance == null) - instance = new AssetManager(); - return instance; + if (_instance == null) + _instance = new AssetManager(); + return _instance; } AssetManager() { - support_version.Add("Poiyomi 7.3", 73); - support_version.Add("Poiyomi 8.0", 80); - support_version.Add("Poiyomi 8.1", 81); - support_version.Add("Poiyomi 8.2", 82); - support_version.Add("Poiyomi 9.0", 90); - support_version.Add("Poiyomi 9.1", 91); - support_version.Add("Poiyomi 9.2", 92); - support_version.Add("Poiyomi 9.3", 93); - support_version.Add("lilToon", 0); + _supportedVersions.Add("Poiyomi 7.3", 73); + _supportedVersions.Add("Poiyomi 8.0", 80); + _supportedVersions.Add("Poiyomi 8.1", 81); + _supportedVersions.Add("Poiyomi 8.2", 82); + _supportedVersions.Add("Poiyomi 9.0", 90); + _supportedVersions.Add("Poiyomi 9.1", 91); + _supportedVersions.Add("Poiyomi 9.2", 92); + _supportedVersions.Add("Poiyomi 9.3", 93); + _supportedVersions.Add("lilToon", 0); } public bool IsPoiyomi(Shader shader) { @@ -39,7 +39,7 @@ public bool IsPoiyomi(Shader shader) return true; return false; } - public bool IslilToon(Shader shader) + public bool IsLilToon(Shader shader) { if (shader.name.Contains("lilToon")) return true; @@ -51,29 +51,29 @@ public bool IsLockPoiyomi(Material mat) } public int GetShaderType(Shader shader) { - foreach (var version in support_version) + foreach (var version in _supportedVersions) { if (shader.name.Contains(version.Key)) - return support_version[version.Key]; + return _supportedVersions[version.Key]; } int poiyomiLabel = shader.FindPropertyIndex("shader_master_label"); if (poiyomiLabel != -1) { var str = shader.GetPropertyDescription(poiyomiLabel); if (str.Contains("Poiyomi 9.3")) - return support_version["Poiyomi 9.3"]; + return _supportedVersions["Poiyomi 9.3"]; else if (str.Contains("Poiyomi 9.2")) - return support_version["Poiyomi 9.2"]; + return _supportedVersions["Poiyomi 9.2"]; else if (str.Contains("Poiyomi 9.1")) - return support_version["Poiyomi 9.1"]; + return _supportedVersions["Poiyomi 9.1"]; else if (str.Contains("Poiyomi 9.0")) - return support_version["Poiyomi 9.0"]; + return _supportedVersions["Poiyomi 9.0"]; else if(str.Contains("Poiymoi 8.0")) - return support_version["Poiyomi 8.0"]; + return _supportedVersions["Poiyomi 8.0"]; else if(str.Contains("Poiyomi 8.1")) - return support_version["Poiyomi 8.1"]; + return _supportedVersions["Poiyomi 8.1"]; else if (str.Contains("Poiyomi 8.2")) - return support_version["Poiyomi 8.2"]; + return _supportedVersions["Poiyomi 8.2"]; } return -1; } @@ -90,15 +90,15 @@ public List CheckShader() Debug.Log("Checking Shader..."); string[] guids = AssetDatabase.FindAssets("lilConstants"); string symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); - string symbols_original = string.Copy(symbols); + string originalSymbols = string.Copy(symbols); symbols = symbols.Replace(";LILTOON", ""); symbols = symbols.Replace(";POIYOMI91", ""); symbols = symbols.Replace(";POIYOMI", ""); - List return_shader = new List(); + List availableShaders = new List(); if(guids.Length > 0) { - return_shader.Add("lilToon"); + availableShaders.Add("lilToon"); symbols += ";LILTOON"; } guids = AssetDatabase.FindAssets("ThryEditor"); @@ -107,22 +107,22 @@ public List CheckShader() if (ClassExists("Thry.ThryEditor.ShaderOptimizer")) { symbols += ";POIYOMI91"; - return_shader.Add("Poiyomi9.1>"); + availableShaders.Add("Poiyomi9.1>"); } else // < 9.1 { symbols += ";POIYOMI"; - return_shader.Add("Poiyomi"); + availableShaders.Add("Poiyomi"); } } - if (symbols_original.Contains(";LILTOON") != symbols.Contains(";LILTOON") || - symbols_original.Contains(";POIYOMI") != symbols.Contains(";POIYOMI")) + if (originalSymbols.Contains(";LILTOON") != symbols.Contains(";LILTOON") || + originalSymbols.Contains(";POIYOMI") != symbols.Contains(";POIYOMI")) { PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, symbols); } - return return_shader; + return availableShaders; } public static bool NamespaceExists(string namespaceName) { @@ -140,20 +140,20 @@ public static bool ClassExists(string className) public void CheckModular() { string symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); - string symbols_original = string.Copy(symbols); + string originalSymbols = string.Copy(symbols); symbols = symbols.Replace(";MODULAR", ""); if (!NamespaceExists("nadena.dev.ndmf")) { Debug.Log("ShellProtector: Can't find Modular!"); - if (symbols != symbols_original) + if (symbols != originalSymbols) PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, symbols); return; } Debug.Log("ShellProtector: Find Modular!"); symbols += ";MODULAR"; - if (symbols_original.Contains(";MODULAR") != symbols.Contains(";MODULAR")) + if (originalSymbols.Contains(";MODULAR") != symbols.Contains(";MODULAR")) { PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, symbols); } @@ -176,12 +176,12 @@ public bool LockShader(Material mat) if (IsLockPoiyomi(mat)) return true; - bool bOldOptimizer = false; + bool isOldOptimizer = false; Type optimizer = Type.GetType("Thry.ThryEditor.ShaderOptimizer, ThryAssemblyDefinition"); if (optimizer == null) { - bOldOptimizer = true; + isOldOptimizer = true; optimizer = Type.GetType("Thry.ShaderOptimizer, ThryAssemblyDefinition"); } @@ -190,7 +190,7 @@ public bool LockShader(Material mat) Debug.LogError("Not found the ShaderOptimizer!"); return false; } - if (!bOldOptimizer) + if (!isOldOptimizer) { MethodInfo lockFn = optimizer.GetMethod("LockMaterials"); if (lockFn == null) @@ -222,4 +222,4 @@ public bool LockShader(Material mat) } } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/EncryptedHistory.cs b/Runtime/Scripts/EncryptedHistory.cs index e90a65b..5d3420f 100644 --- a/Runtime/Scripts/EncryptedHistory.cs +++ b/Runtime/Scripts/EncryptedHistory.cs @@ -40,7 +40,7 @@ public void LoadData() public void Save(Shader shader) { - if (AssetManager.GetInstance().IslilToon(shader)) + if (AssetManager.GetInstance().IsLilToon(shader)) return; string dir = AssetDatabase.GetAssetPath(shader); diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index 77824da..0bf1f39 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -1,19 +1,19 @@ -using System.IO; +using System.IO; using UnityEditor; using UnityEngine; #if UNITY_EDITOR namespace Shell.Protector { - abstract public class Injector : IShaderAdapter + public abstract class Injector : IShaderAdapter { - protected ushort[] keys = new ushort[8]; //16byte - protected AssetManager shader_manager = AssetManager.GetInstance(); - protected int filter = 1; - protected string asset_dir; - protected int user_key_length = 4; + protected readonly ushort[] Keys = new ushort[8]; // 16 byte + protected readonly AssetManager ShaderManager = AssetManager.GetInstance(); + protected int Filter = 1; + protected string AssetDir; + protected int UserKeyLength = 4; - protected string shader_code_nofilter = @" + protected string ShaderCodeNoFilter = @" half4 mainTexture; UNITY_BRANCH @@ -26,7 +26,7 @@ abstract public class Injector : IShaderAdapter mainTexture = _MainTex.Sample(sampler_MainTex, mainUV); } "; - protected string shader_code_bilinear = @" + protected string ShaderCodeBilinear = @" half4 mainTexture; UNITY_BRANCH @@ -40,40 +40,40 @@ abstract public class Injector : IShaderAdapter } "; - protected GameObject target; - protected Texture2D main_tex; - protected IEncryptor encryptor; + protected GameObject Target; + protected Texture2D MainTexture; + protected IEncryptor Encryptor; protected struct Decoder { public Decoder(string decrypt = null, string xxtea = null, string chacha = null) { - this.decrypt = decrypt; - this.xxtea = xxtea; - this.chacha = chacha; + Decrypt = decrypt; + Xxtea = xxtea; + Chacha = chacha; } - public string decrypt; - public string xxtea; - public string chacha; + public string Decrypt; + public string Xxtea; + public string Chacha; } - public void Init(GameObject target, Texture2D main_tex, byte[] key, int user_key_length, int filter, string asset_dir, IEncryptor encryptor) + public void Init(GameObject target, Texture2D mainTexture, byte[] key, int userKeyLength, int filter, string assetDir, IEncryptor encryptor) { if (key.Length != 16) { Debug.LogError("Key bytes requires 16 byte"); return; } - this.target = target; - for (int i = 0, j = 0; i < keys.Length; ++i, j += 2) + Target = target; + for (int i = 0, j = 0; i < Keys.Length; ++i, j += 2) { - keys[i] = (ushort)(key[j] | key[j + 1] << 8); + Keys[i] = (ushort)(key[j] | key[j + 1] << 8); } - this.main_tex = main_tex; - this.filter = filter; - this.asset_dir = asset_dir; - this.user_key_length = user_key_length; - this.encryptor = encryptor; + MainTexture = mainTexture; + Filter = filter; + AssetDir = assetDir; + UserKeyLength = userKeyLength; + Encryptor = encryptor; } public abstract bool CanHandle(Shader shader); @@ -88,7 +88,8 @@ public bool WasInjected(Shader shader) return false; } - public void SetKeywords(Material material, bool has_lim_texture = false) { + public void SetKeywords(Material material, bool hasLimTexture = false) + { // Clear keywords prefixed with _SHELL_PROTECTOR_ var keywords = material.shaderKeywords; foreach (string keyword in keywords) @@ -102,11 +103,11 @@ public void SetKeywords(Material material, bool has_lim_texture = false) { TextureEncryptManager.SetFormatKeywords(material); // Set rimlight keyword - if (has_lim_texture) + if (hasLimTexture) material.EnableKeyword(ShellProtectorShaderProperties.RimLightKeyword); // Set encryptor keyword - material.EnableKeyword(encryptor.Keyword); + material.EnableKeyword(Encryptor.Keyword); } public Shader Inject(Material material, string decoderPath, string outputPath, Texture2D mainTexture, ShellProtectorAuxiliaryTextures auxiliaryTextures) @@ -116,18 +117,18 @@ public Shader Inject(Material material, string decoderPath, string outputPath, T decoderPath, outputPath, mainTexture, - auxiliaryTextures.limTexture != null, - auxiliaryTextures.limTexture2 != null, - auxiliaryTextures.outlineTexture != null + auxiliaryTextures.LimTexture != null, + auxiliaryTextures.LimTexture2 != null, + auxiliaryTextures.OutlineTexture != null ); } - public Shader Inject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) + public Shader Inject(Material mat, string decodeDir, string outputDir, Texture2D tex, bool hasLimTexture = false, bool hasLimTexture2 = false, bool outlineTex = false) { - return CustomInject(mat, decode_dir, output_dir, tex, has_lim_texture, has_lim_texture2, outline_tex); + return CustomInject(mat, decodeDir, outputDir, tex, hasLimTexture, hasLimTexture2, outlineTex); } - protected abstract Shader CustomInject(Material mat, string decode_dir, string output_dir, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false); + protected abstract Shader CustomInject(Material mat, string decodeDir, string outputDir, Texture2D tex, bool hasLimTexture = false, bool hasLimTexture2 = false, bool outlineTex = false); } } #endif diff --git a/Runtime/Scripts/Injector/LilToonInjector.cs b/Runtime/Scripts/Injector/LilToonInjector.cs index b3a194e..4046440 100644 --- a/Runtime/Scripts/Injector/LilToonInjector.cs +++ b/Runtime/Scripts/Injector/LilToonInjector.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR +#if UNITY_EDITOR using System; using System.Collections; using System.Collections.Generic; @@ -15,20 +15,20 @@ public class LilToonInjector : Injector { public override bool CanHandle(Shader shader) { - return shader_manager.IslilToon(shader); + return ShaderManager.IsLilToon(shader); } - protected override Shader CustomInject(Material mat, string decode_dir, string output_path, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) + protected override Shader CustomInject(Material mat, string decodeDir, string outputPath, Texture2D tex, bool hasLimTexture = false, bool hasLimTexture2 = false, bool outlineTex = false) { - string[] files = Directory.GetFiles(Path.Combine(asset_dir, "liltoonProtector", "Shaders")); + string[] files = Directory.GetFiles(Path.Combine(AssetDir, "liltoonProtector", "Shaders")); // Find pass - string shader_dir = AssetDatabase.GetAssetPath(mat.shader); - string shader_name = Path.GetFileNameWithoutExtension(shader_dir); + string shaderDir = AssetDatabase.GetAssetPath(mat.shader); + string shaderName = Path.GetFileNameWithoutExtension(shaderDir); string pass = ""; foreach (string file in files) { string filename = Path.GetFileName(file); - if (filename.Contains(shader_name)) + if (filename.Contains(shaderName)) { string f = File.ReadAllText(file); Match match = Regex.Match(f, "lilPassShaderName \".*/(.*?)\""); @@ -43,10 +43,10 @@ protected override Shader CustomInject(Material mat, string decode_dir, string o string filename = Path.GetFileName(file); if (filename.Contains(".lilcontainer")) { - if (filename == shader_name + ".lilcontainer") + if (filename == shaderName + ".lilcontainer") { Debug.Log(filename); - return AssetDatabase.LoadAssetAtPath(Path.Combine(Path.Combine(asset_dir, "liltoonProtector", "Shaders"), filename)); + return AssetDatabase.LoadAssetAtPath(Path.Combine(Path.Combine(AssetDir, "liltoonProtector", "Shaders"), filename)); } } } diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index c947326..02f42d8 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR +#if UNITY_EDITOR using System.Collections; using System.Collections.Generic; using System.IO; @@ -12,42 +12,42 @@ public class PoiyomiInjector : Injector { public override bool CanHandle(Shader shader) { - return shader_manager.IsPoiyomi(shader); + return ShaderManager.IsPoiyomi(shader); } - protected override Shader CustomInject(Material mat, string decode_dir, string output_path, Texture2D tex, bool has_lim_texture = false, bool has_lim_texture2 = false, bool outline_tex = false) + protected override Shader CustomInject(Material mat, string decodeDir, string outputPath, Texture2D tex, bool hasLimTexture = false, bool hasLimTexture2 = false, bool outlineTex = false) { - if (!File.Exists(decode_dir)) + if (!File.Exists(decodeDir)) { - Debug.LogError(decode_dir + " is not exits."); + Debug.LogError(decodeDir + " is not exits."); return null; } Shader shader = mat.shader; - if (!AssetDatabase.IsValidFolder(output_path)) + if (!AssetDatabase.IsValidFolder(outputPath)) { - AssetDatabase.CreateFolder(Path.GetDirectoryName(output_path), Path.GetFileName(output_path)); + AssetDatabase.CreateFolder(Path.GetDirectoryName(outputPath), Path.GetFileName(outputPath)); AssetDatabase.Refresh(); } - string shader_path = AssetDatabase.GetAssetPath(shader); - string shader_name = Path.GetFileName(shader_path); + string shaderPath = AssetDatabase.GetAssetPath(shader); + string shaderName = Path.GetFileName(shaderPath); - string[] files = Directory.GetFiles(Path.GetDirectoryName(shader_path)); + string[] files = Directory.GetFiles(Path.GetDirectoryName(shaderPath)); foreach (string file in files) { string filename = Path.GetFileName(file); if (filename.Contains(".meta")) continue; - File.Copy(file, Path.Combine(output_path, filename), true); + File.Copy(file, Path.Combine(outputPath, filename), true); } - string shader_data = File.ReadAllText(Path.Combine(output_path, shader_name)); - shader_data = shader_data.Insert(0, "//ShellProtect\n"); + string shaderData = File.ReadAllText(Path.Combine(outputPath, shaderName)); + shaderData = shaderData.Insert(0, "//ShellProtect\n"); - InsertProperties(ref shader_data); - if (shader_data == null) + InsertProperties(ref shaderData); + if (shaderData == null) return null; const string declare = @" @@ -62,56 +62,56 @@ protected override Shader CustomInject(Material mat, string decode_dir, string o int version = AssetManager.GetInstance().GetShaderType(shader); if(version == 73) { - string path = output_path + "/CGI_Poicludes.cginc"; + string path = outputPath + "/CGI_Poicludes.cginc"; string poicludes = File.ReadAllText(path); poicludes = Regex.Replace(poicludes, "UNITY_DECLARE_TEX2D\\(_MainTex\\);(.*?)", declare); File.WriteAllText(path, poicludes); - path = output_path + "/CGI_PoiFrag.cginc"; + path = outputPath + "/CGI_PoiFrag.cginc"; string frag = File.ReadAllText(path); frag = Regex.Replace(frag, "float4 frag\\(", "#include \"Decrypt.cginc\"\nfloat4 frag("); - string shader_code = shader_code_nofilter; - if (filter == 0) - shader_code = shader_code_nofilter; - else if (filter == 1) - shader_code = shader_code_bilinear; + string shaderCode = ShaderCodeNoFilter; + if (Filter == 0) + shaderCode = ShaderCodeNoFilter; + else if (Filter == 1) + shaderCode = ShaderCodeBilinear; - frag = Regex.Replace(frag, "float4 mainTexture = .*?;", shader_code); + frag = Regex.Replace(frag, "float4 mainTexture = .*?;", shaderCode); frag = Regex.Replace(frag, "float4 mip_texture = _MipTex.Sample\\(sampler_MipTex, .*?\\);", "float4 mip_texture = _MipTex.Sample(sampler_MipTex, poiMesh.uv[0]);"); File.WriteAllText(path, frag); } else if(version >= 80) { - shader_data = Regex.Replace(shader_data, "UNITY_DECLARE_TEX2D\\(_MainTex\\);", declare); - - shader_data = Regex.Replace(shader_data, "POI2D_SAMPLER_PAN\\((.*?), _MainTex", "POI2D_SAMPLER_PAN($1, _MipTex"); - shader_data = Regex.Replace(shader_data, "UNITY_SAMPLE_TEX2D_SAMPLER_LOD\\((.*?), _MainTex", "UNITY_SAMPLE_TEX2D_SAMPLER_LOD($1, _MipTex"); - shader_data = Regex.Replace(shader_data, "UNITY_SAMPLE_TEX2D_SAMPLER\\((.*?), _MainTex", "UNITY_SAMPLE_TEX2D_SAMPLER($1, _MipTex"); - shader_data = Regex.Replace(shader_data, "float4 frag\\(", "#include \"" + decode_dir + "\"\n\t\t\tfloat4 frag("); - string shader_code = shader_code_nofilter; - if (filter == 0) - shader_code = shader_code_nofilter; - else if (filter == 1) - shader_code = shader_code_bilinear; - - shader_data = Regex.Replace(shader_data, "float4 mainTexture = .*?;", shader_code); - if (has_lim_texture) + shaderData = Regex.Replace(shaderData, "UNITY_DECLARE_TEX2D\\(_MainTex\\);", declare); + + shaderData = Regex.Replace(shaderData, "POI2D_SAMPLER_PAN\\((.*?), _MainTex", "POI2D_SAMPLER_PAN($1, _MipTex"); + shaderData = Regex.Replace(shaderData, "UNITY_SAMPLE_TEX2D_SAMPLER_LOD\\((.*?), _MainTex", "UNITY_SAMPLE_TEX2D_SAMPLER_LOD($1, _MipTex"); + shaderData = Regex.Replace(shaderData, "UNITY_SAMPLE_TEX2D_SAMPLER\\((.*?), _MainTex", "UNITY_SAMPLE_TEX2D_SAMPLER($1, _MipTex"); + shaderData = Regex.Replace(shaderData, "float4 frag\\(", "#include \"" + decodeDir + "\"\n\t\t\tfloat4 frag("); + string shaderCode = ShaderCodeNoFilter; + if (Filter == 0) + shaderCode = ShaderCodeNoFilter; + else if (Filter == 1) + shaderCode = ShaderCodeBilinear; + + shaderData = Regex.Replace(shaderData, "float4 mainTexture = .*?;", shaderCode); + if (hasLimTexture) { if(version == 80) - shader_data = Regex.Replace(shader_data, @"float4 rimColor = .*?_RimTex.*?;", "float4 rimColor = float4(poiFragData.baseColor, poiFragData.alpha);"); + shaderData = Regex.Replace(shaderData, @"float4 rimColor = .*?_RimTex.*?;", "float4 rimColor = float4(poiFragData.baseColor, poiFragData.alpha);"); else - shader_data = Regex.Replace(shader_data, @"float4 rimColor = .*?_RimTex.*?;", "float4 rimColor = mainTexture;"); + shaderData = Regex.Replace(shaderData, @"float4 rimColor = .*?_RimTex.*?;", "float4 rimColor = mainTexture;"); } - if (has_lim_texture2) + if (hasLimTexture2) { if(version == 80) - shader_data = Regex.Replace(shader_data, @"float4 rim2Color = .*?_Rim2Tex.*?;", "float4 rim2Color = float4(poiFragData.baseColor, poiFragData.alpha);"); + shaderData = Regex.Replace(shaderData, @"float4 rim2Color = .*?_Rim2Tex.*?;", "float4 rim2Color = float4(poiFragData.baseColor, poiFragData.alpha);"); else - shader_data = Regex.Replace(shader_data, @"float4 rim2Color = .*?_Rim2Tex.*?;", "float4 rim2Color = mainTexture;"); + shaderData = Regex.Replace(shaderData, @"float4 rim2Color = .*?_Rim2Tex.*?;", "float4 rim2Color = mainTexture;"); } - if (outline_tex) - shader_data = Regex.Replace(shader_data, @"float4 col = .*?_OutlineTexture.*?\* float4(.*?);", "float4 col = float4(poiFragData.baseColor, poiFragData.alpha) * float4$1;"); + if (outlineTex) + shaderData = Regex.Replace(shaderData, @"float4 col = .*?_OutlineTexture.*?\* float4(.*?);", "float4 col = float4(poiFragData.baseColor, poiFragData.alpha) * float4$1;"); } else @@ -119,12 +119,12 @@ protected override Shader CustomInject(Material mat, string decode_dir, string o Debug.LogErrorFormat("{0} is unsupported Poiyomi version!", mat.name); return null; } - File.WriteAllText(Path.Combine(output_path, shader_name), shader_data); + File.WriteAllText(Path.Combine(outputPath, shaderName), shaderData); AssetDatabase.Refresh(); - Shader return_shader = AssetDatabase.LoadAssetAtPath(Path.Combine(output_path, shader_name), typeof(Shader)) as Shader; - return return_shader; + Shader returnShader = AssetDatabase.LoadAssetAtPath(Path.Combine(outputPath, shaderName), typeof(Shader)) as Shader; + return returnShader; } private void InsertProperties(ref string data) @@ -135,7 +135,7 @@ private void InsertProperties(ref string data) Match match = Regex.Match(data, "Properties\\W*{"); if (match.Success) { - int suffix_idx = match.Index + match.Length; + int suffixIndex = match.Index + match.Length; string properties = @" " + ShellProtectorShaderProperties.MipTexture + @" (""MipReference"", 2D) = ""white"" { } " + ShellProtectorShaderProperties.EncryptTexture0 + @" (""Encrypted0"", 2D) = ""white"" { } @@ -153,7 +153,7 @@ private void InsertProperties(ref string data) for (int i = 0; i < 16; ++i) properties += ShellProtectorShaderProperties.KeyPrefix + i + " (\"key" + i + "\", float) = 0\n"; - data = data.Insert(suffix_idx, properties); + data = data.Insert(suffixIndex, properties); } else { diff --git a/Runtime/Scripts/Obfuscator.cs b/Runtime/Scripts/Obfuscator.cs index 12cdd78..4753e35 100644 --- a/Runtime/Scripts/Obfuscator.cs +++ b/Runtime/Scripts/Obfuscator.cs @@ -1,10 +1,11 @@ -#if UNITY_EDITOR +#if UNITY_EDITOR using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEditor.Animations; using UnityEngine; +using UnityEngine.Serialization; using VRC.SDK3.Avatars.Components; namespace Shell.Protector @@ -18,8 +19,10 @@ public class Obfuscator : ScriptableObject Dictionary obfuscatedClip = new Dictionary(); // before, after HashSet mmdShapes = new HashSet(); - public bool clone = true; - public bool bPreserveMMD = true; + [FormerlySerializedAs("clone")] + public bool Clone = true; + [FormerlySerializedAs("bPreserveMMD")] + public bool PreserveMmd = true; public Obfuscator() { @@ -42,7 +45,7 @@ public Obfuscator() } public void Clean() { - clone = true; + Clone = true; animDir = ""; obfuscatedBlendShapeNames.Clear(); obfuscatedBlendShapeIndex.Clear(); @@ -94,7 +97,7 @@ public Mesh ObfuscateBlendShapeMesh(Mesh mesh, string newPath) float weight = mesh.GetBlendShapeFrameWeight(shapeIndex, frameIndex); string blendShapeName = mesh.GetBlendShapeName(shapeIndex); - if (bPreserveMMD) + if (PreserveMmd) { if (mmdShapes.Contains(blendShapeName)) { @@ -240,7 +243,7 @@ public AnimationClip ChangeBlendShapeInClip(AnimationClip clip, GameObject obj) } AnimationClip newClip = clip; - if (clone) + if (Clone) { if (obfuscatedClip.ContainsKey(clip)) newClip = obfuscatedClip[clip]; diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs b/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs index 045e8c7..9deb1c0 100644 --- a/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs +++ b/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs @@ -6,18 +6,18 @@ namespace Shell.Protector { public struct ShellProtectorProcessedTexture { - public EncryptResult encrypted; - public List fallbacks; - public List fallbackOptions; - public byte[] nonce; + public EncryptResult Encrypted; + public List Fallbacks; + public List FallbackOptions; + public byte[] Nonce; } public struct ShellProtectorAuxiliaryTextures { - public Texture2D limTexture; - public Texture2D limTexture2; - public Texture2D outlineTexture; - public Texture2D limShadeTexture; + public Texture2D LimTexture; + public Texture2D LimTexture2; + public Texture2D OutlineTexture; + public Texture2D LimShadeTexture; } public sealed class ShellProtectorBuildContext diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 4baec13..dcf3dd1 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -2,14 +2,15 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.RegularExpressions; -using UnityEditor; using UnityEngine; -using VRC.SDK3.Avatars.ScriptableObjects; +using UnityEditor.Animations; +using UnityEditor; +using UnityEngine.Serialization; using VRC.SDK3.Avatars.Components; -using System.Linq; +using VRC.SDK3.Avatars.ScriptableObjects; using VRC.SDKBase; -using UnityEditor.Animations; #if MODULAR using nadena.dev.modular_avatar.core; @@ -19,107 +20,139 @@ namespace Shell.Protector { public class ShellProtector : MonoBehaviour, IEditorOnly { + [FormerlySerializedAs("gameobjectList")] [SerializeField] - List gameobjectList = new List(); + List _gameObjectList = new List(); + [FormerlySerializedAs("materialList")] [SerializeField] - List materialList = new List(); + List _materialList = new List(); + [FormerlySerializedAs("obfuscationRenderers")] [SerializeField] - List obfuscationRenderers = new List(); + List _obfuscationRenderers = new List(); - Injector injector; - AssetManager shaderManager = AssetManager.GetInstance(); - bool init = false; - string packageAssetDir = null; + Injector _injector; + readonly AssetManager _shaderManager = AssetManager.GetInstance(); + bool _initialized; + string _packageAssetDir; enum Algorithm { - xxtea = 0, - chacha = 1 + Xxtea = 0, + Chacha = 1 } - public string assetDir = "Assets/ShellProtect"; - public string pwd = "password"; // fixed password - public string pwd2 = "pass"; // user password - public int langIdx = 0; - public string lang = "kor"; - public VRCAvatarDescriptor descriptor; + [FormerlySerializedAs("assetDir")] + [SerializeField] string _assetDir = "Assets/ShellProtect"; + [FormerlySerializedAs("pwd")] + [SerializeField] string _fixedPassword = "password"; + [FormerlySerializedAs("pwd2")] + [SerializeField] string _userPassword = "pass"; + [FormerlySerializedAs("langIdx")] + [SerializeField] int _languageIndex; + [FormerlySerializedAs("lang")] + [SerializeField] string _language = "kor"; + [FormerlySerializedAs("descriptor")] + [SerializeField] VRCAvatarDescriptor _descriptor; + + public string AssetDir { get => _assetDir; set => _assetDir = value; } + public string FixedPassword { get => _fixedPassword; set => _fixedPassword = value; } + public string UserPassword { get => _userPassword; set => _userPassword = value; } + public int LanguageIndex { get => _languageIndex; set => _languageIndex = value; } + public string Language { get => _language; set => _language = value; } + public VRCAvatarDescriptor Descriptor { get => _descriptor; set => _descriptor = value; } [Serializable] public class MatOption { - public bool active = true; - public int filter = -1; - public int fallback = -1; - public bool emissionEnc = false; + [FormerlySerializedAs("active")] + public bool Active = true; + [FormerlySerializedAs("filter")] + public int Filter = -1; + [FormerlySerializedAs("fallback")] + public int Fallback = -1; + [FormerlySerializedAs("emissionEnc")] + public bool EmissionEnc; } [Serializable] public class MaterialOptionPair { - public Material material; - public MatOption option; + [FormerlySerializedAs("material")] + public Material Material; + [FormerlySerializedAs("option")] + public MatOption Option; } + [FormerlySerializedAs("matOptionSaved")] [SerializeField] - List matOptionSaved = new List(); - public Dictionary matOptions = new Dictionary(); - - EncryptedHistory history; - - //Must clear them before start encrypting// - ShellProtectorBuildResult buildResult = new ShellProtectorBuildResult(); - HashSet meshes => buildResult.Meshes; - Dictionary encryptedMaterials => buildResult.EncryptedMaterials; // original, encrypted - Dictionary processedTextures => buildResult.ProcessedTextures; - ////////////////////////////////// - - [SerializeField] uint rounds = 20; - [SerializeField] int filter = 1; - [SerializeField] int fallback = 5; - [SerializeField] int algorithm = 1; + List _matOptionSaved = new List(); + public Dictionary MaterialOptions = new Dictionary(); + + EncryptedHistory _history; + + ShellProtectorBuildResult _buildResult = new ShellProtectorBuildResult(); + HashSet Meshes => _buildResult.Meshes; + Dictionary EncryptedMaterials => _buildResult.EncryptedMaterials; + Dictionary ProcessedTextures => _buildResult.ProcessedTextures; + + [FormerlySerializedAs("rounds")] + [SerializeField] uint _rounds = 20; + [FormerlySerializedAs("filter")] + [SerializeField] int _filter = 1; + [FormerlySerializedAs("fallback")] + [SerializeField] int _fallback = 5; + [FormerlySerializedAs("algorithm")] + [SerializeField] int _algorithm = 1; #pragma warning disable CS0414 - [SerializeField] int keySizeIdx = 3; + [FormerlySerializedAs("keySizeIdx")] + [SerializeField] int _keySizeIndex = 3; #pragma warning restore CS0414 - [SerializeField] int keySize = 12; - [SerializeField] int syncSize = 1; - [SerializeField] bool deleteFolders = true; - [SerializeField] bool bUseSmallMipTexture = true; + [FormerlySerializedAs("keySize")] + [SerializeField] int _keySize = 12; + [FormerlySerializedAs("syncSize")] + [SerializeField] int _syncSize = 1; + [FormerlySerializedAs("deleteFolders")] + [SerializeField] bool _deleteFolders = true; + [FormerlySerializedAs("bUseSmallMipTexture")] + [SerializeField] bool _useSmallMipTexture = true; - [SerializeField] bool bPreserveMMD = true; + [FormerlySerializedAs("bPreserveMMD")] + [SerializeField] bool _preserveMmd = true; - [SerializeField] bool turnOnAllSafetyFallback = true; + [FormerlySerializedAs("turnOnAllSafetyFallback")] + [SerializeField] bool _turnOnAllSafetyFallback = true; - public static readonly string[] filterStrings = new string[2] { "Point", "Bilinear" }; - public static readonly string[] fallbackStrings = new string[8] { "white", "black", "4x4", "8x8", "16x16", "32x32", "64x64", "128x128" }; + public static readonly string[] FilterStrings = new string[2] { "Point", "Bilinear" }; + public static readonly string[] FallbackStrings = new string[8] { "white", "black", "4x4", "8x8", "16x16", "32x32", "64x64", "128x128" }; - Texture2D fallbackWhite = null; - Texture2D fallbackBlack = null; + Texture2D _fallbackWhite; + Texture2D _fallbackBlack; string GetPackageAssetDir() { - if (!string.IsNullOrEmpty(packageAssetDir)) - return packageAssetDir; + if (!string.IsNullOrEmpty(_packageAssetDir)) + return _packageAssetDir; MonoScript monoScript = MonoScript.FromMonoBehaviour(this); - string script_path = AssetDatabase.GetAssetPath(monoScript); - packageAssetDir = Path.GetDirectoryName(Path.GetDirectoryName(script_path)); - return packageAssetDir; + string scriptPath = AssetDatabase.GetAssetPath(monoScript); + _packageAssetDir = Path.GetDirectoryName(Path.GetDirectoryName(scriptPath)); + return _packageAssetDir; } string ResolveOutputAssetDir() { string packageDir = GetPackageAssetDir(); - if (string.IsNullOrEmpty(assetDir) || assetDir == "Assets/ShellProtect") - assetDir = packageDir; - return assetDir; + if (string.IsNullOrEmpty(_assetDir) || _assetDir == "Assets/ShellProtect") + _assetDir = packageDir; + return _assetDir; } public void Init() { - if (init) + if (_initialized) return; HashSet rendererSet = new HashSet(); - Transform child = descriptor.transform.Find("Body"); + Transform child = _descriptor.transform.Find("Body"); if (child != null) { SkinnedMeshRenderer renderer = child.GetComponent(); @@ -135,30 +168,30 @@ public void Init() } foreach (var renderer in rendererSet) { - obfuscationRenderers.Add(renderer); + _obfuscationRenderers.Add(renderer); } - init = true; + _initialized = true; } public void SyncMatOption() { - foreach (var pair in matOptionSaved) + foreach (var pair in _matOptionSaved) { - if (pair.material != null) - matOptions[pair.material] = pair.option; + if (pair.Material != null) + MaterialOptions[pair.Material] = pair.Option; } } public void SaveMatOption() { - foreach (var pair in matOptions) + foreach (var pair in MaterialOptions) { - matOptionSaved.Add(new MaterialOptionPair { material = pair.Key, option = pair.Value }); + _matOptionSaved.Add(new MaterialOptionPair { Material = pair.Key, Option = pair.Value }); } } public byte[] GetKeyBytes() { - return KeyGenerator.MakeKeyBytes(pwd, pwd2, keySize); + return KeyGenerator.MakeKeyBytes(_fixedPassword, _userPassword, _keySize); } public GameObject DuplicateAvatar(GameObject avatar) @@ -171,43 +204,43 @@ public GameObject DuplicateAvatar(GameObject avatar) public GameObject Encrypt(bool isModular = true) { - return Encrypt(bUseSmallMipTexture, isModular); + return Encrypt(_useSmallMipTexture, isModular); } - public GameObject Encrypt(bool bUseSmallMip, bool isModular = true) + public GameObject Encrypt(bool useSmallMip, bool isModular = true) { - var request = new ShellProtectorBuildRequest(this, descriptor, bUseSmallMip, isModular); + var request = new ShellProtectorBuildRequest(this, _descriptor, useSmallMip, isModular); var result = new ShellProtectorPipeline().Encrypt(request, CreateSettings()); ApplyBuildResult(result); return result.Avatar; } - internal ShellProtectorBuildResult CurrentBuildResult => buildResult; + internal ShellProtectorBuildResult CurrentBuildResult => _buildResult; internal void ApplyBuildResult(ShellProtectorBuildResult result) { - buildResult = result ?? new ShellProtectorBuildResult(); + _buildResult = result ?? new ShellProtectorBuildResult(); } internal ShellProtectorSettings CreateSettings() { return new ShellProtectorSettings { - AssetDir = assetDir, - FixedPassword = pwd, - UserPassword = pwd2, - Language = lang, - LanguageIndex = langIdx, - Rounds = rounds, - Filter = filter, - Fallback = fallback, - Algorithm = algorithm, - KeySize = keySize, - SyncSize = syncSize, - DeleteFolders = deleteFolders, - UseSmallMipTexture = bUseSmallMipTexture, - PreserveMMD = bPreserveMMD, - TurnOnAllSafetyFallback = turnOnAllSafetyFallback + AssetDir = _assetDir, + FixedPassword = _fixedPassword, + UserPassword = _userPassword, + Language = _language, + LanguageIndex = _languageIndex, + Rounds = _rounds, + Filter = _filter, + Fallback = _fallback, + Algorithm = _algorithm, + KeySize = _keySize, + SyncSize = _syncSize, + DeleteFolders = _deleteFolders, + UseSmallMipTexture = _useSmallMipTexture, + PreserveMMD = _preserveMmd, + TurnOnAllSafetyFallback = _turnOnAllSafetyFallback }; } @@ -216,50 +249,50 @@ internal void ApplySettings(ShellProtectorSettings settings) if (settings == null) return; - assetDir = settings.AssetDir; - pwd = settings.FixedPassword; - pwd2 = settings.UserPassword; - lang = settings.Language; - langIdx = settings.LanguageIndex; - rounds = settings.Rounds; - filter = settings.Filter; - fallback = settings.Fallback; - algorithm = settings.Algorithm; - keySize = settings.KeySize; - syncSize = settings.SyncSize; - deleteFolders = settings.DeleteFolders; - bUseSmallMipTexture = settings.UseSmallMipTexture; - bPreserveMMD = settings.PreserveMMD; - turnOnAllSafetyFallback = settings.TurnOnAllSafetyFallback; + _assetDir = settings.AssetDir; + _fixedPassword = settings.FixedPassword; + _userPassword = settings.UserPassword; + _language = settings.Language; + _languageIndex = settings.LanguageIndex; + _rounds = settings.Rounds; + _filter = settings.Filter; + _fallback = settings.Fallback; + _algorithm = settings.Algorithm; + _keySize = settings.KeySize; + _syncSize = settings.SyncSize; + _deleteFolders = settings.DeleteFolders; + _useSmallMipTexture = settings.UseSmallMipTexture; + _preserveMmd = settings.PreserveMMD; + _turnOnAllSafetyFallback = settings.TurnOnAllSafetyFallback; } - internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) + internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) { - meshes.Clear(); - encryptedMaterials.Clear(); - processedTextures.Clear(); + Meshes.Clear(); + EncryptedMaterials.Clear(); + ProcessedTextures.Clear(); SyncMatOption(); string resourceDir = GetPackageAssetDir(); - assetDir = ResolveOutputAssetDir(); - string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); - buildResult.AvatarDir = avatarDir; + _assetDir = ResolveOutputAssetDir(); + string avatarDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()); + _buildResult.AvatarDir = avatarDir; - Debug.Log("AssetDir: " + assetDir); + Debug.Log("AssetDir: " + _assetDir); - if (fallbackWhite == null) - fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(resourceDir, "white.png"), typeof(Texture2D)) as Texture2D; - if (fallbackBlack == null) - fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(resourceDir, "black.png"), typeof(Texture2D)) as Texture2D; + if (_fallbackWhite == null) + _fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(resourceDir, "white.png"), typeof(Texture2D)) as Texture2D; + if (_fallbackBlack == null) + _fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(resourceDir, "black.png"), typeof(Texture2D)) as Texture2D; - if (descriptor == null) + if (_descriptor == null) { Debug.LogError("Can't find avatar descriptor!"); return null; } - descriptor.gameObject.SetActive(true); + _descriptor.gameObject.SetActive(true); Debug.Log("Key bytes: " + string.Join(", ", GetKeyBytes())); var materials = new List(); @@ -274,12 +307,12 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) GameObject avatar; if (!isModular) { - avatar = DuplicateAvatar(descriptor.gameObject); + avatar = DuplicateAvatar(_descriptor.gameObject); Debug.Log("Duplicate avatar success."); } else { - avatar = descriptor.gameObject; + avatar = _descriptor.gameObject; } if (avatar == null) @@ -288,37 +321,37 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) return null; } byte[] keyBytes = GetKeyBytes(); - buildResult.KeyBytes = keyBytes; + _buildResult.KeyBytes = keyBytes; CreateFolders(); ///////////////////Select crypto algorithm///////////////////// IEncryptor encryptor = new XXTEA(); - if (algorithm == (int)Algorithm.xxtea) + if (_algorithm == (int)Algorithm.Xxtea) { XXTEA xxtea = new XXTEA(); - xxtea.m_rounds = rounds; + xxtea.Rounds = _rounds; encryptor = xxtea; } - else if(algorithm == (int)Algorithm.chacha) + else if (_algorithm == (int)Algorithm.Chacha) { Chacha20 chacha = new Chacha20(); - byte[] hash1 = KeyGenerator.GetKeyHash(keyBytes, KeyGenerator.GenerateRandomString(chacha.nonce.Length)); - Array.Copy(hash1, 0, chacha.nonce, 0, chacha.nonce.Length); + byte[] hash1 = KeyGenerator.GetKeyHash(keyBytes, KeyGenerator.GenerateRandomString(chacha.Nonce.Length)); + Array.Copy(hash1, 0, chacha.Nonce, 0, chacha.Nonce.Length); encryptor = chacha; } /////////////////////////////////////////////////////////////// - if (history == null) + if (_history == null) { - history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; - if (history == null) + _history = AssetDatabase.LoadAssetAtPath(Path.Combine(_assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; + if (_history == null) { - history = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); + _history = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(_history, Path.Combine(_assetDir, "EncryptedHistory.asset")); } } - history.LoadData(); + _history.LoadData(); int progress = 0; int maxprogress = materials.Count; @@ -328,27 +361,27 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) { if (mat == null) continue; - int filter = this.filter; + int materialFilter = _filter; #if UNITY_2022 - MatOption option = matOptions.GetValueOrDefault(mat, null); + MatOption option = MaterialOptions.GetValueOrDefault(mat, null); #else MatOption option = null; - if (matOptions.ContainsKey(mat)) - option = matOptions[mat]; + if (MaterialOptions.ContainsKey(mat)) + option = MaterialOptions[mat]; #endif if (option != null) { - if (option.active == false) + if (option.Active == false) { Debug.LogFormat("{0} : Skip", mat.name); continue; } - filter = option.filter; + materialFilter = option.Filter; } EditorUtility.DisplayProgressBar("Encrypt...", "Encrypt Progress " + ++progress + " of " + maxprogress, (float)progress / (float)maxprogress); - injector = InjectorFactory.GetInjector(mat.shader); - if (injector == null) + _injector = InjectorFactory.GetInjector(mat.shader); + if (_injector == null) { Debug.LogError(mat.shader + " is a unsupported shader! supported type:lilToon, poiyomi"); continue; @@ -356,11 +389,11 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) if (!ConditionCheck(mat)) continue; - if (shaderManager.IsPoiyomi(mat.shader)) + if (_shaderManager.IsPoiyomi(mat.shader)) { - if (!shaderManager.IsLockPoiyomi(mat)) + if (!_shaderManager.IsLockPoiyomi(mat)) { - shaderManager.LockShader(mat); + _shaderManager.LockShader(mat); Debug.LogFormat("Lock: {0} - {1}", mat.name, AssetDatabase.GetAssetPath(mat.shader)); } } @@ -368,12 +401,12 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) Debug.LogFormat("{0} : Start encrypt...", mat.name); Texture2D mainTexture = (Texture2D)mat.mainTexture; - injector.Init(descriptor.gameObject, mainTexture, keyBytes, keySize, filter, resourceDir, encryptor); + _injector.Init(_descriptor.gameObject, mainTexture, keyBytes, _keySize, materialFilter, resourceDir, encryptor); int mipRefSize = Math.Max(mat.mainTexture.width, mat.mainTexture.height); if (!mips.ContainsKey(mipRefSize)) { - Texture2D mipRef = GenerateMipRefTexture(Path.Combine(avatarDir, "tex", "mip_" + mipRefSize + ".asset"), mipRefSize, bUseSmallMip); + Texture2D mipRef = GenerateMipRefTexture(Path.Combine(avatarDir, "tex", "mip_" + mipRefSize + ".asset"), mipRefSize, useSmallMip); if (mipRef != null) mips.Add(mipRefSize, mipRef); } @@ -382,15 +415,15 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) TextureSettings.SetCrunchCompression(mainTexture, false); TextureSettings.SetGenerateMipmap(mainTexture, true); - string encryptedShader_path = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); + string encryptedShaderPath = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); var processedTextureResult = GenerateEncryptedTexture(avatarDir, mat, encryptor, keyBytes); if (!processedTextureResult.HasValue) continue; ShellProtectorProcessedTexture processedTexture = processedTextureResult.Value; - Texture2D encryptedTex1 = processedTexture.encrypted.Texture1; - Texture2D encryptedTex2 = processedTexture.encrypted.Texture2; + Texture2D encryptedTex1 = processedTexture.Encrypted.Texture1; + Texture2D encryptedTex2 = processedTexture.Encrypted.Texture2; //////////////////////Inject shader/////////////////////// ShellProtectorAuxiliaryTextures otherTex = GetLimOutlineTextures(mat); @@ -399,14 +432,14 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) { try { - encryptedShader = injector.Inject( + encryptedShader = _injector.Inject( mat, Path.Combine(resourceDir, "Shader/ShellProtector.cginc"), - encryptedShader_path, + encryptedShaderPath, encryptedTex1, - otherTex.limTexture != null, - otherTex.limTexture2 != null, - otherTex.outlineTexture != null + otherTex.LimTexture != null, + otherTex.LimTexture2 != null, + otherTex.OutlineTexture != null ); Selection.activeObject = encryptedShader; @@ -416,7 +449,7 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) Debug.LogErrorFormat("{0}: Injection failed", mat.name); continue; } - history.Save(mat.shader); + _history.Save(mat.shader); } catch (UnityException e) { @@ -442,7 +475,7 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) ///////////////////////parameter//////////////////// var av3 = avatar.GetComponent(); - av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, keySize, syncSize); + av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, _keySize, _syncSize); AssetDatabase.CreateAsset(av3.expressionParameters, Path.Combine(avatarDir, av3.expressionParameters.name + ".asset")); //////////////////////////////////////////////////// if (!isModular) @@ -450,21 +483,21 @@ internal GameObject EncryptLegacy(bool bUseSmallMip, bool isModular = true) ReplaceMaterials(avatar); RemoveDuplicatedTextures(avatar); - descriptor.gameObject.SetActive(false); + _descriptor.gameObject.SetActive(false); var newDesriptor = avatar.transform.GetComponentInChildren(true).gameObject; var tester = newDesriptor.AddComponent(); - tester.lang = lang; - tester.langIdx = langIdx; - tester.protector = this; - tester.userKeyLength = keySize; + tester.Language = _language; + tester.LanguageIndex = _languageIndex; + tester.Protector = this; + tester.UserKeyLength = _keySize; Selection.activeObject = tester; #if MODULAR var maMergeAnims = avatar.GetComponentsInChildren(true); foreach (var maMergeAnim in maMergeAnims) { - AnimatorController newAnim = AnimatorManager.DuplicateAnimator(maMergeAnim.animator, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); + AnimatorController newAnim = AnimatorManager.DuplicateAnimator(maMergeAnim.animator, Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString())); maMergeAnim.animator = newAnim; } #endif @@ -493,10 +526,10 @@ public void ReplaceMaterials(GameObject avatar) if (mats[j] == null) continue; - if (encryptedMaterials.ContainsKey(mats[j])) + if (EncryptedMaterials.ContainsKey(mats[j])) { - mats[j] = encryptedMaterials[mats[j]]; - meshes.Add(renderers[i].gameObject); + mats[j] = EncryptedMaterials[mats[j]]; + Meshes.Add(renderers[i].gameObject); } } renderers[i].sharedMaterials = mats; @@ -515,10 +548,10 @@ public void ReplaceMaterials(GameObject avatar) if (mats[j] == null) continue; - if (encryptedMaterials.ContainsKey(mats[j])) + if (EncryptedMaterials.ContainsKey(mats[j])) { - mats[j] = encryptedMaterials[mats[j]]; - meshes.Add(skinnedRenderers[i].gameObject); + mats[j] = EncryptedMaterials[mats[j]]; + Meshes.Add(skinnedRenderers[i].gameObject); } } skinnedRenderers[i].sharedMaterials = mats; @@ -528,38 +561,38 @@ public void ReplaceMaterials(GameObject avatar) ShellProtectorAuxiliaryTextures GetLimOutlineTextures(Material mat) { ShellProtectorAuxiliaryTextures others = new ShellProtectorAuxiliaryTextures(); - if (shaderManager.IsPoiyomi(mat.shader)) + if (_shaderManager.IsPoiyomi(mat.shader)) { var tex_properties = mat.GetTexturePropertyNames(); foreach (var t in tex_properties) { if (t == "_RimTex") - others.limTexture = (Texture2D)mat.GetTexture(t); + others.LimTexture = (Texture2D)mat.GetTexture(t); else if (t == "_Rim2Tex") - others.limTexture2 = (Texture2D)mat.GetTexture(t); + others.LimTexture2 = (Texture2D)mat.GetTexture(t); else if (t == "_OutlineTexture") - others.outlineTexture = (Texture2D)mat.GetTexture(t); + others.OutlineTexture = (Texture2D)mat.GetTexture(t); } } - else if (shaderManager.IslilToon(mat.shader)) + else if (_shaderManager.IsLilToon(mat.shader)) { var tex_properties = mat.GetTexturePropertyNames(); foreach (var t in tex_properties) { if (t == "_RimColorTex") - others.limTexture = (Texture2D)mat.GetTexture(t); + others.LimTexture = (Texture2D)mat.GetTexture(t); else if (t == "_OutlineTex") - others.outlineTexture = (Texture2D)mat.GetTexture(t); + others.OutlineTexture = (Texture2D)mat.GetTexture(t); else if (t == "_RimShadeMask") - others.limShadeTexture = (Texture2D)mat.GetTexture(t); + others.LimShadeTexture = (Texture2D)mat.GetTexture(t); } } return others; } public void RemoveDuplicatedTextures(GameObject avatar) { - string avatarDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()); - foreach (var mat in encryptedMaterials.Values) + string avatarDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()); + foreach (var mat in EncryptedMaterials.Values) { ShellProtectorAuxiliaryTextures otherTex = GetLimOutlineTextures(mat); @@ -570,58 +603,58 @@ public void RemoveDuplicatedTextures(GameObject avatar) if (!(mat.GetTexture(name) is Texture2D)) continue; - if (processedTextures.ContainsKey((Texture2D)mat.GetTexture(name))) + if (ProcessedTextures.ContainsKey((Texture2D)mat.GetTexture(name))) { Texture2D mainTexture = (Texture2D)mat.GetTexture(name); - Texture2D encrypted0 = processedTextures[(Texture2D)mat.GetTexture(name)].encrypted.Texture1; + Texture2D encrypted0 = ProcessedTextures[(Texture2D)mat.GetTexture(name)].Encrypted.Texture1; - int idx = processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.IndexOf(processedTextures[(Texture2D)mat.GetTexture(name)].fallbackOptions.Max()); - Texture2D bigFallbackTexture = processedTextures[(Texture2D)mat.GetTexture(name)].fallbacks[idx]; + int idx = ProcessedTextures[(Texture2D)mat.GetTexture(name)].FallbackOptions.IndexOf(ProcessedTextures[(Texture2D)mat.GetTexture(name)].FallbackOptions.Max()); + Texture2D bigFallbackTexture = ProcessedTextures[(Texture2D)mat.GetTexture(name)].Fallbacks[idx]; - if (otherTex.limTexture != null) + if (otherTex.LimTexture != null) { string texName = ""; - if (shaderManager.IsPoiyomi(mat.shader)) + if (_shaderManager.IsPoiyomi(mat.shader)) texName = "_RimTex"; - else if (shaderManager.IslilToon(mat.shader)) + else if (_shaderManager.IsLilToon(mat.shader)) texName = "_RimColorTex"; - if (mainTexture == otherTex.limTexture) + if (mainTexture == otherTex.LimTexture) mat.SetTexture(texName, encrypted0); - else if (processedTextures.ContainsKey(otherTex.limTexture)) + else if (ProcessedTextures.ContainsKey(otherTex.LimTexture)) mat.SetTexture(texName, null); } - if (otherTex.limTexture2 != null) //only poiyomi + if (otherTex.LimTexture2 != null) //only poiyomi { string texName = ""; - if (shaderManager.IsPoiyomi(mat.shader)) + if (_shaderManager.IsPoiyomi(mat.shader)) texName = "_Rim2Tex"; - if (mainTexture == otherTex.limTexture2) + if (mainTexture == otherTex.LimTexture2) mat.SetTexture(texName, encrypted0); - else if (processedTextures.ContainsKey(otherTex.limTexture2)) + else if (ProcessedTextures.ContainsKey(otherTex.LimTexture2)) mat.SetTexture(texName, null); } - if (otherTex.outlineTexture != null) + if (otherTex.OutlineTexture != null) { string texName = ""; - if (shaderManager.IsPoiyomi(mat.shader)) + if (_shaderManager.IsPoiyomi(mat.shader)) texName = "_OutlineTexture"; - else if (shaderManager.IslilToon(mat.shader)) + else if (_shaderManager.IsLilToon(mat.shader)) texName = "_OutlineTex"; - if (mainTexture == otherTex.outlineTexture) + if (mainTexture == otherTex.OutlineTexture) mat.SetTexture(texName, bigFallbackTexture); - else if (processedTextures.ContainsKey(otherTex.outlineTexture)) - mat.SetTexture(texName, processedTextures[otherTex.outlineTexture].fallbacks[0]); + else if (ProcessedTextures.ContainsKey(otherTex.OutlineTexture)) + mat.SetTexture(texName, ProcessedTextures[otherTex.OutlineTexture].Fallbacks[0]); } - if (otherTex.limShadeTexture != null) //only liltoon + if (otherTex.LimShadeTexture != null) //only liltoon { string texName = "_RimShadeMask"; - if (mainTexture == otherTex.limShadeTexture) + if (mainTexture == otherTex.LimShadeTexture) mat.SetTexture(texName, bigFallbackTexture); - else if (processedTextures.ContainsKey(otherTex.limShadeTexture)) + else if (ProcessedTextures.ContainsKey(otherTex.LimShadeTexture)) mat.SetTexture(texName, null); } } @@ -649,10 +682,10 @@ public void RemoveDuplicatedTextures(GameObject avatar) if (!(mats[j].GetTexture(name) is Texture2D)) continue; Texture2D tex = (Texture2D)mats[j].GetTexture(name); - if (processedTextures.ContainsKey(tex)) + if (ProcessedTextures.ContainsKey(tex)) { - int idx = processedTextures[tex].fallbackOptions.IndexOf(processedTextures[tex].fallbackOptions.Max()); - Texture2D bigFallbackTexture = processedTextures[tex].fallbacks[idx]; + int idx = ProcessedTextures[tex].FallbackOptions.IndexOf(ProcessedTextures[tex].FallbackOptions.Max()); + Texture2D bigFallbackTexture = ProcessedTextures[tex].Fallbacks[idx]; if (tmp == null) { Material mat = AssetDatabase.LoadAssetAtPath(Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); @@ -696,11 +729,11 @@ public void RemoveDuplicatedTextures(GameObject avatar) if (!(mats[j].GetTexture(name) is Texture2D)) continue; - if (processedTextures.ContainsKey((Texture2D)mats[j].GetTexture(name))) + if (ProcessedTextures.ContainsKey((Texture2D)mats[j].GetTexture(name))) { Texture2D mainTex = (Texture2D)mats[j].GetTexture(name); - int idx = processedTextures[mainTex].fallbackOptions.IndexOf(processedTextures[mainTex].fallbackOptions.Max()); - Texture2D bigFallbackTexture = processedTextures[mainTex].fallbacks[idx]; + int idx = ProcessedTextures[mainTex].FallbackOptions.IndexOf(ProcessedTextures[mainTex].FallbackOptions.Max()); + Texture2D bigFallbackTexture = ProcessedTextures[mainTex].Fallbacks[idx]; if (tmp == null) { Material mat = AssetDatabase.LoadAssetAtPath(Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); @@ -730,17 +763,17 @@ public void SetAnimations(GameObject avatar, bool clone) var av3 = avatar.GetComponent(); AnimatorController fx; if (clone) - fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); + fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString())); else fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; av3.baseAnimationLayers[4].animatorController = fx; - string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); + string animationDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations"); - GameObject[] meshArray = new GameObject[meshes.Count]; - meshes.CopyTo(meshArray); - AnimatorManager.CreateKeyAniamtions(Path.Combine(GetPackageAssetDir(), "Animations"), animationDir, meshArray); - AnimatorManager.AddKeyLayer(fx, animationDir, keySize, syncSize, 3.0f); + GameObject[] meshArray = new GameObject[Meshes.Count]; + Meshes.CopyTo(meshArray); + AnimatorManager.CreateKeyAnimations(Path.Combine(GetPackageAssetDir(), "Animations"), animationDir, meshArray); + AnimatorManager.AddKeyLayer(fx, animationDir, _keySize, _syncSize, 3.0f); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); @@ -755,24 +788,24 @@ public void ChangeMaterialsInAnims(GameObject avatar, bool clone) { var av3 = avatar.GetComponent(); var fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; - string animationDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); + string animationDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations"); AnimatorManager animManager = ScriptableObject.CreateInstance(); - foreach (var pair in encryptedMaterials) + foreach (var pair in EncryptedMaterials) { Debug.LogFormat("{0}, {1}", pair.Key.name, pair.Value.name); animManager.ChangeAnimationMaterial(fx, pair.Key, pair.Value, clone, animationDir); } #if MODULAR - if (clone) + if (Clone) { var maMergeAnims = avatar.GetComponentsInChildren(true); foreach (var maMergeAnim in maMergeAnims) { if (maMergeAnim.animator == null) continue; - foreach (var pair in encryptedMaterials) + foreach (var pair in EncryptedMaterials) { animManager.ChangeAnimationMaterial(maMergeAnim.animator as AnimatorController, pair.Key, pair.Value, clone, animationDir); } @@ -783,13 +816,13 @@ public void ChangeMaterialsInAnims(GameObject avatar, bool clone) public VRCExpressionParameters GetParameter() { - var av3 = descriptor; + var av3 = _descriptor; if (av3 == null) return null; return av3.expressionParameters; } - public static AnimatorController Getfx(GameObject avatar) + public static AnimatorController GetFx(GameObject avatar) { var av3 = avatar.GetComponent(); if (av3 == null) @@ -797,16 +830,16 @@ public static AnimatorController Getfx(GameObject avatar) return av3.baseAnimationLayers[4].animatorController as AnimatorController; } - public void ObfuscateBlendShape(GameObject avatar, bool bClone) + public void ObfuscateBlendShape(GameObject avatar, bool clone) { - // bClone true = Manual encrypt + // Clone true = Manual encrypt var av3 = avatar.GetComponent(); - AnimatorController fx = Getfx(avatar); - string animDir = Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"); + AnimatorController fx = GetFx(avatar); + string animDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations"); Obfuscator obfuscator = ScriptableObject.CreateInstance(); - obfuscator.clone = bClone; - obfuscator.bPreserveMMD = bPreserveMMD; + obfuscator.Clone = clone; + obfuscator.PreserveMmd = _preserveMmd; var childRenderers = avatar.GetComponentsInChildren(); @@ -825,7 +858,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) } } #endif - foreach (var renderer in obfuscationRenderers) + foreach (var renderer in _obfuscationRenderers) { SkinnedMeshRenderer selectRenderer = null; foreach (var childRenderer in childRenderers) @@ -845,7 +878,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) Debug.LogErrorFormat("{0} haven't mesh", renderer.transform.name); continue; } - Mesh newMesh = obfuscator.ObfuscateBlendShapeMesh(mesh, Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString())); + Mesh newMesh = obfuscator.ObfuscateBlendShapeMesh(mesh, Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString())); selectRenderer.sharedMesh = newMesh; ////////Change renderer component shape keys//////// @@ -895,7 +928,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) } } - if(bClone) + if (Clone) { var maMergeAnims = avatar.GetComponentsInChildren(true); foreach (var maMergeAnim in maMergeAnims) @@ -910,16 +943,16 @@ public void ObfuscateBlendShape(GameObject avatar, bool bClone) } } - public int GetEncyryptedFoldersCount() + public int GetEncryptedFoldersCount() { - if (!Directory.Exists(assetDir)) + if (!Directory.Exists(_assetDir)) { - Debug.LogError($"The specified path does not exist: {assetDir}"); + Debug.LogError($"The specified path does not exist: {_assetDir}"); return 0; } else { - string[] directories = Directory.GetDirectories(assetDir); + string[] directories = Directory.GetDirectories(_assetDir); int deletedCount = 0; foreach (string dir in directories) @@ -935,15 +968,15 @@ public int GetEncyryptedFoldersCount() } public void CleanEncrypted() { - AssetDatabase.DeleteAsset(Path.Combine(assetDir, "EncryptedHistory.asset")); + AssetDatabase.DeleteAsset(Path.Combine(_assetDir, "EncryptedHistory.asset")); - if (!Directory.Exists(assetDir)) + if (!Directory.Exists(_assetDir)) { - Debug.LogError($"The specified path does not exist: {assetDir}"); + Debug.LogError($"The specified path does not exist: {_assetDir}"); } else { - string[] directories = Directory.GetDirectories(assetDir); + string[] directories = Directory.GetDirectories(_assetDir); int deletedCount = 0; foreach (string dir in directories) @@ -971,42 +1004,42 @@ public void CleanEncrypted() public int GetDefaultFilter() { - return filter; + return _filter; } public int GetDefaultFallback() { - return fallback; + return _fallback; } public int GetKeySize() { - return keySize; + return _keySize; } public void ResetMaterialOptions() { - matOptionSaved.Clear(); - matOptions.Clear(); + _matOptionSaved.Clear(); + MaterialOptions.Clear(); } public Shader IsEncryptedBefore(Shader shader) { - if (history == null) + if (_history == null) { - history = AssetDatabase.LoadAssetAtPath(Path.Combine(assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; - if (history == null) + _history = AssetDatabase.LoadAssetAtPath(Path.Combine(_assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; + if (_history == null) { - history = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(history, Path.Combine(assetDir, "EncryptedHistory.asset")); + _history = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(_history, Path.Combine(_assetDir, "EncryptedHistory.asset")); } } - history.LoadData(); - return history.IsEncryptedBefore(shader); + _history.LoadData(); + return _history.IsEncryptedBefore(shader); } - public static int GetRequiredSwitchCount(int key_length, int syncSize) + public static int GetRequiredSwitchCount(int keyLength, int syncSize) { - key_length /= syncSize; - return Mathf.CeilToInt(Mathf.Log(key_length, 2)); + keyLength /= syncSize; + return Mathf.CeilToInt(Mathf.Log(keyLength, 2)); } bool ConditionCheck(Material mat) { @@ -1025,20 +1058,20 @@ bool ConditionCheck(Material mat) Debug.LogErrorFormat("{0} : The texture size must be a multiple of 2!", mat.mainTexture.name); return false; } - if (injector.WasInjected(mat.shader)) + if (_injector.WasInjected(mat.shader)) { Debug.LogWarning(mat.name + ": The shader is already encrypted."); return false; } - var av3 = descriptor.gameObject.GetComponent(); + var av3 = _descriptor.gameObject.GetComponent(); if (av3 == null) { - Debug.LogError(descriptor.gameObject.name + ": can't find VRCAvatarDescriptor!"); + Debug.LogError(_descriptor.gameObject.name + ": can't find VRCAvatarDescriptor!"); return false; } if (av3.expressionParameters == null) { - Debug.LogError(descriptor.gameObject.name + ": can't find expressionParmeters!"); + Debug.LogError(_descriptor.gameObject.name + ": can't find expressionParmeters!"); return false; } return true; @@ -1046,7 +1079,7 @@ bool ConditionCheck(Material mat) public List GetMaterials() { List materials = new List(); - foreach (GameObject g in gameobjectList) + foreach (GameObject g in _gameObjectList) { if (g == null) continue; @@ -1076,7 +1109,7 @@ public List GetMaterials() } } - return materials.Concat(materialList).Distinct().ToList(); + return materials.Concat(_materialList).Distinct().ToList(); } bool CheckIsSupportedFormat(Material mat) { @@ -1092,38 +1125,38 @@ bool CheckIsSupportedFormat(Material mat) } void CreateFolders() { - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()))) + if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()))) { - AssetDatabase.CreateFolder(assetDir, descriptor.gameObject.GetInstanceID().ToString()); + AssetDatabase.CreateFolder(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()); } else { - if (deleteFolders) + if (_deleteFolders) { - AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations")); - AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "mat")); - AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "shader")); - AssetDatabase.DeleteAsset(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "tex")); + AssetDatabase.DeleteAsset(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations")); + AssetDatabase.DeleteAsset(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "mat")); + AssetDatabase.DeleteAsset(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "shader")); + AssetDatabase.DeleteAsset(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "tex")); } } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "tex"))) - AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "tex"); - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "mat"))) - AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "mat"); - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "shader"))) - AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "shader"); - if (!AssetDatabase.IsValidFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString(), "animations"))) - AssetDatabase.CreateFolder(Path.Combine(assetDir, descriptor.gameObject.GetInstanceID().ToString()), "animations"); + if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "tex"))) + AssetDatabase.CreateFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()), "tex"); + if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "mat"))) + AssetDatabase.CreateFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()), "mat"); + if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "shader"))) + AssetDatabase.CreateFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()), "shader"); + if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations"))) + AssetDatabase.CreateFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()), "animations"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } - Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) + Texture2D GenerateMipRefTexture(string outputDir, int size, bool useSmallMip) { - var mip = TextureEncryptManager.GenerateRefMipmap(size, size, bUseSmallMip); + var mip = TextureEncryptManager.GenerateRefMipmap(size, size, useSmallMip); if (mip == null) Debug.LogErrorFormat("{0} : Can't generate mip tex{1}.", outputDir, size); else @@ -1141,36 +1174,36 @@ Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) string texPath1 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt.asset"); string texPath2 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt2.asset"); - bool processed = processedTextures.ContainsKey(mainTexture); + bool processed = ProcessedTextures.ContainsKey(mainTexture); ShellProtectorProcessedTexture processedTexture; if (processed) - processedTexture = processedTextures[mainTexture]; + processedTexture = ProcessedTextures[mainTexture]; else { processedTexture = new ShellProtectorProcessedTexture { - encrypted = new EncryptResult(), - fallbacks = new List(), - fallbackOptions = new List(), - nonce = new byte[12] + Encrypted = new EncryptResult(), + Fallbacks = new List(), + FallbackOptions = new List(), + Nonce = new byte[12] }; } //Set chacha nonce - if (algorithm == (int)Algorithm.chacha) + if (_algorithm == (int)Algorithm.Chacha) { Chacha20 chacha = encryptor as Chacha20; if (!processed) { byte[] hashMat = KeyGenerator.GetHash(mat.GetInstanceID()); - for (int i = 0; i < chacha.nonce.Length; ++i) - chacha.nonce[i] ^= hashMat[i]; - Array.Copy(chacha.nonce, 0, processedTexture.nonce, 0, processedTexture.nonce.Length); + for (int i = 0; i < chacha.Nonce.Length; ++i) + chacha.Nonce[i] ^= hashMat[i]; + Array.Copy(chacha.Nonce, 0, processedTexture.Nonce, 0, processedTexture.Nonce.Length); } else { - byte[] nonce = processedTextures[mainTexture].nonce; - Array.Copy(nonce, 0, chacha.nonce, 0, chacha.nonce.Length); + byte[] nonce = ProcessedTextures[mainTexture].Nonce; + Array.Copy(nonce, 0, chacha.Nonce, 0, chacha.Nonce.Length); } } @@ -1190,20 +1223,20 @@ Texture2D GenerateMipRefTexture(string outputDir, int size, bool bUseSmallMip) if (encryptResult.Texture2 != null) AssetDatabase.CreateAsset(encryptResult.Texture2, texPath2); - processedTexture.encrypted = encryptResult; + processedTexture.Encrypted = encryptResult; - processedTextures.Add(mainTexture, processedTexture); + ProcessedTextures.Add(mainTexture, processedTexture); } return processedTexture; } Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D mainTexture, ref ShellProtectorProcessedTexture processedTexture) { - int fallbackOption = this.fallback; + int fallbackOption = _fallback; if (option != null) - fallbackOption = option.fallback; + fallbackOption = option.Fallback; - int idx = processedTexture.fallbackOptions.FindIndex(option => option == fallbackOption); + int idx = processedTexture.FallbackOptions.FindIndex(option => option == fallbackOption); Texture2D fallback = null; if (idx == -1) { @@ -1240,8 +1273,8 @@ Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D fallback = TextureEncryptManager.GenerateFallback(mainTexture, fallbackSize); if (fallback != null) { - processedTexture.fallbacks.Add(fallback); - processedTexture.fallbackOptions.Add(fallbackOption); + processedTexture.Fallbacks.Add(fallback); + processedTexture.FallbackOptions.Add(fallbackOption); AssetDatabase.CreateAsset(fallback, outputDir); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); @@ -1252,20 +1285,20 @@ Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D switch (fallbackSize) { case 0: - processedTexture.fallbacks.Add(fallbackWhite); - processedTexture.fallbackOptions.Add(fallbackOption); - fallback = fallbackWhite; + processedTexture.Fallbacks.Add(_fallbackWhite); + processedTexture.FallbackOptions.Add(fallbackOption); + fallback = _fallbackWhite; break; case 1: - processedTexture.fallbacks.Add(fallbackBlack); - processedTexture.fallbackOptions.Add(fallbackOption); - fallback = fallbackBlack; + processedTexture.Fallbacks.Add(_fallbackBlack); + processedTexture.FallbackOptions.Add(fallbackOption); + fallback = _fallbackBlack; break; } } } else - fallback = processedTexture.fallbacks[idx]; + fallback = processedTexture.Fallbacks[idx]; return fallback; } @@ -1277,8 +1310,8 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp var originalTex = (Texture2D)newMat.mainTexture; newMat.mainTexture = fallback; - Texture2D encryptedTex0 = processedTexture.encrypted.Texture1; - Texture2D encryptedTex1 = processedTexture.encrypted.Texture2; + Texture2D encryptedTex0 = processedTexture.Encrypted.Texture1; + Texture2D encryptedTex1 = processedTexture.Encrypted.Texture2; newMat.SetTexture(ShellProtectorShaderProperties.MipTexture, mip); @@ -1288,7 +1321,7 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp newMat.SetTexture(ShellProtectorShaderProperties.EncryptTexture1, encryptedTex1); newMat.renderQueue = mat.renderQueue; - if (turnOnAllSafetyFallback) + if (_turnOnAllSafetyFallback) newMat.SetOverrideTag("VRCFallback", "Unlit"); var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(originalTex); @@ -1297,16 +1330,16 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp for (int i = 0; i < keyBytes.Length; ++i) newMat.SetFloat(ShellProtectorShaderProperties.KeyPrefix + i, keyBytes[i]); - if (algorithm == (int)Algorithm.chacha) + if (_algorithm == (int)Algorithm.Chacha) { Chacha20 chacha = encryptor as Chacha20; newMat.SetInteger(ShellProtectorShaderProperties.Nonce0, (int)chacha.GetNonceUint3()[0]); newMat.SetInteger(ShellProtectorShaderProperties.Nonce1, (int)chacha.GetNonceUint3()[1]); newMat.SetInteger(ShellProtectorShaderProperties.Nonce2, (int)chacha.GetNonceUint3()[2]); } - else if (algorithm == (int)Algorithm.xxtea) + else if (_algorithm == (int)Algorithm.Xxtea) { - newMat.SetInteger(ShellProtectorShaderProperties.Rounds, (int)rounds); + newMat.SetInteger(ShellProtectorShaderProperties.Rounds, (int)_rounds); } var key = new byte[16]; @@ -1319,15 +1352,15 @@ Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryp newMat.SetInteger(ShellProtectorShaderProperties.HashMagic, (int)hashMagic); newMat.SetInteger(ShellProtectorShaderProperties.PasswordHash, (int)hash); - injector.SetKeywords(newMat, otherTex.limTexture != null); + _injector.SetKeywords(newMat, otherTex.LimTexture != null); AssetDatabase.CreateAsset(newMat, outputDir); Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(newMat)); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); - if (!encryptedMaterials.ContainsKey(mat)) - encryptedMaterials.Add(mat, newMat); + if (!EncryptedMaterials.ContainsKey(mat)) + EncryptedMaterials.Add(mat, newMat); return newMat; } diff --git a/Runtime/Scripts/ShellProtectorTester.cs b/Runtime/Scripts/ShellProtectorTester.cs index 2318946..549d400 100644 --- a/Runtime/Scripts/ShellProtectorTester.cs +++ b/Runtime/Scripts/ShellProtectorTester.cs @@ -1,29 +1,31 @@ -#if UNITY_EDITOR -using System.Collections; -using System.Collections.Generic; -using System.Text; +#if UNITY_EDITOR using UnityEngine; +using UnityEngine.Serialization; using VRC.SDKBase; namespace Shell.Protector { public class ShellProtectorTester : MonoBehaviour, IEditorOnly { - public string lang = "eng"; - public int langIdx = 0; - public int userKeyLength = 4; + [FormerlySerializedAs("lang")] + public string Language = "eng"; + [FormerlySerializedAs("langIdx")] + public int LanguageIndex; + [FormerlySerializedAs("userKeyLength")] + public int UserKeyLength = 4; - public ShellProtector protector; + [FormerlySerializedAs("protector")] + public ShellProtector Protector; public void CheckEncryption() { - if(protector == null) + if (Protector == null) { Debug.LogWarning("First, you need to set protector"); return; } - byte[] pwd_bytes = protector.GetKeyBytes(); + byte[] passwordBytes = Protector.GetKeyBytes(); var renderers = transform.root.GetComponentsInChildren(true); if (renderers != null) @@ -43,15 +45,15 @@ public void CheckEncryption() if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { for (int i = 0; i < 16; ++i) - mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, pwd_bytes[i]); + mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, passwordBytes[i]); } } } } - var skinned_renderers = transform.root.GetComponentsInChildren(true); - if (skinned_renderers != null) + var skinnedRenderers = transform.root.GetComponentsInChildren(true); + if (skinnedRenderers != null) { - foreach (var r in skinned_renderers) + foreach (var r in skinnedRenderers) { var mats = r.sharedMaterials; if (mats == null) @@ -66,7 +68,7 @@ public void CheckEncryption() if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { for (int i = 0; i < 16; ++i) - mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, pwd_bytes[i]); + mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, passwordBytes[i]); } } } @@ -89,13 +91,13 @@ public void ResetEncryption() continue; if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { - for (int i = 16 - userKeyLength; i < 16; ++i) + for (int i = 16 - UserKeyLength; i < 16; ++i) mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, 0); } } } - var skinned_renderers = transform.root.GetComponentsInChildren(true); - foreach (var r in skinned_renderers) + var skinnedRenderers = transform.root.GetComponentsInChildren(true); + foreach (var r in skinnedRenderers) { var mats = r.sharedMaterials; if (mats == null) @@ -109,7 +111,7 @@ public void ResetEncryption() continue; if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { - for (int i = 16 - userKeyLength; i < 16; ++i) + for (int i = 16 - UserKeyLength; i < 16; ++i) mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, 0); } } diff --git a/Runtime/Scripts/Test.cs b/Runtime/Scripts/Test.cs index 9416717..edb6fa1 100644 --- a/Runtime/Scripts/Test.cs +++ b/Runtime/Scripts/Test.cs @@ -7,21 +7,21 @@ public class Test { public static void XXTEATest(string fixedKey, string userKey, int userKeySize) { - byte[] data_byte = new byte[12] { 255, 250, 245, 240, 235, 230, 225, 220, 215, 210, 205, 200 }; - byte[] key_byte = KeyGenerator.MakeKeyBytes(fixedKey, userKey, userKeySize); + byte[] dataBytes = new byte[12] { 255, 250, 245, 240, 235, 230, 225, 220, 215, 210, 205, 200 }; + byte[] keyBytes = KeyGenerator.MakeKeyBytes(fixedKey, userKey, userKeySize); uint[] data = new uint[3]; - data[0] = (uint)(data_byte[0] | (data_byte[1] << 8) | (data_byte[2] << 16) | (data_byte[3] << 24)); - data[1] = (uint)(data_byte[4] | (data_byte[5] << 8) | (data_byte[6] << 16) | (data_byte[7] << 24)); - data[2] = (uint)(data_byte[8] | (data_byte[9] << 8) | (data_byte[10] << 16) | (data_byte[11] << 24)); + data[0] = (uint)(dataBytes[0] | (dataBytes[1] << 8) | (dataBytes[2] << 16) | (dataBytes[3] << 24)); + data[1] = (uint)(dataBytes[4] | (dataBytes[5] << 8) | (dataBytes[6] << 16) | (dataBytes[7] << 24)); + data[2] = (uint)(dataBytes[8] | (dataBytes[9] << 8) | (dataBytes[10] << 16) | (dataBytes[11] << 24)); uint[] key = new uint[4]; - key[0] = (uint)(key_byte[0] | (key_byte[1] << 8) | (key_byte[2] << 16) | (key_byte[3] << 24)); - key[1] = (uint)(key_byte[4] | (key_byte[5] << 8) | (key_byte[6] << 16) | (key_byte[7] << 24)); - key[2] = (uint)(key_byte[8] | (key_byte[9] << 8) | (key_byte[10] << 16) | (key_byte[11] << 24)); - key[3] = (uint)(key_byte[12] | (key_byte[13] << 8) | (key_byte[14] << 16) | (key_byte[15] << 24)); + key[0] = (uint)(keyBytes[0] | (keyBytes[1] << 8) | (keyBytes[2] << 16) | (keyBytes[3] << 24)); + key[1] = (uint)(keyBytes[4] | (keyBytes[5] << 8) | (keyBytes[6] << 16) | (keyBytes[7] << 24)); + key[2] = (uint)(keyBytes[8] | (keyBytes[9] << 8) | (keyBytes[10] << 16) | (keyBytes[11] << 24)); + key[3] = (uint)(keyBytes[12] | (keyBytes[13] << 8) | (keyBytes[14] << 16) | (keyBytes[15] << 24)); - Debug.Log("Key bytes: " + string.Join(", ", key_byte)); + Debug.Log("Key bytes: " + string.Join(", ", keyBytes)); Debug.Log(string.Format("key1:{0}, key2:{1}, key3:{2}", key[0], key[1], key[2])); Debug.Log("Data: " + string.Join(", ", data)); @@ -34,20 +34,20 @@ public static void XXTEATest(string fixedKey, string userKey, int userKeySize) } public static void ChachaTest(string fixedKey, string userKey, int userKeySize) { - byte[] data_byte = new byte[8] { 255, 255, 245, 240, 235, 230, 225, 220 }; - byte[] key_byte = KeyGenerator.MakeKeyBytes(fixedKey, userKey, userKeySize); + byte[] dataBytes = new byte[8] { 255, 255, 245, 240, 235, 230, 225, 220 }; + byte[] keyBytes = KeyGenerator.MakeKeyBytes(fixedKey, userKey, userKeySize); uint[] data = new uint[2]; - data[0] = (uint)(data_byte[0] | (data_byte[1] << 8) | (data_byte[2] << 16) | (data_byte[3] << 24)); - data[1] = (uint)(data_byte[4] | (data_byte[5] << 8) | (data_byte[6] << 16) | (data_byte[7] << 24)); + data[0] = (uint)(dataBytes[0] | (dataBytes[1] << 8) | (dataBytes[2] << 16) | (dataBytes[3] << 24)); + data[1] = (uint)(dataBytes[4] | (dataBytes[5] << 8) | (dataBytes[6] << 16) | (dataBytes[7] << 24)); uint[] key = new uint[4]; - key[0] = (uint)(key_byte[0] | (key_byte[1] << 8) | (key_byte[2] << 16) | (key_byte[3] << 24)); - key[1] = (uint)(key_byte[4] | (key_byte[5] << 8) | (key_byte[6] << 16) | (key_byte[7] << 24)); - key[2] = (uint)(key_byte[8] | (key_byte[9] << 8) | (key_byte[10] << 16) | (key_byte[11] << 24)); - key[3] = (uint)(key_byte[12] | (key_byte[13] << 8) | (key_byte[14] << 16) | (key_byte[15] << 24)); + key[0] = (uint)(keyBytes[0] | (keyBytes[1] << 8) | (keyBytes[2] << 16) | (keyBytes[3] << 24)); + key[1] = (uint)(keyBytes[4] | (keyBytes[5] << 8) | (keyBytes[6] << 16) | (keyBytes[7] << 24)); + key[2] = (uint)(keyBytes[8] | (keyBytes[9] << 8) | (keyBytes[10] << 16) | (keyBytes[11] << 24)); + key[3] = (uint)(keyBytes[12] | (keyBytes[13] << 8) | (keyBytes[14] << 16) | (keyBytes[15] << 24)); - Debug.Log("Key bytes: " + string.Join(", ", key_byte)); + Debug.Log("Key bytes: " + string.Join(", ", keyBytes)); Debug.Log(string.Format("key1:{0}, key2:{1}, key3:{2}", key[0], key[1], key[2])); Debug.Log("Data: " + string.Join(", ", data)); diff --git a/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs b/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs index 4105f38..95fb709 100644 --- a/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs +++ b/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs @@ -46,7 +46,7 @@ public void TearDown() [TestCase(TextureFormat.DXT5, true, true)] public void XxteaEncryptedTexture_DecryptsToOriginalGpuSample(TextureFormat format, bool alpha, bool bilinear) { - XXTEA xxtea = new XXTEA { m_rounds = 20 }; + XXTEA xxtea = new XXTEA { Rounds = 20 }; AssertDecryptsToOriginalGpuSample(format, alpha, bilinear, xxtea, material => { @@ -65,8 +65,8 @@ public void XxteaEncryptedTexture_DecryptsToOriginalGpuSample(TextureFormat form public void ChachaEncryptedTexture_DecryptsToOriginalGpuSample(TextureFormat format, bool alpha, bool bilinear) { Chacha20 chacha = new Chacha20(); - for (int i = 0; i < chacha.nonce.Length; i++) - chacha.nonce[i] = (byte)i; + for (int i = 0; i < chacha.Nonce.Length; i++) + chacha.Nonce[i] = (byte)i; AssertDecryptsToOriginalGpuSample(format, alpha, bilinear, chacha, material => { diff --git a/Tests/Editor/Integration/ShellProtectorPipelineTests.cs b/Tests/Editor/Integration/ShellProtectorPipelineTests.cs index e02b77b..e1cd30b 100644 --- a/Tests/Editor/Integration/ShellProtectorPipelineTests.cs +++ b/Tests/Editor/Integration/ShellProtectorPipelineTests.cs @@ -124,14 +124,14 @@ private Fixture CreateFixture(string name) }; ShellProtector protector = avatar.AddComponent(); - protector.descriptor = descriptor; - protector.assetDir = TestAssetScope.GeneratedRoot; - SetSerializedField(protector, "gameobjectList", new List { avatar }); - SetSerializedField(protector, "algorithm", 1); - SetSerializedField(protector, "filter", 0); - SetSerializedField(protector, "fallback", 5); - SetSerializedField(protector, "keySize", 12); - SetSerializedField(protector, "syncSize", 1); + protector.Descriptor = descriptor; + protector.AssetDir = TestAssetScope.GeneratedRoot; + SetSerializedField(protector, "_gameObjectList", new List { avatar }); + SetSerializedField(protector, "_algorithm", 1); + SetSerializedField(protector, "_filter", 0); + SetSerializedField(protector, "_fallback", 5); + SetSerializedField(protector, "_keySize", 12); + SetSerializedField(protector, "_syncSize", 1); protector.Init(); return new Fixture @@ -202,7 +202,7 @@ private static void AssertEncryptedRenderer(GameObject avatar, Material original private static void AssertFxController(GameObject avatar) { - AnimatorController fx = ShellProtector.Getfx(avatar); + AnimatorController fx = ShellProtector.GetFx(avatar); Assert.That(fx, Is.Not.Null); Assert.That(fx.layers.Select(l => l.name), Does.Contain("ShellProtector")); @@ -234,7 +234,7 @@ private static void AssertBlendShapeWasObfuscated(GameObject avatar) private static void AssertAnimationMaterialWasRewritten(GameObject avatar, Material originalMaterial) { - AnimatorController fx = ShellProtector.Getfx(avatar); + AnimatorController fx = ShellProtector.GetFx(avatar); Material encryptedMaterial = avatar.transform.Find("Body").GetComponent().sharedMaterial; List clips = new List(); diff --git a/Tests/Editor/Unit/CryptoTests.cs b/Tests/Editor/Unit/CryptoTests.cs index 0f84857..86f8d8d 100644 --- a/Tests/Editor/Unit/CryptoTests.cs +++ b/Tests/Editor/Unit/CryptoTests.cs @@ -48,7 +48,7 @@ public void Xxtea_EncryptsStableVector_AndRoundTrips() [Test] public void Xxtea_WithExplicitShellProtectorRounds_RoundTrips() { - XXTEA xxtea = new XXTEA { m_rounds = 20 }; + XXTEA xxtea = new XXTEA { Rounds = 20 }; uint[] data = { 0x10203040u, 0x50607080u }; uint[] key = { 0x00112233u, 0x44556677u, 0x8899aabbu, 0xccddeeffu }; @@ -62,8 +62,8 @@ public void Xxtea_WithExplicitShellProtectorRounds_RoundTrips() public void Chacha20_EncryptsStableVector_AndRoundTrips() { Chacha20 chacha = new Chacha20(); - for (int i = 0; i < chacha.nonce.Length; i++) - chacha.nonce[i] = (byte)i; + for (int i = 0; i < chacha.Nonce.Length; i++) + chacha.Nonce[i] = (byte)i; uint[] data = { 0x00010203u, 0x04050607u, 0x08090a0bu, 0x0c0d0e0fu }; uint[] key = { 0x00112233u, 0x44556677u, 0x8899aabbu, 0xccddeeffu }; diff --git a/Tests/Editor/Unit/TextureEncryptManagerTests.cs b/Tests/Editor/Unit/TextureEncryptManagerTests.cs index d233f3d..0a84113 100644 --- a/Tests/Editor/Unit/TextureEncryptManagerTests.cs +++ b/Tests/Editor/Unit/TextureEncryptManagerTests.cs @@ -61,7 +61,7 @@ public void GeneratesFallbackTexture() public void EncryptTextureCreatesExpectedResultTextures(TextureFormat sourceFormat, bool alpha, TextureFormat encryptedFormat) { Texture2D texture = TestAssetScope.CreatePatternTexture(16, 16, sourceFormat, alpha); - XXTEA xxtea = new XXTEA { m_rounds = 20 }; + XXTEA xxtea = new XXTEA { Rounds = 20 }; byte[] key = KeyGenerator.MakeKeyBytes("password", "pass", 12); EncryptResult result = TextureEncryptManager.EncryptTexture(texture, key, xxtea); From e0f264d959ff926e10f74928e650e7dc99c642a9 Mon Sep 17 00:00:00 2001 From: Lee Sangyeop Date: Sat, 4 Jul 2026 00:39:42 +0900 Subject: [PATCH 39/59] refactor: simplify protector output pipeline --- Editor/ShellProtectorEditor.cs | 18 +- Editor/liltoonCustom/CustomInspector.cs | 12 +- Runtime/Scripts/Algorithm/Chacha20.cs | 2 +- Runtime/Scripts/Algorithm/XXTEA.cs | 2 +- Runtime/Scripts/AnimatorManager.cs | 30 +- ...hellProtectorConstants.cs => Constants.cs} | 2 +- ...torConstants.cs.meta => Constants.cs.meta} | 0 Runtime/Scripts/Formats/DXTFormat.cs | 4 +- Runtime/Scripts/Formats/RGBFormat.cs | 8 +- Runtime/Scripts/Injector/IShaderAdapter.cs | 2 +- Runtime/Scripts/Injector/Injector.cs | 6 +- Runtime/Scripts/Injector/PoiyomiInjector.cs | 24 +- .../Scripts/{lang.meta => Localization.meta} | 0 .../{lang => Localization}/LanguageManager.cs | 0 .../LanguageManager.cs.meta | 0 ...llProtectorNDMFPlugin.cs => NdmfPlugin.cs} | 6 +- ...rNDMFPlugin.cs.meta => NdmfPlugin.cs.meta} | 0 Runtime/Scripts/Obfuscator.cs | 17 +- Runtime/Scripts/Pipeline/AssetWriter.cs | 105 ++++++ ...ssetWriter.cs.meta => AssetWriter.cs.meta} | 0 Runtime/Scripts/Pipeline/AvatarProcessor.cs | 41 +++ .../Scripts/Pipeline/AvatarProcessor.cs.meta | 11 + ...otectorBuildContext.cs => BuildContext.cs} | 16 +- ...ldContext.cs.meta => BuildContext.cs.meta} | 0 ...otectorBuildRequest.cs => BuildRequest.cs} | 4 +- ...ldRequest.cs.meta => BuildRequest.cs.meta} | 0 ...ProtectorBuildResult.cs => BuildResult.cs} | 4 +- ...uildResult.cs.meta => BuildResult.cs.meta} | 0 ...lProtectorSettings.cs => BuildSettings.cs} | 2 +- ...Settings.cs.meta => BuildSettings.cs.meta} | 0 Runtime/Scripts/Pipeline/IAssetWriter.cs | 38 -- Runtime/Scripts/Pipeline/MaterialEncryptor.cs | 73 ++++ .../Pipeline/MaterialEncryptor.cs.meta | 11 + Runtime/Scripts/Pipeline/OutputPaths.cs | 152 ++++++++ Runtime/Scripts/Pipeline/OutputPaths.cs.meta | 11 + ...{ShellProtectorPipeline.cs => Pipeline.cs} | 8 +- ...ectorPipeline.cs.meta => Pipeline.cs.meta} | 0 Runtime/Scripts/ShellProtector.cs | 337 +++++++----------- Runtime/Scripts/ShellProtectorTester.cs | 8 +- .../{ShellProtector.cginc => Protector.cginc} | 0 ...tector.cginc.meta => Protector.cginc.meta} | 0 .../Shaders/custom_insert.hlsl | 4 +- Tests/Editor/Gpu/GpuTextureDecryptionTests.cs | 2 +- ...cryptTest.shader => GpuDecryptTest.shader} | 6 +- ...shader.meta => GpuDecryptTest.shader.meta} | 0 ...ectorPipelineTests.cs => PipelineTests.cs} | 53 ++- ...ineTests.cs.meta => PipelineTests.cs.meta} | 0 Tests/Editor/Shared/TestAssetScope.cs | 11 + 48 files changed, 688 insertions(+), 342 deletions(-) rename Runtime/Scripts/{ShellProtectorConstants.cs => Constants.cs} (96%) rename Runtime/Scripts/{ShellProtectorConstants.cs.meta => Constants.cs.meta} (100%) rename Runtime/Scripts/{lang.meta => Localization.meta} (100%) rename Runtime/Scripts/{lang => Localization}/LanguageManager.cs (100%) rename Runtime/Scripts/{lang => Localization}/LanguageManager.cs.meta (100%) rename Runtime/Scripts/NDMF/{ShellProtectorNDMFPlugin.cs => NdmfPlugin.cs} (88%) rename Runtime/Scripts/NDMF/{ShellProtectorNDMFPlugin.cs.meta => NdmfPlugin.cs.meta} (100%) create mode 100644 Runtime/Scripts/Pipeline/AssetWriter.cs rename Runtime/Scripts/Pipeline/{IAssetWriter.cs.meta => AssetWriter.cs.meta} (100%) create mode 100644 Runtime/Scripts/Pipeline/AvatarProcessor.cs create mode 100644 Runtime/Scripts/Pipeline/AvatarProcessor.cs.meta rename Runtime/Scripts/Pipeline/{ShellProtectorBuildContext.cs => BuildContext.cs} (60%) rename Runtime/Scripts/Pipeline/{ShellProtectorBuildContext.cs.meta => BuildContext.cs.meta} (100%) rename Runtime/Scripts/Pipeline/{ShellProtectorBuildRequest.cs => BuildRequest.cs} (71%) rename Runtime/Scripts/Pipeline/{ShellProtectorBuildRequest.cs.meta => BuildRequest.cs.meta} (100%) rename Runtime/Scripts/Pipeline/{ShellProtectorBuildResult.cs => BuildResult.cs} (76%) rename Runtime/Scripts/Pipeline/{ShellProtectorBuildResult.cs.meta => BuildResult.cs.meta} (100%) rename Runtime/Scripts/Pipeline/{ShellProtectorSettings.cs => BuildSettings.cs} (94%) rename Runtime/Scripts/Pipeline/{ShellProtectorSettings.cs.meta => BuildSettings.cs.meta} (100%) delete mode 100644 Runtime/Scripts/Pipeline/IAssetWriter.cs create mode 100644 Runtime/Scripts/Pipeline/MaterialEncryptor.cs create mode 100644 Runtime/Scripts/Pipeline/MaterialEncryptor.cs.meta create mode 100644 Runtime/Scripts/Pipeline/OutputPaths.cs create mode 100644 Runtime/Scripts/Pipeline/OutputPaths.cs.meta rename Runtime/Scripts/Pipeline/{ShellProtectorPipeline.cs => Pipeline.cs} (56%) rename Runtime/Scripts/Pipeline/{ShellProtectorPipeline.cs.meta => Pipeline.cs.meta} (100%) rename Runtime/Shader/{ShellProtector.cginc => Protector.cginc} (100%) rename Runtime/Shader/{ShellProtector.cginc.meta => Protector.cginc.meta} (100%) rename Tests/Editor/Gpu/Shaders/Hidden/{ShellProtectorGpuDecryptTest.shader => GpuDecryptTest.shader} (95%) rename Tests/Editor/Gpu/Shaders/Hidden/{ShellProtectorGpuDecryptTest.shader.meta => GpuDecryptTest.shader.meta} (100%) rename Tests/Editor/Integration/{ShellProtectorPipelineTests.cs => PipelineTests.cs} (81%) rename Tests/Editor/Integration/{ShellProtectorPipelineTests.cs.meta => PipelineTests.cs.meta} (100%) diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index 4154ae3..bccebe9 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -65,11 +65,6 @@ void OnEnable() { root = target as ShellProtector; - MonoScript monoScript = MonoScript.FromMonoBehaviour(root); - string scriptPath = AssetDatabase.GetAssetPath(monoScript); - - root.AssetDir = Path.GetDirectoryName(Path.GetDirectoryName(scriptPath)); - gameobjectList = new ReorderableList(serializedObject, serializedObject.FindProperty("_gameObjectList"), true, true, true, true); gameobjectList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Object list")); gameobjectList.drawElementCallback = (rect, index, is_active, is_focused) => @@ -428,15 +423,14 @@ public override void OnInspectorGUI() last = result.Texture1; - if (!AssetDatabase.IsValidFolder(root.AssetDir + '/' + root.Descriptor.gameObject.name)) - AssetDatabase.CreateFolder(root.AssetDir, root.Descriptor.gameObject.name); - if (!AssetDatabase.IsValidFolder(root.AssetDir + '/' + root.Descriptor.gameObject.name + "/mat")) - AssetDatabase.CreateFolder(root.AssetDir + '/' + root.Descriptor.gameObject.name, "mat"); + var writer = new AssetWriter(); + var outputPaths = new OutputPaths(root.AssetDir, root.Descriptor.gameObject); + outputPaths.PrepareFolders(writer, false); - AssetDatabase.CreateAsset(result.Texture1, root.AssetDir + '/' + root.Descriptor.gameObject.name + '/' + texture.name + "_encrypt.asset"); - File.WriteAllBytes(root.AssetDir + '/' + root.Descriptor.gameObject.name + '/' + texture.name + "_encrypt.png", result.Texture2.EncodeToPNG()); + writer.CreateAssetInFolder(result.Texture1, outputPaths.Folders.TexGuid, outputPaths.EncryptedTextureName(texture, 0)); + File.WriteAllBytes(writer.UniquePathInFolder(outputPaths.Folders.TexGuid, OutputPaths.Sanitize(texture.name) + "_encrypt.png"), result.Texture2.EncodeToPNG()); if (result.Texture2 != null) - AssetDatabase.CreateAsset(result.Texture2, root.AssetDir + '/' + root.Descriptor.gameObject.name + '/' + texture.name + "_encrypt2.asset"); + writer.CreateAssetInFolder(result.Texture2, outputPaths.Folders.TexGuid, outputPaths.EncryptedTextureName(texture, 2)); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); diff --git a/Editor/liltoonCustom/CustomInspector.cs b/Editor/liltoonCustom/CustomInspector.cs index 722c919..9c4c7ed 100644 --- a/Editor/liltoonCustom/CustomInspector.cs +++ b/Editor/liltoonCustom/CustomInspector.cs @@ -33,14 +33,14 @@ protected override void LoadCustomProperties(MaterialProperty[] props, Material //isShowRenderMode = false; //LoadCustomLanguage(""); - mipTexture = FindProperty(ShellProtectorShaderProperties.MipTexture, props); - encryptedTexture0 = FindProperty(ShellProtectorShaderProperties.EncryptTexture0, props); - encryptedTexture1 = FindProperty(ShellProtectorShaderProperties.EncryptTexture1, props); - passwordHash = FindProperty(ShellProtectorShaderProperties.PasswordHash, props); - hashMagic = FindProperty(ShellProtectorShaderProperties.HashMagic, props); + mipTexture = FindProperty(ShaderProperties.MipTexture, props); + encryptedTexture0 = FindProperty(ShaderProperties.EncryptTexture0, props); + encryptedTexture1 = FindProperty(ShaderProperties.EncryptTexture1, props); + passwordHash = FindProperty(ShaderProperties.PasswordHash, props); + hashMagic = FindProperty(ShaderProperties.HashMagic, props); for (int i = 0; i < key.Length; ++i) - key[i] = FindProperty(ShellProtectorShaderProperties.KeyPrefix + i, props); + key[i] = FindProperty(ShaderProperties.KeyPrefix + i, props); } protected override void DrawCustomProperties(Material material) diff --git a/Runtime/Scripts/Algorithm/Chacha20.cs b/Runtime/Scripts/Algorithm/Chacha20.cs index 4bb9766..4e9c029 100644 --- a/Runtime/Scripts/Algorithm/Chacha20.cs +++ b/Runtime/Scripts/Algorithm/Chacha20.cs @@ -7,7 +7,7 @@ namespace Shell.Protector { public class Chacha20 : IEncryptor { - public string Keyword => ShellProtectorShaderProperties.ChachaKeyword; + public string Keyword => ShaderProperties.ChachaKeyword; public byte[] Nonce { get; } = new byte[12]; [MethodImpl(MethodImplOptions.AggressiveInlining)] byte[] U32t8le(uint v) diff --git a/Runtime/Scripts/Algorithm/XXTEA.cs b/Runtime/Scripts/Algorithm/XXTEA.cs index 191594c..4d38ba7 100644 --- a/Runtime/Scripts/Algorithm/XXTEA.cs +++ b/Runtime/Scripts/Algorithm/XXTEA.cs @@ -5,7 +5,7 @@ namespace Shell.Protector { public class XXTEA : IEncryptor { - public string Keyword => ShellProtectorShaderProperties.XXTEAKeyword; + public string Keyword => ShaderProperties.XXTEAKeyword; const uint Delta = 0x9E3779B9; public uint Rounds { get; set; } diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 3ed08a6..8b56e0c 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -16,11 +16,10 @@ public class AnimatorManager : ScriptableObject { Dictionary encryptedClip = new Dictionary(); - public static AnimatorController DuplicateAnimator(RuntimeAnimatorController anim, string newDir) + public static AnimatorController DuplicateAnimator(RuntimeAnimatorController anim, OutputPaths paths, AssetWriter writer) { string dir = AssetDatabase.GetAssetPath(anim); - string output = Path.Combine(newDir, anim.name + anim.GetInstanceID().ToString() + "_encrypted.anim"); - if (!AssetDatabase.CopyAsset(dir, output)) + if (!writer.CopyAssetToFolder(dir, paths.Folders.AnimGuid, paths.ControllerName(anim), out string output)) { Debug.LogErrorFormat("Failed to copy a animator: {0}", anim.name); } @@ -30,7 +29,7 @@ public static AnimatorController DuplicateAnimator(RuntimeAnimatorController ani return AssetDatabase.LoadAssetAtPath(output, typeof(RuntimeAnimatorController)) as AnimatorController; } - public static void CreateKeyAnimations(string animationDir, string newDir, GameObject[] objs) + public static void CreateKeyAnimations(string animationDir, OutputPaths paths, AssetWriter writer, GameObject[] objs) { string[] files = Directory.GetFiles(animationDir); foreach (string file in files) @@ -41,8 +40,8 @@ public static void CreateKeyAnimations(string animationDir, string newDir, GameO if (filename.Contains("dummy")) continue; - string path = Path.Combine(newDir, filename); - AssetDatabase.CopyAsset(file, path); + if (!writer.CopyAssetToFolder(file, paths.Folders.AnimGuid, filename, out string path)) + continue; AnimationClip clip = AssetDatabase.LoadAssetAtPath(path); if (clip == null) continue; @@ -66,7 +65,7 @@ static void AddKeyCurve(AnimationClip clip, GameObject obj, int keyIndex, bool s var binding = new EditorCurveBinding { path = GetAnimationPath(obj.transform), - propertyName = "material." + ShellProtectorShaderProperties.KeyPrefix + keyIndex, + propertyName = "material." + ShaderProperties.KeyPrefix + keyIndex, type = obj.GetComponent() == null ? typeof(MeshRenderer) : typeof(SkinnedMeshRenderer) }; @@ -448,12 +447,12 @@ public static void ChangeMaterialInClip(AnimationClip clip, Material source, Mat } } - void SearchStateMachine(AnimatorStateMachine stateMachine, Material targetMaterial, Material encrypted, bool clone, string clonePath) + void SearchStateMachine(AnimatorStateMachine stateMachine, Material targetMaterial, Material encrypted, bool clone, OutputPaths paths, AssetWriter writer) { for (int i = 0; i < stateMachine.states.Length; i++) { ChildAnimatorState state = stateMachine.states[i]; - AnimationClip clip = SearchMotion(state.state.motion, targetMaterial, encrypted, clone, clonePath); + AnimationClip clip = SearchMotion(state.state.motion, targetMaterial, encrypted, clone, paths, writer); if (clip != null) { state.state.motion = clip; @@ -463,11 +462,11 @@ void SearchStateMachine(AnimatorStateMachine stateMachine, Material targetMateri foreach (ChildAnimatorStateMachine childStateMachine in stateMachine.stateMachines) { - SearchStateMachine(childStateMachine.stateMachine, targetMaterial, encrypted, clone, clonePath); + SearchStateMachine(childStateMachine.stateMachine, targetMaterial, encrypted, clone, paths, writer); } } - AnimationClip SearchMotion(Motion motion, Material targetMaterial, Material encrypted, bool clone, string clonePath) + AnimationClip SearchMotion(Motion motion, Material targetMaterial, Material encrypted, bool clone, OutputPaths paths, AssetWriter writer) { if (motion is AnimationClip clip) { @@ -477,7 +476,6 @@ AnimationClip SearchMotion(Motion motion, Material targetMaterial, Material encr if (clone) { string path = AssetDatabase.GetAssetPath(clip); - string copyPath = Path.Combine(clonePath, clip.name + "_encrypted.anim"); if (encryptedClip.ContainsKey(clip)) { @@ -485,7 +483,7 @@ AnimationClip SearchMotion(Motion motion, Material targetMaterial, Material encr } else { - if (!AssetDatabase.CopyAsset(path, copyPath)) + if (!writer.CopyAssetToFolder(path, paths.Folders.AnimGuid, paths.AnimationClipName(clip, "_encrypted"), out string copyPath)) { Debug.LogError("Copy error: " + copyPath); return null; @@ -506,7 +504,7 @@ AnimationClip SearchMotion(Motion motion, Material targetMaterial, Material encr for (int i = 0; i < blendTree.children.Length; ++i) { ChildMotion childMotion = blendTree.children[i]; - AnimationClip result = SearchMotion(childMotion.motion, targetMaterial, encrypted, clone, clonePath); + AnimationClip result = SearchMotion(childMotion.motion, targetMaterial, encrypted, clone, paths, writer); if (result != null) { childMotion.motion = result; @@ -520,7 +518,7 @@ AnimationClip SearchMotion(Motion motion, Material targetMaterial, Material encr return null; } - public void ChangeAnimationMaterial(AnimatorController anim, Material original, Material encrypted, bool clone, string clonePath) + public void ChangeAnimationMaterial(AnimatorController anim, Material original, Material encrypted, bool clone, OutputPaths paths, AssetWriter writer) { if (anim == null || original == null) return; @@ -535,7 +533,7 @@ public void ChangeAnimationMaterial(AnimatorController anim, Material original, var stateMachine = layer.stateMachine; if (stateMachine == null) continue; - SearchStateMachine(stateMachine, original, encrypted, clone, clonePath); + SearchStateMachine(stateMachine, original, encrypted, clone, paths, writer); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); diff --git a/Runtime/Scripts/ShellProtectorConstants.cs b/Runtime/Scripts/Constants.cs similarity index 96% rename from Runtime/Scripts/ShellProtectorConstants.cs rename to Runtime/Scripts/Constants.cs index 5a7baa2..342e56a 100644 --- a/Runtime/Scripts/ShellProtectorConstants.cs +++ b/Runtime/Scripts/Constants.cs @@ -24,7 +24,7 @@ public enum ShellProtectorFallback Size128 = 7 } - public static class ShellProtectorShaderProperties + public static class ShaderProperties { public const string KeywordPrefix = "_SHELL_PROTECTOR_"; public const string XXTEAKeyword = "_SHELL_PROTECTOR_XXTEA"; diff --git a/Runtime/Scripts/ShellProtectorConstants.cs.meta b/Runtime/Scripts/Constants.cs.meta similarity index 100% rename from Runtime/Scripts/ShellProtectorConstants.cs.meta rename to Runtime/Scripts/Constants.cs.meta diff --git a/Runtime/Scripts/Formats/DXTFormat.cs b/Runtime/Scripts/Formats/DXTFormat.cs index e3e83fe..ae40564 100644 --- a/Runtime/Scripts/Formats/DXTFormat.cs +++ b/Runtime/Scripts/Formats/DXTFormat.cs @@ -42,8 +42,8 @@ protected Texture2D HandleCrunchedFormat(Texture2D texture, int mip_lv, bool isD } public override void SetFormatKeywords(Material material) { - material.DisableKeyword(ShellProtectorShaderProperties.Format0Keyword); - material.DisableKeyword(ShellProtectorShaderProperties.Format1Keyword); + material.DisableKeyword(ShaderProperties.Format0Keyword); + material.DisableKeyword(ShaderProperties.Format1Keyword); } public override (int, int) CalculateOffsets(Texture2D texture) { diff --git a/Runtime/Scripts/Formats/RGBFormat.cs b/Runtime/Scripts/Formats/RGBFormat.cs index 9f2723d..6e1b607 100644 --- a/Runtime/Scripts/Formats/RGBFormat.cs +++ b/Runtime/Scripts/Formats/RGBFormat.cs @@ -59,8 +59,8 @@ public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor } public override void SetFormatKeywords(Material material) { - material.EnableKeyword(ShellProtectorShaderProperties.Format0Keyword); - material.DisableKeyword(ShellProtectorShaderProperties.Format1Keyword); + material.EnableKeyword(ShaderProperties.Format0Keyword); + material.DisableKeyword(ShaderProperties.Format1Keyword); } public override (int, int) CalculateOffsets(Texture2D texture) { @@ -110,8 +110,8 @@ public override EncryptResult Encrypt(Texture2D texture, byte[] key, IEncryptor } public override void SetFormatKeywords(Material material) { - material.DisableKeyword(ShellProtectorShaderProperties.Format0Keyword); - material.EnableKeyword(ShellProtectorShaderProperties.Format1Keyword); + material.DisableKeyword(ShaderProperties.Format0Keyword); + material.EnableKeyword(ShaderProperties.Format1Keyword); } public override (int, int) CalculateOffsets(Texture2D texture) { diff --git a/Runtime/Scripts/Injector/IShaderAdapter.cs b/Runtime/Scripts/Injector/IShaderAdapter.cs index b4a7bc7..b0fca1b 100644 --- a/Runtime/Scripts/Injector/IShaderAdapter.cs +++ b/Runtime/Scripts/Injector/IShaderAdapter.cs @@ -8,7 +8,7 @@ public interface IShaderAdapter bool CanHandle(Shader shader); bool WasInjected(Shader shader); void SetKeywords(Material material, bool hasLimTexture = false); - Shader Inject(Material material, string decoderPath, string outputPath, Texture2D mainTexture, ShellProtectorAuxiliaryTextures auxiliaryTextures); + Shader Inject(Material material, string decoderPath, string outputPath, Texture2D mainTexture, AuxiliaryTextures auxiliaryTextures); } } #endif diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index 0bf1f39..9f25bfc 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -94,7 +94,7 @@ public void SetKeywords(Material material, bool hasLimTexture = false) var keywords = material.shaderKeywords; foreach (string keyword in keywords) { - if (keyword.StartsWith(ShellProtectorShaderProperties.KeywordPrefix)) { + if (keyword.StartsWith(ShaderProperties.KeywordPrefix)) { material.DisableKeyword(keyword); } } @@ -104,13 +104,13 @@ public void SetKeywords(Material material, bool hasLimTexture = false) // Set rimlight keyword if (hasLimTexture) - material.EnableKeyword(ShellProtectorShaderProperties.RimLightKeyword); + material.EnableKeyword(ShaderProperties.RimLightKeyword); // Set encryptor keyword material.EnableKeyword(Encryptor.Keyword); } - public Shader Inject(Material material, string decoderPath, string outputPath, Texture2D mainTexture, ShellProtectorAuxiliaryTextures auxiliaryTextures) + public Shader Inject(Material material, string decoderPath, string outputPath, Texture2D mainTexture, AuxiliaryTextures auxiliaryTextures) { return Inject( material, diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index 02f42d8..ce051f4 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -137,21 +137,21 @@ private void InsertProperties(ref string data) { int suffixIndex = match.Index + match.Length; string properties = @" -" + ShellProtectorShaderProperties.MipTexture + @" (""MipReference"", 2D) = ""white"" { } -" + ShellProtectorShaderProperties.EncryptTexture0 + @" (""Encrypted0"", 2D) = ""white"" { } -" + ShellProtectorShaderProperties.EncryptTexture1 + @" (""Encrypted1"", 2D) = ""white"" { } -" + ShellProtectorShaderProperties.WidthOffset + @" (""Woffset"", integer) = 0 -" + ShellProtectorShaderProperties.HeightOffset + @" (""Hoffset"", integer) = 0 -" + ShellProtectorShaderProperties.Nonce0 + @" (""Nonce"", integer) = 0 -" + ShellProtectorShaderProperties.Nonce1 + @" (""Nonce"", integer) = 0 -" + ShellProtectorShaderProperties.Nonce2 + @" (""Nonce"", integer) = 0 -" + ShellProtectorShaderProperties.Rounds + @" (""Rounds"", integer) = 0 -" + ShellProtectorShaderProperties.PasswordHash + @" (""PasswordHash"", integer) = 0 -" + ShellProtectorShaderProperties.HashMagic + @" (""HashMagic"", integer) = 0 +" + ShaderProperties.MipTexture + @" (""MipReference"", 2D) = ""white"" { } +" + ShaderProperties.EncryptTexture0 + @" (""Encrypted0"", 2D) = ""white"" { } +" + ShaderProperties.EncryptTexture1 + @" (""Encrypted1"", 2D) = ""white"" { } +" + ShaderProperties.WidthOffset + @" (""Woffset"", integer) = 0 +" + ShaderProperties.HeightOffset + @" (""Hoffset"", integer) = 0 +" + ShaderProperties.Nonce0 + @" (""Nonce"", integer) = 0 +" + ShaderProperties.Nonce1 + @" (""Nonce"", integer) = 0 +" + ShaderProperties.Nonce2 + @" (""Nonce"", integer) = 0 +" + ShaderProperties.Rounds + @" (""Rounds"", integer) = 0 +" + ShaderProperties.PasswordHash + @" (""PasswordHash"", integer) = 0 +" + ShaderProperties.HashMagic + @" (""HashMagic"", integer) = 0 "; for (int i = 0; i < 16; ++i) - properties += ShellProtectorShaderProperties.KeyPrefix + i + " (\"key" + i + "\", float) = 0\n"; + properties += ShaderProperties.KeyPrefix + i + " (\"key" + i + "\", float) = 0\n"; data = data.Insert(suffixIndex, properties); } diff --git a/Runtime/Scripts/lang.meta b/Runtime/Scripts/Localization.meta similarity index 100% rename from Runtime/Scripts/lang.meta rename to Runtime/Scripts/Localization.meta diff --git a/Runtime/Scripts/lang/LanguageManager.cs b/Runtime/Scripts/Localization/LanguageManager.cs similarity index 100% rename from Runtime/Scripts/lang/LanguageManager.cs rename to Runtime/Scripts/Localization/LanguageManager.cs diff --git a/Runtime/Scripts/lang/LanguageManager.cs.meta b/Runtime/Scripts/Localization/LanguageManager.cs.meta similarity index 100% rename from Runtime/Scripts/lang/LanguageManager.cs.meta rename to Runtime/Scripts/Localization/LanguageManager.cs.meta diff --git a/Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs b/Runtime/Scripts/NDMF/NdmfPlugin.cs similarity index 88% rename from Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs rename to Runtime/Scripts/NDMF/NdmfPlugin.cs index a3ad366..1078c50 100644 --- a/Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs +++ b/Runtime/Scripts/NDMF/NdmfPlugin.cs @@ -4,9 +4,9 @@ using Shell.Protector; using UnityEngine; -[assembly: ExportsPlugin(typeof(ShellProtectorNDMFPlugin))] +[assembly: ExportsPlugin(typeof(NdmfPlugin))] -public class ShellProtectorNDMFPlugin : Plugin +public class NdmfPlugin : Plugin { protected override void Configure() { @@ -31,4 +31,4 @@ protected override void Configure() } } #endif -#endif \ No newline at end of file +#endif diff --git a/Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs.meta b/Runtime/Scripts/NDMF/NdmfPlugin.cs.meta similarity index 100% rename from Runtime/Scripts/NDMF/ShellProtectorNDMFPlugin.cs.meta rename to Runtime/Scripts/NDMF/NdmfPlugin.cs.meta diff --git a/Runtime/Scripts/Obfuscator.cs b/Runtime/Scripts/Obfuscator.cs index 4753e35..7122218 100644 --- a/Runtime/Scripts/Obfuscator.cs +++ b/Runtime/Scripts/Obfuscator.cs @@ -12,7 +12,8 @@ namespace Shell.Protector { public class Obfuscator : ScriptableObject { - string animDir = ""; + OutputPaths outputPaths; + AssetWriter assetWriter; List obfuscatedBlendShapeIndex = new List(); Dictionary obfuscatedBlendShapeNames = new Dictionary(); // before, after @@ -46,7 +47,8 @@ public Obfuscator() public void Clean() { Clone = true; - animDir = ""; + outputPaths = null; + assetWriter = null; obfuscatedBlendShapeNames.Clear(); obfuscatedBlendShapeIndex.Clear(); obfuscatedClip.Clear(); @@ -65,7 +67,7 @@ static string GetAnimationPath(Transform transform) return string.Join("/", names); } - public Mesh ObfuscateBlendShapeMesh(Mesh mesh, string newPath) + public Mesh ObfuscateBlendShapeMesh(Mesh mesh, OutputPaths paths, AssetWriter writer) { Mesh obfuscatedMesh = Instantiate(mesh); obfuscatedMesh.ClearBlendShapes(); @@ -123,7 +125,7 @@ public Mesh ObfuscateBlendShapeMesh(Mesh mesh, string newPath) } Debug.LogFormat("Obfuscator blendshapes : {0}", string.Join(", ", obfuscatedBlendShapeNames.Select(kv => $"{kv.Key}: {kv.Value}"))); - AssetDatabase.CreateAsset(obfuscatedMesh, Path.Combine(newPath, obfuscatedMesh.name + obfuscatedMesh.GetHashCode() + ".asset")); + writer.CreateAssetInFolder(obfuscatedMesh, paths.Folders.MeshGuid, paths.MeshAssetName(mesh)); AssetDatabase.Refresh(); return obfuscatedMesh; } @@ -148,12 +150,13 @@ public void ChangeObfuscatedBlendShapeInDescriptor(VRCAvatarDescriptor descripto } - public void ObfuscateBlendshapeInAnim(AnimatorController anim, GameObject obj, string newDir) + public void ObfuscateBlendshapeInAnim(AnimatorController anim, GameObject obj, OutputPaths paths, AssetWriter writer) { if (anim == null) return; - animDir = newDir; + outputPaths = paths; + assetWriter = writer; var layers = anim.layers; foreach (var layer in layers) @@ -250,7 +253,7 @@ public AnimationClip ChangeBlendShapeInClip(AnimationClip clip, GameObject obj) else { newClip = Instantiate(clip); - AssetDatabase.CreateAsset(newClip, Path.Combine(animDir, clip.name + clip.GetHashCode() + "_obfuscated.anim")); + assetWriter.CreateAssetInFolder(newClip, outputPaths.Folders.AnimGuid, outputPaths.AnimationClipName(clip, "_obfuscated")); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); obfuscatedClip.Add(clip, newClip); diff --git a/Runtime/Scripts/Pipeline/AssetWriter.cs b/Runtime/Scripts/Pipeline/AssetWriter.cs new file mode 100644 index 0000000..3ca5556 --- /dev/null +++ b/Runtime/Scripts/Pipeline/AssetWriter.cs @@ -0,0 +1,105 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace Shell.Protector +{ + public interface IAssetWriter + { + void CreateAsset(Object asset, string path); + bool CopyAsset(string sourcePath, string targetPath); + T LoadAssetAtPath(string path) where T : Object; + bool IsValidFolder(string path); + void CreateFolder(string parentFolder, string newFolderName); + void DeleteAsset(string path); + void SaveAssets(); + void Refresh(); + void SaveAndRefresh(); + string EnsureFolderAndGetGuid(string path); + string ResolveFolderPath(string guid); + string UniquePathInFolder(string folderGuid, string fileName); + void CreateAssetInFolder(Object asset, string folderGuid, string fileName); + bool CopyAssetToFolder(string sourcePath, string folderGuid, string fileName, out string targetPath); + } + + public sealed class AssetWriter : IAssetWriter + { + public void CreateAsset(Object asset, string path) => AssetDatabase.CreateAsset(asset, path); + public bool CopyAsset(string sourcePath, string targetPath) => AssetDatabase.CopyAsset(sourcePath, targetPath); + public T LoadAssetAtPath(string path) where T : Object => AssetDatabase.LoadAssetAtPath(path); + public bool IsValidFolder(string path) => AssetDatabase.IsValidFolder(path); + public void CreateFolder(string parentFolder, string newFolderName) => AssetDatabase.CreateFolder(parentFolder, newFolderName); + public void DeleteAsset(string path) => AssetDatabase.DeleteAsset(path); + public void SaveAssets() => AssetDatabase.SaveAssets(); + public void Refresh() => AssetDatabase.Refresh(); + + public void SaveAndRefresh() + { + SaveAssets(); + Refresh(); + } + + public string UniquePath(string path) => AssetDatabase.GenerateUniqueAssetPath(OutputPaths.Normalize(path)); + + public string EnsureFolderAndGetGuid(string path) + { + string normalized = OutputPaths.Normalize(path).TrimEnd('/'); + EnsureFolder(normalized); + + string guid = AssetDatabase.AssetPathToGUID(normalized); + if (string.IsNullOrEmpty(guid)) + { + SaveAndRefresh(); + guid = AssetDatabase.AssetPathToGUID(normalized); + } + + if (string.IsNullOrEmpty(guid)) + throw new System.InvalidOperationException("Failed to resolve folder GUID: " + normalized); + + return guid; + } + + public string ResolveFolderPath(string guid) + { + string path = AssetDatabase.GUIDToAssetPath(guid); + if (string.IsNullOrEmpty(path) || !AssetDatabase.IsValidFolder(path)) + throw new System.InvalidOperationException("Failed to resolve folder path from GUID: " + guid); + return OutputPaths.Normalize(path); + } + + public string UniquePathInFolder(string folderGuid, string fileName) + { + string folder = ResolveFolderPath(folderGuid); + return AssetDatabase.GenerateUniqueAssetPath(OutputPaths.Combine(folder, OutputPaths.Sanitize(fileName))); + } + + public void CreateAssetInFolder(Object asset, string folderGuid, string fileName) + { + CreateAsset(asset, UniquePathInFolder(folderGuid, fileName)); + } + + public bool CopyAssetToFolder(string sourcePath, string folderGuid, string fileName, out string targetPath) + { + targetPath = UniquePathInFolder(folderGuid, fileName); + return CopyAsset(sourcePath, targetPath); + } + + public void EnsureFolder(string path) + { + string normalized = OutputPaths.Normalize(path).TrimEnd('/'); + string[] parts = normalized.Split('/'); + if (parts.Length == 0 || parts[0] != "Assets") + return; + + string current = "Assets"; + for (int i = 1; i < parts.Length; i++) + { + string next = current + "/" + parts[i]; + if (!AssetDatabase.IsValidFolder(next)) + AssetDatabase.CreateFolder(current, parts[i]); + current = next; + } + } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/IAssetWriter.cs.meta b/Runtime/Scripts/Pipeline/AssetWriter.cs.meta similarity index 100% rename from Runtime/Scripts/Pipeline/IAssetWriter.cs.meta rename to Runtime/Scripts/Pipeline/AssetWriter.cs.meta diff --git a/Runtime/Scripts/Pipeline/AvatarProcessor.cs b/Runtime/Scripts/Pipeline/AvatarProcessor.cs new file mode 100644 index 0000000..54a6084 --- /dev/null +++ b/Runtime/Scripts/Pipeline/AvatarProcessor.cs @@ -0,0 +1,41 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using UnityEngine; + +namespace Shell.Protector +{ + internal static class AvatarProcessor + { + public static void ReplaceMaterials(GameObject avatar, BuildResult result) + { + ReplaceRendererMaterials(avatar.GetComponentsInChildren(true), result); + ReplaceRendererMaterials(avatar.GetComponentsInChildren(true), result); + } + + static void ReplaceRendererMaterials(IEnumerable renderers, BuildResult result) where T : Renderer + { + foreach (T renderer in renderers) + { + Material[] materials = renderer.sharedMaterials; + if (materials == null) + continue; + + bool changed = false; + for (int i = 0; i < materials.Length; ++i) + { + Material material = materials[i]; + if (material == null || !result.EncryptedMaterials.ContainsKey(material)) + continue; + + materials[i] = result.EncryptedMaterials[material]; + result.Meshes.Add(renderer.gameObject); + changed = true; + } + + if (changed) + renderer.sharedMaterials = materials; + } + } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/AvatarProcessor.cs.meta b/Runtime/Scripts/Pipeline/AvatarProcessor.cs.meta new file mode 100644 index 0000000..ac9c15e --- /dev/null +++ b/Runtime/Scripts/Pipeline/AvatarProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d3b131f08a0e41bd8be4bb2421140d82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs b/Runtime/Scripts/Pipeline/BuildContext.cs similarity index 60% rename from Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs rename to Runtime/Scripts/Pipeline/BuildContext.cs index 9deb1c0..f473e94 100644 --- a/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs +++ b/Runtime/Scripts/Pipeline/BuildContext.cs @@ -4,7 +4,7 @@ namespace Shell.Protector { - public struct ShellProtectorProcessedTexture + public struct ProcessedTexture { public EncryptResult Encrypted; public List Fallbacks; @@ -12,7 +12,7 @@ public struct ShellProtectorProcessedTexture public byte[] Nonce; } - public struct ShellProtectorAuxiliaryTextures + public struct AuxiliaryTextures { public Texture2D LimTexture; public Texture2D LimTexture2; @@ -20,18 +20,18 @@ public struct ShellProtectorAuxiliaryTextures public Texture2D LimShadeTexture; } - public sealed class ShellProtectorBuildContext + public sealed class BuildContext { - public ShellProtectorBuildContext(ShellProtectorBuildRequest request, ShellProtectorSettings settings) + public BuildContext(BuildRequest request, BuildSettings settings) { Request = request; Settings = settings; - Result = new ShellProtectorBuildResult(); + Result = new BuildResult(); } - public ShellProtectorBuildRequest Request { get; } - public ShellProtectorSettings Settings { get; } - public ShellProtectorBuildResult Result { get; } + public BuildRequest Request { get; } + public BuildSettings Settings { get; } + public BuildResult Result { get; } public IEncryptor Encryptor { get; set; } public EncryptedHistory History { get; set; } public Texture2D FallbackWhite { get; set; } diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs.meta b/Runtime/Scripts/Pipeline/BuildContext.cs.meta similarity index 100% rename from Runtime/Scripts/Pipeline/ShellProtectorBuildContext.cs.meta rename to Runtime/Scripts/Pipeline/BuildContext.cs.meta diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs b/Runtime/Scripts/Pipeline/BuildRequest.cs similarity index 71% rename from Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs rename to Runtime/Scripts/Pipeline/BuildRequest.cs index 5924408..4d5bfc3 100644 --- a/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs +++ b/Runtime/Scripts/Pipeline/BuildRequest.cs @@ -3,9 +3,9 @@ namespace Shell.Protector { - public sealed class ShellProtectorBuildRequest + public sealed class BuildRequest { - public ShellProtectorBuildRequest(ShellProtector owner, VRCAvatarDescriptor descriptor, bool useSmallMipTexture, bool isModular) + public BuildRequest(ShellProtector owner, VRCAvatarDescriptor descriptor, bool useSmallMipTexture, bool isModular) { Owner = owner; Descriptor = descriptor; diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs.meta b/Runtime/Scripts/Pipeline/BuildRequest.cs.meta similarity index 100% rename from Runtime/Scripts/Pipeline/ShellProtectorBuildRequest.cs.meta rename to Runtime/Scripts/Pipeline/BuildRequest.cs.meta diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs b/Runtime/Scripts/Pipeline/BuildResult.cs similarity index 76% rename from Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs rename to Runtime/Scripts/Pipeline/BuildResult.cs index 0ed5cb4..759a237 100644 --- a/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs +++ b/Runtime/Scripts/Pipeline/BuildResult.cs @@ -4,14 +4,14 @@ namespace Shell.Protector { - public sealed class ShellProtectorBuildResult + public sealed class BuildResult { public GameObject Avatar { get; set; } public string AvatarDir { get; set; } public byte[] KeyBytes { get; set; } public HashSet Meshes { get; } = new HashSet(); public Dictionary EncryptedMaterials { get; } = new Dictionary(); - public Dictionary ProcessedTextures { get; } = new Dictionary(); + public Dictionary ProcessedTextures { get; } = new Dictionary(); public void Clear() { diff --git a/Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs.meta b/Runtime/Scripts/Pipeline/BuildResult.cs.meta similarity index 100% rename from Runtime/Scripts/Pipeline/ShellProtectorBuildResult.cs.meta rename to Runtime/Scripts/Pipeline/BuildResult.cs.meta diff --git a/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs b/Runtime/Scripts/Pipeline/BuildSettings.cs similarity index 94% rename from Runtime/Scripts/Pipeline/ShellProtectorSettings.cs rename to Runtime/Scripts/Pipeline/BuildSettings.cs index a657300..54e23b1 100644 --- a/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs +++ b/Runtime/Scripts/Pipeline/BuildSettings.cs @@ -1,7 +1,7 @@ #if UNITY_EDITOR namespace Shell.Protector { - public sealed class ShellProtectorSettings + public sealed class BuildSettings { public string AssetDir { get; set; } public string FixedPassword { get; set; } diff --git a/Runtime/Scripts/Pipeline/ShellProtectorSettings.cs.meta b/Runtime/Scripts/Pipeline/BuildSettings.cs.meta similarity index 100% rename from Runtime/Scripts/Pipeline/ShellProtectorSettings.cs.meta rename to Runtime/Scripts/Pipeline/BuildSettings.cs.meta diff --git a/Runtime/Scripts/Pipeline/IAssetWriter.cs b/Runtime/Scripts/Pipeline/IAssetWriter.cs deleted file mode 100644 index 6a0e509..0000000 --- a/Runtime/Scripts/Pipeline/IAssetWriter.cs +++ /dev/null @@ -1,38 +0,0 @@ -#if UNITY_EDITOR -using UnityEditor; -using UnityEngine; - -namespace Shell.Protector -{ - public interface IAssetWriter - { - void CreateAsset(Object asset, string path); - bool CopyAsset(string sourcePath, string targetPath); - T LoadAssetAtPath(string path) where T : Object; - bool IsValidFolder(string path); - void CreateFolder(string parentFolder, string newFolderName); - void DeleteAsset(string path); - void SaveAssets(); - void Refresh(); - void SaveAndRefresh(); - } - - public sealed class UnityAssetWriter : IAssetWriter - { - public void CreateAsset(Object asset, string path) => AssetDatabase.CreateAsset(asset, path); - public bool CopyAsset(string sourcePath, string targetPath) => AssetDatabase.CopyAsset(sourcePath, targetPath); - public T LoadAssetAtPath(string path) where T : Object => AssetDatabase.LoadAssetAtPath(path); - public bool IsValidFolder(string path) => AssetDatabase.IsValidFolder(path); - public void CreateFolder(string parentFolder, string newFolderName) => AssetDatabase.CreateFolder(parentFolder, newFolderName); - public void DeleteAsset(string path) => AssetDatabase.DeleteAsset(path); - public void SaveAssets() => AssetDatabase.SaveAssets(); - public void Refresh() => AssetDatabase.Refresh(); - - public void SaveAndRefresh() - { - SaveAssets(); - Refresh(); - } - } -} -#endif diff --git a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs new file mode 100644 index 0000000..61806e0 --- /dev/null +++ b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs @@ -0,0 +1,73 @@ +#if UNITY_EDITOR +using UnityEngine; + +namespace Shell.Protector +{ + internal sealed class MaterialEncryptor + { + readonly AssetWriter writer; + readonly bool turnOnAllSafetyFallback; + readonly int algorithm; + readonly uint rounds; + + public MaterialEncryptor(AssetWriter writer, bool turnOnAllSafetyFallback, int algorithm, uint rounds) + { + this.writer = writer; + this.turnOnAllSafetyFallback = turnOnAllSafetyFallback; + this.algorithm = algorithm; + this.rounds = rounds; + } + + public Material CreateEncryptedMaterial(string folderGuid, string fileName, Material source, Shader shader, Texture2D fallback, Texture2D mip, AuxiliaryTextures auxiliary, ProcessedTexture texture, byte[] keyBytes, IEncryptor encryptor, Injector injector) + { + Material result = new Material(source.shader); + result.CopyPropertiesFromMaterial(source); + result.shader = shader; + var originalTex = (Texture2D)result.mainTexture; + result.mainTexture = fallback; + + if (texture.Encrypted.Texture1 != null) + result.SetTexture(ShaderProperties.EncryptTexture0, texture.Encrypted.Texture1); + if (texture.Encrypted.Texture2 != null) + result.SetTexture(ShaderProperties.EncryptTexture1, texture.Encrypted.Texture2); + + result.SetTexture(ShaderProperties.MipTexture, mip); + result.renderQueue = source.renderQueue; + if (turnOnAllSafetyFallback) + result.SetOverrideTag("VRCFallback", "Unlit"); + + var (widthOffset, heightOffset) = TextureEncryptManager.CalculateOffsets(originalTex); + result.SetInteger(ShaderProperties.WidthOffset, widthOffset); + result.SetInteger(ShaderProperties.HeightOffset, heightOffset); + for (int i = 0; i < keyBytes.Length; ++i) + result.SetFloat(ShaderProperties.KeyPrefix + i, keyBytes[i]); + + if (algorithm == (int)ShellProtectorAlgorithm.Chacha) + { + Chacha20 chacha = encryptor as Chacha20; + result.SetInteger(ShaderProperties.Nonce0, (int)chacha.GetNonceUint3()[0]); + result.SetInteger(ShaderProperties.Nonce1, (int)chacha.GetNonceUint3()[1]); + result.SetInteger(ShaderProperties.Nonce2, (int)chacha.GetNonceUint3()[2]); + } + else if (algorithm == (int)ShellProtectorAlgorithm.XXTEA) + { + result.SetInteger(ShaderProperties.Rounds, (int)rounds); + } + + var key = new byte[16]; + for (int i = 0; i < 16; i++) + key[i] = keyBytes[i]; + + uint hashMagic = (uint)source.GetInstanceID(); + var hash = KeyGenerator.SimpleHash(key, hashMagic); + result.SetInteger(ShaderProperties.HashMagic, (int)hashMagic); + result.SetInteger(ShaderProperties.PasswordHash, (int)hash); + + injector.SetKeywords(result, auxiliary.LimTexture != null); + writer.CreateAssetInFolder(result, folderGuid, fileName); + writer.SaveAndRefresh(); + return result; + } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs.meta b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs.meta new file mode 100644 index 0000000..78afe5e --- /dev/null +++ b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b77120fbe64f4d67b9352e318588ee57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/OutputPaths.cs b/Runtime/Scripts/Pipeline/OutputPaths.cs new file mode 100644 index 0000000..8b3c715 --- /dev/null +++ b/Runtime/Scripts/Pipeline/OutputPaths.cs @@ -0,0 +1,152 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace Shell.Protector +{ + public sealed class OutputPaths + { + public const string TexFolder = "Tex"; + public const string MatFolder = "Mat"; + public const string ShaderFolder = "Shader"; + public const string AnimFolder = "Anim"; + public const string MeshFolder = "Mesh"; + + readonly Dictionary shaderFolderGuids = new Dictionary(); + + public OutputPaths(string root, GameObject avatar) + { + Root = Normalize(root); + AvatarName = Sanitize(avatar != null ? avatar.name : "Avatar"); + Avatar = Combine(Root, AvatarName); + Tex = Combine(Avatar, TexFolder); + Mat = Combine(Avatar, MatFolder); + Shader = Combine(Avatar, ShaderFolder); + Anim = Combine(Avatar, AnimFolder); + Mesh = Combine(Avatar, MeshFolder); + } + + public string Root { get; } + public string AvatarName { get; } + public string Avatar { get; } + public string Tex { get; } + public string Mat { get; } + public string Shader { get; } + public string Anim { get; } + public string Mesh { get; } + public OutputFolders Folders { get; private set; } + + public string MipTexture(int size) => Combine(Tex, "mip_" + size + ".asset"); + public string MipTextureName(int size) => "mip_" + size + ".asset"; + public string EncryptedTexture(Texture2D texture, int index) => Combine(Tex, BaseName(texture) + "_encrypt" + (index == 0 ? "" : index.ToString()) + ".asset"); + public string EncryptedTextureName(Texture2D texture, int index) => BaseName(texture) + "_encrypt" + (index == 0 ? "" : index.ToString()) + ".asset"; + public string FallbackTexture(Texture2D texture) => Combine(Tex, BaseName(texture) + "_fallback.asset"); + public string FallbackTextureName(Texture2D texture) => BaseName(texture) + "_fallback.asset"; + public string EncryptedMaterial(Material material) => Combine(Mat, BaseName(material) + "_encrypted.mat"); + public string EncryptedMaterialName(Material material) => BaseName(material) + "_encrypted.mat"; + public string DuplicatedMaterial(Material material) => Combine(Mat, BaseName(material) + "_duplicated.mat"); + public string DuplicatedMaterialName(Material material) => BaseName(material) + "_duplicated.mat"; + public string ShaderDirectory(Material material) => Combine(Shader, BaseName(material)); + public string Parameters(string name) => Combine(Avatar, Sanitize(name) + ".asset"); + public string ParametersName(string name) => Sanitize(name) + ".asset"; + public string Controller(RuntimeAnimatorController controller) => Combine(Anim, BaseName(controller) + "_encrypted.controller"); + public string ControllerName(RuntimeAnimatorController controller) => BaseName(controller) + "_encrypted.controller"; + public string AnimationClip(AnimationClip clip, string suffix) => Combine(Anim, BaseName(clip) + suffix + ".anim"); + public string AnimationClipName(AnimationClip clip, string suffix) => BaseName(clip) + suffix + ".anim"; + public string MeshAsset(Mesh mesh) => Combine(Mesh, BaseName(mesh) + ".asset"); + public string MeshAssetName(Mesh mesh) => BaseName(mesh) + ".asset"; + public string History() => Combine(Root, "EncryptedHistory.asset"); + public string HistoryName() => "EncryptedHistory.asset"; + + public OutputFolders PrepareFolders(AssetWriter writer, bool deleteExistingChildren) + { + string rootGuid = writer.EnsureFolderAndGetGuid(Root); + string avatarGuid = writer.EnsureFolderAndGetGuid(Avatar); + + if (deleteExistingChildren) + { + writer.DeleteAsset(Anim); + writer.DeleteAsset(Mat); + writer.DeleteAsset(Shader); + writer.DeleteAsset(Tex); + writer.DeleteAsset(Mesh); + writer.SaveAndRefresh(); + } + + Folders = new OutputFolders( + rootGuid, + avatarGuid, + writer.EnsureFolderAndGetGuid(Tex), + writer.EnsureFolderAndGetGuid(Mat), + writer.EnsureFolderAndGetGuid(Shader), + writer.EnsureFolderAndGetGuid(Anim), + writer.EnsureFolderAndGetGuid(Mesh) + ); + writer.SaveAndRefresh(); + shaderFolderGuids.Clear(); + return Folders; + } + + public string EnsureShaderFolder(AssetWriter writer, Material material) + { + if (material != null && shaderFolderGuids.TryGetValue(material, out string guid)) + return guid; + + string folderGuid = writer.EnsureFolderAndGetGuid(ShaderDirectory(material)); + if (material != null) + shaderFolderGuids[material] = folderGuid; + return folderGuid; + } + + public static string Combine(params string[] parts) + { + return Normalize(Path.Combine(parts)); + } + + public static string Normalize(string path) + { + return string.IsNullOrEmpty(path) ? string.Empty : path.Replace('\\', '/'); + } + + public static string Sanitize(string name) + { + if (string.IsNullOrWhiteSpace(name)) + return "Asset"; + + string cleaned = Regex.Replace(name.Trim(), @"[^\w\- .가-힣ぁ-んァ-ヶ一-龠]", "_"); + cleaned = Regex.Replace(cleaned, @"\s+", "_"); + cleaned = cleaned.Trim('.', '_'); + return string.IsNullOrEmpty(cleaned) ? "Asset" : cleaned; + } + + static string BaseName(Object asset) + { + return Sanitize(asset != null ? asset.name : "Asset"); + } + } + + public sealed class OutputFolders + { + public OutputFolders(string rootGuid, string avatarGuid, string texGuid, string matGuid, string shaderGuid, string animGuid, string meshGuid) + { + RootGuid = rootGuid; + AvatarGuid = avatarGuid; + TexGuid = texGuid; + MatGuid = matGuid; + ShaderGuid = shaderGuid; + AnimGuid = animGuid; + MeshGuid = meshGuid; + } + + public string RootGuid { get; } + public string AvatarGuid { get; } + public string TexGuid { get; } + public string MatGuid { get; } + public string ShaderGuid { get; } + public string AnimGuid { get; } + public string MeshGuid { get; } + } +} +#endif diff --git a/Runtime/Scripts/Pipeline/OutputPaths.cs.meta b/Runtime/Scripts/Pipeline/OutputPaths.cs.meta new file mode 100644 index 0000000..d61e90a --- /dev/null +++ b/Runtime/Scripts/Pipeline/OutputPaths.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1e52fb9784c4c34991285ea9885af75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs b/Runtime/Scripts/Pipeline/Pipeline.cs similarity index 56% rename from Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs rename to Runtime/Scripts/Pipeline/Pipeline.cs index 859087e..9085027 100644 --- a/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs +++ b/Runtime/Scripts/Pipeline/Pipeline.cs @@ -3,17 +3,17 @@ namespace Shell.Protector { - public sealed class ShellProtectorPipeline + public sealed class Pipeline { - public ShellProtectorBuildResult Encrypt(ShellProtectorBuildRequest request, ShellProtectorSettings settings) + public BuildResult Encrypt(BuildRequest request, BuildSettings settings) { if (request == null || request.Owner == null) - return new ShellProtectorBuildResult(); + return new BuildResult(); request.Owner.ApplySettings(settings); GameObject avatar = request.Owner.EncryptLegacy(request.UseSmallMipTexture, request.IsModular); - ShellProtectorBuildResult result = request.Owner.CurrentBuildResult; + BuildResult result = request.Owner.CurrentBuildResult; result.Avatar = avatar; return result; } diff --git a/Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs.meta b/Runtime/Scripts/Pipeline/Pipeline.cs.meta similarity index 100% rename from Runtime/Scripts/Pipeline/ShellProtectorPipeline.cs.meta rename to Runtime/Scripts/Pipeline/Pipeline.cs.meta diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index dcf3dd1..2712947 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -20,6 +20,10 @@ namespace Shell.Protector { public class ShellProtector : MonoBehaviour, IEditorOnly { + const string LegacyOutputDir = "Assets/ShellProtect"; + const string WrongRuntimeOutputDir = "Assets/ShellProtector/Runtime"; + const string DefaultOutputDir = "Assets/ShellProtector/Generated"; + [FormerlySerializedAs("gameobjectList")] [SerializeField] List _gameObjectList = new List(); @@ -32,8 +36,10 @@ public class ShellProtector : MonoBehaviour, IEditorOnly Injector _injector; readonly AssetManager _shaderManager = AssetManager.GetInstance(); + readonly AssetWriter _assetWriter = new AssetWriter(); bool _initialized; string _packageAssetDir; + OutputPaths _outputPaths; enum Algorithm { @@ -42,7 +48,7 @@ enum Algorithm } [FormerlySerializedAs("assetDir")] - [SerializeField] string _assetDir = "Assets/ShellProtect"; + [SerializeField] string _assetDir = DefaultOutputDir; [FormerlySerializedAs("pwd")] [SerializeField] string _fixedPassword = "password"; [FormerlySerializedAs("pwd2")] @@ -89,10 +95,10 @@ public class MaterialOptionPair EncryptedHistory _history; - ShellProtectorBuildResult _buildResult = new ShellProtectorBuildResult(); + BuildResult _buildResult = new BuildResult(); HashSet Meshes => _buildResult.Meshes; Dictionary EncryptedMaterials => _buildResult.EncryptedMaterials; - Dictionary ProcessedTextures => _buildResult.ProcessedTextures; + Dictionary ProcessedTextures => _buildResult.ProcessedTextures; [FormerlySerializedAs("rounds")] [SerializeField] uint _rounds = 20; @@ -134,18 +140,41 @@ string GetPackageAssetDir() MonoScript monoScript = MonoScript.FromMonoBehaviour(this); string scriptPath = AssetDatabase.GetAssetPath(monoScript); - _packageAssetDir = Path.GetDirectoryName(Path.GetDirectoryName(scriptPath)); + _packageAssetDir = OutputPaths.Normalize(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(scriptPath)))); return _packageAssetDir; } + string GetRuntimeAssetDir() + { + return OutputPaths.Combine(GetPackageAssetDir(), "Runtime"); + } + string ResolveOutputAssetDir() { - string packageDir = GetPackageAssetDir(); - if (string.IsNullOrEmpty(_assetDir) || _assetDir == "Assets/ShellProtect") - _assetDir = packageDir; + string normalized = OutputPaths.Normalize(_assetDir).TrimEnd('/'); + if (string.IsNullOrEmpty(normalized) || normalized == LegacyOutputDir || normalized == WrongRuntimeOutputDir || normalized == GetRuntimeAssetDir()) + normalized = DefaultOutputDir; + + _assetDir = normalized; return _assetDir; } + OutputPaths GetOutputPaths() + { + if (_outputPaths == null) + _outputPaths = new OutputPaths(_assetDir, _descriptor != null ? _descriptor.gameObject : null); + return _outputPaths; + } + + OutputPaths EnsureOutputFolders() + { + _assetDir = ResolveOutputAssetDir(); + OutputPaths paths = GetOutputPaths(); + if (paths.Folders == null) + paths.PrepareFolders(_assetWriter, false); + return paths; + } + public void Init() { if (_initialized) @@ -209,22 +238,22 @@ public GameObject Encrypt(bool isModular = true) public GameObject Encrypt(bool useSmallMip, bool isModular = true) { - var request = new ShellProtectorBuildRequest(this, _descriptor, useSmallMip, isModular); - var result = new ShellProtectorPipeline().Encrypt(request, CreateSettings()); + var request = new BuildRequest(this, _descriptor, useSmallMip, isModular); + var result = new Pipeline().Encrypt(request, CreateSettings()); ApplyBuildResult(result); return result.Avatar; } - internal ShellProtectorBuildResult CurrentBuildResult => _buildResult; + internal BuildResult CurrentBuildResult => _buildResult; - internal void ApplyBuildResult(ShellProtectorBuildResult result) + internal void ApplyBuildResult(BuildResult result) { - _buildResult = result ?? new ShellProtectorBuildResult(); + _buildResult = result ?? new BuildResult(); } - internal ShellProtectorSettings CreateSettings() + internal BuildSettings CreateSettings() { - return new ShellProtectorSettings + return new BuildSettings { AssetDir = _assetDir, FixedPassword = _fixedPassword, @@ -244,12 +273,13 @@ internal ShellProtectorSettings CreateSettings() }; } - internal void ApplySettings(ShellProtectorSettings settings) + internal void ApplySettings(BuildSettings settings) { if (settings == null) return; _assetDir = settings.AssetDir; + _outputPaths = null; _fixedPassword = settings.FixedPassword; _userPassword = settings.UserPassword; _language = settings.Language; @@ -274,17 +304,18 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) SyncMatOption(); - string resourceDir = GetPackageAssetDir(); + string resourceDir = GetRuntimeAssetDir(); _assetDir = ResolveOutputAssetDir(); - string avatarDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()); + _outputPaths = new OutputPaths(_assetDir, _descriptor != null ? _descriptor.gameObject : null); + string avatarDir = _outputPaths.Avatar; _buildResult.AvatarDir = avatarDir; Debug.Log("AssetDir: " + _assetDir); if (_fallbackWhite == null) - _fallbackWhite = AssetDatabase.LoadAssetAtPath(Path.Combine(resourceDir, "white.png"), typeof(Texture2D)) as Texture2D; + _fallbackWhite = AssetDatabase.LoadAssetAtPath(OutputPaths.Combine(resourceDir, "white.png"), typeof(Texture2D)) as Texture2D; if (_fallbackBlack == null) - _fallbackBlack = AssetDatabase.LoadAssetAtPath(Path.Combine(resourceDir, "black.png"), typeof(Texture2D)) as Texture2D; + _fallbackBlack = AssetDatabase.LoadAssetAtPath(OutputPaths.Combine(resourceDir, "black.png"), typeof(Texture2D)) as Texture2D; if (_descriptor == null) { @@ -344,11 +375,11 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) if (_history == null) { - _history = AssetDatabase.LoadAssetAtPath(Path.Combine(_assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; + _history = AssetDatabase.LoadAssetAtPath(_outputPaths.History(), typeof(EncryptedHistory)) as EncryptedHistory; if (_history == null) { _history = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(_history, Path.Combine(_assetDir, "EncryptedHistory.asset")); + _assetWriter.CreateAssetInFolder(_history, _outputPaths.Folders.RootGuid, _outputPaths.HistoryName()); } } _history.LoadData(); @@ -406,7 +437,7 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) int mipRefSize = Math.Max(mat.mainTexture.width, mat.mainTexture.height); if (!mips.ContainsKey(mipRefSize)) { - Texture2D mipRef = GenerateMipRefTexture(Path.Combine(avatarDir, "tex", "mip_" + mipRefSize + ".asset"), mipRefSize, useSmallMip); + Texture2D mipRef = GenerateMipRefTexture(_outputPaths.MipTextureName(mipRefSize), mipRefSize, useSmallMip); if (mipRef != null) mips.Add(mipRefSize, mipRef); } @@ -415,18 +446,19 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) TextureSettings.SetCrunchCompression(mainTexture, false); TextureSettings.SetGenerateMipmap(mainTexture, true); - string encryptedShaderPath = Path.Combine(avatarDir, "shader", mat.GetInstanceID().ToString()); + string encryptedShaderFolderGuid = _outputPaths.EnsureShaderFolder(_assetWriter, mat); + string encryptedShaderPath = _assetWriter.ResolveFolderPath(encryptedShaderFolderGuid); - var processedTextureResult = GenerateEncryptedTexture(avatarDir, mat, encryptor, keyBytes); + var processedTextureResult = GenerateEncryptedTexture(_outputPaths, mat, encryptor, keyBytes); if (!processedTextureResult.HasValue) continue; - ShellProtectorProcessedTexture processedTexture = processedTextureResult.Value; + ProcessedTexture processedTexture = processedTextureResult.Value; Texture2D encryptedTex1 = processedTexture.Encrypted.Texture1; Texture2D encryptedTex2 = processedTexture.Encrypted.Texture2; //////////////////////Inject shader/////////////////////// - ShellProtectorAuxiliaryTextures otherTex = GetLimOutlineTextures(mat); + AuxiliaryTextures otherTex = GetLimOutlineTextures(mat); Shader encryptedShader = IsEncryptedBefore(mat.shader); if (encryptedShader == null) { @@ -434,7 +466,7 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) { encryptedShader = _injector.Inject( mat, - Path.Combine(resourceDir, "Shader/ShellProtector.cginc"), + OutputPaths.Combine(resourceDir, "Shader/Protector.cginc"), encryptedShaderPath, encryptedTex1, otherTex.LimTexture != null, @@ -458,8 +490,7 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) } } ///////////////////////////////////////////////////////// - string fallbackDir = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_fallback.asset"); - Texture2D fallback = GenerateFallbackTexture(fallbackDir, option, mainTexture, ref processedTexture); + Texture2D fallback = GenerateFallbackTexture(_outputPaths.FallbackTextureName(mainTexture), option, mainTexture, ref processedTexture); if (fallback == null) Debug.LogErrorFormat("Failed to generate fallback texture: {0}", mainTexture.name); @@ -468,15 +499,14 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) if (mipTex == null) Debug.LogWarningFormat("mip_{0} is not exsist", maxSize); - string encryptedMatPath = Path.Combine(avatarDir, "mat", mat.GetInstanceID() + "_encrypted.mat"); - GenerateEncryptedMaterial(encryptedMatPath, mat, encryptedShader, fallback, mipTex, otherTex, processedTexture, keyBytes, encryptor); + GenerateEncryptedMaterial(_outputPaths.EncryptedMaterialName(mat), mat, encryptedShader, fallback, mipTex, otherTex, processedTexture, keyBytes, encryptor); } // Material loop EditorUtility.ClearProgressBar(); ///////////////////////parameter//////////////////// var av3 = avatar.GetComponent(); av3.expressionParameters = ParameterManager.AddKeyParameter(av3.expressionParameters, _keySize, _syncSize); - AssetDatabase.CreateAsset(av3.expressionParameters, Path.Combine(avatarDir, av3.expressionParameters.name + ".asset")); + _assetWriter.CreateAssetInFolder(av3.expressionParameters, _outputPaths.Folders.AvatarGuid, _outputPaths.ParametersName(av3.expressionParameters.name)); //////////////////////////////////////////////////// if (!isModular) { @@ -497,7 +527,7 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) var maMergeAnims = avatar.GetComponentsInChildren(true); foreach (var maMergeAnim in maMergeAnims) { - AnimatorController newAnim = AnimatorManager.DuplicateAnimator(maMergeAnim.animator, Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString())); + AnimatorController newAnim = AnimatorManager.DuplicateAnimator(maMergeAnim.animator, _outputPaths, _assetWriter); maMergeAnim.animator = newAnim; } #endif @@ -513,54 +543,11 @@ internal GameObject EncryptLegacy(bool useSmallMip, bool isModular = true) public void ReplaceMaterials(GameObject avatar) { - var renderers = avatar.GetComponentsInChildren(true); - if (renderers != null) - { - for (int i = 0; i < renderers.Length; ++i) - { - var mats = renderers[i].sharedMaterials; - if (mats == null) - continue; - for (int j = 0; j < mats.Length; ++j) - { - if (mats[j] == null) - continue; - - if (EncryptedMaterials.ContainsKey(mats[j])) - { - mats[j] = EncryptedMaterials[mats[j]]; - Meshes.Add(renderers[i].gameObject); - } - } - renderers[i].sharedMaterials = mats; - } - } - var skinnedRenderers = avatar.GetComponentsInChildren(true); - if (skinnedRenderers != null) - { - for (int i = 0; i < skinnedRenderers.Length; ++i) - { - var mats = skinnedRenderers[i].sharedMaterials; - if (mats == null) - continue; - for (int j = 0; j < mats.Length; ++j) - { - if (mats[j] == null) - continue; - - if (EncryptedMaterials.ContainsKey(mats[j])) - { - mats[j] = EncryptedMaterials[mats[j]]; - Meshes.Add(skinnedRenderers[i].gameObject); - } - } - skinnedRenderers[i].sharedMaterials = mats; - } - } + AvatarProcessor.ReplaceMaterials(avatar, _buildResult); } - ShellProtectorAuxiliaryTextures GetLimOutlineTextures(Material mat) + AuxiliaryTextures GetLimOutlineTextures(Material mat) { - ShellProtectorAuxiliaryTextures others = new ShellProtectorAuxiliaryTextures(); + AuxiliaryTextures others = new AuxiliaryTextures(); if (_shaderManager.IsPoiyomi(mat.shader)) { var tex_properties = mat.GetTexturePropertyNames(); @@ -591,10 +578,10 @@ ShellProtectorAuxiliaryTextures GetLimOutlineTextures(Material mat) } public void RemoveDuplicatedTextures(GameObject avatar) { - string avatarDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()); + OutputPaths paths = EnsureOutputFolders(); foreach (var mat in EncryptedMaterials.Values) { - ShellProtectorAuxiliaryTextures otherTex = GetLimOutlineTextures(mat); + AuxiliaryTextures otherTex = GetLimOutlineTextures(mat); foreach (var name in mat.GetTexturePropertyNames()) { @@ -688,11 +675,12 @@ public void RemoveDuplicatedTextures(GameObject avatar) Texture2D bigFallbackTexture = ProcessedTextures[tex].Fallbacks[idx]; if (tmp == null) { - Material mat = AssetDatabase.LoadAssetAtPath(Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); + string duplicatedPath = OutputPaths.Combine(_assetWriter.ResolveFolderPath(paths.Folders.MatGuid), paths.DuplicatedMaterialName(mats[j])); + Material mat = AssetDatabase.LoadAssetAtPath(duplicatedPath); if (mat == null) { tmp = Instantiate(mats[j]); - AssetDatabase.CreateAsset(tmp, Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); + _assetWriter.CreateAssetInFolder(tmp, paths.Folders.MatGuid, paths.DuplicatedMaterialName(mats[j])); AssetDatabase.SaveAssets(); } else @@ -736,11 +724,12 @@ public void RemoveDuplicatedTextures(GameObject avatar) Texture2D bigFallbackTexture = ProcessedTextures[mainTex].Fallbacks[idx]; if (tmp == null) { - Material mat = AssetDatabase.LoadAssetAtPath(Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); + string duplicatedPath = OutputPaths.Combine(_assetWriter.ResolveFolderPath(paths.Folders.MatGuid), paths.DuplicatedMaterialName(mats[j])); + Material mat = AssetDatabase.LoadAssetAtPath(duplicatedPath); if (mat == null) { tmp = Instantiate(mats[j]); - AssetDatabase.CreateAsset(tmp, Path.Combine(avatarDir, "mat", (mats[j].GetInstanceID() + "_duplicated.mat"))); + _assetWriter.CreateAssetInFolder(tmp, paths.Folders.MatGuid, paths.DuplicatedMaterialName(mats[j])); AssetDatabase.SaveAssets(); } else @@ -762,17 +751,18 @@ public void SetAnimations(GameObject avatar, bool clone) { var av3 = avatar.GetComponent(); AnimatorController fx; + OutputPaths paths = EnsureOutputFolders(); if (clone) - fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString())); + fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, paths, _assetWriter); else fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; av3.baseAnimationLayers[4].animatorController = fx; - string animationDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations"); + string animationDir = _assetWriter.ResolveFolderPath(paths.Folders.AnimGuid); GameObject[] meshArray = new GameObject[Meshes.Count]; Meshes.CopyTo(meshArray); - AnimatorManager.CreateKeyAnimations(Path.Combine(GetPackageAssetDir(), "Animations"), animationDir, meshArray); + AnimatorManager.CreateKeyAnimations(OutputPaths.Combine(GetRuntimeAssetDir(), "Animations"), paths, _assetWriter, meshArray); AnimatorManager.AddKeyLayer(fx, animationDir, _keySize, _syncSize, 3.0f); AssetDatabase.SaveAssets(); @@ -788,17 +778,17 @@ public void ChangeMaterialsInAnims(GameObject avatar, bool clone) { var av3 = avatar.GetComponent(); var fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; - string animationDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations"); + OutputPaths paths = EnsureOutputFolders(); AnimatorManager animManager = ScriptableObject.CreateInstance(); foreach (var pair in EncryptedMaterials) { Debug.LogFormat("{0}, {1}", pair.Key.name, pair.Value.name); - animManager.ChangeAnimationMaterial(fx, pair.Key, pair.Value, clone, animationDir); + animManager.ChangeAnimationMaterial(fx, pair.Key, pair.Value, clone, paths, _assetWriter); } #if MODULAR - if (Clone) + if (clone) { var maMergeAnims = avatar.GetComponentsInChildren(true); foreach (var maMergeAnim in maMergeAnims) @@ -807,7 +797,7 @@ public void ChangeMaterialsInAnims(GameObject avatar, bool clone) continue; foreach (var pair in EncryptedMaterials) { - animManager.ChangeAnimationMaterial(maMergeAnim.animator as AnimatorController, pair.Key, pair.Value, clone, animationDir); + animManager.ChangeAnimationMaterial(maMergeAnim.animator as AnimatorController, pair.Key, pair.Value, clone, paths, _assetWriter); } } } @@ -835,7 +825,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool clone) // Clone true = Manual encrypt var av3 = avatar.GetComponent(); AnimatorController fx = GetFx(avatar); - string animDir = Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations"); + OutputPaths paths = EnsureOutputFolders(); Obfuscator obfuscator = ScriptableObject.CreateInstance(); obfuscator.Clone = clone; @@ -878,7 +868,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool clone) Debug.LogErrorFormat("{0} haven't mesh", renderer.transform.name); continue; } - Mesh newMesh = obfuscator.ObfuscateBlendShapeMesh(mesh, Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString())); + Mesh newMesh = obfuscator.ObfuscateBlendShapeMesh(mesh, paths, _assetWriter); selectRenderer.sharedMesh = newMesh; ////////Change renderer component shape keys//////// @@ -928,16 +918,16 @@ public void ObfuscateBlendShape(GameObject avatar, bool clone) } } - if (Clone) + if (clone) { var maMergeAnims = avatar.GetComponentsInChildren(true); foreach (var maMergeAnim in maMergeAnims) { - obfuscator.ObfuscateBlendshapeInAnim(maMergeAnim.animator as AnimatorController, selectRenderer.gameObject, animDir); + obfuscator.ObfuscateBlendshapeInAnim(maMergeAnim.animator as AnimatorController, selectRenderer.gameObject, paths, _assetWriter); } } #endif - obfuscator.ObfuscateBlendshapeInAnim(fx, selectRenderer.gameObject, animDir); + obfuscator.ObfuscateBlendshapeInAnim(fx, selectRenderer.gameObject, paths, _assetWriter); obfuscator.ChangeObfuscatedBlendShapeInDescriptor(av3); obfuscator.Clean(); } @@ -945,6 +935,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool clone) public int GetEncryptedFoldersCount() { + _assetDir = ResolveOutputAssetDir(); if (!Directory.Exists(_assetDir)) { Debug.LogError($"The specified path does not exist: {_assetDir}"); @@ -957,18 +948,16 @@ public int GetEncryptedFoldersCount() foreach (string dir in directories) { - string folderName = Path.GetFileName(dir); - if (Regex.IsMatch(folderName, @"^-*\d+$")) - { + if (IsGeneratedOutputFolder(dir)) deletedCount++; - } } return deletedCount; } } public void CleanEncrypted() { - AssetDatabase.DeleteAsset(Path.Combine(_assetDir, "EncryptedHistory.asset")); + _assetDir = ResolveOutputAssetDir(); + AssetDatabase.DeleteAsset(OutputPaths.Combine(_assetDir, "EncryptedHistory.asset")); if (!Directory.Exists(_assetDir)) { @@ -981,12 +970,11 @@ public void CleanEncrypted() foreach (string dir in directories) { - string folderName = Path.GetFileName(dir); - if (Regex.IsMatch(folderName, @"^-*\d+$")) + if (IsGeneratedOutputFolder(dir)) { try { - AssetDatabase.DeleteAsset(dir); + AssetDatabase.DeleteAsset(OutputPaths.Normalize(dir)); deletedCount++; Debug.Log($"Deleted folder: {dir}"); } @@ -1002,6 +990,20 @@ public void CleanEncrypted() } } + bool IsGeneratedOutputFolder(string path) + { + string normalized = OutputPaths.Normalize(path); + string folderName = Path.GetFileName(normalized); + if (Regex.IsMatch(folderName, @"^-*\d+$")) + return true; + + return AssetDatabase.IsValidFolder(OutputPaths.Combine(normalized, OutputPaths.TexFolder)) || + AssetDatabase.IsValidFolder(OutputPaths.Combine(normalized, OutputPaths.MatFolder)) || + AssetDatabase.IsValidFolder(OutputPaths.Combine(normalized, OutputPaths.ShaderFolder)) || + AssetDatabase.IsValidFolder(OutputPaths.Combine(normalized, OutputPaths.AnimFolder)) || + AssetDatabase.IsValidFolder(OutputPaths.Combine(normalized, OutputPaths.MeshFolder)); + } + public int GetDefaultFilter() { return _filter; @@ -1025,11 +1027,14 @@ public Shader IsEncryptedBefore(Shader shader) { if (_history == null) { - _history = AssetDatabase.LoadAssetAtPath(Path.Combine(_assetDir, "EncryptedHistory.asset"), typeof(EncryptedHistory)) as EncryptedHistory; + _history = AssetDatabase.LoadAssetAtPath(GetOutputPaths().History(), typeof(EncryptedHistory)) as EncryptedHistory; if (_history == null) { _history = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(_history, Path.Combine(_assetDir, "EncryptedHistory.asset")); + OutputPaths paths = GetOutputPaths(); + if (paths.Folders == null) + paths.PrepareFolders(_assetWriter, false); + _assetWriter.CreateAssetInFolder(_history, paths.Folders.RootGuid, paths.HistoryName()); } } _history.LoadData(); @@ -1125,62 +1130,36 @@ bool CheckIsSupportedFormat(Material mat) } void CreateFolders() { - if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()))) - { - AssetDatabase.CreateFolder(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()); - } - else - { - if (_deleteFolders) - { - AssetDatabase.DeleteAsset(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations")); - AssetDatabase.DeleteAsset(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "mat")); - AssetDatabase.DeleteAsset(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "shader")); - AssetDatabase.DeleteAsset(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "tex")); - } - } - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - - if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "tex"))) - AssetDatabase.CreateFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()), "tex"); - if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "mat"))) - AssetDatabase.CreateFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()), "mat"); - if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "shader"))) - AssetDatabase.CreateFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()), "shader"); - if (!AssetDatabase.IsValidFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString(), "animations"))) - AssetDatabase.CreateFolder(Path.Combine(_assetDir, _descriptor.gameObject.GetInstanceID().ToString()), "animations"); - - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); + OutputPaths paths = GetOutputPaths(); + paths.PrepareFolders(_assetWriter, _deleteFolders && AssetDatabase.IsValidFolder(paths.Avatar)); } - Texture2D GenerateMipRefTexture(string outputDir, int size, bool useSmallMip) + + Texture2D GenerateMipRefTexture(string fileName, int size, bool useSmallMip) { var mip = TextureEncryptManager.GenerateRefMipmap(size, size, useSmallMip); if (mip == null) - Debug.LogErrorFormat("{0} : Can't generate mip tex{1}.", outputDir, size); + Debug.LogErrorFormat("{0} : Can't generate mip tex{1}.", fileName, size); else { - AssetDatabase.CreateAsset(mip, outputDir); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); + _assetWriter.CreateAssetInFolder(mip, GetOutputPaths().Folders.TexGuid, fileName); + _assetWriter.SaveAndRefresh(); } return mip; } - ShellProtectorProcessedTexture? GenerateEncryptedTexture(string avatarDir, Material mat, IEncryptor encryptor, byte[] keyBytes) + ProcessedTexture? GenerateEncryptedTexture(OutputPaths paths, Material mat, IEncryptor encryptor, byte[] keyBytes) { Texture2D mainTexture = (Texture2D)mat.mainTexture; - string texPath1 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt.asset"); - string texPath2 = Path.Combine(avatarDir, "tex", mainTexture.GetInstanceID() + "_encrypt2.asset"); + string texName1 = paths.EncryptedTextureName(mainTexture, 0); + string texName2 = paths.EncryptedTextureName(mainTexture, 2); bool processed = ProcessedTextures.ContainsKey(mainTexture); - ShellProtectorProcessedTexture processedTexture; + ProcessedTexture processedTexture; if (processed) processedTexture = ProcessedTextures[mainTexture]; else { - processedTexture = new ShellProtectorProcessedTexture + processedTexture = new ProcessedTexture { Encrypted = new EncryptResult(), Fallbacks = new List(), @@ -1219,9 +1198,9 @@ Texture2D GenerateMipRefTexture(string outputDir, int size, bool useSmallMip) Debug.LogErrorFormat("{0} : ArgumentException - {1}", mainTexture.name, e.Message); return null; } - AssetDatabase.CreateAsset(encryptResult.Texture1, texPath1); + _assetWriter.CreateAssetInFolder(encryptResult.Texture1, paths.Folders.TexGuid, texName1); if (encryptResult.Texture2 != null) - AssetDatabase.CreateAsset(encryptResult.Texture2, texPath2); + _assetWriter.CreateAssetInFolder(encryptResult.Texture2, paths.Folders.TexGuid, texName2); processedTexture.Encrypted = encryptResult; @@ -1230,7 +1209,7 @@ Texture2D GenerateMipRefTexture(string outputDir, int size, bool useSmallMip) return processedTexture; } - Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D mainTexture, ref ShellProtectorProcessedTexture processedTexture) + Texture2D GenerateFallbackTexture(string fileName, MatOption option, Texture2D mainTexture, ref ProcessedTexture processedTexture) { int fallbackOption = _fallback; if (option != null) @@ -1275,9 +1254,8 @@ Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D { processedTexture.Fallbacks.Add(fallback); processedTexture.FallbackOptions.Add(fallbackOption); - AssetDatabase.CreateAsset(fallback, outputDir); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); + _assetWriter.CreateAssetInFolder(fallback, GetOutputPaths().Folders.TexGuid, fileName); + _assetWriter.SaveAndRefresh(); } } else @@ -1302,62 +1280,11 @@ Texture2D GenerateFallbackTexture(string outputDir, MatOption option, Texture2D return fallback; } - Material GenerateEncryptedMaterial(string outputDir, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, ShellProtectorAuxiliaryTextures otherTex, ShellProtectorProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) + Material GenerateEncryptedMaterial(string fileName, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, AuxiliaryTextures otherTex, ProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) { - Material newMat = new Material(mat.shader); - newMat.CopyPropertiesFromMaterial(mat); - newMat.shader = encryptedShader; - var originalTex = (Texture2D)newMat.mainTexture; - newMat.mainTexture = fallback; - - Texture2D encryptedTex0 = processedTexture.Encrypted.Texture1; - Texture2D encryptedTex1 = processedTexture.Encrypted.Texture2; - - newMat.SetTexture(ShellProtectorShaderProperties.MipTexture, mip); - - if (encryptedTex0 != null) - newMat.SetTexture(ShellProtectorShaderProperties.EncryptTexture0, encryptedTex0); - if (encryptedTex1 != null) - newMat.SetTexture(ShellProtectorShaderProperties.EncryptTexture1, encryptedTex1); - - newMat.renderQueue = mat.renderQueue; - if (_turnOnAllSafetyFallback) - newMat.SetOverrideTag("VRCFallback", "Unlit"); - - var (woffset, hoffset) = TextureEncryptManager.CalculateOffsets(originalTex); - newMat.SetInteger(ShellProtectorShaderProperties.WidthOffset, woffset); - newMat.SetInteger(ShellProtectorShaderProperties.HeightOffset, hoffset); - for (int i = 0; i < keyBytes.Length; ++i) - newMat.SetFloat(ShellProtectorShaderProperties.KeyPrefix + i, keyBytes[i]); - - if (_algorithm == (int)Algorithm.Chacha) - { - Chacha20 chacha = encryptor as Chacha20; - newMat.SetInteger(ShellProtectorShaderProperties.Nonce0, (int)chacha.GetNonceUint3()[0]); - newMat.SetInteger(ShellProtectorShaderProperties.Nonce1, (int)chacha.GetNonceUint3()[1]); - newMat.SetInteger(ShellProtectorShaderProperties.Nonce2, (int)chacha.GetNonceUint3()[2]); - } - else if (_algorithm == (int)Algorithm.Xxtea) - { - newMat.SetInteger(ShellProtectorShaderProperties.Rounds, (int)_rounds); - } - - var key = new byte[16]; - for (int i = 0; i < 16; i++) - key[i] = keyBytes[i]; - - uint hashMagic = (uint)mat.GetInstanceID(); - - var hash = KeyGenerator.SimpleHash(key, hashMagic); - newMat.SetInteger(ShellProtectorShaderProperties.HashMagic, (int)hashMagic); - newMat.SetInteger(ShellProtectorShaderProperties.PasswordHash, (int)hash); - - _injector.SetKeywords(newMat, otherTex.LimTexture != null); - - AssetDatabase.CreateAsset(newMat, outputDir); + MaterialEncryptor materialEncryptor = new MaterialEncryptor(_assetWriter, _turnOnAllSafetyFallback, _algorithm, _rounds); + Material newMat = materialEncryptor.CreateEncryptedMaterial(GetOutputPaths().Folders.MatGuid, fileName, mat, encryptedShader, fallback, mip, otherTex, processedTexture, keyBytes, encryptor, _injector); Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(newMat)); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); if (!EncryptedMaterials.ContainsKey(mat)) EncryptedMaterials.Add(mat, newMat); diff --git a/Runtime/Scripts/ShellProtectorTester.cs b/Runtime/Scripts/ShellProtectorTester.cs index 549d400..df4c523 100644 --- a/Runtime/Scripts/ShellProtectorTester.cs +++ b/Runtime/Scripts/ShellProtectorTester.cs @@ -45,7 +45,7 @@ public void CheckEncryption() if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { for (int i = 0; i < 16; ++i) - mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, passwordBytes[i]); + mat.SetInt(ShaderProperties.KeyPrefix + i, passwordBytes[i]); } } } @@ -68,7 +68,7 @@ public void CheckEncryption() if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { for (int i = 0; i < 16; ++i) - mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, passwordBytes[i]); + mat.SetInt(ShaderProperties.KeyPrefix + i, passwordBytes[i]); } } } @@ -92,7 +92,7 @@ public void ResetEncryption() if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { for (int i = 16 - UserKeyLength; i < 16; ++i) - mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, 0); + mat.SetInt(ShaderProperties.KeyPrefix + i, 0); } } } @@ -112,7 +112,7 @@ public void ResetEncryption() if (mat.name.Contains("_encrypted") || mat.name.Contains("_duplicated")) { for (int i = 16 - UserKeyLength; i < 16; ++i) - mat.SetInt(ShellProtectorShaderProperties.KeyPrefix + i, 0); + mat.SetInt(ShaderProperties.KeyPrefix + i, 0); } } } diff --git a/Runtime/Shader/ShellProtector.cginc b/Runtime/Shader/Protector.cginc similarity index 100% rename from Runtime/Shader/ShellProtector.cginc rename to Runtime/Shader/Protector.cginc diff --git a/Runtime/Shader/ShellProtector.cginc.meta b/Runtime/Shader/Protector.cginc.meta similarity index 100% rename from Runtime/Shader/ShellProtector.cginc.meta rename to Runtime/Shader/Protector.cginc.meta diff --git a/Runtime/liltoonProtector/Shaders/custom_insert.hlsl b/Runtime/liltoonProtector/Shaders/custom_insert.hlsl index 1fd566c..a9ab7b1 100644 --- a/Runtime/liltoonProtector/Shaders/custom_insert.hlsl +++ b/Runtime/liltoonProtector/Shaders/custom_insert.hlsl @@ -1,2 +1,2 @@ -#include "../../Shader/ShellProtector.cginc" -#include "UnityCG.cginc" \ No newline at end of file +#include "../../Shader/Protector.cginc" +#include "UnityCG.cginc" diff --git a/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs b/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs index 95fb709..4344806 100644 --- a/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs +++ b/Tests/Editor/Gpu/GpuTextureDecryptionTests.cs @@ -16,7 +16,7 @@ public class GpuTextureDecryptionTests [SetUp] public void SetUp() { - Shader shader = Shader.Find("Hidden/ShellProtectorGpuDecryptTest"); + Shader shader = Shader.Find("Hidden/GpuDecryptTest"); Assert.That(shader, Is.Not.Null, "GPU decrypt test shader was not imported."); Assert.That(shader.isSupported, Is.True, "GPU decrypt test shader is not supported or failed to compile."); diff --git a/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader b/Tests/Editor/Gpu/Shaders/Hidden/GpuDecryptTest.shader similarity index 95% rename from Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader rename to Tests/Editor/Gpu/Shaders/Hidden/GpuDecryptTest.shader index aa9e01f..c702905 100644 --- a/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader +++ b/Tests/Editor/Gpu/Shaders/Hidden/GpuDecryptTest.shader @@ -1,4 +1,4 @@ -Shader "Hidden/ShellProtectorGpuDecryptTest" +Shader "Hidden/GpuDecryptTest" { Properties { @@ -79,7 +79,7 @@ Shader "Hidden/ShellProtectorGpuDecryptTest" half4 _EncryptTex0_TexelSize; int _PasswordHash; - #include "../../../../../Runtime/Shader/ShellProtector.cginc" + #include "../../../../../Runtime/Shader/Protector.cginc" half4 frag(v2f_img i) : SV_Target { @@ -108,7 +108,7 @@ Shader "Hidden/ShellProtectorGpuDecryptTest" half4 _EncryptTex0_TexelSize; int _PasswordHash; - #include "../../../../../Runtime/Shader/ShellProtector.cginc" + #include "../../../../../Runtime/Shader/Protector.cginc" half4 frag(v2f_img i) : SV_Target { diff --git a/Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader.meta b/Tests/Editor/Gpu/Shaders/Hidden/GpuDecryptTest.shader.meta similarity index 100% rename from Tests/Editor/Gpu/Shaders/Hidden/ShellProtectorGpuDecryptTest.shader.meta rename to Tests/Editor/Gpu/Shaders/Hidden/GpuDecryptTest.shader.meta diff --git a/Tests/Editor/Integration/ShellProtectorPipelineTests.cs b/Tests/Editor/Integration/PipelineTests.cs similarity index 81% rename from Tests/Editor/Integration/ShellProtectorPipelineTests.cs rename to Tests/Editor/Integration/PipelineTests.cs index e1cd30b..e9a409a 100644 --- a/Tests/Editor/Integration/ShellProtectorPipelineTests.cs +++ b/Tests/Editor/Integration/PipelineTests.cs @@ -12,7 +12,7 @@ namespace Shell.Protector.Tests.Integration { - public class ShellProtectorPipelineTests + public class PipelineTests { private readonly List sceneObjects = new List(); @@ -27,6 +27,7 @@ public void TearDown() { TestAssetScope.DestroyObjects(sceneObjects); TestAssetScope.DeleteGeneratedRoot(); + TestAssetScope.DeleteDefaultGeneratedRoot(); } [Test] @@ -45,6 +46,7 @@ public void ManualEncrypt_CreatesEncryptedAvatarAndRewritesProtectionAssets() Assert.That(encryptedAvatar.GetComponentInChildren(true), Is.Not.Null); AssertEncryptedRenderer(encryptedAvatar, fixture.Material); + AssertGeneratedPaths(encryptedAvatar, fixture.Material, TestAssetScope.GeneratedRoot); AssertFxController(encryptedAvatar); AssertExpressionParameters(encryptedAvatar); AssertBlendShapeWasObfuscated(encryptedAvatar); @@ -70,13 +72,27 @@ public void InPlaceEncrypt_RewritesOriginalAvatarWhenNdmfStyleStepsRun() Assert.That(avatar.GetComponentInChildren(true), Is.Null); AssertEncryptedRenderer(avatar, fixture.Material); + AssertGeneratedPaths(avatar, fixture.Material, TestAssetScope.GeneratedRoot); AssertFxController(avatar); AssertExpressionParameters(avatar); AssertBlendShapeWasObfuscated(avatar); AssertAnimationMaterialWasRewritten(avatar, fixture.Material); } - private Fixture CreateFixture(string name) + [Test] + public void DefaultAssetDir_UsesGeneratedRootAndFolderGuids() + { + Fixture fixture = CreateFixture("Default", null); + + GameObject encryptedAvatar = fixture.Protector.Encrypt(false); + sceneObjects.Add(encryptedAvatar); + + string avatarName = encryptedAvatar.name.Replace("_encrypted", ""); + AssertGeneratedPaths(encryptedAvatar, fixture.Material, TestAssetScope.DefaultGeneratedRoot); + AssertOutputFoldersHaveGuids(TestAssetScope.DefaultGeneratedRoot, avatarName); + } + + private Fixture CreateFixture(string name, string assetDir = TestAssetScope.GeneratedRoot) { Texture2D texture = TestAssetScope.CreatePatternTexture(128, 128, TextureFormat.RGBA32, true); texture.name = name + "Texture"; @@ -125,7 +141,8 @@ private Fixture CreateFixture(string name) ShellProtector protector = avatar.AddComponent(); protector.Descriptor = descriptor; - protector.AssetDir = TestAssetScope.GeneratedRoot; + if (assetDir != null) + protector.AssetDir = assetDir; SetSerializedField(protector, "_gameObjectList", new List { avatar }); SetSerializedField(protector, "_algorithm", 1); SetSerializedField(protector, "_filter", 0); @@ -200,6 +217,36 @@ private static void AssertEncryptedRenderer(GameObject avatar, Material original Assert.That(AssetDatabase.GetAssetPath(encryptedMaterial), Does.StartWith(TestAssetScope.GeneratedRoot)); } + private static void AssertGeneratedPaths(GameObject avatar, Material originalMaterial, string expectedRoot) + { + string avatarRoot = expectedRoot + "/" + avatar.name.Replace("_encrypted", ""); + Material encryptedMaterial = avatar.transform.Find("Body").GetComponent().sharedMaterial; + Texture2D encryptedTexture = encryptedMaterial.GetTexture("_EncryptTex0") as Texture2D; + Texture2D mipTexture = encryptedMaterial.GetTexture("_MipTex") as Texture2D; + string materialPath = AssetDatabase.GetAssetPath(encryptedMaterial).Replace('\\', '/'); + string texturePath = AssetDatabase.GetAssetPath(encryptedTexture).Replace('\\', '/'); + string mipPath = AssetDatabase.GetAssetPath(mipTexture).Replace('\\', '/'); + + Assert.That(materialPath, Does.StartWith(avatarRoot + "/Mat/")); + Assert.That(texturePath, Does.StartWith(avatarRoot + "/Tex/")); + Assert.That(mipPath, Does.StartWith(avatarRoot + "/Tex/")); + Assert.That(materialPath, Does.Contain(originalMaterial.name)); + Assert.That(materialPath, Does.Not.Contain(originalMaterial.GetInstanceID().ToString())); + Assert.That(materialPath, Does.Not.StartWith("Assets/ShellProtector/Runtime/")); + } + + private static void AssertOutputFoldersHaveGuids(string root, string avatarName) + { + string avatarRoot = root + "/" + avatarName; + foreach (string folderName in new[] { "Tex", "Mat", "Shader", "Anim", "Mesh" }) + { + string folderPath = avatarRoot + "/" + folderName; + string guid = AssetDatabase.AssetPathToGUID(folderPath); + Assert.That(guid, Is.Not.Empty, folderPath); + Assert.That(AssetDatabase.GUIDToAssetPath(guid).Replace('\\', '/'), Is.EqualTo(folderPath)); + } + } + private static void AssertFxController(GameObject avatar) { AnimatorController fx = ShellProtector.GetFx(avatar); diff --git a/Tests/Editor/Integration/ShellProtectorPipelineTests.cs.meta b/Tests/Editor/Integration/PipelineTests.cs.meta similarity index 100% rename from Tests/Editor/Integration/ShellProtectorPipelineTests.cs.meta rename to Tests/Editor/Integration/PipelineTests.cs.meta diff --git a/Tests/Editor/Shared/TestAssetScope.cs b/Tests/Editor/Shared/TestAssetScope.cs index 0c653a9..e1068d7 100644 --- a/Tests/Editor/Shared/TestAssetScope.cs +++ b/Tests/Editor/Shared/TestAssetScope.cs @@ -10,10 +10,12 @@ namespace Shell.Protector.Tests internal static class TestAssetScope { public const string GeneratedRoot = "Assets/ShellProtector/Tests/__Generated"; + public const string DefaultGeneratedRoot = "Assets/ShellProtector/Generated"; public static void Reset() { DeleteGeneratedRoot(); + DeleteDefaultGeneratedRoot(); EnsureFolder(GeneratedRoot); } @@ -26,6 +28,15 @@ public static void DeleteGeneratedRoot() } } + public static void DeleteDefaultGeneratedRoot() + { + if (AssetDatabase.IsValidFolder(DefaultGeneratedRoot)) + { + AssetDatabase.DeleteAsset(DefaultGeneratedRoot); + AssetDatabase.Refresh(); + } + } + public static void EnsureFolder(string assetPath) { string normalized = assetPath.Replace('\\', '/').TrimEnd('/'); From 6fa13fbb67aa75fa08c065d839fda452fc8883bf Mon Sep 17 00:00:00 2001 From: Lee Sangyeop Date: Thu, 9 Jul 2026 20:07:51 +0900 Subject: [PATCH 40/59] fix: harden shell protector workflow --- Editor/ShellProtectorEditor.cs | 26 ++- Runtime/Scripts/AnimatorManager.cs | 47 +++--- Runtime/Scripts/KeyGenerator.cs | 12 +- Runtime/Scripts/Pipeline/BuildContext.cs | 41 ----- Runtime/Scripts/Pipeline/BuildContext.cs.meta | 11 -- Runtime/Scripts/Pipeline/BuildResult.cs | 16 ++ Runtime/Scripts/ShellProtector.cs | 148 ++++++++---------- Runtime/Scripts/TextureEncryptManager.cs | 4 +- Tests/Editor/Integration/PipelineTests.cs | 18 +++ Tests/Editor/Unit/CryptoTests.cs | 12 ++ .../Editor/Unit/TextureEncryptManagerTests.cs | 17 ++ 11 files changed, 176 insertions(+), 176 deletions(-) delete mode 100644 Runtime/Scripts/Pipeline/BuildContext.cs delete mode 100644 Runtime/Scripts/Pipeline/BuildContext.cs.meta diff --git a/Editor/ShellProtectorEditor.cs b/Editor/ShellProtectorEditor.cs index bccebe9..993ac74 100644 --- a/Editor/ShellProtectorEditor.cs +++ b/Editor/ShellProtectorEditor.cs @@ -1,13 +1,10 @@ #if UNITY_EDITOR -using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditorInternal; -using System.Text; using System; using System.IO; -using System.Text.RegularExpressions; using VRC.SDK3.Avatars.Components; namespace Shell.Protector @@ -47,13 +44,10 @@ public class ShellProtectorEditor : Editor readonly string[] keyLengthLabels = new string[5]; List shaders = new List(); + readonly List debugTextures = new List(); bool showPassword = false; - Texture2D tex; - - string githubVersion; - private string Lang(string word) { if (root == null) @@ -81,12 +75,11 @@ void OnEnable() EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none); }; - textureList = new ReorderableList(serializedObject, serializedObject.FindProperty("textureList"), true, true, true, true); + textureList = new ReorderableList(debugTextures, typeof(Texture2D), true, true, true, true); textureList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, Lang("Texture List")); textureList.drawElementCallback = (rect, index, is_active, is_focused) => { - SerializedProperty element = textureList.serializedProperty.GetArrayElementAtIndex(index); - EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none); + debugTextures[index] = EditorGUI.ObjectField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), debugTextures[index], typeof(Texture2D), false) as Texture2D; }; obfuscationList = new ReorderableList(serializedObject, serializedObject.FindProperty("_obfuscationRenderers"), true, true, true, true); @@ -412,14 +405,17 @@ public override void OnInspectorGUI() if (GUILayout.Button(Lang("Encrypt"))) { Texture2D last = null; - for (int i = 0; i < textureList.count; i++) + for (int i = 0; i < debugTextures.Count; i++) { - SerializedProperty element = textureList.serializedProperty.GetArrayElementAtIndex(i); - Texture2D texture = element.objectReferenceValue as Texture2D; + Texture2D texture = debugTextures[i]; + if (texture == null) + continue; TextureSettings.SetRWEnableTexture(texture); var result = TextureEncryptManager.EncryptTexture(texture, KeyGenerator.MakeKeyBytes(root.FixedPassword, root.UserPassword, keySize.intValue), new XXTEA()); + if (result.Texture1 == null) + continue; last = result.Texture1; @@ -428,9 +424,11 @@ public override void OnInspectorGUI() outputPaths.PrepareFolders(writer, false); writer.CreateAssetInFolder(result.Texture1, outputPaths.Folders.TexGuid, outputPaths.EncryptedTextureName(texture, 0)); - File.WriteAllBytes(writer.UniquePathInFolder(outputPaths.Folders.TexGuid, OutputPaths.Sanitize(texture.name) + "_encrypt.png"), result.Texture2.EncodeToPNG()); if (result.Texture2 != null) + { + File.WriteAllBytes(writer.UniquePathInFolder(outputPaths.Folders.TexGuid, OutputPaths.Sanitize(texture.name) + "_encrypt.png"), result.Texture2.EncodeToPNG()); writer.CreateAssetInFolder(result.Texture2, outputPaths.Folders.TexGuid, outputPaths.EncryptedTextureName(texture, 2)); + } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 8b56e0c..6714c06 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -1,5 +1,4 @@ #if UNITY_EDITOR -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -154,47 +153,59 @@ private static void AddTransition(AnimatorStateTransition transition, int keyLen private static void AddParameters(AnimatorController anim, int keyLength, int syncSize) { bool bLegacy = syncSize == 1; - anim.AddParameter(new AnimatorControllerParameter + AddParameterIfMissing(anim, new AnimatorControllerParameter { defaultFloat = 1.0f, name = "key_weight", type = AnimatorControllerParameterType.Float }); - if (anim.parameters.All(p => p.name != ParameterManager.GetIsLocalName())) + AddParameterIfMissing(anim, new AnimatorControllerParameter { - anim.AddParameter(new AnimatorControllerParameter - { - defaultBool = false, - name = ParameterManager.GetIsLocalName(), - type = AnimatorControllerParameterType.Bool - }); - } + defaultBool = false, + name = ParameterManager.GetIsLocalName(), + type = AnimatorControllerParameterType.Bool + }); for (var i = 0; i < keyLength; ++i) - anim.AddParameter(ParameterManager.GetKeyName(i), AnimatorControllerParameterType.Float); + AddParameterIfMissing(anim, ParameterManager.GetKeyName(i), AnimatorControllerParameterType.Float); - anim.AddParameter(ParameterManager.GetSyncLockName(bLegacy), AnimatorControllerParameterType.Bool); + AddParameterIfMissing(anim, ParameterManager.GetSyncLockName(bLegacy), AnimatorControllerParameterType.Bool); var switchCount = ShellProtector.GetRequiredSwitchCount(keyLength, syncSize); if (!bLegacy) { for (var i = 0; i < keyLength; ++i) - anim.AddParameter(ParameterManager.GetSavedKeyName(i), AnimatorControllerParameterType.Float); + AddParameterIfMissing(anim, ParameterManager.GetSavedKeyName(i), AnimatorControllerParameterType.Float); } for (var i = 0; i < syncSize; ++i) - anim.AddParameter(ParameterManager.GetSyncedKeyName(i, bLegacy), AnimatorControllerParameterType.Float); + AddParameterIfMissing(anim, ParameterManager.GetSyncedKeyName(i, bLegacy), AnimatorControllerParameterType.Float); for (var i = 0; i < switchCount; ++i) - anim.AddParameter(ParameterManager.GetSyncSwitchName(i, bLegacy), AnimatorControllerParameterType.Bool); + AddParameterIfMissing(anim, ParameterManager.GetSyncSwitchName(i, bLegacy), AnimatorControllerParameterType.Bool); } - public static void AddKeyLayer(AnimatorController anim, string animationDir, int keyLength, int syncSize, float speed) + private static void AddParameterIfMissing(AnimatorController anim, string name, AnimatorControllerParameterType type) { - bool bLegacy = syncSize == 1; - AddParameters(anim, keyLength, syncSize); + if (anim.parameters.Any(p => p.name == name)) + return; + + anim.AddParameter(name, type); + } + private static void AddParameterIfMissing(AnimatorController anim, AnimatorControllerParameter parameter) + { + if (anim.parameters.Any(p => p.name == parameter.name)) + return; + + anim.AddParameter(parameter); + } + + public static void AddKeyLayer(AnimatorController anim, string animationDir, int keyLength, int syncSize, float speed) + { if (anim.layers.Any(l => l.name == "ShellProtector")) return; + AddParameters(anim, keyLength, syncSize); + AddMuxLayer(anim, keyLength, syncSize, 0.15f, 0.1f, 1f); // 10hz AddDemuxLayer(anim, keyLength, syncSize); diff --git a/Runtime/Scripts/KeyGenerator.cs b/Runtime/Scripts/KeyGenerator.cs index 08772df..42b8442 100644 --- a/Runtime/Scripts/KeyGenerator.cs +++ b/Runtime/Scripts/KeyGenerator.cs @@ -62,11 +62,15 @@ public static string GenerateRandomString(int length) const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+=|\\/?.>,<~`\'\" "; StringBuilder builder = new StringBuilder(); - System.Random random = new System.Random(); - for (int i = 0; i < length; i++) + using (RandomNumberGenerator random = RandomNumberGenerator.Create()) { - int index = random.Next(chars.Length); - builder.Append(chars[index]); + byte[] buffer = new byte[4]; + for (int i = 0; i < length; i++) + { + random.GetBytes(buffer); + int index = (int)(BitConverter.ToUInt32(buffer, 0) % chars.Length); + builder.Append(chars[index]); + } } return builder.ToString(); diff --git a/Runtime/Scripts/Pipeline/BuildContext.cs b/Runtime/Scripts/Pipeline/BuildContext.cs deleted file mode 100644 index f473e94..0000000 --- a/Runtime/Scripts/Pipeline/BuildContext.cs +++ /dev/null @@ -1,41 +0,0 @@ -#if UNITY_EDITOR -using System.Collections.Generic; -using UnityEngine; - -namespace Shell.Protector -{ - public struct ProcessedTexture - { - public EncryptResult Encrypted; - public List Fallbacks; - public List FallbackOptions; - public byte[] Nonce; - } - - public struct AuxiliaryTextures - { - public Texture2D LimTexture; - public Texture2D LimTexture2; - public Texture2D OutlineTexture; - public Texture2D LimShadeTexture; - } - - public sealed class BuildContext - { - public BuildContext(BuildRequest request, BuildSettings settings) - { - Request = request; - Settings = settings; - Result = new BuildResult(); - } - - public BuildRequest Request { get; } - public BuildSettings Settings { get; } - public BuildResult Result { get; } - public IEncryptor Encryptor { get; set; } - public EncryptedHistory History { get; set; } - public Texture2D FallbackWhite { get; set; } - public Texture2D FallbackBlack { get; set; } - } -} -#endif diff --git a/Runtime/Scripts/Pipeline/BuildContext.cs.meta b/Runtime/Scripts/Pipeline/BuildContext.cs.meta deleted file mode 100644 index 10cc8e3..0000000 --- a/Runtime/Scripts/Pipeline/BuildContext.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0967a630bc624e6499905c432cf46547 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/Scripts/Pipeline/BuildResult.cs b/Runtime/Scripts/Pipeline/BuildResult.cs index 759a237..62138f3 100644 --- a/Runtime/Scripts/Pipeline/BuildResult.cs +++ b/Runtime/Scripts/Pipeline/BuildResult.cs @@ -4,6 +4,22 @@ namespace Shell.Protector { + public struct ProcessedTexture + { + public EncryptResult Encrypted; + public List Fallbacks; + public List FallbackOptions; + public byte[] Nonce; + } + + public struct AuxiliaryTextures + { + public Texture2D LimTexture; + public Texture2D LimTexture2; + public Texture2D OutlineTexture; + public Texture2D LimShadeTexture; + } + public sealed class BuildResult { public GameObject Avatar { get; set; } diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 2712947..8ba5a31 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -648,103 +648,79 @@ public void RemoveDuplicatedTextures(GameObject avatar) } } // Encrypted materials loop - var renderers = avatar.GetComponentsInChildren(true); - if (renderers != null) + var duplicatedMaterials = new Dictionary(); + bool changedFallbackMaterials = ReplaceProcessedTexturesWithFallbacks(avatar.GetComponentsInChildren(true), paths, duplicatedMaterials); + changedFallbackMaterials |= ReplaceProcessedTexturesWithFallbacks(avatar.GetComponentsInChildren(true), paths, duplicatedMaterials); + if (changedFallbackMaterials) + _assetWriter.SaveAndRefresh(); + } + + bool ReplaceProcessedTexturesWithFallbacks(IEnumerable renderers, OutputPaths paths, Dictionary duplicatedMaterials) where T : Renderer + { + bool changed = false; + foreach (T renderer in renderers) { - for (int i = 0; i < renderers.Length; ++i) + Material[] mats = renderer.sharedMaterials; + if (mats == null) + continue; + + bool rendererChanged = false; + for (int i = 0; i < mats.Length; ++i) { - var mats = renderers[i].sharedMaterials; - if (mats == null) + Material sourceMaterial = mats[i]; + if (sourceMaterial == null) continue; - for (int j = 0; j < mats.Length; ++j) + + Material duplicatedMaterial = null; + foreach (string name in sourceMaterial.GetTexturePropertyNames()) { - if (mats[j] == null) + Texture2D texture = sourceMaterial.GetTexture(name) as Texture2D; + if (texture == null || !ProcessedTextures.TryGetValue(texture, out ProcessedTexture processedTexture)) continue; - Material tmp = null; - foreach (var name in mats[j].GetTexturePropertyNames()) - { - if (mats[j].GetTexture(name) == null) - continue; - if (!(mats[j].GetTexture(name) is Texture2D)) - continue; - Texture2D tex = (Texture2D)mats[j].GetTexture(name); - if (ProcessedTextures.ContainsKey(tex)) - { - int idx = ProcessedTextures[tex].FallbackOptions.IndexOf(ProcessedTextures[tex].FallbackOptions.Max()); - Texture2D bigFallbackTexture = ProcessedTextures[tex].Fallbacks[idx]; - if (tmp == null) - { - string duplicatedPath = OutputPaths.Combine(_assetWriter.ResolveFolderPath(paths.Folders.MatGuid), paths.DuplicatedMaterialName(mats[j])); - Material mat = AssetDatabase.LoadAssetAtPath(duplicatedPath); - if (mat == null) - { - tmp = Instantiate(mats[j]); - _assetWriter.CreateAssetInFolder(tmp, paths.Folders.MatGuid, paths.DuplicatedMaterialName(mats[j])); - AssetDatabase.SaveAssets(); - } - else - tmp = mat; - } - tmp.SetTexture(name, bigFallbackTexture); - } - } - AssetDatabase.Refresh(); - if (tmp != null) - mats[j] = tmp; + if (duplicatedMaterial == null) + duplicatedMaterial = GetOrCreateDuplicatedMaterial(sourceMaterial, paths, duplicatedMaterials); + + duplicatedMaterial.SetTexture(name, GetLargestFallback(processedTexture)); + EditorUtility.SetDirty(duplicatedMaterial); + changed = true; } - renderers[i].sharedMaterials = mats; - } - } - var skinnedRenderers = avatar.GetComponentsInChildren(true); - if (skinnedRenderers != null) - { - for (int i = 0; i < skinnedRenderers.Length; ++i) - { - var mats = skinnedRenderers[i].sharedMaterials; - if (mats == null) - continue; - for (int j = 0; j < mats.Length; ++j) - { - if (mats[j] == null) - continue; - Material tmp = null; - foreach (var name in mats[j].GetTexturePropertyNames()) - { - if (mats[j].GetTexture(name) == null) - continue; - if (!(mats[j].GetTexture(name) is Texture2D)) - continue; - - if (ProcessedTextures.ContainsKey((Texture2D)mats[j].GetTexture(name))) - { - Texture2D mainTex = (Texture2D)mats[j].GetTexture(name); - int idx = ProcessedTextures[mainTex].FallbackOptions.IndexOf(ProcessedTextures[mainTex].FallbackOptions.Max()); - Texture2D bigFallbackTexture = ProcessedTextures[mainTex].Fallbacks[idx]; - if (tmp == null) - { - string duplicatedPath = OutputPaths.Combine(_assetWriter.ResolveFolderPath(paths.Folders.MatGuid), paths.DuplicatedMaterialName(mats[j])); - Material mat = AssetDatabase.LoadAssetAtPath(duplicatedPath); - if (mat == null) - { - tmp = Instantiate(mats[j]); - _assetWriter.CreateAssetInFolder(tmp, paths.Folders.MatGuid, paths.DuplicatedMaterialName(mats[j])); - AssetDatabase.SaveAssets(); - } - else - tmp = mat; - } - tmp.SetTexture(name, bigFallbackTexture); - } - } - AssetDatabase.Refresh(); - if (tmp != null) - mats[j] = tmp; + if (duplicatedMaterial != null) + { + mats[i] = duplicatedMaterial; + rendererChanged = true; } - skinnedRenderers[i].sharedMaterials = mats; } + + if (rendererChanged) + renderer.sharedMaterials = mats; + } + + return changed; + } + + Material GetOrCreateDuplicatedMaterial(Material source, OutputPaths paths, Dictionary duplicatedMaterials) + { + if (duplicatedMaterials.TryGetValue(source, out Material duplicatedMaterial)) + return duplicatedMaterial; + + string duplicatedPath = OutputPaths.Combine(_assetWriter.ResolveFolderPath(paths.Folders.MatGuid), paths.DuplicatedMaterialName(source)); + duplicatedMaterial = AssetDatabase.LoadAssetAtPath(duplicatedPath); + if (duplicatedMaterial == null) + { + duplicatedMaterial = Instantiate(source); + _assetWriter.CreateAssetInFolder(duplicatedMaterial, paths.Folders.MatGuid, paths.DuplicatedMaterialName(source)); } + + duplicatedMaterials[source] = duplicatedMaterial; + return duplicatedMaterial; + } + + static Texture2D GetLargestFallback(ProcessedTexture processedTexture) + { + int idx = processedTexture.FallbackOptions.IndexOf(processedTexture.FallbackOptions.Max()); + return processedTexture.Fallbacks[idx]; } public void SetAnimations(GameObject avatar, bool clone) diff --git a/Runtime/Scripts/TextureEncryptManager.cs b/Runtime/Scripts/TextureEncryptManager.cs index 26f0f04..f6b7a4c 100644 --- a/Runtime/Scripts/TextureEncryptManager.cs +++ b/Runtime/Scripts/TextureEncryptManager.cs @@ -118,7 +118,7 @@ private static ITextureFormat GetFormat(Material material) public static EncryptResult EncryptTexture(Texture2D texture, byte[] key, IEncryptor encryptor) { - if (texture.width % 2 != 0 && texture.height % 2 != 0) + if (texture.width % 2 != 0 || texture.height % 2 != 0) { Debug.LogErrorFormat("{0} : The texture size must be a multiple of 2!", texture.name); return new EncryptResult(); @@ -165,4 +165,4 @@ public static (int, int) CalculateOffsets(Texture2D texture) } } -#endif \ No newline at end of file +#endif diff --git a/Tests/Editor/Integration/PipelineTests.cs b/Tests/Editor/Integration/PipelineTests.cs index e9a409a..16fb9e1 100644 --- a/Tests/Editor/Integration/PipelineTests.cs +++ b/Tests/Editor/Integration/PipelineTests.cs @@ -92,6 +92,24 @@ public void DefaultAssetDir_UsesGeneratedRootAndFolderGuids() AssertOutputFoldersHaveGuids(TestAssetScope.DefaultGeneratedRoot, avatarName); } + [Test] + public void AddKeyLayer_DoesNotDuplicateShellProtectorParametersOrLayers() + { + string controllerDir = TestAssetScope.GeneratedRoot + "/Repeat"; + TestAssetScope.EnsureFolder(controllerDir); + AnimatorController controller = AnimatorController.CreateAnimatorControllerAtPath(controllerDir + "/fx.controller"); + string animationDir = "Assets/ShellProtector/Runtime/Animations"; + + AnimatorManager.AddKeyLayer(controller, animationDir, 4, 1, 3.0f); + AnimatorManager.AddKeyLayer(controller, animationDir, 4, 1, 3.0f); + + Assert.That(controller.layers.Count(l => l.name == "ShellProtector"), Is.EqualTo(1)); + Assert.That(controller.parameters.Count(p => p.name == "key_weight"), Is.EqualTo(1)); + Assert.That(controller.parameters.Count(p => p.name == ParameterManager.GetKeyName(0)), Is.EqualTo(1)); + Assert.That(controller.parameters.Count(p => p.name == ParameterManager.GetSyncLockName(true)), Is.EqualTo(1)); + Assert.That(controller.parameters.Count(p => p.name == ParameterManager.GetSyncSwitchName(0, true)), Is.EqualTo(1)); + } + private Fixture CreateFixture(string name, string assetDir = TestAssetScope.GeneratedRoot) { Texture2D texture = TestAssetScope.CreatePatternTexture(128, 128, TextureFormat.RGBA32, true); diff --git a/Tests/Editor/Unit/CryptoTests.cs b/Tests/Editor/Unit/CryptoTests.cs index 86f8d8d..ffb13c1 100644 --- a/Tests/Editor/Unit/CryptoTests.cs +++ b/Tests/Editor/Unit/CryptoTests.cs @@ -32,6 +32,18 @@ public void SimpleHash_ReturnsStableHash() Assert.That(KeyGenerator.SimpleHash(key, 0x12345678u), Is.EqualTo(0x94f301a9u)); } + [Test] + public void GenerateRandomString_UsesRequestedLengthAndAllowedCharacters() + { + const string allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+=|\\/?.>,<~`\'\" "; + + string value = KeyGenerator.GenerateRandomString(64); + + Assert.That(value, Has.Length.EqualTo(64)); + Assert.That(value.All(c => allowedChars.IndexOf(c) >= 0), Is.True); + Assert.That(KeyGenerator.GenerateRandomString(0), Is.Empty); + } + [Test] public void Xxtea_EncryptsStableVector_AndRoundTrips() { diff --git a/Tests/Editor/Unit/TextureEncryptManagerTests.cs b/Tests/Editor/Unit/TextureEncryptManagerTests.cs index 0a84113..c586d74 100644 --- a/Tests/Editor/Unit/TextureEncryptManagerTests.cs +++ b/Tests/Editor/Unit/TextureEncryptManagerTests.cs @@ -1,6 +1,7 @@ #if UNITY_EDITOR using NUnit.Framework; using UnityEngine; +using UnityEngine.TestTools; namespace Shell.Protector.Tests.Unit { @@ -78,6 +79,22 @@ public void EncryptTextureCreatesExpectedResultTextures(TextureFormat sourceForm Assert.That(result.Texture2, Is.Null); } } + + [TestCase(15, 16)] + [TestCase(16, 15)] + public void EncryptTextureRejectsTextureWhenEitherDimensionIsOdd(int width, int height) + { + Texture2D texture = TestAssetScope.CreatePatternTexture(width, height, TextureFormat.RGBA32, true); + texture.name = "OddTexture"; + XXTEA xxtea = new XXTEA { Rounds = 20 }; + byte[] key = KeyGenerator.MakeKeyBytes("password", "pass", 12); + + LogAssert.Expect(LogType.Error, "OddTexture : The texture size must be a multiple of 2!"); + EncryptResult result = TextureEncryptManager.EncryptTexture(texture, key, xxtea); + + Assert.That(result.Texture1, Is.Null); + Assert.That(result.Texture2, Is.Null); + } } } #endif From 0574b61f9b30277acf786b8badaf9633a572ad43 Mon Sep 17 00:00:00 2001 From: Lee Sangyeop Date: Thu, 9 Jul 2026 20:59:34 +0900 Subject: [PATCH 41/59] test: add supported shader rendering smoke tests --- Runtime/Scripts/Pipeline/MaterialEncryptor.cs | 1 + Runtime/Scripts/TextureEncryptManager.cs | 7 + .../SupportedShaderRenderingTests.cs | 490 ++++++++++++++++++ .../SupportedShaderRenderingTests.cs.meta | 11 + 4 files changed, 509 insertions(+) create mode 100644 Tests/Editor/Integration/SupportedShaderRenderingTests.cs create mode 100644 Tests/Editor/Integration/SupportedShaderRenderingTests.cs.meta diff --git a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs index 61806e0..fd35645 100644 --- a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs +++ b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs @@ -64,6 +64,7 @@ public Material CreateEncryptedMaterial(string folderGuid, string fileName, Mate result.SetInteger(ShaderProperties.PasswordHash, (int)hash); injector.SetKeywords(result, auxiliary.LimTexture != null); + TextureEncryptManager.SetFormatKeywords(result, originalTex); writer.CreateAssetInFolder(result, folderGuid, fileName); writer.SaveAndRefresh(); return result; diff --git a/Runtime/Scripts/TextureEncryptManager.cs b/Runtime/Scripts/TextureEncryptManager.cs index f6b7a4c..05f9252 100644 --- a/Runtime/Scripts/TextureEncryptManager.cs +++ b/Runtime/Scripts/TextureEncryptManager.cs @@ -156,6 +156,13 @@ public static void SetFormatKeywords(Material material) format.SetFormatKeywords(material); } + public static void SetFormatKeywords(Material material, Texture texture) + { + var format = GetFormat(texture); + if (format == null) return; + format.SetFormatKeywords(material); + } + public static (int, int) CalculateOffsets(Texture2D texture) { var format = GetFormat(texture); diff --git a/Tests/Editor/Integration/SupportedShaderRenderingTests.cs b/Tests/Editor/Integration/SupportedShaderRenderingTests.cs new file mode 100644 index 0000000..7f0944e --- /dev/null +++ b/Tests/Editor/Integration/SupportedShaderRenderingTests.cs @@ -0,0 +1,490 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using NUnit.Framework; +using Shell.Protector; +using UnityEditor; +using UnityEditor.Animations; +using UnityEngine; +using UnityEngine.Rendering; +using VRC.SDK3.Avatars.Components; +using VRC.SDKBase; +using Object = UnityEngine.Object; + +namespace Shell.Protector.Tests.Integration +{ + public class SupportedShaderRenderingTests + { + private const int TextureSize = 128; + private const int RenderSize = TextureSize; + private const int RenderLayer = 30; + private const byte ChannelTolerance = 16; + private const double AverageChannelTolerance = 5.0; + private const double CorruptedAverageChannelDifference = 20.0; + private readonly List sceneObjects = new List(); + + [SetUp] + public void SetUp() + { + TestAssetScope.Reset(); + } + + [TearDown] + public void TearDown() + { + TestAssetScope.DestroyObjects(sceneObjects); + TestAssetScope.DeleteGeneratedRoot(); + TestAssetScope.DeleteDefaultGeneratedRoot(); + } + + [Test] + public void LilToonEncryptedMaterial_RendersLikeOriginal() + { + Shader shader = FindSupportedShader("lilToon"); + Fixture fixture = CreateFixture("LilToonSmoke", shader); + Color32[] reference = RenderMaterial(fixture.Material); + + GameObject encryptedAvatar = fixture.Protector.Encrypt(false); + sceneObjects.Add(encryptedAvatar); + + Material encryptedMaterial = GetBodyMaterial(encryptedAvatar); + AssertEncryptedMaterial(encryptedMaterial); + Assert.That(AssetDatabase.GetAssetPath(encryptedMaterial.shader).Replace('\\', '/'), Does.Contain("liltoonProtector/Shaders")); + + Color32[] actual = RenderMaterial(encryptedMaterial); + AssertRenderedRgbClose(reference, actual, "lilToon"); + } + + [Test] + public void LilToonEncryptedMaterial_WithTamperedKey_RendersCorruptedOutput() + { + Shader shader = FindSupportedShader("lilToon"); + Fixture fixture = CreateFixture("LilToonTamperedKey", shader); + Color32[] reference = RenderMaterial(fixture.Material); + + GameObject encryptedAvatar = fixture.Protector.Encrypt(false); + sceneObjects.Add(encryptedAvatar); + + Material encryptedMaterial = GetBodyMaterial(encryptedAvatar); + AssertEncryptedMaterial(encryptedMaterial); + Assert.That(AssetDatabase.GetAssetPath(encryptedMaterial.shader).Replace('\\', '/'), Does.Contain("liltoonProtector/Shaders")); + + AssertTamperedKeyRendersCorruptedOutput(encryptedMaterial, reference, "lilToon"); + } + + [Test] + public void PoiyomiEncryptedMaterial_RendersLikeOriginal() + { + Shader shader = FindSupportedShader(".poiyomi/Poiyomi Toon"); + Fixture fixture = CreateFixture("PoiyomiSmoke", shader); + Color32[] reference = RenderMaterial(fixture.Material); + + GameObject encryptedAvatar = fixture.Protector.Encrypt(false); + sceneObjects.Add(encryptedAvatar); + + Material encryptedMaterial = GetBodyMaterial(encryptedAvatar); + AssertEncryptedMaterial(encryptedMaterial); + AssertPoiyomiShaderWasInjected(encryptedMaterial.shader); + + Color32[] actual = RenderMaterial(encryptedMaterial); + AssertRenderedRgbClose(reference, actual, "Poiyomi"); + } + + [Test] + public void PoiyomiEncryptedMaterial_WithTamperedKey_RendersCorruptedOutput() + { + Shader shader = FindSupportedShader(".poiyomi/Poiyomi Toon"); + Fixture fixture = CreateFixture("PoiyomiTamperedKey", shader); + Color32[] reference = RenderMaterial(fixture.Material); + + GameObject encryptedAvatar = fixture.Protector.Encrypt(false); + sceneObjects.Add(encryptedAvatar); + + Material encryptedMaterial = GetBodyMaterial(encryptedAvatar); + AssertEncryptedMaterial(encryptedMaterial); + AssertPoiyomiShaderWasInjected(encryptedMaterial.shader); + + AssertTamperedKeyRendersCorruptedOutput(encryptedMaterial, reference, "Poiyomi"); + } + + private Fixture CreateFixture(string name, Shader shader) + { + Texture2D texture = CreateSrgbPatternTexture(TextureSize, TextureSize, true); + texture.name = name + "Texture"; + string texturePath = TestAssetScope.CreateAsset(texture, name + "/texture.asset"); + texture = AssetDatabase.LoadAssetAtPath(texturePath); + + Material material = new Material(shader); + material.name = name + "Material"; + material.mainTexture = texture; + if (material.HasProperty("_Color")) + material.SetColor("_Color", Color.white); + + string materialPath = TestAssetScope.CreateAsset(material, name + "/material.mat"); + material = AssetDatabase.LoadAssetAtPath(materialPath); + + Mesh mesh = TestAssetScope.CreateBlendShapeQuadMesh(); + string meshPath = TestAssetScope.CreateAsset(mesh, name + "/mesh.asset"); + mesh = AssetDatabase.LoadAssetAtPath(meshPath); + + AnimatorController fx = CreateFxController(name); + ScriptableObject parameters = CreateExpressionParameters(name); + + GameObject avatar = new GameObject(name + "Avatar"); + sceneObjects.Add(avatar); + + GameObject body = new GameObject("Body"); + body.transform.SetParent(avatar.transform, false); + SkinnedMeshRenderer renderer = body.AddComponent(); + renderer.sharedMesh = mesh; + renderer.sharedMaterial = material; + + VRCAvatarDescriptor descriptor = avatar.AddComponent(); + VrcExpressionParametersTestUtil.SetDescriptorParameters(descriptor, parameters); + descriptor.VisemeBlendShapes = Enumerable.Repeat(string.Empty, (int)VRC_AvatarDescriptor.Viseme.Count).ToArray(); + var eyeSettings = descriptor.customEyeLookSettings; + eyeSettings.eyelidsBlendshapes = new int[0]; + descriptor.customEyeLookSettings = eyeSettings; + descriptor.baseAnimationLayers = new VRCAvatarDescriptor.CustomAnimLayer[5]; + descriptor.baseAnimationLayers[4] = new VRCAvatarDescriptor.CustomAnimLayer + { + type = VRCAvatarDescriptor.AnimLayerType.FX, + isDefault = false, + animatorController = fx + }; + + ShellProtector protector = avatar.AddComponent(); + protector.Descriptor = descriptor; + protector.AssetDir = TestAssetScope.GeneratedRoot; + SetSerializedField(protector, "_gameObjectList", new List { avatar }); + SetSerializedField(protector, "_algorithm", (int)ShellProtectorAlgorithm.Chacha); + SetSerializedField(protector, "_filter", (int)ShellProtectorTextureFilter.Bilinear); + SetSerializedField(protector, "_fallback", (int)ShellProtectorFallback.Size32); + SetSerializedField(protector, "_keySize", 12); + SetSerializedField(protector, "_syncSize", 1); + protector.Init(); + + return new Fixture + { + Avatar = avatar, + Protector = protector, + Material = material + }; + } + + private static Shader FindSupportedShader(string shaderName) + { + Shader shader = Shader.Find(shaderName); + if (shader == null) + Assert.Ignore(shaderName + " shader was not found."); + if (!shader.isSupported) + Assert.Ignore(shaderName + " shader is not supported or failed to compile."); + return shader; + } + + private static Texture2D CreateSrgbPatternTexture(int width, int height, bool alpha) + { + Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, true, false); + Color32[] pixels = new Color32[width * height]; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + byte r = (byte)((x * 17 + y * 3) & 0xFF); + byte g = (byte)((x * 5 + y * 29) & 0xFF); + byte b = (byte)((x * 11 + y * 7 + 31) & 0xFF); + byte a = alpha ? (byte)(64 + ((x * 13 + y * 19) & 0x7F)) : (byte)255; + pixels[y * width + x] = new Color32(r, g, b, a); + } + } + + texture.SetPixels32(pixels); + texture.Apply(true, false); + texture.filterMode = FilterMode.Bilinear; + texture.wrapMode = TextureWrapMode.Repeat; + return texture; + } + + private static AnimatorController CreateFxController(string name) + { + string dir = TestAssetScope.GeneratedRoot + "/" + name; + TestAssetScope.EnsureFolder(dir); + AnimatorController controller = AnimatorController.CreateAnimatorControllerAtPath(dir + "/fx.controller"); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + return AssetDatabase.LoadAssetAtPath(dir + "/fx.controller"); + } + + private static ScriptableObject CreateExpressionParameters(string name) + { + ScriptableObject parameters = VrcExpressionParametersTestUtil.Create( + name + "Params", + new VrcExpressionParametersTestUtil.ParameterSpec + { + Name = "existing", + Saved = false, + NetworkSynced = false, + ValueType = "Bool", + DefaultValue = 0f + }); + string path = TestAssetScope.CreateAsset(parameters, name + "/params.asset"); + return AssetDatabase.LoadAssetAtPath(path); + } + + private static Material GetBodyMaterial(GameObject avatar) + { + Assert.That(avatar, Is.Not.Null); + Transform body = avatar.transform.Find("Body"); + Assert.That(body, Is.Not.Null); + SkinnedMeshRenderer renderer = body.GetComponent(); + Assert.That(renderer, Is.Not.Null); + Assert.That(renderer.sharedMaterial, Is.Not.Null); + return renderer.sharedMaterial; + } + + private static void AssertEncryptedMaterial(Material material) + { + Assert.That(material, Is.Not.Null); + Assert.That(material.shader, Is.Not.Null); + Assert.That(material.shader.isSupported, Is.True, "Encrypted shader should be supported: " + material.shader.name); + Assert.That(material.GetTexture(ShaderProperties.EncryptTexture0), Is.Not.Null); + Assert.That(material.GetTexture(ShaderProperties.MipTexture), Is.Not.Null); + Assert.That(material.IsKeywordEnabled(ShaderProperties.ChachaKeyword), Is.True, "Encrypted material should enable Chacha keyword."); + Assert.That(material.IsKeywordEnabled(ShaderProperties.Format1Keyword), Is.True, "Encrypted material should enable RGBA format keyword."); + Assert.That(material.IsKeywordEnabled(ShaderProperties.Format0Keyword), Is.False, "Encrypted material should not enable RGB format keyword."); + } + + private static void AssertPoiyomiShaderWasInjected(Shader shader) + { + string path = AssetDatabase.GetAssetPath(shader).Replace('\\', '/'); + Assert.That(path, Does.StartWith(TestAssetScope.GeneratedRoot)); + string shaderData = File.ReadAllText(path); + Assert.That(shaderData, Does.Contain("//ShellProtect")); + Assert.That(shaderData, Does.Contain("Protector.cginc")); + Assert.That(shaderData, Does.Contain(ShaderProperties.EncryptTexture0)); + Assert.That(shaderData, Does.Contain(ShaderProperties.MipTexture)); + } + + private static void AssertTamperedKeyRendersCorruptedOutput(Material encryptedMaterial, Color32[] reference, string label) + { + Material fallbackProbe = null; + Material tamperedProbe = null; + + try + { + fallbackProbe = CreateFallbackProbe(encryptedMaterial); + tamperedProbe = CreateTamperedKeyProbe(encryptedMaterial); + + Color32[] fallback = RenderMaterial(fallbackProbe); + Color32[] tampered = RenderMaterial(tamperedProbe); + + AssertRenderedRgbDifferent(reference, tampered, label + " tampered output should differ from the original"); + AssertRenderedRgbDifferent(fallback, tampered, label + " tampered output should differ from fallback"); + } + finally + { + if (fallbackProbe != null) + Object.DestroyImmediate(fallbackProbe); + if (tamperedProbe != null) + Object.DestroyImmediate(tamperedProbe); + } + } + + private static Material CreateFallbackProbe(Material encryptedMaterial) + { + Material fallbackProbe = new Material(encryptedMaterial); + fallbackProbe.name = encryptedMaterial.name + "FallbackProbe"; + fallbackProbe.SetInteger(ShaderProperties.PasswordHash, fallbackProbe.GetInteger(ShaderProperties.PasswordHash) ^ 0x5A5A5A5A); + return fallbackProbe; + } + + private static Material CreateTamperedKeyProbe(Material encryptedMaterial) + { + Material tamperedProbe = new Material(encryptedMaterial); + tamperedProbe.name = encryptedMaterial.name + "TamperedKeyProbe"; + + byte[] key = ReadKeyBytes(tamperedProbe); + key[0] = (byte)(key[0] ^ 0x5A); + ApplyKeyBytes(tamperedProbe, key); + return tamperedProbe; + } + + private static byte[] ReadKeyBytes(Material material) + { + byte[] key = new byte[16]; + for (int i = 0; i < key.Length; i++) + key[i] = (byte)Mathf.RoundToInt(material.GetFloat(ShaderProperties.KeyPrefix + i)); + return key; + } + + private static void ApplyKeyBytes(Material material, byte[] key) + { + for (int i = 0; i < key.Length; i++) + material.SetFloat(ShaderProperties.KeyPrefix + i, key[i]); + + uint hashMagic = unchecked((uint)material.GetInteger(ShaderProperties.HashMagic)); + uint passwordHash = KeyGenerator.SimpleHash(key, hashMagic); + material.SetInteger(ShaderProperties.PasswordHash, unchecked((int)passwordHash)); + } + + private static Color32[] RenderMaterial(Material material) + { + GameObject subject = null; + GameObject cameraObject = null; + GameObject lightObject = null; + Camera camera = null; + RenderTexture target = null; + Texture2D readback = null; + RenderTexture previous = RenderTexture.active; + AmbientMode previousAmbientMode = RenderSettings.ambientMode; + Color previousAmbientLight = RenderSettings.ambientLight; + + try + { + subject = GameObject.CreatePrimitive(PrimitiveType.Quad); + subject.layer = RenderLayer; + subject.transform.position = Vector3.zero; + subject.transform.localScale = new Vector3(1.1f, 1.1f, 1f); + Object.DestroyImmediate(subject.GetComponent()); + MeshRenderer renderer = subject.GetComponent(); + renderer.sharedMaterial = material; + + cameraObject = new GameObject("ShellProtectorRenderCamera"); + camera = cameraObject.AddComponent(); + cameraObject.transform.position = new Vector3(0f, 0f, -2f); + cameraObject.transform.rotation = Quaternion.identity; + camera.orthographic = true; + camera.orthographicSize = 0.55f; + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = Color.black; + camera.cullingMask = 1 << RenderLayer; + camera.nearClipPlane = 0.1f; + camera.farClipPlane = 10f; + + lightObject = new GameObject("ShellProtectorRenderLight"); + lightObject.layer = RenderLayer; + Light light = lightObject.AddComponent(); + light.type = LightType.Directional; + light.color = Color.white; + light.intensity = 1f; + light.cullingMask = 1 << RenderLayer; + lightObject.transform.rotation = Quaternion.Euler(35f, 30f, 0f); + + RenderSettings.ambientMode = AmbientMode.Flat; + RenderSettings.ambientLight = Color.white; + + target = new RenderTexture(RenderSize, RenderSize, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); + target.Create(); + camera.targetTexture = target; + camera.Render(); + + RenderTexture.active = target; + readback = new Texture2D(RenderSize, RenderSize, TextureFormat.RGBA32, false, true); + readback.ReadPixels(new Rect(0, 0, RenderSize, RenderSize), 0, 0); + readback.Apply(false, false); + return readback.GetPixels32(); + } + finally + { + RenderSettings.ambientMode = previousAmbientMode; + RenderSettings.ambientLight = previousAmbientLight; + RenderTexture.active = previous; + + if (camera != null) + camera.targetTexture = null; + if (target != null) + target.Release(); + if (target != null) + Object.DestroyImmediate(target); + if (readback != null) + Object.DestroyImmediate(readback); + if (subject != null) + Object.DestroyImmediate(subject); + if (cameraObject != null) + Object.DestroyImmediate(cameraObject); + if (lightObject != null) + Object.DestroyImmediate(lightObject); + } + } + + private static void AssertRenderedRgbClose(Color32[] expected, Color32[] actual, string label) + { + Assert.That(actual.Length, Is.EqualTo(expected.Length)); + + int compared = 0; + long totalDifference = 0; + for (int y = 8; y < RenderSize - 8; y++) + { + for (int x = 8; x < RenderSize - 8; x++) + { + int i = y * RenderSize + x; + AssertChannelClose(expected[i].r, actual[i].r, label, i, "r"); + AssertChannelClose(expected[i].g, actual[i].g, label, i, "g"); + AssertChannelClose(expected[i].b, actual[i].b, label, i, "b"); + totalDifference += AbsDiff(expected[i].r, actual[i].r); + totalDifference += AbsDiff(expected[i].g, actual[i].g); + totalDifference += AbsDiff(expected[i].b, actual[i].b); + compared += 3; + } + } + + double averageDifference = (double)totalDifference / compared; + Assert.That(averageDifference, Is.LessThanOrEqualTo(AverageChannelTolerance), label + " average RGB difference"); + } + + private static void AssertRenderedRgbDifferent(Color32[] expected, Color32[] actual, string label) + { + Assert.That(actual.Length, Is.EqualTo(expected.Length)); + double averageDifference = CalculateAverageRgbDifference(expected, actual); + Assert.That(averageDifference, Is.GreaterThanOrEqualTo(CorruptedAverageChannelDifference), label + " average RGB difference"); + } + + private static double CalculateAverageRgbDifference(Color32[] expected, Color32[] actual) + { + long totalDifference = 0; + int compared = 0; + for (int y = 8; y < RenderSize - 8; y++) + { + for (int x = 8; x < RenderSize - 8; x++) + { + int i = y * RenderSize + x; + totalDifference += AbsDiff(expected[i].r, actual[i].r); + totalDifference += AbsDiff(expected[i].g, actual[i].g); + totalDifference += AbsDiff(expected[i].b, actual[i].b); + compared += 3; + } + } + + return (double)totalDifference / compared; + } + + private static void AssertChannelClose(byte expected, byte actual, string label, int pixel, string channel) + { + int difference = AbsDiff(expected, actual); + if (difference > ChannelTolerance) + Assert.Fail("{0} pixel {1} channel {2} differs. Expected {3}, Actual {4}, Difference {5}", label, pixel, channel, expected, actual, difference); + } + + private static int AbsDiff(byte a, byte b) + { + return a > b ? a - b : b - a; + } + + private static void SetSerializedField(ShellProtector protector, string fieldName, T value) + { + FieldInfo field = typeof(ShellProtector).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + Assert.That(field, Is.Not.Null, fieldName); + field.SetValue(protector, value); + } + + private class Fixture + { + public GameObject Avatar; + public ShellProtector Protector; + public Material Material; + } + } +} +#endif diff --git a/Tests/Editor/Integration/SupportedShaderRenderingTests.cs.meta b/Tests/Editor/Integration/SupportedShaderRenderingTests.cs.meta new file mode 100644 index 0000000..cedea1d --- /dev/null +++ b/Tests/Editor/Integration/SupportedShaderRenderingTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a99c1715f3c49afbaf0060f76031833 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 4e78bf0a9ccef7e59e23efea30f6e77993f2bcc7 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:03:17 +0900 Subject: [PATCH 42/59] feat: Support poiyomi10, Optimize decryption shader --- Runtime/Scripts/AssetManager.cs | 15 ++- Runtime/Scripts/Injector/Injector.cs | 4 +- Runtime/Scripts/Injector/PoiyomiInjector.cs | 36 ++---- Runtime/Shader/Chacha.cginc | 136 +++++++++----------- 4 files changed, 82 insertions(+), 109 deletions(-) diff --git a/Runtime/Scripts/AssetManager.cs b/Runtime/Scripts/AssetManager.cs index 0a686b9..239ceda 100644 --- a/Runtime/Scripts/AssetManager.cs +++ b/Runtime/Scripts/AssetManager.cs @@ -29,6 +29,7 @@ public static AssetManager GetInstance() _supportedVersions.Add("Poiyomi 9.1", 91); _supportedVersions.Add("Poiyomi 9.2", 92); _supportedVersions.Add("Poiyomi 9.3", 93); + _supportedVersions.Add("Poiyomi 10.0", 100); _supportedVersions.Add("lilToon", 0); } public bool IsPoiyomi(Shader shader) @@ -60,19 +61,21 @@ public int GetShaderType(Shader shader) if (poiyomiLabel != -1) { var str = shader.GetPropertyDescription(poiyomiLabel); + if (str.Contains("Poiyomi 10.0")) + return _supportedVersions["Poiyomi 10.0"]; if (str.Contains("Poiyomi 9.3")) return _supportedVersions["Poiyomi 9.3"]; - else if (str.Contains("Poiyomi 9.2")) + if (str.Contains("Poiyomi 9.2")) return _supportedVersions["Poiyomi 9.2"]; - else if (str.Contains("Poiyomi 9.1")) + if (str.Contains("Poiyomi 9.1")) return _supportedVersions["Poiyomi 9.1"]; - else if (str.Contains("Poiyomi 9.0")) + if (str.Contains("Poiyomi 9.0")) return _supportedVersions["Poiyomi 9.0"]; - else if(str.Contains("Poiymoi 8.0")) + if(str.Contains("Poiymoi 8.0")) return _supportedVersions["Poiyomi 8.0"]; - else if(str.Contains("Poiyomi 8.1")) + if(str.Contains("Poiyomi 8.1")) return _supportedVersions["Poiyomi 8.1"]; - else if (str.Contains("Poiyomi 8.2")) + if (str.Contains("Poiyomi 8.2")) return _supportedVersions["Poiyomi 8.2"]; } return -1; diff --git a/Runtime/Scripts/Injector/Injector.cs b/Runtime/Scripts/Injector/Injector.cs index 9f25bfc..9166314 100644 --- a/Runtime/Scripts/Injector/Injector.cs +++ b/Runtime/Scripts/Injector/Injector.cs @@ -17,7 +17,7 @@ public abstract class Injector : IShaderAdapter half4 mainTexture; UNITY_BRANCH - if(IsDecrypted()) + if(isDecrypted) { mainTexture = DecryptTextureBox(_EncryptTex0, _EncryptTex1, sampler_EncryptTex0, _EncryptTex0_TexelSize, _MipTex, sampler_MipTex, mainUV); } @@ -30,7 +30,7 @@ public abstract class Injector : IShaderAdapter half4 mainTexture; UNITY_BRANCH - if(IsDecrypted()) + if(isDecrypted) { mainTexture = DecryptTextureBilinear(_EncryptTex0, _EncryptTex1, sampler_EncryptTex0, _EncryptTex0_TexelSize, _MipTex, sampler_MipTex, mainUV); } diff --git a/Runtime/Scripts/Injector/PoiyomiInjector.cs b/Runtime/Scripts/Injector/PoiyomiInjector.cs index ce051f4..7c482ee 100644 --- a/Runtime/Scripts/Injector/PoiyomiInjector.cs +++ b/Runtime/Scripts/Injector/PoiyomiInjector.cs @@ -59,43 +59,33 @@ protected override Shader CustomInject(Material mat, string decodeDir, string ou float4 _EncryptTex0_TexelSize; int _PasswordHash; "; - int version = AssetManager.GetInstance().GetShaderType(shader); - if(version == 73) - { - string path = outputPath + "/CGI_Poicludes.cginc"; - string poicludes = File.ReadAllText(path); - poicludes = Regex.Replace(poicludes, "UNITY_DECLARE_TEX2D\\(_MainTex\\);(.*?)", declare); - File.WriteAllText(path, poicludes); - - path = outputPath + "/CGI_PoiFrag.cginc"; - string frag = File.ReadAllText(path); - frag = Regex.Replace(frag, "float4 frag\\(", "#include \"Decrypt.cginc\"\nfloat4 frag("); + const string vertexOut = @" + struct VertexOut + { + int isDecrypted : SHELL0;"; + const string returnO = @" + o.isDecrypted = IsDecrypted(); + return o;"; - string shaderCode = ShaderCodeNoFilter; - if (Filter == 0) - shaderCode = ShaderCodeNoFilter; - else if (Filter == 1) - shaderCode = ShaderCodeBilinear; - - frag = Regex.Replace(frag, "float4 mainTexture = .*?;", shaderCode); - frag = Regex.Replace(frag, "float4 mip_texture = _MipTex.Sample\\(sampler_MipTex, .*?\\);", "float4 mip_texture = _MipTex.Sample(sampler_MipTex, poiMesh.uv[0]);"); - File.WriteAllText(path, frag); - } - else if(version >= 80) + int version = AssetManager.GetInstance().GetShaderType(shader); + if(version >= 80) { + string includeStr = string.Format("#include \"{0}\"", decodeDir); shaderData = Regex.Replace(shaderData, "UNITY_DECLARE_TEX2D\\(_MainTex\\);", declare); shaderData = Regex.Replace(shaderData, "POI2D_SAMPLER_PAN\\((.*?), _MainTex", "POI2D_SAMPLER_PAN($1, _MipTex"); shaderData = Regex.Replace(shaderData, "UNITY_SAMPLE_TEX2D_SAMPLER_LOD\\((.*?), _MainTex", "UNITY_SAMPLE_TEX2D_SAMPLER_LOD($1, _MipTex"); shaderData = Regex.Replace(shaderData, "UNITY_SAMPLE_TEX2D_SAMPLER\\((.*?), _MainTex", "UNITY_SAMPLE_TEX2D_SAMPLER($1, _MipTex"); shaderData = Regex.Replace(shaderData, "float4 frag\\(", "#include \"" + decodeDir + "\"\n\t\t\tfloat4 frag("); + shaderData = Regex.Replace(shaderData, "struct VertexOut[\r\n]+[ \t]*\\{", string.Format("{0}\r\n{1}", includeStr, vertexOut)); + shaderData = Regex.Replace(shaderData, "\t+return o;", returnO); string shaderCode = ShaderCodeNoFilter; if (Filter == 0) shaderCode = ShaderCodeNoFilter; else if (Filter == 1) shaderCode = ShaderCodeBilinear; - shaderData = Regex.Replace(shaderData, "float4 mainTexture = .*?;", shaderCode); + shaderData = Regex.Replace(shaderData, "float4 mainTexture = .*?;", string.Format("bool isDecrypted = i.isDecrypted == 1;\r\n{0}", shaderCode)); if (hasLimTexture) { if(version == 80) diff --git a/Runtime/Shader/Chacha.cginc b/Runtime/Shader/Chacha.cginc index 39ded16..552af84 100644 --- a/Runtime/Shader/Chacha.cginc +++ b/Runtime/Shader/Chacha.cginc @@ -9,6 +9,14 @@ uint Rotl32(uint x, int n) return x << n | (x >> (32 - n)); } +void ChaChaQuarterRound(inout uint a, inout uint b, inout uint c, inout uint d) +{ + a += b; d = Rotl32(d ^ a, 16u); + c += d; b = Rotl32(b ^ c, 12u); + a += b; d = Rotl32(d ^ a, 8u); + c += d; b = Rotl32(b ^ c, 7u); +} + void Chacha20QuarterRound(inout uint state[16], int a, int b, int c, int d) { state[a] += state[b]; state[d] = Rotl32(state[d] ^ state[a], 16); @@ -17,91 +25,63 @@ void Chacha20QuarterRound(inout uint state[16], int a, int b, int c, int d) state[c] += state[d]; state[b] = Rotl32(state[b] ^ state[c], 7); } -void Decrypt(inout uint data[2], const uint key[4]) +uint3 ChaCha8KeyStream3(const uint key[4]) { - uint4 state[4]; - state[0] = uint4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574); - state[1] = uint4(key[0], key[1], key[2], key[3]); - state[2] = state[1]; - state[3] = uint4(1, _Nonce0, _Nonce1, _Nonce2); + uint x0 = 0x61707865u; + uint x1 = 0x3320646eu; + uint x2 = 0x79622d32u; + uint x3 = 0x6b206574u; - uint block[16]; - //block - [unroll] - int i = 0; - for(i = 0; i < 4; ++i) - { - block[i * 4 + 0] = state[i].x; - block[i * 4 + 1] = state[i].y; - block[i * 4 + 2] = state[i].z; - block[i * 4 + 3] = state[i].w; - } - [unroll] - for (i = 0; i < CHACHA20_ROUNDS; i += 2) + uint x4 = key[0]; + uint x5 = key[1]; + uint x6 = key[2]; + uint x7 = key[3]; + + uint x8 = key[0]; + uint x9 = key[1]; + uint x10 = key[2]; + uint x11 = key[3]; + + uint x12 = 1u; + uint x13 = _Nonce0; + uint x14 = _Nonce1; + uint x15 = _Nonce2; + + [unroll] + for (uint round = 0u; round < CHACHA20_ROUNDS; round += 2u) { - Chacha20QuarterRound(block, 0, 4, 8, 12); - Chacha20QuarterRound(block, 1, 5, 9, 13); - Chacha20QuarterRound(block, 2, 6, 10, 14); - Chacha20QuarterRound(block, 3, 7, 11, 15); - Chacha20QuarterRound(block, 0, 5, 10, 15); - Chacha20QuarterRound(block, 1, 6, 11, 12); - Chacha20QuarterRound(block, 2, 7, 8, 13); - Chacha20QuarterRound(block, 3, 4, 9, 14); + // Column round + ChaChaQuarterRound(x0, x4, x8, x12); + ChaChaQuarterRound(x1, x5, x9, x13); + ChaChaQuarterRound(x2, x6, x10, x14); + ChaChaQuarterRound(x3, x7, x11, x15); + + // Diagonal round + ChaChaQuarterRound(x0, x5, x10, x15); + ChaChaQuarterRound(x1, x6, x11, x12); + ChaChaQuarterRound(x2, x7, x8, x13); + ChaChaQuarterRound(x3, x4, x9, x14); } - [unroll] - for(i = 0; i < 4; ++i) - { - block[i * 4 + 0] += state[i].x; - block[i * 4 + 1] += state[i].y; - block[i * 4 + 2] += state[i].z; - block[i * 4 + 3] += state[i].w; - } - // - data[0] ^= block[0]; - data[1] ^= block[1]; + + return uint3( + x0 + 0x61707865u, + x1 + 0x3320646eu, + x2 + 0x79622d32u + ); +} + +void Decrypt(inout uint data[2], const uint key[4]) +{ + uint3 stream = ChaCha8KeyStream3(key); + data[0] ^= stream.x; + data[1] ^= stream.y; } void Decrypt(inout uint data[3], const uint key[4]) { - uint4 state[4]; - state[0] = uint4(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574); - state[1] = uint4(key[0], key[1], key[2], key[3]); - state[2] = state[1]; - state[3] = uint4(1, _Nonce0, _Nonce1, _Nonce2); + uint3 stream = ChaCha8KeyStream3(key); - uint block[16]; - //block - int i = 0; - [unroll] - for(i = 0; i < 4; ++i) - { - block[i * 4 + 0] = state[i].x; - block[i * 4 + 1] = state[i].y; - block[i * 4 + 2] = state[i].z; - block[i * 4 + 3] = state[i].w; - } - [unroll] - for (i = 0; i < CHACHA20_ROUNDS; i += 2) - { - Chacha20QuarterRound(block, 0, 4, 8, 12); - Chacha20QuarterRound(block, 1, 5, 9, 13); - Chacha20QuarterRound(block, 2, 6, 10, 14); - Chacha20QuarterRound(block, 3, 7, 11, 15); - Chacha20QuarterRound(block, 0, 5, 10, 15); - Chacha20QuarterRound(block, 1, 6, 11, 12); - Chacha20QuarterRound(block, 2, 7, 8, 13); - Chacha20QuarterRound(block, 3, 4, 9, 14); - } - [unroll] - for(i = 0; i < 4; ++i) - { - block[i * 4 + 0] += state[i].x; - block[i * 4 + 1] += state[i].y; - block[i * 4 + 2] += state[i].z; - block[i * 4 + 3] += state[i].w; - } - // - data[0] ^= block[0]; - data[1] ^= block[1]; - data[2] ^= block[2]; + data[0] ^= stream.x; + data[1] ^= stream.y; + data[2] ^= stream.z; } \ No newline at end of file From 962169fdfbc65fae0fd386dd373558273d5a2485 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:06:36 +0900 Subject: [PATCH 43/59] perf: improve decryption performance by up to 2x --- Runtime/Shader/DXT.cginc | 38 +++++----- Runtime/Shader/Protector.cginc | 73 +++++++++++++++----- Runtime/Shader/RGB.cginc | 4 +- Runtime/Shader/RGBA.cginc | 12 ++-- Runtime/Shader/Utility.cginc | 12 ++-- Runtime/liltoonProtector/Shaders/custom.hlsl | 8 ++- 6 files changed, 94 insertions(+), 53 deletions(-) diff --git a/Runtime/Shader/DXT.cginc b/Runtime/Shader/DXT.cginc index aecd0ee..5005faf 100644 --- a/Runtime/Shader/DXT.cginc +++ b/Runtime/Shader/DXT.cginc @@ -3,36 +3,36 @@ #define _SHELL_PROTECTOR_DATA_LENGTH 2 #define _SHELL_PROTECTOR_INDEX_ALIGNMENT 1 -int GetIndex(half2 uv, int m) +int GetBlockIndex(float2 uv, int m) { - half x = frac(uv.x); - half y = frac(uv.y); + const float x = frac(uv.x); + const float y = frac(uv.y); - uint w = mw[m + (uint)_Woffset]; - uint h = mh[m + _Hoffset]; + const uint encW = mw[m + _Woffset]; + const uint encH = mh[m + _Hoffset]; - return (w * floor(y * h)) + floor(x * w); + return (encW * floor(y * encH)) + floor(x * encW); } -void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[2], half2 uv, int m) +void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[2], float2 uv, int m) { - int idx = GetIndex(uv, m); + int idx = GetBlockIndex(uv, m); int offset = (idx & 1) == 0 ? 0 : -1; - half4 pixels[2]; - pixels[0] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 0 + offset, m, (uint) _Woffset, _Hoffset), m); - pixels[1] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 1 + offset, m, (uint) _Woffset, _Hoffset), m); + float4 pixels[2]; + pixels[0] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 0 + offset, m, _Woffset, _Hoffset), m); + pixels[1] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 1 + offset, m, _Woffset, _Hoffset), m); - data[0] = ((uint)round(pixels[0].r * 255.0f) | ((uint)round(pixels[0].g * 255.0f) << 8) | ((uint)round(pixels[0].b * 255.0f) << 16) | ((uint)round(pixels[0].a * 255.0f) << 24)); - data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); + data[0] = ((uint)round(pixels[0].r * 255.0) | ((uint)round(pixels[0].g * 255.0) << 8) | ((uint)round(pixels[0].b * 255.0) << 16) | ((uint)round(pixels[0].a * 255.0) << 24)); + data[1] = ((uint)round(pixels[1].r * 255.0) | ((uint)round(pixels[1].g * 255.0) << 8) | ((uint)round(pixels[1].b * 255.0) << 16) | ((uint)round(pixels[1].a * 255.0) << 24)); } -half4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, inout uint data[2], half2 uv, int m) +float4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, in uint data[2], float2 uv, int m) { - int idx = GetIndex(uv, m); + int idx = GetBlockIndex(uv, m); int offset = (idx & 1) == 0 ? 0 : -1; - half4 col = tex0.SampleLevel(tex0Sampler, uv, m); + float4 col = tex0.SampleLevel(tex0Sampler, uv, m); uint r = (data[idx & 1] & 0x000000FF) >> 0; uint g = (data[idx & 1] & 0x0000FF00) >> 8; uint b = (data[idx & 1] & 0x00FF0000) >> 16; @@ -55,10 +55,10 @@ half4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, inout uint data[2], hal uint color2_b= color2 & 0x1F; color2_b = color2_b << 3 | color2_b >> 2; - half3 col1 = half3(color1_r / (half)255, color1_g / (half)255, color1_b / (half)255); - half3 col2 = half3(color2_r / (half)255, color2_g / (half)255, color2_b / (half)255); + float3 col1 = float3(color1_r / 255.0, color1_g / 255.0, color1_b / 255.0); + float3 col2 = float3(color2_r / 255.0, color2_g / 255.0, color2_b / 255.0); - half3 result; + float3 result; result = lerp(col2, col1, color1 > color2 ? col.rgb : 0.5); return half4(GammaCorrection(result), col.a); diff --git a/Runtime/Shader/Protector.cginc b/Runtime/Shader/Protector.cginc index e660657..6d3fcbc 100644 --- a/Runtime/Shader/Protector.cginc +++ b/Runtime/Shader/Protector.cginc @@ -54,9 +54,13 @@ uint _HashMagic; #include "DXT.cginc" #endif -half4 DecryptTexture(Texture2D tex0, Texture2D tex1, SamplerState tex0Sampler, half2 uv, int m) +void DecryptData(inout uint data[_SHELL_PROTECTOR_DATA_LENGTH], Texture2D tex0, Texture2D tex1, SamplerState tex0Sampler, float2 uv, int m) { - int idx = GetIndex(uv, m); + #ifdef _SHELL_PROTECTOR_DXT + const int idx = GetBlockIndex(uv, m); +#else + const int idx = GetIndex(uv, m); +#endif const uint key[4] = { ((uint)round(_Key0) | ((uint)round(_Key1) << 8) | ((uint)round(_Key2) << 16) | ((uint)round(_Key3) << 24)), @@ -64,49 +68,80 @@ half4 DecryptTexture(Texture2D tex0, Texture2D tex1, SamplerState tex0Sampler, h ((uint)round(_Key8) | ((uint)round(_Key9) << 8) | ((uint)round(_Key10) << 16) | ((uint)round(_Key11) << 24)), ((uint)round(_Key12) | ((uint)round(_Key13) << 8) | ((uint)round(_Key14) << 16) | ((uint)round(_Key15) << 24)) ^ (uint)((idx >> _SHELL_PROTECTOR_INDEX_ALIGNMENT) << _SHELL_PROTECTOR_INDEX_ALIGNMENT) }; - - uint data[_SHELL_PROTECTOR_DATA_LENGTH]; #ifdef _SHELL_PROTECTOR_DXT GetData(tex1, tex0Sampler, data, uv, m); #else GetData(tex0, tex0Sampler, data, uv, m); #endif Decrypt(data, key); +} + +float4 DecryptTexture(Texture2D tex0, Texture2D tex1, SamplerState tex0Sampler, float2 uv, int m) +{ + uint data[_SHELL_PROTECTOR_DATA_LENGTH]; + DecryptData(data, tex0, tex1, tex0Sampler, uv, m); return GetPixel(tex0, tex0Sampler, data, uv, m); } -half4 DecryptTextureBox(Texture2D tex0, Texture2D tex1, SamplerState texSampler, half4 texSize, Texture2D mipTex, SamplerState mipSamp, half2 uv) +float4 DecryptTextureBox(Texture2D tex0, Texture2D tex1, SamplerState texSampler, float4 texSize, Texture2D mipTex, SamplerState mipSamp, float2 uv) { - half4 mipPixel = mipTex.Sample(mipSamp, uv); + float4 mipPixel = mipTex.Sample(mipSamp, uv); int mip = round(mipPixel.r * 255 / 10); //fucking precision problems const int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 }; // max size 4k - half4 c00 = DecryptTexture(tex0, tex1, texSampler, uv, m[mip]); + float4 c00 = DecryptTexture(tex0, tex1, texSampler, uv, m[mip]); return c00; } -half4 DecryptTextureBilinear(Texture2D tex0, Texture2D tex1, SamplerState texSampler, half4 texSize, Texture2D mipTex, SamplerState mipSamp, half2 uv) +float4 DecryptTextureBilinear(Texture2D tex0, Texture2D tex1, SamplerState texSampler, float4 originalTexSize, Texture2D mipTex, SamplerState mipSamp, float2 uv) { - const half4 mipPixel = mipTex.Sample(mipSamp, uv); - const half2 uvUnit = texSize.xy; + const float4 mipPixel = mipTex.Sample(mipSamp, uv); + const float2 uvUnit = originalTexSize.xy; //bilinear interpolation - const half2 uvBilinear = uv - 0.5 * uvUnit; + const float2 uvBilinear = uv - 0.5 * uvUnit; const int mip = round(mipPixel.r * 255 / 10); //fucking precision problems const int m[13] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10 }; // max size 4k - half4 c00 = DecryptTexture(tex0, tex1, texSampler, uvBilinear + half2(uvUnit.x * 0, uvUnit.y * 0), m[mip]); - half4 c10 = DecryptTexture(tex0, tex1, texSampler, uvBilinear + half2(uvUnit.x * 1, uvUnit.y * 0), m[mip]); - half4 c01 = DecryptTexture(tex0, tex1, texSampler, uvBilinear + half2(uvUnit.x * 0, uvUnit.y * 1), m[mip]); - half4 c11 = DecryptTexture(tex0, tex1, texSampler, uvBilinear + half2(uvUnit.x * 1, uvUnit.y * 1), m[mip]); + const float2 uv00 = uvBilinear + float2(uvUnit.x * 0, uvUnit.y * 0); + const float2 uv10 = uvBilinear + float2(uvUnit.x * 1, uvUnit.y * 0); + const float2 uv01 = uvBilinear + float2(uvUnit.x * 0, uvUnit.y * 1); + const float2 uv11 = uvBilinear + float2(uvUnit.x * 1, uvUnit.y * 1); + +#ifdef _SHELL_PROTECTOR_DXT + const int idx00 = GetBlockIndex(uv00, m[mip]); + const int idx10 = GetBlockIndex(uv10, m[mip]); + const int idx01 = GetBlockIndex(uv01, m[mip]); + const int idx11 = GetBlockIndex(uv11, m[mip]); + if ((idx00 == idx10) && (idx10 == idx01) && (idx01 == idx11)) + { + uint data[_SHELL_PROTECTOR_DATA_LENGTH]; + DecryptData(data, tex0, tex1, texSampler, uv00, m[mip]); + + const float4 c00 = GetPixel(tex0, texSampler, data, uv00, m[mip]); + const float4 c10 = GetPixel(tex0, texSampler, data, uv10, m[mip]); + const float4 c01 = GetPixel(tex0, texSampler, data, uv01, m[mip]); + const float4 c11 = GetPixel(tex0, texSampler, data, uv11, m[mip]); + const float2 f = frac(uvBilinear * originalTexSize.zw); + const float4 c0 = lerp(c00, c10, f.x); + const float4 c1 = lerp(c01, c11, f.x); + const float4 bilinear = lerp(c0, c1, f.y); + return bilinear; + } +#endif + + float4 c00 = DecryptTexture(tex0, tex1, texSampler, uv00, m[mip]); + float4 c10 = DecryptTexture(tex0, tex1, texSampler, uv10, m[mip]); + float4 c01 = DecryptTexture(tex0, tex1, texSampler, uv01, m[mip]); + float4 c11 = DecryptTexture(tex0, tex1, texSampler, uv11, m[mip]); - half2 f = frac(uvBilinear * texSize.zw); + float2 f = frac(uvBilinear * originalTexSize.zw); - half4 c0 = lerp(c00, c10, f.x); - half4 c1 = lerp(c01, c11, f.x); + float4 c0 = lerp(c00, c10, f.x); + float4 c1 = lerp(c01, c11, f.x); - half4 bilinear = lerp(c0, c1, f.y); + float4 bilinear = lerp(c0, c1, f.y); return bilinear; } diff --git a/Runtime/Shader/RGB.cginc b/Runtime/Shader/RGB.cginc index 933f23b..6f02f95 100644 --- a/Runtime/Shader/RGB.cginc +++ b/Runtime/Shader/RGB.cginc @@ -15,7 +15,7 @@ void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[3], half2 int idx = GetIndex(uv, m); int offset = pos[idx % 4]; - half3 pixels[4]; + float3 pixels[4]; pixels[0] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); pixels[1] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); pixels[2] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 2 + offset, m, (uint)_Woffset, _Hoffset), m); @@ -34,7 +34,7 @@ half4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, inout uint data[3], hal half r[4] = { (data[0] & 0x000000FF)/255.0f, ((data[0] & 0xFF000000) >> 24)/255.0f, ((data[1] & 0x00FF0000) >> 16)/255.0f, ((data[2] & 0x0000FF00) >> 8)/255.0f }; half g[4] = { ((data[0] & 0x0000FF00) >> 8)/255.0f, ((data[1] & 0x000000FF) >> 0)/255.0f, ((data[1] & 0xFF000000) >> 24)/255.0f, ((data[2] & 0x00FF0000) >> 16)/255.0f }; half b[4] = { ((data[0] & 0x00FF0000) >> 16)/255.0f, ((data[1] & 0x0000FF00) >> 8)/255.0f, ((data[2] & 0x000000FF) >> 0)/255.0f, ((data[2] & 0xFF000000) >> 24)/255.0f }; - half3 decrypt = half3(r[idx % 4], g[idx % 4], b[idx % 4]); + float3 decrypt = float3(r[idx % 4], g[idx % 4], b[idx % 4]); return half4(GammaCorrection(decrypt), 1.0); } diff --git a/Runtime/Shader/RGBA.cginc b/Runtime/Shader/RGBA.cginc index 8004fe7..3db8984 100644 --- a/Runtime/Shader/RGBA.cginc +++ b/Runtime/Shader/RGBA.cginc @@ -3,18 +3,18 @@ #define _SHELL_PROTECTOR_DATA_LENGTH 2 #define _SHELL_PROTECTOR_INDEX_ALIGNMENT 1 -int GetIndex(half2 uv, int m) { +int GetIndex(float2 uv, int m) { half x = frac(uv.x); half y = frac(uv.y); return (mw[m + (uint)_Woffset] * floor(y * mh[m + _Hoffset])) + floor(x * mw[m + (uint)_Woffset]); } -void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[2], half2 uv, int m) { +void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[2], float2 uv, int m) { int idx = GetIndex(uv, m); int offset = (idx & 1) == 0 ? 0 : -1; - half4 pixels[2]; + float4 pixels[2]; pixels[0] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 0 + offset, m, (uint)_Woffset, _Hoffset), m); pixels[1] = tex1.SampleLevel(tex0Sampler, GetUV(idx + 1 + offset, m, (uint)_Woffset, _Hoffset), m); @@ -22,13 +22,13 @@ void GetData(Texture2D tex1, SamplerState tex0Sampler, inout uint data[2], half2 data[1] = ((uint)round(pixels[1].r * 255.0f) | ((uint)round(pixels[1].g * 255.0f) << 8) | ((uint)round(pixels[1].b * 255.0f) << 16) | ((uint)round(pixels[1].a * 255.0f) << 24)); } -half4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, inout uint data[2], half2 uv, int m) { +float4 GetPixel(Texture2D tex0, SamplerState tex0Sampler, inout uint data[2], float2 uv, int m) { int idx = GetIndex(uv, m); half r = ((data[idx & 1] & 0x000000FF) >> 0)/255.0f; half g = ((data[idx & 1] & 0x0000FF00) >> 8)/255.0f; half b = ((data[idx & 1] & 0x00FF0000) >> 16)/255.0f; half a = ((data[idx & 1] & 0xFF000000) >> 24)/255.0f; - half4 decrypt = half4(r, g, b, a); + float4 decrypt = float4(r, g, b, a); - return half4(GammaCorrection(decrypt.rgb), decrypt.a); + return float4(GammaCorrection(decrypt.rgb), decrypt.a); } diff --git a/Runtime/Shader/Utility.cginc b/Runtime/Shader/Utility.cginc index 8f9b665..7327664 100644 --- a/Runtime/Shader/Utility.cginc +++ b/Runtime/Shader/Utility.cginc @@ -1,21 +1,21 @@ #pragma once -half3 InverseGammaCorrection(half3 rgb) +float3 InverseGammaCorrection(float3 rgb) { - half3 result = pow(rgb, 0.454545); + float3 result = pow(rgb, 0.454545); return result; } -half3 GammaCorrection(half3 rgb) +float3 GammaCorrection(float3 rgb) { //half3 result = pow(rgb, 2.2); - half3 result = rgb * rgb * (rgb * (half)0.2 + (half)0.8); //fast pow + float3 result = rgb * rgb * (rgb * (half)0.2 + (half)0.8); //fast pow return result; } -half2 GetUV(int idx, int m, int woffset = 0, int hoffset = 0) +float2 GetUV(int idx, int m, int woffset = 0, int hoffset = 0) { int w = idx % mw[m + woffset]; int h = idx / mw[m + woffset]; - return half2((half)w/mw[m + woffset], (half)h/mh[m + hoffset]); + return float2((float)w/mw[m + woffset], (float)h/mh[m + hoffset]); } diff --git a/Runtime/liltoonProtector/Shaders/custom.hlsl b/Runtime/liltoonProtector/Shaders/custom.hlsl index e59464d..b772f18 100644 --- a/Runtime/liltoonProtector/Shaders/custom.hlsl +++ b/Runtime/liltoonProtector/Shaders/custom.hlsl @@ -1,6 +1,12 @@ //---------------------------------------------------------------------------------------------------------------------- // Macro +#define LIL_CUSTOM_V2F_MEMBER(id0,id1,id2,id3,id4,id5,id6,id7) \ + int isDecrypted : TEXCOORD##id0; + +#define LIL_CUSTOM_VERT_COPY \ + output.isDecrypted = IsDecrypted(); + #define LIL_CUSTOM_PROPERTIES\ half4 _EncryptTex0_TexelSize;\ int _PasswordHash; @@ -24,7 +30,7 @@ #define OVERRIDE_MAIN\ LIL_GET_MAIN_TEX\ UNITY_BRANCH\ - if(!IsDecrypted())\ + if(input.isDecrypted == 0)\ {\ LIL_APPLY_MAIN_TONECORRECTION\ fd.col *= _Color;\ From b839190d83a731568f7c9e50797dbc92b96aa6fe Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:07:07 +0900 Subject: [PATCH 44/59] fix: prevent repeated errors when the directory does not exist --- Runtime/Scripts/ShellProtector.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 8ba5a31..0683231 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -914,7 +914,6 @@ public int GetEncryptedFoldersCount() _assetDir = ResolveOutputAssetDir(); if (!Directory.Exists(_assetDir)) { - Debug.LogError($"The specified path does not exist: {_assetDir}"); return 0; } else From 05f8a735ee24cd4bb00229845f46ee703259f6db Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:07:47 +0900 Subject: [PATCH 45/59] chore!: drop support for Poiyomi 7.3 --- Runtime/Scripts/AssetManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Runtime/Scripts/AssetManager.cs b/Runtime/Scripts/AssetManager.cs index 239ceda..c678512 100644 --- a/Runtime/Scripts/AssetManager.cs +++ b/Runtime/Scripts/AssetManager.cs @@ -21,7 +21,6 @@ public static AssetManager GetInstance() } AssetManager() { - _supportedVersions.Add("Poiyomi 7.3", 73); _supportedVersions.Add("Poiyomi 8.0", 80); _supportedVersions.Add("Poiyomi 8.1", 81); _supportedVersions.Add("Poiyomi 8.2", 82); From bfd211d2453719f627f8a08ad46bea6a1e8d8403 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:08:22 +0900 Subject: [PATCH 46/59] feat: obfuscate playable layers other than FX --- Runtime/Scripts/ShellProtector.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 0683231..a58919e 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -788,12 +788,12 @@ public VRCExpressionParameters GetParameter() return av3.expressionParameters; } - public static AnimatorController GetFx(GameObject avatar) + public static AnimatorController GetFx(GameObject avatar, int playableLayer = 4) { var av3 = avatar.GetComponent(); if (av3 == null) return null; - return av3.baseAnimationLayers[4].animatorController as AnimatorController; + return av3.baseAnimationLayers[playableLayer].animatorController as AnimatorController; } public void ObfuscateBlendShape(GameObject avatar, bool clone) @@ -903,7 +903,13 @@ public void ObfuscateBlendShape(GameObject avatar, bool clone) } } #endif - obfuscator.ObfuscateBlendshapeInAnim(fx, selectRenderer.gameObject, paths, _assetWriter); + for (int i = 0; i <= 4; ++i) + { + AnimatorController playableLayer = GetFx(avatar, 0); + if (playableLayer == null) + continue; + obfuscator.ObfuscateBlendshapeInAnim(playableLayer, selectRenderer.gameObject, paths, _assetWriter); + } obfuscator.ChangeObfuscatedBlendShapeInDescriptor(av3); obfuscator.Clean(); } From 072ba8d7e44c5f35bc9deebe795d359d15884476 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:27:21 +0900 Subject: [PATCH 47/59] build: 2.6.0 --- README.ENG.md | 17 +++++++++++------ README.JP.md | 13 +++++++++---- README.md | 15 ++++++++++----- package.json | 2 +- version.json | 2 +- 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/README.ENG.md b/README.ENG.md index 39e451c..85d6b67 100644 --- a/README.ENG.md +++ b/README.ENG.md @@ -19,13 +19,15 @@ You can easily enter the password with the OSC program. Source code of OSC: https://github.com/Shell4026/ShellProtectorOSC ## Supported shaders -- Poiyomi 8.0, 8.1, 8.2, 9.0, 9.1, 9.2, 9.3(pro), PCSS(Need to more testing) -- lilToon (1.3.8 ~ 2.3.2)(VCC) - +- Poiyomi 8.x, 9.x, 10.x(pro), PCSS(Not recommended) +- lilToon (1.3.8 ~ 2.3.4)(VCC) + ## Supported texture formats - RGB24, RGBA32 - DXT1, DXT5 - The Crunch Compression format will auto-convert to DXT1 or DXT5. +- **BC7 is not supported** + - For textures without transparency, halving the size while keeping the RGB24 format will result in similar quality and texture size. ## Features - Texture Encryption @@ -38,7 +40,7 @@ Source code of OSC: https://github.com/Shell4026/ShellProtectorOSC 1. Right-click on your avatar and select "Shell Protector" to add the component. 2. Set a password and specify the Material or GameObject to encrypt. -(The steps below are not required when using Modular avatar) +(The steps(3,4) below are not required when using Modular avatar) 3. Click the Encrypt button. 4. Check the encryption via the Testor component in the new avatar and press the Done button. @@ -47,10 +49,13 @@ Source code of OSC: https://github.com/Shell4026/ShellProtectorOSC ### If your password is more than 4 digits (OSC) 1. Download ShellProtectorOSC.zip from the release, unzip it, and run ShellProtectorOSC.exe. (You only need to run it once the first time. If you use a reset avatar, keep it on.) 2. Replace your uploaded avatar and enter your user password in the OSC program. -3. If changing the password doesn't change the appearance of your avatar, try going to the Action menu - Options - OSC - Reset Config in VRChat. -4. If you're still having trouble, try clearing the C:\Users\UserName\AppData\LocalLow\VRChat\VRChat\OSC folder. +3. Starting with version 2.5.0, you must enable 'Parameter-multiplexing'. +4. If changing the password doesn't change the appearance of your avatar, try going to the Action menu - Options - OSC - Reset Config in VRChat. +5. If you're still having trouble, try clearing the C:\Users\UserName\AppData\LocalLow\VRChat\VRChat\OSC folder. ### Parameter-multiplexing +**Supported by default starting with version 2.5.0.** + Detailed principle:https://github.com/seanedwards/vrc-worldobject/blob/main/docs/parameter-multiplexing.md This is a parameter-saving technique. After checking, OSC must always be turned on and Parameter-multiplexing must be checked in the OSC program. diff --git a/README.JP.md b/README.JP.md index 09aa3d4..a1170f8 100644 --- a/README.JP.md +++ b/README.JP.md @@ -21,13 +21,15 @@ OSCプログラムで簡単にパスワードを入力することができま OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC ## 対応シェーダー -- Poiyomi 8.0, 8.1, 8.2, 9.0, 9.1, 9.2, 9.3(pro), PCSS(より多くのテストが必要) -- lilToon (1.3.8 ~ 2.3.2)(VCC) +- Poiyomi 8.x, 9.x, 10.x(pro), PCSS(推奨しません) +- lilToon (1.3.8 ~ 2.3.4)(VCC) ## 対応テクスチャ形式 - RGB24, RGBA32 - DXT1, DXT5 - Crunch Compressionフォーマットは自動的にDXT1またはDXT5に変換されます。 +- **BC7はサポートしていません** + - 透明度のないテクスチャの場合、RGB24でサイズを半分に縮小すると、同等の品質とテクスチャサイズが得られます。 ## サポートする機能 - テクスチャ暗号化 @@ -48,10 +50,13 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC ### 自分のパスワードが4桁以上の場合 (OSC) 1. ReleaseにあるShellProtectorOSC.zipをダウンロードし、解凍してShellProtectorOSC.exeを実行します。 (最初に一度だけ実行するだけです。リセットアバターやパラメータ多重化を使用する場合は、継続してオンにしてください。) 2. アップロードしたアバターに変更した後、OSCプログラムでユーザーパスワードを入力します。 -3. もしパスワードが変わってもアバターの外観に変化がなければ、VRChatでアクションメニュー - Options - OSC - Reset Configを押してください。 -4. それでも問題がある場合は、C:\Users\ユーザー\AppData\LocalLow\VRChat\VRChat\OSCフォルダを消去してください。 +3. バージョン2.5.0以降では、「Parameter-multiplexing」に必ずチェックを入れる必要があります。 +4. もしパスワードが変わってもアバターの外観に変化がなければ、VRChatでアクションメニュー - Options - OSC - Reset Configを押してください。 +5. それでも問題がある場合は、C:\Users\ユーザー\AppData\LocalLow\VRChat\VRChat\OSCフォルダを消去してください。 ### パラメータマルチプレックス +**バージョン2.5.0から標準でサポートされています。** + 詳細原理:https://github.com/seanedwards/vrc-worldobject/blob/main/docs/parameter-multiplexing.md パラメータ節約技術です。チェック後、OSCを常にオンにしておく必要があり、OSCプログラムにもParameter-multiplexingをチェックする必要があります。 diff --git a/README.md b/README.md index dd370bb..3e02412 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,15 @@ OSC 프로그램으로 간편하게 비밀번호를 입력할 수 있습니다. OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC ## 지원 셰이더 -- Poiyomi 8.0, 8.1, 8.2, 9.0, 9.1, 9.2, 9.3(pro), PCSS(테스트 더 필요) -- lilToon (1.3.8 ~ 2.3.2)(VCC) +- Poiyomi 8.x, 9.x, 10.x(pro) PCSS(권장하지 않음) +- lilToon (1.3.8 ~ 2.3.4)(VCC) ## 지원 텍스쳐 형식 - RGB24, RGBA32 - DXT1, DXT5 - Crunch Compression 포멧은 자동으로 DXT1이나 DXT5로 변환 됩니다. +- **BC7은 지원하지 않습니다** + - 투명도가 없는 텍스쳐의 경우 RGB24에 사이즈를 반으로 줄이면 비슷한 품질과 텍스쳐 사이즈를 보입니다. ## 지원하는 기능 - 텍스쳐 암호화 @@ -37,7 +39,7 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC 1. 아바타를 우클릭해 'Shell Protector'를 눌러 컴포넌트를 추가합니다. 2. 비밀번호를 설정해주고 암호화 할 메테리얼이나 게임오브젝트를 지정해줍니다. -(모듈러 사용시 아래 과정은 필요 없습니다.) +(모듈러 사용시 아래 과정(3,4)은 필요 없습니다.) 3. Encrypt 버튼을 누르세요. 4. 새로 생긴 아바타에 들어간 Testor컴포넌트를 통해 암호화 여부를 확인하고 완료 버튼을 누르세요. @@ -46,10 +48,13 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC ### 자신의 비밀번호가 4자리 이상인 경우 (OSC) 1. Release에 있는 ShellProtectorOSC.zip을 다운 후 압축을 풀고 ShellProtectorOSC.exe를 실행시킵니다. (최초 한 번만 실행하면 됩니다. 리셋 아바타나 파라미터 멀티플렉싱을 사용한다면 계속 켜두세요.) 2. 업로드 한 아바타로 바꾼 후 OSC프로그램에서 사용자 비밀번호를 입력합니다. -3. 만약 비밀번호가 바뀌어도 아바타의 외형에 변화가 없다면 VRChat에서 액션 메뉴 - Options - OSC - Reset Config를 눌러보세요. -4. 그래도 문제가 있다면 C:\Users\유저\AppData\LocalLow\VRChat\VRChat\OSC 폴더를 지워보세요. +3. 2.5.0이후부터 Parameter-multiplexing를 필수로 체크해야 합니다. +4. 만약 비밀번호가 바뀌어도 아바타의 외형에 변화가 없다면 VRChat에서 액션 메뉴 - Options - OSC - Reset Config를 눌러보세요. +5. 그래도 문제가 있다면 C:\Users\유저\AppData\LocalLow\VRChat\VRChat\OSC 폴더를 지워보세요. ### 파라미터 멀티플렉싱 +**2.5.0부터 기본적으로 지원합니다.** + 세부 원리:https://github.com/seanedwards/vrc-worldobject/blob/main/docs/parameter-multiplexing.md 파라미터 절약 기술입니다. 체크 후 OSC를 항상 켜둬야하며 OSC프로그램에도 Parameter-multiplexing을 체크 해야합니다. diff --git a/package.json b/package.json index d6e8549..1cf5999 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shell.protector", "displayName": "Shell Protector", - "version": "2.5.1-beta", + "version": "2.6.0", "unity": "2022.3", "description": "Texture encryption for VRChat. Save avatars from ripping", "author": { diff --git a/version.json b/version.json index f18a908..15fecae 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "latestVersion": "2.5.1-beta", "downloadPage": "/releases/tag/2.5.1-beta" } \ No newline at end of file +{ "latestVersion": "2.6.0", "downloadPage": "/releases/tag/2.6.0" } \ No newline at end of file From 17a77b94e964e08e8070f6e247077acdc387f9e0 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Mon, 13 Jul 2026 01:45:54 +0900 Subject: [PATCH 48/59] fix: Security issues!!! --- Runtime/Scripts/Pipeline/MaterialEncryptor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs index fd35645..7b2f64f 100644 --- a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs +++ b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs @@ -39,8 +39,8 @@ public Material CreateEncryptedMaterial(string folderGuid, string fileName, Mate var (widthOffset, heightOffset) = TextureEncryptManager.CalculateOffsets(originalTex); result.SetInteger(ShaderProperties.WidthOffset, widthOffset); result.SetInteger(ShaderProperties.HeightOffset, heightOffset); - for (int i = 0; i < keyBytes.Length; ++i) - result.SetFloat(ShaderProperties.KeyPrefix + i, keyBytes[i]); + //for (int i = 0; i < keyBytes.Length; ++i) + // result.SetFloat(ShaderProperties.KeyPrefix + i, keyBytes[i]); if (algorithm == (int)ShellProtectorAlgorithm.Chacha) { From d7c39e04985cbee0d9d67bb5162dfbf2d9171c5b Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Mon, 13 Jul 2026 01:49:16 +0900 Subject: [PATCH 49/59] build: 2.6.1 --- package.json | 2 +- version.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1cf5999..ba6acc7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shell.protector", "displayName": "Shell Protector", - "version": "2.6.0", + "version": "2.6.1", "unity": "2022.3", "description": "Texture encryption for VRChat. Save avatars from ripping", "author": { diff --git a/version.json b/version.json index 15fecae..9f16ac1 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "latestVersion": "2.6.0", "downloadPage": "/releases/tag/2.6.0" } \ No newline at end of file +{ "latestVersion": "2.6.1", "downloadPage": "/releases/tag/2.6.1" } \ No newline at end of file From a555329543a58833a52637f001b43b063b97c71d Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Mon, 13 Jul 2026 02:58:18 +0900 Subject: [PATCH 50/59] fix: Fix a bug where fixed key values were not reflected in the material --- Runtime/Scripts/Pipeline/MaterialEncryptor.cs | 6 +++--- Runtime/Scripts/ShellProtector.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs index 7b2f64f..b138787 100644 --- a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs +++ b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs @@ -18,7 +18,7 @@ public MaterialEncryptor(AssetWriter writer, bool turnOnAllSafetyFallback, int a this.rounds = rounds; } - public Material CreateEncryptedMaterial(string folderGuid, string fileName, Material source, Shader shader, Texture2D fallback, Texture2D mip, AuxiliaryTextures auxiliary, ProcessedTexture texture, byte[] keyBytes, IEncryptor encryptor, Injector injector) + public Material CreateEncryptedMaterial(string folderGuid, string fileName, Material source, Shader shader, Texture2D fallback, Texture2D mip, AuxiliaryTextures auxiliary, ProcessedTexture texture, byte[] keyBytes, int fixedKeySize, IEncryptor encryptor, Injector injector) { Material result = new Material(source.shader); result.CopyPropertiesFromMaterial(source); @@ -39,8 +39,8 @@ public Material CreateEncryptedMaterial(string folderGuid, string fileName, Mate var (widthOffset, heightOffset) = TextureEncryptManager.CalculateOffsets(originalTex); result.SetInteger(ShaderProperties.WidthOffset, widthOffset); result.SetInteger(ShaderProperties.HeightOffset, heightOffset); - //for (int i = 0; i < keyBytes.Length; ++i) - // result.SetFloat(ShaderProperties.KeyPrefix + i, keyBytes[i]); + for (int i = 0; i < fixedKeySize; ++i) + result.SetFloat(ShaderProperties.KeyPrefix + i, keyBytes[i]); if (algorithm == (int)ShellProtectorAlgorithm.Chacha) { diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index a58919e..86e0031 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -1264,7 +1264,7 @@ Texture2D GenerateFallbackTexture(string fileName, MatOption option, Texture2D m Material GenerateEncryptedMaterial(string fileName, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, AuxiliaryTextures otherTex, ProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) { MaterialEncryptor materialEncryptor = new MaterialEncryptor(_assetWriter, _turnOnAllSafetyFallback, _algorithm, _rounds); - Material newMat = materialEncryptor.CreateEncryptedMaterial(GetOutputPaths().Folders.MatGuid, fileName, mat, encryptedShader, fallback, mip, otherTex, processedTexture, keyBytes, encryptor, _injector); + Material newMat = materialEncryptor.CreateEncryptedMaterial(GetOutputPaths().Folders.MatGuid, fileName, mat, encryptedShader, fallback, mip, otherTex, processedTexture, keyBytes, _keySize, encryptor, _injector); Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(newMat)); if (!EncryptedMaterials.ContainsKey(mat)) From d5981b5cb5eb8d26c459362f7cd584c104d28be9 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Mon, 13 Jul 2026 03:11:43 +0900 Subject: [PATCH 51/59] fix: Fix a bug where shape keys were obfuscated only the base layer --- Runtime/Scripts/ShellProtector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 86e0031..6850a2a 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -905,7 +905,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool clone) #endif for (int i = 0; i <= 4; ++i) { - AnimatorController playableLayer = GetFx(avatar, 0); + AnimatorController playableLayer = GetFx(avatar, i); if (playableLayer == null) continue; obfuscator.ObfuscateBlendshapeInAnim(playableLayer, selectRenderer.gameObject, paths, _assetWriter); From 7942c738b2422e7a5b27f687db6846bcab498a0d Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Mon, 13 Jul 2026 03:19:50 +0900 Subject: [PATCH 52/59] build: 2.6.2 --- package.json | 2 +- version.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ba6acc7..3c38e57 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shell.protector", "displayName": "Shell Protector", - "version": "2.6.1", + "version": "2.6.2", "unity": "2022.3", "description": "Texture encryption for VRChat. Save avatars from ripping", "author": { diff --git a/version.json b/version.json index 9f16ac1..a9e2abb 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "latestVersion": "2.6.1", "downloadPage": "/releases/tag/2.6.1" } \ No newline at end of file +{ "latestVersion": "2.6.2", "downloadPage": "/releases/tag/2.6.2" } \ No newline at end of file From 26a30ac349feabe3a65dd077d624db2d78d57ce9 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Tue, 14 Jul 2026 00:39:06 +0900 Subject: [PATCH 53/59] fix: Playable layers were not copied when manual encryption --- Runtime/Scripts/AnimatorManager.cs | 38 +++++++++++-- Runtime/Scripts/ShellProtector.cs | 18 +++++-- Tests/Editor/Integration/PipelineTests.cs | 65 +++++++++++++++++++++++ 3 files changed, 114 insertions(+), 7 deletions(-) diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 6714c06..67a7ebc 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -17,15 +17,45 @@ public class AnimatorManager : ScriptableObject public static AnimatorController DuplicateAnimator(RuntimeAnimatorController anim, OutputPaths paths, AssetWriter writer) { - string dir = AssetDatabase.GetAssetPath(anim); - if (!writer.CopyAssetToFolder(dir, paths.Folders.AnimGuid, paths.ControllerName(anim), out string output)) + return DuplicateAnimators(new[] { anim }, paths, writer)[0]; + } + + public static AnimatorController[] DuplicateAnimators(RuntimeAnimatorController[] animators, OutputPaths paths, AssetWriter writer) + { + var sourcePaths = new string[animators.Length]; + var targetPaths = new Dictionary(); + + for (int i = 0; i < animators.Length; ++i) { - Debug.LogErrorFormat("Failed to copy a animator: {0}", anim.name); + RuntimeAnimatorController animator = animators[i]; + if (animator == null) + continue; + + string sourcePath = AssetDatabase.GetAssetPath(animator); + sourcePaths[i] = sourcePath; + if (targetPaths.ContainsKey(sourcePath)) + continue; + + if (!writer.CopyAssetToFolder(sourcePath, paths.Folders.AnimGuid, paths.ControllerName(animator), out string targetPath)) + { + Debug.LogErrorFormat("Failed to copy a animator: {0}", animator.name); + continue; + } + + targetPaths.Add(sourcePath, targetPath); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); - return AssetDatabase.LoadAssetAtPath(output, typeof(RuntimeAnimatorController)) as AnimatorController; + + var duplicatedAnimators = new AnimatorController[animators.Length]; + for (int i = 0; i < sourcePaths.Length; ++i) + { + if (!string.IsNullOrEmpty(sourcePaths[i]) && targetPaths.TryGetValue(sourcePaths[i], out string targetPath)) + duplicatedAnimators[i] = AssetDatabase.LoadAssetAtPath(targetPath); + } + + return duplicatedAnimators; } public static void CreateKeyAnimations(string animationDir, OutputPaths paths, AssetWriter writer, GameObject[] objs) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 6850a2a..141f732 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -726,14 +726,26 @@ static Texture2D GetLargestFallback(ProcessedTexture processedTexture) public void SetAnimations(GameObject avatar, bool clone) { var av3 = avatar.GetComponent(); - AnimatorController fx; OutputPaths paths = EnsureOutputFolders(); + AnimatorController fx; if (clone) - fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, paths, _assetWriter); + { + var sourceControllers = new RuntimeAnimatorController[av3.baseAnimationLayers.Length]; + for (int i = 0; i < av3.baseAnimationLayers.Length; ++i) + sourceControllers[i] = av3.baseAnimationLayers[i].animatorController; + + AnimatorController[] duplicatedLayers = AnimatorManager.DuplicateAnimators(sourceControllers, paths, _assetWriter); + for (int i = 0; i < duplicatedLayers.Length; ++i) + { + if (duplicatedLayers[i] != null) + av3.baseAnimationLayers[i].animatorController = duplicatedLayers[i]; + } + + fx = duplicatedLayers[4]; + } else fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; - av3.baseAnimationLayers[4].animatorController = fx; string animationDir = _assetWriter.ResolveFolderPath(paths.Folders.AnimGuid); GameObject[] meshArray = new GameObject[Meshes.Count]; diff --git a/Tests/Editor/Integration/PipelineTests.cs b/Tests/Editor/Integration/PipelineTests.cs index 16fb9e1..17e2562 100644 --- a/Tests/Editor/Integration/PipelineTests.cs +++ b/Tests/Editor/Integration/PipelineTests.cs @@ -53,6 +53,38 @@ public void ManualEncrypt_CreatesEncryptedAvatarAndRewritesProtectionAssets() AssertAnimationMaterialWasRewritten(encryptedAvatar, fixture.Material); } + [Test] + public void ManualEncrypt_DuplicatesNonFxControllerBeforeObfuscatingBlendShapes() + { + Fixture fixture = CreateFixture("ControllerIsolation"); + VRCAvatarDescriptor originalDescriptor = fixture.Avatar.GetComponent(); + AnimationClip originalClip = CreateBlendShapeClip("ControllerIsolation"); + AnimatorController originalController = CreateBlendShapeController("ControllerIsolation", originalClip); + originalDescriptor.baseAnimationLayers[0] = new VRCAvatarDescriptor.CustomAnimLayer + { + type = VRCAvatarDescriptor.AnimLayerType.Base, + isDefault = false, + animatorController = originalController + }; + + GameObject encryptedAvatar = fixture.Protector.Encrypt(false); + sceneObjects.Add(encryptedAvatar); + + AnimatorController sourceController = originalDescriptor.baseAnimationLayers[0].animatorController as AnimatorController; + AnimatorController encryptedController = encryptedAvatar.GetComponent() + .baseAnimationLayers[0].animatorController as AnimatorController; + AnimationClip sourceClip = GetFirstClip(sourceController); + AnimationClip encryptedClip = GetFirstClip(encryptedController); + + Assert.That(sourceController, Is.SameAs(originalController)); + Assert.That(encryptedController, Is.Not.SameAs(originalController)); + Assert.That(sourceClip, Is.SameAs(originalClip)); + Assert.That(encryptedClip, Is.Not.SameAs(originalClip)); + Assert.That(AnimationUtility.GetCurveBindings(sourceClip).Single().propertyName, Is.EqualTo("blendShape.Smile")); + Assert.That(AnimationUtility.GetCurveBindings(encryptedClip).Single().propertyName, Does.StartWith("blendShape.")); + Assert.That(AnimationUtility.GetCurveBindings(encryptedClip).Single().propertyName, Is.Not.EqualTo("blendShape.Smile")); + } + [Test] public void InPlaceEncrypt_RewritesOriginalAvatarWhenNdmfStyleStepsRun() { @@ -190,6 +222,39 @@ private static AnimationClip CreateMaterialClip(string name, Material material) return AssetDatabase.LoadAssetAtPath(path); } + private static AnimationClip CreateBlendShapeClip(string name) + { + AnimationClip clip = new AnimationClip(); + clip.name = name + "BlendShape"; + EditorCurveBinding binding = new EditorCurveBinding + { + path = "Body", + type = typeof(SkinnedMeshRenderer), + propertyName = "blendShape.Smile" + }; + AnimationUtility.SetEditorCurve(clip, binding, AnimationCurve.Constant(0f, 1f, 100f)); + string path = TestAssetScope.CreateAsset(clip, name + "/blendShape.anim"); + return AssetDatabase.LoadAssetAtPath(path); + } + + private static AnimatorController CreateBlendShapeController(string name, AnimationClip clip) + { + string path = TestAssetScope.GeneratedRoot + "/" + name + "/base.controller"; + TestAssetScope.EnsureFolder(TestAssetScope.GeneratedRoot + "/" + name); + AnimatorController controller = AnimatorController.CreateAnimatorControllerAtPath(path); + AnimatorControllerLayer layer = controller.layers[0]; + AnimatorState state = layer.stateMachine.AddState("BlendShape"); + state.motion = clip; + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + return AssetDatabase.LoadAssetAtPath(path); + } + + private static AnimationClip GetFirstClip(AnimatorController controller) + { + return controller.layers[0].stateMachine.states[0].state.motion as AnimationClip; + } + private static AnimatorController CreateFxController(string name, AnimationClip materialClip) { string path = TestAssetScope.GeneratedRoot + "/" + name + "/fx.controller"; From 07df1a14aade2bf1507926b8e9d090ac3fec4d75 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:11:17 +0900 Subject: [PATCH 54/59] fix: Fix a bug where part of the key was included in the material --- Runtime/Scripts/ShellProtector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index 141f732..48b37d3 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -1276,7 +1276,7 @@ Texture2D GenerateFallbackTexture(string fileName, MatOption option, Texture2D m Material GenerateEncryptedMaterial(string fileName, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, AuxiliaryTextures otherTex, ProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) { MaterialEncryptor materialEncryptor = new MaterialEncryptor(_assetWriter, _turnOnAllSafetyFallback, _algorithm, _rounds); - Material newMat = materialEncryptor.CreateEncryptedMaterial(GetOutputPaths().Folders.MatGuid, fileName, mat, encryptedShader, fallback, mip, otherTex, processedTexture, keyBytes, _keySize, encryptor, _injector); + Material newMat = materialEncryptor.CreateEncryptedMaterial(GetOutputPaths().Folders.MatGuid, fileName, mat, encryptedShader, fallback, mip, otherTex, processedTexture, keyBytes, 16 - _keySize, encryptor, _injector); Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(newMat)); if (!EncryptedMaterials.ContainsKey(mat)) From 987b179edcbaa2b0293c59122a71d634d028999d Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:11:33 +0900 Subject: [PATCH 55/59] build: 2.6.3 --- package.json | 2 +- version.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3c38e57..b609102 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shell.protector", "displayName": "Shell Protector", - "version": "2.6.2", + "version": "2.6.3", "unity": "2022.3", "description": "Texture encryption for VRChat. Save avatars from ripping", "author": { diff --git a/version.json b/version.json index a9e2abb..3c35077 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "latestVersion": "2.6.2", "downloadPage": "/releases/tag/2.6.2" } \ No newline at end of file +{ "latestVersion": "2.6.3", "downloadPage": "/releases/tag/2.6.3" } \ No newline at end of file From 3afd9e81574ce0ba54cdfcec0ea11bb0a0860655 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Wed, 15 Jul 2026 01:20:29 +0900 Subject: [PATCH 56/59] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bd60ec..5ab7f3b 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC **** -텍스쳐를 선택 후 Inspector에서 압축 포멧을 DXT1이나 DXT5중 하나로 바꿔주세요. (투명도가 있는 텍스쳐는 DXT5) +텍스쳐를 선택 후 Inspector에서 압축 포멧을 DXT1/RGB24나 DXT5/RGBA32중 하나로 바꿔주세요. (투명도가 있는 텍스쳐는 DXT5) ![texture](https://github.com/Shell4026/ShellProtector/assets/104874910/872f9d15-7b89-4381-b940-00514bd60638) @@ -105,6 +105,8 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC 이 경우 OSC 1.5.0에 추가된 Refresh rate를 조금 올려보시길 바랍니다. +파라미터 압축 에셋이 있는 경우에도 발생할 수 있습니다. + **<메테리얼에서 있던 텍스쳐가 빠지는 경우>** 메인 컬러와 같은 텍스쳐 사용시 보안상의 이유로 해당 텍스쳐는 빠집니다. From 3c8c31786f3c2f18c52973cc07908b35670a472a Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Wed, 15 Jul 2026 01:21:30 +0900 Subject: [PATCH 57/59] Update README.ENG.md --- README.ENG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.ENG.md b/README.ENG.md index a2ae330..b23c937 100644 --- a/README.ENG.md +++ b/README.ENG.md @@ -80,7 +80,7 @@ If you find the issue, please raise it in Issues. **[is not supported texture format! Error]** -Select the texture and change the compression format to either DXT1 or DXT5 in the Inspector. (DXT5 for textures with transparency) +Select the texture and change the compression format to either DXT1/RGB24 or DXT5/RGBA32 in the Inspector. (DXT5 for textures with transparency) ![texture](https://github.com/Shell4026/ShellProtector/assets/104874910/872f9d15-7b89-4381-b940-00514bd60638) @@ -106,6 +106,8 @@ When using parameter multiplexing, depending on the server or network conditions In this case, try increasing the refresh rate slightly, which was added in OSC 1.5.0. +This can also occur when there are parameter-compressed assets. + **[When a Texture that was present in a Material is missing]** When using a texture such as a main color/texture, it will be stripped for security reasons. From ce23f643605b758743022077fb38642b2db16527 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Wed, 15 Jul 2026 01:22:04 +0900 Subject: [PATCH 58/59] Update README.JP.md --- README.JP.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.JP.md b/README.JP.md index 0f8453a..4a533b2 100644 --- a/README.JP.md +++ b/README.JP.md @@ -81,7 +81,7 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC **** -テクスチャを選択後、Inspectorで圧縮フォーマットをDXT1またはDXT5のいずれかに変更してください。 (透明度のあるテクスチャはDXT5) +テクスチャを選択後、Inspectorで圧縮フォーマットをDXT1/RGB24またはDXT5/RGBA32のいずれかに変更してください。 (透明度のあるテクスチャはDXT5) ![texture](https://github.com/Shell4026/ShellProtector/assets/104874910/872f9d15-7b89-4381-b940-00514bd60638) @@ -107,6 +107,8 @@ lilToonのバグなので、無視してアップロードするか、3つの方 この場合、OSC 1.5.0で追加されたRefresh rateを少し上げてみてください。 +パラメータ圧縮されたアセットがある場合にも発生する可能性があります。 + **<マテリアルにあったテクスチャが消える場合>** メインカラーと同じテクスチャを使用する場合、セキュリティ上の理由でそのテクスチャは削除されます。 From 13e0df2a63df3c8600a27892109dd9d8b66d1c18 Mon Sep 17 00:00:00 2001 From: Shell <104874910+Shell4026@users.noreply.github.com> Date: Wed, 15 Jul 2026 01:30:48 +0900 Subject: [PATCH 59/59] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5ab7f3b..3596671 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2FShell4026%2FShellProtector&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) # VCC : https://shell4026.github.io/VCC/ +347042998-b6e62237-67fe-4a4b-a415-12080ca64f01 +https://Shell4026.github.io/VCC/index.json + + 권장 유니티 버전 : 2022 ## | 한국어 | [English](./README.ENG.md) | [日本語](./README.JP.md) |