From 38d0204e7233b2dac3dcdc83f30065f4ec53ee2d Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 28 Aug 2019 17:30:35 +0200 Subject: [PATCH] [unity] Providing error dialog and verbose error log message in case of version mismatch. Also providing error description when viewing skeletonData in inspector. Closes #1463. --- .../Asset Types/SkeletonDataAssetInspector.cs | 34 +++- .../Editor/Utility/AssetUtility.cs | 151 +++++++---------- .../Asset Types/SkeletonDataAsset.cs | 51 +++--- .../Asset Types/SkeletonDataCompatibility.cs | 160 ++++++++++++++++++ .../SkeletonDataCompatibility.cs.meta | 12 ++ 5 files changed, 291 insertions(+), 117 deletions(-) create mode 100644 spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs create mode 100644 spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs.meta diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs index 720933b11..21aaace86 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs @@ -40,7 +40,7 @@ using System.Collections.Generic; using UnityEditor; using UnityEngine; -using Spine; +using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo; namespace Spine.Unity.Editor { using Event = UnityEngine.Event; @@ -69,6 +69,7 @@ namespace Spine.Unity.Editor { SkeletonData targetSkeletonData; readonly List warnings = new List(); + CompatibilityProblemInfo compatibilityProblemInfo = null; readonly SkeletonInspectorPreview preview = new SkeletonInspectorPreview(); GUIStyle activePlayButtonStyle, idlePlayButtonStyle; @@ -140,9 +141,9 @@ namespace Spine.Unity.Editor { return; } - targetSkeletonData = warnings.Count == 0 ? targetSkeletonDataAsset.GetSkeletonData(false) : null; + targetSkeletonData = NoProblems() ? targetSkeletonDataAsset.GetSkeletonData(false) : null; - if (targetSkeletonData != null && warnings.Count <= 0) { + if (targetSkeletonData != null && NoProblems()) { preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); } @@ -174,12 +175,15 @@ namespace Spine.Unity.Editor { // Header EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(target.name + " (SkeletonDataAsset)", Icons.spine), EditorStyles.whiteLargeLabel); if (targetSkeletonData != null) EditorGUILayout.LabelField("(Drag and Drop to instantiate.)", EditorStyles.miniLabel); - + // Main Serialized Fields using (var changeCheck = new EditorGUI.ChangeCheckScope()) { using (new SpineInspectorUtility.BoxScope()) DrawSkeletonDataFields(); + if (compatibilityProblemInfo != null) + return; + using (new SpineInspectorUtility.BoxScope()) { DrawAtlasAssetsFields(); HandleAtlasAssetsNulls(); @@ -197,11 +201,11 @@ namespace Spine.Unity.Editor { } } } - + // Unity Quirk: Some code depends on valid preview. If preview is initialized elsewhere, this can cause contents to change between Layout and Repaint events, causing GUILayout control count errors. - if (warnings.Count <= 0) + if (NoProblems()) preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); - + if (targetSkeletonData != null) { GUILayout.Space(20f); @@ -317,6 +321,12 @@ namespace Spine.Unity.Editor { } } EditorGUILayout.PropertyField(skeletonJSON, SpineInspectorUtility.TempContent(skeletonJSON.displayName, Icons.spine)); + + if (compatibilityProblemInfo != null) { + EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(compatibilityProblemInfo.DescriptionString(), Icons.warning), GUILayout.Height(52)); + return; + } + EditorGUILayout.DelayedFloatField(scale); //EditorGUILayout.PropertyField(scale); EditorGUILayout.Space(); EditorGUILayout.PropertyField(skeletonDataModifiers, true); @@ -581,12 +591,14 @@ namespace Spine.Unity.Editor { void PopulateWarnings () { warnings.Clear(); + compatibilityProblemInfo = null; if (skeletonJSON.objectReferenceValue == null) { warnings.Add("Missing Skeleton JSON"); } else { var fieldValue = (TextAsset)skeletonJSON.objectReferenceValue; - if (!AssetUtility.IsSpineData(fieldValue)) { + + if (!AssetUtility.IsSpineData(fieldValue, out compatibilityProblemInfo)) { warnings.Add("Skeleton data file is not a valid Spine JSON or binary file."); } else { #if SPINE_TK2D @@ -657,6 +669,10 @@ namespace Spine.Unity.Editor { EditorPrefs.SetString(LastSkinKey, skinName); } + bool NoProblems() { + return warnings.Count == 0 && compatibilityProblemInfo == null; + } + #region Preview Handlers void HandleOnDestroyPreview () { EditorApplication.update -= preview.HandleEditorUpdate; @@ -677,7 +693,7 @@ namespace Spine.Unity.Editor { } override public void OnInteractivePreviewGUI (Rect r, GUIStyle background) { - if (warnings.Count <= 0) { + if (NoProblems()) { preview.Initialize(this.Repaint, targetSkeletonDataAsset, this.LastSkinName); preview.HandleInteractivePreviewGUI(r, background); } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs index 550dd88b1..045ff2b1e 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs @@ -46,18 +46,17 @@ using System.IO; using System.Text; using System.Linq; using System.Reflection; -using System.Globalization; + +using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo; namespace Spine.Unity.Editor { + using PathAndProblemInfo = System.Collections.Generic.KeyValuePair; public static class AssetUtility { + public const string SkeletonDataSuffix = "_SkeletonData"; public const string AtlasSuffix = "_Atlas"; - static readonly int[][] compatibleBinaryVersions = { new[] { 3, 8, 0 }, new[] { 3, 9, 0 } }; - static readonly int[][] compatibleJsonVersions = { new[] { 3, 8, 0 }, new[] { 3, 9, 0 } }; - //static bool isFixVersionRequired = false; - /// HACK: This list keeps the asset reference temporarily during importing. /// /// In cases of very large projects/sufficient RAM pressure, when AssetDatabase.SaveAssets is called, @@ -125,7 +124,11 @@ namespace Spine.Unity.Editor { if (root == null || !root.ContainsKey("skins")) return requiredPaths; - foreach (Dictionary skinMap in (List)root["skins"]) { + var skinsList = root["skins"] as List; + if (skinsList == null) + return requiredPaths; + + foreach (Dictionary skinMap in skinsList) { if (!skinMap.ContainsKey("attachments")) continue; foreach (var slot in (Dictionary)skinMap["attachments"]) { @@ -243,7 +246,8 @@ namespace Spine.Unity.Editor { public static void ImportSpineContent (string[] imported, bool reimport = false) { var atlasPaths = new List(); var imagePaths = new List(); - var skeletonPaths = new List(); + var skeletonPaths = new List(); + CompatibilityProblemInfo compatibilityProblemInfo = null; foreach (string str in imported) { string extension = Path.GetExtension(str).ToLower(); @@ -263,13 +267,13 @@ namespace Spine.Unity.Editor { break; case ".json": var jsonAsset = AssetDatabase.LoadAssetAtPath(str); - if (jsonAsset != null && IsSpineData(jsonAsset)) - skeletonPaths.Add(str); + if (jsonAsset != null && IsSpineData(jsonAsset, out compatibilityProblemInfo)) + skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo)); break; case ".bytes": if (str.ToLower().EndsWith(".skel.bytes", System.StringComparison.Ordinal)) { - if (IsSpineData(AssetDatabase.LoadAssetAtPath(str))) - skeletonPaths.Add(str); + if (IsSpineData(AssetDatabase.LoadAssetAtPath(str), out compatibilityProblemInfo)) + skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo)); } break; } @@ -287,24 +291,32 @@ namespace Spine.Unity.Editor { // Import skeletons and match them with atlases. bool abortSkeletonImport = false; - foreach (string skeletonPath in skeletonPaths) { + foreach (var skeletonPathEntry in skeletonPaths) { + string skeletonPath = skeletonPathEntry.Key; + var compatibilityProblems = skeletonPathEntry.Value; if (skeletonPath.StartsWith("Packages")) continue; if (!reimport && CheckForValidSkeletonData(skeletonPath)) { - ReloadSkeletonData(skeletonPath); + ReloadSkeletonData(skeletonPath, compatibilityProblems); + continue; + } + + var loadedAsset = AssetDatabase.LoadAssetAtPath(skeletonPath); + if (compatibilityProblems != null) { + IngestIncompatibleSpineProject(loadedAsset, compatibilityProblems); continue; } string dir = Path.GetDirectoryName(skeletonPath); #if SPINE_TK2D - IngestSpineProject(AssetDatabase.LoadAssetAtPath(skeletonPath), null); + IngestSpineProject(loadedAsset, compatibilityProblems, null); #else var localAtlases = FindAtlasesAtPath(dir); var requiredPaths = GetRequiredAtlasRegions(skeletonPath); var atlasMatch = GetMatchingAtlas(requiredPaths, localAtlases); if (atlasMatch != null || requiredPaths.Count == 0) { - IngestSpineProject(AssetDatabase.LoadAssetAtPath(skeletonPath), atlasMatch); + IngestSpineProject(loadedAsset, atlasMatch); } else { SkeletonImportDialog(skeletonPath, localAtlases, requiredPaths, ref abortSkeletonImport); } @@ -338,7 +350,7 @@ namespace Spine.Unity.Editor { } } - static void ReloadSkeletonData (string skeletonJSONPath) { + static void ReloadSkeletonData (string skeletonJSONPath, CompatibilityProblemInfo compatibilityProblemInfo) { string dir = Path.GetDirectoryName(skeletonJSONPath); TextAsset textAsset = AssetDatabase.LoadAssetAtPath(skeletonJSONPath); DirectoryInfo dirInfo = new DirectoryInfo(dir); @@ -353,6 +365,11 @@ namespace Spine.Unity.Editor { if (Selection.activeObject == skeletonDataAsset) Selection.activeObject = null; + if (compatibilityProblemInfo != null) { + SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), textAsset); + return; + } + Debug.LogFormat("Changes to '{0}' detected. Clearing SkeletonDataAsset: {1}", skeletonJSONPath, localPath); skeletonDataAsset.Clear(); @@ -545,10 +562,34 @@ namespace Spine.Unity.Editor { #endregion #region Import SkeletonData (json or binary) - internal static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAssetBase[] atlasAssets) { + internal static string GetSkeletonDataAssetFilePath(TextAsset spineJson) { string primaryName = Path.GetFileNameWithoutExtension(spineJson.name); string assetPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(spineJson)); - string filePath = assetPath + "/" + primaryName + SkeletonDataSuffix + ".asset"; + return assetPath + "/" + primaryName + SkeletonDataSuffix + ".asset"; + } + + internal static SkeletonDataAsset IngestIncompatibleSpineProject(TextAsset spineJson, + CompatibilityProblemInfo compatibilityProblemInfo) { + + string filePath = GetSkeletonDataAssetFilePath(spineJson); + + if (spineJson == null) + return null; + + SkeletonDataAsset skeletonDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset)); + if (skeletonDataAsset == null) { + skeletonDataAsset = SkeletonDataAsset.CreateInstance(); + skeletonDataAsset.skeletonJSON = spineJson; + AssetDatabase.CreateAsset(skeletonDataAsset, filePath); + } + EditorUtility.SetDirty(skeletonDataAsset); + + SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), spineJson); + return skeletonDataAsset; + } + + internal static SkeletonDataAsset IngestSpineProject (TextAsset spineJson, params AtlasAssetBase[] atlasAssets) { + string filePath = GetSkeletonDataAssetFilePath(spineJson); #if SPINE_TK2D if (spineJson != null) { @@ -622,76 +663,10 @@ namespace Spine.Unity.Editor { return false; } - public static bool IsSpineData (TextAsset asset) { - if (asset == null) - return false; - - bool isSpineData = false; - string rawVersion = null; - - int[][] compatibleVersions; - if (asset.name.Contains(".skel")) { - try { - using (var memStream = new MemoryStream(asset.bytes)) { - rawVersion = SkeletonBinary.GetVersionString(memStream); - } - isSpineData = !(string.IsNullOrEmpty(rawVersion)); - compatibleVersions = compatibleBinaryVersions; - } catch (System.Exception e) { - Debug.LogErrorFormat("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e); - return false; - } - } else { - object obj = Json.Deserialize(new StringReader(asset.text)); - if (obj == null) { - Debug.LogErrorFormat("'{0}' is not valid JSON.", asset.name); - return false; - } - - var root = obj as Dictionary; - if (root == null) { - Debug.LogError("Parser returned an incorrect type."); - return false; - } - - isSpineData = root.ContainsKey("skeleton"); - if (isSpineData) { - var skeletonInfo = (Dictionary)root["skeleton"]; - object jv; - skeletonInfo.TryGetValue("spine", out jv); - rawVersion = jv as string; - } - - compatibleVersions = compatibleJsonVersions; - } - - // Version warning - if (isSpineData) { - string primaryRuntimeVersionDebugString = compatibleVersions[0][0] + "." + compatibleVersions[0][1]; - - if (string.IsNullOrEmpty(rawVersion)) { - Debug.LogWarningFormat("Skeleton '{0}' has no version information. It may be incompatible with your runtime version: spine-unity v{1}", asset.name, primaryRuntimeVersionDebugString); - } else { - string[] versionSplit = rawVersion.Split('.'); - bool match = false; - foreach (var version in compatibleVersions) { - bool primaryMatch = version[0] == int.Parse(versionSplit[0], CultureInfo.InvariantCulture); - bool secondaryMatch = version[1] == int.Parse(versionSplit[1], CultureInfo.InvariantCulture); - - // if (isFixVersionRequired) secondaryMatch &= version[2] <= int.Parse(jsonVersionSplit[2], CultureInfo.InvariantCulture); - - if (primaryMatch && secondaryMatch) { - match = true; - break; - } - } - - if (!match) - Debug.LogWarningFormat("Skeleton '{0}' (exported with Spine {1}) may be incompatible with your runtime version: spine-csharp v{2}", asset.name, rawVersion, primaryRuntimeVersionDebugString); - } - } - - return isSpineData; + public static bool IsSpineData (TextAsset asset, out CompatibilityProblemInfo compatibilityProblemInfo) { + SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(asset); + compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion); + return fileVersion != null; } #endregion diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs index f88c223c3..12af217e0 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs @@ -32,7 +32,7 @@ using System.Collections.Generic; using System.IO; using UnityEngine; -using Spine; +using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo; namespace Spine.Unity { @@ -114,26 +114,26 @@ namespace Spine.Unity { Clear(); return null; } - + // Disabled to support attachmentless/skinless SkeletonData. -// if (atlasAssets == null) { -// atlasAssets = new AtlasAsset[0]; -// if (!quiet) -// Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); -// Clear(); -// return null; -// } -// #if !SPINE_TK2D -// if (atlasAssets.Length == 0) { -// Clear(); -// return null; -// } -// #else -// if (atlasAssets.Length == 0 && spriteCollection == null) { -// Clear(); -// return null; -// } -// #endif + // if (atlasAssets == null) { + // atlasAssets = new AtlasAsset[0]; + // if (!quiet) + // Debug.LogError("Atlas not set for SkeletonData asset: " + name, this); + // Clear(); + // return null; + // } + // #if !SPINE_TK2D + // if (atlasAssets.Length == 0) { + // Clear(); + // return null; + // } + // #else + // if (atlasAssets.Length == 0 && spriteCollection == null) { + // Clear(); + // return null; + // } + // #endif if (skeletonData != null) return skeletonData; @@ -163,6 +163,17 @@ namespace Spine.Unity { bool isBinary = skeletonJSON.name.ToLower().Contains(".skel"); SkeletonData loadedSkeletonData; + #if UNITY_EDITOR + if (skeletonJSON) { + SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON); + CompatibilityProblemInfo compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion); + if (compatibilityProblemInfo != null) { + SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), skeletonJSON); + return null; + } + } + #endif + try { if (isBinary) loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale); diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs new file mode 100644 index 000000000..4c1a52a2d --- /dev/null +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs @@ -0,0 +1,160 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated May 1, 2019. Replaces all prior versions. + * + * Copyright (c) 2013-2019, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS + * INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using System.Globalization; + +namespace Spine.Unity { + + public static class SkeletonDataCompatibility { + + static readonly int[][] compatibleBinaryVersions = { new[] { 3, 9, 0 }, new[] { 3, 8, 0 } }; + static readonly int[][] compatibleJsonVersions = { new[] { 3, 9, 0 }, new[] { 3, 8, 0 } }; + + static bool wasVersionDialogShown = false; + + public enum SourceType { + Json, + Binary + } + + [System.Serializable] + public class VersionInfo { + public string rawVersion = null; + public int[] version = null; + public SourceType sourceType; + } + + [System.Serializable] + public class CompatibilityProblemInfo { + public VersionInfo actualVersion; + public int[][] compatibleVersions; + + public string DescriptionString () { + string compatibleVersionString = ""; + string optionalOr = null; + foreach (int[] version in compatibleVersions) { + compatibleVersionString += string.Format("{0}{1}.{2}", optionalOr, version[0], version[1]); + optionalOr = " or "; + } + return string.Format("Skeleton data could not be loaded. Data version: {0}. Required version: {1}.\nPlease re-export skeleton data with Spine {1} or change runtime to version {2}.{3}.", + actualVersion.rawVersion, compatibleVersionString, actualVersion.version[0], actualVersion.version[1]); + } + } + + #if UNITY_EDITOR + public static VersionInfo GetVersionInfo (TextAsset asset) { + if (asset == null) + return null; + + VersionInfo fileVersion = new VersionInfo(); + fileVersion.sourceType = asset.name.Contains(".skel") ? SourceType.Binary : SourceType.Json; + + if (fileVersion.sourceType == SourceType.Binary) { + try { + using (var memStream = new MemoryStream(asset.bytes)) { + fileVersion.rawVersion = SkeletonBinary.GetVersionString(memStream); + } + } + catch (System.Exception e) { + Debug.LogErrorFormat("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e); + return null; + } + } + else { + object obj = Json.Deserialize(new StringReader(asset.text)); + if (obj == null) { + Debug.LogErrorFormat("'{0}' is not valid JSON.", asset.name); + return null; + } + + var root = obj as Dictionary; + if (root == null) { + Debug.LogErrorFormat("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name); + return null; + } + + if (root.ContainsKey("skeleton")) { + var skeletonInfo = (Dictionary)root["skeleton"]; + object jv; + skeletonInfo.TryGetValue("spine", out jv); + fileVersion.rawVersion = jv as string; + } + } + + string primaryRuntimeVersionDebugString = compatibleBinaryVersions[0][0] + "." + compatibleBinaryVersions[0][1]; + if (string.IsNullOrEmpty(fileVersion.rawVersion)) { + Debug.LogWarningFormat("Skeleton '{0}' has no version information. It is incompatible with your runtime version: spine-unity v{1}", asset.name, primaryRuntimeVersionDebugString); + return null; + } + + var versionSplit = fileVersion.rawVersion.Split('.'); + try { + fileVersion.version = new[]{ int.Parse(versionSplit[0], CultureInfo.InvariantCulture), + int.Parse(versionSplit[1], CultureInfo.InvariantCulture) }; + } + catch (System.Exception e) { + Debug.LogErrorFormat("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e); + return null; + } + return fileVersion; + } + + public static CompatibilityProblemInfo GetCompatibilityProblemInfo (VersionInfo fileVersion) { + CompatibilityProblemInfo info = new CompatibilityProblemInfo(); + info.actualVersion = fileVersion; + info.compatibleVersions = (fileVersion.sourceType == SourceType.Binary) ? compatibleBinaryVersions + : compatibleJsonVersions; + + if (fileVersion == null) + return info; + + foreach (var compatibleVersion in info.compatibleVersions) { + bool majorMatch = fileVersion.version[0] == compatibleVersion[0]; + bool minorMatch = fileVersion.version[1] == compatibleVersion[1]; + if (majorMatch && minorMatch) { + return null; // is compatible, thus no problem info returned + } + } + return info; + } + + public static void DisplayCompatibilityProblem (string descriptionString, TextAsset spineJson) { + if (!wasVersionDialogShown) { + wasVersionDialogShown = true; + UnityEditor.EditorUtility.DisplayDialog("Version mismatch!", descriptionString, "OK"); + } + Debug.LogError(string.Format("Error importing skeleton '{0}': {1}", + spineJson.name, descriptionString), spineJson); + } + #endif // UNITY_EDITOR + } +} diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs.meta new file mode 100644 index 000000000..1df7111a1 --- /dev/null +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4224df6e20549f0449154531ae080201 +timeCreated: 1567002861 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: