292 lines
11 KiB
C#
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 |