[unity] Now detecting and warning when binary or json file has been exported with the opposite ending. Improved loading error reporting behaviour. Closes #1857.

This commit is contained in:
Harald Csaszar 2021-03-16 15:09:04 +01:00
parent 305bb21615
commit 417388d2e7
4 changed files with 126 additions and 38 deletions

View File

@ -625,9 +625,12 @@ namespace Spine.Unity.Editor {
warnings.Add("Missing Skeleton JSON");
} else {
var fieldValue = (TextAsset)skeletonJSON.objectReferenceValue;
if (!AssetUtility.IsSpineData(fieldValue, out compatibilityProblemInfo)) {
warnings.Add("Skeleton data file is not a valid Spine JSON or binary file.");
string problemDescription = null;
if (!AssetUtility.IsSpineData(fieldValue, out compatibilityProblemInfo, ref problemDescription)) {
if (problemDescription != null)
warnings.Add(problemDescription);
else
warnings.Add("Skeleton data file is not a valid Spine JSON or binary file.");
} else {
#if SPINE_TK2D
bool searchForSpineAtlasAssets = true;
@ -644,11 +647,12 @@ namespace Spine.Unity.Editor {
var actualAtlasAssets = targetSkeletonDataAsset.atlasAssets;
for (int i = 0; i < actualAtlasAssets.Length; i++) {
if (targetSkeletonDataAsset.atlasAssets[i] == null) {
if (actualAtlasAssets[i] == null) {
detectedNullAtlasEntry = true;
break;
} else {
atlasList.Add(actualAtlasAssets[i].GetAtlas());
if (actualAtlasAssets[i].MaterialCount > 0)
atlasList.Add(actualAtlasAssets[i].GetAtlas());
}
}
@ -658,8 +662,9 @@ namespace Spine.Unity.Editor {
List<string> missingPaths = null;
if (atlasAssets.arraySize > 0) {
missingPaths = AssetUtility.GetRequiredAtlasRegions(AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue));
foreach (var atlas in atlasList) {
if (atlas == null)
continue;
for (int i = 0; i < missingPaths.Count; i++) {
if (atlas.FindRegion(missingPaths[i]) != null) {
missingPaths.RemoveAt(i);

View File

@ -58,7 +58,18 @@ using System.Reflection;
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
namespace Spine.Unity.Editor {
using PathAndProblemInfo = System.Collections.Generic.KeyValuePair<string, CompatibilityProblemInfo>;
public class PathAndProblemInfo {
public string path;
public CompatibilityProblemInfo compatibilityProblems;
public string otherProblemDescription;
public PathAndProblemInfo (string path, CompatibilityProblemInfo compatibilityInfo, string otherProblemDescription) {
this.path = path;
this.compatibilityProblems = compatibilityInfo;
this.otherProblemDescription = otherProblemDescription;
}
}
public static class AssetUtility {
@ -276,17 +287,26 @@ namespace Spine.Unity.Editor {
case ".jpg":
imagePaths.Add(str);
break;
case ".json":
case ".json": {
var jsonAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(str);
if (jsonAsset != null && IsSpineData(jsonAsset, out compatibilityProblemInfo))
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo));
string problemDescription = null;
if (jsonAsset != null && IsSpineData(jsonAsset, out compatibilityProblemInfo, ref problemDescription))
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo, problemDescription));
if (problemDescription != null)
Debug.LogError(problemDescription, jsonAsset);
break;
case ".bytes":
}
case ".bytes": {
if (str.ToLower().EndsWith(".skel.bytes", System.StringComparison.Ordinal)) {
if (IsSpineData(AssetDatabase.LoadAssetAtPath<TextAsset>(str), out compatibilityProblemInfo))
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo));
var binaryAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(str);
string problemDescription = null;
if (IsSpineData(binaryAsset, out compatibilityProblemInfo, ref problemDescription))
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo, problemDescription));
if (problemDescription != null)
Debug.LogError(problemDescription, binaryAsset);
}
break;
}
}
}
@ -304,8 +324,9 @@ namespace Spine.Unity.Editor {
// Import skeletons and match them with atlases.
bool abortSkeletonImport = false;
foreach (var skeletonPathEntry in skeletonPaths) {
string skeletonPath = skeletonPathEntry.Key;
var compatibilityProblems = skeletonPathEntry.Value;
string skeletonPath = skeletonPathEntry.path;
var compatibilityProblems = skeletonPathEntry.compatibilityProblems;
string otherProblemDescription = skeletonPathEntry.otherProblemDescription;
if (skeletonPath.StartsWith("Packages"))
continue;
if (!reimport && CheckForValidSkeletonData(skeletonPath)) {
@ -318,6 +339,9 @@ namespace Spine.Unity.Editor {
IngestIncompatibleSpineProject(loadedAsset, compatibilityProblems);
continue;
}
if (otherProblemDescription != null) {
continue;
}
string dir = Path.GetDirectoryName(skeletonPath).Replace('\\', '/');
@ -378,8 +402,12 @@ namespace Spine.Unity.Editor {
if (usedSkeletonPath == null)
continue;
if (skeletonPaths.FindIndex(p => { return p.Key == usedSkeletonPath; } ) < 0) {
skeletonPaths.Add(new PathAndProblemInfo(usedSkeletonPath, null));
if (skeletonPaths.FindIndex(p => { return p.path == usedSkeletonPath; } ) < 0) {
string problemDescription = null;
CompatibilityProblemInfo compatibilityProblemInfo = null;
TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(usedSkeletonPath);
if (textAsset != null && IsSpineData(textAsset, out compatibilityProblemInfo, ref problemDescription))
skeletonPaths.Add(new PathAndProblemInfo(usedSkeletonPath, compatibilityProblemInfo, problemDescription));
}
}
}
@ -427,7 +455,8 @@ namespace Spine.Unity.Editor {
}
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true);
BlendModeMaterialsUtility.UpdateBlendModeMaterials(skeletonDataAsset, ref skeletonData);
if (skeletonData != null)
BlendModeMaterialsUtility.UpdateBlendModeMaterials(skeletonDataAsset, ref skeletonData);
string currentHash = skeletonData != null ? skeletonData.Hash : null;
@ -834,11 +863,10 @@ namespace Spine.Unity.Editor {
internal static SkeletonDataAsset IngestIncompatibleSpineProject(TextAsset spineJson,
CompatibilityProblemInfo compatibilityProblemInfo) {
string filePath = GetSkeletonDataAssetFilePath(spineJson);
if (spineJson == null)
return null;
string filePath = GetSkeletonDataAssetFilePath(spineJson);
SkeletonDataAsset skeletonDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset));
if (skeletonDataAsset == null) {
skeletonDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>();
@ -924,14 +952,14 @@ namespace Spine.Unity.Editor {
if (skeletonDataAsset != null && skeletonDataAsset.skeletonJSON == textAsset)
return true;
}
return false;
}
public static bool IsSpineData (TextAsset asset, out CompatibilityProblemInfo compatibilityProblemInfo) {
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(asset);
public static bool IsSpineData (TextAsset asset, out CompatibilityProblemInfo compatibilityProblemInfo, ref string problemDescription) {
bool isSpineSkeletonData;
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(asset, out isSpineSkeletonData, ref problemDescription);
compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
return fileVersion != null;
return isSpineSkeletonData;
}
#endregion

View File

@ -163,22 +163,29 @@ namespace Spine.Unity {
}
#endif
bool isBinary = skeletonJSON.name.ToLower().Contains(".skel");
bool hasBinaryExtension = skeletonJSON.name.ToLower().Contains(".skel");
SkeletonData loadedSkeletonData = null;
try {
if (isBinary)
if (hasBinaryExtension)
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
else
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
} catch (Exception ex) {
if (!quiet)
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this);
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, skeletonJSON);
}
#if UNITY_EDITOR
if (loadedSkeletonData == null && !quiet && skeletonJSON != null) {
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON);
string problemDescription = null;
bool isSpineSkeletonData;
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON, out isSpineSkeletonData, ref problemDescription);
if (problemDescription != null) {
if (!quiet)
Debug.LogError(problemDescription, skeletonJSON);
return null;
}
CompatibilityProblemInfo compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
if (compatibilityProblemInfo != null) {
SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), skeletonJSON);
@ -250,7 +257,6 @@ namespace Spine.Unity {
};
return json.ReadSkeletonData(input);
}
}
}

