// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
namespace UnrealBuildTool
{
///
/// The version format for .uplugin files. This rarely changes now; plugin descriptors should maintain backwards compatibility automatically.
///
public enum PluginDescriptorVersion
{
///
/// Invalid
///
Invalid = 0,
///
/// Initial version
///
Initial = 1,
///
/// Adding SampleNameHash
///
NameHash = 2,
///
/// Unifying plugin/project files (since abandoned, but backwards compatibility maintained)
///
ProjectPluginUnification = 3,
///
/// This needs to be the last line, so we can calculate the value of Latest below
///
LatestPlusOne,
///
/// The latest plugin descriptor version
///
Latest = LatestPlusOne - 1
}
///
/// In-memory representation of a .uplugin file
///
public class PluginDescriptor
{
///
/// Descriptor version number
///
public int FileVersion;
///
/// Version number for the plugin. The version number must increase with every version of the plugin, so that the system
/// can determine whether one version of a plugin is newer than another, or to enforce other requirements. This version
/// number is not displayed in front-facing UI. Use the VersionName for that.
///
public int Version;
///
/// Name of the version for this plugin. This is the front-facing part of the version number. It doesn't need to match
/// the version number numerically, but should be updated when the version number is increased accordingly.
///
public string VersionName;
///
/// Friendly name of the plugin
///
public string FriendlyName;
///
/// Description of the plugin
///
public string Description;
///
/// The name of the category this plugin
///
public string Category;
///
/// The company or individual who created this plugin. This is an optional field that may be displayed in the user interface.
///
public string CreatedBy;
///
/// Hyperlink URL string for the company or individual who created this plugin. This is optional.
///
public string CreatedByURL;
///
/// Documentation URL string.
///
public string DocsURL;
///
/// Marketplace URL for this plugin. This URL will be embedded into projects that enable this plugin, so we can redirect to the marketplace if a user doesn't have it installed.
///
public string MarketplaceURL;
///
/// Support URL/email for this plugin.
///
public string SupportURL;
///
/// Sets the version of the engine that this plugin is compatible with.
///
public string EngineVersion;
///
/// For packaged plugins, contains the changelist that this plugin is compatible with
///
public int CompatibleChangelist;
///
/// List of all modules associated with this plugin
///
public ModuleDescriptor[] Modules;
///
/// List of all localization targets associated with this plugin
///
public LocalizationTargetDescriptor[] LocalizationTargets;
///
/// Whether this plugin should be enabled by default for all projects
///
public bool bEnabledByDefault;
///
/// Can this plugin contain content?
///
public bool bCanContainContent;
///
/// Marks the plugin as beta in the UI
///
public bool bIsBetaVersion;
///
/// Whether this plugin is a mod
///
public bool bIsMod;
///
/// Whether this plugin can be used by UnrealHeaderTool
///
public bool bCanBeUsedWithUnrealHeaderTool;
///
/// Set for plugins which are installed
///
public bool bInstalled;
///
/// For plugins that are under a platform folder (eg. /PS4/), determines whether compiling the plugin requires the build platform and/or SDK to be available
///
public bool bRequiresBuildPlatform;
///
/// Set of pre-build steps to execute, keyed by host platform name.
///
public CustomBuildSteps PreBuildSteps;
///
/// Set of post-build steps to execute, keyed by host platform name.
///
public CustomBuildSteps PostBuildSteps;
///
/// Private constructor. This object should not be created directly; read it from disk using FromFile() instead.
///
private PluginDescriptor()
{
FileVersion = (int)PluginDescriptorVersion.Latest;
bRequiresBuildPlatform = true;
}
///
/// Creates a plugin descriptor from a file on disk
///
/// The filename to read
/// Whether this plugin should be enabled by default based on its location
/// New plugin descriptor
public static PluginDescriptor FromFile(FileReference FileName, bool bPluginTypeEnabledByDefault)
{
JsonObject RawObject = JsonObject.Read(FileName.FullName);
try
{
PluginDescriptor Descriptor = new PluginDescriptor();
// Read the version
if (!RawObject.TryGetIntegerField("FileVersion", out Descriptor.FileVersion))
{
if (!RawObject.TryGetIntegerField("PluginFileVersion", out Descriptor.FileVersion))
{
throw new BuildException("Plugin descriptor file '{0}' does not contain a valid FileVersion entry", FileName);
}
}
// Check it's not newer than the latest version we can parse
if (Descriptor.FileVersion > (int)PluginDescriptorVersion.Latest)
{
throw new BuildException("Plugin descriptor file '{0}' appears to be in a newer version ({1}) of the file format that we can load (max version: {2}).", FileName, Descriptor.FileVersion, (int)PluginDescriptorVersion.Latest);
}
// Read the other fields
RawObject.TryGetIntegerField("Version", out Descriptor.Version);
RawObject.TryGetStringField("VersionName", out Descriptor.VersionName);
RawObject.TryGetStringField("FriendlyName", out Descriptor.FriendlyName);
RawObject.TryGetStringField("Description", out Descriptor.Description);
if (!RawObject.TryGetStringField("Category", out Descriptor.Category))
{
// Category used to be called CategoryPath in .uplugin files
RawObject.TryGetStringField("CategoryPath", out Descriptor.Category);
}
// Due to a difference in command line parsing between Windows and Mac, we shipped a few Mac samples containing
// a category name with escaped quotes. Remove them here to make sure we can list them in the right category.
if (Descriptor.Category != null && Descriptor.Category.Length >= 2 && Descriptor.Category.StartsWith("\"") && Descriptor.Category.EndsWith("\""))
{
Descriptor.Category = Descriptor.Category.Substring(1, Descriptor.Category.Length - 2);
}
RawObject.TryGetStringField("CreatedBy", out Descriptor.CreatedBy);
RawObject.TryGetStringField("CreatedByURL", out Descriptor.CreatedByURL);
RawObject.TryGetStringField("DocsURL", out Descriptor.DocsURL);
RawObject.TryGetStringField("MarketplaceURL", out Descriptor.MarketplaceURL);
RawObject.TryGetStringField("SupportURL", out Descriptor.SupportURL);
RawObject.TryGetStringField("EngineVersion", out Descriptor.EngineVersion);
RawObject.TryGetIntegerField("CompatibleChangelist", out Descriptor.CompatibleChangelist);
JsonObject[] ModulesArray;
if (RawObject.TryGetObjectArrayField("Modules", out ModulesArray))
{
Descriptor.Modules = Array.ConvertAll(ModulesArray, x => ModuleDescriptor.FromJsonObject(x));
}
JsonObject[] LocalizationTargetsArray;
if (RawObject.TryGetObjectArrayField("LocalizationTargets", out LocalizationTargetsArray))
{
Descriptor.LocalizationTargets = Array.ConvertAll(LocalizationTargetsArray, x => LocalizationTargetDescriptor.FromJsonObject(x));
}
if(!RawObject.TryGetBoolField("EnabledByDefault", out Descriptor.bEnabledByDefault))
{
Descriptor.bEnabledByDefault = bPluginTypeEnabledByDefault;
}
RawObject.TryGetBoolField("CanContainContent", out Descriptor.bCanContainContent);
RawObject.TryGetBoolField("IsBetaVersion", out Descriptor.bIsBetaVersion);
RawObject.TryGetBoolField("IsMod", out Descriptor.bIsMod);
RawObject.TryGetBoolField("Installed", out Descriptor.bInstalled);
RawObject.TryGetBoolField("CanBeUsedWithUnrealHeaderTool", out Descriptor.bCanBeUsedWithUnrealHeaderTool);
RawObject.TryGetBoolField("RequiresBuildPlatform", out Descriptor.bRequiresBuildPlatform);
CustomBuildSteps.TryRead(RawObject, "PreBuildSteps", out Descriptor.PreBuildSteps);
CustomBuildSteps.TryRead(RawObject, "PostBuildSteps", out Descriptor.PostBuildSteps);
return Descriptor;
}
catch (JsonParseException ParseException)
{
throw new JsonParseException("{0} (in {1})", ParseException.Message, FileName);
}
}
///
/// Saves the descriptor to disk
///
/// The filename to write to
/// Whether the plugin is enabled by default based on its location
public void Save(string FileName, bool bPluginTypeEnabledByDefault)
{
using (JsonWriter Writer = new JsonWriter(FileName))
{
Writer.WriteObjectStart();
Writer.WriteValue("FileVersion", (int)ProjectDescriptorVersion.Latest);
Writer.WriteValue("Version", Version);
Writer.WriteValue("VersionName", VersionName);
Writer.WriteValue("FriendlyName", FriendlyName);
Writer.WriteValue("Description", Description);
Writer.WriteValue("Category", Category);
Writer.WriteValue("CreatedBy", CreatedBy);
Writer.WriteValue("CreatedByURL", CreatedByURL);
Writer.WriteValue("DocsURL", DocsURL);
Writer.WriteValue("MarketplaceURL", MarketplaceURL);
Writer.WriteValue("SupportURL", SupportURL);
if(!String.IsNullOrEmpty(EngineVersion))
{
Writer.WriteValue("EngineVersion", EngineVersion);
}
if(CompatibleChangelist != 0)
{
Writer.WriteValue("CompatibleChangelist", CompatibleChangelist);
}
if(bEnabledByDefault != bPluginTypeEnabledByDefault)
{
Writer.WriteValue("EnabledByDefault", bEnabledByDefault);
}
Writer.WriteValue("CanContainContent", bCanContainContent);
Writer.WriteValue("IsBetaVersion", bIsBetaVersion);
if(bIsMod)
{
Writer.WriteValue("IsMod", bIsMod);
}
Writer.WriteValue("Installed", bInstalled);
Writer.WriteValue("RequiresBuildPlatform", bRequiresBuildPlatform);
ModuleDescriptor.WriteArray(Writer, "Modules", Modules);
if(PreBuildSteps != null)
{
PreBuildSteps.Write(Writer, "PreBuildSteps");
}
if(PostBuildSteps != null)
{
PostBuildSteps.Write(Writer, "PostBuildSteps");
}
Writer.WriteObjectEnd();
}
}
}
///
/// Representation of a reference to a plugin from a project file
///
[DebuggerDisplay("Name={Name}")]
public class PluginReferenceDescriptor
{
///
/// Name of the plugin
///
public string Name;
///
/// Whether it should be enabled by default
///
public bool bEnabled;
///
/// Whether this plugin is optional, and the game should silently ignore it not being present
///
public bool bOptional;
///
/// Description of the plugin for users that do not have it installed.
///
public string Description;
///
/// URL for this plugin on the marketplace, if the user doesn't have it installed.
///
public string MarketplaceURL;
///
/// If enabled, list of platforms for which the plugin should be enabled (or all platforms if blank).
///
UnrealTargetPlatform[] WhitelistPlatforms;
///
/// If enabled, list of platforms for which the plugin should be disabled.
///
UnrealTargetPlatform[] BlacklistPlatforms;
///
/// If enabled, list of targets for which the plugin should be enabled (or all targets if blank).
///
TargetType[] WhitelistTargets;
///
/// If enabled, list of targets for which the plugin should be disabled.
///
TargetType[] BlacklistTargets;
///
/// Constructor
///
/// Name of the plugin
/// The marketplace URL for plugins which are not installed
/// Whether the plugin is enabled
public PluginReferenceDescriptor(string InName, string InMarketplaceURL, bool bInEnabled)
{
Name = InName;
MarketplaceURL = InMarketplaceURL;
bEnabled = bInEnabled;
}
///
/// Construct a PluginReferenceDescriptor from a Json object
///
/// The Json object containing a plugin reference descriptor
/// New PluginReferenceDescriptor object
public static PluginReferenceDescriptor FromJsonObject(JsonObject RawObject)
{
PluginReferenceDescriptor Descriptor = new PluginReferenceDescriptor(RawObject.GetStringField("Name"), null, RawObject.GetBoolField("Enabled"));
RawObject.TryGetBoolField("Optional", out Descriptor.bOptional);
RawObject.TryGetStringField("Description", out Descriptor.Description);
RawObject.TryGetStringField("MarketplaceURL", out Descriptor.MarketplaceURL);
RawObject.TryGetEnumArrayField("WhitelistPlatforms", out Descriptor.WhitelistPlatforms);
RawObject.TryGetEnumArrayField("BlacklistPlatforms", out Descriptor.BlacklistPlatforms);
RawObject.TryGetEnumArrayField("WhitelistTargets", out Descriptor.WhitelistTargets);
RawObject.TryGetEnumArrayField("BlacklistTargets", out Descriptor.BlacklistTargets);
return Descriptor;
}
///
/// Determines if this reference enables the plugin for a given platform
///
/// The platform to check
/// True if the plugin should be enabled
public bool IsEnabledForPlatform(UnrealTargetPlatform Platform)
{
if (!bEnabled)
{
return false;
}
if (WhitelistPlatforms != null && WhitelistPlatforms.Length > 0 && !WhitelistPlatforms.Contains(Platform))
{
return false;
}
if (BlacklistPlatforms != null && BlacklistPlatforms.Contains(Platform))
{
return false;
}
return true;
}
///
/// Determines if this reference enables the plugin for a given target
///
/// The target to check
/// True if the plugin should be enabled
public bool IsEnabledForTarget(TargetType Target)
{
if (!bEnabled)
{
return false;
}
if (WhitelistTargets != null && WhitelistTargets.Length > 0 && !WhitelistTargets.Contains(Target))
{
return false;
}
if (BlacklistTargets != null && BlacklistTargets.Contains(Target))
{
return false;
}
return true;
}
}
}