Merge branch 'develop' into stable
This commit is contained in:
commit
aaf354761f
|
@ -71,22 +71,29 @@ directory containing `src`).
|
||||||
3. If you did everything right so far, you should have a directory like this:
|
3. If you did everything right so far, you should have a directory like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
SMAPI-1.0/
|
SMAPI-1.x/
|
||||||
Mono/
|
Mono/
|
||||||
Mods/*
|
Mods/*
|
||||||
|
Mono.Cecil.dll
|
||||||
|
Mono.Cecil.Rocks.dll
|
||||||
Newtonsoft.Json.dll
|
Newtonsoft.Json.dll
|
||||||
StardewModdingAPI
|
StardewModdingAPI
|
||||||
StardewModdingAPI.exe
|
StardewModdingAPI.exe
|
||||||
StardewModdingAPI.exe.mdb
|
StardewModdingAPI.exe.mdb
|
||||||
StardewModdingAPI-settings.json
|
StardewModdingAPI-settings.json
|
||||||
|
StardewModdingAPI.AssemblyRewriters.dll
|
||||||
System.Numerics.dll
|
System.Numerics.dll
|
||||||
steam_appid.txt
|
steam_appid.txt
|
||||||
Windows/
|
Windows/
|
||||||
Mods/*
|
Mods/*
|
||||||
|
Mono.Cecil.dll
|
||||||
|
Mono.Cecil.Rocks.dll
|
||||||
|
Newtonsoft.Json.dll
|
||||||
StardewModdingAPI.exe
|
StardewModdingAPI.exe
|
||||||
StardewModdingAPI.pdb
|
StardewModdingAPI.pdb
|
||||||
StardewModdingAPI.xml
|
StardewModdingAPI.xml
|
||||||
StardewModdingAPI-settings.json
|
StardewModdingAPI-settings.json
|
||||||
|
StardewModdingAPI.AssemblyRewriters.dll
|
||||||
steam_appid.txt
|
steam_appid.txt
|
||||||
install.exe
|
install.exe
|
||||||
readme.txt
|
readme.txt
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
# Release notes
|
# Release notes
|
||||||
|
|
||||||
|
## 1.3
|
||||||
|
See [log](https://github.com/CLxS/SMAPI/compare/1.2...1.3).
|
||||||
|
|
||||||
|
For players:
|
||||||
|
* You can now run most mods on any platform (e.g. run Windows mods on Linux/Mac).
|
||||||
|
* Fixed the normal uninstaller not removing files added by the 'SMAPI for developers' installer.
|
||||||
|
|
||||||
## 1.2
|
## 1.2
|
||||||
See [log](https://github.com/CLxS/SMAPI/compare/1.1.1...1.2).
|
See [log](https://github.com/CLxS/SMAPI/compare/1.1.1...1.2).
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
[assembly: AssemblyVersion("1.2.0.0")]
|
[assembly: AssemblyVersion("1.3.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.2.0.0")]
|
[assembly: AssemblyFileVersion("1.3.0.0")]
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.AssemblyRewriters
|
||||||
|
{
|
||||||
|
/// <summary>Rewrites a method for compatibility.</summary>
|
||||||
|
public interface IMethodRewriter
|
||||||
|
{
|
||||||
|
/// <summary>Get whether the given method reference can be rewritten.</summary>
|
||||||
|
/// <param name="methodRef">The method reference.</param>
|
||||||
|
bool ShouldRewrite(MethodReference methodRef);
|
||||||
|
|
||||||
|
/// <summary>Rewrite a method for compatibility.</summary>
|
||||||
|
/// <param name="module">The module being rewritten.</param>
|
||||||
|
/// <param name="cil">The CIL rewriter.</param>
|
||||||
|
/// <param name="callOp">The instruction which calls the method.</param>
|
||||||
|
/// <param name="methodRef">The method reference invoked by the <paramref name="callOp"/>.</param>
|
||||||
|
/// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
|
||||||
|
void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction callOp, MethodReference methodRef, PlatformAssemblyMap assemblyMap);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace StardewModdingAPI.AssemblyRewriters
|
||||||
|
{
|
||||||
|
/// <summary>The game's platform version.</summary>
|
||||||
|
public enum Platform
|
||||||
|
{
|
||||||
|
/// <summary>The Linux/Mac version of the game.</summary>
|
||||||
|
Mono,
|
||||||
|
|
||||||
|
/// <summary>The Windows version of the game.</summary>
|
||||||
|
Windows
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Mono.Cecil;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.AssemblyRewriters
|
||||||
|
{
|
||||||
|
/// <summary>Metadata for mapping assemblies to the current <see cref="Platform"/>.</summary>
|
||||||
|
public class PlatformAssemblyMap
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Accessors
|
||||||
|
*********/
|
||||||
|
/****
|
||||||
|
** Data
|
||||||
|
****/
|
||||||
|
/// <summary>The target game platform.</summary>
|
||||||
|
public readonly Platform TargetPlatform;
|
||||||
|
|
||||||
|
/// <summary>The short assembly names to remove as assembly reference, and replace with the <see cref="Targets"/>. These should be short names (like "Stardew Valley").</summary>
|
||||||
|
public readonly string[] RemoveNames;
|
||||||
|
|
||||||
|
/// <summary>The assembly filenames to target. Equivalent types should be rewritten to use these assemblies.</summary>
|
||||||
|
|
||||||
|
/****
|
||||||
|
** Metadata
|
||||||
|
****/
|
||||||
|
/// <summary>The assemblies to target. Equivalent types should be rewritten to use these assemblies.</summary>
|
||||||
|
public readonly Assembly[] Targets;
|
||||||
|
|
||||||
|
/// <summary>An assembly => reference cache.</summary>
|
||||||
|
public readonly IDictionary<Assembly, AssemblyNameReference> TargetReferences;
|
||||||
|
|
||||||
|
/// <summary>An assembly => module cache.</summary>
|
||||||
|
public readonly IDictionary<Assembly, ModuleDefinition> TargetModules;
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="targetPlatform">The target game platform.</param>
|
||||||
|
/// <param name="removeAssemblyNames">The assembly short names to remove (like <c>Stardew Valley</c>).</param>
|
||||||
|
/// <param name="targetAssemblies">The assemblies to target.</param>
|
||||||
|
public PlatformAssemblyMap(Platform targetPlatform, string[] removeAssemblyNames, Assembly[] targetAssemblies)
|
||||||
|
{
|
||||||
|
// save data
|
||||||
|
this.TargetPlatform = targetPlatform;
|
||||||
|
this.RemoveNames = removeAssemblyNames;
|
||||||
|
|
||||||
|
// cache assembly metadata
|
||||||
|
this.Targets = targetAssemblies;
|
||||||
|
this.TargetReferences = this.Targets.ToDictionary(assembly => assembly, assembly => AssemblyNameReference.Parse(assembly.FullName));
|
||||||
|
this.TargetModules = this.Targets.ToDictionary(assembly => assembly, assembly => ModuleDefinition.ReadModule(assembly.Modules.Single().FullyQualifiedName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("StardewModdingAPI.AssemblyRewriters")]
|
||||||
|
[assembly: AssemblyDescription("Contains internal SMAPI code for converting mods between Linux/Mac and Windows. This assembly is used by SMAPI internally and shouldn't be referenced directly by mods.")]
|
||||||
|
[assembly: AssemblyProduct("StardewModdingAPI.CrossplatformRewriters")]
|
||||||
|
[assembly: Guid("10db0676-9fc1-4771-a2c8-e2519f091e49")]
|
|
@ -0,0 +1,92 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.AssemblyRewriters.Rewriters
|
||||||
|
{
|
||||||
|
/// <summary>Base class for a method rewriter.</summary>
|
||||||
|
public abstract class BaseMethodRewriter : IMethodRewriter
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Get whether the given method reference can be rewritten.</summary>
|
||||||
|
/// <param name="methodRef">The method reference.</param>
|
||||||
|
public abstract bool ShouldRewrite(MethodReference methodRef);
|
||||||
|
|
||||||
|
/// <summary>Rewrite a method for compatibility.</summary>
|
||||||
|
/// <param name="module">The module being rewritten.</param>
|
||||||
|
/// <param name="cil">The CIL rewriter.</param>
|
||||||
|
/// <param name="callOp">The instruction which calls the method.</param>
|
||||||
|
/// <param name="methodRef">The method reference invoked by the <paramref name="callOp"/>.</param>
|
||||||
|
/// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
|
||||||
|
public abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction callOp, MethodReference methodRef, PlatformAssemblyMap assemblyMap);
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Protected methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Get whether a method definition matches the signature expected by a method reference.</summary>
|
||||||
|
/// <param name="definition">The method definition.</param>
|
||||||
|
/// <param name="reference">The method reference.</param>
|
||||||
|
protected bool HasMatchingSignature(MethodInfo definition, MethodReference reference)
|
||||||
|
{
|
||||||
|
// same name
|
||||||
|
if (definition.Name != reference.Name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// same arguments
|
||||||
|
ParameterInfo[] definitionParameters = definition.GetParameters();
|
||||||
|
ParameterDefinition[] referenceParameters = reference.Parameters.ToArray();
|
||||||
|
if (referenceParameters.Length != definitionParameters.Length)
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < referenceParameters.Length; i++)
|
||||||
|
{
|
||||||
|
if (!this.IsMatchingType(definitionParameters[i].ParameterType, referenceParameters[i].ParameterType))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get whether a type has a method whose signature matches the one expected by a method reference.</summary>
|
||||||
|
/// <param name="type">The type to check.</param>
|
||||||
|
/// <param name="reference">The method reference.</param>
|
||||||
|
protected bool HasMatchingSignature(Type type, MethodReference reference)
|
||||||
|
{
|
||||||
|
return type
|
||||||
|
.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)
|
||||||
|
.Any(method => this.HasMatchingSignature(method, reference));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get whether a type matches a type reference.</summary>
|
||||||
|
/// <param name="type">The defined type.</param>
|
||||||
|
/// <param name="reference">The type reference.</param>
|
||||||
|
private bool IsMatchingType(Type type, TypeReference reference)
|
||||||
|
{
|
||||||
|
// same namespace & name
|
||||||
|
if (type.Namespace != reference.Namespace || type.Name != reference.Name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// same generic parameters
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
if (!reference.IsGenericInstance)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Type[] defGenerics = type.GetGenericArguments();
|
||||||
|
TypeReference[] refGenerics = ((GenericInstanceType)reference).GenericArguments.ToArray();
|
||||||
|
if (defGenerics.Length != refGenerics.Length)
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < defGenerics.Length; i++)
|
||||||
|
{
|
||||||
|
if (!this.IsMatchingType(defGenerics[i], refGenerics[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using StardewModdingAPI.AssemblyRewriters.Wrappers;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.AssemblyRewriters.Rewriters
|
||||||
|
{
|
||||||
|
/// <summary>Rewrites references to <see cref="SpriteBatch"/> to fix inconsistent method signatures between MonoGame and XNA.</summary>
|
||||||
|
/// <remarks>MonoGame has one <c>SpriteBatch.Begin</c> method with optional arguments, but XNA has multiple method overloads. Incompatible method references are rewritten to use <see cref="CompatibleSpriteBatch"/>, which redirects all method signatures to the proper compiled MonoGame/XNA method.</remarks>
|
||||||
|
public class SpriteBatchRewriter : BaseMethodRewriter
|
||||||
|
{
|
||||||
|
/// <summary>Get whether the given method reference can be rewritten.</summary>
|
||||||
|
/// <param name="methodRef">The method reference.</param>
|
||||||
|
public override bool ShouldRewrite(MethodReference methodRef)
|
||||||
|
{
|
||||||
|
return methodRef.DeclaringType.FullName == typeof(SpriteBatch).FullName && this.HasMatchingSignature(typeof(CompatibleSpriteBatch), methodRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Rewrite a method for compatibility.</summary>
|
||||||
|
/// <param name="module">The module being rewritten.</param>
|
||||||
|
/// <param name="cil">The CIL rewriter.</param>
|
||||||
|
/// <param name="callOp">The instruction which calls the method.</param>
|
||||||
|
/// <param name="methodRef">The method reference invoked by the <paramref name="callOp"/>.</param>
|
||||||
|
/// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
|
||||||
|
public override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction callOp, MethodReference methodRef, PlatformAssemblyMap assemblyMap)
|
||||||
|
{
|
||||||
|
methodRef.DeclaringType = module.Import(typeof(CompatibleSpriteBatch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{10DB0676-9FC1-4771-A2C8-E2519F091E49}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>StardewModdingAPI.AssemblyRewriters</RootNamespace>
|
||||||
|
<AssemblyName>StardewModdingAPI.AssemblyRewriters</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE;SMAPI_FOR_WINDOWS</DefineConstants>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||||
|
<OutputPath>bin\x86\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE;SMAPI_FOR_WINDOWS</DefineConstants>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(SolutionDir)\dependencies.targets" />
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\GlobalAssemblyInfo.cs">
|
||||||
|
<Link>Properties\GlobalAssemblyInfo.cs</Link>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="IMethodRewriter.cs" />
|
||||||
|
<Compile Include="Platform.cs" />
|
||||||
|
<Compile Include="PlatformAssemblyMap.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Rewriters\BaseMethodRewriter.cs" />
|
||||||
|
<Compile Include="Rewriters\SpriteBatchRewriter.cs" />
|
||||||
|
<Compile Include="Wrappers\CompatibleSpriteBatch.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup />
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
|
@ -0,0 +1,52 @@
|
||||||
|
using Microsoft.Xna.Framework;
|
||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
|
#pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required
|
||||||
|
namespace StardewModdingAPI.AssemblyRewriters.Wrappers
|
||||||
|
{
|
||||||
|
/// <summary>Wraps <see cref="SpriteBatch"/> methods that are incompatible when converting compiled code between MonoGame and XNA.</summary>
|
||||||
|
public class CompatibleSpriteBatch : SpriteBatch
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
public CompatibleSpriteBatch(GraphicsDevice graphicsDevice) : base(graphicsDevice) { }
|
||||||
|
|
||||||
|
/****
|
||||||
|
** MonoGame signatures
|
||||||
|
****/
|
||||||
|
public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix? matrix)
|
||||||
|
{
|
||||||
|
base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, matrix ?? Matrix.Identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
** XNA signatures
|
||||||
|
****/
|
||||||
|
public new void Begin()
|
||||||
|
{
|
||||||
|
base.Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Begin(SpriteSortMode sortMode, BlendState blendState)
|
||||||
|
{
|
||||||
|
base.Begin(sortMode, blendState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState)
|
||||||
|
{
|
||||||
|
base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect)
|
||||||
|
{
|
||||||
|
base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix transformMatrix)
|
||||||
|
{
|
||||||
|
base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, transformMatrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net452" />
|
||||||
|
</packages>
|
|
@ -27,6 +27,27 @@ namespace StardewModdingApi.Installer
|
||||||
@"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley"
|
@"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>The files to remove when uninstalling SMAPI.</summary>
|
||||||
|
private readonly string[] UninstallFiles =
|
||||||
|
{
|
||||||
|
// common
|
||||||
|
"StardewModdingAPI.exe",
|
||||||
|
"StardewModdingAPI-settings.json",
|
||||||
|
"StardewModdingAPI.AssemblyRewriters.dll",
|
||||||
|
"steam_appid.txt",
|
||||||
|
|
||||||
|
// Linux/Mac only
|
||||||
|
"Mono.Cecil.dll",
|
||||||
|
"Mono.Cecil.Rocks.dll",
|
||||||
|
"Newtonsoft.Json.dll",
|
||||||
|
"StardewModdingAPI",
|
||||||
|
"StardewModdingAPI.exe.mdb",
|
||||||
|
"System.Numerics.dll",
|
||||||
|
|
||||||
|
// Windows only
|
||||||
|
"StardewModdingAPI.pdb"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Public methods
|
** Public methods
|
||||||
|
@ -47,7 +68,7 @@ namespace StardewModdingApi.Installer
|
||||||
///
|
///
|
||||||
/// Uninstall logic:
|
/// Uninstall logic:
|
||||||
/// 1. On Linux/Mac: if a backup of the launcher exists, delete the launcher and restore the backup.
|
/// 1. On Linux/Mac: if a backup of the launcher exists, delete the launcher and restore the backup.
|
||||||
/// 2. Delete all files in the game directory matching a file under package/Windows or package/Mono.
|
/// 2. Delete all files in the game directory matching one of the <see cref="UninstallFiles"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void Run(string[] args)
|
public void Run(string[] args)
|
||||||
{
|
{
|
||||||
|
@ -127,9 +148,9 @@ namespace StardewModdingApi.Installer
|
||||||
|
|
||||||
// remove SMAPI files
|
// remove SMAPI files
|
||||||
this.PrintDebug("Removing SMAPI files...");
|
this.PrintDebug("Removing SMAPI files...");
|
||||||
foreach (FileInfo sourceFile in packageDir.EnumerateFiles())
|
foreach (string filename in this.UninstallFiles)
|
||||||
{
|
{
|
||||||
string targetPath = Path.Combine(installDir.FullName, sourceFile.Name);
|
string targetPath = Path.Combine(installDir.FullName, filename);
|
||||||
if (File.Exists(targetPath))
|
if (File.Exists(targetPath))
|
||||||
File.Delete(targetPath);
|
File.Delete(targetPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,18 +66,25 @@
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(CompiledInstallerPath)\install.exe" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFiles="$(CompiledInstallerPath)\install.exe" />
|
||||||
<Copy SourceFiles="$(TargetDir)\readme.txt" DestinationFiles="$(CompiledInstallerPath)\readme.txt" />
|
<Copy SourceFiles="$(TargetDir)\readme.txt" DestinationFiles="$(CompiledInstallerPath)\readme.txt" />
|
||||||
<!-- copy SMAPI files for Mono -->
|
<!-- copy SMAPI files for Mono -->
|
||||||
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Mono.Cecil.dll" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Mono.Cecil.Rocks.dll" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Newtonsoft.Json.dll" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Newtonsoft.Json.dll" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.exe" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.exe" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.exe.mdb" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.exe.mdb" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.AssemblyRewriters.dll" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI-settings.json" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI-settings.json" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\System.Numerics.dll" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\System.Numerics.dll" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\unix-launcher.sh" DestinationFiles="$(CompiledInstallerPath)\Mono\StardewModdingAPI" />
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\unix-launcher.sh" DestinationFiles="$(CompiledInstallerPath)\Mono\StardewModdingAPI" />
|
||||||
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\steam_appid.txt" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\steam_appid.txt" DestinationFolder="$(CompiledInstallerPath)\Mono" />
|
||||||
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="@(CompiledMods)" DestinationFolder="$(CompiledInstallerPath)\Mono\Mods\%(RecursiveDir)" />
|
<Copy Condition="$(OS) != 'Windows_NT'" SourceFiles="@(CompiledMods)" DestinationFolder="$(CompiledInstallerPath)\Mono\Mods\%(RecursiveDir)" />
|
||||||
<!-- copy SMAPI files for Windows -->
|
<!-- copy SMAPI files for Windows -->
|
||||||
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Mono.Cecil.dll" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Mono.Cecil.Rocks.dll" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\Newtonsoft.Json.dll" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.exe" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.exe" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.pdb" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.pdb" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.xml" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.xml" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI.AssemblyRewriters.dll" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI-settings.json" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\StardewModdingAPI-settings.json" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\steam_appid.txt" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="$(CompiledSmapiPath)\steam_appid.txt" DestinationFolder="$(CompiledInstallerPath)\Windows" />
|
||||||
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="@(CompiledMods)" DestinationFolder="$(CompiledInstallerPath)\Windows\Mods\%(RecursiveDir)" />
|
<Copy Condition="$(OS) == 'Windows_NT'" SourceFiles="@(CompiledMods)" DestinationFolder="$(CompiledInstallerPath)\Windows\Mods\%(RecursiveDir)" />
|
||||||
|
|
|
@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StardewModdingAPI.Installer
|
||||||
{F1A573B0-F436-472C-AE29-0B91EA6B9F8F} = {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}
|
{F1A573B0-F436-472C-AE29-0B91EA6B9F8F} = {F1A573B0-F436-472C-AE29-0B91EA6B9F8F}
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StardewModdingAPI.AssemblyRewriters", "StardewModdingAPI.AssemblyRewriters\StardewModdingAPI.AssemblyRewriters.csproj", "{10DB0676-9FC1-4771-A2C8-E2519F091E49}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -67,6 +69,18 @@ Global
|
||||||
{443DDF81-6AAF-420A-A610-3459F37E5575}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{443DDF81-6AAF-420A-A610-3459F37E5575}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{443DDF81-6AAF-420A-A610-3459F37E5575}.Release|x86.ActiveCfg = Release|Any CPU
|
{443DDF81-6AAF-420A-A610-3459F37E5575}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{443DDF81-6AAF-420A-A610-3459F37E5575}.Release|x86.Build.0 = Release|Any CPU
|
{443DDF81-6AAF-420A-A610-3459F37E5575}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{10DB0676-9FC1-4771-A2C8-E2519F091E49}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using StardewModdingAPI.AssemblyRewriters;
|
||||||
|
using StardewModdingAPI.AssemblyRewriters.Rewriters;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
|
||||||
namespace StardewModdingAPI
|
namespace StardewModdingAPI
|
||||||
|
@ -23,13 +26,13 @@ namespace StardewModdingAPI
|
||||||
** Accessors
|
** Accessors
|
||||||
*********/
|
*********/
|
||||||
/// <summary>SMAPI's current semantic version.</summary>
|
/// <summary>SMAPI's current semantic version.</summary>
|
||||||
public static readonly Version Version = new Version(1, 2, 0, null);
|
public static readonly Version Version = new Version(1, 3, 0, null);
|
||||||
|
|
||||||
/// <summary>The minimum supported version of Stardew Valley.</summary>
|
/// <summary>The minimum supported version of Stardew Valley.</summary>
|
||||||
public const string MinimumGameVersion = "1.1";
|
public const string MinimumGameVersion = "1.1";
|
||||||
|
|
||||||
/// <summary>The GitHub repository to check for updates.</summary>
|
/// <summary>The GitHub repository to check for updates.</summary>
|
||||||
public const string GitHubRepository = "ClxS/SMAPI";
|
public const string GitHubRepository = "Pathoschild/SMAPI";
|
||||||
|
|
||||||
/// <summary>The directory path containing Stardew Valley's app data.</summary>
|
/// <summary>The directory path containing Stardew Valley's app data.</summary>
|
||||||
public static string DataPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley");
|
public static string DataPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley");
|
||||||
|
@ -63,8 +66,63 @@ namespace StardewModdingAPI
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Private field
|
** Protected methods
|
||||||
*********/
|
*********/
|
||||||
|
/// <summary>Get metadata for mapping assemblies to the current platform.</summary>
|
||||||
|
/// <param name="targetPlatform">The target game platform.</param>
|
||||||
|
internal static PlatformAssemblyMap GetAssemblyMap(Platform targetPlatform)
|
||||||
|
{
|
||||||
|
// get assembly changes needed for platform
|
||||||
|
string[] removeAssemblyReferences;
|
||||||
|
Assembly[] targetAssemblies;
|
||||||
|
switch (targetPlatform)
|
||||||
|
{
|
||||||
|
case Platform.Mono:
|
||||||
|
removeAssemblyReferences = new[]
|
||||||
|
{
|
||||||
|
"Stardew Valley",
|
||||||
|
"Microsoft.Xna.Framework",
|
||||||
|
"Microsoft.Xna.Framework.Game",
|
||||||
|
"Microsoft.Xna.Framework.Graphics"
|
||||||
|
};
|
||||||
|
targetAssemblies = new[]
|
||||||
|
{
|
||||||
|
typeof(StardewValley.Game1).Assembly,
|
||||||
|
typeof(Microsoft.Xna.Framework.Vector2).Assembly
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Platform.Windows:
|
||||||
|
removeAssemblyReferences = new[]
|
||||||
|
{
|
||||||
|
"StardewValley",
|
||||||
|
"MonoGame.Framework"
|
||||||
|
};
|
||||||
|
targetAssemblies = new[]
|
||||||
|
{
|
||||||
|
typeof(StardewValley.Game1).Assembly,
|
||||||
|
typeof(Microsoft.Xna.Framework.Vector2).Assembly,
|
||||||
|
typeof(Microsoft.Xna.Framework.Game).Assembly,
|
||||||
|
typeof(Microsoft.Xna.Framework.Graphics.SpriteBatch).Assembly
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"Unknown target platform '{targetPlatform}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences, targetAssemblies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get method rewriters which fix incompatible method calls in mod assemblies.</summary>
|
||||||
|
internal static IEnumerable<IMethodRewriter> GetMethodRewriters()
|
||||||
|
{
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new SpriteBatchRewriter()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Get the name of a save directory for the current player.</summary>
|
/// <summary>Get the name of a save directory for the current player.</summary>
|
||||||
private static string GetSaveFolderName()
|
private static string GetSaveFolderName()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Weavers>
|
|
||||||
<Costura>
|
|
||||||
<ExcludeAssemblies>
|
|
||||||
Stardew Valley
|
|
||||||
xTile
|
|
||||||
Steamworks.NET
|
|
||||||
Lidgren.Network
|
|
||||||
</ExcludeAssemblies>
|
|
||||||
</Costura>
|
|
||||||
|
|
||||||
</Weavers>
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Rocks;
|
||||||
|
using StardewModdingAPI.AssemblyRewriters;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Framework.AssemblyRewriting
|
||||||
|
{
|
||||||
|
/// <summary>Rewrites type references.</summary>
|
||||||
|
internal class AssemblyTypeRewriter
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Properties
|
||||||
|
*********/
|
||||||
|
/// <summary>Metadata for mapping assemblies to the current <see cref="Platform"/>.</summary>
|
||||||
|
private readonly PlatformAssemblyMap AssemblyMap;
|
||||||
|
|
||||||
|
/// <summary>A type => assembly lookup for types which should be rewritten.</summary>
|
||||||
|
private readonly IDictionary<string, Assembly> TypeAssemblies;
|
||||||
|
|
||||||
|
/// <summary>Encapsulates monitoring and logging.</summary>
|
||||||
|
private readonly IMonitor Monitor;
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="assemblyMap">Metadata for mapping assemblies to the current <see cref="Platform"/>.</param>
|
||||||
|
/// <param name="monitor">Encapsulates monitoring and logging.</param>
|
||||||
|
public AssemblyTypeRewriter(PlatformAssemblyMap assemblyMap, IMonitor monitor)
|
||||||
|
{
|
||||||
|
// save config
|
||||||
|
this.AssemblyMap = assemblyMap;
|
||||||
|
this.Monitor = monitor;
|
||||||
|
|
||||||
|
// collect type => assembly lookup
|
||||||
|
this.TypeAssemblies = new Dictionary<string, Assembly>();
|
||||||
|
foreach (Assembly assembly in assemblyMap.Targets)
|
||||||
|
{
|
||||||
|
ModuleDefinition module = this.AssemblyMap.TargetModules[assembly];
|
||||||
|
foreach (TypeDefinition type in module.GetTypes())
|
||||||
|
{
|
||||||
|
if (!type.IsPublic)
|
||||||
|
continue; // no need to rewrite
|
||||||
|
if (type.Namespace.Contains("<"))
|
||||||
|
continue; // ignore assembly metadata
|
||||||
|
this.TypeAssemblies[type.FullName] = assembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Rewrite the types referenced by an assembly.</summary>
|
||||||
|
/// <param name="assembly">The assembly to rewrite.</param>
|
||||||
|
public void RewriteAssembly(AssemblyDefinition assembly)
|
||||||
|
{
|
||||||
|
ModuleDefinition module = assembly.Modules.Single(); // technically an assembly can have multiple modules, but none of the build tools (including MSBuild) support it; simplify by assuming one module
|
||||||
|
|
||||||
|
// remove old assembly references
|
||||||
|
bool shouldRewrite = false;
|
||||||
|
for (int i = 0; i < module.AssemblyReferences.Count; i++)
|
||||||
|
{
|
||||||
|
if (this.AssemblyMap.RemoveNames.Any(name => module.AssemblyReferences[i].Name == name))
|
||||||
|
{
|
||||||
|
this.Monitor.Log($"removing reference to {module.AssemblyReferences[i]}", LogLevel.Trace);
|
||||||
|
shouldRewrite = true;
|
||||||
|
module.AssemblyReferences.RemoveAt(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!shouldRewrite)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// add target assembly references
|
||||||
|
foreach (AssemblyNameReference target in this.AssemblyMap.TargetReferences.Values)
|
||||||
|
{
|
||||||
|
this.Monitor.Log($" adding reference to {target}", LogLevel.Trace);
|
||||||
|
module.AssemblyReferences.Add(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewrite type scopes to use target assemblies
|
||||||
|
IEnumerable<TypeReference> typeReferences = module.GetTypeReferences().OrderBy(p => p.FullName);
|
||||||
|
string lastTypeLogged = null;
|
||||||
|
foreach (TypeReference type in typeReferences)
|
||||||
|
{
|
||||||
|
this.ChangeTypeScope(type, shouldLog: type.FullName != lastTypeLogged);
|
||||||
|
lastTypeLogged = type.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewrite incompatible methods
|
||||||
|
IMethodRewriter[] methodRewriters = Constants.GetMethodRewriters().ToArray();
|
||||||
|
foreach (MethodDefinition method in this.GetMethods(module))
|
||||||
|
{
|
||||||
|
// skip methods with no rewritable method
|
||||||
|
bool hasMethodToRewrite = method.Body.Instructions.Any(op => (op.OpCode == OpCodes.Call || op.OpCode == OpCodes.Callvirt) && methodRewriters.Any(rewriter => rewriter.ShouldRewrite((MethodReference)op.Operand)));
|
||||||
|
if (!hasMethodToRewrite)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// rewrite method references
|
||||||
|
method.Body.SimplifyMacros();
|
||||||
|
ILProcessor cil = method.Body.GetILProcessor();
|
||||||
|
Instruction[] instructions = cil.Body.Instructions.ToArray();
|
||||||
|
foreach (Instruction op in instructions)
|
||||||
|
{
|
||||||
|
if (op.OpCode == OpCodes.Call || op.OpCode == OpCodes.Callvirt)
|
||||||
|
{
|
||||||
|
IMethodRewriter rewriter = methodRewriters.FirstOrDefault(p => p.ShouldRewrite((MethodReference)op.Operand));
|
||||||
|
if (rewriter != null)
|
||||||
|
{
|
||||||
|
MethodReference methodRef = (MethodReference)op.Operand;
|
||||||
|
this.Monitor.Log($"rewriting method reference {methodRef.DeclaringType.FullName}.{methodRef.Name}", LogLevel.Trace);
|
||||||
|
rewriter.Rewrite(module, cil, op, methodRef, this.AssemblyMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
method.Body.OptimizeMacros();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Private methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Get the correct reference to use for compatibility with the current platform.</summary>
|
||||||
|
/// <param name="type">The type reference to rewrite.</param>
|
||||||
|
/// <param name="shouldLog">Whether to log a message.</param>
|
||||||
|
private void ChangeTypeScope(TypeReference type, bool shouldLog)
|
||||||
|
{
|
||||||
|
// check skip conditions
|
||||||
|
if (type == null || type.FullName.StartsWith("System."))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get assembly
|
||||||
|
Assembly assembly;
|
||||||
|
if (!this.TypeAssemblies.TryGetValue(type.FullName, out assembly))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// replace scope
|
||||||
|
AssemblyNameReference assemblyRef = this.AssemblyMap.TargetReferences[assembly];
|
||||||
|
if (shouldLog)
|
||||||
|
this.Monitor.Log($"redirecting {type.FullName} from {type.Scope.Name} to {assemblyRef.Name}", LogLevel.Trace);
|
||||||
|
type.Scope = assemblyRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Get all methods in a module.</summary>
|
||||||
|
/// <param name="module">The module to search.</param>
|
||||||
|
private IEnumerable<MethodDefinition> GetMethods(ModuleDefinition module)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
from type in module.GetTypes()
|
||||||
|
where type.HasMethods
|
||||||
|
from method in type.Methods
|
||||||
|
where method.HasBody
|
||||||
|
select method
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
namespace StardewModdingAPI.Framework.AssemblyRewriting
|
||||||
|
{
|
||||||
|
/// <summary>Contains the paths for an assembly's cached data.</summary>
|
||||||
|
internal struct CachePaths
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Accessors
|
||||||
|
*********/
|
||||||
|
/// <summary>The directory path which contains the assembly.</summary>
|
||||||
|
public string Directory { get; }
|
||||||
|
|
||||||
|
/// <summary>The file path of the assembly file.</summary>
|
||||||
|
public string Assembly { get; }
|
||||||
|
|
||||||
|
/// <summary>The file path containing the MD5 hash for the assembly.</summary>
|
||||||
|
public string Hash { get; }
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="directory">The directory path which contains the assembly.</param>
|
||||||
|
/// <param name="assembly">The file path of the assembly file.</param>
|
||||||
|
/// <param name="hash">The file path containing the MD5 hash for the assembly.</param>
|
||||||
|
public CachePaths(string directory, string assembly, string hash)
|
||||||
|
{
|
||||||
|
this.Directory = directory;
|
||||||
|
this.Assembly = assembly;
|
||||||
|
this.Hash = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Mono.Cecil;
|
||||||
|
using StardewModdingAPI.AssemblyRewriters;
|
||||||
|
using StardewModdingAPI.Framework.AssemblyRewriting;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Framework
|
||||||
|
{
|
||||||
|
/// <summary>Preprocesses and loads mod assemblies.</summary>
|
||||||
|
internal class ModAssemblyLoader
|
||||||
|
{
|
||||||
|
/*********
|
||||||
|
** Properties
|
||||||
|
*********/
|
||||||
|
/// <summary>The directory in which to cache data.</summary>
|
||||||
|
private readonly string CacheDirPath;
|
||||||
|
|
||||||
|
/// <summary>Metadata for mapping assemblies to the current <see cref="Platform"/>.</summary>
|
||||||
|
private readonly PlatformAssemblyMap AssemblyMap;
|
||||||
|
|
||||||
|
/// <summary>Rewrites assembly types to match the current platform.</summary>
|
||||||
|
private readonly AssemblyTypeRewriter AssemblyTypeRewriter;
|
||||||
|
|
||||||
|
/// <summary>Encapsulates monitoring and logging.</summary>
|
||||||
|
private readonly IMonitor Monitor;
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Public methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Construct an instance.</summary>
|
||||||
|
/// <param name="cacheDirPath">The cache directory.</param>
|
||||||
|
/// <param name="targetPlatform">The current game platform.</param>
|
||||||
|
/// <param name="monitor">Encapsulates monitoring and logging.</param>
|
||||||
|
public ModAssemblyLoader(string cacheDirPath, Platform targetPlatform, IMonitor monitor)
|
||||||
|
{
|
||||||
|
this.CacheDirPath = cacheDirPath;
|
||||||
|
this.Monitor = monitor;
|
||||||
|
this.AssemblyMap = Constants.GetAssemblyMap(targetPlatform);
|
||||||
|
this.AssemblyTypeRewriter = new AssemblyTypeRewriter(this.AssemblyMap, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Preprocess an assembly and cache the modified version.</summary>
|
||||||
|
/// <param name="assemblyPath">The assembly file path.</param>
|
||||||
|
public void ProcessAssembly(string assemblyPath)
|
||||||
|
{
|
||||||
|
// read assembly data
|
||||||
|
string assemblyFileName = Path.GetFileName(assemblyPath);
|
||||||
|
string assemblyDir = Path.GetDirectoryName(assemblyPath);
|
||||||
|
byte[] assemblyBytes = File.ReadAllBytes(assemblyPath);
|
||||||
|
string hash = $"SMAPI {Constants.Version}|" + string.Join("", MD5.Create().ComputeHash(assemblyBytes).Select(p => p.ToString("X2")));
|
||||||
|
|
||||||
|
// check cache
|
||||||
|
CachePaths cachePaths = this.GetCacheInfo(assemblyPath);
|
||||||
|
bool canUseCache = File.Exists(cachePaths.Assembly) && File.Exists(cachePaths.Hash) && hash == File.ReadAllText(cachePaths.Hash);
|
||||||
|
|
||||||
|
// process assembly if not cached
|
||||||
|
if (!canUseCache)
|
||||||
|
{
|
||||||
|
this.Monitor.Log($"Loading {assemblyFileName} for the first time; preprocessing...", LogLevel.Trace);
|
||||||
|
|
||||||
|
// read assembly definition
|
||||||
|
AssemblyDefinition assembly;
|
||||||
|
using (Stream readStream = new MemoryStream(assemblyBytes))
|
||||||
|
assembly = AssemblyDefinition.ReadAssembly(readStream);
|
||||||
|
|
||||||
|
// rewrite assembly to match platform
|
||||||
|
this.AssemblyTypeRewriter.RewriteAssembly(assembly);
|
||||||
|
|
||||||
|
// write cache
|
||||||
|
using (MemoryStream outStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
// get assembly bytes
|
||||||
|
assembly.Write(outStream);
|
||||||
|
byte[] outBytes = outStream.ToArray();
|
||||||
|
|
||||||
|
// write assembly data
|
||||||
|
Directory.CreateDirectory(cachePaths.Directory);
|
||||||
|
File.WriteAllBytes(cachePaths.Assembly, outBytes);
|
||||||
|
File.WriteAllText(cachePaths.Hash, hash);
|
||||||
|
|
||||||
|
// copy any mdb/pdb files
|
||||||
|
foreach (string path in Directory.GetFiles(assemblyDir, "*.mdb").Concat(Directory.GetFiles(assemblyDir, "*.pdb")))
|
||||||
|
{
|
||||||
|
string filename = Path.GetFileName(path);
|
||||||
|
File.Copy(path, Path.Combine(cachePaths.Directory, filename), overwrite: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Load a preprocessed assembly.</summary>
|
||||||
|
/// <param name="assemblyPath">The assembly file path.</param>
|
||||||
|
public Assembly LoadCachedAssembly(string assemblyPath)
|
||||||
|
{
|
||||||
|
CachePaths cachePaths = this.GetCacheInfo(assemblyPath);
|
||||||
|
if (!File.Exists(cachePaths.Assembly))
|
||||||
|
throw new InvalidOperationException($"The assembly {assemblyPath} doesn't exist in the preprocessed cache.");
|
||||||
|
return Assembly.UnsafeLoadFrom(cachePaths.Assembly); // unsafe load allows DLLs downloaded from the Internet without the user needing to 'unblock' them
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Resolve an assembly from its name.</summary>
|
||||||
|
/// <param name="name">The assembly name.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This implementation returns the first loaded assembly which matches the short form of
|
||||||
|
/// the assembly name, to resolve assembly resolution issues when rewriting
|
||||||
|
/// assemblies (especially with Mono). Since this is meant to be called on <see cref="AppDomain.AssemblyResolve"/>,
|
||||||
|
/// the implicit assumption is that loading the exact assembly failed.
|
||||||
|
/// </remarks>
|
||||||
|
public Assembly ResolveAssembly(string name)
|
||||||
|
{
|
||||||
|
string shortName = name.Split(new[] { ',' }, 2).First(); // get simple name (without version and culture)
|
||||||
|
return AppDomain.CurrentDomain
|
||||||
|
.GetAssemblies()
|
||||||
|
.FirstOrDefault(p => p.GetName().Name == shortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*********
|
||||||
|
** Private methods
|
||||||
|
*********/
|
||||||
|
/// <summary>Get the cache details for an assembly.</summary>
|
||||||
|
/// <param name="assemblyPath">The assembly file path.</param>
|
||||||
|
private CachePaths GetCacheInfo(string assemblyPath)
|
||||||
|
{
|
||||||
|
string key = Path.GetFileNameWithoutExtension(assemblyPath);
|
||||||
|
string dirPath = Path.Combine(this.CacheDirPath, new DirectoryInfo(Path.GetDirectoryName(assemblyPath)).Name);
|
||||||
|
string cacheAssemblyPath = Path.Combine(dirPath, $"{key}.dll");
|
||||||
|
string cacheHashPath = Path.Combine(dirPath, $"{key}.hash");
|
||||||
|
return new CachePaths(dirPath, cacheAssemblyPath, cacheHashPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ using System.Windows.Forms;
|
||||||
#endif
|
#endif
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using StardewModdingAPI.AssemblyRewriters;
|
||||||
using StardewModdingAPI.Events;
|
using StardewModdingAPI.Events;
|
||||||
using StardewModdingAPI.Framework;
|
using StardewModdingAPI.Framework;
|
||||||
using StardewModdingAPI.Inheritance;
|
using StardewModdingAPI.Inheritance;
|
||||||
|
@ -23,16 +24,23 @@ namespace StardewModdingAPI
|
||||||
/*********
|
/*********
|
||||||
** Properties
|
** Properties
|
||||||
*********/
|
*********/
|
||||||
/// <summary>The full path to the Stardew Valley executable.</summary>
|
/// <summary>The target game platform.</summary>
|
||||||
|
private static readonly Platform TargetPlatform =
|
||||||
#if SMAPI_FOR_WINDOWS
|
#if SMAPI_FOR_WINDOWS
|
||||||
private static readonly string GameExecutablePath = Path.Combine(Constants.ExecutionPath, "Stardew Valley.exe");
|
Platform.Windows;
|
||||||
#else
|
#else
|
||||||
private static readonly string GameExecutablePath = Path.Combine(Constants.ExecutionPath, "StardewValley.exe");
|
Platform.Mono;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// <summary>The full path to the Stardew Valley executable.</summary>
|
||||||
|
private static readonly string GameExecutablePath = Path.Combine(Constants.ExecutionPath, Program.TargetPlatform == Platform.Windows ? "Stardew Valley.exe" : "StardewValley.exe");
|
||||||
|
|
||||||
/// <summary>The full path to the folder containing mods.</summary>
|
/// <summary>The full path to the folder containing mods.</summary>
|
||||||
private static readonly string ModPath = Path.Combine(Constants.ExecutionPath, "Mods");
|
private static readonly string ModPath = Path.Combine(Constants.ExecutionPath, "Mods");
|
||||||
|
|
||||||
|
/// <summary>The full path to the folder containing cached SMAPI data.</summary>
|
||||||
|
private static readonly string CachePath = Path.Combine(Program.ModPath, ".cache");
|
||||||
|
|
||||||
/// <summary>The log file to which to write messages.</summary>
|
/// <summary>The log file to which to write messages.</summary>
|
||||||
private static readonly LogFileManager LogFile = new LogFileManager(Constants.LogPath);
|
private static readonly LogFileManager LogFile = new LogFileManager(Constants.LogPath);
|
||||||
|
|
||||||
|
@ -126,6 +134,7 @@ namespace StardewModdingAPI
|
||||||
Program.Monitor.Log("Loading SMAPI...");
|
Program.Monitor.Log("Loading SMAPI...");
|
||||||
Console.Title = Constants.ConsoleTitle;
|
Console.Title = Constants.ConsoleTitle;
|
||||||
Program.VerifyPath(Program.ModPath);
|
Program.VerifyPath(Program.ModPath);
|
||||||
|
Program.VerifyPath(Program.CachePath);
|
||||||
Program.VerifyPath(Constants.LogDir);
|
Program.VerifyPath(Constants.LogDir);
|
||||||
if (!File.Exists(Program.GameExecutablePath))
|
if (!File.Exists(Program.GameExecutablePath))
|
||||||
{
|
{
|
||||||
|
@ -293,10 +302,18 @@ namespace StardewModdingAPI
|
||||||
private static void LoadMods()
|
private static void LoadMods()
|
||||||
{
|
{
|
||||||
Program.Monitor.Log("Loading mods...");
|
Program.Monitor.Log("Loading mods...");
|
||||||
|
|
||||||
|
// get assembly loader
|
||||||
|
ModAssemblyLoader modAssemblyLoader = new ModAssemblyLoader(Program.CachePath, Program.TargetPlatform, Program.Monitor);
|
||||||
|
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => modAssemblyLoader.ResolveAssembly(e.Name);
|
||||||
|
|
||||||
|
// load mods
|
||||||
foreach (string directory in Directory.GetDirectories(Program.ModPath))
|
foreach (string directory in Directory.GetDirectories(Program.ModPath))
|
||||||
{
|
{
|
||||||
foreach (string manifestPath in Directory.GetFiles(directory, "manifest.json"))
|
// ignore internal directory
|
||||||
{
|
if (new DirectoryInfo(directory).Name == ".cache")
|
||||||
|
continue;
|
||||||
|
|
||||||
// check for cancellation
|
// check for cancellation
|
||||||
if (Program.CancellationTokenSource.IsCancellationRequested)
|
if (Program.CancellationTokenSource.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
@ -304,10 +321,12 @@ namespace StardewModdingAPI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get helper
|
||||||
IModHelper helper = new ModHelper(directory);
|
IModHelper helper = new ModHelper(directory);
|
||||||
string errorPrefix = $"Couldn't load mod for manifest '{manifestPath}'";
|
|
||||||
|
|
||||||
// read manifest
|
// get manifest path
|
||||||
|
string manifestPath = Path.Combine(directory, "manifest.json");
|
||||||
|
string errorPrefix = $"Couldn't load mod for manifest '{manifestPath}'";
|
||||||
Manifest manifest;
|
Manifest manifest;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -377,23 +396,51 @@ namespace StardewModdingAPI
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Program.Monitor.Log($"{errorPrefix}: couldm't create the per-save configuration directory ('psconfigs') requested by this mod.\n{ex.GetLogSummary()}", LogLevel.Error);
|
Program.Monitor.Log($"{errorPrefix}: couldn't create the per-save configuration directory ('psconfigs') requested by this mod.\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load DLL & hook up mod
|
// preprocess mod assemblies
|
||||||
|
{
|
||||||
|
bool succeeded = true;
|
||||||
|
foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
modAssemblyLoader.ProcessAssembly(assemblyPath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Program.Monitor.Log($"{errorPrefix}: an error occurred while preprocessing '{Path.GetFileName(assemblyPath)}'.\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||||
|
succeeded = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!succeeded)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load assembly
|
||||||
|
Assembly modAssembly;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string assemblyPath = Path.Combine(directory, manifest.EntryDll);
|
string assemblyPath = Path.Combine(directory, manifest.EntryDll);
|
||||||
if (!File.Exists(assemblyPath))
|
modAssembly = modAssemblyLoader.LoadCachedAssembly(assemblyPath);
|
||||||
|
if (modAssembly.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) == 0)
|
||||||
{
|
{
|
||||||
Program.Monitor.Log($"{errorPrefix}: target DLL '{assemblyPath}' does not exist.", LogLevel.Error);
|
Program.Monitor.Log($"{errorPrefix}: the mod DLL does not contain an implementation of the 'Mod' class.", LogLevel.Error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Program.Monitor.Log($"{errorPrefix}: an error occurred while optimising the target DLL.\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assembly modAssembly = Assembly.UnsafeLoadFrom(assemblyPath);
|
// hook up mod
|
||||||
if (modAssembly.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) > 0)
|
try
|
||||||
{
|
{
|
||||||
TypeInfo modEntryType = modAssembly.DefinedTypes.First(x => x.BaseType == typeof(Mod));
|
TypeInfo modEntryType = modAssembly.DefinedTypes.First(x => x.BaseType == typeof(Mod));
|
||||||
Mod modEntry = (Mod)modAssembly.CreateInstance(modEntryType.ToString());
|
Mod modEntry = (Mod)modAssembly.CreateInstance(modEntryType.ToString());
|
||||||
|
@ -420,15 +467,11 @@ namespace StardewModdingAPI
|
||||||
Program.DeprecationManager.Warn(manifest.Name, $"an old version of {nameof(Mod)}.{nameof(Mod.Entry)}", "1.1", DeprecationLevel.Notice);
|
Program.DeprecationManager.Warn(manifest.Name, $"an old version of {nameof(Mod)}.{nameof(Mod.Entry)}", "1.1", DeprecationLevel.Notice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Program.Monitor.Log($"{errorPrefix}: the mod DLL does not contain an implementation of the 'Mod' class.", LogLevel.Error);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Program.Monitor.Log($"{errorPrefix}: an error occurred while loading the target DLL.\n{ex.GetLogSummary()}", LogLevel.Error);
|
Program.Monitor.Log($"{errorPrefix}: an error occurred while loading the target DLL.\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// print result
|
// print result
|
||||||
Program.Monitor.Log($"Loaded {Program.ModsLoaded} mods.");
|
Program.Monitor.Log($"Loaded {Program.ModsLoaded} mods.");
|
||||||
|
|
|
@ -79,67 +79,24 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<Import Project="$(SolutionDir)\dependencies.targets" />
|
||||||
<!-- Linux paths -->
|
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/GOG Games/Stardew Valley/game</GamePath>
|
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.local/share/Steam/steamapps/common/Stardew Valley</GamePath>
|
|
||||||
<!-- Mac paths -->
|
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS</GamePath>
|
|
||||||
<!-- Windows paths -->
|
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley</GamePath>
|
|
||||||
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley</GamePath>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Choose>
|
|
||||||
<When Condition="$(OS) == 'Windows_NT'">
|
|
||||||
<PropertyGroup>
|
|
||||||
<DefineConstants>$(DefineConstants);SMAPI_FOR_WINDOWS</DefineConstants>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
<Private>False</Private>
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
<Private>False</Private>
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
<Private>False</Private>
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||||
<Private>False</Private>
|
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Stardew Valley">
|
|
||||||
<HintPath>$(GamePath)\Stardew Valley.exe</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
|
|
||||||
<HintPath>$(GamePath)\xTile.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
</When>
|
|
||||||
<Otherwise>
|
|
||||||
<PropertyGroup>
|
|
||||||
<DefineConstants>$(DefineConstants);SMAPI_FOR_UNIX</DefineConstants>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="MonoGame.Framework">
|
|
||||||
<HintPath>$(GamePath)\MonoGame.Framework.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
<SpecificVersion>False</SpecificVersion>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="StardewValley">
|
|
||||||
<HintPath>$(GamePath)\StardewValley.exe</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="xTile">
|
|
||||||
<HintPath>$(GamePath)\xTile.dll</HintPath>
|
|
||||||
<Private>False</Private>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
</Otherwise>
|
|
||||||
</Choose>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@ -197,9 +154,12 @@
|
||||||
<Compile Include="Events\PlayerEvents.cs" />
|
<Compile Include="Events\PlayerEvents.cs" />
|
||||||
<Compile Include="Events\TimeEvents.cs" />
|
<Compile Include="Events\TimeEvents.cs" />
|
||||||
<Compile Include="Extensions.cs" />
|
<Compile Include="Extensions.cs" />
|
||||||
|
<Compile Include="Framework\AssemblyRewriting\CachePaths.cs" />
|
||||||
|
<Compile Include="Framework\AssemblyRewriting\AssemblyTypeRewriter.cs" />
|
||||||
<Compile Include="Framework\DeprecationLevel.cs" />
|
<Compile Include="Framework\DeprecationLevel.cs" />
|
||||||
<Compile Include="Framework\DeprecationManager.cs" />
|
<Compile Include="Framework\DeprecationManager.cs" />
|
||||||
<Compile Include="Framework\InternalExtensions.cs" />
|
<Compile Include="Framework\InternalExtensions.cs" />
|
||||||
|
<Compile Include="Framework\ModAssemblyLoader.cs" />
|
||||||
<Compile Include="IModHelper.cs" />
|
<Compile Include="IModHelper.cs" />
|
||||||
<Compile Include="Framework\LogFileManager.cs" />
|
<Compile Include="Framework\LogFileManager.cs" />
|
||||||
<Compile Include="LogLevel.cs" />
|
<Compile Include="LogLevel.cs" />
|
||||||
|
@ -238,7 +198,6 @@
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="FodyWeavers.xml" />
|
|
||||||
<Content Include="icon.ico" />
|
<Content Include="icon.ico" />
|
||||||
<Content Include="steam_appid.txt">
|
<Content Include="steam_appid.txt">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
@ -256,19 +215,17 @@
|
||||||
<Install>false</Install>
|
<Install>false</Install>
|
||||||
</BootstrapperPackage>
|
</BootstrapperPackage>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\StardewModdingAPI.AssemblyRewriters\StardewModdingAPI.AssemblyRewriters.csproj">
|
||||||
|
<Project>{10db0676-9fc1-4771-a2c8-e2519f091e49}</Project>
|
||||||
|
<Name>StardewModdingAPI.AssemblyRewriters</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
</PostBuildEvent>
|
</PostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- merge Json.NET into the assembly (only in release mode to allow debugging, and only for Windows since Costura isn't crossplatform) -->
|
|
||||||
<Import Project="..\packages\Fody.1.29.4\build\dotnet\Fody.targets" Condition="$(Configuration) != 'Debug' AND $(OS) == 'WINDOWS_NT' AND Exists('..\packages\Fody.1.29.4\build\dotnet\Fody.targets')" />
|
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
|
||||||
<PropertyGroup>
|
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Error Condition="!Exists('..\packages\Fody.1.29.4\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.29.4\build\dotnet\Fody.targets'))" />
|
|
||||||
</Target>
|
|
||||||
<!-- if game path is invalid, show one user-friendly error instead of a slew of reference errors -->
|
<!-- if game path is invalid, show one user-friendly error instead of a slew of reference errors -->
|
||||||
<Target Name="BeforeBuild">
|
<Target Name="BeforeBuild">
|
||||||
<Error Condition="!Exists('$(GamePath)')" Text="Failed to find the game install path automatically; edit the *.csproj file and manually add a <GamePath> setting with the full directory path containing the Stardew Valley executable." />
|
<Error Condition="!Exists('$(GamePath)')" Text="Failed to find the game install path automatically; edit the *.csproj file and manually add a <GamePath> setting with the full directory path containing the Stardew Valley executable." />
|
||||||
|
@ -279,10 +236,14 @@
|
||||||
<StartProgram>$(GamePath)\StardewModdingAPI.exe</StartProgram>
|
<StartProgram>$(GamePath)\StardewModdingAPI.exe</StartProgram>
|
||||||
<StartWorkingDirectory>$(GamePath)</StartWorkingDirectory>
|
<StartWorkingDirectory>$(GamePath)</StartWorkingDirectory>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Target Name="AfterBuild" Condition="$(Configuration) == 'Debug' AND $(OS) == 'Windows_NT'">
|
<Target Name="AfterBuild" Condition="$(Configuration) == 'Debug'">
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFolder="$(GamePath)" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe" DestinationFolder="$(GamePath)" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)" />
|
<Copy SourceFiles="$(TargetDir)\StardewModdingAPI.AssemblyRewriters.dll" DestinationFolder="$(GamePath)" />
|
||||||
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" />
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).exe.mdb" DestinationFolder="$(GamePath)" Condition="$(OS) != 'Windows_NT'" />
|
||||||
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).pdb" DestinationFolder="$(GamePath)" Condition="$(OS) == 'Windows_NT'" />
|
||||||
|
<Copy SourceFiles="$(TargetDir)\$(TargetName).xml" DestinationFolder="$(GamePath)" Condition="$(OS) == 'Windows_NT'" />
|
||||||
<Copy SourceFiles="$(TargetDir)\Newtonsoft.Json.dll" DestinationFolder="$(GamePath)" />
|
<Copy SourceFiles="$(TargetDir)\Newtonsoft.Json.dll" DestinationFolder="$(GamePath)" />
|
||||||
|
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.dll" DestinationFolder="$(GamePath)" />
|
||||||
|
<Copy SourceFiles="$(TargetDir)\Mono.Cecil.Rocks.dll" DestinationFolder="$(GamePath)" />
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net461" developmentDependency="true" />
|
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
|
||||||
<package id="Fody" version="1.29.4" targetFramework="net461" developmentDependency="true" />
|
|
||||||
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net461" />
|
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Linux paths -->
|
||||||
|
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/GOG Games/Stardew Valley/game</GamePath>
|
||||||
|
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/.local/share/Steam/steamapps/common/Stardew Valley</GamePath>
|
||||||
|
<!-- Mac paths -->
|
||||||
|
<GamePath Condition="!Exists('$(GamePath)')">$(HOME)/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS</GamePath>
|
||||||
|
<!-- Windows paths -->
|
||||||
|
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\GalaxyClient\Games\Stardew Valley</GamePath>
|
||||||
|
<GamePath Condition="!Exists('$(GamePath)')">C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley</GamePath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Choose>
|
||||||
|
<When Condition="$(OS) == 'Windows_NT'">
|
||||||
|
<PropertyGroup>
|
||||||
|
<DefineConstants>$(DefineConstants);SMAPI_FOR_WINDOWS</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Stardew Valley">
|
||||||
|
<HintPath>$(GamePath)\Stardew Valley.exe</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
|
||||||
|
<HintPath>$(GamePath)\xTile.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</When>
|
||||||
|
<Otherwise>
|
||||||
|
<PropertyGroup>
|
||||||
|
<DefineConstants>$(DefineConstants);SMAPI_FOR_UNIX</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="MonoGame.Framework">
|
||||||
|
<HintPath>$(GamePath)\MonoGame.Framework.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="StardewValley">
|
||||||
|
<HintPath>$(GamePath)\StardewValley.exe</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="xTile">
|
||||||
|
<HintPath>$(GamePath)\xTile.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Otherwise>
|
||||||
|
</Choose>
|
||||||
|
</Project>
|
Loading…
Reference in New Issue