Compare commits
9 Commits
develop
...
archived/c
Author | SHA1 | Date |
---|---|---|
ClxS | 65f1e5bec4 | |
ClxS | 1ec0570ed9 | |
ClxS | e1bfd54894 | |
ClxS | 792cfcd796 | |
ClxS | 608a5cb257 | |
ClxS | 8201b96034 | |
ClxS | c8e09331d2 | |
ClxS | 27bc612a46 | |
ClxS | 318f5feada |
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.API
|
||||
{
|
||||
class Game
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -20,6 +20,11 @@ namespace StardewModdingAPI
|
|||
/// </summary>
|
||||
public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
/// <summary>
|
||||
/// Execution path to execute the code.
|
||||
/// </summary>
|
||||
public static string StardewExePath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\Stardew Valley.exe";
|
||||
|
||||
/// <summary>
|
||||
/// Title for the API console
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.Events
|
||||
{
|
||||
public class FarmAnimal
|
||||
{
|
||||
public void eatGrass_OnEnter()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,6 +46,8 @@ namespace StardewModdingAPI.Events
|
|||
{
|
||||
try
|
||||
{
|
||||
Program.IsGameReferenceDirty = true;
|
||||
var test = Program.gamePtr;
|
||||
UpdateTick.Invoke(null, EventArgs.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.ExtensionMethods
|
||||
{
|
||||
public static class ArrayExtensions
|
||||
{
|
||||
public static void ForEach(this Array array, Action<Array, int[]> action)
|
||||
{
|
||||
if (array.LongLength == 0) return;
|
||||
ArrayTraverse walker = new ArrayTraverse(array);
|
||||
do action(array, walker.Position);
|
||||
while (walker.Step());
|
||||
}
|
||||
|
||||
//public static void ForEach(Array array, Action<Array, int[]> action)
|
||||
//{
|
||||
// if (array.LongLength == 0) return;
|
||||
// ArrayTraverse walker = new ArrayTraverse(array);
|
||||
// do action(array, walker.Position);
|
||||
// while (walker.Step());
|
||||
//}
|
||||
}
|
||||
|
||||
internal class ArrayTraverse
|
||||
{
|
||||
public int[] Position;
|
||||
private int[] maxLengths;
|
||||
|
||||
public ArrayTraverse(Array array)
|
||||
{
|
||||
maxLengths = new int[array.Rank];
|
||||
for (int i = 0; i < array.Rank; ++i)
|
||||
{
|
||||
maxLengths[i] = array.GetLength(i) - 1;
|
||||
}
|
||||
Position = new int[array.Rank];
|
||||
}
|
||||
|
||||
public bool Step()
|
||||
{
|
||||
for (int i = 0; i < Position.Length; ++i)
|
||||
{
|
||||
if (Position[i] < maxLengths[i])
|
||||
{
|
||||
Position[i]++;
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
Position[j] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace StardewModdingAPI.ExtensionMethods
|
||||
{
|
||||
//From https://github.com/jpmikkers/net-object-deep-copy/blob/master/ObjectExtensions.cs
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
public static T Copy<T>(this object original)
|
||||
{
|
||||
return (T)new DeepCopyContext().InternalCopy(original, true);
|
||||
}
|
||||
|
||||
private class DeepCopyContext
|
||||
{
|
||||
private static readonly Func<object, object> CloneMethod;
|
||||
private readonly Dictionary<Object, Object> m_Visited;
|
||||
private readonly Dictionary<Type, FieldInfo[]> m_NonShallowFieldCache;
|
||||
|
||||
static DeepCopyContext()
|
||||
{
|
||||
MethodInfo cloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var p1 = Expression.Parameter(typeof(object));
|
||||
var body = Expression.Call(p1, cloneMethod);
|
||||
CloneMethod = Expression.Lambda<Func<object, object>>(body, p1).Compile();
|
||||
//Console.WriteLine("typeof(object) contains {0} nonshallow fields", NonShallowFields(typeof(object)).Count());
|
||||
}
|
||||
|
||||
public DeepCopyContext()
|
||||
{
|
||||
m_Visited = new Dictionary<object, object>(new ReferenceEqualityComparer());
|
||||
m_NonShallowFieldCache = new Dictionary<Type, FieldInfo[]>();
|
||||
}
|
||||
|
||||
private static bool IsPrimitive(Type type)
|
||||
{
|
||||
if (type.IsValueType && type.IsPrimitive) return true;
|
||||
if (type == typeof(String)) return true;
|
||||
if (type == typeof(Decimal)) return true;
|
||||
if (type == typeof(DateTime)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object InternalCopy(Object originalObject, bool includeInObjectGraph)
|
||||
{
|
||||
if (originalObject == null) return null;
|
||||
var typeToReflect = originalObject.GetType();
|
||||
if (IsPrimitive(typeToReflect)) return originalObject;
|
||||
|
||||
if (typeof(XElement).IsAssignableFrom(typeToReflect)) return new XElement(originalObject as XElement);
|
||||
if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
|
||||
|
||||
if (includeInObjectGraph)
|
||||
{
|
||||
object result;
|
||||
if (m_Visited.TryGetValue(originalObject, out result)) return result;
|
||||
}
|
||||
|
||||
var cloneObject = CloneMethod(originalObject);
|
||||
|
||||
if (includeInObjectGraph)
|
||||
{
|
||||
m_Visited.Add(originalObject, cloneObject);
|
||||
}
|
||||
|
||||
if (typeToReflect.IsArray)
|
||||
{
|
||||
var arrayElementType = typeToReflect.GetElementType();
|
||||
|
||||
if (IsPrimitive(arrayElementType))
|
||||
{
|
||||
// for an array of primitives, do nothing. The shallow clone is enough.
|
||||
}
|
||||
else if (arrayElementType.IsValueType)
|
||||
{
|
||||
// if its an array of structs, there's no need to check and add the individual elements to 'visited', because in .NET it's impossible to create
|
||||
// references to individual array elements.
|
||||
Array clonedArray = (Array)cloneObject;
|
||||
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), false), indices));
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's an array of ref types
|
||||
Array clonedArray = (Array)cloneObject;
|
||||
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), true), indices));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var fieldInfo in CachedNonShallowFields(typeToReflect))
|
||||
{
|
||||
var originalFieldValue = fieldInfo.GetValue(originalObject);
|
||||
// a valuetype field can never have a reference pointing to it, so don't check the object graph in that case
|
||||
|
||||
Log.Error("Replace this with a recurse-less version");
|
||||
var clonedFieldValue = InternalCopy(originalFieldValue, !fieldInfo.FieldType.IsValueType);
|
||||
fieldInfo.SetValue(cloneObject, clonedFieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
return cloneObject;
|
||||
}
|
||||
|
||||
private FieldInfo[] CachedNonShallowFields(Type typeToReflect)
|
||||
{
|
||||
FieldInfo[] result;
|
||||
|
||||
if (!m_NonShallowFieldCache.TryGetValue(typeToReflect, out result))
|
||||
{
|
||||
result = NonShallowFields(typeToReflect).ToArray();
|
||||
m_NonShallowFieldCache[typeToReflect] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From the given type hierarchy (i.e. including all base types), return all fields that should be deep-copied
|
||||
/// </summary>
|
||||
/// <param name="typeToReflect"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<FieldInfo> NonShallowFields(Type typeToReflect)
|
||||
{
|
||||
while (typeToReflect != typeof(object))
|
||||
{
|
||||
foreach (var fieldInfo in typeToReflect.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
if (IsPrimitive(fieldInfo.FieldType)) continue; // this is 5% faster than a where clause..
|
||||
yield return fieldInfo;
|
||||
}
|
||||
typeToReflect = typeToReflect.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ReferenceEqualityComparer : EqualityComparer<Object>
|
||||
{
|
||||
public override bool Equals(object x, object y)
|
||||
{
|
||||
return ReferenceEquals(x, y);
|
||||
}
|
||||
|
||||
public override int GetHashCode(object obj)
|
||||
{
|
||||
if (obj == null) return 0;
|
||||
// The RuntimeHelpers.GetHashCode method always calls the Object.GetHashCode method non-virtually,
|
||||
// even if the object's type has overridden the Object.GetHashCode method.
|
||||
return RuntimeHelpers.GetHashCode(obj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.Helpers
|
||||
{
|
||||
public enum CecilContextType
|
||||
{
|
||||
SMAPI,
|
||||
Stardew
|
||||
}
|
||||
public class CecilContext
|
||||
{
|
||||
public CecilContextType ContextType { get; private set;}
|
||||
|
||||
private AssemblyDefinition _assemblyDefinition { get; set; }
|
||||
private bool _isMemoryStreamDirty { get; set; }
|
||||
|
||||
private MemoryStream _modifiedAssembly;
|
||||
public MemoryStream ModifiedAssembly
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_modifiedAssembly == null)
|
||||
{
|
||||
_modifiedAssembly = new MemoryStream();
|
||||
_assemblyDefinition.Write(_modifiedAssembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_isMemoryStreamDirty)
|
||||
{
|
||||
_modifiedAssembly.Dispose();
|
||||
_modifiedAssembly = new MemoryStream();
|
||||
_assemblyDefinition.Write(_modifiedAssembly);
|
||||
}
|
||||
}
|
||||
return _modifiedAssembly;
|
||||
}
|
||||
}
|
||||
|
||||
public CecilContext(CecilContextType contextType)
|
||||
{
|
||||
ContextType = contextType;
|
||||
if (ContextType == CecilContextType.SMAPI)
|
||||
_assemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
|
||||
else
|
||||
_assemblyDefinition = AssemblyDefinition.ReadAssembly(Constants.StardewExePath);
|
||||
}
|
||||
|
||||
public ILProcessor GetMethodILProcessor(string type, string method)
|
||||
{
|
||||
if (_assemblyDefinition == null)
|
||||
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(method))
|
||||
throw new ArgumentNullException("Both type and method must be set");
|
||||
|
||||
Mono.Cecil.Cil.ILProcessor ilProcessor = null;
|
||||
TypeDefinition typeDef = GetTypeDefinition(type);
|
||||
if (typeDef != null)
|
||||
{
|
||||
MethodDefinition methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method);
|
||||
if (methodDef != null)
|
||||
{
|
||||
ilProcessor = methodDef.Body.GetILProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
return ilProcessor;
|
||||
}
|
||||
|
||||
public TypeDefinition GetTypeDefinition(string type)
|
||||
{
|
||||
if (_assemblyDefinition == null)
|
||||
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(type))
|
||||
throw new ArgumentNullException("Both type and method must be set");
|
||||
|
||||
TypeDefinition typeDef = _assemblyDefinition.MainModule.Types.FirstOrDefault(n => n.FullName == type);
|
||||
return typeDef;
|
||||
}
|
||||
|
||||
public MethodDefinition GetMethodDefinition(string type, string method)
|
||||
{
|
||||
MethodDefinition methodDef = null;
|
||||
TypeDefinition typeDef = GetTypeDefinition(type);
|
||||
|
||||
if (typeDef != null)
|
||||
{
|
||||
methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method);
|
||||
}
|
||||
|
||||
return methodDef;
|
||||
}
|
||||
|
||||
public ConstructorInfo GetSMAPITypeContructor(string type)
|
||||
{
|
||||
if (_assemblyDefinition == null)
|
||||
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
||||
|
||||
if (ContextType != CecilContextType.SMAPI)
|
||||
throw new Exception("GetSMAPIMethodReference can only be called on the SMAPI context");
|
||||
|
||||
ConstructorInfo methodInfo = null;
|
||||
|
||||
var reflectionType = Assembly.GetExecutingAssembly().GetType(type);
|
||||
if (reflectionType != null)
|
||||
{
|
||||
methodInfo = reflectionType.GetConstructor(Type.EmptyTypes);
|
||||
}
|
||||
|
||||
return methodInfo;
|
||||
}
|
||||
|
||||
public MethodInfo GetSMAPIMethodReference(string type, string method)
|
||||
{
|
||||
if (_assemblyDefinition == null)
|
||||
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
||||
|
||||
if (ContextType != CecilContextType.SMAPI)
|
||||
throw new Exception("GetSMAPIMethodReference can only be called on the SMAPI context");
|
||||
|
||||
MethodInfo methodInfo = null;
|
||||
|
||||
var reflectionType = Assembly.GetExecutingAssembly().GetType(type);
|
||||
if (reflectionType != null)
|
||||
{
|
||||
methodInfo = reflectionType.GetMethod(method);
|
||||
}
|
||||
|
||||
return methodInfo;
|
||||
}
|
||||
|
||||
public MethodReference ImportSMAPIMethodInStardew(CecilContext destinationContext, MethodBase method)
|
||||
{
|
||||
if (_assemblyDefinition == null)
|
||||
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
||||
|
||||
if (ContextType != CecilContextType.SMAPI)
|
||||
throw new Exception("ImportSmapiMethodInStardew can only be called on the Stardew context");
|
||||
|
||||
MethodReference reference = null;
|
||||
if (method != null)
|
||||
{
|
||||
reference = destinationContext._assemblyDefinition.MainModule.Import(method);
|
||||
}
|
||||
return reference;
|
||||
}
|
||||
|
||||
internal void WriteAssembly(string file)
|
||||
{
|
||||
_assemblyDefinition.Write(file);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.Helpers
|
||||
{
|
||||
public static class CecilHelper
|
||||
{
|
||||
//System.Void StardewValley.Game1::.ctor()
|
||||
|
||||
private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method)
|
||||
{
|
||||
Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, method);
|
||||
|
||||
if(method.HasThis)
|
||||
{
|
||||
Instruction loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0);
|
||||
ilProcessor.InsertBefore(target, loadObjInstruction);
|
||||
}
|
||||
|
||||
if (method.HasParameters)
|
||||
{
|
||||
Instruction loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0);
|
||||
ilProcessor.InsertBefore(target, loadObjInstruction);
|
||||
ilProcessor.InsertAfter(loadObjInstruction, callEnterInstruction);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilProcessor.InsertBefore(target, callEnterInstruction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void InjectMethod(ILProcessor ilProcessor, IEnumerable<Instruction> targets, MethodReference method)
|
||||
{
|
||||
foreach(var target in targets.ToList())
|
||||
{
|
||||
InjectMethod(ilProcessor, target, method);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Instruction> GetMatchingInstructions(Collection<Instruction> instructions, OpCode opcode, object @object)
|
||||
{
|
||||
return instructions.Where(n => n.OpCode == opcode && n.Operand == @object).ToList();
|
||||
}
|
||||
|
||||
public static void RedirectConstructor(CecilContext stardewContext, CecilContext smapiContext,
|
||||
string typeToAlter, string methodToAlter,
|
||||
string injecteeType, string injecteeMethod,
|
||||
string injectedType, string injectedMethod)
|
||||
{
|
||||
var ilProcessor = stardewContext.GetMethodILProcessor(typeToAlter, methodToAlter);
|
||||
var methodDefinition = stardewContext.GetMethodDefinition(injecteeType, injecteeMethod);
|
||||
|
||||
var methodInfo = smapiContext.GetSMAPITypeContructor(injectedType);
|
||||
var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo);
|
||||
|
||||
var instructionsToAlter = GetMatchingInstructions(ilProcessor.Body.Instructions, OpCodes.Newobj, methodDefinition);
|
||||
|
||||
var newInstruction = ilProcessor.Create(OpCodes.Newobj, reference);
|
||||
foreach(var instruction in instructionsToAlter)
|
||||
{
|
||||
ilProcessor.Replace(instruction, newInstruction);
|
||||
}
|
||||
}
|
||||
|
||||
// public void ReplaceInstruction(ILProcessor processor, OpCode opcode, string oldOperand, string newOperand)
|
||||
//{
|
||||
//var instructions = processor.Body.Instructions.Where(i => i.OpCode == opcode && i.Operand == oldOperand);
|
||||
// processor.Create()
|
||||
//}
|
||||
|
||||
public static void InjectEntryMethod(CecilContext stardewContext, CecilContext smapiContext, string injecteeType, string injecteeMethod,
|
||||
string injectedType, string injectedMethod)
|
||||
{
|
||||
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
|
||||
var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo);
|
||||
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
|
||||
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.First(), reference);
|
||||
}
|
||||
|
||||
public static void InjectExitMethod(CecilContext stardewContext, CecilContext smapiContext, string injecteeType, string injecteeMethod,
|
||||
string injectedType, string injectedMethod)
|
||||
{
|
||||
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
|
||||
var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo);
|
||||
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
|
||||
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret), reference);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.Helpers
|
||||
{
|
||||
public static class ReflectionHelper
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.Helpers
|
||||
{
|
||||
public static class StardewAssembly
|
||||
{
|
||||
private static Assembly ModifiedGameAssembly { get; set; }
|
||||
private static CecilContext StardewContext { get; set; }
|
||||
private static CecilContext SmapiContext { get; set; }
|
||||
|
||||
public static void ModifyStardewAssembly()
|
||||
{
|
||||
StardewContext = new CecilContext(CecilContextType.Stardew);
|
||||
SmapiContext = new CecilContext(CecilContextType.SMAPI);
|
||||
|
||||
CecilHelper.InjectEntryMethod(StardewContext, SmapiContext, "StardewValley.Game1", ".ctor", "StardewModdingAPI.Program", "Test");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Initialize", "StardewModdingAPI.Events.GameEvents", "InvokeInitialize");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "LoadContent", "StardewModdingAPI.Events.GameEvents", "InvokeLoadContent");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Update", "StardewModdingAPI.Events.GameEvents", "InvokeUpdateTick");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Draw", "StardewModdingAPI.Events.GraphicsEvents", "InvokeDrawTick");
|
||||
}
|
||||
|
||||
public static void LoadStardewAssembly()
|
||||
{
|
||||
ModifiedGameAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer());
|
||||
//ModifiedGameAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe");
|
||||
}
|
||||
|
||||
internal static void Launch()
|
||||
{
|
||||
ModifiedGameAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
|
||||
}
|
||||
|
||||
internal static void WriteModifiedExe()
|
||||
{
|
||||
StardewContext.WriteAssembly("StardewValley-Modified.exe");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -181,40 +181,7 @@ namespace StardewModdingAPI.Inheritance
|
|||
public SGame()
|
||||
{
|
||||
instance = this;
|
||||
|
||||
#if DEBUG
|
||||
SaveGame.serializer = new XmlSerializer(typeof (SaveGame), new Type[28]
|
||||
{
|
||||
typeof (Tool),
|
||||
typeof (GameLocation),
|
||||
typeof (Crow),
|
||||
typeof (Duggy),
|
||||
typeof (Bug),
|
||||
typeof (BigSlime),
|
||||
typeof (Fireball),
|
||||
typeof (Ghost),
|
||||
typeof (Child),
|
||||
typeof (Pet),
|
||||
typeof (Dog),
|
||||
typeof (StardewValley.Characters.Cat),
|
||||
typeof (Horse),
|
||||
typeof (GreenSlime),
|
||||
typeof (LavaCrab),
|
||||
typeof (RockCrab),
|
||||
typeof (ShadowGuy),
|
||||
typeof (SkeletonMage),
|
||||
typeof (SquidKid),
|
||||
typeof (Grub),
|
||||
typeof (Fly),
|
||||
typeof (DustSpirit),
|
||||
typeof (Quest),
|
||||
typeof (MetalHead),
|
||||
typeof (ShadowGirl),
|
||||
typeof (Monster),
|
||||
typeof (TerrainFeature),
|
||||
typeof (SObject)
|
||||
});
|
||||
#endif
|
||||
graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
|
|
|
@ -78,6 +78,16 @@ namespace StardewModdingAPI
|
|||
/// <param name="message"></param>
|
||||
/// <param name="values"></param>
|
||||
public static void Comment(object message, params object[] values)
|
||||
{
|
||||
Log.PrintLog(message?.ToString(), false, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional comment to display to console and logging.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="values"></param>
|
||||
public static void Warning(object message, params object[] values)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Log.PrintLog(message?.ToString(), false, values);
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using StardewModdingAPI.Events;
|
||||
using StardewModdingAPI.ExtensionMethods;
|
||||
using StardewModdingAPI.Helpers;
|
||||
using StardewModdingAPI.Inheritance;
|
||||
using StardewModdingAPI.Inheritance.Menus;
|
||||
using StardewValley;
|
||||
|
@ -23,19 +27,31 @@ namespace StardewModdingAPI
|
|||
|
||||
public static Texture2D DebugPixel { get; private set; }
|
||||
|
||||
public static SGame gamePtr;
|
||||
public static bool IsGameReferenceDirty { get; set; }
|
||||
|
||||
public static object gameInst;
|
||||
|
||||
public static Game1 _gamePtr;
|
||||
public static Game1 gamePtr
|
||||
{
|
||||
get
|
||||
{
|
||||
if(IsGameReferenceDirty && gameInst != null)
|
||||
{
|
||||
_gamePtr = gameInst.Copy<Game1>();
|
||||
}
|
||||
return _gamePtr;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ready;
|
||||
|
||||
public static Assembly StardewAssembly;
|
||||
public static Type StardewProgramType;
|
||||
public static FieldInfo StardewGameInfo;
|
||||
public static Form StardewForm;
|
||||
|
||||
public static Thread gameThread;
|
||||
public static Thread consoleInputThread;
|
||||
|
||||
public static bool StardewInjectorLoaded { get; private set; }
|
||||
public static Mod StardewInjectorMod { get; private set; }
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -49,9 +65,8 @@ namespace StardewModdingAPI
|
|||
{
|
||||
ConfigureUI();
|
||||
ConfigurePaths();
|
||||
ConfigureInjector();
|
||||
ConfigureMethodInjection();
|
||||
ConfigureSDV();
|
||||
|
||||
GameRunInvoker();
|
||||
}
|
||||
catch (Exception e)
|
||||
|
@ -64,6 +79,25 @@ namespace StardewModdingAPI
|
|||
Console.ReadKey();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures Mono.Cecil injections
|
||||
/// </summary>
|
||||
private static void ConfigureMethodInjection()
|
||||
{
|
||||
StardewAssembly.ModifyStardewAssembly();
|
||||
|
||||
#if DEBUG
|
||||
StardewAssembly.WriteModifiedExe();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void Test(object instance)
|
||||
{
|
||||
gameInst = instance;
|
||||
IsGameReferenceDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set up the console properties
|
||||
/// </summary>
|
||||
|
@ -86,6 +120,7 @@ namespace StardewModdingAPI
|
|||
_modPaths = new List<string>();
|
||||
_modContentPaths = new List<string>();
|
||||
|
||||
|
||||
//TODO: Have an app.config and put the paths inside it so users can define locations to load mods from
|
||||
_modPaths.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "Mods"));
|
||||
_modPaths.Add(Path.Combine(Constants.ExecutionPath, "Mods"));
|
||||
|
@ -101,50 +136,8 @@ namespace StardewModdingAPI
|
|||
|
||||
if (!File.Exists(Constants.ExecutionPath + "\\Stardew Valley.exe"))
|
||||
{
|
||||
throw new FileNotFoundException(string.Format("Could not found: {0}\\Stardew Valley.exe", Constants.ExecutionPath));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the injector.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will load the injector before anything else if it sees it
|
||||
/// It doesn't matter though
|
||||
/// I'll leave it as a feature in case anyone in the community wants to tinker with it
|
||||
/// All you need is a DLL that inherits from mod and is called StardewInjector.dll with an Entry() method
|
||||
/// </remarks>
|
||||
private static void ConfigureInjector()
|
||||
{
|
||||
foreach (string ModPath in _modPaths)
|
||||
{
|
||||
foreach (String s in Directory.GetFiles(ModPath, "StardewInjector.dll"))
|
||||
{
|
||||
StardewModdingAPI.Log.Success(ConsoleColor.Green, "Found Stardew Injector DLL: " + s);
|
||||
try
|
||||
{
|
||||
Assembly mod = Assembly.UnsafeLoadFrom(s); //to combat internet-downloaded DLLs
|
||||
|
||||
if (mod.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) > 0)
|
||||
{
|
||||
StardewModdingAPI.Log.Success("Loading Injector DLL...");
|
||||
TypeInfo tar = mod.DefinedTypes.First(x => x.BaseType == typeof(Mod));
|
||||
Mod m = (Mod)mod.CreateInstance(tar.ToString());
|
||||
Console.WriteLine("LOADED: {0} by {1} - Version {2} | Description: {3}", m.Name, m.Authour, m.Version, m.Description);
|
||||
m.Entry(false);
|
||||
StardewInjectorLoaded = true;
|
||||
StardewInjectorMod = m;
|
||||
}
|
||||
else
|
||||
{
|
||||
StardewModdingAPI.Log.Error("Invalid Mod DLL");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StardewModdingAPI.Log.Error("Failed to load mod '{0}'. Exception details:\n" + ex, s);
|
||||
}
|
||||
}
|
||||
StardewModdingAPI.Log.Error("Replace this");
|
||||
//throw new FileNotFoundException(string.Format("Could not found: {0}\\Stardew Valley.exe", Constants.ExecutionPath));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,45 +149,26 @@ namespace StardewModdingAPI
|
|||
StardewModdingAPI.Log.Info("Initializing SDV Assembly...");
|
||||
|
||||
// Load in the assembly - ignores security
|
||||
StardewAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe");
|
||||
StardewProgramType = StardewAssembly.GetType("StardewValley.Program", true);
|
||||
StardewGameInfo = StardewProgramType.GetField("gamePtr");
|
||||
StardewAssembly.LoadStardewAssembly();
|
||||
StardewModdingAPI.Log.Comment("SDV Loaded Into Memory");
|
||||
|
||||
// Change the game's version
|
||||
StardewModdingAPI.Log.Verbose("Injecting New SDV Version...");
|
||||
Game1.version += string.Format("-Z_MODDED | SMAPI {0}", Constants.VersionString);
|
||||
|
||||
// Create the thread for the game to run in.
|
||||
gameThread = new Thread(RunGame);
|
||||
StardewModdingAPI.Log.Info("Starting SDV...");
|
||||
gameThread.Start();
|
||||
Application.ThreadException += StardewModdingAPI.Log.Application_ThreadException;
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
AppDomain.CurrentDomain.UnhandledException += StardewModdingAPI.Log.CurrentDomain_UnhandledException;
|
||||
|
||||
// Wait for the game to load up
|
||||
while (!ready) ;
|
||||
|
||||
//SDV is running
|
||||
StardewModdingAPI.Log.Comment("SDV Loaded Into Memory");
|
||||
|
||||
//Create definition to listen for input
|
||||
StardewModdingAPI.Log.Verbose("Initializing Console Input Thread...");
|
||||
consoleInputThread = new Thread(ConsoleInputThread);
|
||||
|
||||
// The only command in the API (at least it should be, for now)
|
||||
Command.RegisterCommand("help", "Lists all commands | 'help <cmd>' returns command description").CommandFired += help_CommandFired;
|
||||
//Command.RegisterCommand("crash", "crashes sdv").CommandFired += delegate { Game1.player.draw(null); };
|
||||
|
||||
//Subscribe to events
|
||||
Events.ControlEvents.KeyPressed += Events_KeyPressed;
|
||||
Events.GameEvents.LoadContent += Events_LoadContent;
|
||||
//Events.MenuChanged += Events_MenuChanged; //Idk right now
|
||||
|
||||
StardewModdingAPI.Log.Verbose("Applying Final SDV Tweaks...");
|
||||
StardewInvoke(() =>
|
||||
{
|
||||
gamePtr.IsMouseVisible = false;
|
||||
gamePtr.Window.Title = "Stardew Valley - Version " + Game1.version;
|
||||
StardewForm.Resize += Events.GraphicsEvents.InvokeResize;
|
||||
});
|
||||
StardewAssembly.Launch();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -248,48 +222,6 @@ namespace StardewModdingAPI
|
|||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void RunGame()
|
||||
{
|
||||
Application.ThreadException += StardewModdingAPI.Log.Application_ThreadException;
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
AppDomain.CurrentDomain.UnhandledException += StardewModdingAPI.Log.CurrentDomain_UnhandledException;
|
||||
|
||||
try
|
||||
{
|
||||
gamePtr = new SGame();
|
||||
StardewModdingAPI.Log.Verbose("Patching SDV Graphics Profile...");
|
||||
Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
||||
LoadMods();
|
||||
|
||||
StardewForm = Control.FromHandle(Program.gamePtr.Window.Handle).FindForm();
|
||||
StardewForm.Closing += StardewForm_Closing;
|
||||
|
||||
ready = true;
|
||||
|
||||
StardewGameInfo.SetValue(StardewProgramType, gamePtr);
|
||||
gamePtr.Run();
|
||||
|
||||
#region deprecated
|
||||
if (false)
|
||||
{
|
||||
//Nope, I can't get it to work. I depend on Game1 being an SGame, and can't cast a parent to a child
|
||||
//I'm leaving this here in case the community is interested
|
||||
//StardewInjectorMod.Entry(true);
|
||||
Type gt = StardewAssembly.GetType("StardewValley.Game1", true);
|
||||
gamePtr = (SGame)Activator.CreateInstance(gt);
|
||||
|
||||
ready = true;
|
||||
|
||||
StardewGameInfo.SetValue(StardewProgramType, gamePtr);
|
||||
gamePtr.Run();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StardewModdingAPI.Log.Error("Game failed to start: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void StardewForm_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
|
@ -352,72 +284,6 @@ namespace StardewModdingAPI
|
|||
}
|
||||
}
|
||||
|
||||
static void Events_LoadContent(object o, EventArgs e)
|
||||
{
|
||||
StardewModdingAPI.Log.Info("Initializing Debug Assets...");
|
||||
DebugPixel = new Texture2D(Game1.graphics.GraphicsDevice, 1, 1);
|
||||
DebugPixel.SetData(new Color[] { Color.White });
|
||||
|
||||
#if DEBUG
|
||||
StardewModdingAPI.Log.Verbose("REGISTERING BASE CUSTOM ITEM");
|
||||
SObject so = new SObject();
|
||||
so.Name = "Mario Block";
|
||||
so.CategoryName = "SMAPI Test Mod";
|
||||
so.Description = "It's a block from Mario!\nLoaded in realtime by SMAPI.";
|
||||
so.Texture = Texture2D.FromStream(Game1.graphics.GraphicsDevice, new FileStream(_modContentPaths[0] + "\\Test.png", FileMode.Open));
|
||||
so.IsPassable = true;
|
||||
so.IsPlaceable = true;
|
||||
StardewModdingAPI.Log.Verbose("REGISTERED WITH ID OF: " + SGame.RegisterModItem(so));
|
||||
|
||||
//StardewModdingAPI.Log.Verbose("REGISTERING SECOND CUSTOM ITEM");
|
||||
//SObject so2 = new SObject();
|
||||
//so2.Name = "Mario Painting";
|
||||
//so2.CategoryName = "SMAPI Test Mod";
|
||||
//so2.Description = "It's a painting of a creature from Mario!\nLoaded in realtime by SMAPI.";
|
||||
//so2.Texture = Texture2D.FromStream(Game1.graphics.GraphicsDevice, new FileStream(_modContentPaths[0] + "\\PaintingTest.png", FileMode.Open));
|
||||
//so2.IsPassable = true;
|
||||
//so2.IsPlaceable = true;
|
||||
//StardewModdingAPI.Log.Verbose("REGISTERED WITH ID OF: " + SGame.RegisterModItem(so2));
|
||||
|
||||
Command.CallCommand("load");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Events_KeyPressed(object o, EventArgsKeyPressed e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void Events_MenuChanged(IClickableMenu newMenu)
|
||||
{
|
||||
StardewModdingAPI.Log.Verbose("NEW MENU: " + newMenu.GetType());
|
||||
if (newMenu is GameMenu)
|
||||
{
|
||||
Game1.activeClickableMenu = SGameMenu.ConstructFromBaseClass(Game1.activeClickableMenu as GameMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Events_LocationsChanged(List<GameLocation> newLocations)
|
||||
{
|
||||
#if DEBUG
|
||||
SGame.ModLocations = SGameLocation.ConstructFromBaseClasses(Game1.locations);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Events_CurrentLocationChanged(GameLocation newLocation)
|
||||
{
|
||||
//SGame.CurrentLocation = null;
|
||||
//System.Threading.Thread.Sleep(10);
|
||||
#if DEBUG
|
||||
Console.WriteLine(newLocation.name);
|
||||
SGame.CurrentLocation = SGame.LoadOrCreateSGameLocationFromName(newLocation.name);
|
||||
#endif
|
||||
//Game1.currentLocation = SGame.CurrentLocation;
|
||||
//Log.LogComment(((SGameLocation) newLocation).name);
|
||||
//Log.LogComment("LOC CHANGED: " + SGame.currentLocation.name);
|
||||
}
|
||||
|
||||
public static void StardewInvoke(Action a)
|
||||
{
|
||||
StardewForm.Invoke(a);
|
||||
|
|
|
@ -37,16 +37,16 @@
|
|||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(SteamInstallPath)' != ''">
|
||||
<PropertyGroup>
|
||||
<SteamPath>$(SteamInstallPath)</SteamPath>
|
||||
<When Condition="'$(SteamInstallPath)' != ''">
|
||||
<PropertyGroup>
|
||||
<SteamPath>$(SteamInstallPath)</SteamPath>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<SteamPath>..\..\..\..\Games\SteamLibrary</SteamPath>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<SteamPath>..\..\..\..\Games\SteamLibrary</SteamPath>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
|
@ -73,8 +73,7 @@
|
|||
<PlatformTarget>x86</PlatformTarget>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<DefineConstants>DEBUG</DefineConstants>
|
||||
<UseVSHostingProcess>true</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
|
@ -90,9 +89,25 @@
|
|||
<Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||
<Reference Include="Stardew Valley, Version=1.0.5905.5747, Culture=neutral, processorArchitecture=x86">
|
||||
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Mono.Cecil.0.9.6.1\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.1\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.1\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.1\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Stardew Valley, Version=1.0.5912.41135, Culture=neutral, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>$(SteamPath)\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
||||
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -103,12 +118,12 @@
|
|||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>$(SteamPath)\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
<Reference Include="xTile">
|
||||
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="API\Game.cs" />
|
||||
<Compile Include="Command.cs" />
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="Entities\SCharacter.cs" />
|
||||
|
@ -118,6 +133,7 @@
|
|||
<Compile Include="Entities\SPlayer.cs" />
|
||||
<Compile Include="Events\Controls.cs" />
|
||||
<Compile Include="Events\EventArgs.cs" />
|
||||
<Compile Include="Events\FarmAnimal.cs" />
|
||||
<Compile Include="Events\Game.cs" />
|
||||
<Compile Include="Events\Graphics.cs" />
|
||||
<Compile Include="Events\Location.cs" />
|
||||
|
@ -125,7 +141,13 @@
|
|||
<Compile Include="Events\Mine.cs" />
|
||||
<Compile Include="Events\Player.cs" />
|
||||
<Compile Include="Events\Time.cs" />
|
||||
<Compile Include="ExtensionMethods\Array.cs" />
|
||||
<Compile Include="ExtensionMethods\Object.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="Helpers\CecilContext.cs" />
|
||||
<Compile Include="Helpers\CecilHelper.cs" />
|
||||
<Compile Include="Helpers\ReflectionHelper.cs" />
|
||||
<Compile Include="Helpers\StardewAssembly.cs" />
|
||||
<Compile Include="Inheritance\ItemStackChange.cs" />
|
||||
<Compile Include="Inheritance\Menus\SBobberBar.cs" />
|
||||
<Compile Include="Inheritance\Menus\SGameMenu.cs" />
|
||||
|
@ -142,6 +164,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="icon.ico" />
|
||||
|
@ -159,6 +182,7 @@
|
|||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Mono.Cecil" version="0.9.6.1" targetFramework="net45" />
|
||||
</packages>
|
|
@ -34,16 +34,16 @@
|
|||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(SteamInstallPath)' != ''">
|
||||
<PropertyGroup>
|
||||
<SteamPath>$(SteamInstallPath)</SteamPath>
|
||||
<When Condition="'$(SteamInstallPath)' != ''">
|
||||
<PropertyGroup>
|
||||
<SteamPath>$(SteamInstallPath)</SteamPath>
|
||||
</PropertyGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<SteamPath>..\..\..\..\Games\SteamLibrary</SteamPath>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<PropertyGroup>
|
||||
<SteamPath>..\..\..\..\Games\SteamLibrary</SteamPath>
|
||||
</PropertyGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86">
|
||||
|
@ -53,8 +53,7 @@
|
|||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Stardew Valley">
|
||||
<HintPath>$(SteamPath)\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
||||
<Private>False</Private>
|
||||
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -65,8 +64,7 @@
|
|||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="xTile">
|
||||
<HintPath>$(SteamPath)\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
Loading…
Reference in New Issue