#region copyright
// ------------------------------------------------------------------------
// Copyright (C) 2013-2019 Dmitriy Yukhanov - focus [http://codestage.net]
// ------------------------------------------------------------------------
#endregion
#if UNITY_EDITOR
namespace CodeStage.AntiCheat.EditorCode
{
using Common;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
///
/// Class with utility functions to help with ACTk migrations after updates.
///
public class MigrateUtils
{
private const string MigrationVersion = "2";
///
/// Checks all prefabs in project for old version of obscured types and tries to migrate values to the new version.
///
[MenuItem(ACTkEditorGlobalStuff.WindowsMenuPath + "Migrate obscured types on prefabs...", false, 1100)]
public static void MigrateObscuredTypesOnPrefabs()
{
if (!EditorUtility.DisplayDialog("ACTk Obscured types migration",
"Are you sure you wish to scan all prefabs in your project and automatically migrate values to the new format?\n" + GetWhatMigratesString(),
"Yes", "No"))
{
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Obscured types migration was canceled by user.");
return;
}
AssetDatabase.SaveAssets();
var touchedCount = 0;
try
{
var objectsToMigrate = new List();
EditorUtility.DisplayProgressBar("Looking through objects", "Collecting data...", 0);
var assets = AssetDatabase.FindAssets("t:ScriptableObject");
var count = assets.Length;
for (var i = 0; i < count; i++)
{
var guid = assets[i];
var path = AssetDatabase.GUIDToAssetPath(guid);
if (!path.StartsWith("assets", StringComparison.OrdinalIgnoreCase)) continue;
var objects = AssetDatabase.LoadAllAssetsAtPath(path);
foreach (var unityObject in objects)
{
if (unityObject == null) continue;
if (unityObject.name == "Deprecated EditorExtensionImpl") continue;
objectsToMigrate.Add(unityObject);
}
}
assets = AssetDatabase.FindAssets("t:Prefab");
count = assets.Length;
for (var i = 0; i < count; i++)
{
var guid = assets[i];
var path = AssetDatabase.GUIDToAssetPath(guid);
if (!path.StartsWith("assets", StringComparison.OrdinalIgnoreCase)) continue;
var prefabRoot = AssetDatabase.LoadMainAssetAtPath(path) as GameObject;
if (prefabRoot == null) continue;
var components = prefabRoot.GetComponentsInChildren();
foreach (var component in components)
{
if (component == null) continue;
objectsToMigrate.Add(component);
}
}
count = objectsToMigrate.Count;
for (var i = 0; i < count; i++)
{
if (EditorUtility.DisplayCancelableProgressBar("Looking through objects", "Object " + (i + 1) + " from " + count,
i / (float)count))
{
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Obscured types migration was canceled by user.");
break;
}
var unityObject = objectsToMigrate[i];
var so = new SerializedObject(unityObject);
var modified = MigrateObject(so, unityObject.name);
if (modified)
{
touchedCount++;
so.ApplyModifiedProperties();
}
}
}
catch (Exception e)
{
Debug.LogError(e);
}
finally
{
AssetDatabase.SaveAssets();
EditorUtility.ClearProgressBar();
}
if (touchedCount > 0)
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Migrated obscured types on " + touchedCount + " objects.");
else
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "No objects were found for obscured types migration.");
}
///
/// Checks all scenes in project for old version of obscured types and tries to migrate values to the new version.
///
[MenuItem(ACTkEditorGlobalStuff.WindowsMenuPath + "Migrate obscured types in opened scene(s)...", false, 1101)]
public static void MigrateObscuredTypesInScene()
{
if (!EditorUtility.DisplayDialog("ACTk Obscured types migration",
"Are you sure you wish to scan all opened scenes and automatically migrate values to the new format?\n" + GetWhatMigratesString(),
"Yes", "No"))
{
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Obscured types migration was canceled by user.");
return;
}
var touchedCount = 0;
try
{
var allTransformsInOpenedScenes = Resources.FindObjectsOfTypeAll();
var count = allTransformsInOpenedScenes.Length;
var updateStep = Math.Max(count / 10, 1);
for (var i = 0; i < count; i++)
{
var transform = allTransformsInOpenedScenes[i];
if (i % updateStep == 0 && EditorUtility.DisplayCancelableProgressBar("Looking through objects", "Object " + (i + 1) + " from " + count,
i / (float)count))
{
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Obscured types migration was canceled by user.");
break;
}
if (transform == null) continue;
var components = transform.GetComponents();
foreach (var component in components)
{
if (component == null) continue;
var so = new SerializedObject(component);
var modified = MigrateObject(so, transform.name);
if (modified)
{
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(component.gameObject.scene);
touchedCount++;
so.ApplyModifiedProperties();
}
}
}
}
catch (Exception e)
{
Debug.LogError(e);
}
finally
{
if (touchedCount > 0)
{
EditorUtility.DisplayDialog(touchedCount + " objects migrated", "Objects with old obscured types migrated: " + touchedCount + ".\nPlease save your scenes to keep the changes.", "Fine");
UnityEditor.SceneManagement.EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
}
AssetDatabase.SaveAssets();
EditorUtility.ClearProgressBar();
}
if (touchedCount > 0)
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Migrated obscured types on " + touchedCount + " objects in opened scene(s).");
else
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "No objects were found in opened scene(s) for obscured types migration.");
}
private static string GetWhatMigratesString()
{
return "ObscuredFloat, ObscuredDouble, ObscuredVector2, ObscuredVector3 and ObscuredQuaternion will be migrated.";
}
private static bool MigrateObject(SerializedObject so, string label)
{
var modified = false;
var sp = so.GetIterator();
if (sp == null) return false;
while (sp.NextVisible(true))
{
if (sp.propertyType != SerializedPropertyType.Generic) continue;
var type = sp.type;
switch (type)
{
case "ObscuredDouble":
{
var hiddenValue = sp.FindPropertyRelative("hiddenValue");
if (hiddenValue == null) continue;
var fakeValue = sp.FindPropertyRelative("fakeValue");
var migratedVersion = sp.FindPropertyRelative("migratedVersion");
if (migratedVersion != null)
{
if (migratedVersion.stringValue == MigrationVersion)
{
if (!fakeValue.prefabOverride) continue;
}
migratedVersion.stringValue = MigrationVersion;
}
var hiddenValueOldProperty = sp.FindPropertyRelative("hiddenValueOldByte8");
var hiddenValueOld = default(ACTkByte8);
var oldValueExists = false;
if (hiddenValueOldProperty != null)
{
if (hiddenValueOldProperty.FindPropertyRelative("b1") != null)
{
hiddenValueOld.b1 = (byte)hiddenValueOldProperty.FindPropertyRelative("b1").intValue;
hiddenValueOld.b2 = (byte)hiddenValueOldProperty.FindPropertyRelative("b2").intValue;
hiddenValueOld.b3 = (byte)hiddenValueOldProperty.FindPropertyRelative("b3").intValue;
hiddenValueOld.b4 = (byte)hiddenValueOldProperty.FindPropertyRelative("b4").intValue;
hiddenValueOld.b5 = (byte)hiddenValueOldProperty.FindPropertyRelative("b5").intValue;
hiddenValueOld.b6 = (byte)hiddenValueOldProperty.FindPropertyRelative("b6").intValue;
hiddenValueOld.b7 = (byte)hiddenValueOldProperty.FindPropertyRelative("b7").intValue;
hiddenValueOld.b8 = (byte)hiddenValueOldProperty.FindPropertyRelative("b8").intValue;
if (hiddenValueOld.b1 != 0 ||
hiddenValueOld.b2 != 0 ||
hiddenValueOld.b3 != 0 ||
hiddenValueOld.b4 != 0 ||
hiddenValueOld.b5 != 0 ||
hiddenValueOld.b6 != 0 ||
hiddenValueOld.b7 != 0 ||
hiddenValueOld.b8 != 0)
{
oldValueExists = true;
}
}
}
if (oldValueExists)
{
var union = new LongBytesUnion {b8 = hiddenValueOld};
union.b8.Shuffle();
hiddenValue.longValue = union.l;
hiddenValueOldProperty.FindPropertyRelative("b1").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b2").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b3").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b4").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b5").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b6").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b7").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b8").intValue = 0;
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Migrated property " + sp.displayName + ":" +
type +
" at the object " + label);
modified = true;
}
break;
}
case "ObscuredFloat":
{
var hiddenValue = sp.FindPropertyRelative("hiddenValue");
if (hiddenValue == null) continue;
var fakeValue = sp.FindPropertyRelative("fakeValue");
var migratedVersion = sp.FindPropertyRelative("migratedVersion");
if (migratedVersion != null)
{
if (migratedVersion.stringValue == MigrationVersion)
{
if (!fakeValue.prefabOverride) continue;
}
migratedVersion.stringValue = MigrationVersion;
}
var hiddenValueOldProperty = sp.FindPropertyRelative("hiddenValueOldByte4");
var hiddenValueOld = default(ACTkByte4);
var oldValueExists = false;
if (hiddenValueOldProperty != null)
{
if (hiddenValueOldProperty.FindPropertyRelative("b1") != null)
{
hiddenValueOld.b1 = (byte)hiddenValueOldProperty.FindPropertyRelative("b1").intValue;
hiddenValueOld.b2 = (byte)hiddenValueOldProperty.FindPropertyRelative("b2").intValue;
hiddenValueOld.b3 = (byte)hiddenValueOldProperty.FindPropertyRelative("b3").intValue;
hiddenValueOld.b4 = (byte)hiddenValueOldProperty.FindPropertyRelative("b4").intValue;
if (hiddenValueOld.b1 != 0 ||
hiddenValueOld.b2 != 0 ||
hiddenValueOld.b3 != 0 ||
hiddenValueOld.b4 != 0)
{
oldValueExists = true;
}
}
}
if (oldValueExists)
{
var union = new FloatIntBytesUnion {b4 = hiddenValueOld};
union.b4.Shuffle();
hiddenValue.longValue = union.i;
hiddenValueOldProperty.FindPropertyRelative("b1").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b2").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b3").intValue = 0;
hiddenValueOldProperty.FindPropertyRelative("b4").intValue = 0;
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Migrated property " + sp.displayName + ":" +
type +
" at the object " + label);
modified = true;
}
break;
}
case "ObscuredVector2":
{
var hiddenValue = sp.FindPropertyRelative("hiddenValue");
if (hiddenValue == null) continue;
var fakeValue = sp.FindPropertyRelative("fakeValue");
var migratedVersion = sp.FindPropertyRelative("migratedVersion");
if (migratedVersion != null)
{
if (migratedVersion.stringValue == MigrationVersion)
{
if (!fakeValue.prefabOverride) continue;
}
migratedVersion.stringValue = MigrationVersion;
}
var hiddenValueX = hiddenValue.FindPropertyRelative("x");
var hiddenValueY = hiddenValue.FindPropertyRelative("y");
var union = new FloatIntBytesUnion {i = hiddenValueX.intValue};
union.b4.Shuffle();
hiddenValueX.intValue = union.i;
union.i = hiddenValueY.intValue;
union.b4.Shuffle();
hiddenValueY.intValue = union.i;
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Migrated property " + sp.displayName + ":" + type +
" at the object " + label);
modified = true;
break;
}
case "ObscuredVector3":
{
var hiddenValue = sp.FindPropertyRelative("hiddenValue");
if (hiddenValue == null) continue;
var fakeValue = sp.FindPropertyRelative("fakeValue");
var migratedVersion = sp.FindPropertyRelative("migratedVersion");
if (migratedVersion != null)
{
if (migratedVersion.stringValue == MigrationVersion)
{
if (!fakeValue.prefabOverride) continue;
}
migratedVersion.stringValue = MigrationVersion;
}
var hiddenValueX = hiddenValue.FindPropertyRelative("x");
var hiddenValueY = hiddenValue.FindPropertyRelative("y");
var hiddenValueZ = hiddenValue.FindPropertyRelative("z");
var union = new FloatIntBytesUnion {i = hiddenValueX.intValue};
union.b4.Shuffle();
hiddenValueX.intValue = union.i;
union.i = hiddenValueY.intValue;
union.b4.Shuffle();
hiddenValueY.intValue = union.i;
union.i = hiddenValueZ.intValue;
union.b4.Shuffle();
hiddenValueZ.intValue = union.i;
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Migrated property " + sp.displayName + ":" + type +
" at the object " + label);
modified = true;
break;
}
case "ObscuredQuaternion":
{
var hiddenValue = sp.FindPropertyRelative("hiddenValue");
if (hiddenValue == null) continue;
var fakeValue = sp.FindPropertyRelative("fakeValue");
var migratedVersion = sp.FindPropertyRelative("migratedVersion");
if (migratedVersion != null)
{
if (migratedVersion.stringValue == MigrationVersion)
{
if (!fakeValue.prefabOverride) continue;
}
migratedVersion.stringValue = MigrationVersion;
}
var hiddenValueX = hiddenValue.FindPropertyRelative("x");
var hiddenValueY = hiddenValue.FindPropertyRelative("y");
var hiddenValueZ = hiddenValue.FindPropertyRelative("z");
var hiddenValueW = hiddenValue.FindPropertyRelative("w");
var union = new FloatIntBytesUnion {i = hiddenValueX.intValue};
union.b4.Shuffle();
hiddenValueX.intValue = union.i;
union.i = hiddenValueY.intValue;
union.b4.Shuffle();
hiddenValueY.intValue = union.i;
union.i = hiddenValueZ.intValue;
union.b4.Shuffle();
hiddenValueZ.intValue = union.i;
union.i = hiddenValueW.intValue;
union.b4.Shuffle();
hiddenValueW.intValue = union.i;
Debug.Log(ACTkEditorGlobalStuff.LogPrefix + "Migrated property " + sp.displayName + ":" + type +
" at the object " + label);
modified = true;
break;
}
}
}
return modified;
}
/*private static void EncryptAndSetBytes(string val, SerializedProperty prop, string key)
{
var encrypted = ObscuredString.EncryptDecrypt(val, key);
var encryptedBytes = GetBytes(encrypted);
prop.ClearArray();
prop.arraySize = encryptedBytes.Length;
for (var i = 0; i < encryptedBytes.Length; i++)
{
prop.GetArrayElementAtIndex(i).intValue = encryptedBytes[i];
}
}
private static byte[] GetBytes(string str)
{
var bytes = new byte[str.Length * sizeof(char)];
Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
private static string GetString(byte[] bytes)
{
var chars = new char[bytes.Length / sizeof(char)];
Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}*/
[StructLayout(LayoutKind.Explicit)]
private struct LongBytesUnion
{
[FieldOffset(0)]
public long l;
[FieldOffset(0)]
public ACTkByte8 b8;
}
[StructLayout(LayoutKind.Explicit)]
internal struct FloatIntBytesUnion
{
[FieldOffset(0)]
public float f;
[FieldOffset(0)]
public int i;
[FieldOffset(0)]
public ACTkByte4 b4;
}
}
}
#endif