Compare commits

...

9 commits

Author SHA1 Message Date
Actions User
eec8ee7094 [CI] Updating repo.json for 1.5.1.13
Some checks failed
.NET Build / build (push) Has been cancelled
2026-01-27 15:30:23 +00:00
Marc-Aurel Zent
13500264b7 Use iced to create AsmHooks in PapRewriter.
Some checks failed
.NET Build / build (push) Has been cancelled
2025-12-22 15:31:12 +01:00
Actions User
6ba735eefb [CI] Updating repo.json for 1.5.1.12
Some checks failed
.NET Build / build (push) Has been cancelled
2025-12-20 20:53:36 +00:00
Ottermandias
73f02851a6 Cherry pick API support for other block compression types from Luna branch. 2025-12-20 21:51:40 +01:00
Actions User
069323cfb8 [CI] Updating repo.json for 1.5.1.11
Some checks are pending
.NET Build / build (push) Waiting to run
2025-12-20 14:38:27 +00:00
Ottermandias
9aa566f521 Fix typo in new IPC providers. 2025-12-20 15:36:11 +01:00
Ottermandias
eff3784a85 Fix multi-release bug in texturearrayslicer. 2025-12-20 15:36:03 +01:00
Ottermandias
9cf7030f87 ...
Some checks failed
.NET Build / build (push) Has been cancelled
2025-12-19 01:13:08 +01:00
Actions User
deb3686df5 [CI] Updating repo.json for 1.5.1.9 2025-12-19 00:12:44 +00:00
6 changed files with 74 additions and 42 deletions

View file

@ -9,7 +9,7 @@ jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
with: with:
submodules: recursive submodules: recursive
- name: Setup .NET - name: Setup .NET

@ -1 +1 @@
Subproject commit 1750c41b53e1000c99a7fb9d8a0f082aef639a41 Subproject commit 52a3216a525592205198303df2844435e382cf87

View file

@ -19,6 +19,12 @@ public class EditingApi(TextureManager textureManager) : IPenumbraApiEditing, IA
TextureType.Bc3Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC3, mipMaps, false, inputFile, outputFile), TextureType.Bc3Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC3, mipMaps, false, inputFile, outputFile),
TextureType.Bc7Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, true, inputFile, outputFile), TextureType.Bc7Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, true, inputFile, outputFile),
TextureType.Bc7Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, false, inputFile, outputFile), TextureType.Bc7Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, false, inputFile, outputFile),
TextureType.Bc1Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC1, mipMaps, true, inputFile, outputFile),
TextureType.Bc1Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC1, mipMaps, false, inputFile, outputFile),
TextureType.Bc4Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC4, mipMaps, true, inputFile, outputFile),
TextureType.Bc4Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC4, mipMaps, false, inputFile, outputFile),
TextureType.Bc5Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC5, mipMaps, true, inputFile, outputFile),
TextureType.Bc5Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC5, mipMaps, false, inputFile, outputFile),
_ => Task.FromException(new Exception($"Invalid input value {textureType}.")), _ => Task.FromException(new Exception($"Invalid input value {textureType}.")),
}; };
@ -36,6 +42,12 @@ public class EditingApi(TextureManager textureManager) : IPenumbraApiEditing, IA
TextureType.Bc3Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC3, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width), TextureType.Bc3Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC3, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
TextureType.Bc7Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width), TextureType.Bc7Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
TextureType.Bc7Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width), TextureType.Bc7Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC7, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
TextureType.Bc1Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC1, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
TextureType.Bc1Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC1, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
TextureType.Bc4Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC4, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
TextureType.Bc4Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC4, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
TextureType.Bc5Tex => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC5, mipMaps, true, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
TextureType.Bc5Dds => textureManager.SaveAs(CombinedTexture.TextureSaveType.BC5, mipMaps, false, new BaseImage(), outputFile, rgbaData, width, rgbaData.Length / 4 / width),
_ => Task.FromException(new Exception($"Invalid input value {textureType}.")), _ => Task.FromException(new Exception($"Invalid input value {textureType}.")),
}; };
// @formatter:on // @formatter:on

View file

