diff --git a/DisPlacePlugin.json b/DisPlacePlugin.json index e662142..672b8f8 100644 --- a/DisPlacePlugin.json +++ b/DisPlacePlugin.json @@ -2,7 +2,7 @@ "Author": "Drakansoul", "Name": "DisPlace Plugin", "InternalName": "DisPlacePlugin", - "AssemblyVersion": "3.5.1", + "AssemblyVersion": "3.6.0", "Punchline": "Automatically save & load furniture layouts in ANY house.", "Description": "Automatically save & load the positions of house furniture. Copy other housing layouts. Import/export layouts from the MakePlace program.", "ApplicableVersion": "any", @@ -19,9 +19,9 @@ "LoadPriority": 0, "IconUrl": "https://raw.githubusercontent.com/Drakansoul/DisPlaced/master/icon.png", "DownloadCount": 0, - "DownloadLinkInstall": "https://github.com/Drakansoul/DisPlaced/releases/download/v3.5.1-d/DisPlacePlugin.zip", - "DownloadLinkTesting": "https://github.com/Drakansoul/DisPlaced/releases/download/v3.5.1-d/DisPlacePlugin.zip", - "DownloadLinkUpdate": "https://github.com/Drakansoul/DisPlaced/releases/download/v3.5.1-d/DisPlacePlugin.zip", + "DownloadLinkInstall": "https://github.com/Drakansoul/DisPlaced/releases/download/v3.6.0/DisPlacePlugin.zip", + "DownloadLinkTesting": "https://github.com/Drakansoul/DisPlaced/releases/download/v3.6.0/DisPlacePlugin.zip", + "DownloadLinkUpdate": "https://github.com/Drakansoul/DisPlaced/releases/download/v3.6.0/DisPlacePlugin.zip", "_isDip17Plugin": false, "_Dip17Channel": null }] diff --git a/DisPlacePlugin/DisPlacePlugin.cs b/DisPlacePlugin/DisPlacePlugin.cs index efd40dc..113d1bf 100644 --- a/DisPlacePlugin/DisPlacePlugin.cs +++ b/DisPlacePlugin/DisPlacePlugin.cs @@ -30,6 +30,9 @@ public sealed class DisPlacePlugin : IDalamudPlugin public delegate void SelectItemDelegate(IntPtr housingStruct, IntPtr item); private static HookWrapper SelectItemHook; + public delegate void PlaceItemDelegate(IntPtr housingStruct, IntPtr item); + private static HookWrapper PlaceItemHook; + public static bool CurrentlyPlacingItems = false; public static bool ApplyChange = false; @@ -49,6 +52,13 @@ public void Dispose() { HookManager.Dispose(); + try { + Memory.Instance.SetPlaceAnywhere(false); + } + catch (Exception ex) + { + DalamudApi.PluginLog.Error(ex, "Error while calling PluginMemory.Dispose()"); + } DalamudApi.ClientState.TerritoryChanged -= TerritoryChanged; DalamudApi.CommandManager.RemoveHandler("/displace"); @@ -75,9 +85,10 @@ public DisPlacePlugin(IDalamudPluginInterface pi) HousingData.Init(this); Memory.Init(); + Memory.Instance.SetPlaceAnywhere(true); LayoutManager = new SaveLayoutManager(this, Config); - DalamudApi.PluginLog.Info("DisPlace Plugin v3.5.1-d initialized"); + DalamudApi.PluginLog.Info("DisPlace Plugin v3.6.0 initialized"); } public void Initialize() { @@ -85,7 +96,9 @@ public void Initialize() IsSaveLayoutHook = HookManager.Hook("40 53 48 83 ec 20 48 8b d9 48 8b 0d ?? ?? ?? ?? e8 ?? ?? ?? ?? 33 d2 48 8b c8 e8 ?? ?? ?? ?? 84 c0 75 ?? 38 83 ?? 01 00 00", IsSaveLayoutDetour); SelectItemHook = HookManager.Hook("48 85 D2 0F 84 49 09 00 00 53 41 56 48 83 EC 48 48 89 6C 24 60 48 8B DA 48 89 74 24 70 4C 8B F1", SelectItemDetour); - + + PlaceItemHook = HookManager.Hook("48 89 5C 24 10 48 89 74 24 18 57 48 83 EC 20 4c 8B 41 18 33 FF 0F B6 F2", PlaceItemDetour); + UpdateYardObjHook = HookManager.Hook("48 89 74 24 18 57 48 83 ec 20 b8 dc 02 00 00 0f b7 f2 ??", UpdateYardObj); GetGameObjectHook = HookManager.Hook("48 89 5c 24 08 48 89 74 24 10 57 48 83 ec 20 0f b7 f2 33 db 0f 1f 40 00 0f 1f 84 00 00 00 00 00", GetGameObject); @@ -132,6 +145,7 @@ private void UpdateYardObj(IntPtr objectList, ushort index) unsafe static public void SelectItemDetour(IntPtr housing, IntPtr item) { + DalamudApi.PluginLog.Debug(string.Format("selecting item {0}",item.ToString())); SelectItemHook.Original(housing, item); } @@ -140,6 +154,24 @@ unsafe static public void SelectItem(IntPtr item) { SelectItemDetour((IntPtr)Memory.Instance.HousingStructure, item); } + + unsafe static public void PlaceItemDetour(IntPtr housing, IntPtr item) + { + /* + The call made by the XIV client has some strange behaviour. + It can either place the item pointer passed to it or it retrieves the activeItem from the housing object. + I tried passing the active item but I believe that doing so led to more crashes. + As such I've just defaulted to the easier path of just passing in a zero pointer so that the call populates itself form the housing object. + */ + DalamudApi.PluginLog.Debug(string.Format("placing item {0}",(housing+24).ToString())); + PlaceItemHook.Original(housing, item); + } + + + unsafe static public void PlaceItem(IntPtr item) + { + PlaceItemDetour((IntPtr)Memory.Instance.HousingStructure, item); + } public unsafe void PlaceItems() @@ -234,6 +266,8 @@ unsafe public static void SetItemPosition(HousingItem rowItem) MemInstance.WritePosition(position); MemInstance.WriteRotation(rotation); + PlaceItem(nint.Zero); + rowItem.CorrectLocation = true; rowItem.CorrectRotation = true; diff --git a/DisPlacePlugin/DisPlacePlugin.csproj b/DisPlacePlugin/DisPlacePlugin.csproj index 0cf3a93..66e1a4f 100644 --- a/DisPlacePlugin/DisPlacePlugin.csproj +++ b/DisPlacePlugin/DisPlacePlugin.csproj @@ -2,7 +2,7 @@ $(AppData)\XIVLauncher\addon\Hooks\dev\ - 3.5.1 + 3.6.0 diff --git a/DisPlacePlugin/Memory.cs b/DisPlacePlugin/Memory.cs index 570919d..aa495e1 100644 --- a/DisPlacePlugin/Memory.cs +++ b/DisPlacePlugin/Memory.cs @@ -15,10 +15,19 @@ public unsafe class Memory public static GetInventoryContainerDelegate GetInventoryContainer; public delegate InventoryContainer* GetInventoryContainerDelegate(IntPtr inventoryManager, InventoryType inventoryType); + // Pointers to modify assembly to enable place anywhere. + public IntPtr placeAnywhere; + public IntPtr wallAnywhere; + public IntPtr wallmountAnywhere; private Memory() { try { + // Assembly address for asm rewrites. + placeAnywhere = DalamudApi.SigScanner.ScanText("C6 ?? ?? ?? 00 00 00 8B FE 48 89") + 6; + wallAnywhere = DalamudApi.SigScanner.ScanText("48 85 C0 74 ?? C6 87 ?? ?? 00 00 00") + 11; + wallmountAnywhere = DalamudApi.SigScanner.ScanText("c6 87 83 01 00 00 00 48 83 c4 ??") + 6; + housingModulePtr = DalamudApi.SigScanner.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 8B 52"); LayoutWorldPtr = DalamudApi.SigScanner.GetStaticAddressFromSig("48 8B D1 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 0A", 3); @@ -38,7 +47,7 @@ private Memory() public IntPtr housingModulePtr { get; } public IntPtr LayoutWorldPtr { get; } - public unsafe HousingModule* HousingModule => housingModulePtr != IntPtr.Zero ? (HousingModule*) Marshal.ReadIntPtr(housingModulePtr) : null; + public unsafe HousingModule* HousingModule => housingModulePtr != IntPtr.Zero ? (HousingModule*)Marshal.ReadIntPtr(housingModulePtr) : null; public unsafe LayoutWorld* LayoutWorld => LayoutWorldPtr != IntPtr.Zero ? (LayoutWorld*)Marshal.ReadIntPtr(LayoutWorldPtr) : null; public unsafe HousingObjectManager* CurrentManager => HousingModule->currentTerritory; @@ -289,7 +298,6 @@ public unsafe HousingArea GetCurrentTerritory() var territoryRow = DalamudApi.DataManager.GetExcelSheet().GetRow(GetTerritoryTypeId()); if (territoryRow == null) { - LogError("Mem Cannot identify territory"); return HousingArea.None; } @@ -308,7 +316,7 @@ public unsafe bool IsHousingMode() { if (HousingStructure == null) return false; - + return HousingStructure->Mode != HousingLayoutMode.None; } @@ -338,7 +346,8 @@ public unsafe void WritePosition(Vector3 newPosition) try { var item = HousingStructure->ActiveItem; - if (item == null) { + if (item == null) + { return; } @@ -371,5 +380,69 @@ public unsafe void WriteRotation(Vector3 newRotation) DalamudApi.PluginLog.Error(ex, "Error occured while writing rotation!"); } } + private static void WriteProtectedBytes(IntPtr addr, byte[] b) + { + if (addr == IntPtr.Zero) return; + VirtualProtect(addr, 1, Protection.PAGE_EXECUTE_READWRITE, out var oldProtection); + Marshal.Copy(b, 0, addr, b.Length); + VirtualProtect(addr, 1, oldProtection, out _); + } + + private static void WriteProtectedBytes(IntPtr addr, byte b) + { + if (addr == IntPtr.Zero) return; + WriteProtectedBytes(addr, [b]); + } + + /// + /// Sets the flag for place anywhere in memory. + /// + /// Boolean state for if you can place anywhere. + public void SetPlaceAnywhere(bool state) + { + + if (placeAnywhere == IntPtr.Zero || wallAnywhere == IntPtr.Zero || wallmountAnywhere == IntPtr.Zero){ + DalamudApi.PluginLog.Debug(string.Format("Cannot Set PlaceAnywhere",state.ToString())); + return; + } + DalamudApi.PluginLog.Debug(string.Format("Setting PlaceAnywhere to {0}",state.ToString())); + + + // The byte state from boolean. + var bstate = (byte)(state ? 1 : 0); + + // Write the bytes for place anywhere. + WriteProtectedBytes(placeAnywhere, bstate); + WriteProtectedBytes(wallAnywhere, bstate); + WriteProtectedBytes(wallmountAnywhere, bstate); + + // Which bytes to write. + // byte[] showcaseBytes = state ? [0x90, 0x90, 0x90, 0x90, 0x90, 0x90] : [0x88, 0x87, 0x98, 0x02, 0x00, 0x00]; + + // // Write bytes for showcase anywhere (nop or original bytes). + // WriteProtectedBytes(showcaseAnywhereRotate, showcaseBytes); + // WriteProtectedBytes(showcaseAnywherePlace, showcaseBytes); + } + #region Kernel32 + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, Protection flNewProtect, out Protection lpflOldProtect); + + public enum Protection + { + PAGE_NOACCESS = 0x01, + PAGE_READONLY = 0x02, + PAGE_READWRITE = 0x04, + PAGE_WRITECOPY = 0x08, + PAGE_EXECUTE = 0x10, + PAGE_EXECUTE_READ = 0x20, + PAGE_EXECUTE_READWRITE = 0x40, + PAGE_EXECUTE_WRITECOPY = 0x80, + PAGE_GUARD = 0x100, + PAGE_NOCACHE = 0x200, + PAGE_WRITECOMBINE = 0x400 + } + + #endregion } } \ No newline at end of file