|
@@ -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<string, CompatibilityProblemInfo>;
|
|
|
|
|
|
public static class AssetUtility {
|
|
|
+
|
|
|
public const string SkeletonDataSuffix = "_SkeletonData";
|
|
|
public const string AtlasSuffix = "_Atlas";
|
|
|
|
|
|
- static readonly int[][] compatibleBinaryVersions = { new[] { 3, 8, 0 } };
|
|
|
- static readonly int[][] compatibleJsonVersions = { new[] { 3, 8, 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<string, object> skinMap in (List<object>)root["skins"]) {
|
|
|
+ var skinsList = root["skins"] as List<object>;
|
|
|
+ if (skinsList == null)
|
|
|
+ return requiredPaths;
|
|
|
+
|
|
|
+ foreach (Dictionary<string, object> skinMap in skinsList) {
|
|
|
if (!skinMap.ContainsKey("attachments"))
|
|
|
continue;
|
|
|
foreach (var slot in (Dictionary<string, object>)skinMap["attachments"]) {
|
|
@@ -243,7 +246,8 @@ namespace Spine.Unity.Editor {
|
|
|
public static void ImportSpineContent (string[] imported, bool reimport = false) {
|
|
|
var atlasPaths = new List<string>();
|
|
|
var imagePaths = new List<string>();
|
|
|
- var skeletonPaths = new List<string>();
|
|
|
+ var skeletonPaths = new List<PathAndProblemInfo>();
|
|
|
+ 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<TextAsset>(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<TextAsset>(str)))
|
|
|
- skeletonPaths.Add(str);
|
|
|
+ if (IsSpineData(AssetDatabase.LoadAssetAtPath<TextAsset>(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<TextAsset>(skeletonPath);
|
|
|
+ if (compatibilityProblems != null) {
|
|
|
+ IngestIncompatibleSpineProject(loadedAsset, compatibilityProblems);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
string dir = Path.GetDirectoryName(skeletonPath);
|
|
|
|
|
|
#if SPINE_TK2D
|
|
|
- IngestSpineProject(AssetDatabase.LoadAssetAtPath<TextAsset>(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<TextAsset>(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<TextAsset>(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>();
|
|
|
+ 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<string, object>;
|
|
|
- if (root == null) {
|
|
|
- Debug.LogError("Parser returned an incorrect type.");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- isSpineData = root.ContainsKey("skeleton");
|
|
|
- if (isSpineData) {
|
|
|
- var skeletonInfo = (Dictionary<string, object>)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
|
|
|
|