using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.AssemblyRewriters.Rewriters.Crossplatform; using StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2; using StardewValley; namespace StardewModdingAPI { /// Contains SMAPI's constants and assumptions. public static class Constants { /********* ** Properties *********/ /// The directory name containing the current save's data (if a save is loaded). private static string RawSaveFolderName => Constants.PlayerNull ? string.Empty : Constants.GetSaveFolderName(); /// The directory path containing the current save's data (if a save is loaded). private static string RawSavePath => Constants.PlayerNull ? string.Empty : Path.Combine(Constants.SavesPath, Constants.RawSaveFolderName); /********* ** Accessors *********/ /**** ** Public ****/ /// SMAPI's current semantic version. public static ISemanticVersion ApiVersion => new SemanticVersion(1, 8, 0, null); /// The minimum supported version of Stardew Valley. public const string MinimumGameVersion = "1.2"; /// The directory path containing Stardew Valley's app data. public static string DataPath => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley"); /// The directory path where all saves are stored. public static string SavesPath => Path.Combine(Constants.DataPath, "Saves"); /// The directory name containing the current save's data (if a save is loaded and the directory exists). public static string SaveFolderName => Constants.CurrentSavePathExists ? Constants.RawSaveFolderName : ""; /// The directory path containing the current save's data (if a save is loaded and the directory exists). public static string CurrentSavePath => Constants.CurrentSavePathExists ? Constants.RawSavePath : ""; /// Whether a player save has been loaded. public static bool PlayerNull => !Game1.hasLoadedGame || Game1.player == null || string.IsNullOrEmpty(Game1.player.name); /// The path to the current assembly being executing. public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); /// The directory path in which error logs should be stored. public static string LogDir => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "ErrorLogs"); /**** ** Internal ****/ /// The GitHub repository to check for updates. internal const string GitHubRepository = "Pathoschild/SMAPI"; /// The title of the SMAPI console window. internal static string ConsoleTitle => $"Stardew Modding API Console - Version {Constants.ApiVersion} - Mods Loaded: {Program.ModsLoaded}"; /// The file path for the SMAPI configuration file. internal static string ApiConfigPath => Path.Combine(Constants.ExecutionPath, $"{typeof(Program).Assembly.GetName().Name}.config.json"); /// The file path for the SMAPI data file containing metadata about known mods. internal static string ApiModMetadataPath => Path.Combine(Constants.ExecutionPath, $"{typeof(Program).Assembly.GetName().Name}.data.json"); /// The file path to the log where the latest output should be saved. internal static string LogPath => Path.Combine(Constants.LogDir, "SMAPI-latest.txt"); /// Whether the directory containing the current save's data exists on disk. internal static bool CurrentSavePathExists => Directory.Exists(Constants.RawSavePath); /********* ** Protected methods *********/ /// Get metadata for mapping assemblies to the current platform. /// The target game platform. 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); } /// Get rewriters which fix incompatible CIL instructions in mod assemblies. internal static IEnumerable GetRewriters() { return new IInstructionRewriter[] { // crossplatform new SpriteBatch_MethodRewriter(), // Stardew Valley 1.2 new Game1_ActiveClickableMenu_FieldRewriter(), new Game1_GameMode_FieldRewriter(), new Game1_Player_FieldRewriter() }; } /// Get the name of a save directory for the current player. private static string GetSaveFolderName() { string prefix = new string(Game1.player.name.Where(char.IsLetterOrDigit).ToArray()); return $"{prefix}_{Game1.uniqueIDForThisGame}"; } } }