PlatformInfo.cs 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. using System.Runtime.InteropServices;
  2. namespace PCL.Communist.Core;
  3. public enum PlatformKind { Windows, MacOS, Linux, Android, IOS }
  4. public sealed record PlatformInfo(PlatformKind Kind, Architecture Architecture, string Version)
  5. {
  6. public static PlatformInfo Current => new(
  7. OperatingSystem.IsAndroid() ? PlatformKind.Android :
  8. OperatingSystem.IsIOS() ? PlatformKind.IOS :
  9. OperatingSystem.IsMacOS() ? PlatformKind.MacOS :
  10. OperatingSystem.IsWindows() ? PlatformKind.Windows : PlatformKind.Linux,
  11. RuntimeInformation.ProcessArchitecture, Environment.OSVersion.Version.ToString());
  12. public bool IsMobile => Kind is PlatformKind.Android or PlatformKind.IOS;
  13. public string DisplayName => Kind switch
  14. {
  15. PlatformKind.MacOS => $"macOS · {(Architecture == Architecture.Arm64 ? "Apple Silicon" : "Intel")}",
  16. PlatformKind.IOS => "iOS / iPadOS",
  17. _ => $"{Kind} · {Architecture}"
  18. };
  19. public string MinecraftOs => Kind switch
  20. {
  21. PlatformKind.Windows => "windows",
  22. PlatformKind.MacOS => "osx",
  23. PlatformKind.Linux => "linux",
  24. _ => throw new PlatformNotSupportedException("移动端需要专用 Java、图形和输入运行时,不能使用桌面原生库。")
  25. };
  26. public string MinecraftArch => Architecture switch
  27. {
  28. Architecture.X64 => "x86_64", Architecture.X86 => "x86",
  29. Architecture.Arm64 => "aarch64", Architecture.Arm => "arm",
  30. _ => throw new PlatformNotSupportedException($"不支持的架构:{Architecture}")
  31. };
  32. public string ClasspathSeparator => Kind == PlatformKind.Windows ? ";" : ":";
  33. public string JavaExecutable => Kind == PlatformKind.Windows ? "java.exe" : "java";
  34. }
  35. public sealed class LauncherPaths
  36. {
  37. public LauncherPaths(string root) => Root = Path.GetFullPath(root);
  38. public string Root { get; }
  39. public string Versions => Path.Combine(Root, "versions");
  40. public string Libraries => Path.Combine(Root, "libraries");
  41. public string Assets => Path.Combine(Root, "assets");
  42. public string Runtimes => Path.Combine(Root, "runtimes");
  43. public string VersionDirectory(string id) => SafeChild(Versions, id);
  44. public string VersionJson(string id) => Path.Combine(VersionDirectory(id), id + ".json");
  45. public static LauncherPaths Default => new(Path.Combine(
  46. Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "PCL Communist", "minecraft"));
  47. public static string SafeChild(string root, string relative)
  48. {
  49. if (string.IsNullOrWhiteSpace(relative) || relative.Contains('\\') || relative.Contains(':') ||
  50. relative.StartsWith('/') || relative.Split('/').Any(p => p is "." or ".." or ""))
  51. throw new InvalidDataException($"不安全的相对路径:{relative}");
  52. var path = Path.GetFullPath(Path.Combine(root, relative.Replace('/', Path.DirectorySeparatorChar)));
  53. var prefix = Path.TrimEndingDirectorySeparator(Path.GetFullPath(root)) + Path.DirectorySeparatorChar;
  54. if (!path.StartsWith(prefix, OperatingSystem.IsWindows() ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
  55. throw new InvalidDataException("文件路径超出游戏目录。");
  56. return path;
  57. }
  58. }