293 lines
8.6 KiB
C#
293 lines
8.6 KiB
C#
using System;
|
|
using System.Linq;
|
|
|
|
#if UNITY_EDITOR_WIN
|
|
using Microsoft.Win32;
|
|
using System.Text;
|
|
#elif UNITY_EDITOR_OSX
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Text.RegularExpressions;
|
|
#elif UNITY_EDITOR_LINUX
|
|
using System.IO;
|
|
using System.Xml;
|
|
using System.Xml.Linq;
|
|
#endif
|
|
|
|
|
|
#pragma warning disable IDE0005
|
|
using Serilog = Meryel.Serilog;
|
|
#pragma warning restore IDE0005
|
|
|
|
|
|
#nullable enable
|
|
|
|
|
|
//namespace BgTools.PlayerPrefsEditor
|
|
namespace Meryel.UnityCodeAssist.Editor.Preferences
|
|
{
|
|
public abstract class PreferanceStorageAccessor
|
|
{
|
|
protected string prefPath;
|
|
protected string[] cachedData = new string[0];
|
|
|
|
protected abstract void FetchKeysFromSystem();
|
|
|
|
protected PreferanceStorageAccessor(string pathToPrefs)
|
|
{
|
|
prefPath = pathToPrefs;
|
|
}
|
|
|
|
public string[] GetKeys(bool reloadData = true)
|
|
{
|
|
if (reloadData || cachedData.Length == 0)
|
|
{
|
|
FetchKeysFromSystem();
|
|
}
|
|
|
|
return cachedData;
|
|
}
|
|
|
|
public Action? PrefEntryChangedDelegate;
|
|
protected bool ignoreNextChange = false;
|
|
|
|
public void IgnoreNextChange()
|
|
{
|
|
ignoreNextChange = true;
|
|
}
|
|
|
|
protected virtual void OnPrefEntryChanged()
|
|
{
|
|
if (ignoreNextChange)
|
|
{
|
|
ignoreNextChange = false;
|
|
return;
|
|
}
|
|
|
|
PrefEntryChangedDelegate?.Invoke();
|
|
}
|
|
|
|
public Action? StartLoadingDelegate;
|
|
public Action? StopLoadingDelegate;
|
|
|
|
public abstract void StartMonitoring();
|
|
public abstract void StopMonitoring();
|
|
public abstract bool IsMonitoring();
|
|
}
|
|
|
|
#if UNITY_EDITOR_WIN
|
|
|
|
public class WindowsPrefStorage : PreferanceStorageAccessor
|
|
{
|
|
readonly RegistryMonitor monitor;
|
|
|
|
public WindowsPrefStorage(string pathToPrefs) : base(pathToPrefs)
|
|
{
|
|
monitor = new RegistryMonitor(RegistryHive.CurrentUser, prefPath);
|
|
monitor.RegChanged += new EventHandler(OnRegChanged);
|
|
}
|
|
|
|
private void OnRegChanged(object sender, EventArgs e)
|
|
{
|
|
OnPrefEntryChanged();
|
|
}
|
|
|
|
protected override void FetchKeysFromSystem()
|
|
{
|
|
cachedData = new string[0];
|
|
|
|
using (RegistryKey rootKey = Registry.CurrentUser.OpenSubKey(prefPath))
|
|
{
|
|
if (rootKey != null)
|
|
{
|
|
cachedData = rootKey.GetValueNames();
|
|
rootKey.Close();
|
|
}
|
|
}
|
|
|
|
// Clean <key>_h3320113488 nameing
|
|
//cachedData = cachedData.Select((key) => { return key.Substring(0, key.LastIndexOf("_h", StringComparison.Ordinal)); }).ToArray();
|
|
for (int i = 0; i < cachedData.Length; i++)
|
|
{
|
|
var indexOfSuffix = cachedData[i].LastIndexOf("_h", StringComparison.Ordinal);
|
|
if (indexOfSuffix >= 0)
|
|
cachedData[i] = cachedData[i].Substring(0, indexOfSuffix);
|
|
}
|
|
|
|
EncodeAnsiInPlace();
|
|
}
|
|
|
|
public override void StartMonitoring()
|
|
{
|
|
monitor.Start();
|
|
}
|
|
|
|
public override void StopMonitoring()
|
|
{
|
|
monitor.Stop();
|
|
}
|
|
|
|
public override bool IsMonitoring()
|
|
{
|
|
return monitor.IsMonitoring;
|
|
}
|
|
|
|
private void EncodeAnsiInPlace()
|
|
{
|
|
Encoding utf8 = Encoding.UTF8;
|
|
Encoding ansi = Encoding.GetEncoding(1252);
|
|
|
|
for (int i = 0; i < cachedData.Length; i++)
|
|
{
|
|
cachedData[i] = utf8.GetString(ansi.GetBytes(cachedData[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
#elif UNITY_EDITOR_LINUX
|
|
|
|
public class LinuxPrefStorage : PreferanceStorageAccessor
|
|
{
|
|
readonly FileSystemWatcher fileWatcher;
|
|
|
|
public LinuxPrefStorage(string pathToPrefs) : base(Path.Combine(Environment.GetEnvironmentVariable("HOME"), pathToPrefs))
|
|
{
|
|
fileWatcher = new FileSystemWatcher
|
|
{
|
|
Path = Path.GetDirectoryName(prefPath),
|
|
NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite,
|
|
Filter = "prefs"
|
|
};
|
|
|
|
fileWatcher.Changed += OnWatchedFileChanged;
|
|
}
|
|
|
|
protected override void FetchKeysFromSystem()
|
|
{
|
|
cachedData = new string[0];
|
|
|
|
if (File.Exists(prefPath))
|
|
{
|
|
XmlReaderSettings settings = new XmlReaderSettings();
|
|
XmlReader reader = XmlReader.Create(prefPath, settings);
|
|
|
|
XDocument doc = XDocument.Load(reader);
|
|
|
|
cachedData = doc.Element("unity_prefs").Elements().Select((e) => e.Attribute("name").Value).ToArray();
|
|
}
|
|
}
|
|
|
|
public override void StartMonitoring()
|
|
{
|
|
fileWatcher.EnableRaisingEvents = true;
|
|
}
|
|
|
|
public override void StopMonitoring()
|
|
{
|
|
fileWatcher.EnableRaisingEvents = false;
|
|
}
|
|
|
|
public override bool IsMonitoring()
|
|
{
|
|
return fileWatcher.EnableRaisingEvents;
|
|
}
|
|
|
|
private void OnWatchedFileChanged(object source, FileSystemEventArgs e)
|
|
{
|
|
OnPrefEntryChanged();
|
|
}
|
|
}
|
|
|
|
#elif UNITY_EDITOR_OSX
|
|
|
|
public class MacPrefStorage : PreferanceStorageAccessor
|
|
{
|
|
private readonly FileSystemWatcher fileWatcher;
|
|
private readonly DirectoryInfo prefsDirInfo;
|
|
private readonly String prefsFileNameWithoutExtension;
|
|
|
|
public MacPrefStorage(string pathToPrefs) : base(Path.Combine(Environment.GetEnvironmentVariable("HOME"), pathToPrefs))
|
|
{
|
|
prefsDirInfo = new DirectoryInfo(Path.GetDirectoryName(prefPath));
|
|
prefsFileNameWithoutExtension = Path.GetFileNameWithoutExtension(prefPath);
|
|
|
|
fileWatcher = new FileSystemWatcher
|
|
{
|
|
Path = Path.GetDirectoryName(prefPath),
|
|
NotifyFilter = NotifyFilters.LastWrite,
|
|
Filter = Path.GetFileName(prefPath)
|
|
};
|
|
|
|
// MAC delete the old and create a new file instead of updating
|
|
fileWatcher.Created += OnWatchedFileChanged;
|
|
}
|
|
|
|
protected override void FetchKeysFromSystem()
|
|
{
|
|
// Workaround to avoid incomplete tmp phase from MAC OS
|
|
foreach (FileInfo info in prefsDirInfo.GetFiles())
|
|
{
|
|
// Check if tmp PlayerPrefs file exist
|
|
if (info.FullName.Contains(prefsFileNameWithoutExtension) && !info.FullName.EndsWith(".plist"))
|
|
{
|
|
StartLoadingDelegate?.Invoke();
|
|
return;
|
|
}
|
|
}
|
|
StopLoadingDelegate?.Invoke();
|
|
|
|
cachedData = new string[0];
|
|
|
|
if (File.Exists(prefPath))
|
|
{
|
|
string fixedPrefsPath = prefPath.Replace("\"", "\\\"").Replace("'", "\\'").Replace("`", "\\`");
|
|
var cmdStr = string.Format(@"-p '{0}'", fixedPrefsPath);
|
|
|
|
string stdOut = String.Empty;
|
|
string errOut = String.Empty;
|
|
|
|
var process = new System.Diagnostics.Process();
|
|
process.StartInfo.UseShellExecute = false;
|
|
process.StartInfo.FileName = "plutil";
|
|
process.StartInfo.Arguments = cmdStr;
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
process.StartInfo.RedirectStandardError = true;
|
|
process.OutputDataReceived += new DataReceivedEventHandler((sender, evt) => { stdOut += evt.Data + "\n"; });
|
|
process.ErrorDataReceived += new DataReceivedEventHandler((sender, evt) => { errOut += evt.Data + "\n"; });
|
|
|
|
process.Start();
|
|
|
|
process.BeginOutputReadLine();
|
|
process.BeginErrorReadLine();
|
|
|
|
process.WaitForExit();
|
|
|
|
MatchCollection matches = Regex.Matches(stdOut, @"(?: "")(.*)(?:"" =>.*)");
|
|
cachedData = matches.Cast<Match>().Select((e) => e.Groups[1].Value).ToArray();
|
|
}
|
|
}
|
|
|
|
public override void StartMonitoring()
|
|
{
|
|
fileWatcher.EnableRaisingEvents = true;
|
|
}
|
|
|
|
public override void StopMonitoring()
|
|
{
|
|
fileWatcher.EnableRaisingEvents = false;
|
|
}
|
|
|
|
public override bool IsMonitoring()
|
|
{
|
|
return fileWatcher.EnableRaisingEvents;
|
|
}
|
|
|
|
private void OnWatchedFileChanged(object source, FileSystemEventArgs e)
|
|
{
|
|
OnPrefEntryChanged();
|
|
}
|
|
|
|
}
|
|
#endif
|
|
}
|