using System;
using System.IO;
using System.Linq;
using System.Reflection;
using StardewModdingAPI.Enums;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Framework.ModLoading;
using StardewModdingAPI.Internal;
using StardewValley;
namespace StardewModdingAPI
{
/// Contains SMAPI's constants and assumptions.
public static class Constants
{
/*********
** Accessors
*********/
/****
** Public
****/
/// SMAPI's current semantic version.
public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("2.11.0");
/// The minimum supported version of Stardew Valley.
public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.3.36");
/// The maximum supported version of Stardew Valley.
public static ISemanticVersion MaximumGameVersion { get; } = new GameVersion("1.3.36");
/// The target game platform.
public static GamePlatform TargetPlatform => (GamePlatform)Constants.Platform;
/// The path to the game folder.
public static string ExecutionPath { get; } = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
/// The directory path containing Stardew Valley's app data.
public static string DataPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley");
/// The directory path in which error logs should be stored.
public static string LogDir { get; } = Path.Combine(Constants.DataPath, "ErrorLogs");
/// The directory path where all saves are stored.
public static string SavesPath { get; } = Path.Combine(Constants.DataPath, "Saves");
/// The name of the current save folder (if save info is available, regardless of whether the save file exists yet).
public static string SaveFolderName
{
get
{
return Constants.GetSaveFolderName()
#if SMAPI_3_0_STRICT
;
#else
?? "";
#endif
}
}
/// The absolute path to the current save folder (if save info is available and the save file exists).
public static string CurrentSavePath
{
get
{
return Constants.GetSaveFolderPathIfExists()
#if SMAPI_3_0_STRICT
;
#else
?? "";
#endif
}
}
/****
** Internal
****/
/// The URL of the SMAPI home page.
internal const string HomePageUrl = "https://smapi.io";
/// The absolute path to the folder containing SMAPI's internal files.
internal static readonly string InternalFilesPath = Program.DllSearchPath;
/// The file path for the SMAPI configuration file.
internal static string ApiConfigPath => Path.Combine(Constants.InternalFilesPath, "StardewModdingAPI.config.json");
/// The file path for the SMAPI metadata file.
internal static string ApiMetadataPath => Path.Combine(Constants.InternalFilesPath, "StardewModdingAPI.metadata.json");
/// The filename prefix used for all SMAPI logs.
internal static string LogNamePrefix { get; } = "SMAPI-";
/// The filename for SMAPI's main log, excluding the .
internal static string LogFilename { get; } = $"{Constants.LogNamePrefix}latest";
/// The filename extension for SMAPI log files.
internal static string LogExtension { get; } = "txt";
/// The file path for the log containing the previous fatal crash, if any.
internal static string FatalCrashLog => Path.Combine(Constants.LogDir, "SMAPI-crash.txt");
/// The file path which stores a fatal crash message for the next run.
internal static string FatalCrashMarker => Path.Combine(Constants.InternalFilesPath, "StardewModdingAPI.crash.marker");
/// The file path which stores the detected update version for the next run.
internal static string UpdateMarker => Path.Combine(Constants.InternalFilesPath, "StardewModdingAPI.update.marker");
/// The default full path to search for mods.
internal static string DefaultModsPath { get; } = Path.Combine(Constants.ExecutionPath, "Mods");
/// The actual full path to search for mods.
internal static string ModsPath { get; set; }
/// The game's current semantic version.
internal static ISemanticVersion GameVersion { get; } = new GameVersion(Constants.GetGameVersion());
/// The target game platform.
internal static Platform Platform { get; } = EnvironmentUtility.DetectPlatform();
/// The game's assembly name.
internal static string GameAssemblyName => Constants.Platform == Platform.Windows ? "Stardew Valley" : "StardewValley";
/*********
** Internal methods
*********/
/// Get the SMAPI version to recommend for an older game version, if any.
/// The game version to search.
/// Returns the compatible SMAPI version, or null if none was found.
internal static ISemanticVersion GetCompatibleApiVersion(ISemanticVersion version)
{
switch (version.ToString())
{
case "1.3.28":
return new SemanticVersion(2, 7, 0);
case "1.2.30":
case "1.2.31":
case "1.2.32":
case "1.2.33":
return new SemanticVersion(2, 5, 5);
}
return null;
}
/// 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.Linux:
case Platform.Mac:
removeAssemblyReferences = new[]
{
"Netcode",
"Stardew Valley",
"Microsoft.Xna.Framework",
"Microsoft.Xna.Framework.Game",
"Microsoft.Xna.Framework.Graphics",
"Microsoft.Xna.Framework.Xact"
};
targetAssemblies = new[]
{
typeof(StardewValley.Game1).Assembly, // note: includes Netcode types on Linux/Mac
typeof(Microsoft.Xna.Framework.Vector2).Assembly
};
break;
case Platform.Windows:
removeAssemblyReferences = new[]
{
"StardewValley",
"MonoGame.Framework"
};
targetAssemblies = new[]
{
typeof(Netcode.NetBool).Assembly,
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);
}
/*********
** Private methods
*********/
/// Get the game's current version string.
private static string GetGameVersion()
{
// we need reflection because it's a constant, so SMAPI's references to it are inlined at compile-time
FieldInfo field = typeof(Game1).GetField(nameof(Game1.version), BindingFlags.Public | BindingFlags.Static);
if (field == null)
throw new InvalidOperationException($"The {nameof(Game1)}.{nameof(Game1.version)} field could not be found.");
return (string)field.GetValue(null);
}
/// Get the name of the save folder, if any.
internal static string GetSaveFolderName()
{
// save not available
if (Context.LoadStage == LoadStage.None)
return null;
// get basic info
string playerName;
ulong saveID;
if (Context.LoadStage == LoadStage.SaveParsed)
{
playerName = SaveGame.loaded.player.Name;
saveID = SaveGame.loaded.uniqueIDForThisGame;
}
else
{
playerName = Game1.player.Name;
saveID = Game1.uniqueIDForThisGame;
}
// build folder name
return $"{new string(playerName.Where(char.IsLetterOrDigit).ToArray())}_{saveID}";
}
/// Get the path to the current save folder, if any.
internal static string GetSaveFolderPathIfExists()
{
string folderName = Constants.GetSaveFolderName();
if (folderName == null)
return null;
string path = Path.Combine(Constants.SavesPath, folderName);
return Directory.Exists(path)
? path
: null;
}
}
}