@ -1,6 +1,7 @@
using System.Text.Unicode; using System.Text.Unicode;
using Dalamud.Hooking; using Dalamud.Hooking;
using Iced.Intel; using Iced.Intel;
using static Iced.Intel.AssemblerRegisters;
using OtterGui.Extensions; using OtterGui.Extensions;
using Penumbra.String.Classes; using Penumbra.String.Classes;
using Swan; using Swan;
@ -46,36 +47,32 @@ public sealed class PapRewriter(PeSigScanner sigScanner, PapRewriter.PapResource
stackAccesses.RemoveAll(instr => instr.IP == hp.IP); stackAccesses.RemoveAll(instr => instr.IP == hp.IP);
var detourPointer = Marshal.GetFunctionPointerForDelegate(papResourceHandler); var detourPointer = Marshal.GetFunctionPointerForDelegate(papResourceHandler);
var targetRegister = hookPoint.Op0Register.ToString().ToLower(); var targetRegister = GetRegister64(hookPoint.Op0Register);
var hookAddress = new IntPtr((long)detourPoint.IP); var hookAddress = new IntPtr((long)detourPoint.IP);
var caveAllocation = NativeAllocCave(16); var caveAllocation = NativeAllocCave(16);
var hook = new AsmHook( var assembler = new Assembler(64);
hookAddress, assembler.mov(targetRegister, stringAllocation); // Move our char *path into the relevant register (rdx)
[
"use64",
$"mov {targetRegister}, 0x{stringAllocation:x8}", // Move our char *path into the relevant register (rdx)
// After this asm stub, we have a call to Crc32(); since r9 is a volatile, unused register, we can use it ourselves // After this asm stub, we have a call to Crc32(); since r9 is a volatile, unused register, we can use it ourselves
// We're essentially storing the original 2 arguments ('this', 'path'), in case they get mangled in our call // We're essentially storing the original 2 arguments ('this', 'path'), in case they get mangled in our call
// We technically don't need to save rdx ('path'), since it'll be stringLoc, but eh // We technically don't need to save rdx ('path'), since it'll be stringLoc, but eh
$"mov r9, 0x{caveAllocation:x8}", assembler.mov(r9, caveAllocation);
"mov [r9], rcx", assembler.mov(__qword_ptr[r9], rcx);
"mov [r9+0x8], rdx", assembler.mov(__qword_ptr[r9 + 8], rdx);
// We can use 'rax' here too since it's also volatile, and it'll be overwritten by Crc32()'s return anyway // We can use 'rax' here too since it's also volatile, and it'll be overwritten by Crc32()'s return anyway
$"mov rax, 0x{detourPointer:x8}", // Get a pointer to our detour in place assembler.mov(rax, detourPointer);
"call rax", // Call detour assembler.call(rax);
// Do the reverse process and retrieve the stored stuff // Do the reverse process and retrieve the stored stuff
$"mov r9, 0x{caveAllocation:x8}", assembler.mov(r9, caveAllocation);
"mov rcx, [r9]", assembler.mov(rcx, __qword_ptr[r9]);
"mov rdx, [r9+0x8]", assembler.mov(rdx, __qword_ptr[r9 + 8]);
// Plop 'rax' (our return value, the path size) into r8, so it's the third argument for the subsequent Crc32() call // Plop 'rax' (our return value, the path size) into r8, so it's the third argument for the subsequent Crc32() call
"mov r8, rax", assembler.mov(r8, rax);
], $"{name}.PapRedirection" var hook = new AsmHook(hookAddress, AssembleToBytes(assembler), $"{name}.PapRedirection");
);
_hooks.Add(hookAddress, hook); _hooks.Add(hookAddress, hook);
hook.Enable(); hook.Enable();
@ -95,19 +92,45 @@ public sealed class PapRewriter(PeSigScanner sigScanner, PapRewriter.PapResource
if (_hooks.ContainsKey(hookAddress)) if (_hooks.ContainsKey(hookAddress))
continue; continue;
var targetRegister = stackAccess.Op0Register.ToString().ToLower(); var targetRegister = GetRegister64(stackAccess.Op0Register);
var hook = new AsmHook( var assembler = new Assembler(64);
hookAddress, assembler.mov(targetRegister, stringAllocation);
[ var hook = new AsmHook(hookAddress, AssembleToBytes(assembler), $"{name}.PapStackAccess[{index}]");
"use64",
$"mov {targetRegister}, 0x{stringAllocation:x8}",
], $"{name}.PapStackAccess[{index}]"
);
_hooks.Add(hookAddress, hook); _hooks.Add(hookAddress, hook);
hook.Enable(); hook.Enable();
} }
} }
private static AssemblerRegister64 GetRegister64(Register reg)
=> reg switch
{
Register.RAX => rax,
Register.RCX => rcx,
Register.RDX => rdx,
Register.RBX => rbx,
Register.RSP => rsp,
Register.RBP => rbp,
Register.RSI => rsi,
Register.RDI => rdi,
Register.R8 => r8,
Register.R9 => r9,
Register.R10 => r10,
Register.R11 => r11,
Register.R12 => r12,
Register.R13 => r13,
Register.R14 => r14,
Register.R15 => r15,
_ => throw new ArgumentOutOfRangeException(nameof(reg), reg, "Unsupported register."),
};
private static byte[] AssembleToBytes(Assembler assembler)
{
using var stream = new MemoryStream();
var writer = new StreamCodeWriter(stream);
assembler.Assemble(writer, 0);
return stream.ToArray();
}
private static IEnumerable<Instruction> ScanStackAccesses(IEnumerable<Instruction> instructions, Instruction hookPoint) private static IEnumerable<Instruction> ScanStackAccesses(IEnumerable<Instruction> instructions, Instruction hookPoint)
{ {

View file

@ -18,7 +18,7 @@ public sealed unsafe class TextureArraySlicer : IUiService, IDisposable
/// <remarks> Caching this across frames will cause a crash to desktop. </remarks> /// <remarks> Caching this across frames will cause a crash to desktop. </remarks>
public ImTextureID GetImGuiHandle(Texture* texture, byte sliceIndex) public ImTextureID GetImGuiHandle(Texture* texture, byte sliceIndex)
{ {
if (texture == null) if (texture is null)
throw new ArgumentNullException(nameof(texture)); throw new ArgumentNullException(nameof(texture));
if (sliceIndex >= texture->ArraySize) if (sliceIndex >= texture->ArraySize)
throw new ArgumentOutOfRangeException(nameof(sliceIndex), throw new ArgumentOutOfRangeException(nameof(sliceIndex),
@ -74,9 +74,6 @@ public sealed unsafe class TextureArraySlicer : IUiService, IDisposable
{ {
ID3D11ShaderResourceView* slicedSrv = null; ID3D11ShaderResourceView* slicedSrv = null;
Marshal.ThrowExceptionForHR(device->CreateShaderResourceView(resource, &description, &slicedSrv)); Marshal.ThrowExceptionForHR(device->CreateShaderResourceView(resource, &description, &slicedSrv));
resource->Release();
device->Release();
state = new SliceState(slicedSrv); state = new SliceState(slicedSrv);
_activeSlices.Add(((nint)texture, sliceIndex), state); _activeSlices.Add(((nint)texture, sliceIndex), state);
return new ImTextureID((nint)state.ShaderResourceView); return new ImTextureID((nint)state.ShaderResourceView);

View file

@ -5,8 +5,8 @@
"Punchline": "Runtime mod loader and manager.", "Punchline": "Runtime mod loader and manager.",
"Description": "Runtime mod loader and manager.", "Description": "Runtime mod loader and manager.",
"InternalName": "Penumbra", "InternalName": "Penumbra",
"AssemblyVersion": "1.5.1.8", "AssemblyVersion": "1.5.1.13",
"TestingAssemblyVersion": "1.5.1.8", "TestingAssemblyVersion": "1.5.1.13",
"RepoUrl": "https://github.com/xivdev/Penumbra", "RepoUrl": "https://github.com/xivdev/Penumbra",
"ApplicableVersion": "any", "ApplicableVersion": "any",
"DalamudApiLevel": 14, "DalamudApiLevel": 14,
@ -18,9 +18,9 @@
"LoadPriority": 69420, "LoadPriority": 69420,
"LoadRequiredState": 2, "LoadRequiredState": 2,
"LoadSync": true, "LoadSync": true,
"DownloadLinkInstall": "https://github.com/xivdev/Penumbra/releases/download/1.5.1.8/Penumbra.zip", "DownloadLinkInstall": "https://github.com/xivdev/Penumbra/releases/download/1.5.1.13/Penumbra.zip",
"DownloadLinkTesting": "https://github.com/xivdev/Penumbra/releases/download/1.5.1.8/Penumbra.zip", "DownloadLinkTesting": "https://github.com/xivdev/Penumbra/releases/download/1.5.1.13/Penumbra.zip",
"DownloadLinkUpdate": "https://github.com/xivdev/Penumbra/releases/download/1.5.1.8/Penumbra.zip", "DownloadLinkUpdate": "https://github.com/xivdev/Penumbra/releases/download/1.5.1.13/Penumbra.zip",
"IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png" "IconUrl": "https://raw.githubusercontent.com/xivdev/Penumbra/master/images/icon.png"
} }
] ]