#if UNITY_EDITOR using System.Collections.Generic; using System.Reflection; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; namespace ConjureOS.MetadataWindow { public class ConjureArcadeMetadataValidator { // String errors private const string EmptyStringError = "Text field cannot be empty. "; private const string CharLimitExceededError = "The field should contain less than {0} characters. "; private const string VersionFormatError = "The version doesn't match the required format 'x.y.z'."; private const string DevelopersCharLimitExceedError = "One or more names exceed the maximum characters limit of {0}."; // Min/max player errors private const string IncorrectPlayerValueError = "Incorrect value. The number of players can't be 0 or lower. "; private const string MinPlayerHigherThanMaxError = "Minimum number of players can't be higher than the maximum. "; private const string MaxPlayerLowerThanMinError = "Maximum number of players can't be lower than the minimum. "; // Image errors private const string MissingImageError = "No image was selected. "; private const string InvalidAspectRatioError = "Invalid aspect ratio. Aspect ratio of the image should be {0}:{1}. "; private const string InvalidFileSizeError = "Invalid file size. File size should be lower than {0} MB. "; private const string InvalidImagePathError = "Invalid path. Chosen picture should be placed inside the 'Assets/' folder. "; // Other errors private const string SameGenreSelectedMultipleTimeError = "Same genre is selected twice or more"; // Metadata error messages private string gameTitleErrorMessage = ""; public string GameTitleErrorMessage => gameTitleErrorMessage; private string versionErrorMessage = ""; public string VersionErrorMessage => versionErrorMessage; private string descriptionErrorMessage = ""; public string DescriptionErrorMessage => descriptionErrorMessage; private string developersErrorMessage = ""; public string DevelopersErrorMessage => developersErrorMessage; private string minNumPlayerErrorMessage = ""; public string MinNumPlayerErrorMessage => minNumPlayerErrorMessage; private string maxNumPlayerErrorMessage = ""; public string MaxNumPlayerErrorMessage => maxNumPlayerErrorMessage; private string thumbnailErrorMessage = ""; public string ThumbnailErrorMessage => thumbnailErrorMessage; private string gameplayImageErrorMessage = ""; public string GameplayImageErrorMessage => gameplayImageErrorMessage; private string genresErrorMessage = ""; public string GenresErrorMessage => genresErrorMessage; private List developersErrorIndex = new List(); // Used to indicate which index have an error public List DevelopersErrorIndex => developersErrorIndex; private int errorCount = -1; // Starts at -1 because no validation have been done yet public int ErrorCount => errorCount; /// /// Validate the specified game metadata /// /// The metadata to be validated /// Return true if metadata are valid. Otherwise, returns false. public bool ValidateMetadata(ConjureArcadeMetadata metadata) { errorCount = 0; // Validate text fields ValidateTextField(metadata.GameTitle, ConjureArcadeMetadata.GameTitleLimit, out gameTitleErrorMessage); ValidateVersion(metadata.Version); ValidateTextField(metadata.Description, ConjureArcadeMetadata.DescriptionLimit, out descriptionErrorMessage); // Validate player fields ValidateNumPlayerFields(metadata.MinNumPlayer, metadata.MaxNumPlayer); // Validate image fields Texture2D thumbnailTexture = metadata.Thumbnail; ValidateImageField(thumbnailTexture, ConjureArcadeMetadata.RequiredThumbnailAR, ConjureArcadeMetadata.MaxThumbnailFileSize, out thumbnailErrorMessage); Texture2D gameplayImageTexture = metadata.GameplayImage; ValidateImageField(gameplayImageTexture, ConjureArcadeMetadata.RequiredGameplayImageAR, ConjureArcadeMetadata.MaxGameplayImageFileSize, out gameplayImageErrorMessage); // Other validations ValidateDevelopersList(metadata.Developers); ValidateGenresList(metadata.Genres); return errorCount == 0; } private void ValidateTextField(string text, int limit, out string outErrorMessage) { outErrorMessage = ""; // Check for empty string if (string.IsNullOrWhiteSpace(text)) { outErrorMessage = EmptyStringError; errorCount++; return; } // Check if string exceeds char limit if (limit > 0) { if (text.Length > limit) { outErrorMessage = string.Format(CharLimitExceededError, limit); errorCount++; } } } private void ValidateVersion(string version) { versionErrorMessage = ""; // Required format is "x.y.z" Regex rx = new Regex(@"^[a-zA-Z0-9]+\.[a-zA-Z0-9]+\.[a-zA-Z-0-9]+$"); var matches = rx.Match(version); if (!matches.Success) { versionErrorMessage += VersionFormatError; errorCount++; } } private void ValidateNumPlayerFields(int minNumPlayer, int maxNumPlayer) { minNumPlayerErrorMessage = ""; maxNumPlayerErrorMessage = ""; if (minNumPlayer <= 0) { minNumPlayerErrorMessage += IncorrectPlayerValueError; errorCount++; } if (maxNumPlayer <= 0) { maxNumPlayerErrorMessage += IncorrectPlayerValueError; errorCount++; } if (minNumPlayer > maxNumPlayer) { minNumPlayerErrorMessage += MinPlayerHigherThanMaxError; maxNumPlayerErrorMessage += MaxPlayerLowerThanMinError; errorCount += 2; } } private void ValidateImageField(Texture2D image, float[] desiredRatio, float maxFileSize, out string outErrorMessage) { outErrorMessage = ""; if (image == null) { outErrorMessage += MissingImageError; errorCount++; return; } // Verifying file size and aspect ratio string imageRelativePath = AssetDatabase.GetAssetPath(image); if (imageRelativePath.StartsWith("Assets/")) { // We check and remove "Assets" at the start of the relative path because // Application.dataPath already gives us the path to the assets folder string imageAbsolutePath = Application.dataPath + imageRelativePath.Substring(6); long length = new System.IO.FileInfo(imageAbsolutePath).Length; if (length / 1024f > maxFileSize) { // File is bigger outErrorMessage += string.Format(InvalidFileSizeError, maxFileSize); errorCount++; } // Verifying aspect ratio float delta = 0.00001f; float[] originalImageDimension = GetOriginalImageDimension(imageRelativePath); float ratioW = originalImageDimension[0] / desiredRatio[0]; float ratioH = originalImageDimension[1] / desiredRatio[1]; if (Mathf.Abs(ratioW - ratioH) > delta) { outErrorMessage += string.Format(InvalidAspectRatioError, desiredRatio[0], desiredRatio[1]); errorCount++; } } else { // Images should be located inside the Assets folder outErrorMessage += InvalidImagePathError; errorCount++; } } private void ValidateGenresList(GameGenre[] genres) { genresErrorMessage = ""; // Check if a genre has been selected twice or more for (int i = 0; i < genres.Length - 1; i++) { for (int j = 0; j < genres.Length; j++) { if (i == j) { continue; } if (genres[i].selectedGenre == genres[j].selectedGenre) { genresErrorMessage += SameGenreSelectedMultipleTimeError; errorCount++; return; } } } } private float[] GetOriginalImageDimension(string assetPath) { int width = 0; int height = 0; TextureImporter importer = AssetImporter.GetAtPath(assetPath) as TextureImporter; if (importer != null) { object[] args = new object[2] { 0, 0 }; MethodInfo mi = typeof(TextureImporter).GetMethod("GetWidthAndHeight", BindingFlags.NonPublic | BindingFlags.Instance); mi.Invoke(importer, args); width = (int)args[0]; height = (int)args[1]; } return new float[] { width, height }; } /// /// Validate a developers list /// /// The developers list to be analyzed public void ValidateDevelopersList(string[] developers) { developersErrorIndex.Clear(); developersErrorMessage = ""; for (int i = 0; i < developers.Length; i++) { if (developers[i].Length > ConjureArcadeMetadata.DevelopersNameLimit) { developersErrorMessage += string.Format(DevelopersCharLimitExceedError, ConjureArcadeMetadata.DevelopersNameLimit); developersErrorIndex.Add(i); errorCount++; } } } /// /// /// Returns the validation state based on the current amount of errors detected public MetadataValidationStateType GetValidationStateType() { if (errorCount < 0) { // Smaller than 0, which means data haven't been verified yet return MetadataValidationStateType.NotVerified; } else if (errorCount == 0) { // No error, means it's a success return MetadataValidationStateType.Validated; } else { // Errors were detected return MetadataValidationStateType.Failed; } } } } #endif