diff --git a/IPA.Injector/AntiPiracy.cs b/IPA.Injector/AntiPiracy.cs
index ed610f86..bfa1116e 100644
--- a/IPA.Injector/AntiPiracy.cs
+++ b/IPA.Injector/AntiPiracy.cs
@@ -1,5 +1,7 @@
using System;
using System.IO;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
using IPA.Utilities;
#if NET3
using Net3_Proxy;
@@ -16,6 +18,20 @@ namespace IPA.Injector
{
var dataPlugins = Path.Combine(GameVersionEarly.ResolveDataPath(path), "Plugins");
+ try
+ {
+ var userDir = GetPath(new Guid("374DE290-123F-4565-9164-39C4925E467B"),
+ KnownFolderFlags.AliasOnly | KnownFolderFlags.DontVerify);
+ var userDir2 = GetPath(new Guid("7d83ee9b-2244-4e70-b1f5-5393042af1e4"),
+ KnownFolderFlags.AliasOnly | KnownFolderFlags.DontVerify);
+
+ var curdir = Environment.CurrentDirectory;
+
+ if (curdir.IsSubPathOf(userDir) ||
+ curdir.IsSubPathOf(userDir2)) return false;
+ }
+ catch { }
+
return
File.Exists(Path.Combine(path, "IGG-GAMES.COM.url")) ||
File.Exists(Path.Combine(path, "SmartSteamEmu.ini")) ||
@@ -24,5 +40,60 @@ namespace IPA.Injector
File.Exists(Path.Combine(dataPlugins, "HUHUVR_steam_api64.dll")) ||
Directory.GetFiles(dataPlugins, "*.ini", SearchOption.TopDirectoryOnly).Length > 0;
}
+
+ private static string GetPath(Guid guid, KnownFolderFlags flags)
+ {
+ int result = SHGetKnownFolderPath(guid, (uint)flags, WindowsIdentity.GetCurrent().Token, out IntPtr outPath);
+ if (result >= 0)
+ {
+ string path = Marshal.PtrToStringUni(outPath);
+ Marshal.FreeCoTaskMem(outPath);
+ return path;
+ }
+ else
+ {
+ throw new ExternalException("Cannot get the known folder path. It may not be available on this system.",
+ result);
+ }
+ }
+
+ ///
+ /// Retrieves the full path of a known folder identified by the folder's known folder ID.
+ ///
+ /// A known folder ID that identifies the folder.
+ /// Flags that specify special retrieval options. This value can be 0; otherwise, one or
+ /// more of the values.
+ /// An access token that represents a particular user. If this parameter is NULL, which is
+ /// the most common usage, the function requests the known folder for the current user. Assigning a value of -1
+ /// indicates the Default User. The default user profile is duplicated when any new user account is created.
+ /// Note that access to the Default User folders requires administrator privileges.
+ /// When this method returns, contains the address of a string that specifies the path of
+ /// the known folder. The returned path does not include a trailing backslash.
+ /// Returns S_OK if successful, or an error value otherwise.
+ /// bb762188
+ [DllImport("Shell32.dll")]
+ private static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)]Guid rfid, uint dwFlags,
+ IntPtr hToken, out IntPtr ppszPath);
+
+ ///
+ /// Represents the retrieval options for known folders.
+ ///
+ /// dd378447
+ [Flags]
+ private enum KnownFolderFlags : uint
+ {
+ None = 0x00000000,
+ SimpleIDList = 0x00000100,
+ NotParentRelative = 0x00000200,
+ DefaultPath = 0x00000400,
+ Init = 0x00000800,
+ NoAlias = 0x00001000,
+ DontUnexpand = 0x00002000,
+ DontVerify = 0x00004000,
+ Create = 0x00008000,
+ NoAppcontainerRedirection = 0x00010000,
+ AliasOnly = 0x80000000
+ }
+
}
}
diff --git a/IPA.Loader/Utilities/Extensions.cs b/IPA.Loader/Utilities/Extensions.cs
index cfcfcb29..6161705f 100644
--- a/IPA.Loader/Utilities/Extensions.cs
+++ b/IPA.Loader/Utilities/Extensions.cs
@@ -1,4 +1,8 @@
using System;
+using System.IO;
+#if NET3
+using Path = Net3_Proxy.Path;
+#endif
namespace IPA.Utilities
{
@@ -23,5 +27,64 @@ namespace IPA.Utilities
/// the bool? to unwrap
/// the unwrapped value, or if it was
public static bool Unwrap(this bool? self) => self != null && self.Value;
+
+ ///
+ /// Returns true if starts with the path .
+ /// The comparison is case-insensitive, handles / and \ slashes as folder separators and
+ /// only matches if the base dir folder name is matched exactly ("c:\foobar\file.txt" is not a sub path of "c:\foo").
+ ///
+ public static bool IsSubPathOf(this string path, string baseDirPath)
+ {
+ string normalizedPath = Path.GetFullPath(path.Replace('/', '\\')
+ .WithEnding("\\"));
+
+ string normalizedBaseDirPath = Path.GetFullPath(baseDirPath.Replace('/', '\\')
+ .WithEnding("\\"));
+
+ return normalizedPath.StartsWith(normalizedBaseDirPath, StringComparison.OrdinalIgnoreCase);
+ }
+
+ ///
+ /// Returns with the minimal concatenation of (starting from end) that
+ /// results in satisfying .EndsWith(ending).
+ ///
+ /// "hel".WithEnding("llo") returns "hello", which is the result of "hel" + "lo".
+ public static string WithEnding(this string str, string ending)
+ {
+ if (str == null)
+ return ending;
+
+ string result = str;
+
+ // Right() is 1-indexed, so include these cases
+ // * Append no characters
+ // * Append up to N characters, where N is ending length
+ for (int i = 0; i <= ending.Length; i++)
+ {
+ string tmp = result + ending.Right(i);
+ if (tmp.EndsWith(ending))
+ return tmp;
+ }
+
+ return result;
+ }
+
+ /// Gets the rightmost characters from a string.
+ /// The string to retrieve the substring from.
+ /// The number of characters to retrieve.
+ /// The substring.
+ public static string Right(this string value, int length)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException("value");
+ }
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException("length", length, "Length is less than zero");
+ }
+
+ return (length < value.Length) ? value.Substring(value.Length - length) : value;
+ }
}
}