diff --git a/Assets/VRM/UniGLTF/Scripts/Extensions/StringExtensions.cs b/Assets/VRM/UniGLTF/Scripts/Extensions/StringExtensions.cs index d181c7d40..92dbdbb7e 100644 --- a/Assets/VRM/UniGLTF/Scripts/Extensions/StringExtensions.cs +++ b/Assets/VRM/UniGLTF/Scripts/Extensions/StringExtensions.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Text.RegularExpressions; using UnityEngine; namespace UniGLTF @@ -63,6 +64,8 @@ namespace UniGLTF }; public static string EscapeFilePath(this string path) { + path = Regex.Replace(path, @"[\u0000-\u001F\u007F]", "+"); + foreach(var x in EscapeChars) { path = path.Replace(x, '+'); diff --git a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs index 3a5838a1e..7fcab98a8 100644 --- a/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs +++ b/Assets/VRM/UniVRM/Editor/Format/VRMExportSettings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using UniGLTF; +using UnityEditor; using UnityEngine; @@ -92,6 +93,11 @@ namespace VRM public bool RemoveVertexColor = false; #endregion + public static bool IsFileNameLengthTooLong(string fileName) + { + return fileName.Length > 64; + } + public struct Validation { /// @@ -190,7 +196,7 @@ namespace VRM if (ReduceBlendshape && Source.GetComponent() == null) { - yield return Validation.Error("ReduceBlendshapeSize is need VRMBlendShapeProxy, you need to convert to VRM once."); + yield return Validation.Error("ReduceBlendshapeSize needs VRMBlendShapeProxy. You need to convert to VRM once."); } var vertexColor = Source.GetComponentsInChildren().Any(x => x.sharedMesh.colors.Length > 0); @@ -228,6 +234,61 @@ namespace VRM yield return Validation.Warning(string.Format("unknown material '{0}' is used. this will export as `Standard` fallback", material.shader.name)); } + + foreach (var material in materials) + { + if (IsFileNameLengthTooLong(material.name)) + yield return Validation.Error(string.Format("FileName '{0}' is too long. ", material.name)); + } + + var textureNameList = new List(); + foreach (var material in materials) + { + var shader = material.shader; + int propertyCount = ShaderUtil.GetPropertyCount(shader); + for (int i = 0; i < propertyCount; i++) + { + if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv) + { + if ((material.GetTexture(ShaderUtil.GetPropertyName(shader, i)) != null)) + { + var textureName = material.GetTexture(ShaderUtil.GetPropertyName(shader, i)).name; + if (!textureNameList.Contains(textureName)) + textureNameList.Add(textureName); + } + } + } + } + + foreach (var textureName in textureNameList) + { + if (IsFileNameLengthTooLong(textureName)) + yield return Validation.Error(string.Format("FileName '{0}' is too long. ", textureName)); + } + + var vrmMeta = Source.GetComponent(); + if (vrmMeta != null && vrmMeta.Meta != null && vrmMeta.Meta.Thumbnail != null) + { + var thumbnailName = vrmMeta.Meta.Thumbnail.name; + if (IsFileNameLengthTooLong(thumbnailName)) + yield return Validation.Error(string.Format("FileName '{0}' is too long. ", thumbnailName)); + } + + var meshFilters = Source.GetComponentsInChildren(); + var meshesName = meshFilters.Select(x => x.sharedMesh.name).Distinct(); + foreach (var meshName in meshesName) + { + if (IsFileNameLengthTooLong(meshName)) + yield return Validation.Error(string.Format("FileName '{0}' is too long. ", meshName)); + } + + var skinnedmeshRenderers = Source.GetComponentsInChildren(); + var skinnedmeshesName = skinnedmeshRenderers.Select(x => x.sharedMesh.name).Distinct(); + foreach (var skinnedmeshName in skinnedmeshesName) + { + if (IsFileNameLengthTooLong(skinnedmeshName)) + yield return Validation.Error(string.Format("FileName '{0}' is too long. ", skinnedmeshName)); + } } /// diff --git a/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs b/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs new file mode 100644 index 000000000..ef6d0831c --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs @@ -0,0 +1,48 @@ +using NUnit.Framework; +using System.Linq; +using System.IO; + +namespace VRM +{ + public class InvalidFileNameTest + { + [Test] + [TestCase("VRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMVRMV", true)] + [TestCase("VRMFormatVRMFormatVRMFormatVRMFormatVRMFormatVRMFormatVRMFormat", false)] + [TestCase("UniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRM", true)] + [TestCase("UniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniVRMUniV", false)] + [TestCase("AliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAliciaAlicia", true)] + public void DetectFileNameLength(string fileName, bool isIllegal) + { + var result = VRMExportSettings.IsFileNameLengthTooLong(fileName); + Assert.AreEqual(result, isIllegal); + } + + [Test] + [TestCase("\u0000\u0042\u0062", true)] + [TestCase("\u0045\u0046\u0047\u0065\u0068\u0036", false)] + [TestCase("\u0043\u0045\u0047\u007F", true)] + [TestCase("\u0000\u0042\u0062", true)] + [TestCase("\u003A\u0039\u005C\u0060\u0074", false)] + [TestCase("\u005D\u006F\u001C\u007A\u0036\u0049", true)] + public void DetectControlCharacters(string fileName, bool isIllegal) + { + var result = fileName.Any(x => char.IsControl(x)); + Assert.AreEqual(result, isIllegal); + } + + [Test] + [TestCase("VRM|Alicia?VRM", true)] + [TestCase("UniVRMUniVRM:UniVRM", true)] + [TestCase("VRMIsVRFileFormat", false)] + [TestCase("AliciaAlicia", true)] + [TestCase("UniVRMIsVRMImplementationInUnityPlatform", false)] + [TestCase("Avator*Avator/Avator", true)] + public void DetectInvalidCharacters(string fileName, bool isIllegal) + { + char[] invalidPathChars = Path.GetInvalidFileNameChars(); + var result = fileName.Any(x => invalidPathChars.Contains(x)); + Assert.AreEqual(result, isIllegal); + } + } +} diff --git a/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs.meta b/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs.meta new file mode 100644 index 000000000..aa5f11465 --- /dev/null +++ b/Assets/VRM/UniVRM/Editor/Tests/InvalidFileNameTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8511ed091b59bca4da4fd280693b7c82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef b/Assets/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef index 2b3357214..0aa9a540b 100644 --- a/Assets/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef +++ b/Assets/VRM/UniVRM/Editor/Tests/UniVRM.Editor.Tests.asmdef @@ -2,7 +2,8 @@ "name": "UniVRM.Editor.Tests", "references": [ "VRM", - "UniJSON" + "UniJSON", + "UniVRM.Editor" ], "optionalUnityReferences": [ "TestAssemblies" @@ -11,5 +12,9 @@ "Editor" ], "excludePlatforms": [], - "allowUnsafeCode": false + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] } \ No newline at end of file