View File

@ -30,6 +30,7 @@
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using System;
#if UNITY_EDITOR
using System.Globalization;
using System.Text.RegularExpressions;
@ -63,8 +64,12 @@ namespace Spine.Unity {
public class CompatibilityProblemInfo {
public VersionInfo actualVersion;
public int[][] compatibleVersions;
public string explicitProblemDescription = null;
public string DescriptionString () {
if (!string.IsNullOrEmpty(explicitProblemDescription))
return explicitProblemDescription;
string compatibleVersionString = "";
string optionalOr = null;
foreach (int[] version in compatibleVersions) {
@ -77,12 +82,28 @@ namespace Spine.Unity {
}
#if UNITY_EDITOR
public static VersionInfo GetVersionInfo (TextAsset asset) {
public static VersionInfo GetVersionInfo (TextAsset asset, out bool isSpineSkeletonData, ref string problemDescription) {
isSpineSkeletonData = false;
if (asset == null)
return null;
VersionInfo fileVersion = new VersionInfo();
fileVersion.sourceType = asset.name.Contains(".skel") ? SourceType.Binary : SourceType.Json;
bool hasBinaryExtension = asset.name.Contains(".skel");
fileVersion.sourceType = hasBinaryExtension ? SourceType.Binary : SourceType.Json;
bool isJsonFileByContent = IsJsonFile(asset);
if (hasBinaryExtension == isJsonFileByContent) {
if (hasBinaryExtension) {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.skel.bytes' but content looks like a '.json' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
}
else {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.json' but content looks like binary 'skel.bytes' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
}
isSpineSkeletonData = false;
return null;
}
if (fileVersion.sourceType == SourceType.Binary) {
try {
@ -91,7 +112,8 @@ namespace Spine.Unity {
}
}
catch (System.Exception e) {
Debug.LogErrorFormat("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null;
}
}
@ -103,13 +125,15 @@ namespace Spine.Unity {
else {
object obj = Json.Deserialize(new StringReader(asset.text));
if (obj == null) {
Debug.LogErrorFormat("'{0}' is not valid JSON.", asset.name);
problemDescription = string.Format("'{0}' is not valid JSON.", asset.name);
isSpineSkeletonData = false;
return null;
}
var root = obj as Dictionary<string, object>;
if (root == null) {
Debug.LogErrorFormat("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name);
problemDescription = string.Format("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name);
isSpineSkeletonData = false;
return null;
}
@ -123,7 +147,8 @@ namespace Spine.Unity {
}
if (string.IsNullOrEmpty(fileVersion.rawVersion)) {
// very likely not a Spine skeleton json file at all.
// very likely not a Spine skeleton json file at all. Could be another valid json file, don't report errors.
isSpineSkeletonData = false;
return null;
}
@ -133,15 +158,39 @@ namespace Spine.Unity {
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);
problemDescription = string.Format("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null;
}
isSpineSkeletonData = true;
return fileVersion;
}
public static bool IsJsonFile (TextAsset file) {
string fileText = file.text;
const int maxCharsToCheck = 256;
int numCharsToCheck = Math.Min(fileText.Length, maxCharsToCheck);
if (fileText.IndexOf("\"skeleton\"", 0, numCharsToCheck) != -1 ||
fileText.IndexOf("\"hash\"", 0, numCharsToCheck) != -1 ||
fileText.IndexOf("\"spine\"", 0, numCharsToCheck) != -1)
return true;
int jsonCharCount = 0;
const string jsonChars = "{}:\",";
for (int i = 0; i < numCharsToCheck; ++i) {
char c = fileText[i];
if (jsonChars.IndexOf(c) != -1 || char.IsWhiteSpace(c))
++jsonCharCount;
}
if (jsonCharCount > numCharsToCheck / 10)
return true;
return false;
}
public static CompatibilityProblemInfo GetCompatibilityProblemInfo (VersionInfo fileVersion) {
if (fileVersion == null)
return null;
if (fileVersion == null) {
return null; // it's most likely not a Spine skeleton file, e.g. another json file. don't report problems.
}
CompatibilityProblemInfo info = new CompatibilityProblemInfo();
info.actualVersion = fileVersion;