summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2017-10-07 23:07:10 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2017-10-07 23:07:10 -0400
commit929dccb75a1405737975d76648e015a3e7c00177 (patch)
tree659fe16509327e694555db363caf7f47f326443b /src/SMAPI/Framework
parent926894f8f52c2a5cf104fcac2f7f34b637f7b531 (diff)
downloadSMAPI-929dccb75a1405737975d76648e015a3e7c00177.tar.gz
SMAPI-929dccb75a1405737975d76648e015a3e7c00177.tar.bz2
SMAPI-929dccb75a1405737975d76648e015a3e7c00177.zip
reorganise repo structure
Diffstat (limited to 'src/SMAPI/Framework')
-rw-r--r--src/SMAPI/Framework/Command.cs40
-rw-r--r--src/SMAPI/Framework/CommandManager.cs116
-rw-r--r--src/SMAPI/Framework/Content/AssetData.cs44
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForDictionary.cs45
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForImage.cs70
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForObject.cs54
-rw-r--r--src/SMAPI/Framework/Content/AssetInfo.cs82
-rw-r--r--src/SMAPI/Framework/ContentManagerShim.cs50
-rw-r--r--src/SMAPI/Framework/CursorPosition.cs35
-rw-r--r--src/SMAPI/Framework/DeprecationLevel.cs15
-rw-r--r--src/SMAPI/Framework/DeprecationManager.cs105
-rw-r--r--src/SMAPI/Framework/Exceptions/SAssemblyLoadFailedException.cs16
-rw-r--r--src/SMAPI/Framework/Exceptions/SContentLoadException.cs18
-rw-r--r--src/SMAPI/Framework/Exceptions/SParseException.cs17
-rw-r--r--src/SMAPI/Framework/GameVersion.cs68
-rw-r--r--src/SMAPI/Framework/IModMetadata.cs47
-rw-r--r--src/SMAPI/Framework/InternalExtensions.cs131
-rw-r--r--src/SMAPI/Framework/Logging/ConsoleInterceptionManager.cs86
-rw-r--r--src/SMAPI/Framework/Logging/InterceptingTextWriter.cs63
-rw-r--r--src/SMAPI/Framework/Logging/LogFileManager.cs57
-rw-r--r--src/SMAPI/Framework/ModHelpers/BaseHelper.cs23
-rw-r--r--src/SMAPI/Framework/ModHelpers/CommandHelper.cs54
-rw-r--r--src/SMAPI/Framework/ModHelpers/ContentHelper.cs476
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModHelper.cs129
-rw-r--r--src/SMAPI/Framework/ModHelpers/ModRegistryHelper.cs48
-rw-r--r--src/SMAPI/Framework/ModHelpers/ReflectionHelper.cs200
-rw-r--r--src/SMAPI/Framework/ModHelpers/TranslationHelper.cs140
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs61
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoadStatus.cs15
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyLoader.cs333
-rw-r--r--src/SMAPI/Framework/ModLoading/AssemblyParseResult.cs36
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/EventFinder.cs82
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/FieldFinder.cs82
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/MethodFinder.cs82
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/PropertyFinder.cs82
-rw-r--r--src/SMAPI/Framework/ModLoading/Finders/TypeFinder.cs133
-rw-r--r--src/SMAPI/Framework/ModLoading/IInstructionHandler.cs34
-rw-r--r--src/SMAPI/Framework/ModLoading/IncompatibleInstructionException.cs35
-rw-r--r--src/SMAPI/Framework/ModLoading/InstructionHandleResult.cs24
-rw-r--r--src/SMAPI/Framework/ModLoading/InvalidModStateException.cs14
-rw-r--r--src/SMAPI/Framework/ModLoading/ModDependencyStatus.cs18
-rw-r--r--src/SMAPI/Framework/ModLoading/ModMetadata.cs68
-rw-r--r--src/SMAPI/Framework/ModLoading/ModMetadataStatus.cs12
-rw-r--r--src/SMAPI/Framework/ModLoading/ModResolver.cs366
-rw-r--r--src/SMAPI/Framework/ModLoading/Platform.cs12
-rw-r--r--src/SMAPI/Framework/ModLoading/PlatformAssemblyMap.cs55
-rw-r--r--src/SMAPI/Framework/ModLoading/RewriteHelper.cs94
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs50
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs51
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs88
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs154
-rw-r--r--src/SMAPI/Framework/ModLoading/Rewriters/VirtualEntryCallRemover.cs90
-rw-r--r--src/SMAPI/Framework/ModRegistry.cs113
-rw-r--r--src/SMAPI/Framework/Models/Manifest.cs47
-rw-r--r--src/SMAPI/Framework/Models/ManifestDependency.cs34
-rw-r--r--src/SMAPI/Framework/Models/ModCompatibility.cs55
-rw-r--r--src/SMAPI/Framework/Models/ModDataID.cs85
-rw-r--r--src/SMAPI/Framework/Models/ModDataRecord.cs63
-rw-r--r--src/SMAPI/Framework/Models/ModStatus.cs18
-rw-r--r--src/SMAPI/Framework/Models/SConfig.cs27
-rw-r--r--src/SMAPI/Framework/Monitor.cs194
-rw-r--r--src/SMAPI/Framework/Reflection/CacheEntry.cs30
-rw-r--r--src/SMAPI/Framework/Reflection/PrivateField.cs93
-rw-r--r--src/SMAPI/Framework/Reflection/PrivateMethod.cs99
-rw-r--r--src/SMAPI/Framework/Reflection/PrivateProperty.cs93
-rw-r--r--src/SMAPI/Framework/Reflection/Reflector.cs276
-rw-r--r--src/SMAPI/Framework/RequestExitDelegate.cs7
-rw-r--r--src/SMAPI/Framework/SContentManager.cs531
-rw-r--r--src/SMAPI/Framework/SGame.cs1403
-rw-r--r--src/SMAPI/Framework/Serialisation/JsonHelper.cs96
-rw-r--r--src/SMAPI/Framework/Serialisation/SFieldConverter.cs121
-rw-r--r--src/SMAPI/Framework/Serialisation/SelectiveStringEnumConverter.cs37
-rw-r--r--src/SMAPI/Framework/Utilities/ContextHash.cs61
-rw-r--r--src/SMAPI/Framework/Utilities/Countdown.cs44
-rw-r--r--src/SMAPI/Framework/WebApiClient.cs73
75 files changed, 7900 insertions, 0 deletions
diff --git a/src/SMAPI/Framework/Command.cs b/src/SMAPI/Framework/Command.cs
new file mode 100644
index 00000000..943e018d
--- /dev/null
+++ b/src/SMAPI/Framework/Command.cs
@@ -0,0 +1,40 @@
+using System;
+
+namespace StardewModdingAPI.Framework
+{
+ /// <summary>A command that can be submitted through the SMAPI console to interact with SMAPI.</summary>
+ internal class Command
+ {
+ /*********
+ ** Accessor
+ *********/
+ /// <summary>The friendly name for the mod that registered the command.</summary>
+ public string ModName { get; }
+
+ /// <summary>The command name, which the user must type to trigger it.</summary>
+ public string Name { get; }
+
+ /// <summary>The human-readable documentation shown when the player runs the built-in 'help' command.</summary>
+ public string Documentation { get; }
+
+ /// <summary>The method to invoke when the command is triggered. This method is passed the command name and arguments submitted by the user.</summary>
+ public Action<string, string[]> Callback { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="modName">The friendly name for the mod that registered the command.</param>
+ /// <param name="name">The command name, which the user must type to trigger it.</param>
+ /// <param name="documentation">The human-readable documentation shown when the player runs the built-in 'help' command.</param>
+ /// <param name="callback">The method to invoke when the command is triggered. This method is passed the command name and arguments submitted by the user.</param>
+ public Command(string modName, string name, string documentation, Action<string, string[]> callback)
+ {
+ this.ModName = modName;
+ this.Name = name;
+ this.Documentation = documentation;
+ this.Callback = callback;
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/CommandManager.cs b/src/SMAPI/Framework/CommandManager.cs
new file mode 100644
index 00000000..79a23d03
--- /dev/null
+++ b/src/SMAPI/Framework/CommandManager.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace StardewModdingAPI.Framework
+{
+ /// <summary>Manages console commands.</summary>
+ internal class CommandManager
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The commands registered with SMAPI.</summary>
+ private readonly IDictionary<string, Command> Commands = new Dictionary<string, Command>(StringComparer.InvariantCultureIgnoreCase);
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Add a console command.</summary>
+ /// <param name="modName">The friendly mod name for this instance.</param>
+ /// <param name="name">The command name, which the user must type to trigger it.</param>
+ /// <param name="documentation">The human-readable documentation shown when the player runs the built-in 'help' command.</param>
+ /// <param name="callback">The method to invoke when the command is triggered. This method is passed the command name and arguments submitted by the user.</param>
+ /// <param name="allowNullCallback">Whether to allow a null <paramref name="callback"/> argument; this should only used for backwards compatibility.</param>
+ /// <exception cref="ArgumentNullException">The <paramref name="name"/> or <paramref name="callback"/> is null or empty.</exception>
+ /// <exception cref="FormatException">The <paramref name="name"/> is not a valid format.</exception>
+ /// <exception cref="ArgumentException">There's already a command with that name.</exception>
+ public void Add(string modName, string name, string documentation, Action<string, string[]> callback, bool allowNullCallback = false)
+ {
+ name = this.GetNormalisedName(name);
+
+ // validate format
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullException(nameof(name), "Can't register a command with no name.");
+ if (name.Any(char.IsWhiteSpace))
+ throw new FormatException($"Can't register the '{name}' command because the name can't contain whitespace.");
+ if (callback == null && !allowNullCallback)
+ throw new ArgumentNullException(nameof(callback), $"Can't register the '{name}' command because without a callback.");
+
+ // ensure uniqueness
+ if (this.Commands.ContainsKey(name))
+ throw new ArgumentException(nameof(callback), $"Can't register the '{name}' command because there's already a command with that name.");
+
+ // add command
+ this.Commands.Add(name, new Command(modName, name, documentation, callback));
+ }
+
+ /// <summary>Get a command by its unique name.</summary>
+ /// <param name="name">The command name.</param>
+ /// <returns>Returns the matching command, or <c>null</c> if not found.</returns>
+ public Command Get(string name)
+ {
+ name = this.GetNormalisedName(name);
+ this.Commands.TryGetValue(name, out Command command);
+ return command;
+ }
+
+ /// <summary>Get all registered commands.</summary>
+ public IEnumerable<Command> GetAll()
+ {
+ return this.Commands
+ .Values
+ .OrderBy(p => p.Name);
+ }
+
+ /// <summary>Trigger a command.</summary>
+ /// <param name="input">The raw command input.</param>
+ /// <returns>Returns whether a matching command was triggered.</returns>
+ public bool Trigger(string input)
+ {
+ if (string.IsNullOrWhiteSpace(input))
+ return false;
+
+ string[] args = input.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ string name = args[0];
+ args = args.Skip(1).ToArray();
+
+ return this.Trigger(name, args);
+ }
+
+ /// <summary>Trigger a command.</summary>
+ /// <param name="name">The command name.</param>
+ /// <param name="arguments">The command arguments.</param>
+ /// <returns>Returns whether a matching command was triggered.</returns>
+ public bool Trigger(string name, string[] arguments)
+ {
+ // get normalised name
+ name = this.GetNormalisedName(name);
+ if (name == null)
+ return false;
+
+ // get command
+ if (this.Commands.TryGetValue(name, out Command command))
+ {
+ command.Callback.Invoke(name, arguments);
+ return true;
+ }
+ return false;
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Get a normalised command name.</summary>
+ /// <param name="name">The command name.</param>
+ private string GetNormalisedName(string name)
+ {
+ name = name?.Trim().ToLower();
+ return !string.IsNullOrWhiteSpace(name)
+ ? name
+ : null;
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/Content/AssetData.cs b/src/SMAPI/Framework/Content/AssetData.cs
new file mode 100644
index 00000000..1ab9eebd
--- /dev/null
+++ b/src/SMAPI/Framework/Content/AssetData.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace StardewModdingAPI.Framework.Content
+{
+ /// <summary>Base implementation for a content helper which encapsulates access and changes to content being read from a data file.</summary>
+ /// <typeparam name="TValue">The interface value type.</typeparam>
+ internal class AssetData<TValue> : AssetInfo, IAssetData<TValue>
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The content data being read.</summary>
+ public TValue Data { get; protected set; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="locale">The content's locale code, if the content is localised.</param>
+ /// <param name="assetName">The normalised asset name being read.</param>
+ /// <param name="data">The content data being read.</param>
+ /// <param name="getNormalisedPath">Normalises an asset key to match the cache key.</param>
+ public AssetData(string locale, string assetName, TValue data, Func<string, string> getNormalisedPath)
+ : base(locale, assetName, data.GetType(), getNormalisedPath)
+ {
+ this.Data = data;
+ }
+
+ /// <summary>Replace the entire content value with the given value. This is generally not recommended, since it may break compatibility with other mods or different versions of the game.</summary>
+ /// <param name="value">The new content value.</param>
+ /// <exception cref="ArgumentNullException">The <paramref name="value"/> is null.</exception>
+ /// <exception cref="InvalidCastException">The <paramref name="value"/>'s type is not compatible with the loaded asset's type.</exception>
+ public void ReplaceWith(TValue value)
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value), "Can't set a loaded asset to a null value.");
+ if (!this.DataType.IsInstanceOfType(value))
+ throw new InvalidCastException($"Can't replace loaded asset of type {this.GetFriendlyTypeName(this.DataType)} with value of type {this.GetFriendlyTypeName(value.GetType())}. The new type must be compatible to prevent game errors.");
+
+ this.Data = value;
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/Content/AssetDataForDictionary.cs b/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
new file mode 100644
index 00000000..e9b29b12
--- /dev/null
+++ b/src/SMAPI/Framework/Content/AssetDataForDictionary.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace StardewModdingAPI.Framework.Content
+{
+ /// <summary>Encapsulates access and changes to dictionary content being read from a data file.</summary>
+ internal class AssetDataForDictionary<TKey, TValue> : AssetData<IDictionary<TKey, TValue>>, IAssetDataForDictionary<TKey, TValue>
+ {
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="locale">The content's locale code, if the content is localised.</param>
+ /// <param name="assetName">The normalised asset name being read.</param>
+ /// <param name="data">The content data being read.</param>
+ /// <param name="getNormalisedPath">Normalises an asset key to match the cache key.</param>
+ public AssetDataForDictionary(string locale, string assetName, IDictionary<TKey, TValue> data, Func<string, string> getNormalisedPath)
+ : base(locale, assetName, data, getNormalisedPath) { }
+
+ /// <summary>Add or replace an entry in the dictionary.</summary>
+ /// <param name="key">The entry key.</param>
+ /// <param name="value">The entry value.</param>
+ public void Set(TKey key, TValue value)
+ {
+ this.Data[key] = value;
+ }
+
+ /// <summary>Add or replace an entry in the dictionary.</summary>
+ /// <param name="key">The entry key.</param>
+ /// <param name="value">A callback which accepts the current value and returns the new value.</param>
+ public void Set(TKey key, Func<TValue, TValue> value)
+ {
+ this.Data[key] = value(this.Data[key]);
+ }
+
+ /// <summary>Dynamically replace values in the dictionary.</summary>
+ /// <param name="replacer">A lambda which takes the current key and value for an entry, and returns the new value.</param>
+ public void Set(Func<TKey, TValue, TValue> replacer)
+ {
+ foreach (var pair in this.Data.ToArray())
+ this.Data[pair.Key] = replacer(pair.Key, pair.Value);
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/Content/AssetDataForImage.cs b/src/SMAPI/Framework/Content/AssetDataForImage.cs
new file mode 100644
index 00000000..45c5588b
--- /dev/null
+++ b/src/SMAPI/Framework/Content/AssetDataForImage.cs
@@ -0,0 +1,70 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace StardewModdingAPI.Framework.Content
+{
+ /// <summary>Encapsulates access and changes to dictionary content being read from a data file.</summary>
+ internal class AssetDataForImage : AssetData<Texture2D>, IAssetDataForImage
+ {
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="locale">The content's locale code, if the content is localised.</param>
+ /// <param name="assetName">The normalised asset name being read.</param>
+ /// <param name="data">The content data being read.</param>
+ /// <param name="getNormalisedPath">Normalises an asset key to match the cache key.</param>
+ public AssetDataForImage(string locale, string assetName, Texture2D data, Func<string, string> getNormalisedPath)
+ : base(locale, assetName, data, getNormalisedPath) { }
+
+ /// <summary>Overwrite part of the image.</summary>
+ /// <param name="source">The image to patch into the content.</param>
+ /// <param name="sourceArea">The part of the <paramref name="source"/> to copy (or <c>null</c> to take the whole texture). This must be within the bounds of the <paramref name="source"/> texture.</param>
+ /// <param name="targetArea">The part of the content to patch (or <c>null</c> to patch the whole texture). The original content within this area will be erased. This must be within the bounds of the existing spritesheet.</param>
+ /// <param name="patchMode">Indicates how an image should be patched.</param>
+ /// <exception cref="ArgumentNullException">One of the arguments is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">The <paramref name="targetArea"/> is outside the bounds of the spritesheet.</exception>
+ public void PatchImage(Texture2D source, Rectangle? sourceArea = null, Rectangle? targetArea = null, PatchMode patchMode = PatchMode.Replace)
+ {
+ // get texture
+ Texture2D target = this.Data;
+
+ // get areas
+ sourceArea = sourceArea ?? new Rectangle(0, 0, source.Width, source.Height);
+ targetArea = targetArea ?? new Rectangle(0, 0, Math.Min(sourceArea.Value.Width, target.Width), Math.Min(sourceArea.Value.Height, target.Height));
+
+ // validate
+ if (source == null)
+ throw new ArgumentNullException(nameof(source), "Can't patch from a null source texture.");
+ if (sourceArea.Value.X < 0 || sourceArea.Value.Y < 0 || sourceArea.Value.Right > source.Width || sourceArea.Value.Bottom > source.Height)
+ throw new ArgumentOutOfRangeException(nameof(sourceArea), "The source area is outside the bounds of the source texture.");
+ if (targetArea.Value.X < 0 || targetArea.Value.Y < 0 || targetArea.Value.Right > target.Width || targetArea.Value.Bottom > target.Height)
+ throw new ArgumentOutOfRangeExc