update libs

This commit is contained in:
Kirill Chikalin
2025-02-13 17:48:12 +03:00
parent e17e7c2786
commit 275dc598c7
816 changed files with 22479 additions and 10792 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0a52c1c4a2b7caa458af5b9a212b80a5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,55 @@
using AssetStoreTools.Utility;
using AssetStoreTools.Validator.UI.Data.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace AssetStoreTools.Validator.Services
{
internal class CachingService : ICachingService
{
public bool GetCachedValidatorStateData(out ValidatorStateData stateData)
{
return GetCachedValidatorStateData(Constants.RootProjectPath, out stateData);
}
public bool GetCachedValidatorStateData(string projectPath, out ValidatorStateData stateData)
{
stateData = null;
if (!CacheUtil.GetFileFromProjectPersistentCache(projectPath, Constants.Cache.ValidationResultFile, out var filePath))
return false;
try
{
var serializerSettings = new JsonSerializerSettings()
{
ContractResolver = ValidatorStateDataContractResolver.Instance,
TypeNameHandling = TypeNameHandling.Auto,
Converters = new List<JsonConverter>() { new StringEnumConverter() }
};
stateData = JsonConvert.DeserializeObject<ValidatorStateData>(File.ReadAllText(filePath, Encoding.UTF8), serializerSettings);
return true;
}
catch
{
return false;
}
}
public void CacheValidatorStateData(ValidatorStateData stateData)
{
var serializerSettings = new JsonSerializerSettings()
{
ContractResolver = ValidatorStateDataContractResolver.Instance,
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.Auto,
Converters = new List<JsonConverter>() { new StringEnumConverter() }
};
CacheUtil.CreateFileInPersistentCache(Constants.Cache.ValidationResultFile, JsonConvert.SerializeObject(stateData, serializerSettings), true);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b2d545f659acb4343bf485ffb20ecf72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/CachingService.cs
uploadId: 724584

View File

@@ -0,0 +1,11 @@
using AssetStoreTools.Validator.UI.Data.Serialization;
namespace AssetStoreTools.Validator.Services
{
internal interface ICachingService : IValidatorService
{
void CacheValidatorStateData(ValidatorStateData stateData);
bool GetCachedValidatorStateData(out ValidatorStateData stateData);
bool GetCachedValidatorStateData(string projectPath, out ValidatorStateData stateData);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a8a3e36c133848447b043a91e709c63e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/ICachingService.cs
uploadId: 724584

View File

@@ -0,0 +1,26 @@
using Newtonsoft.Json.Serialization;
namespace AssetStoreTools.Previews.Services
{
internal class PreviewDatabaseContractResolver : DefaultContractResolver
{
private static PreviewDatabaseContractResolver _instance;
public static PreviewDatabaseContractResolver Instance => _instance ?? (_instance = new PreviewDatabaseContractResolver());
private NamingStrategy _namingStrategy;
private PreviewDatabaseContractResolver()
{
_namingStrategy = new SnakeCaseNamingStrategy();
}
protected override string ResolvePropertyName(string propertyName)
{
var resolvedName = _namingStrategy.GetPropertyName(propertyName, false);
if (resolvedName.StartsWith("_"))
return resolvedName.Substring(1);
return resolvedName;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: aee615e9aaf50fb4f989cd4698e8947e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/CachingService/PreviewDatabaseContractResolver.cs
uploadId: 724584

View File

@@ -0,0 +1,4 @@
namespace AssetStoreTools.Validator.Services
{
internal interface IValidatorService { }
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 075953f4ab4a65d4fae6e891360df0d0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/IValidatorService.cs
uploadId: 724584

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 184dcfbfe1d21454fa8cf49f1c637871
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ed0af5acc22551645ae4cb7d75bd1c36
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace AssetStoreTools.Validator.Services.Validation
{
internal interface IAssetUtilityService : IValidatorService
{
IEnumerable<string> GetAssetPathsFromAssets(string[] searchPaths, AssetType type);
IEnumerable<T> GetObjectsFromAssets<T>(string[] searchPaths, AssetType type) where T : Object;
IEnumerable<Object> GetObjectsFromAssets(string[] searchPaths, AssetType type);
string ObjectToAssetPath(Object obj);
T AssetPathToObject<T>(string assetPath) where T : Object;
Object AssetPathToObject(string assetPath);
AssetImporter GetAssetImporter(string assetPath);
AssetImporter GetAssetImporter(Object asset);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d28c5ea40f4c9954bae02804e416b898
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IAssetUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,7 @@
namespace AssetStoreTools.Validator.Services.Validation
{
internal interface IFileSignatureUtilityService : IValidatorService
{
ArchiveType GetArchiveType(string filePath);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 609c423482ecf8844a71166b4ef49cb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IFileSignatureUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
using UnityEngine;
namespace AssetStoreTools.Validator.Services.Validation
{
internal interface IMeshUtilityService : IValidatorService
{
IEnumerable<Mesh> GetCustomMeshesInObject(GameObject obj);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: acde6f9b97c9cac4b88a84aa9001a0fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IMeshUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
using UnityEngine;
namespace AssetStoreTools.Validator.Services.Validation
{
internal interface IModelUtilityService : IValidatorService
{
Dictionary<Object, List<LogEntry>> GetImportLogs(params Object[] models);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 91f6bacccdfecb84fb5ab0ba384353b4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IModelUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,13 @@
using UnityEngine;
using UnityEngine.SceneManagement;
namespace AssetStoreTools.Validator.Services.Validation
{
internal interface ISceneUtilityService : IValidatorService
{
string CurrentScenePath { get; }
Scene OpenScene(string scenePath);
GameObject[] GetRootGameObjects();
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: cf5ef331063e5aa4e95dfe3eadedf9af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/ISceneUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using Object = UnityEngine.Object;
namespace AssetStoreTools.Validator.Services.Validation
{
internal interface IScriptUtilityService : IValidatorService
{
IReadOnlyDictionary<MonoScript, IList<(string Name, string Namespace)>> GetTypeNamespacesFromScriptAssets(IList<MonoScript> monoScripts);
IReadOnlyDictionary<Object, IList<Type>> GetTypesFromAssemblies(IList<Object> assemblies);
IReadOnlyDictionary<MonoScript, IList<Type>> GetTypesFromScriptAssets(IList<MonoScript> monoScripts);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e0a9f88d37222e4428853b6d3d00b1bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Abstractions/IScriptUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,216 @@
using AssetStoreTools.Utility;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEngine;
using Object = UnityEngine.Object;
namespace AssetStoreTools.Validator.Services.Validation
{
internal class AssetUtilityService : IAssetUtilityService
{
public IEnumerable<string> GetAssetPathsFromAssets(string[] searchPaths, AssetType type)
{
string filter = string.Empty;
string[] extensions = null;
switch (type)
{
// General Types
case AssetType.All:
filter = "";
break;
case AssetType.Prefab:
filter = "t:prefab";
break;
case AssetType.Material:
filter = "t:material";
break;
case AssetType.Model:
filter = "t:model";
break;
case AssetType.Scene:
filter = "t:scene";
break;
case AssetType.Texture:
filter = "t:texture";
break;
case AssetType.Video:
filter = "t:VideoClip";
break;
// Specific Types
case AssetType.LossyAudio:
filter = "t:AudioClip";
extensions = new[] { ".mp3", ".ogg" };
break;
case AssetType.NonLossyAudio:
filter = "t:AudioClip";
extensions = new[] { ".wav", ".aif", ".aiff" };
break;
case AssetType.JavaScript:
filter = "t:TextAsset";
extensions = new[] { ".js" };
break;
case AssetType.Mixamo:
filter = "t:model";
extensions = new[] { ".fbx" };
break;
case AssetType.JPG:
filter = "t:texture";
extensions = new[] { ".jpg", "jpeg" };
break;
case AssetType.Executable:
filter = string.Empty;
extensions = new[] { ".exe", ".bat", ".msi", ".apk" };
break;
case AssetType.Documentation:
filter = string.Empty;
extensions = new[] { ".txt", ".pdf", ".html", ".rtf", ".md" };
break;
case AssetType.SpeedTree:
filter = string.Empty;
extensions = new[] { ".spm", ".srt", ".stm", ".scs", ".sfc", ".sme", ".st" };
break;
case AssetType.Shader:
filter = string.Empty;
extensions = new[] { ".shader", ".shadergraph", ".raytrace", ".compute" };
break;
case AssetType.MonoScript:
filter = "t:script";
extensions = new[] { ".cs" };
break;
case AssetType.UnityPackage:
filter = string.Empty;
extensions = new[] { ".unitypackage" };
break;
case AssetType.PrecompiledAssembly:
var assemblyPaths = GetPrecompiledAssemblies(searchPaths);
return assemblyPaths;
default:
return Array.Empty<string>();
}
var guids = AssetDatabase.FindAssets(filter, searchPaths);
var paths = guids.Select(AssetDatabase.GUIDToAssetPath);
if (extensions != null)
paths = paths.Where(x => extensions.Any(x.ToLower().EndsWith));
if (type == AssetType.Mixamo)
paths = paths.Where(IsMixamoFbx);
paths = paths.Distinct();
return paths;
}
public IEnumerable<T> GetObjectsFromAssets<T>(string[] searchPaths, AssetType type) where T : Object
{
var paths = GetAssetPathsFromAssets(searchPaths, type);
#if !AB_BUILDER
var objects = paths.Select(AssetDatabase.LoadAssetAtPath<T>).Where(x => x != null);
#else
var objects = new AssetEnumerator<T>(paths);
#endif
return objects;
}
public IEnumerable<Object> GetObjectsFromAssets(string[] searchPaths, AssetType type)
{
return GetObjectsFromAssets<Object>(searchPaths, type);
}
private IEnumerable<string> GetPrecompiledAssemblies(string[] searchPaths)
{
// Note - for packages, Compilation Pipeline returns full paths, as they appear on disk, not Asset Database
var allDllPaths = CompilationPipeline.GetPrecompiledAssemblyPaths(CompilationPipeline.PrecompiledAssemblySources.UserAssembly);
var rootProjectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length);
var packages = PackageUtility.GetAllLocalPackages();
var result = new List<string>();
foreach (var dllPath in allDllPaths)
{
var absoluteDllPath = Path.GetFullPath(dllPath).Replace("\\", "/");
foreach (var validationPath in searchPaths)
{
var absoluteValidationPath = Path.GetFullPath(validationPath).Replace("\\", "/");
if (absoluteDllPath.StartsWith(absoluteValidationPath))
{
int pathSeparatorLength = 1;
if (absoluteDllPath.StartsWith(Application.dataPath))
{
var adbPath = $"Assets/{absoluteDllPath.Remove(0, Application.dataPath.Length + pathSeparatorLength)}";
result.Add(adbPath);
}
else
{
// For non-Asset folder paths (i.e. local and embedded packages), convert disk path to ADB path
var package = packages.FirstOrDefault(x => dllPath.StartsWith(x.resolvedPath.Replace('\\', '/')));
if (package == null)
continue;
var dllPathInPackage = absoluteDllPath.Remove(0, Path.GetFullPath(package.resolvedPath).Length + pathSeparatorLength);
var adbPath = $"Packages/{package.name}/{dllPathInPackage}";
result.Add(adbPath);
}
}
}
}
return result;
}
private bool IsMixamoFbx(string fbxPath)
{
// Location of Mixamo Header, this is located in every mixamo fbx file exported
//const int mixamoHeader = 0x4c0 + 2; // < this is the original location from A$ Tools, unsure if Mixamo file headers were changed since then
const int mixamoHeader = 1622;
// Length of Mixamo header
const int length = 0xa;
var fs = new FileStream(fbxPath, FileMode.Open);
// Check if length is further than
if (fs.Length < mixamoHeader)
return false;
byte[] buffer = new byte[length];
using (BinaryReader reader = new BinaryReader(fs))
{
reader.BaseStream.Seek(mixamoHeader, SeekOrigin.Begin);
reader.Read(buffer, 0, length);
}
string result = System.Text.Encoding.ASCII.GetString(buffer);
return result.Contains("Mixamo");
}
public string ObjectToAssetPath(Object obj)
{
return AssetDatabase.GetAssetPath(obj);
}
public T AssetPathToObject<T>(string assetPath) where T : Object
{
return AssetDatabase.LoadAssetAtPath<T>(assetPath);
}
public Object AssetPathToObject(string assetPath)
{
return AssetPathToObject<Object>(assetPath);
}
public AssetImporter GetAssetImporter(string assetPath)
{
return AssetImporter.GetAtPath(assetPath);
}
public AssetImporter GetAssetImporter(Object asset)
{
return GetAssetImporter(ObjectToAssetPath(asset));
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9634968648d355c47b7cb12aead7abab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/AssetUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8dcc2f4da0b6cea4ab4733ebf32edab4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
namespace AssetStoreTools.Validator.Services.Validation
{
internal enum ArchiveType
{
None,
TarGz,
Zip,
Rar,
Tar,
TarZip,
Bz2,
LZip,
SevenZip,
GZip,
QuickZip,
Xz,
Wim
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 4061cb7aed3883346a66494c23e2e77b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/ArchiveType.cs
uploadId: 724584

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using Object = UnityEngine.Object;
namespace AssetStoreTools.Validator.Services.Validation
{
internal class AssetEnumerator<T> : IEnumerator<T>, IEnumerable<T> where T : Object
{
public const int Capacity = 32;
private Queue<string> _pathQueue;
private Queue<T> _loadedAssetQueue;
private T _currentElement;
public AssetEnumerator(IEnumerable<string> paths)
{
_pathQueue = new Queue<string>(paths);
_loadedAssetQueue = new Queue<T>();
}
public bool MoveNext()
{
bool hasPathsButHasNoAssets = _pathQueue.Count != 0 && _loadedAssetQueue.Count == 0;
if (hasPathsButHasNoAssets)
{
LoadMore();
}
bool dequeued = false;
if (_loadedAssetQueue.Count != 0)
{
_currentElement = _loadedAssetQueue.Dequeue();
dequeued = true;
}
return dequeued;
}
private void LoadMore()
{
int limit = Capacity;
while (limit > 0 && _pathQueue.Count != 0)
{
string path = _pathQueue.Dequeue();
T asset = AssetDatabase.LoadAssetAtPath<T>(path);
if (asset != null)
{
_loadedAssetQueue.Enqueue(asset);
limit--;
}
}
// Unload other loose asset references
EditorUtility.UnloadUnusedAssetsImmediate();
}
public void Reset()
{
throw new NotSupportedException("Asset Enumerator cannot be reset.");
}
public T Current => _currentElement;
object IEnumerator.Current => Current;
public void Dispose()
{
// No need to dispose
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return this;
}
public IEnumerator GetEnumerator()
{
return this;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0859579889cc56f4aa26eb863a1487b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetEnumerator.cs
uploadId: 724584

View File

@@ -0,0 +1,25 @@
namespace AssetStoreTools.Validator.Services.Validation
{
internal enum AssetType
{
All,
Documentation,
Executable,
JPG,
JavaScript,
LossyAudio,
Material,
Mixamo,
Model,
MonoScript,
NonLossyAudio,
PrecompiledAssembly,
Prefab,
Scene,
Shader,
SpeedTree,
Texture,
UnityPackage,
Video
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b81d00d4ed0a7da4289d4d6248ef9d34
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/AssetType.cs
uploadId: 724584

View File

@@ -0,0 +1,10 @@
using UnityEngine;
namespace AssetStoreTools.Validator.Services.Validation
{
internal class LogEntry
{
public string Message;
public LogType Severity;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a1e81104d6b0f4c449ee57503c3b6669
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/Data/LogEntry.cs
uploadId: 724584

View File

@@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
namespace AssetStoreTools.Validator.Services.Validation
{
internal class FileSignatureUtilityService : IFileSignatureUtilityService
{
private class FileSignature
{
public byte[] SignatureBytes;
public int Offset;
public FileSignature(byte[] signatureBytes, int offset)
{
SignatureBytes = signatureBytes;
Offset = offset;
}
}
private static readonly Dictionary<FileSignature, ArchiveType> ArchiveSignatures = new Dictionary<FileSignature, ArchiveType>
{
{ new FileSignature(new byte[] { 0x1f, 0x8b }, 0), ArchiveType.TarGz },
{ new FileSignature(new byte[] { 0x50, 0x4b, 0x03, 0x04 }, 0), ArchiveType.Zip },
{ new FileSignature(new byte[] { 0x50, 0x4b, 0x05, 0x06 }, 0), ArchiveType.Zip }, // Empty Zip Archive
{ new FileSignature(new byte[] { 0x50, 0x4b, 0x07, 0x08 }, 0), ArchiveType.Zip }, // Spanned Zip Archive
{ new FileSignature(new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }, 0), ArchiveType.Rar }, // RaR v1.50+
{ new FileSignature(new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }, 0), ArchiveType.Rar }, // RaR v5.00+
{ new FileSignature(new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }, 257), ArchiveType.Tar },
{ new FileSignature(new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }, 257), ArchiveType.Tar },
{ new FileSignature(new byte[] { 0x1f, 0x9d }, 0), ArchiveType.TarZip }, // TarZip LZW algorithm
{ new FileSignature(new byte[] { 0x1f, 0xa0 }, 0), ArchiveType.TarZip }, // TarZip LZH algorithm
{ new FileSignature(new byte[] { 0x42, 0x5a, 0x68 }, 0), ArchiveType.Bz2 },
{ new FileSignature(new byte[] { 0x4c, 0x5a, 0x49, 0x50 }, 0), ArchiveType.LZip },
{ new FileSignature(new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }, 0), ArchiveType.SevenZip },
{ new FileSignature(new byte[] { 0x1f, 0x8b }, 0), ArchiveType.GZip },
{ new FileSignature(new byte[] { 0x52, 0x53, 0x56, 0x4b, 0x44, 0x41, 0x54, 0x41 }, 0), ArchiveType.QuickZip },
{ new FileSignature(new byte[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }, 0), ArchiveType.Xz },
{ new FileSignature(new byte[] { 0x4D, 0x53, 0x57, 0x49, 0x4D, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00 }, 0), ArchiveType.Wim }
};
public ArchiveType GetArchiveType(string filePath)
{
if (!File.Exists(filePath))
return ArchiveType.None;
try
{
using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
foreach (var kvp in ArchiveSignatures)
{
var fileSignature = kvp.Key;
var archiveType = kvp.Value;
if (stream.Length < fileSignature.SignatureBytes.Length)
continue;
var bytes = new byte[fileSignature.SignatureBytes.Length];
stream.Seek(fileSignature.Offset, SeekOrigin.Begin);
stream.Read(bytes, 0, bytes.Length);
if (fileSignature.SignatureBytes.SequenceEqual(bytes.Take(fileSignature.SignatureBytes.Length)))
return archiveType;
}
}
}
catch (DirectoryNotFoundException)
{
Debug.LogWarning($"File '{filePath}' exists, but could not be opened for reading. Please make sure the project path lengths are not too long for the Operating System");
}
return ArchiveType.None;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 695ed79ad88c3b44b8ae41b650ebe16c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/FileSignatureUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace AssetStoreTools.Validator.Services.Validation
{
internal class MeshUtilityService : IMeshUtilityService
{
public IEnumerable<Mesh> GetCustomMeshesInObject(GameObject obj)
{
var meshes = new List<Mesh>();
var meshFilters = obj.GetComponentsInChildren<MeshFilter>(true);
var skinnedMeshes = obj.GetComponentsInChildren<SkinnedMeshRenderer>(true);
meshes.AddRange(meshFilters.Select(m => m.sharedMesh));
meshes.AddRange(skinnedMeshes.Select(m => m.sharedMesh));
meshes = meshes.Where(m => AssetDatabase.GetAssetPath(m).StartsWith("Assets/") ||
AssetDatabase.GetAssetPath(m).StartsWith("Packages/")).ToList();
return meshes;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 307f5dd7be983e246adbda52ac50ecf3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/MeshUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,147 @@
#if !UNITY_2022_2_OR_NEWER
using System;
using System.Reflection;
#endif
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
#if UNITY_2022_2_OR_NEWER
using UnityEditor.AssetImporters;
#endif
using UnityEngine;
using Object = UnityEngine.Object;
namespace AssetStoreTools.Validator.Services.Validation
{
internal class ModelUtilityService : IModelUtilityService
{
private IAssetUtilityService _assetUtility;
#if !UNITY_2022_2_OR_NEWER
// Rig fields
private const string RigImportWarningsField = "m_RigImportWarnings";
private const string RigImportErrorsField = "m_RigImportErrors";
// Animation fields
private const string AnimationImportWarningsField = "m_AnimationImportWarnings";
private const string AnimationImportErrorsField = "m_AnimationImportErrors";
private static Editor _modelImporterEditor = null;
#endif
public ModelUtilityService(IAssetUtilityService assetUtility)
{
_assetUtility = assetUtility;
}
public Dictionary<Object, List<LogEntry>> GetImportLogs(params Object[] models)
{
#if UNITY_2022_2_OR_NEWER
return GetImportLogsDefault(models);
#else
return GetImportLogsLegacy(models);
#endif
}
#if UNITY_2022_2_OR_NEWER
private Dictionary<Object, List<LogEntry>> GetImportLogsDefault(params Object[] models)
{
var modelsWithLogs = new Dictionary<Object, List<LogEntry>>();
foreach (var model in models)
{
var modelLogs = new List<LogEntry>();
var importLog = AssetImporter.GetImportLog(_assetUtility.ObjectToAssetPath(model));
if (importLog == null)
continue;
var entries = importLog.logEntries.Where(x => x.flags.HasFlag(ImportLogFlags.Warning) || x.flags.HasFlag(ImportLogFlags.Error));
foreach (var entry in entries)
{
var severity = entry.flags.HasFlag(ImportLogFlags.Error) ? LogType.Error : LogType.Warning;
modelLogs.Add(new LogEntry() { Message = entry.message, Severity = severity });
}
if (modelLogs.Count > 0)
modelsWithLogs.Add(model, modelLogs);
}
return modelsWithLogs;
}
#endif
#if !UNITY_2022_2_OR_NEWER
private Dictionary<Object, List<LogEntry>> GetImportLogsLegacy(params Object[] models)
{
var modelsWithLogs = new Dictionary<Object, List<LogEntry>>();
foreach (var model in models)
{
var modelLogs = new List<LogEntry>();
// Load the Model Importer
var modelImporter = _assetUtility.GetAssetImporter(model) as ModelImporter;
var editorAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.GetName().Name.Equals("UnityEditor"));
var modelImporterEditorType = editorAssembly.GetType("UnityEditor.ModelImporterEditor");
// Load its Model Importer Editor
Editor.CreateCachedEditorWithContext(new Object[] { modelImporter }, model, modelImporterEditorType, ref _modelImporterEditor);
// Find the base type
var modelImporterEditorTypeBase = _modelImporterEditor.GetType().BaseType;
// Get the tabs value
var tabsArrayType = modelImporterEditorTypeBase.GetRuntimeProperties().FirstOrDefault(x => x.Name == "tabs");
var tabsArray = (Array)tabsArrayType.GetValue(_modelImporterEditor);
// Get the tabs (Model | Rig | Animation | Materials)
var rigTab = tabsArray.GetValue(1);
var animationTab = tabsArray.GetValue(2);
var rigErrorsCheckSuccess = CheckFieldForSerializedProperty(rigTab, RigImportErrorsField, out var rigErrors);
var rigWarningsCheckSuccess = CheckFieldForSerializedProperty(rigTab, RigImportWarningsField, out var rigWarnings);
var animationErrorsCheckSuccess = CheckFieldForSerializedProperty(animationTab, AnimationImportErrorsField, out var animationErrors);
var animationWarningsCheckSuccess = CheckFieldForSerializedProperty(animationTab, AnimationImportWarningsField, out var animationWarnings);
if (!rigErrorsCheckSuccess || !rigWarningsCheckSuccess || !animationErrorsCheckSuccess || !animationWarningsCheckSuccess)
UnityEngine.Debug.LogWarning($"An error was encountered when checking import logs for model '{model.name}'");
if (!string.IsNullOrEmpty(rigWarnings))
modelLogs.Add(new LogEntry() { Message = rigWarnings, Severity = LogType.Warning });
if (!string.IsNullOrEmpty(rigErrors))
modelLogs.Add(new LogEntry() { Message = rigErrors, Severity = LogType.Error });
if (!string.IsNullOrEmpty(animationWarnings))
modelLogs.Add(new LogEntry() { Message = animationWarnings, Severity = LogType.Warning });
if (!string.IsNullOrEmpty(animationErrors))
modelLogs.Add(new LogEntry() { Message = animationErrors, Severity = LogType.Error });
if (modelLogs.Count > 0)
modelsWithLogs.Add(model, modelLogs);
}
return modelsWithLogs;
}
private static bool CheckFieldForSerializedProperty(object source, string propertyName, out string message)
{
message = string.Empty;
try
{
var propertyType = source.GetType().GetRuntimeFields().FirstOrDefault(x => x.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
var propertyValue = propertyType.GetValue(source) as SerializedProperty;
message = propertyValue.stringValue;
return true;
}
catch
{
return false;
}
}
#endif
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c50ca4c87e66f1b478279e5d1db4a08e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ModelUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,26 @@
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace AssetStoreTools.Validator.Services.Validation
{
internal class SceneUtilityService : ISceneUtilityService
{
public string CurrentScenePath => SceneManager.GetActiveScene().path;
public Scene OpenScene(string scenePath)
{
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
if (string.IsNullOrEmpty(scenePath) || AssetDatabase.LoadAssetAtPath<SceneAsset>(scenePath) == null)
return EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
else
return EditorSceneManager.OpenScene(scenePath);
}
public GameObject[] GetRootGameObjects()
{
return SceneManager.GetSceneByPath(CurrentScenePath).GetRootGameObjects();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 53e8deb0ebfb7ea47956f3a859580cd4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/SceneUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,658 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using UnityEditor;
using Object = UnityEngine.Object;
namespace AssetStoreTools.Validator.Services.Validation
{
internal class ScriptUtilityService : IScriptUtilityService
{
private const int ScriptTimeoutMs = 10000;
private const string IgnoredAssemblyCharacters = "!@#$%^*&()-+=[]{}\\|;:'\",.<>/?";
/// <summary>
/// For a given list of script assets, retrieves a list of types and their namespaces
/// </summary>
/// <param name="monoScripts"></param>
/// <returns>A dictionary mapping each script asset with a list of its types.
/// The type tuple contains a name (e.g. <i>class MyClass</i>) and its namespace (e.g. <i>MyNamespace</i>)
/// </returns>
public IReadOnlyDictionary<MonoScript, IList<(string Name, string Namespace)>> GetTypeNamespacesFromScriptAssets(IList<MonoScript> monoScripts)
{
var typesAndNamespaces = new Dictionary<MonoScript, IList<(string Name, string Namespace)>>();
var typeInfos = GetTypeInfosFromScriptAssets(monoScripts);
foreach (var kvp in typeInfos)
{
var namespacesInScript = new List<(string Name, string Namespace)>();
foreach (var typeInfo in kvp.Value)
{
bool isValidType = typeInfo.TypeName == ScriptParser.TypeName.Class || typeInfo.TypeName == ScriptParser.TypeName.Struct ||
typeInfo.TypeName == ScriptParser.TypeName.Interface || typeInfo.TypeName == ScriptParser.TypeName.Enum;
if (isValidType)
namespacesInScript.Add(($"{typeInfo.TypeName.ToString().ToLower()} {typeInfo.Name}", typeInfo.Namespace));
}
typesAndNamespaces.Add(kvp.Key, namespacesInScript);
}
return typesAndNamespaces;
}
/// <summary>
/// Scans the given precompiled assembly assets to retrieve a list of their contained types
/// </summary>
/// <param name="assemblies"></param>
/// <returns>A dictionary mapping each precompiled assembly asset with a list of <see cref="Type"> System.Type </see> objects.</returns>
public IReadOnlyDictionary<Object, IList<Type>> GetTypesFromAssemblies(IList<Object> assemblies)
{
var dllPaths = assemblies.ToDictionary(t => AssetDatabase.GetAssetPath(t), t => t);
var types = new ConcurrentDictionary<Object, IList<Type>>();
var failedDllPaths = new ConcurrentBag<string>();
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
Parallel.ForEach(dllPaths.Keys,
(assemblyPath) =>
{
try
{
var assembly = allAssemblies.FirstOrDefault(x => Path.GetFullPath(x.Location).Equals(Path.GetFullPath(assemblyPath), StringComparison.OrdinalIgnoreCase));
if (assembly == null)
return;
var assemblyTypes = assembly.GetTypes().Where(x => !IgnoredAssemblyCharacters.Any(c => x.Name.Contains(c))).ToList();
types.TryAdd(dllPaths[assemblyPath], assemblyTypes);
}
catch
{
failedDllPaths.Add(assemblyPath);
}
});
if (failedDllPaths.Count > 0)
{
var message = new StringBuilder("The following precompiled assemblies could not be checked:");
foreach (var path in failedDllPaths)
message.Append($"\n{path}");
UnityEngine.Debug.LogWarning(message);
}
// Types are sorted randomly due to parallelism, therefore need to be sorted before returning
var sortedTypes = dllPaths.Where(x => types.ContainsKey(x.Value))
.Select(x => new KeyValuePair<Object, IList<Type>>(x.Value, types[x.Value]))
.ToDictionary(t => t.Key, t => t.Value);
return sortedTypes;
}
/// <summary>
/// Scans the given script assets to retrieve a list of their contained types
/// </summary>
/// <param name="monoScripts"></param>
/// <returns>A dictionary mapping each precompiled assembly asset with a list of <see cref="Type"> System.Type </see> objects.</returns>
public IReadOnlyDictionary<MonoScript, IList<Type>> GetTypesFromScriptAssets(IList<MonoScript> monoScripts)
{
var realTypes = new Dictionary<MonoScript, IList<Type>>();
var typeInfos = GetTypeInfosFromScriptAssets(monoScripts);
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var kvp in typeInfos)
{
var realTypesInScript = new List<Type>();
foreach (var typeInfo in kvp.Value)
{
bool isValidType = typeInfo.TypeName == ScriptParser.TypeName.Class || typeInfo.TypeName == ScriptParser.TypeName.Struct ||
typeInfo.TypeName == ScriptParser.TypeName.Interface || typeInfo.TypeName == ScriptParser.TypeName.Enum;
if (isValidType)
{
var realType = assemblies.Where(a => a.GetType(typeInfo.GetReflectionFriendlyFullName()) != null)
.Select(a => a.GetType(typeInfo.GetReflectionFriendlyFullName())).FirstOrDefault();
if (realType != null)
realTypesInScript.Add(realType);
}
}
realTypes.Add(kvp.Key, realTypesInScript);
}
return realTypes;
}
/// <summary>
/// Scans the given MonoScript assets to retrieve a list of their contained types
/// </summary>
/// <param name="monoScripts"></param>
/// <returns>A dictionary mapping each script asset with a list of <see cref="TypeInfo"> TypeInfo </see> objects. </returns>
private IReadOnlyDictionary<MonoScript, IList<ScriptParser.BlockInfo>> GetTypeInfosFromScriptAssets(IList<MonoScript> monoScripts)
{
var types = new ConcurrentDictionary<MonoScript, IList<ScriptParser.BlockInfo>>();
var monoScriptContents = new Dictionary<MonoScript, string>();
var failedScripts = new ConcurrentBag<MonoScript>();
// A separate dictionary is needed because MonoScript contents cannot be accessed outside of the main thread
foreach (var kvp in monoScripts)
monoScriptContents.Add(kvp, kvp.text);
var tasks = new List<Tuple<Task, CancellationTokenSource>>();
try
{
foreach (var kvp in monoScriptContents)
{
var cancellationTokenSource = new CancellationTokenSource(ScriptTimeoutMs);
var task = Task.Run(() =>
{
var parsingTask = new ScriptParser(cancellationTokenSource.Token);
var parsed = parsingTask.GetTypesInScript(kvp.Value, out IList<ScriptParser.BlockInfo> parsedTypes);
if (parsed)
types.TryAdd(kvp.Key, parsedTypes);
else
failedScripts.Add(kvp.Key);
});
tasks.Add(new Tuple<Task, CancellationTokenSource>(task, cancellationTokenSource));
}
foreach (var t in tasks)
t.Item1.Wait();
}
finally
{
foreach (var t in tasks)
t.Item2.Dispose();
}
if (failedScripts.Count > 0)
{
var message = new StringBuilder("The following scripts could not be checked:");
foreach (var s in failedScripts)
message.Append($"\n{AssetDatabase.GetAssetPath(s)}");
UnityEngine.Debug.LogWarning(message);
}
// Types are sorted randomly due to parallelism, therefore need to be sorted before returning
var sortedTypes = monoScriptContents.Where(x => types.ContainsKey(x.Key))
.Select(x => new KeyValuePair<MonoScript, IList<ScriptParser.BlockInfo>>(x.Key, types[x.Key]))
.ToDictionary(t => t.Key, t => t.Value);
return sortedTypes;
}
/// <summary>
/// A simple script parser class to detect types declared within a script
/// </summary>
private class ScriptParser
{
/// <summary>
/// Types that can be identified by the script parser
/// </summary>
public enum TypeName
{
Undefined,
Namespace,
Class,
Struct,
Interface,
Enum,
IdentationStart,
IdentationEnd
}
/// <summary>
/// A class containing information about each block of a C# script
/// </summary>
/// <remarks> A block in this context is defined as script text that is contained within curly brackets.
/// If it's a type, it may have a preceding name and a namespace
/// </remarks>
public class BlockInfo
{
public TypeName TypeName = TypeName.Undefined;
public string Name = string.Empty;
public string FullName = string.Empty;
public string Namespace = string.Empty;
public int FoundIndex;
public int StartIndex;
public BlockInfo ParentBlock;
public string GetReflectionFriendlyFullName()
{
StringBuilder sb = new StringBuilder(FullName);
for (int i = sb.Length - 1; i >= Namespace.Length + 1; i--)
if (sb[i] == '.')
sb[i] = '+';
return sb.ToString();
}
}
private CancellationToken _token;
public ScriptParser(CancellationToken token)
{
_token = token;
}
public bool GetTypesInScript(string text, out IList<BlockInfo> types)
{
types = null;
try
{
var sanitized = SanitizeScript(text);
types = ScanForTypes(sanitized);
return true;
}
catch
{
return false;
}
}
private string SanitizeScript(string source)
{
var sb = new StringBuilder(source);
// Remove comments and strings
sb = RemoveStringsAndComments(sb);
// Replace newlines with spaces
sb.Replace("\r", " ").Replace("\n", " ");
// Space out the brackets
sb.Replace("{", " { ").Replace("}", " } ");
// Insert a space at the start for more convenient parsing
sb.Insert(0, " ");
// Remove repeating spaces
var sanitized = Regex.Replace(sb.ToString(), @"\s{2,}", " ");
return sanitized;
}
private StringBuilder RemoveStringsAndComments(StringBuilder sb)
{
void CheckStringIdentifiers(int index, out bool isVerbatim, out bool isInterpolated)
{
isVerbatim = false;
isInterpolated = false;
string precedingChars = string.Empty;
for (int i = index - 1; i >= 0; i--)
{
if (sb[i] == ' ')
break;
precedingChars += sb[i];
}
if (precedingChars.Contains("@"))
isVerbatim = true;
if (precedingChars.Contains("$"))
isInterpolated = true;
}
bool IsRegion(int index)
{
if (sb.Length - index < "#region".Length)
return false;
if (sb[index] == '#' && sb[index + 1] == 'r' && sb[index + 2] == 'e' && sb[index + 3] == 'g' && sb[index + 4] == 'i' &&
sb[index + 5] == 'o' && sb[index + 6] == 'n')
return true;
return false;
}
var removeRanges = new List<Tuple<int, int>>();
for (int i = 0; i < sb.Length; i++)
{
_token.ThrowIfCancellationRequested();
// Comment code
if (sb[i] == '/')
{
if (sb[i + 1] == '/')
{
for (int j = i + 1; j < sb.Length; j++)
{
_token.ThrowIfCancellationRequested();
if (sb[j] == '\n' || j == sb.Length - 1)
{
removeRanges.Add(new Tuple<int, int>(i, j - i + 1));
i = j;
break;
}
}
}
else if (sb[i + 1] == '*')
{
for (int j = i + 2; j < sb.Length; j++)
{
_token.ThrowIfCancellationRequested();
if (sb[j] == '/' && sb[j - 1] == '*')
{
removeRanges.Add(new Tuple<int, int>(i, j - i + 1));
i = j + 1;
break;
}
}
}
}
// Char code
else if (sb[i] == '\'')
{
for (int j = i + 1; j < sb.Length; j++)
{
_token.ThrowIfCancellationRequested();
if (sb[j] == '\'')
{
if (sb[j - 1] == '\\')
{
int slashCount = 0;
int k = j - 1;
while (sb[k--] == '\\')
slashCount++;
if (slashCount % 2 != 0)
continue;
}
removeRanges.Add(new Tuple<int, int>(i, j - i + 1));
i = j;
break;
}
}
}
// String code
else if (sb[i] == '"')
{
if (sb[i - 1] == '\'' && sb[i + 1] == '\'' || (sb[i - 2] == '\'' && sb[i - 1] == '\\' && sb[i + 1] == '\''))
continue;
CheckStringIdentifiers(i, out bool isVerbatim, out bool isInterpolated);
var bracketCount = 0;
bool interpolationEnd = true;
for (int j = i + 1; j < sb.Length; j++)
{
_token.ThrowIfCancellationRequested();
if (isInterpolated && (sb[j] == '{' || sb[j] == '}'))
{
if (sb[j] == '{')
{
if (sb[j + 1] != '{')
bracketCount++;
else
j += 1;
}
else if (sb[j] == '}')
{
if (sb[j + 1] != '}')
bracketCount--;
else
j += 1;
}
if (bracketCount == 0)
interpolationEnd = true;
else
interpolationEnd = false;
continue;
}
if (sb[j] == '\"')
{
if (isVerbatim)
{
if (sb[j + 1] != '\"')
{
if (!isInterpolated || isInterpolated && interpolationEnd == true)
{
removeRanges.Add(new Tuple<int, int>(i, j - i + 1));
i = j + 1;
break;
}
}
else
j += 1;
}
else
{
bool endOfComment = false;
if (sb[j - 1] != '\\')
endOfComment = true;
else
{
int slashCount = 0;
int k = j - 1;
while (sb[k--] == '\\')
slashCount++;
if (slashCount % 2 == 0)
endOfComment = true;
}
if (!isInterpolated && endOfComment || (isInterpolated && interpolationEnd && endOfComment))
{
removeRanges.Add(new Tuple<int, int>(i, j - i + 1));
i = j + 1;
break;
}
}
}
}
}
// Region code
else if (IsRegion(i))
{
i += "#region".Length;
for (int j = i; j < sb.Length; j++)
{
_token.ThrowIfCancellationRequested();
if (sb[j] == '\n')
{
removeRanges.Add(new Tuple<int, int>(i, j - i + 1));
i = j;
break;
}
}
}
}
for (int i = removeRanges.Count - 1; i >= 0; i--)
sb = sb.Remove(removeRanges[i].Item1, removeRanges[i].Item2);
return sb;
}
private IList<BlockInfo> ScanForTypes(string script)
{
var typeList = new SortedList<int, BlockInfo>();
BlockInfo currentActiveBlock = new BlockInfo();
int i = 0;
BlockInfo nextNamespace = null;
BlockInfo nextClass = null;
BlockInfo nextStruct = null;
BlockInfo nextInterface = null;
BlockInfo nextEnum = null;
while (i < script.Length)
{
_token.ThrowIfCancellationRequested();
if (nextNamespace == null)
nextNamespace = FindNextTypeBlock(script, i, TypeName.Namespace);
if (nextClass == null)
nextClass = FindNextTypeBlock(script, i, TypeName.Class);
if (nextStruct == null)
nextStruct = FindNextTypeBlock(script, i, TypeName.Struct);
if (nextInterface == null)
nextInterface = FindNextTypeBlock(script, i, TypeName.Interface);
if (nextEnum == null)
nextEnum = FindNextTypeBlock(script, i, TypeName.Enum);
var nextIdentationIncrease = FindNextTypeBlock(script, i, TypeName.IdentationStart);
var nextIdentationDecrease = FindNextTypeBlock(script, i, TypeName.IdentationEnd);
if (!TryFindClosestBlock(out var closestBlock, nextNamespace, nextClass,
nextStruct, nextInterface, nextEnum, nextIdentationIncrease, nextIdentationDecrease))
break;
switch (closestBlock)
{
case var _ when closestBlock == nextIdentationIncrease:
closestBlock.ParentBlock = currentActiveBlock;
currentActiveBlock = closestBlock;
break;
case var _ when closestBlock == nextIdentationDecrease:
if (currentActiveBlock.TypeName != TypeName.Undefined)
typeList.Add(currentActiveBlock.StartIndex, currentActiveBlock);
currentActiveBlock = currentActiveBlock.ParentBlock;
break;
case var _ when closestBlock == nextNamespace:
closestBlock.Namespace = currentActiveBlock.TypeName == TypeName.Namespace ? currentActiveBlock.FullName : currentActiveBlock.Namespace;
closestBlock.FullName = string.IsNullOrEmpty(currentActiveBlock.FullName) ? closestBlock.Name : $"{currentActiveBlock.FullName}.{closestBlock.Name}";
closestBlock.ParentBlock = currentActiveBlock;
currentActiveBlock = closestBlock;
nextNamespace = null;
break;
case var _ when closestBlock == nextClass:
case var _ when closestBlock == nextStruct:
case var _ when closestBlock == nextInterface:
case var _ when closestBlock == nextEnum:
closestBlock.FullName = string.IsNullOrEmpty(currentActiveBlock.FullName) ? closestBlock.Name : $"{currentActiveBlock.FullName}.{closestBlock.Name}";
closestBlock.Namespace = currentActiveBlock.TypeName == TypeName.Namespace ? currentActiveBlock.FullName : currentActiveBlock.Namespace;
closestBlock.ParentBlock = currentActiveBlock;
currentActiveBlock = closestBlock;
switch (closestBlock)
{
case var _ when closestBlock == nextClass:
nextClass = null;
break;
case var _ when closestBlock == nextStruct:
nextStruct = null;
break;
case var _ when closestBlock == nextInterface:
nextInterface = null;
break;
case var _ when closestBlock == nextEnum:
nextEnum = null;
break;
}
break;
}
i = closestBlock.StartIndex;
}
return typeList.Select(x => x.Value).ToList();
}
private bool TryFindClosestBlock(out BlockInfo closestBlock, params BlockInfo[] blocks)
{
closestBlock = null;
for (int i = 0; i < blocks.Length; i++)
{
if (blocks[i].FoundIndex == -1)
continue;
if (closestBlock == null || closestBlock.FoundIndex > blocks[i].FoundIndex)
closestBlock = blocks[i];
}
return closestBlock != null;
}
private BlockInfo FindNextTypeBlock(string text, int startIndex, TypeName blockType)
{
string typeKeyword;
switch (blockType)
{
case TypeName.Namespace:
typeKeyword = "namespace";
break;
case TypeName.Class:
typeKeyword = "class";
break;
case TypeName.Struct:
typeKeyword = "struct";
break;
case TypeName.Interface:
typeKeyword = "interface";
break;
case TypeName.Enum:
typeKeyword = "enum";
break;
case TypeName.IdentationStart:
var identationStart = text.IndexOf("{", startIndex);
return new BlockInfo() { FoundIndex = identationStart, StartIndex = identationStart + 1, TypeName = TypeName.Undefined };
case TypeName.IdentationEnd:
var identationEnd = text.IndexOf("}", startIndex);
return new BlockInfo() { FoundIndex = identationEnd, StartIndex = identationEnd + 1, TypeName = TypeName.Undefined };
default:
throw new ArgumentException("Invalid block type provided");
}
int start = -1;
int blockStart = -1;
string name = string.Empty;
while (startIndex < text.Length)
{
_token.ThrowIfCancellationRequested();
start = text.IndexOf($" {typeKeyword} ", startIndex);
if (start == -1)
return new BlockInfo { FoundIndex = -1 };
// Check if the caught type keyword matches the type definition
var openingBracket = text.IndexOf("{", start);
if (openingBracket == -1)
return new BlockInfo { FoundIndex = -1 };
var declaration = text.Substring(start, openingBracket - start);
var split = declaration.Split(' ');
// Namespace detection
if (typeKeyword == "namespace")
{
// Expected result: [null] [namespace] [null]
if (split.Length == 4)
{
name = split[2];
blockStart = openingBracket + 1;
break;
}
else
startIndex = openingBracket + 1;
}
// Class, Interface, Struct, Enum detection
else
{
// Expected result: [null] [keywordName] [typeName] ... [null]
// Skip any keywords that only contains [null] [keywordName] [null]
if (split.Length != 3)
{
name = split[2];
blockStart = openingBracket + 1;
break;
}
else
startIndex = openingBracket + 1;
}
}
var info = new BlockInfo() { FoundIndex = start, StartIndex = blockStart, Name = name, TypeName = blockType };
return info;
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9db4298044e2add44bc3aa6ba898d7c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/Validation/ScriptUtilityService.cs
uploadId: 724584

View File

@@ -0,0 +1,24 @@
using AssetStoreTools.Utility;
using AssetStoreTools.Validator.Services.Validation;
namespace AssetStoreTools.Validator.Services
{
internal class ValidatorServiceProvider : ServiceProvider<IValidatorService>
{
public static ValidatorServiceProvider Instance => _instance ?? (_instance = new ValidatorServiceProvider());
private static ValidatorServiceProvider _instance;
private ValidatorServiceProvider() { }
protected override void RegisterServices()
{
Register<ICachingService, CachingService>();
Register<IAssetUtilityService, AssetUtilityService>();
Register<IFileSignatureUtilityService, FileSignatureUtilityService>();
Register<IMeshUtilityService, MeshUtilityService>();
Register<IModelUtilityService, ModelUtilityService>();
Register<ISceneUtilityService, SceneUtilityService>();
Register<IScriptUtilityService, ScriptUtilityService>();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 47ac495c61171824abb2b72b1b7ef676
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 115
packageName: Asset Store Publishing Tools
packageVersion: 12.0.1
assetPath: Packages/com.unity.asset-store-tools/Editor/Validator/Scripts/Services/ValidatorServiceProvider.cs
uploadId: 724584