using Dalamud.Hooking; using Dalamud.Utility.Signatures; namespace OrangeGuidanceTomestone.MiniPenumbra; internal unsafe class VfxReplacer : IDisposable { private delegate byte ReadSqPackDelegate(void* resourceManager, SeFileDescriptor* pFileDesc, int priority, bool isSync); [Signature("E8 ?? ?? ?? ?? EB 05 E8 ?? ?? ?? ?? 84 C0 0F 84 ?? 00 00 00 4C 8B C3", DetourName = nameof(ReadSqPackDetour))] private Hook _readSqPackHook; [Signature("E8 ?? ?? ?? ?? 84 C0 0F 84 ?? 00 00 00 4C 8B C3 BA 05")] private delegate* unmanaged _readFile; private Plugin Plugin { get; } internal VfxReplacer(Plugin plugin) { this.Plugin = plugin; this.Plugin.GameInteropProvider.InitializeFromAttributes(this); this._readSqPackHook!.Enable(); } public void Dispose() { this._readSqPackHook.Dispose(); } private byte ReadSqPackDetour(void* resourceManager, SeFileDescriptor* fileDescriptor, int priority, bool isSync) { if (!this.Plugin.Config.RemoveGlow) { goto Original; } if (fileDescriptor == null || fileDescriptor->ResourceHandle == null) { goto Original; } var path = fileDescriptor->ResourceHandle->FileName.ToString(); var index = Array.IndexOf(Messages.VfxPaths, path); if (index == -1) { goto Original; } var letter = (char) ('a' + index); var newPath = Path.Join(this.Plugin.AvfxFilePath, $"sign_{letter}.avfx"); return this.DefaultRootedResourceLoad(newPath, resourceManager, fileDescriptor, priority, isSync); Original: return this._readSqPackHook.Original(resourceManager, fileDescriptor, priority, isSync); } // Load the resource from a path on the users hard drives. private byte DefaultRootedResourceLoad(string gamePath, void* resourceManager, SeFileDescriptor* fileDescriptor, int priority, bool isSync) { // Specify that we are loading unpacked files from the drive. // We need to copy the actual file path in UTF16 (Windows-Unicode) on two locations, // but since we only allow ASCII in the game paths, this is just a matter of upcasting. fileDescriptor->FileMode = SeFileMode.LoadUnpackedResource; var fd = stackalloc byte[0x20 + 2 * gamePath.Length + 0x16]; fileDescriptor->FileDescriptor = fd; var fdPtr = (char*) (fd + 0x21); for (var i = 0; i < gamePath.Length; ++i) { (&fileDescriptor->Utf16FileName)[i] = gamePath[i]; fdPtr[i] = gamePath[i]; } (&fileDescriptor->Utf16FileName)[gamePath.Length] = '\0'; fdPtr[gamePath.Length] = '\0'; // Use the SE ReadFile function. var ret = this._readFile(resourceManager, fileDescriptor, priority, isSync); return ret; } }