overmelted/Assets/ConjureOS/Scripts/MetadataWindow/ConjureArcadeMetadataValidator.cs

292 lines
11 KiB
C#

#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<int> developersErrorIndex = new List<int>(); // Used to indicate which index have an error
public List<int> DevelopersErrorIndex => developersErrorIndex;
private int errorCount = -1; // Starts at -1 because no validation have been done yet
public int ErrorCount => errorCount;
/// <summary>
/// Validate the specified game metadata
/// </summary>
/// <param name="metadata">The metadata to be validated</param>
/// <returns>Return true if metadata are valid. Otherwise, returns false.</returns>
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 };
}
/// <summary>
/// Validate a developers list
/// </summary>
/// <param name="developers">The developers list to be analyzed</param>
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++;
}
}
}
/// <summary>
/// </summary>
/// <returns>Returns the validation state based on the current amount of errors detected</returns>
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