mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Merge branch 'refs/heads/Exter-N/cldapi'
Some checks are pending
.NET Build / build (push) Waiting to run
Some checks are pending
.NET Build / build (push) Waiting to run
This commit is contained in:
commit
18a6ce2a5f
5 changed files with 111 additions and 5 deletions
47
Penumbra/Interop/CloudApi.cs
Normal file
47
Penumbra/Interop/CloudApi.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
namespace Penumbra.Interop;
|
||||||
|
|
||||||
|
public static unsafe partial class CloudApi
|
||||||
|
{
|
||||||
|
private const int CfSyncRootInfoBasic = 0;
|
||||||
|
|
||||||
|
/// <summary> Determines whether a file or directory is cloud-synced using OneDrive or other providers that use the Cloud API. </summary>
|
||||||
|
/// <remarks> Can be expensive. Callers should cache the result when relevant. </remarks>
|
||||||
|
public static bool IsCloudSynced(string path)
|
||||||
|
{
|
||||||
|
var buffer = stackalloc long[1];
|
||||||
|
int hr;
|
||||||
|
uint length;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
hr = CfGetSyncRootInfoByPath(path, CfSyncRootInfoBasic, buffer, sizeof(long), out length);
|
||||||
|
}
|
||||||
|
catch (DllNotFoundException)
|
||||||
|
{
|
||||||
|
Penumbra.Log.Debug($"{nameof(CfGetSyncRootInfoByPath)} threw DllNotFoundException");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (EntryPointNotFoundException)
|
||||||
|
{
|
||||||
|
Penumbra.Log.Debug($"{nameof(CfGetSyncRootInfoByPath)} threw EntryPointNotFoundException");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Penumbra.Log.Debug($"{nameof(CfGetSyncRootInfoByPath)} returned HRESULT 0x{hr:X8}");
|
||||||
|
if (hr < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (length != sizeof(long))
|
||||||
|
{
|
||||||
|
Penumbra.Log.Debug($"Expected {nameof(CfGetSyncRootInfoByPath)} to return {sizeof(long)} bytes, got {length} bytes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Penumbra.Log.Debug($"{nameof(CfGetSyncRootInfoByPath)} returned {{ SyncRootFileId = 0x{*buffer:X16} }}");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LibraryImport("cldapi.dll", StringMarshalling = StringMarshalling.Utf16)]
|
||||||
|
private static partial int CfGetSyncRootInfoByPath(string filePath, int infoClass, void* infoBuffer, uint infoBufferLength,
|
||||||
|
out uint returnedLength);
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using Penumbra.Communication;
|
using Penumbra.Communication;
|
||||||
|
using Penumbra.Interop;
|
||||||
using Penumbra.Mods.Editor;
|
using Penumbra.Mods.Editor;
|
||||||
using Penumbra.Mods.Manager.OptionEditor;
|
using Penumbra.Mods.Manager.OptionEditor;
|
||||||
using Penumbra.Services;
|
using Penumbra.Services;
|
||||||
|
|
@ -303,6 +304,9 @@ public sealed class ModManager : ModStorage, IDisposable, IService
|
||||||
if (!firstTime && _config.ModDirectory != BasePath.FullName)
|
if (!firstTime && _config.ModDirectory != BasePath.FullName)
|
||||||
TriggerModDirectoryChange(BasePath.FullName, Valid);
|
TriggerModDirectoryChange(BasePath.FullName, Valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CloudApi.IsCloudSynced(BasePath.FullName))
|
||||||
|
Penumbra.Log.Warning($"Mod base directory {BasePath.FullName} is cloud-synced. This may cause issues.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerModDirectoryChange(string newPath, bool valid)
|
private void TriggerModDirectoryChange(string newPath, bool valid)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ using Dalamud.Plugin.Services;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
using Penumbra.GameData;
|
using Penumbra.GameData;
|
||||||
using Penumbra.GameData.Data;
|
using Penumbra.GameData.Data;
|
||||||
|
using Penumbra.Interop;
|
||||||
using Penumbra.Interop.Hooks;
|
using Penumbra.Interop.Hooks;
|
||||||
using Penumbra.Interop.Hooks.PostProcessing;
|
using Penumbra.Interop.Hooks.PostProcessing;
|
||||||
using Penumbra.Interop.Hooks.ResourceLoading;
|
using Penumbra.Interop.Hooks.ResourceLoading;
|
||||||
|
|
@ -213,6 +214,7 @@ public class Penumbra : IDalamudPlugin
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder(10240);
|
var sb = new StringBuilder(10240);
|
||||||
var exists = _config.ModDirectory.Length > 0 && Directory.Exists(_config.ModDirectory);
|
var exists = _config.ModDirectory.Length > 0 && Directory.Exists(_config.ModDirectory);
|
||||||
|
var cloudSynced = exists && CloudApi.IsCloudSynced(_config.ModDirectory);
|
||||||
var hdrEnabler = _services.GetService<RenderTargetHdrEnabler>();
|
var hdrEnabler = _services.GetService<RenderTargetHdrEnabler>();
|
||||||
var drive = exists ? new DriveInfo(new DirectoryInfo(_config.ModDirectory).Root.FullName) : null;
|
var drive = exists ? new DriveInfo(new DirectoryInfo(_config.ModDirectory).Root.FullName) : null;
|
||||||
sb.AppendLine("**Settings**");
|
sb.AppendLine("**Settings**");
|
||||||
|
|
@ -223,7 +225,8 @@ public class Penumbra : IDalamudPlugin
|
||||||
sb.Append($"> **`Operating System: `** {(Dalamud.Utility.Util.IsWine() ? "Mac/Linux (Wine)" : "Windows")}\n");
|
sb.Append($"> **`Operating System: `** {(Dalamud.Utility.Util.IsWine() ? "Mac/Linux (Wine)" : "Windows")}\n");
|
||||||
if (Dalamud.Utility.Util.IsWine())
|
if (Dalamud.Utility.Util.IsWine())
|
||||||
sb.Append($"> **`Locale Environment Variables:`** {CollectLocaleEnvironmentVariables()}\n");
|
sb.Append($"> **`Locale Environment Variables:`** {CollectLocaleEnvironmentVariables()}\n");
|
||||||
sb.Append($"> **`Root Directory: `** `{_config.ModDirectory}`, {(exists ? "Exists" : "Not Existing")}\n");
|
sb.Append(
|
||||||
|
$"> **`Root Directory: `** `{_config.ModDirectory}`, {(exists ? "Exists" : "Not Existing")}{(cloudSynced ? ", Cloud-Synced" : "")}\n");
|
||||||
sb.Append(
|
sb.Append(
|
||||||
$"> **`Free Drive Space: `** {(drive != null ? Functions.HumanReadableSize(drive.AvailableFreeSpace) : "Unknown")}\n");
|
$"> **`Free Drive Space: `** {(drive != null ? Functions.HumanReadableSize(drive.AvailableFreeSpace) : "Unknown")}\n");
|
||||||
sb.Append($"> **`Game Data Files: `** {(_gameData.HasModifiedGameDataFiles ? "Modified" : "Pristine")}\n");
|
sb.Append($"> **`Game Data Files: `** {(_gameData.HasModifiedGameDataFiles ? "Modified" : "Pristine")}\n");
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Interface.Colors;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
|
|
@ -41,6 +42,7 @@ using Penumbra.GameData.Data;
|
||||||
using Penumbra.Interop.Hooks.PostProcessing;
|
using Penumbra.Interop.Hooks.PostProcessing;
|
||||||
using Penumbra.Interop.Hooks.ResourceLoading;
|
using Penumbra.Interop.Hooks.ResourceLoading;
|
||||||
using Penumbra.GameData.Files.StainMapStructs;
|
using Penumbra.GameData.Files.StainMapStructs;
|
||||||
|
using Penumbra.Interop;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
using Penumbra.UI.AdvancedWindow.Materials;
|
using Penumbra.UI.AdvancedWindow.Materials;
|
||||||
|
|
||||||
|
|
@ -206,6 +208,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
_hookOverrides.Draw();
|
_hookOverrides.Draw();
|
||||||
DrawPlayerModelInfo();
|
DrawPlayerModelInfo();
|
||||||
_globalVariablesDrawer.Draw();
|
_globalVariablesDrawer.Draw();
|
||||||
|
DrawCloudApi();
|
||||||
DrawDebugTabIpc();
|
DrawDebugTabIpc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1199,6 +1202,42 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private string _cloudTesterPath = string.Empty;
|
||||||
|
private bool? _cloudTesterReturn;
|
||||||
|
private Exception? _cloudTesterError;
|
||||||
|
|
||||||
|
private void DrawCloudApi()
|
||||||
|
{
|
||||||
|
if (!ImUtf8.CollapsingHeader("Cloud API"u8))
|
||||||
|
return;
|
||||||
|
|
||||||
|
using var id = ImRaii.PushId("CloudApiTester"u8);
|
||||||
|
|
||||||
|
if (ImUtf8.InputText("Path"u8, ref _cloudTesterPath, flags: ImGuiInputTextFlags.EnterReturnsTrue))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_cloudTesterReturn = CloudApi.IsCloudSynced(_cloudTesterPath);
|
||||||
|
_cloudTesterError = null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_cloudTesterReturn = null;
|
||||||
|
_cloudTesterError = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cloudTesterReturn.HasValue)
|
||||||
|
ImUtf8.Text($"Is Cloud Synced? {_cloudTesterReturn}");
|
||||||
|
|
||||||
|
if (_cloudTesterError is not null)
|
||||||
|
{
|
||||||
|
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed);
|
||||||
|
ImUtf8.Text($"{_cloudTesterError}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary> Draw information about IPC options and availability. </summary>
|
/// <summary> Draw information about IPC options and availability. </summary>
|
||||||
private void DrawDebugTabIpc()
|
private void DrawDebugTabIpc()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ using OtterGui.Text;
|
||||||
using OtterGui.Widgets;
|
using OtterGui.Widgets;
|
||||||
using Penumbra.Api;
|
using Penumbra.Api;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.Interop;
|
||||||
using Penumbra.Interop.Hooks.PostProcessing;
|
using Penumbra.Interop.Hooks.PostProcessing;
|
||||||
using Penumbra.Interop.Services;
|
using Penumbra.Interop.Services;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
|
|
@ -59,6 +60,9 @@ public class SettingsTab : ITab, IUiService
|
||||||
|
|
||||||
private readonly TagButtons _sharedTags = new();
|
private readonly TagButtons _sharedTags = new();
|
||||||
|
|
||||||
|
private string _lastCloudSyncTestedPath = string.Empty;
|
||||||
|
private bool _lastCloudSyncTestResult = false;
|
||||||
|
|
||||||
public SettingsTab(IDalamudPluginInterface pluginInterface, Configuration config, FontReloader fontReloader, TutorialService tutorial,
|
public SettingsTab(IDalamudPluginInterface pluginInterface, Configuration config, FontReloader fontReloader, TutorialService tutorial,
|
||||||
Penumbra penumbra, FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector,
|
Penumbra penumbra, FileDialogService fileDialog, ModManager modManager, ModFileSystemSelector selector,
|
||||||
CharacterUtility characterUtility, ResidentResourceManager residentResources, ModExportManager modExportManager, HttpApi httpApi,
|
CharacterUtility characterUtility, ResidentResourceManager residentResources, ModExportManager modExportManager, HttpApi httpApi,
|
||||||
|
|
@ -208,6 +212,15 @@ public class SettingsTab : ITab, IUiService
|
||||||
if (IsSubPathOf(gameDir, newName))
|
if (IsSubPathOf(gameDir, newName))
|
||||||
return ("Path is not allowed to be inside your game folder.", false);
|
return ("Path is not allowed to be inside your game folder.", false);
|
||||||
|
|
||||||
|
if (_lastCloudSyncTestedPath != newName)
|
||||||
|
{
|
||||||
|
_lastCloudSyncTestResult = CloudApi.IsCloudSynced(newName);
|
||||||
|
_lastCloudSyncTestedPath = newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_lastCloudSyncTestResult)
|
||||||
|
return ("Path is not allowed to be cloud-synced.", false);
|
||||||
|
|
||||||
return selected
|
return selected
|
||||||
? ($"Press Enter or Click Here to Save (Current Directory: {old})", true)
|
? ($"Press Enter or Click Here to Save (Current Directory: {old})", true)
|
||||||
: ($"Click Here to Save (Current Directory: {old})", true);
|
: ($"Click Here to Save (Current Directory: {old})", true);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue