//#define ENABLE_ASSERTS //#define OVERSIZED_SHADOW_MAP //#define SHADER_FEATURES //#define RESOURCE_ADJUSTMENT using System.Numerics; using System.Globalization; using System.Runtime.CompilerServices; #if ENABLE_ASSERTS using System.Diagnostics; #endif using ImGuiNET; using SharpPluginLoader.Core; using SharpPluginLoader.Core.Configuration; using SharpPluginLoader.Core.Memory; #if SHADER_FEATURES using SharpPluginLoader.Core.Rendering; #endif #if RESOURCE_ADJUSTMENT using SharpPluginLoader.Core.Resources; #endif // @TODO: // - Configurable Broad Area Shadow Resolution. // - Hook sMhScene constructor to set hqMode. // - Optimized update logic. // - Only update non-override values when UI is shown. // - Map sky params. // - Lights refactor. // - Descriptions. // - float format. // - Lights iterop. // - SPL Patches: // - No crash on empty config. // - Style change // - Hotload nukes config? // // Areas that need Shadow Distance retuning: // - Exit of central camp in Horfrost Reach. // - Sporepuff area in The Ancient Forest. // - Wildspire Waste entrence to enclosed area past the waterfall. // - The Rotten Vale jump out of Southeast Camp. Wall on the opposite side slightly to the left. // - Rotten vale lower area. // - Special Arena. // - Elder's Recess Lavasioth area. // - Hoarfrost pit area with wedge beetles. // // Known issues to think about: // - Hair clipping when character looks down. // - Vangis headband. // - Volume rendering can look really bad with multiple overlapped sources(?) (Guding lands vines). // - Screen space reflections often look bad. // - Facial contact shadows unnaturally move based on camera position. namespace WorldTuningTool { using static Config; public unsafe class Plugin : IPlugin { public string Name => "World Tuning Tool"; public string Author => "Akon City Software"; private static bool loggedAssertFailed = false; public static void Assert(bool condition, [CallerLineNumber] int line = 0) { if (!condition && !loggedAssertFailed) { Log.Error($"Assert failed on line {line}."); loggedAssertFailed = true; } #if ENABLE_ASSERTS Trace.Assert(condition); #endif } private static nint lton(long l) { unchecked { return (nint)l; } } private static byte ByteFlag(bool f) { return f ? (byte)0x1 : (byte)0x0; } private static Vector4 ColorVectorFromInt(int c) { return new Vector4( ((c ) & 0xFF) / 255.0f, ((c >> 8 ) & 0xFF) / 255.0f, ((c >> 16) & 0xFF) / 255.0f, ((c >> 24) & 0xFF) / 255.0f); } private static int ColorVectorToInt(Vector4 v) { return ((int)MathF.Round(v.X * 255.0f) | ((int)MathF.Round(v.Y * 255.0f) << 8) | ((int)MathF.Round(v.Z * 255.0f) << 16) | ((int)MathF.Round(v.W * 255.0f) << 24)); } private static float StringToSingle(string s) { return Convert.ToSingle(s, CultureInfo.InvariantCulture); } private static bool StringToBoolean(string s) { return Convert.ToBoolean(s, CultureInfo.InvariantCulture); } private static int StringToInt32(string s, int fromBase = 10) { if (fromBase == 10) return Convert.ToInt32(s, CultureInfo.InvariantCulture); return Convert.ToInt32(s, fromBase); } private static string SingleToString(float f, string? format = null) { return f.ToString(format, CultureInfo.InvariantCulture); } private static string BooleanToString(bool b) { return b.ToString(CultureInfo.InvariantCulture); } private static string Int32ToString(int i, string? format = null) { return i.ToString(format, CultureInfo.InvariantCulture); } public static Vector2 StringToVector2(string s, char delim = ',') { if (String.IsNullOrEmpty(s)) throw new FormatException(); string[] sp = s.Split(delim).Select(v => v.Trim()).ToArray(); if (sp.Length != 2) throw new FormatException(); return new Vector2(StringToSingle(sp[0]), StringToSingle(sp[1])); } public static Vector3 StringToVector3(string s, char delim = ',') { if (String.IsNullOrEmpty(s)) throw new FormatException(); string[] sp = s.Split(delim).Select(v => v.Trim()).ToArray(); if (sp.Length != 3) throw new FormatException(); return new Vector3(StringToSingle(sp[0]), StringToSingle(sp[1]), StringToSingle(sp[2])); } public static Vector4 StringToVector4(string s, char delim = ',') { if (String.IsNullOrEmpty(s)) throw new FormatException(); string[] sp = s.Split(delim).Select(v => v.Trim()).ToArray(); if (sp.Length != 4) throw new FormatException(); return new Vector4(StringToSingle(sp[0]), StringToSingle(sp[1]), StringToSingle(sp[2]), StringToSingle(sp[3])); } private static string Vector2ToString(Vector2 v) { return string.Format(CultureInfo.InvariantCulture, "{0},{1}", v.X, v.Y); } private static string Vector3ToString(Vector3 v) { return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", v.X, v.Y, v.Z); } private static string Vector4ToString(Vector4 v) { return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", v.X, v.Y, v.Z, v.W); } public enum ParameterType { BOOL, FLAG, BYTE, INT, ALIGNED_INT, HEX, COLOR, COLOR_FACTOR, FLOAT, VECTOR3, VECTOR2, VECTOR4, PATCH_FLOAT, SHADOW_RESOLUTION } private static IPlugin? Instance = null; private static Config getConfig() { return ConfigManager.GetConfig(Instance!); } private static void rebuildConfig(Config config, List overrides) { List selectedGlobals = config.Globals[selectedGlobal]; selectedGlobals.Clear(); foreach (Override ovG in overrides) { selectedGlobals.Add(ovG); } } private static int configState = 0; private const int SelectedNotSaved = 1; private const int SetNotSaved = (1 << 1); private static void saveConfig(Config config) { List tmpOverrides = config.Overrides[globalStage]; config.Overrides.Remove(globalStage); ConfigManager.SaveConfig(Instance!); config.Overrides[globalStage] = tmpOverrides; configState = 0; } private const float defaultParamWidth = 0.215f; private static bool ignoreMinMax = false; private const StageExt globalStage = StageExt.Global; private static StageExt currentStage => (StageExt)Area.CurrentStage; private static List? maybeGetStageOverrides(Config config) { if (!config.Overrides.ContainsKey(currentStage)) { return null; } return (currentStage != StageExt.Global) ? config.Overrides[currentStage] : null; } private static StageExt selectedStage = globalStage; private static StageExt previousStage = selectedStage; private static string selectedGlobal = ""; private static string selectedSet = ""; private static bool renamingSet = false; private static string typedSetName = ""; private static Dictionary externalOverrides = new Dictionary(); private static int externalId = 0; private static bool overridesContainsParam(IEnumerable overrides, Parameter param) { foreach (Override ov in overrides) { if (ov.Param == param) { return true; } } return false; } private static bool unsetIfOverridesContainsParam(IEnumerable overrides, Parameter param) { foreach (Override ov in overrides) { if (ov.Param == param) { ov.Unset(); return true; } } return false; } private static bool setIfOverridesContainsParam(IEnumerable overrides, Parameter param) { foreach (Override ov in overrides) { if (ov.Param == param) { ov.Set(); return true; } } return false; } // globalOverrides == null: selectedStage is globalStage. private static bool selectedOverrideSuperseded(Config config, List? globalOverrides, List? stageOverrides, Parameter? param) { // Non-current stage always superseded (inactive). bool superseded = globalOverrides != null && selectedStage != currentStage; if (!superseded && param != null) { // If Global is selected, check if superseded by a currentStage override. superseded |= globalOverrides == null && stageOverrides != null && overridesContainsParam(stageOverrides, param); // Any other selectedStage override is either inactive or could only be superseded by a set or external override. superseded |= selectedSet != "" && overridesContainsParam(config.Sets[selectedSet], param); superseded |= overridesContainsParam(externalOverrides.Values, param); } return superseded; } private static bool stageOverrideSuperseded(Config config, Parameter? param) { bool superseded = false; if (param != null) { superseded |= selectedSet != "" && overridesContainsParam(config.Sets[selectedSet], param); superseded |= overridesContainsParam(externalOverrides.Values, param); } return superseded; } private static class ParameterFlags { public const int PerFrame = 1; public const int NoOverride = 1 << 1; } public abstract class Parameter { public string Name; private string hiddenName; public ParameterType Type; private nint offset; private int mask; private nint lastAddr = 0x0; private bool overrideValue = false; private bool overrideForStage = false; private bool overrideWasOn = false; private int flags; public bool NoOverride => (flags & ParameterFlags.NoOverride) == ParameterFlags.NoOverride; public bool PerFrame => (flags & ParameterFlags.PerFrame) == ParameterFlags.PerFrame; protected float stepf, minf, maxf; protected int step, min, max; private bool fallbackUpdate = true; private bool pendingWrite = false; private Vector4 valueV = default; private int valueInt = 0; private Vector4 oValueV; // Original value. private int oValueInt; private bool pendingUpdate = true; private bool queuedOverride = false; private bool queuedValue = false; private Vector4 qValueV; // Queued value. private int qValueInt; private bool bValue = false; private Vector4 bValueV; // B value in A/B. private int bValueInt; public Parameter(string name, nint offset, int mask, ParameterType type, int flags) { Name = name; hiddenName = "##" + Name; Type = type; this.offset = offset; this.mask = mask; this.flags = flags; if (Type == ParameterType.PATCH_FLOAT) { valueV.X = MemoryUtil.Read(offset); pendingUpdate = false; } else if (Type == ParameterType.SHADOW_RESOLUTION) { valueInt = 1; pendingUpdate = false; } } public bool PendingUpdate() { return pendingUpdate; } public (Vector4, int) GetValue() { return (valueV, valueInt); } public int GetMask() { return mask; } public void Resolve() { Assert(!InTransition && pendingWrite); pendingWrite = false; maybeWriteCurrentValue(); } private void maybeWriteCurrentValue() { if (InTransition) { if (!DirtyParams.Contains(this)) { pendingWrite = true; DirtyParams.Add(this); } else { Assert(pendingWrite); } return; } Assert(!pendingWrite); switch (Type) { case ParameterType.PATCH_FLOAT: new Patch(offset, BitConverter.GetBytes(valueV.X)).Enable(); break; case ParameterType.SHADOW_RESOLUTION: if (valueInt > 1) { shadowResEnable(valueInt); } else { shadowResDisable(); } break; } if (PerFrame || lastAddr == 0x0) { return; } switch (Type) { case ParameterType.BOOL: case ParameterType.BYTE: MemoryUtil.GetRef(lastAddr + offset) = (byte)valueInt; break; case ParameterType.FLAG: if (valueInt != 0) { MemoryUtil.GetRef(lastAddr + offset) |= mask; } else { MemoryUtil.GetRef(lastAddr + offset) &= ~mask; } break; case ParameterType.INT: case ParameterType.ALIGNED_INT: case ParameterType.HEX: case ParameterType.COLOR: MemoryUtil.GetRef(lastAddr + offset) = valueInt; break; case ParameterType.FLOAT: MemoryUtil.GetRef(lastAddr + offset) = valueV.X; break; case ParameterType.VECTOR2: MemoryUtil.GetRef(lastAddr + offset) = new Vector2(valueV.X, valueV.Y); break; case ParameterType.VECTOR3: case ParameterType.COLOR_FACTOR: MemoryUtil.GetRef(lastAddr + offset) = new Vector3(valueV.X, valueV.Y, valueV.Z); break; case ParameterType.VECTOR4: MemoryUtil.GetRef(lastAddr + offset) = valueV; break; } } public void OverrideOn(bool forStage) { if (pendingUpdate) { if (queuedOverride) { Assert(!overrideWasOn && forStage); overrideWasOn = true; } else { queuedOverride = true; } } else { if (overrideValue) { Assert(!overrideWasOn && forStage); overrideWasOn = true; } else { oValueV = valueV; oValueInt = valueInt; maybeWriteCurrentValue(); overrideValue = true; } } overrideForStage = forStage; } public void OverrideOff() { if (queuedOverride) { if (overrideWasOn) { Assert(overrideForStage); overrideWasOn = false; } else { queuedOverride = false; } } else { Assert(overrideValue); if (overrideWasOn) { Assert(overrideForStage); overrideWasOn = false; } else { valueV = oValueV; valueInt = oValueInt; maybeWriteCurrentValue(); bValue = false; overrideValue = false; } } overrideForStage = false; } public void SetOverrideValue(Vector4 v4, int i1) { if (queuedOverride) { qValueV = v4; qValueInt = i1; queuedValue = true; } else { valueV = v4; valueInt = i1; maybeWriteCurrentValue(); } } private int bitIndex(int mask) { return BitOperations.Log2((uint)(mask & (-mask))); } public void Update(nint baseObject, bool fromHook = false) { lastAddr = baseObject; if (baseObject == 0x0) { pendingUpdate = true; return; } if (fromHook) { fallbackUpdate = false; } else if (!fallbackUpdate) { fallbackUpdate = true; return; } // Trying to track changes to the original value is a "best effort" // approach because it can't work if the in-game value is updated // to the same value as a current override. switch (Type) { case ParameterType.BOOL: case ParameterType.BYTE: { ref byte b1 = ref MemoryUtil.GetRef(baseObject + offset); if (overrideValue) { if (b1 != (byte)valueInt) { oValueInt = b1; } b1 = (byte)valueInt; } else { valueInt = b1; } break; } case ParameterType.FLAG: { ref int i1 = ref MemoryUtil.GetRef(baseObject + offset); int b1 = ByteFlag((i1 & mask) == mask); if (overrideValue) { if (b1 != valueInt) { oValueInt = b1; } if (valueInt != 0) { i1 |= mask; } else { i1 &= ~mask; } } else { valueInt = b1; } break; } case ParameterType.INT: case ParameterType.ALIGNED_INT: case ParameterType.HEX: case ParameterType.COLOR: { ref int i1 = ref MemoryUtil.GetRef(baseObject + offset); if (mask != 0) { i1 = (i1 & mask) >> bitIndex(mask); } if (overrideValue) { if (i1 != valueInt) { oValueInt = i1; } i1 = valueInt; } else { valueInt = i1; } break; } case ParameterType.FLOAT: { ref float f1 = ref MemoryUtil.GetRef(baseObject + offset); if (overrideValue) { if (f1 != valueV.X) { oValueV.X = f1; } f1 = valueV.X; } else { valueV.X = f1; } break; } case ParameterType.VECTOR2: { ref Vector2 v2 = ref MemoryUtil.GetRef(baseObject + offset); Vector2 valueV2 = new Vector2(valueV.X, valueV.Y); if (overrideValue) { if (v2 != valueV2) { oValueV = new Vector4(v2.X, v2.Y, 0.0f, 0.0f); } v2 = valueV2; } else { valueV = new Vector4(v2.X, v2.Y, 0.0f, 0.0f); } break; } case ParameterType.VECTOR3: case ParameterType.COLOR_FACTOR: { ref Vector3 v3 = ref MemoryUtil.GetRef(baseObject + offset); Vector3 valueV3 = new Vector3(valueV.X, valueV.Y, valueV.Z); if (overrideValue) { if (v3 != valueV3) { oValueV = new Vector4(v3.X, v3.Y, v3.Z, 0.0f); } v3 = valueV3; } else { valueV = new Vector4(v3.X, v3.Y, v3.Z, 0.0f); } break; } case ParameterType.VECTOR4: { ref Vector4 v4 = ref MemoryUtil.GetRef(baseObject + offset); if (overrideValue) { if (v4 != valueV) { oValueV = v4; } v4 = valueV; } else { valueV = v4; } break; } } if (pendingUpdate) { pendingUpdate = false; if (queuedOverride) { Assert(!overrideValue); OverrideOn(overrideForStage); if (queuedValue) { valueV = qValueV; valueInt = qValueInt; maybeWriteCurrentValue(); queuedValue = false; } queuedOverride = false; } } } public void AdjustValue(ref Vector4 v4, ref int i1) { if (Type == ParameterType.SHADOW_RESOLUTION) // Hard limit. { #if OVERSIZED_SHADOW_MAP i1 = Math.Clamp(i1, 1, 6); #else i1 = Math.Clamp(i1, 1, 5); #endif } else if (!ignoreMinMax) { switch (Type) { case ParameterType.BOOL: case ParameterType.FLAG: case ParameterType.BYTE: case ParameterType.INT: case ParameterType.HEX: i1 = Math.Clamp(i1, min, max); break; case ParameterType.ALIGNED_INT: i1 = Math.Clamp((i1 + (step - 1))&~(step - 1), min, max); break; case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: case ParameterType.VECTOR2: case ParameterType.VECTOR3: case ParameterType.COLOR_FACTOR: case ParameterType.VECTOR4: v4 = new Vector4( Math.Clamp(v4.X, minf, maxf), Math.Clamp(v4.Y, minf, maxf), Math.Clamp(v4.Z, minf, maxf), Math.Clamp(v4.W, minf, maxf)); break; } } } public bool DrawValue(ref Vector4 v4, ref int i1, float width, bool drawLabel) { bool valueChanged = false; string label = drawLabel ? Name : hiddenName; float displayMinf = ignoreMinMax ? 0.0f : minf; float displayMaxf = ignoreMinMax ? 0.0f : maxf; switch (Type) { case ParameterType.BOOL: case ParameterType.FLAG: { bool b = i1 != 0; if (ImGui.Checkbox(label, ref b)) { i1 = ByteFlag(b); valueChanged = true; } break; } case ParameterType.BYTE: { ImGuiInputTextFlags flags = ImGuiInputTextFlags.EnterReturnsTrue; ImGui.SetNextItemWidth(width * defaultParamWidth); if (ImGui.InputInt(label, ref i1, step, 0, flags)) { valueChanged = true; } break; } case ParameterType.INT: case ParameterType.ALIGNED_INT: case ParameterType.HEX: case ParameterType.SHADOW_RESOLUTION: { ImGuiInputTextFlags flags = ImGuiInputTextFlags.EnterReturnsTrue; if (Type == ParameterType.HEX) { flags |= ImGuiInputTextFlags.CharsHexadecimal; } ImGui.SetNextItemWidth(width * defaultParamWidth); if (ImGui.InputInt(label, ref i1, step, 0, flags)) { valueChanged = true; } break; } case ParameterType.COLOR: { Vector4 colorV = ColorVectorFromInt(i1); ImGui.SetNextItemWidth(width * defaultParamWidth * 3.0f); if (ImGui.ColorEdit4(label, ref colorV)) { i1 = ColorVectorToInt(colorV); valueChanged = true; } break; } case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: { float f1 = v4.X; ImGui.SetNextItemWidth(width * defaultParamWidth); if (ImGui.DragFloat(label, ref f1, stepf, displayMinf, displayMaxf, "%.6f", ImGuiSliderFlags.NoRoundToFormat)) { v4 = new Vector4(f1, 0.0f, 0.0f, 0.0f); valueChanged = true; } break; } case ParameterType.VECTOR2: { Vector2 v2 = new Vector2(v4.X, v4.Y); ImGui.SetNextItemWidth(width * defaultParamWidth * 2.0f); if (ImGui.DragFloat2(label, ref v2, stepf, displayMinf, displayMaxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat)) { v4 = new Vector4(v2.X, v2.Y, 0.0f, 0.0f); valueChanged = true; } break; } case ParameterType.VECTOR3: case ParameterType.COLOR_FACTOR: { Vector3 v3 = new Vector3(v4.X, v4.Y, v4.Z); if (Type == ParameterType.COLOR_FACTOR) { Vector3 v3Scale = Vector3.Abs(v3); float max = MathF.Max(v3.X, MathF.Max(v3.Y, v3.Z)); if (max != 0.0f) { v3Scale = (v3Scale / max) * v3Scale; } ImGui.ColorEdit3(label, ref v3Scale, ImGuiColorEditFlags.NoPicker | ImGuiColorEditFlags.NoLabel | ImGuiColorEditFlags.NoInputs); ImGui.SameLine(); } ImGui.SetNextItemWidth(width * defaultParamWidth * 3.0f); if (ImGui.DragFloat3(label, ref v3, stepf, displayMinf, displayMaxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat)) { v4 = new Vector4(v3.X, v3.Y, v3.Z, 0.0f); valueChanged = true; } break; } case ParameterType.VECTOR4: { ImGui.SetNextItemWidth(width * defaultParamWidth * 4.0f); if (ImGui.DragFloat4(label, ref v4, stepf, displayMinf, displayMaxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat)) { valueChanged = true; } break; } } if (valueChanged) { AdjustValue(ref v4, ref i1); } return valueChanged; } public void DrawReferenceValue(string name, Vector4 v4, int i1) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.7f); switch (Type) { case ParameterType.BOOL: case ParameterType.FLAG: ImGui.Text($"({name}: {i1 != 0})"); break; case ParameterType.BYTE: case ParameterType.INT: case ParameterType.ALIGNED_INT: case ParameterType.SHADOW_RESOLUTION: ImGui.Text($"({name}: {i1})"); break; case ParameterType.HEX: case ParameterType.COLOR: ImGui.Text($"({name}: {i1:X})"); break; case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: ImGui.Text($"({name}: {v4.X:0.000000})"); break; case ParameterType.VECTOR2: ImGui.Text($"({name}: {v4.X:0.00000}, {v4.Y:0.00000})"); break; case ParameterType.VECTOR3: case ParameterType.COLOR_FACTOR: ImGui.Text($"({name}: {v4.X:0.00000}, {v4.Y:0.00000}, {v4.Z:0.00000})"); break; case ParameterType.VECTOR4: ImGui.Text($"({name}: {v4.X:0.00000}, {v4.Y:0.00000}, {v4.Z:0.00000}, {v4.W:0.00000})"); break; } ImGui.PopStyleVar(); } public string SetByLine(Config config, List? stageOverrides, List? globalOverrides, bool forSet, bool allowGlobal) { // Reverse order of selectedOverrideSuperseded(). if (overridesContainsParam(externalOverrides.Values, this)) { return "Set by 'External'"; } Assert(!forSet); if (selectedSet != "" && overridesContainsParam(config.Sets[selectedSet], this)) { return $"Set by '{selectedSet}'"; } if ((allowGlobal || globalOverrides == null) && stageOverrides != null && overridesContainsParam(stageOverrides, this)) { return $"Set by '{StageToString(currentStage)}'"; } if (allowGlobal) { return "Set by \'Global\'"; } // Superseded because inactive. Assert(globalOverrides != null && selectedStage != currentStage); return ""; } public void Draw(float width, bool assumeOverride = false) { Draw(ref valueV, ref valueInt, width, assumeOverride); } public void Draw(ref Vector4 v4, ref int i1, float width, bool assumeOverride) { ImGui.PushID(Name); if (NoOverride) { ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.X, 0.0f)); } else if (!assumeOverride) { if (overrideForStage) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushItemFlag(ImGuiItemFlags.MixedValue, true); } bool toggleOverride = overrideValue; if (ImGui.Checkbox("##Override", ref toggleOverride)) { if (toggleOverride) { OverrideOn(false); } else { OverrideOff(); } } if (ImGui.BeginItemTooltip()) { if (overrideForStage) { Config config = getConfig(); List? globalOverrides = config.Overrides[globalStage]; List? stageOverrides = maybeGetStageOverrides(config); ImGui.Text(SetByLine(config, stageOverrides, globalOverrides, false, true)); } else if (overrideValue) { ImGui.Text("Set"); } else { ImGui.Text("Set override"); } ImGui.EndTooltip(); } if (overrideForStage) { ImGui.PopItemFlag(); ImGui.PopItemFlag(); } ImGui.SameLine(); } if (!overrideValue) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); } if (DrawValue(ref valueV, ref valueInt, width, !bValue)) { maybeWriteCurrentValue(); } if (!overrideValue) { ImGui.PopItemFlag(); } bool showButtons = !assumeOverride && overrideValue && !pendingUpdate; if (showButtons) { ImGui.SameLine(); if (!bValue) { if (ImGui.Button("B")) { bValueV = oValueV; bValueInt = oValueInt; bValue = true; } } else { if (ImGui.Button("A/B")) { (bValueV, valueV) = (valueV, bValueV); (bValueInt, valueInt) = (valueInt, bValueInt); maybeWriteCurrentValue(); } ImGui.SameLine(); DrawValue(ref bValueV, ref bValueInt, width, true); ImGui.SameLine(); if (ImGui.Button("< B")) { bValue = false; } } Config config = getConfig(); List selectedOverrides = config.Overrides[selectedStage]; List? stageOverrides = maybeGetStageOverrides(config); // Deciding when to set globalOverrides copies the logic in OnImGuiRender(). // Selected: Set if selectedStage != globalStage. // Set: Always set. List? globalOverrides = null; if (selectedSet != "") { List setOverrides = config.Sets[selectedSet]; if (!overridesContainsParam(setOverrides, this)) { ImGui.SameLine(); if (ImGui.Button("▲")) { // Create the new override before unsetting any lower priority overrides. // Effectively, copy the current override value. Override ov = new Override(this, v4, i1); globalOverrides = config.Overrides[globalStage]; bool superseded = overridesContainsParam(externalOverrides.Values, this); if (!superseded) { BeginTransition(); if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, this))) { unsetIfOverridesContainsParam(globalOverrides, this); } ov.Set(); EndTransition(); } setOverrides.Add(ov); configState |= SetNotSaved; } if (ImGui.BeginItemTooltip()) { ImGui.Text($"Add to '{selectedSet}'"); ImGui.EndTooltip(); } } } else if (!overridesContainsParam(selectedOverrides, this)) { ImGui.SameLine(); if (ImGui.Button("▲")) { Override ov = new Override(this, v4, i1); if (selectedStage != globalStage) { globalOverrides = config.Overrides[globalStage]; } bool superseded = selectedOverrideSuperseded(config, globalOverrides, stageOverrides, this); if (!superseded) { BeginTransition(); if (globalOverrides != null) { unsetIfOverridesContainsParam(globalOverrides, this); } ov.Set(); EndTransition(); } selectedOverrides.Add(ov); if (selectedStage == globalStage) { rebuildConfig(config, selectedOverrides); } configState |= SelectedNotSaved; } if (ImGui.BeginItemTooltip()) { ImGui.Text($"Add to '{StageToString(selectedStage)}'"); ImGui.EndTooltip(); } } ImGui.SameLine(); DrawReferenceValue("Original", oValueV, oValueInt); } ImGui.PopID(); } } public class Parameter : Parameter where T : unmanaged { public Parameter(string name, nint offset, int mask, ParameterType type, T? min, T? max, T? step, int flags) : base(name, offset, mask, type, flags) { if (typeof(T) == typeof(float)) { this.stepf = (step != null) ? Convert.ToSingle(step) : 0.1f; this.minf = (min != null) ? Convert.ToSingle(min) : -Single.MaxValue; this.maxf = (max != null) ? Convert.ToSingle(max) : Single.MaxValue; } else if (typeof(T) == typeof(int) || typeof(T) == typeof(byte) || typeof(T) == typeof(bool)) { this.step = (step != null) ? Convert.ToInt32(step) : 0; if (typeof(T) == typeof(int)) { this.min = (min != null) ? Convert.ToInt32(min) : Int32.MinValue; this.max = (max != null) ? Convert.ToInt32(max) : Int32.MaxValue; } else if (typeof(T) == typeof(byte)) { this.min = (min != null) ? Convert.ToInt32(min) : 0x0; this.max = (max != null) ? Convert.ToInt32(max) : 0xFF; } else if (typeof(T) == typeof(bool)) { this.min = 0; this.max = 1; } } } } public static int pFlags(string s) { int flags = 0; for (int i = 0; i < s.Length; i++) { switch (s[i]) { case 'f': flags |= ParameterFlags.PerFrame; break; default: break; } } return flags; } public static (float?, float?, float?) pStep(float step) { return (null, null, step); } public static (float?, float?, float?) pMin(float min) { return (min, null, null); } public static (float?, float?, float?) pMax(float max) { return (null, max, null); } public static (float?, float?, float?) pMinStep(float min, float step) { return (min, null, step); } public static (float?, float?, float?) pMaxStep(float max, float step) { return (null, max, step); } public static (float?, float?, float?) pMinMaxStep(float min, float max, float step) { return (min, max, step); } public static (int?, int?, int?) pStep(int step) { return (null, null, step); } public static (int?, int?, int?) pMin(int min) { return (min, null, null); } public static (int?, int?, int?) pMax(int max) { return (null, max, null); } public static (int?, int?, int?) pMinStep(int min, int step) { return (min, null, step); } public static (int?, int?, int?) pMaxStep(int max, int step) { return (null, max, step); } public static (int?, int?, int?) pMinMaxStep(int min, int max, int step) { return (min, max, step); } public static (byte?, byte?, byte?) pStep(byte step) { return (null, null, step); } public static Parameter newParameter(string name, nint offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged { return new Parameter(name, offset, 0, type, m.min, m.max, m.step, 0); } public static Parameter P(string name, nint offset, ParameterType type, int flags, (T? min, T? max, T? step) m = default) where T : unmanaged { return new Parameter(name, offset, 0, type, m.min, m.max, m.step, flags); } public static Parameter newFrameParameter(string name, nint offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged { return new Parameter(name, offset, 0, type, m.min, m.max, m.step, ParameterFlags.PerFrame); } public static Parameter newParameterView(string name, nint offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged { return new Parameter(name, offset, 0, type, m.min, m.max, m.step, ParameterFlags.NoOverride); } public static Parameter newFlag(string name, nint offset, ParameterType type, int mask) where T : unmanaged { return new Parameter(name, offset, mask, type, null, null, null, 0); } private static Parameter? getParameterByName(string name) { foreach (Parameter param in allParameters) { if (param.Name == name) { return param; } } return null; } public class Override { public Parameter? Param = null; public string Name { get => (Param != null) ? Param.Name : ""; set => Param = getParameterByName(value); } public string Value { get { if (Param != null) { switch (Param.Type) { case ParameterType.BOOL: case ParameterType.FLAG: return BooleanToString(sValueInt != 0); case ParameterType.BYTE: case ParameterType.INT: case ParameterType.ALIGNED_INT: case ParameterType.SHADOW_RESOLUTION: return Int32ToString(sValueInt); case ParameterType.HEX: case ParameterType.COLOR: return Int32ToString(sValueInt, "X8"); case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: return SingleToString(sValueV.X); case ParameterType.VECTOR2: Vector2 sValueV2 = new Vector2(sValueV.X, sValueV.Y); return Vector2ToString(sValueV2); case ParameterType.VECTOR3: case ParameterType.COLOR_FACTOR: Vector3 sValueV3 = new Vector3(sValueV.X, sValueV.Y, sValueV.Z); return Vector3ToString(sValueV3); case ParameterType.VECTOR4: return Vector4ToString(sValueV); } } return ""; } set { if (Param == null) return; try { switch (Param.Type) { case ParameterType.BOOL: case ParameterType.FLAG: valueInt = ByteFlag(StringToBoolean(value)); break; case ParameterType.BYTE: case ParameterType.INT: case ParameterType.ALIGNED_INT: case ParameterType.SHADOW_RESOLUTION: valueInt = StringToInt32(value); break; case ParameterType.HEX: case ParameterType.COLOR: valueInt = StringToInt32(value, 16); break; case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: valueV.X = StringToSingle(value); break; case ParameterType.VECTOR2: Vector2 valueV2 = StringToVector2(value); valueV = new Vector4(valueV2.X, valueV2.Y, 0.0f, 0.0f); break; case ParameterType.VECTOR3: case ParameterType.COLOR_FACTOR: Vector3 valueV3 = StringToVector3(value); valueV = new Vector4(valueV3.X, valueV3.Y, valueV3.Z, 0.0f); break; case ParameterType.VECTOR4: valueV = StringToVector4(value); break; } } catch (FormatException) { Log.Warn($"Failed to parse value of '{Name}' override."); } catch (OverflowException) { Log.Warn($"Value of '{Name}' override too large."); } Param.AdjustValue(ref valueV, ref valueInt); Save(); } } // This could different from the Parameter's valueV/valueInt if this override is unset. private Vector4 valueV = default; private int valueInt = 0; private bool isSet = false; private Vector4 sValueV = default; // Saved value. private int sValueInt = 0; public Override() { } public Override(Parameter param, Vector4 v4, int i1) { Param = param; valueV = v4; valueInt = i1; Save(); } public void Save() { (sValueV, sValueInt) = (valueV, valueInt); } public void Set() { Assert(!isSet); isSet = true; if (Param != null) { Param.OverrideOn(true); Param.SetOverrideValue(valueV, valueInt); } } public void Unset() { Assert(isSet); isSet = false; if (Param != null) { Param.OverrideOff(); } } public void Draw(bool forSet, List? currentOverrides, List? stageOverrides, List? globalOverrides, bool superseded, float width) { Assert(!superseded == isSet); if (superseded) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (currentOverrides == null) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); } ImGui.PushItemWidth(width * 0.475f); bool beginCombo = ImGui.BeginCombo("##Parameters", (Param != null) ? Param.Name : "", ImGuiComboFlags.HeightLarge); if (superseded) { ImGui.PopStyleVar(); } Config config = getConfig(); if (beginCombo) { if (currentOverrides != null) { foreach (Parameter iterParam in allParameters) { if (iterParam.NoOverride) continue; if (overridesContainsParam(currentOverrides, iterParam)) continue; bool isSelected = Param == iterParam; if (ImGui.Selectable(iterParam.Name, isSelected)) { BeginTransition(); if (!superseded) { Unset(); if (Param != null) { if (forSet) { if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, Param))) { if (globalOverrides != null) { setIfOverridesContainsParam(globalOverrides, Param); } } } else { if (globalOverrides != null) { setIfOverridesContainsParam(globalOverrides, Param); } } } } Param = iterParam; (valueV, valueInt) = Param.GetValue(); Save(); if (forSet) { Assert(globalOverrides != null); superseded = overridesContainsParam(externalOverrides.Values, Param); if (!superseded) { if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, Param))) { unsetIfOverridesContainsParam(globalOverrides!, Param); } Set(); } configState |= SetNotSaved; } else { superseded = selectedOverrideSuperseded(config, globalOverrides, stageOverrides, Param); if (!superseded) { if (globalOverrides != null) { unsetIfOverridesContainsParam(globalOverrides, Param); } Set(); } configState |= SelectedNotSaved; } EndTransition(); Assert(!superseded == isSet); } if (isSelected) ImGui.SetItemDefaultFocus(); } } ImGui.EndCombo(); } ImGui.PopItemWidth(); if (currentOverrides == null) { ImGui.PopItemFlag(); } if (Param != null) { ImGui.SameLine(); if (superseded) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (Param.DrawValue(ref valueV, ref valueInt, width, false)) { if (!superseded) { Param.SetOverrideValue(valueV, valueInt); } } (Vector4 v4, int i1) = Param.GetValue(); bool valueDiffers = (valueV, valueInt) != (v4, i1); bool valueNotSaved = (valueV, valueInt) != (sValueV, sValueInt); if ((!Param.PendingUpdate() && valueDiffers && !superseded) || valueNotSaved) { ImGui.SameLine(); if (ImGui.Button("⇄")) { valueV = sValueV; valueInt = sValueInt; if (!superseded) { Param.SetOverrideValue(valueV, valueInt); } } } if (superseded) { ImGui.PopStyleVar(); } if (valueNotSaved) { ImGui.SameLine(); Param.DrawReferenceValue("Saved", sValueV, sValueInt); } if (Param.PendingUpdate()) { ImGui.SameLine(); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.7f); ImGui.Text("(Pending)"); ImGui.PopStyleVar(); if (ImGui.BeginItemTooltip()) { ImGui.Text("The type this parameter belongs to has no current object."); ImGui.EndTooltip(); } } else if (valueDiffers) { ImGui.SameLine(); string byLine = ""; if (superseded) { // Global is the lowest priority so "Set by 'Global'" never applies here. byLine = Param.SetByLine(config, stageOverrides, globalOverrides, forSet, false); } Param.DrawReferenceValue((byLine == "") ? "Current" : byLine, v4, i1); } } } } // Start of Parameter Definitions. private delegate void OnAreaChange(nint unknownPtr); private Hook? onAreaChange; private static bool InTransition = false; private static List DirtyParams = new List(); private static void BeginTransition() { Assert(!InTransition && DirtyParams.Count == 0); InTransition = true; } private static void EndTransition() { Assert(InTransition); InTransition = false; foreach (Parameter param in DirtyParams) { param.Resolve(); } DirtyParams.Clear(); } private static MtObject? sMhScene = null; private static MtObject? sMhMain = null; private static MtObject? sMhRender = null; private static Lights lights = new Lights(); private static Parameter hqMode = newParameter("HQ Mode", 0xE9A3, ParameterType.BOOL); private static Parameter[] lodParameters = { newParameter("LOD Bias 1", 0x21C, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Bias 2", 0x220, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Caster Bias", 0x1E4, ParameterType.FLOAT, pMin(0.0f)), // If platform is PS4/Xbox/PC it appears [2] is PC. newParameter("LOD Length Platform Bias[2]", 0x22C + (4 * 2), ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Pixel Size Platform Bias[2]", 0x244 + (4 * 2), ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Limit", 0xE898, ParameterType.INT, pStep(1)), newParameter("LOD Passthrough Culling Rate", 0x1F0, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Far Culling Fade Length Max", 0x20C, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Far Culling Fade Pixel Max", 0x210, ParameterType.FLOAT, pMin(0.0f)), newParameter("Speed Tree LOD Billboard Fade Range", 0x214, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Culling Length Bias", 0x224, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Culling Pixel Bias", 0x228, ParameterType.FLOAT, pMin(0.0f)) }; private static Parameter[] snowParameters = { newParameter("Snow Field 4 Global LOD Param", 0x5718, ParameterType.FLOAT) }; private static Parameter[] passthroughParameters = { newParameter("Passthrough Active", 0xE9A0, ParameterType.BOOL), newParameter("Passthrough Culling Active", 0xE9A2, ParameterType.BOOL), newParameterView("Passthrough Near", 0xE940, ParameterType.FLOAT), newParameterView("Passthrough Far", 0xE944, ParameterType.FLOAT), newParameter("Passthrough Near Alpha", 0xE948, ParameterType.FLOAT), newParameter("Passthrough Far Alpha", 0xE94C, ParameterType.FLOAT), newParameter("Passthrough Correct", 0xE950, ParameterType.FLOAT) }; private static Parameter[] shadowCascadeParameters = { newParameter("Primary Shadow HQ", 0xE5C0, ParameterType.BOOL), newParameter("Primary Shadow HQ (HQ)", 0xE5C2, ParameterType.BOOL), newParameter("Shadow Cascade 2Way Bias (HQ)", 0x58, ParameterType.FLOAT), newParameter("Primary Shadow Sample Num (HQ)", 0xE5CC, ParameterType.INT, pStep(1)) }; private delegate void EvalSceneParams(nint unknownPtr, nint unknownPtr2, int unknownInt, nint unknownPtr3); private Hook? evalSceneParams; private static Parameter[] broadAreaShadowParameters = { newParameter("Broad Area Shadow Enable", 0x5534, ParameterType.BOOL), newParameter("Broad Area Shadow Center", 0x5540 + 0x40, ParameterType.VECTOR3), newParameter("Broad Area Shadow Range", 0x5560 + 0x40, ParameterType.VECTOR3), newParameter("Broad Area Shadow Direction", 0x5550 + 0x40, ParameterType.VECTOR3, pStep(0.00025f)), newParameter("Broad Area Shadow Max LOD Level", 0x5524, ParameterType.FLOAT, pMin(0.0f)), newParameter("Broad Area Shadow Culling Size", 0x5528, ParameterType.FLOAT, pMin(0.0f)), newParameter("Broad Area Shadow Precision", 0x5518, ParameterType.INT, pMinMaxStep(0, 2, 1)) }; private static Parameter[] contactShadowParameters = { newParameter("Fake Light Intensity", 0xEC40, ParameterType.FLOAT), // MonsterHunterWorld.exe+1B18711 - mov [rbx+0000EC44],3F800000 (Other unknown reference). newParameter("Fake Light Intensity (Gameplay)", lton(0x141B1A19F) + 0x6, ParameterType.PATCH_FLOAT), newParameter("Fake Light Blend", lton(0x141CDE0CE) + 0x6, ParameterType.PATCH_FLOAT), newParameter("Force Disable Contact Shadows", 0xB4ED, ParameterType.BOOL), newParameter("Facial Contact Shadows Enabled", 0xB4EE, ParameterType.BOOL), newParameter("Facial Contact Shadows Enable Noise", 0xB501, ParameterType.BOOL), newParameter("Contact Shadow Accept Maximum Length", 0xB4F8, ParameterType.FLOAT), newParameter("Contact Shadow Accept Minimum Length", 0xB4FC, ParameterType.FLOAT) }; private delegate void UpdateCapsuleAOParams(nint sceneObjectInternal); private Hook? updateCapsuleAoParams; private static Parameter[] capsuleLightParameters = { newParameter("Force Disable Capsule AO", 0xB503, ParameterType.BOOL), newParameter("Capsule AO Enabled", 0xB502, ParameterType.BOOL), newParameter("Capsule AO Distance Fall Coef", 0xE5B4, ParameterType.FLOAT, pStep(0.0001f)), newParameter("Capsule AO Light Channel Mask", 0xE5BC, ParameterType.INT, pStep(1)), newParameter("Capsule Light Dir XZY", 0xE548, ParameterType.VECTOR3), newParameterView("Capsule Light W Dir", 0xE560, ParameterType.VECTOR3), newParameter("Capsule Light Angle", 0xE5B0, ParameterType.FLOAT), newParameter("Capsule AO Intensity", 0xE5B8, ParameterType.FLOAT, pStep(0.00025f)) }; private delegate void UpdateSSAOParams(nint stackOffset); private Hook? updateSSAOParams; private static Parameter[] ssaoParameters = { newParameter("SSAO Depth Bias", 0xB4B0, ParameterType.FLOAT, pStep(0.00001f)), newParameter("SSAO Sloped Depth Bias", 0xB4B4, ParameterType.FLOAT, pStep(0.00001f)), newParameter("SSAO Max Depth Bias", 0xB4B8, ParameterType.FLOAT, pStep(0.00001f)), newParameter("SSAO Dispersion", 0xB4BC, ParameterType.FLOAT), newParameter("SSAO Effect", 0xB430, ParameterType.FLOAT, pStep(0.001f)), newParameter("SSAO Effect GI", 0xB434, ParameterType.FLOAT, pStep(0.001f)), newParameter("SSAO Depth Difference", 0xB4C0, ParameterType.FLOAT, pStep(0.01f)), newParameter("SSAO Samples Per Pixel", 0xB4C4, ParameterType.FLOAT, pStep(1.0f)), newParameter("SSAO Max Sample Num", 0xB4C8, ParameterType.INT, pStep(1)), newParameter("SSAO Max Sample Num (HQ)", 0xB4D0, ParameterType.INT, pStep(1)), newParameter("SSAO Radius", 0xB4D4, ParameterType.FLOAT), newParameter("SSAO Bias", 0xB4D8, ParameterType.FLOAT, pStep(0.0001f)), newParameter("SSAO Intensity", 0xB4E0, ParameterType.FLOAT, pMin(0.0f)), newParameter("SSAO Use HiZ", 0xB4E5, ParameterType.BOOL), newParameter("SSAO Edge Atten Rate", 0xB4DC, ParameterType.FLOAT, pStep(0.001f)) }; private delegate void UpdateSSLRParams(nint stackOffset); private Hook? updateSSLRParams; private static Parameter[] sslrParameters = { newParameter("SSLR Loop Count", 0xE628, ParameterType.INT, pStep(1)), newParameter("SSLR Loop Count Factor for CBR", 0xE62C, ParameterType.FLOAT), newParameter("SSLR Eliminate Depth", 0xE630, ParameterType.FLOAT), newParameter("SSLR Accurate Threshold", 0xE644, ParameterType.FLOAT, pStep(0.001f)), newParameter("SSLR Accurate Threshold (HQ)", 0xE64C, ParameterType.FLOAT, pStep(0.001f)), newParameter("SSLR Dither Radius", 0xE634, ParameterType.FLOAT), newParameter("SSLR Importance Bias", 0xE638, ParameterType.FLOAT), newParameter("SSLR Mip Scale", 0xE63C, ParameterType.FLOAT), newParameter("SSLR Mip Bias", 0xE640, ParameterType.FLOAT), newParameter("SSLR Dither Resolve", 0xB43F, ParameterType.BOOL), newParameter("SSLR Edge Atten Rate", 0xB428, ParameterType.FLOAT), newParameter("SSLR Mip 0 Count Threshold", 0xB438, ParameterType.INT, pStep(1)), newParameter("SSLR Depth Eliminate Rate", 0xB42C, ParameterType.FLOAT), newParameter("SSLR Use Mipmap", 0xE650, ParameterType.BOOL), newParameter("SSLR GBuffer Jitter", 0xB440, ParameterType.BOOL), newParameter("SSLR Intensity", 0xB424, ParameterType.FLOAT) }; private static Parameter shadowResParameter = newParameter("Shadow Resolution Multiplier", 0, ParameterType.SHADOW_RESOLUTION, pStep(1)); private delegate void UpdateShadowParams(nint shadowParams, nint stackOffset); private delegate void StaticShadowParams(nint shadowParamsStatic, nint shadowObjectInternal); private Hook? updateShadowParams; private Hook? staticShadowParams; private static Parameter[] shadowParameters1 = { }; // This is actually an InfiniteLight object. // Non-PerFrame parameters will be written directly to the light object. private static Parameter[] shadowParameters2 = { newFrameParameter("Sunlight Group", 0x14, ParameterType.INT, pMinMaxStep(1, 255, 1)), newFrameParameter("Sunlight Direction", 0x110, ParameterType.VECTOR3, pStep(0.00025f)), newFrameParameter("Sunlight Color", 0xE0, ParameterType.COLOR_FACTOR, pMaxStep(1.0f, 0.001f)), newFrameParameter("Sunlight Intensity", 0xF4, ParameterType.FLOAT), newParameter("Sunlight Min Roughness", 0x58C, ParameterType.FLOAT, pStep(0.0025f)), // +0xFC in params but is never read. newFrameParameter("Sunlight Is Primary", 0x1D, ParameterType.BOOL), newFrameParameter("Do Volumetric", 0x1F, ParameterType.BOOL), newFrameParameter("Shadow Cast", 0x23, ParameterType.BOOL), newFrameParameter("Shadow Map Size", 0x30, ParameterType.ALIGNED_INT, pMinStep(0, 128)), // 0 = 64x64 no multiplier @TODO: Verify in the code. newFrameParameter("Shadow Near Clip Distance", 0x28, ParameterType.FLOAT), newFrameParameter("Shadow Cascade Mode", 0x174, ParameterType.INT, pMinMaxStep(0, 3, 1)), newFrameParameter("Shadow Cascade 2Way Bias", 0x17C, ParameterType.FLOAT), newFrameParameter("Shadow Distance", 0x6C, ParameterType.FLOAT, pStep(5.0f)), newFrameParameter("Shadow Backforward Distance", 0x7C, ParameterType.FLOAT, pStep(5.0f)), newFrameParameter("Shadow Is Fixed Fov Mode", 0x1F9, ParameterType.BOOL), newFrameParameter("Shadow Fov", 0x1F4, ParameterType.FLOAT, pStep(0.0001f)), newFrameParameter("Shadow Is Discretization Fov Mode", 0x205, ParameterType.BOOL), newFrameParameter("Shadow Discretization Angle", 0x200, ParameterType.FLOAT, pStep(0.0001f)), newFrameParameter("Shadow Distribution", 0x74, ParameterType.FLOAT, pStep(0.001f)), newFrameParameter("Cascade Manual Split", 0x4D, ParameterType.BOOL), newFrameParameter("Cascade Manual Split Distance[0]", 0x54, ParameterType.FLOAT), newFrameParameter("Cascade Manual Split Distance[1]", 0x5C, ParameterType.FLOAT), newFrameParameter("Cascade Manual Split Distance[2]", 0x64, ParameterType.FLOAT), newFrameParameter("Shadow Sloped Depth Bias", 0x40, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Shadow Depth Bias", 0x38, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Shadow Max Depth Bias", 0x48, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Individual Shadow Bias", 0x81, ParameterType.BOOL), newFrameParameter("Cascade Depth Bias[0]", 0x88, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Sloped Depth Bias[0]", 0xA0, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Max Depth Bias[0]", 0xB8, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Depth Bias[1]", 0x90, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Sloped Depth Bias[1]", 0xA8, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Max Depth Bias[1]", 0xC0, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Depth Bias[2]", 0x98, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Sloped Depth Bias[2]", 0xB0, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Max Depth Bias[2]", 0xC8, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Broad Area Shadow Depth Bias", 0x1C4, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Broad Area Shadow Sloped Depth Bias", 0x1CC, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Broad Area Shadow Max Depth Bias", 0x1D4, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Projection Scale", 0x134, ParameterType.VECTOR3), newFrameParameter("Projection Offset Speed", 0x144, ParameterType.FLOAT), newFrameParameter("Projection Up", 0x160, ParameterType.VECTOR3), newFrameParameter("Primary Shadow Sample Num", 0x1DC, ParameterType.INT, pStep(1)), newFrameParameter("Primary Shadow Radius", 0x1EC, ParameterType.FLOAT, pStep(0.0001f)) }; private delegate nint CreateLightingObject(); private delegate nint DestroyLightingObject(nint lightingObjectInternal, int unknownInt); private Hook? createLightingObject; private Hook? destroyLightingObject; private nint lightingObject = 0x0; private delegate void UpdateLightingParameters(nint stackOffset, nint lightingObjectInternal); private Hook? updateLightingParams; private Hook? updateLutBlend; private static LUT luts = new LUT(); private static Parameter[] lightingParameters = { newParameter("Light Tone Map Type", 0x16C, ParameterType.INT, pMinMaxStep(1, 6, 1)), newParameter("Light Compute Luminance", 0x170, ParameterType.BOOL), newParameter("Light Luminance Version", 0x168, ParameterType.BOOL), newParameter("Light Shoulder Strength", 0x174, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light Linear Strength", 0x178, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light Linear Angle", 0x17C, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light Toe Strength", 0x180, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light Toe Num", 0x184, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light Toe Denum", 0x188, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light White Point", 0x18C, ParameterType.FLOAT, pStep(0.01f)), newFrameParameter("Light LUT Blend", 0x1C8, ParameterType.FLOAT, pMinMaxStep(0.0f, 1.0f, 0.01f)), newParameter("Light Vfx LUT Blend", 0x1D0, ParameterType.FLOAT, pMinMaxStep(0.0f, 1.0f, 0.01f)), newParameter("Light Enable Color Grading", 0x1D8, ParameterType.BOOL), newParameter("Light Is Linear To PQ", 0x1A1, ParameterType.BOOL), newParameter("Light Is PQ To Linear", 0x1A0, ParameterType.BOOL), newParameter("Volume Dispersion", 0x19C, ParameterType.FLOAT), newParameter("Volume Edge Sharpness", 0x198, ParameterType.FLOAT, pStep(0.00001f)), newParameter("Volume Downsample", 0x210, ParameterType.BOOL) }; private static Parameter[] brightnessParameters = { newParameter("Gamma Correct", 0x1C8, ParameterType.FLOAT, pStep(0.001f)), newParameter("Gamma Correct EX", 0x1CC, ParameterType.FLOAT, pStep(0.001f)), newParameterView("Hdr Output", 0x21A, ParameterType.BOOL), newParameter("Hdr Output White Level", 0x21C, ParameterType.FLOAT, pStep(0.05f)), newParameter("Hdr Output Gamut Mapping Ratio", 0x220, ParameterType.FLOAT, pStep(0.001f)), newParameter("Hdr Output Gamma", 0x224, ParameterType.FLOAT, pStep(0.001f)), newParameter("Hdr Is Gui Hdr Gamma", 0x228, ParameterType.BOOL) }; private static MtObject? sLightProbes = null; private static Parameter[] sLightProbesParameters = { newParameter("Debug Mode", 0x98, ParameterType.INT, pStep(1)) }; private delegate nint CreateBloomObject(); private delegate nint CreateBloomObject2(nint unknownPtr); private delegate nint DestroyBloomObject(nint bloomObjectInternal, int unknownInt); private Hook? createBloomObject; private Hook? createBloomObject2; private Hook? destroyBloomObject; private nint bloomObject = 0x0; private static Parameter[] bloomParameters = { // Disabling 'Draw' is a bad way to disable bloom. It breaks the filter on initial load which // can be seen when returning to title or opening a guild card for the first time. Setting 'Bloom Renormalize' // to 0.0 or 'Bloom Reduction Resolution' to -1 can both effectively disable bloom without that issue. //newFlag("Bloom Enabled", 0x14, ParameterType.FLAG, (1 << 1)), // Draw newParameter("Bloom Threshold", 0x204, ParameterType.FLOAT), newParameter("Bloom Renormalize", 0x208, ParameterType.FLOAT, pMin(0.0f)), newParameter("Bloom Color", 0x200, ParameterType.COLOR), newParameter("Bloom Downsample Count", 0x168, ParameterType.INT, pMinMaxStep(0, 8, 1)), newParameter("Bloom Reduction Resolution", 0x16C, ParameterType.INT, pStep(1)), newParameter("Bloom Is SRGB Gamut", 0x214, ParameterType.BOOL), newParameter("Bloom Compute Luminance", 0x241, ParameterType.BOOL) }; private delegate nint CreateDofObject(); private delegate nint CreateDofObject2(nint unknownPtr); private delegate nint DestroyDofObject(nint dofObjectInternal, int unknownInt); private Hook? createDofObject; private Hook? createDofObject2; private Hook? destroyDofObject; private delegate void UpdateDofParams(nint unknownPtr); private Hook? updateDofParams; private nint dofObject = 0x0; private static Parameter[] dofParameters = { newParameter("Dof Enabled", 0x1DD, ParameterType.BOOL), newParameter("Dof New Version", 0x1DC, ParameterType.BOOL), newFrameParameter("Dof F Number", 0x1A0, ParameterType.FLOAT, pMinStep(0.0f, 0.001f)), newFrameParameter("Dof Sensor Size", 0x1A4, ParameterType.FLOAT, pStep(0.001f)), newFrameParameter("Dof Focus Distance", 0x1A8, ParameterType.FLOAT, pStep(0.2f)), newParameter("Dof Near Coef", 0x1CC, ParameterType.FLOAT, pStep(0.001f)), newParameter("Dof Far Coef", 0x1C8, ParameterType.FLOAT, pStep(0.001f)), newParameter("Dof Near Enable", 0x1DE, ParameterType.BOOL), newParameter("Dof Far Enable", 0x1DF, ParameterType.BOOL), newParameter("Dof Debug Draw", 0x1E0, ParameterType.BOOL), newParameter("Dof Radius", 0x1D0, ParameterType.FLOAT, pStep(0.001f)), newParameter("Dof Depth Scale Foreground", 0x1D4, ParameterType.FLOAT, pMinStep(0.0f, 0.0001f)), newParameter("Dof Aspect", 0x1D8, ParameterType.FLOAT, pMinStep(0.0f, 0.001f)), newFrameParameter("Vignetting Enabled", 0x200, ParameterType.BOOL), newFrameParameter("Vignetting Ellipse", 0x201, ParameterType.BOOL), newFrameParameter("Vignetting Ellipticity", 0x204, ParameterType.FLOAT, pStep(0.01f)), newFrameParameter("Vignetting Offset", 0x1F8, ParameterType.FLOAT, pStep(0.001f)), newFrameParameter("Vignetting Pow", 0x1FC, ParameterType.FLOAT, pStep(0.01f)), newFrameParameter("Vignetting Color", 0x208, ParameterType.COLOR) }; private delegate nint CreateMotionBlurObject(); private delegate nint CreateMotionBlurObject2(nint unknownPtr); private delegate nint DestroyMotionBlurObject(nint motionBlurObjectInternal, int unknownInt); private Hook? createMotionBlurObject; private Hook? createMotionBlurObject2; private Hook? destroyMotionBlurObject; private delegate void UpdateMotionBlurParams(nint unknownPtr, nint unknownPtr2); private Hook? updateMotionBlurParams; private nint motionBlurObject = 0x0; private static Parameter[] motionBlurParameters = { newParameter("Motion Blur Type", 0x168, ParameterType.INT, pStep(1)), newParameter("Motion Blur Sample Num", 0x180, ParameterType.INT, pMinStep(0, 1)), newFrameParameter("Motion Blur Shutter Speed", 0x184, ParameterType.FLOAT, pMinStep(0.0f, 0.001f)), newParameter("Motion Blur Fur Shutter Speed", 0x188, ParameterType.FLOAT, pStep(0.001f)), newParameter("Motion Blur Threshold", 0x18C, ParameterType.FLOAT, pMinStep(0.0f, 0.01f)) }; private delegate nint CreateSimpleSkyObject(nint unknownPtr); private delegate nint DestroySimpleSkyObject(nint simpleSkyObjectInternal, int unknownInt); private delegate void RenderSimpleSky(nint simpleSkyObjectInternal, nint unknownPtr); private Hook? createSimpleSkyObject; private Hook? destroySimpleSkyObject; private Hook? renderSimpleSky; private nint simpleSkyObject = 0x0; private static Parameter[] simpleSkyParameters = { newParameter("Sky Global Intensity", 0x270, ParameterType.COLOR_FACTOR, pStep(0.001f)), newParameter("Sky Global Cloud Speed", 0x3A8, ParameterType.FLOAT), newParameter("Sky Top Cloud UV Scale[0]", 0x3D0, ParameterType.FLOAT), newParameter("Sky Top Cloud UV Scale[1]", 0x3D8, ParameterType.FLOAT), newParameter("Sky Blend", 0x3B0, ParameterType.FLOAT), newParameter("Sky Water Reflection Factor", 0x7A0, ParameterType.COLOR_FACTOR, pStep(0.001f)), newParameter("Sky Fog", 0x3B4, ParameterType.BOOL), newParameter("Sky Fog Blend", 0x3B8, ParameterType.FLOAT), newParameter("Sky DeGamma", 0x3BC, ParameterType.BOOL), newParameter("Sky DeGamma Value", 0x3C0, ParameterType.FLOAT), newParameter("Sky Cloud Highlight Intensity", 0x3C4, ParameterType.FLOAT), newParameter("Sky Cloud Shadow Intensity", 0x3C8, ParameterType.FLOAT), newParameter("Sky Cloud Contrast", 0x3CC, ParameterType.FLOAT), newParameter("Sky Sun Map Factor", 0x280, ParameterType.COLOR_FACTOR), newParameter("Sky Sun Size", 0x2A8, ParameterType.FLOAT), newParameter("Sky Sun Rotation Y", 0x3A4, ParameterType.FLOAT), newParameter("Sky Sun Height", 0x39C, ParameterType.FLOAT, pStep(0.0001f)), newParameter("Sky Sun Light Mask Height Adjustment", 0x3A0, ParameterType.FLOAT), newParameter("Sky Sun Light Mask Transparency", 0x2CC, ParameterType.FLOAT), newParameter("Sky Sun Light Mask UV Scale", 0x2D0, ParameterType.FLOAT), newParameter("Sky Sun Bloom Intensity", 0x2AC, ParameterType.FLOAT), newParameter("Sky Sun Bloom Transparency Coefficient", 0x2B0, ParameterType.FLOAT), newParameter("Sky Sun Bloom Threshold", 0x2B4, ParameterType.FLOAT), newParameter("Sky Sun Bloom Dispersion", 0x2B8, ParameterType.FLOAT), newParameter("Sky Sun Bloom Downsample Count", 0x2C0, ParameterType.FLOAT), newParameter("Sky Sun Bloom Reduction Resolution", 0x2C4, ParameterType.FLOAT), newParameter("Sky Sun Light Sky Color", 0x2E0, ParameterType.COLOR_FACTOR, pStep(0.001f)), newParameter("Sky Sun Light Sky Mask UV Scale", 0x2F0, ParameterType.FLOAT), newParameter("Sky Base Fov", 0x2BC, ParameterType.FLOAT), newParameter("Sky Base Map Factor", 0x3E0, ParameterType.COLOR_FACTOR, pStep(0.001f)), newParameter("Sky Base Side Cloud UV Offset", 0x3F0, ParameterType.FLOAT), newParameter("Sky Base Top Cloud0 UV Offset", 0x3F8, ParameterType.FLOAT), newParameter("Sky Base Top Cloud1 UV Offset", 0x400, ParameterType.FLOAT), newParameter("Sky Base Sun Cloud Highlight Color[0]", 0x440, ParameterType.COLOR_FACTOR), newParameter("Sky Base Sun Cloud Shadow Color[0]", 0x480, ParameterType.COLOR_FACTOR), newParameter("Sky Base Middle Highlight Color[0]", 0x4C0, ParameterType.COLOR_FACTOR), newParameter("Sky Base Middle Shadow Color[0]", 0x500, ParameterType.COLOR_FACTOR), newParameter("Sky Base Background Highlight Color[0]", 0x540, ParameterType.COLOR_FACTOR), newParameter("Sky Base Background Shadow Color[0]", 0x580, ParameterType.COLOR_FACTOR), newParameter("Sky Base Cloud Speed[0]", 0x408, ParameterType.VECTOR2), newParameter("Sky Base Cloud Alpha[0]", 0x428, ParameterType.FLOAT), newParameter("Sky Base Sun Cloud Highlight Color[1]", 0x450, ParameterType.COLOR_FACTOR), newParameter("Sky Base Sun Cloud Shadow Color[1]", 0x490, ParameterType.COLOR_FACTOR), newParameter("Sky Base Middle Highlight Color[1]", 0x4D0, ParameterType.COLOR_FACTOR), newParameter("Sky Base Middle Shadow Color[1]", 0x510, ParameterType.COLOR_FACTOR), newParameter("Sky Base Background Highlight Color[1]", 0x550, ParameterType.COLOR_FACTOR), newParameter("Sky Base Background Shadow Color[1]", 0x590, ParameterType.COLOR_FACTOR), newParameter("Sky Base Cloud Speed[1]", 0x410, ParameterType.VECTOR2), newParameter("Sky Base Cloud Alpha[1]", 0x42C, ParameterType.FLOAT), newParameter("Sky Base Sun Cloud Highlight Color[2]", 0x460, ParameterType.COLOR_FACTOR), newParameter("Sky Base Sun Cloud Shadow Color[2]", 0x4A0, ParameterType.COLOR_FACTOR), newParameter("Sky Base Middle Highlight Color[2]", 0x4E0, ParameterType.COLOR_FACTOR), newParameter("Sky Base Middle Shadow Color[2]", 0x520, ParameterType.COLOR_FACTOR), newParameter("Sky Base Background Highlight Color[2]", 0x560, ParameterType.COLOR_FACTOR), newParameter("Sky Base Background Shadow Color[2]", 0x5A0, ParameterType.COLOR_FACTOR), newParameter("Sky Base Cloud Speed[2]", 0x418, ParameterType.VECTOR2), newParameter("Sky Base Cloud Alpha[2]", 0x430, ParameterType.FLOAT), newParameter("Sky Base Sun Cloud Highlight Color[3]", 0x470, ParameterType.COLOR_FACTOR), newParameter("Sky Base Sun Cloud Shadow Color[3]", 0x4B0, ParameterType.COLOR_FACTOR), newParameter("Sky Base Middle Highlight Color[3]", 0x4F0, ParameterType.COLOR_FACTOR), newParameter("Sky Base Middle Shadow Color[3]", 0x530, ParameterType.COLOR_FACTOR), newParameter("Sky Base Background Highlight Color[3]", 0x570, ParameterType.COLOR_FACTOR), newParameter("Sky Base Background Shadow Color[3]", 0x5B0, ParameterType.COLOR_FACTOR), newParameter("Sky Base Cloud Speed[3]", 0x420, ParameterType.VECTOR2), newParameter("Sky Base Cloud Alpha[3]", 0x434, ParameterType.FLOAT), newParameter("Sky Blend Base Map Factor", 0x5C0, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Side Cloud UV Offset", 0x5D0, ParameterType.FLOAT), newParameter("Sky Blend Top Cloud0 UV Offset", 0x5D8, ParameterType.FLOAT), newParameter("Sky Blend Top Cloud1 UV Offset", 0x5E0, ParameterType.FLOAT), newParameter("Sky Blend Sun Cloud Highlight Color[0]", 0x620, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Sun Cloud Shadow Color[0]", 0x660, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Middle Highlight Color[0]", 0x6A0, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Middle Shadow Color[0]", 0x6E0, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Background Highlight Color[0]", 0x720, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Background Shadow Color[0]", 0x760, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Cloud Speed[0]", 0x5E8, ParameterType.VECTOR2), newParameter("Sky Blend Cloud Alpha[0]", 0x608, ParameterType.FLOAT), newParameter("Sky Blend Sun Cloud Highlight Color[1]", 0x630, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Sun Cloud Shadow Color[1]", 0x670, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Middle Highlight Color[1]", 0x6B0, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Middle Shadow Color[1]", 0x6F0, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Background Highlight Color[1]", 0x730, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Background Shadow Color[1]", 0x770, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Cloud Speed[1]", 0x5F0, ParameterType.VECTOR2), newParameter("Sky Blend Cloud Alpha[1]", 0x60C, ParameterType.FLOAT), newParameter("Sky Blend Sun Cloud Highlight Color[2]", 0x640, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Sun Cloud Shadow Color[2]", 0x680, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Middle Highlight Color[2]", 0x6C0, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Middle Shadow Color[2]", 0x700, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Background Highlight Color[2]", 0x740, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Background Shadow Color[2]", 0x780, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Cloud Speed[2]", 0x5F8, ParameterType.VECTOR2), newParameter("Sky Blend Cloud Alpha[2]", 0x610, ParameterType.FLOAT), newParameter("Sky Blend Sun Cloud Highlight Color[3]", 0x650, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Sun Cloud Shadow Color[3]", 0x690, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Middle Highlight Color[3]", 0x6D0, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Middle Shadow Color[3]", 0x710, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Background Highlight Color[3]", 0x750, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Background Shadow Color[3]", 0x790, ParameterType.COLOR_FACTOR), newParameter("Sky Blend Cloud Speed[3]", 0x600, ParameterType.VECTOR2), newParameter("Sky Blend Cloud Alpha[3]", 0x614, ParameterType.FLOAT), newParameter("Sky Starry Sky Map Factor", 0x290, ParameterType.COLOR_FACTOR, pStep(0.001f)), }; private delegate nint CreateFogObject(); private delegate nint CreateFogObject2(nint unknownPtr); private delegate nint DestroyFogObject(nint fogObjectInternal, int unknownInt); private Hook? createFogObject; private Hook? createFogObject2; private Hook? destroyFogObject; private delegate void UpdateFogParams(nint unknownPtr, int unknownInt); private Hook? updateFogParams; private nint fogObject = 0x0; private static Parameter[] fogParameters = { newParameter("Fog Mip Fog Intensity", 0x2C0, ParameterType.FLOAT, pStep(0.001f)), newParameter("Fog Mip Fog Color", 0x330, ParameterType.COLOR_FACTOR, pStep(0.001f)) }; private delegate nint CreateWaterWaveObject(nint unknownPtr); private delegate nint DestroyWaterWaveObject(nint waterWaveObjectInternal, int unknownInt); private Hook? createWaterWaveObject; private Hook? destroyWaterWaveObject; private nint waterWaveObject = 0x0; private static Parameter[] waterWaveParameters = { newParameter("Water Murkiness", 0x40C, ParameterType.FLOAT), newParameter("Water Color", 0x404, ParameterType.COLOR), newParameter("Water Scattering Intensity", 0x408, ParameterType.FLOAT, pStep(0.001f)) }; private delegate nint CreateFXAAObject(); private delegate nint CreateFXAAObject2(nint unknownPtr); private delegate nint DestroyFXAAObject(nint fxaaObjectInternal, int unknownInt); private Hook? createFXAAObject; private Hook? createFXAAObject2; private Hook? destroyFXAAObject; private nint fxaaObject = 0x0; private static Parameter[] fxaaParameters = { newParameter("FXAA Subpix", 0x178, ParameterType.FLOAT, pMinMaxStep(0.0f, 1.0f, 0.005f)), newParameter("FXAA Edge Threshold", 0x17C, ParameterType.FLOAT, pMinMaxStep(0.0f, 1.0f, 0.001f)), newParameter("FXAA Edge Threshold Min", 0x180, ParameterType.FLOAT, pMinMaxStep(0.0f, 1.0f, 0.001f)) }; private delegate nint CreateTAAObject(); private delegate nint CreateTAAObject2(nint unknownPtr); private delegate nint DestroyTAAObject(nint taaObjectInternal, int unknownInt); private Hook? createTAAObject; private Hook? createTAAObject2; private Hook? destroyTAAObject; private nint taaObject = 0x0; private static Parameter[] taaParameters = { newParameter("TAA Reprojection", 0x19D, ParameterType.BOOL), newParameter("TAA Jitter Enable", 0x184, ParameterType.BOOL), newParameter("TAA Blend Rate", 0x180, ParameterType.FLOAT), newParameter("TAA Variance Gamma", 0x194, ParameterType.FLOAT), newParameter("TAA Sharpen", 0x187, ParameterType.BOOL), newParameter("TAA Sharpen Amount", 0x198, ParameterType.FLOAT), newParameter("TAA Sharpend Ignore Edges", 0x19C, ParameterType.BOOL) }; private static Parameter[] allParameters = new Parameter[]{ hqMode } .Concat(lodParameters) .Concat(snowParameters) .Append(shadowResParameter) .Concat(shadowParameters2) .Concat(shadowCascadeParameters) .Concat(broadAreaShadowParameters) .Concat(contactShadowParameters) .Concat(capsuleLightParameters) .Concat(ssaoParameters) .Concat(sslrParameters) .Concat(passthroughParameters) .Concat(lightingParameters) .Concat(brightnessParameters) .Concat(sLightProbesParameters) .Concat(bloomParameters) .Concat(dofParameters) .Concat(motionBlurParameters) .Concat(simpleSkyParameters) .Concat(fogParameters) .Concat(waterWaveParameters) .Concat(fxaaParameters) .Concat(taaParameters) .ToArray(); private static int currentShadowResMultiplier = 1; private static NativeAction setShadowQuality; private static Patch shadowRes1_2x; private static Patch shadowRes1_3x; private static Patch shadowRes1_4x; private static Patch shadowRes1_5x; #if OVERSIZED_SHADOW_MAP private static Patch shadowRes1_6x; #endif private static Patch shadowRes1_1; private static Patch shadowRes1_2; private static Patch shadowRes1_3; private static Patch shadowRes1_4; private static nint broadAreaShadowRes1_Addr; private static nint broadAreaShadowRes2_Addr; private static void setBroadAreaShadowResolution(int multiplier) { bool reset = multiplier == 1; int unused1Value = Math.Clamp(0x1000 * multiplier, 0x1000, 0x4000); int unused2Value = Math.Clamp(0xC00 * multiplier, 0xC00, 0x4000); int highValue = Math.Clamp((reset ? 0x800 : 0xC00) * multiplier, reset ? 0x800 : 0xC00, 0x4000); int midValue = Math.Clamp( (reset ? 0x600 : 0x800) * multiplier, reset ? 0x600 : 0x800, 0x4000); int lowValue = Math.Clamp( (reset ? 0x400 : 0x600) * multiplier, reset ? 0x400 : 0x600, 0x4000); new Patch(broadAreaShadowRes1_Addr + 0x45, BitConverter.GetBytes(unused1Value)).Enable(); new Patch(broadAreaShadowRes1_Addr + 0x45 + 0xB, BitConverter.GetBytes(unused2Value)).Enable(); new Patch(broadAreaShadowRes1_Addr + 0x45 + 0x16, BitConverter.GetBytes(highValue)).Enable(); new Patch(broadAreaShadowRes1_Addr + 0x45 + 0x21, BitConverter.GetBytes(midValue)).Enable(); new Patch(broadAreaShadowRes1_Addr + 0x45 + 0x2C, BitConverter.GetBytes(lowValue)).Enable(); // Likely unused. new Patch(broadAreaShadowRes2_Addr + 0x3A, BitConverter.GetBytes(unused1Value)).Enable(); new Patch(broadAreaShadowRes2_Addr + 0x3A + 0xB, BitConverter.GetBytes(unused2Value)).Enable(); new Patch(broadAreaShadowRes2_Addr + 0x3A + 0x16, BitConverter.GetBytes(highValue)).Enable(); new Patch(broadAreaShadowRes2_Addr + 0x3A + 0x21, BitConverter.GetBytes(midValue)).Enable(); new Patch(broadAreaShadowRes2_Addr + 0x3A + 0x2C, BitConverter.GetBytes(lowValue)).Enable(); } private static void shadowResEnable(int multiplier) { Assert(multiplier != 1); if (multiplier == currentShadowResMultiplier) { return; } switch (currentShadowResMultiplier) { case 1: shadowRes1_1.Enable(); shadowRes1_2.Enable(); shadowRes1_3.Enable(); shadowRes1_4.Enable(); break; case 2: shadowRes1_2x.Disable(); break; case 3: shadowRes1_3x.Disable(); break; case 4: shadowRes1_4x.Disable(); break; case 5: shadowRes1_5x.Disable(); break; #if OVERSIZED_SHADOW_MAP case 6: shadowRes1_6x.Disable(); break; #endif } switch (multiplier) { case 2: shadowRes1_2x.Enable(); break; case 3: shadowRes1_3x.Enable(); break; case 4: shadowRes1_4x.Enable(); break; case 5: shadowRes1_5x.Enable(); break; #if OVERSIZED_SHADOW_MAP case 6: shadowRes1_6x.Enable(); break; #endif } setBroadAreaShadowResolution(multiplier); currentShadowResMultiplier = multiplier; setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); } private static void shadowResDisable() { if (currentShadowResMultiplier == 1) { return; } switch (currentShadowResMultiplier) { case 2: shadowRes1_2x.Disable(); break; case 3: shadowRes1_3x.Disable(); break; case 4: shadowRes1_4x.Disable(); break; case 5: shadowRes1_5x.Disable(); break; #if OVERSIZED_SHADOW_MAP case 6: shadowRes1_6x.Disable(); break; #endif } shadowRes1_1.Disable(); shadowRes1_2.Disable(); shadowRes1_3.Disable(); shadowRes1_4.Disable(); setBroadAreaShadowResolution(1); currentShadowResMultiplier = 1; setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); } #if SHADER_FEATURES private bool fullResSSLR = false; #endif private Patch ssrRes1; private Patch ssrRes2; private Patch ssrRes3; private Patch ssrShr1; private Patch ssrShr2; private Patch ssrShr3; private Patch ssrShr4; private Patch ssrShr5; private Patch ssrShr6; private Patch ssrShr7; private Patch ssrShr8; private Patch ssrSars1; private Patch ssrSars2; private Patch ssrSars3; private Patch ssrSars4; private Patch ssrSars5; private Patch ssrSars6; private Patch ssrSars7; private Patch ssrSars8; private Patch ssrSars9; private Patch ssrSars10; private Patch ssrSars11; private Patch ssrSars12; private Patch ssrSars13; private Patch ssrSars14; private Patch ssrSars15; private Patch ssrSars16; private Patch ssrSars17; private Patch ssrSars18; private Patch ssrSars19; private Patch ssrSars20; private Patch ssrSars21; private Patch ssrSars22; private Patch ssrSars23; private bool skipSSLRMipMapping = false; private bool continousSSLRTemporalReset = false; private Patch forceSSLRTemporalReset; private bool fullResVolumeBlur = false; private Patch volumeSars1; private Patch volumeSars2; private Patch volumeSars3; private Patch volumeSars4; private Patch volumeSars5; private Patch volumeSars6; private Patch volumeSars7; private Patch volumeSars8; private Patch volumeSars9; private void fullResVolumeBlurEnable() { volumeSars1.Enable(); volumeSars2.Enable(); volumeSars3.Enable(); volumeSars4.Enable(); volumeSars5.Enable(); volumeSars6.Enable(); volumeSars7.Enable(); volumeSars8.Enable(); volumeSars9.Enable(); } private void fullResVolumeBlurDisable() { volumeSars1.Disable(); volumeSars2.Disable(); volumeSars3.Disable(); volumeSars4.Disable(); volumeSars5.Disable(); volumeSars6.Disable(); volumeSars7.Disable(); volumeSars8.Disable(); volumeSars9.Disable(); } private bool higherVolumeQuality = false; private Patch value2ForVolumeQuality; private bool disableLODLimits = false; private Patch defaultViewModeLODLimit; private Patch defaultViewModeLODLimit_1; private Patch defaultViewModeLODLimit_2; private Patch defaultViewModeLODLimit_3; private Patch defaultViewModeLODLimit_4; private void disableLODLimitsEnable() { defaultViewModeLODLimit.Enable(); defaultViewModeLODLimit_1.Enable(); defaultViewModeLODLimit_2.Enable(); defaultViewModeLODLimit_3.Enable(); defaultViewModeLODLimit_4.Enable(); } private void disableLODLimitsDisable() { defaultViewModeLODLimit.Disable(); defaultViewModeLODLimit_1.Disable(); defaultViewModeLODLimit_2.Disable(); defaultViewModeLODLimit_3.Disable(); defaultViewModeLODLimit_4.Disable(); } private bool largerFoliageSwayRange = false; private Patch addressHigherValueForFoliageSway; private bool disableReducedRateAnimations = false; private Patch zeroFrameSkip; public int AddOverride(string name, Vector4 v4, int i1) { foreach (Parameter param in allParameters) { if (param.Name == name) { Override ov = new Override(param, v4, i1); Config config = getConfig(); BeginTransition(); if (!(selectedSet != "" && unsetIfOverridesContainsParam(config.Sets[selectedSet], param))) { List? stageOverrides = maybeGetStageOverrides(config); if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, param))) { unsetIfOverridesContainsParam(config.Overrides[globalStage], param); } } ov.Set(); EndTransition(); externalId++; externalOverrides.Add(externalId, ov); return externalId; } } return 0; } public void RemoveOverride(int id) { if (externalOverrides.ContainsKey(id)) { Override ovE = externalOverrides[id]; BeginTransition(); ovE.Unset(); Config config = getConfig(); if (!(selectedSet != "" && setIfOverridesContainsParam(config.Sets[selectedSet], ovE.Param!))) { List? stageOverrides = maybeGetStageOverrides(config); if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, ovE.Param!))) { setIfOverridesContainsParam(config.Overrides[globalStage], ovE.Param!); } } EndTransition(); externalOverrides.Remove(id); } } public PluginData Initialize() { Instance = this; // Assert no duplicate names. foreach (Parameter param in allParameters) { Assert(Array.FindAll(allParameters, (p => p.Name == param.Name)).Length == 1); } // Assert OrderedStages covers all values. foreach (StageExt stage in Enum.GetValues(typeof(StageExt))) { if (stage == StageExt.Global) continue; Assert(OrderedStages.Contains(stage)); } onAreaChange = Hook.Create(0x141AC27D0, OnAreaChangeHook); // nint // Needed for at least "Broad Area Shadow Dir". Otherwise it will flicker on center change. evalSceneParams = Hook.Create(0x1423C3790, EvalSceneParamsHook); // nint, nint, int, nint // Light Dir XZY is updated seperately at 0x1420397F0, we assume this function always happens later, though. updateCapsuleAoParams = Hook.Create(0x141B19E10, UpdateCapsuleAOParamsHook); // nint // These run in a loop during cutscenes. updateSSAOParams = Hook.Create(0x1416D8B10, UpdateSSAOParamsHook); // nint updateSSLRParams = Hook.Create(0x1416DA3F0, UpdateSSLRParamsHook); // nint updateLightingParams = Hook.Create(0x1416DAAB0, UpdateLightingParametersHook); // nint, nint createLightingObject = Hook.Create(0x1424CDE20, CreateLightingObjectHook); destroyLightingObject = Hook.Create(0x1424CE380, DestroyLightingObjectHook); // nint, int updateLutBlend = Hook.Create(0x1416D8DF0, UpdateLutBlendHook); // nint, nint createBloomObject = Hook.Create(0x1424CB3C0, CreateBloomObjectHook); createBloomObject2 = Hook.Create(0x1424CB5E0, CreateBloomObject2Hook); // nint destroyBloomObject = Hook.Create(0x1424CB750, DestroyBloomObjectHook); // nint, int createDofObject = Hook.Create(0x142421E80, CreateDofObjectHook); createDofObject2 = Hook.Create(0x1424220A0, CreateDofObject2Hook); // nint destroyDofObject = Hook.Create(0x142422290, DestroyDofObjectHook); // nint, int updateDofParams = Hook.Create(0x1412BA6A0, UpdateDofParamsHook); // nint createMotionBlurObject = Hook.Create(0x1424C95E0, CreateMotionBlurObjectHook); createMotionBlurObject2 = Hook.Create(0x1424C9740, CreateMotionBlurObject2Hook); // nint destroyMotionBlurObject = Hook.Create(0x1424C97E0, DestroyMotionBlurObjectHook); // nint, int updateMotionBlurParams = Hook.Create(0x1424CADF0, UpdateMotionBlurParamsHook); // nint, nint createSimpleSkyObject = Hook.Create(0x141FCCC20, CreateSimpleSkyObjectHook); // nint destroySimpleSkyObject = Hook.Create(0x141FCD840, DestroySimpleSkyObjectHook); // nint, int renderSimpleSky = Hook.Create(0x141FD58E0, RenderSimpleSkyHook); // nint, nint createFogObject = Hook.Create(0x141FADB50, CreateFogObjectHook); createFogObject2 = Hook.Create(0x141FADC80, CreateFogObject2Hook); // nint destroyFogObject = Hook.Create(0x141FADD50, DestroyFogObjectHook); // nint, int updateFogParams = Hook.Create(0x1416D8F00, UpdateFogParamsHook); // nint, int createWaterWaveObject = Hook.Create(0x1423F0850, CreateWaterWaveObjectHook); // nint destroyWaterWaveObject = Hook.Create(0x1423F0EE0, DestroyWaterWaveObjectHook); // nint, int createFXAAObject = Hook.Create(0x142393560, CreateFXAAObjectHook); createFXAAObject2 = Hook.Create(0x142393680, CreateFXAAObject2Hook); // nint destroyFXAAObject = Hook.Create(0x142393740, DestroyFXAAObjectHook); // nint, int createTAAObject = Hook.Create(0x1423911A0, CreateTAAObjectHook); createTAAObject2 = Hook.Create(0x142391320, CreateTAAObject2Hook); // nint destroyTAAObject = Hook.Create(0x1423916E0, DestroyTAAObjectHook); // nint, int lights.Initialize(); nint addr = PatternScanner.FindFirst(Pattern.FromString("48 89 5C 24 10 48 89 74 24 18 48 89 7C 24 20 55 41 56 41 57 48 8B EC 48 83 EC 30 48 8B FA 48 8B D9 E8 AA B6 C1 FF 48 8B 47 10 48 8D 57 50")); Assert(addr == 0x141AB2260); // nint, nint updateShadowParams = Hook.Create(addr, UpdateShadowParamsHook); staticShadowParams = Hook.Create(0x1416D94F0, StaticShadowParamsHook); // nint, nint setShadowQuality = new NativeAction(0x14043FF80); addr = PatternScanner.FindFirst(Pattern.FromString("48 83 EC 28 F3 0F 10 0D ?? ?? ?? ?? 80 F9 04 75 2F 48 8B 0D ?? ?? ?? ?? E8 D3 8A E4 01 48 8B 0D ?? ?? ?? ?? 33 D2 E8 ?? ?? ?? ??")); Assert(addr == 0x14043FF80); Assert(MemoryUtil.Read(0x142EE1A90) == 0x40000000); // 2.0. Assert(MemoryUtil.Read(0x142E4FE5C) == 0x40400000); // 3.0. Assert(MemoryUtil.Read(0x142F1FD4C) == 0x40800000); // 4.0. Assert(MemoryUtil.Read(0x142F1FD50) == 0x40A00000); // 5.0. Assert(MemoryUtil.Read(0x14321222C) == 0x40C00000); // 6.0. shadowRes1_2x = new Patch(addr + 0x8, [0x04, 0x1B, 0xAA, 0x02]); shadowRes1_3x = new Patch(addr + 0x8, [0xD0, 0xFE, 0xA0, 0x02]); shadowRes1_4x = new Patch(addr + 0x8, [0xC0, 0xFD, 0xAD, 0x02]); shadowRes1_5x = new Patch(addr + 0x8, [0xC4, 0xFD, 0xAD, 0x02]); #if OVERSIZED_SHADOW_MAP shadowRes1_6x = new Patch(addr + 0x8, [0xA0, 0x22, 0xDD, 0x02]); #endif shadowRes1_1 = new Patch(addr + 0x65, [0x17]); // Always set "Value is not 1.0" flag. shadowRes1_2 = new Patch(addr + 0x68, [0x59]); // movss -> mulss. shadowRes1_3 = new Patch(addr + 0x77, [0x59]); // movss -> mulss. #if OVERSIZED_SHADOW_MAP shadowRes1_4 = new Patch(lton(0x142288AAA) + 0x4, [0x7A, 0x97, 0xF8, 0x00]); // Limit 4.0 -> 6.0. #else shadowRes1_4 = new Patch(lton(0x142288AAA) + 0x4, [0x9E]); // Limit 4.0 -> 5.0. #endif addr = PatternScanner.FindFirst(Pattern.FromString("89 91 00 55 00 00 83 FA 05 77 41 48 63 C2 4C 8D 05 ?? ?? ?? ?? 41 8B 94 80 84 8B 28 02 49 03 D0 FF E2 B8 00 08 00 00")); Assert(addr == 0x142288B00); // This value controls the shadow map texture resolution. 16384x16384 is likely the limit for many drivers // and any value higher would crash. Keeping this at the default value (unpatched) *should* work unless the // game does something unpredictable. At 3x Shadow Resolution + High, the shadow map will be 8448x8448. /* At 3x Shadow Resolution, results in a 16320x16320 shadow map. Adjusting the shadow map resolution via this * value feels unsafe, especially if the result is not a multiple of something the game would produce. shadowRes4_Limit = new Patch(addr + 0x29, [ // N/64 = 255, fractional values closer to 256 not tested. 0xB8, 0x40, 0x15, 0x00, 0x00, 0xEB, 0x21 // This case is used for Low, Medium and High in-game. ]); */ // shadowRes2/3 control the detail of the infrequently updated fallback shadows (Broad Area). addr = PatternScanner.FindFirst(Pattern.FromString("83 FA FF 74 06 89 91 1C 55 00 00 8B 91 30 55 00 00 83 FA FF 7F 06 8B 91 1C 55 00 00 85 D2 74 4B 83 EA 01 74 3B 83 EA 01 74 2B 83 EA 01 74 1B 83 FA 01 74 0B C7 81 20 55 00 00 01 00 00 00 C3 C7 81 20 55 00 00 00 10 00 00")); Assert(addr == 0x142287A50); broadAreaShadowRes1_Addr = addr; // This function seems to never be used, but is nearly identical to the first one. So might as well adjust it. addr = PatternScanner.FindFirst(Pattern.FromString("89 91 30 55 00 00 83 FA FF 7F 06 8B 91 1C 55 00 00 85 D2 74 4B 83 EA 01 74 3B 83 EA 01 74 2B 83 EA 01 74 1B 83 FA 01 74 0B C7 81 20 55 00 00 01 00 00 00 C3 C7 81 20 55 00 00 00 10 00 00")); Assert(addr == 0x142287AD0); broadAreaShadowRes2_Addr = addr; // The volume blur filter is created at a different place in the code and happens at a different // time (area change/createLightingObject() vs change of Volume Quality setting). It's also rendered // in a seperate pass that happens after the initial volume rendering. So keep that in mind if inspecting // a frame in renderdoc. volumeSars1 = new Patch(lton(0x1424D0159), [0x90, 0x90]); volumeSars2 = new Patch(lton(0x1424D01A0), [0x90, 0x90]); volumeSars3 = new Patch(lton(0x1424D01C6), [0x90, 0x90]); volumeSars4 = new Patch(lton(0x1424D0219), [0x90, 0x90]); volumeSars5 = new Patch(lton(0x1424D0242), [0x90, 0x90]); volumeSars6 = new Patch(lton(0x1424D029D), [0x90, 0x90]); volumeSars7 = new Patch(lton(0x1424D02C6), [0x90, 0x90]); volumeSars8 = new Patch(lton(0x1424D0321), [0x90, 0x90]); volumeSars9 = new Patch(lton(0x1424D034A), [0x90, 0x90]); /* As far as I can tell, this is limited by buffers created cpu-side. Seems like * it would be an extremely hard change. froxelRes1 = new Patch(lton(0x142384F15) + 0x1, [0x02]); // 4 -> 2 froxelRes2 = new Patch(lton(0x142389711) + 0x1, [0x02]); // 4 -> 2 froxelRes3 = new Patch(lton(0x142389878) + 0x1, [0x02]); // 4 -> 2 froxelRes4 = new Patch(lton(0x14238989E) + 0x1, [0x02]); // 4 -> 2 froxelRes5 = new Patch(lton(0x1423898D8) + 0x1, [0x02]); // 4 -> 2 froxelRes6 = new Patch(lton(0x14238992B) + 0x1, [0x02]); // 4 -> 2 froxelRes7 = new Patch(lton(0x14238FED1) + 0x4, [0x7B, 0xFF, 0xAB, 0x00]); // 0.25 -> 0.50 froxelRes8 = new Patch(lton(0x14239009C) + 0x4, [0xB0, 0xFD, 0xAB, 0X00]); // 0.25 -> 0.50 */ /* Would require a complicated shader edit to maybe work. Similar to SSLR. checkerRes1 = new Patch(lton(0x14238580D) + 0x4, [0x43]); // 0.5 -> 1.0 checkerRes2 = new Patch(lton(0x14238FFC3) + 0x4, [0x8D]); // 0.5 -> 1.0 */ value2ForVolumeQuality = new Patch(lton(0x142389652), [0xB8, 0x02, 0x00, 0x00, 0x00, 0x90]); // Gameplay. addr = PatternScanner.FindFirst(Pattern.FromString("80 BB 34 EC 00 00 00 88 8B 31 EC 00 00 C6 83 30 EC 00 00 00 74 1C C6 83 34 EC 00 00 00 48 8B 0D 68 4B 4F 03 80 B9 58 02 00 00 00 75 05 E8 5A F5 01 00 48 8B CB E8 E2 05 00 00 83 BB DC EB 00 00 06 74 0A C7 83 DC EB 00 00 06 00 00 00")); Assert(addr == 0x141B1A0B4); defaultViewModeLODLimit = new Patch(addr + 0x40, [0x07, 0x74, 0x0A, 0xC7, 0x83, 0xDC, 0xEB, 0x00, 0x00, 0x07]); // In room. addr = PatternScanner.FindFirst(Pattern.FromString("8B 9F CC 00 00 00 81 FB F9 01 00 00 74 0C 8B CB E8 95 3F 65 01 83 F8 05 75 11 48 8B 0D 61 C8 F6 04 BA 02 00 00 00 E8 DF 2D 8C 01 48 8B 0D A0 C7 F6 04 8B D3")); Assert(addr == 0x140257AE6); defaultViewModeLODLimit_1 = new Patch(addr + 0x22, [0x07]); // Forging/Changing Equipment. addr = PatternScanner.FindFirst(Pattern.FromString("BA 02 00 00 00 E8 6E C0 80 00 48 8B 74 24 38 48 8B 57 28 48 8D 4F 30 E8 4C 79 B4 FE 48 8B 0D 0D FF A6 03 BA 04 00 00 00 E8 8B 64 3C 00 48 8B 05 D4 01 A7 03 80 B8 FD 46 01 00 00")); Assert(addr == 0x141754438); defaultViewModeLODLimit_2 = new Patch(addr + 0x24, [0x07]); // Talking to NPC. defaultViewModeLODLimit_3 = new Patch(lton(0x14128F1AC) + 0x1, [0x07]); defaultViewModeLODLimit_4 = new Patch(lton(0x14128F1AC) + 0xF, [0x07]); addr = PatternScanner.FindFirst(Pattern.FromString("74 4D 33 C0 48 81 C1 00 02 00 00 F3 0F 10 11 0F 2F CA 72 0C FF C0 48 83 C1 04 83 F8 03 72 EC C3")); Assert(addr == 0x1426D89CA); addressHigherValueForFoliageSway = new Patch(addr + 0x7, [0x04]); // Found using mFrameSkipNum annotation from MHW-DTI-Dumps. zeroFrameSkip = new Patch(lton(0x142246948), [0x31, 0xC0, 0x90, 0x90, 0x90, 0x90]); // The game will crash when trying to switch to the FULL_RES path for SSR. It's likley because // it tries to access shaders that are not available in the game files. // Ex: MonsterHunterWorld.exe+259329B - lea rcx,[rdi+00000120] # Zero. // MonsterHunterWorld.exe+228C4A7 - mov rdi,[rdi+00000120] # Attempted read. // The code that zero's these addresses looks like a shell of where they would be loaded. My assumption // is that it's compiled out and that this method of increasing the SSR resolution can't work. /* // Attempt to forcefully enable FULL_RES path for SSR. ssrFullRes1 = new Patch(lton(0x14228894D), [0x0F, 0x85, 0xB9, 0xAA, 0x30, 0x00]); ssrFullRes1_1 = new Patch(lton(0x14259340C), [0xC6, 0x81, 0x28, 0x02, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90]); ssrFullRes2 = new Patch(lton(0x14228897D), [0x0F, 0x85, 0x99, 0xAA, 0x30, 0x00]); ssrFullRes2_1 = new Patch(lton(0x14259341C), [0xC6, 0x81, 0x30, 0x02, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90]); ssrFullRes3 = new Patch(lton(0x1422889AD), [0x0F, 0x85, 0x79, 0xAA, 0x30, 0x00]); ssrFullRes3_1 = new Patch(lton(0x14259342C), [0xC6, 0x81, 0x2C, 0x02, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90]); ssrFullRes4 = new Patch(lton(0x14259268C), [0x41, 0xC7, 0x86, 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xC7, 0x86, 0x2C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xC7, 0x86, 0x30, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xC7, 0x86, 0x34, 0x02, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x41, 0xC7, 0x86, 0x38, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x41, 0xC6, 0x86, 0x3C, 0x02, 0x00, 0x00, 0x01]); */ // SSLR resolution factor 0.5 -> 1.0 (not related to fSSLRFactor or fSSLRScale). ssrRes1 = new Patch(lton(0x1425926B8) + 0x9, [0x80]); ssrRes2 = new Patch(lton(0x1425934C0) + 0x8, [0x80]); // Skip mip mapping pass (part of _FULL_RES). ssrRes3 = new Patch(lton(0x142282B53), [0x31, 0xC0, 0x90, 0x90, 0x90]); // Depth mips resolution. ssrShr1 = new Patch(lton(0x14269DE47), [0x90, 0x90]); ssrShr2 = new Patch(lton(0x14269DE54), [0x90, 0x90]); ssrShr3 = new Patch(lton(0x14269E07A), [0x90, 0x90]); ssrShr4 = new Patch(lton(0x14269E080), [0x90, 0x90]); // Approximate depth mip resolution. ssrShr5 = new Patch(lton(0x14269DED3) + 0x2, [0x3]); // 4 -> 3 ssrShr6 = new Patch(lton(0x14269DEDA) + 0x3, [0x3]); // Depth mips pass 1 dispatch dimensions. ssrSars1 = new Patch(lton(0x142282FD8) + 0x3, [0x3]); // 4 -> 3 ssrSars2 = new Patch(lton(0x142282FE5) + 0x2, [0x3]); // Depth mips pass 2 iRegion parameter. ssrSars3 = new Patch(lton(0x142283014) + 0x3, [0x3]); ssrSars4 = new Patch(lton(0x142283018) + 0x3, [0x3]); ssrSars5 = new Patch(lton(0x14228301C) + 0x3, [0x3]); ssrSars6 = new Patch(lton(0x142283020) + 0x2, [0x3]); // Depth mips pass 2 dispatch dimensions. ssrSars7 = new Patch(lton(0x1422832B3) + 0x3, [0x3]); ssrSars8 = new Patch(lton(0x1422832C8) + 0x2, [0x3]); // Depth copy, for unknown use, viewport/scissor. ssrSars9 = new Patch(lton(0x1423C5809), [0x90, 0x90]); ssrSars10 = new Patch(lton(0x1423C5818), [0x90, 0x90]); ssrSars11 = new Patch(lton(0x1423C5824), [0x90, 0x90]); ssrSars12 = new Patch(lton(0x1423C5830), [0x90, 0x90]); ssrSars13 = new Patch(lton(0x1423C5ABC), [0x90, 0x90]); ssrSars14 = new Patch(lton(0x1423C5AF3), [0x90, 0x90]); // RayAppendBuffer dispatch dimensions, 4 -> 3 wasteful. //new Patch(lton(0x14228C323) + 0x3, [0x3]); //new Patch(lton(0x14228C32D) + 0x2, [0x3]); // Clear trace buffer, 8 -> 7 wasteful //new Patch(lton(0x14228C3F4) + 0x2, [0x7]); // Approximate mip resolution. ssrShr7 = new Patch(lton(0x142592EBA) + 0x2, [0x3]); // 4 -> 3 ssrShr8 = new Patch(lton(0x142592ED0) + 0x2, [0x3]); // Mips pass 1 dispatch dimensions. ssrSars15 = new Patch(lton(0x142283626) + 0x3, [0x03]); // 4 -> 3 ssrSars16 = new Patch(lton(0x142283633) + 0x2, [0x03]); // Mips pass 2 iRegion parameter. ssrSars17 = new Patch(lton(0x142283667) + 0x2, [0x03]); ssrSars18 = new Patch(lton(0x14228366A) + 0x3, [0x03]); ssrSars19 = new Patch(lton(0x14228366E) + 0x3, [0x03]); ssrSars20 = new Patch(lton(0x142283672) + 0x3, [0x03]); // Mips pass 2 dispatch dimensions. ssrSars21 = new Patch(lton(0x1422838E7) + 0x3, [0x03]); ssrSars22 = new Patch(lton(0x1422838FC) + 0x2, [0x03]); // Mips blur dispatch dimensions. ssrSars23 = new Patch(lton(0x1422839B0), [0x8D, 0x0E, 0x44, 0x8D, 0x76, 0x01, 0x90]); // Resolve pass, 4 -> 3 wasteful. //new Patch(lton(0x14228686D) + 0x3, [0x3]); //new Patch(lton(0x142286877) + 0x2, [0x3]); forceSSLRTemporalReset = new Patch(lton(0x14228CE53), [0xB9, 0x01, 0x00, 0x00, 0x00, 0x90, 0x90]); return new PluginData(); } public void OnPreMain() { Config config = getConfig(); PatchConfig patches = config.Patches; #if SHADER_FEATURES fullResSSLR = patches.FullResolutionSSLR; if (fullResSSLR) { ssrRes1.Enable(); ssrRes2.Enable(); ssrShr1.Enable(); ssrShr2.Enable(); ssrShr3.Enable(); ssrShr4.Enable(); ssrShr5.Enable(); ssrShr6.Enable(); ssrShr7.Enable(); ssrShr8.Enable(); ssrSars1.Enable(); ssrSars2.Enable(); ssrSars3.Enable(); ssrSars4.Enable(); ssrSars5.Enable(); ssrSars6.Enable(); ssrSars7.Enable(); ssrSars8.Enable(); ssrSars9.Enable(); ssrSars10.Enable(); ssrSars11.Enable(); ssrSars12.Enable(); ssrSars13.Enable(); ssrSars14.Enable(); ssrSars15.Enable(); ssrSars16.Enable(); ssrSars17.Enable(); ssrSars18.Enable(); ssrSars19.Enable(); ssrSars20.Enable(); ssrSars21.Enable(); ssrSars22.Enable(); ssrSars23.Enable(); } #endif fullResVolumeBlur = patches.FullResolutionVolumeBlur; if (fullResVolumeBlur) { fullResVolumeBlurEnable(); } higherVolumeQuality = patches.HigherThanHighestVolumeRendering; if (higherVolumeQuality) { value2ForVolumeQuality.Enable(); } disableLODLimits = patches.DisableLODLimits; if (disableLODLimits) { disableLODLimitsEnable(); } largerFoliageSwayRange = patches.LargerFoliageSwayRange; if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Enable(); } disableReducedRateAnimations = patches.DisableReducedRateAnimations; if (disableReducedRateAnimations) { zeroFrameSkip.Enable(); } } public void OnLoad() { sMhMain = SingletonManager.GetSingleton("sMhMain")!; sMhRender = SingletonManager.GetSingleton("sMhRender")!; sMhScene = SingletonManager.GetSingleton("sMhScene")!; sLightProbes = SingletonManager.GetSingleton("sLightProbes")!; Config config = getConfig(); if (config.SelectedGlobal == "") { config.SelectedGlobal = "Base"; } selectedGlobal = config.SelectedGlobal; if (!config.Globals.ContainsKey(selectedGlobal)) { config.Globals[selectedGlobal] = new List(); } // config.Overrides shouldn't contain Global here. if (!config.Overrides.ContainsKey(globalStage)) { config.Overrides.Add(globalStage, new List()); } else { config.Overrides[globalStage].Clear(); } foreach (Override ovG in config.Globals[selectedGlobal]) { config.Overrides[globalStage].Add(ovG); } foreach (StageExt area in OrderedStages) { if (!config.Overrides.ContainsKey(area)) { config.Overrides.Add(area, new List()); } } foreach (Override ovG in config.Overrides[globalStage]) { ovG.Set(); } foreach ((string name, List overrides) in config.Sets) { if (name == "") { config.Sets.Remove(name); break; } } saveConfig(config); } // It's hard to test, but I'm pretty sure just updating stepTime isn't enough for a higher rate to look right. // https://github.com/AsteriskAmpersand/CTC-MHW-Editor/blob/9ec6303042690478398801b1bb860a2d38298eee/structures/Ctc.py#L23 #if RESOURCE_ADJUSTMENT private bool adjustCtcParams = false; private float stepTimeFps = 60.0f; public void OnResourceLoad(Resource? resource, MtDti dti, string path, LoadFlags flags) { if (adjustCtcParams && resource != null && resource.FileExtension == "ctc") { Log.Info($"Attempting to increase physics update rate of {resource.FilePath} @ {resource.Instance:X}."); int numARecords = MemoryUtil.GetRef(resource.Instance + 0xB8); nint aRecords = MemoryUtil.GetRef(resource.Instance + 0xF8); for (int i = 0; i < numARecords; i++) { nint aRecord = aRecords + (0x50 * i); } MemoryUtil.GetRef(resource.Instance + 0xC4) = 1.0f / stepTimeFps; } } #endif public void OnUpdate(float deltaTime) { StageExt stage = currentStage; if (stage == StageExt.Global && previousStage != StageExt.Global) { HandleAreaChange(stage); } hqMode.Update(sMhScene!.Instance); foreach (Parameter param in lodParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in passthroughParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in shadowCascadeParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in contactShadowParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in snowParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in ssaoParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in sslrParameters) { param.Update(sMhScene!.Instance); } if (lightingObject != 0x0) { foreach (Parameter param in lightingParameters) { param.Update(lightingObject); } } foreach (Parameter param in sLightProbesParameters) { param.Update(sLightProbes!.Instance); } foreach (Parameter param in brightnessParameters) { param.Update(sMhRender!.Instance); } if (bloomObject != 0x0) { foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } } if (dofObject != 0x0) { foreach (Parameter param in dofParameters) { if (!param.PerFrame) { param.Update(dofObject); } } } if (motionBlurObject != 0x0) { foreach (Parameter param in motionBlurParameters) { if (!param.PerFrame) { param.Update(motionBlurObject); } } } if (fxaaObject != 0x0) { foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } } if (taaObject != 0x0) { foreach (Parameter param in taaParameters) { param.Update(taaObject); } } if (waterWaveObject != 0x0) { foreach (Parameter param in waterWaveParameters) { param.Update(waterWaveObject); } } } private void EvalSceneParamsHook(nint unknownPtr, nint unknownPtr2, int unknownInt, nint unknownPtr3) { foreach (Parameter param in broadAreaShadowParameters) { param.Update(sMhScene!.Instance, true); } evalSceneParams!.Original(unknownPtr, unknownPtr2, unknownInt, unknownPtr3); } private void UpdateCapsuleAOParamsHook(nint sceneObjectInternal) { updateCapsuleAoParams!.Original(sceneObjectInternal); foreach (Parameter param in capsuleLightParameters) { param.Update(sMhScene!.Instance, true); } } private void UpdateSSAOParamsHook(nint stackOffset) { updateSSAOParams!.Original(stackOffset); foreach (Parameter param in ssaoParameters) { param.Update(sMhScene!.Instance, true); } } private void UpdateSSLRParamsHook(nint stackOffset) { updateSSLRParams!.Original(stackOffset); foreach (Parameter param in sslrParameters) { param.Update(sMhScene!.Instance, true); } } private void UpdateLightingParametersHook(nint stackOffset, nint lightingObjectInternal) { updateLightingParams!.Original(stackOffset, lightingObjectInternal); if (lightingObject == lightingObjectInternal) { foreach (Parameter param in lightingParameters) { param.Update(lightingObject, true); } } } private nint CreateLightingObjectHook() { lightingObject = createLightingObject!.Original(); foreach (Parameter param in lightingParameters) { param.Update(lightingObject); } return lightingObject; } private nint DestroyLightingObjectHook(nint lightingObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (lightingObject == lightingObjectInternal) { lightingObject = 0x0; foreach (Parameter param in lightingParameters) { param.Update(lightingObject); } } return destroyLightingObject!.Original(lightingObjectInternal, unknownInt); } private void UpdateLutBlendHook(nint stackOffset, nint lightingObjectInternal) { updateLutBlend!.Original(stackOffset, lightingObjectInternal); if (lightingObject == lightingObjectInternal) { foreach (Parameter param in lightingParameters) { if (param.PerFrame) { param.Update(lightingObject, true); } } } } private nint CreateBloomObjectHook() { bloomObject = createBloomObject!.Original(); foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } return bloomObject; } private nint CreateBloomObject2Hook(nint unknownPtr) { bloomObject = createBloomObject2!.Original(unknownPtr); foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } return bloomObject; } private nint DestroyBloomObjectHook(nint bloomObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (bloomObject == bloomObjectInternal) { bloomObject = 0x0; foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } } return destroyBloomObject!.Original(bloomObjectInternal, unknownInt); } private nint CreateDofObjectHook() { dofObject = createDofObject!.Original(); foreach (Parameter param in dofParameters) { param.Update(dofObject); } return dofObject; } private nint CreateDofObject2Hook(nint unknownPtr) { dofObject = createDofObject2!.Original(unknownPtr); foreach (Parameter param in dofParameters) { param.Update(dofObject); } return dofObject; } private nint DestroyDofObjectHook(nint dofObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (dofObject == dofObjectInternal) { dofObject = 0x0; foreach (Parameter param in dofParameters) { param.Update(dofObject); } } return destroyDofObject!.Original(dofObjectInternal, unknownInt); } private void UpdateDofParamsHook(nint unknownPtr) { updateDofParams!.Original(unknownPtr); foreach (Parameter param in dofParameters) { if (param.PerFrame) { param.Update(dofObject, true); } } } private nint CreateMotionBlurObjectHook() { motionBlurObject = createMotionBlurObject!.Original(); foreach (Parameter param in motionBlurParameters) { param.Update(motionBlurObject); } return motionBlurObject; } private nint CreateMotionBlurObject2Hook(nint unknownPtr) { motionBlurObject = createMotionBlurObject2!.Original(unknownPtr); foreach (Parameter param in motionBlurParameters) { param.Update(motionBlurObject); } return motionBlurObject; } private nint DestroyMotionBlurObjectHook(nint motionBlurObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (motionBlurObject == motionBlurObjectInternal) { motionBlurObject = 0x0; foreach (Parameter param in motionBlurParameters) { param.Update(motionBlurObject); } } return destroyMotionBlurObject!.Original(motionBlurObjectInternal, unknownInt); } private void UpdateMotionBlurParamsHook(nint unknownPtr, nint unknownPtr2) { foreach (Parameter param in motionBlurParameters) { if (param.PerFrame) { param.Update(motionBlurObject, true); } } updateMotionBlurParams!.Original(unknownPtr, unknownPtr2); } private nint CreateSimpleSkyObjectHook(nint unknownPtr) { simpleSkyObject = createSimpleSkyObject!.Original(unknownPtr); foreach (Parameter param in simpleSkyParameters) { param.Update(simpleSkyObject); } return simpleSkyObject; } private nint DestroySimpleSkyObjectHook(nint simpleSkyObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (simpleSkyObjectInternal == simpleSkyObject) { simpleSkyObject = 0x0; foreach (Parameter param in simpleSkyParameters) { param.Update(simpleSkyObject); } } return destroySimpleSkyObject!.Original(simpleSkyObjectInternal, unknownInt); } private void RenderSimpleSkyHook(nint simpleSkyObjectInternal, nint unknownPtr) { foreach (Parameter param in simpleSkyParameters) { param.Update(simpleSkyObject, true); } renderSimpleSky!.Original(simpleSkyObjectInternal, unknownPtr); } private nint CreateFogObjectHook() { fogObject = createFogObject!.Original(); foreach (Parameter param in fogParameters) { param.Update(fogObject); } return fogObject; } private nint CreateFogObject2Hook(nint unknownPtr) { fogObject = createFogObject2!.Original(unknownPtr); foreach (Parameter param in fogParameters) { param.Update(fogObject); } return fogObject; } private nint DestroyFogObjectHook(nint fogObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (fogObject == fogObjectInternal) { fogObject = 0x0; foreach (Parameter param in fogParameters) { param.Update(fogObject); } } return destroyFogObject!.Original(fogObjectInternal, unknownInt); } private void UpdateFogParamsHook(nint unknownPtr, int unknownInt) { updateFogParams!.Original(unknownPtr, unknownInt); foreach (Parameter param in fogParameters) { param.Update(fogObject, true); } } private nint CreateWaterWaveObjectHook(nint unknownPtr) { waterWaveObject = createWaterWaveObject!.Original(unknownPtr); foreach (Parameter param in waterWaveParameters) { param.Update(waterWaveObject); } return waterWaveObject; } private nint DestroyWaterWaveObjectHook(nint waterWaveObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (waterWaveObjectInternal == waterWaveObject) { waterWaveObject = 0x0; foreach (Parameter param in waterWaveParameters) { param.Update(waterWaveObject); } } return destroyWaterWaveObject!.Original(waterWaveObjectInternal, unknownInt); } private nint CreateFXAAObjectHook() { fxaaObject = createFXAAObject!.Original(); foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } return fxaaObject; } private nint CreateFXAAObject2Hook(nint unknownPtr) { fxaaObject = createFXAAObject2!.Original(unknownPtr); foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } return fxaaObject; } private nint DestroyFXAAObjectHook(nint fxaaObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (fxaaObject == fxaaObjectInternal) { fxaaObject = 0x0; foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } } return destroyFXAAObject!.Original(fxaaObjectInternal, unknownInt); } private nint CreateTAAObjectHook() { taaObject = createTAAObject!.Original(); foreach (Parameter param in taaParameters) { param.Update(taaObject); } return taaObject; } private nint CreateTAAObject2Hook(nint unknownPtr) { taaObject = createTAAObject2!.Original(unknownPtr); foreach (Parameter param in taaParameters) { param.Update(taaObject); } return taaObject; } private nint DestroyTAAObjectHook(nint taaObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (taaObject == taaObjectInternal) { taaObject = 0x0; foreach (Parameter param in taaParameters) { param.Update(taaObject); } } return destroyTAAObject!.Original(taaObjectInternal, unknownInt); } private void UpdateShadowParamsHook(nint shadowParams, nint stackOffset) { bool isPrimary = MemoryUtil.Read(stackOffset + 0x1D) == 0x1; if (isPrimary) { foreach (Parameter param in shadowParameters2) { if (param.PerFrame) { param.Update(stackOffset, true); } } } updateShadowParams!.Original(shadowParams, stackOffset); } private void StaticShadowParamsHook(nint shadowParamsStatic, nint shadowObjectInternal) { if (shadowObjectInternal == 0x0) { // Necessary to apply Broad Area Depth Bias overrides on first update. foreach (Parameter param in shadowParameters2) { if (param.PerFrame) { param.Update(shadowParamsStatic, true); } } } else { foreach (Parameter param in shadowParameters2) { if (!param.PerFrame) { param.Update(shadowObjectInternal, true); } } } staticShadowParams!.Original(shadowParamsStatic, shadowObjectInternal); } private void HandleSetChange(string prevSet, List globalOverrides, List? stageOverrides) { Config config = getConfig(); if (prevSet != "") { List prevOverrides = config.Sets[prevSet]; foreach (Override ovR in prevOverrides) { bool superseded = ovR.Param != null && overridesContainsParam(externalOverrides.Values, ovR.Param); if (!superseded) { ovR.Unset(); if (ovR.Param != null) { if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, ovR.Param))) { setIfOverridesContainsParam(globalOverrides, ovR.Param); } } } } } if (selectedSet != "") { List newOverrides = config.Sets[selectedSet]; foreach (Override ovN in newOverrides) { bool superseded = ovN.Param != null && overridesContainsParam(externalOverrides.Values, ovN.Param); if (!superseded) { if (ovN.Param != null) { if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, ovN.Param))) { unsetIfOverridesContainsParam(globalOverrides, ovN.Param); } } ovN.Set(); } } } } private void HandleGlobalChange() { Config config = getConfig(); // Unset current Global overrides. List? stageOverrides = maybeGetStageOverrides(config); List globalOverrides = config.Overrides[globalStage]; foreach (Override ovG in globalOverrides) { bool superseded = selectedOverrideSuperseded(config, null, stageOverrides, ovG.Param); if (!superseded) { ovG.Unset(); } } // Drop current Global overrides. globalOverrides.Clear(); // Rebuild from new selections. foreach (Override ovG in config.Globals[selectedGlobal]) { globalOverrides.Add(ovG); } // Set new Global overrides. foreach (Override ovG in globalOverrides) { bool superseded = selectedOverrideSuperseded(config, null, stageOverrides, ovG.Param); if (!superseded) { ovG.Set(); } } } private void HandleAreaChange(StageExt stage) { Config config = getConfig(); List prevOverrides = config.Overrides[previousStage]; List globalOverrides = config.Overrides[globalStage]; BeginTransition(); if (previousStage != globalStage) { foreach (Override ovR in prevOverrides) { bool superseded = stageOverrideSuperseded(config, ovR.Param); if (!superseded) { ovR.Unset(); if (ovR.Param != null) { setIfOverridesContainsParam(globalOverrides, ovR.Param); } } } } if (!config.Overrides.ContainsKey(stage)) { Log.Error($"Unknown stage: {stage}"); stage = globalStage; } if (stage != globalStage) { List stageOverrides = config.Overrides[stage]; foreach (Override ovS in stageOverrides) { bool superseded = stageOverrideSuperseded(config, ovS.Param); if (!superseded) { if (ovS.Param != null) { unsetIfOverridesContainsParam(globalOverrides, ovS.Param); } ovS.Set(); } } } EndTransition(); previousStage = stage; } private void OnAreaChangeHook(nint unknownPtr) { onAreaChange!.Original(unknownPtr); StageExt stage = currentStage; if (stage == previousStage) { return; } HandleAreaChange(stage); } public void OnImGuiRender() { float width = ImGui.GetWindowWidth(); width /= width / (600.0f * ImGui.GetIO().FontGlobalScale); ImGui.Text($"Current Stage: {StageToString(currentStage)}"); ImGui.PushItemWidth(width * 0.35f); if (ImGui.BeginCombo("##Stage", StageToString(selectedStage), ImGuiComboFlags.HeightLarge)) { foreach (StageExt area in OrderedStages) { string name = StageToString(area); bool isSelected = selectedStage == area; if (ImGui.Selectable(name, isSelected)) { selectedStage = area; } if (isSelected) ImGui.SetItemDefaultFocus(); } ImGui.EndCombo(); } ImGui.SameLine(); Config config = getConfig(); bool errorStage = !config.Overrides.ContainsKey(currentStage); if (errorStage) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Button("Current")) { selectedStage = currentStage; } if (errorStage) { ImGui.PopStyleVar(); ImGui.PopItemFlag(); } ImGui.SameLine(); if (ImGui.Button("Global")) { selectedStage = globalStage; } bool selectedOrderChanged = false; List selectedOverrides = config.Overrides[selectedStage]; if (ImGui.Button("Add override")) { Override ov = new Override(); if (selectedStage == currentStage || selectedStage == globalStage) { ov.Set(); // Empty override never superseded. } selectedOverrides.Add(ov); selectedOrderChanged = true; } List? stageOverrides = maybeGetStageOverrides(config); List? globalOverrides = null; if (selectedStage != globalStage) { globalOverrides = config.Overrides[globalStage]; } else { ImGui.SameLine(); string prevGlobal = selectedGlobal; ImGui.Text("Preset:"); ImGui.SameLine(); ImGui.SetCursorPos(ImGui.GetCursorPos() - new Vector2(5.0f, 0.0f)); if (ImGui.BeginCombo("##Preset", selectedGlobal)) { foreach ((string name, List overrides) in config.Globals) { bool isSelected = name == selectedGlobal; if (ImGui.Selectable(name, isSelected)) { selectedGlobal = name; } } ImGui.EndCombo(); } if (prevGlobal != selectedGlobal) { BeginTransition(); HandleGlobalChange(); EndTransition(); config.SelectedGlobal = selectedGlobal; saveConfig(config); } } ImGui.PopItemWidth(); for (int i = 0; i < selectedOverrides.Count; i++) { ImGui.PushID(i); Override ovS = selectedOverrides[i]; bool superseded = selectedOverrideSuperseded(config, globalOverrides, stageOverrides, ovS.Param); bool requestRemove = ImGui.Button("X"); ImGui.SameLine(); bool requestUp = ImGui.Button("▲"); ImGui.SameLine(); bool requestDown = ImGui.Button("▼"); ImGui.SameLine(); selectedOrderChanged |= requestRemove || requestUp || requestDown; if (requestDown && i < selectedOverrides.Count - 1) { (selectedOverrides[i], selectedOverrides[i + 1]) = (selectedOverrides[i + 1], selectedOverrides[i]); } ovS.Draw(false, selectedOverrides, stageOverrides, globalOverrides, superseded, width); if (requestUp && i > 0) { (selectedOverrides[i], selectedOverrides[i - 1]) = (selectedOverrides[i - 1], selectedOverrides[i]); } if (requestRemove) { if (!superseded) { BeginTransition(); ovS.Unset(); if (ovS.Param != null && globalOverrides != null) { setIfOverridesContainsParam(globalOverrides, ovS.Param); } EndTransition(); } selectedOverrides.RemoveAt(i); i--; } ImGui.PopID(); } if (ImGui.Button("Save")) { for (int i = 0; i < selectedOverrides.Count; i++) { Override ovS = selectedOverrides[i]; if (ovS.Param == null) { selectedOverrides.RemoveAt(i); i--; continue; } ovS.Save(); } saveConfig(config); } if (selectedOrderChanged) { if (selectedStage == globalStage) { rebuildConfig(config, selectedOverrides); } configState |= SelectedNotSaved; } if ((configState & SelectedNotSaved) == SelectedNotSaved) { ImGui.SameLine(); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); ImGui.Text("(Not Saved)"); ImGui.PopStyleVar(); } ImGui.Separator(); // From this point forward, we have to consider both globalOverrides and stageOverrides. globalOverrides = config.Overrides[globalStage]; ImGui.PushID("Set"); ImGui.Text("Quick Sets:"); ImGui.PushItemWidth(width * 0.35f); string prevSet = selectedSet; if (ImGui.Button("+")) { string setName = "New Set"; int setDup = 0; while (config.Sets.ContainsKey(setName)) { setDup++; setName = $"New Set ({setDup})"; } config.Sets[setName] = new List(); selectedSet = setName; } ImGui.SameLine(); if (renamingSet) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.BeginCombo("##Set", selectedSet)) { if (ImGui.Selectable("(None)", false)) { selectedSet = ""; } foreach ((string name, List overrides) in config.Sets) { bool isSelected = name == selectedSet; if (ImGui.Selectable(name, isSelected)) { selectedSet = name; } } ImGui.EndCombo(); } if (prevSet != selectedSet) { BeginTransition(); HandleSetChange(prevSet, globalOverrides, stageOverrides); EndTransition(); } if (renamingSet) { ImGui.PopStyleVar(); ImGui.PopItemFlag(); } ImGui.PopItemWidth(); if (selectedSet != "") { ImGui.SameLine(); ImGui.PushItemWidth(width * 0.2f); if (renamingSet) { ImGui.InputText("##Set Name", ref typedSetName, 99); ImGui.SameLine(); if (typedSetName == "") { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Button("Save")) { string setName = typedSetName; if (setName != selectedSet) { int setDup = 0; while (config.Sets.ContainsKey(setName)) { setDup++; setName = $"{typedSetName} ({setDup})"; } List tmpOverrides = config.Sets[selectedSet]; config.Sets.Remove(selectedSet); config.Sets[setName] = tmpOverrides; selectedSet = setName; } renamingSet = false; } if (typedSetName == "") { ImGui.PopStyleVar(); ImGui.PopItemFlag(); } } else if (ImGui.Button("Rename")) { renamingSet = true; typedSetName = selectedSet; } ImGui.PopItemWidth(); bool setOrderChanged = false; List setOverrides = config.Sets[selectedSet]; if (ImGui.Button("Add override")) { Override ov = new Override(); ov.Set(); // Empty override never superseded. setOverrides.Add(ov); setOrderChanged = true; } for (int i = 0; i < setOverrides.Count; i++) { ImGui.PushID(i); Override ovP = setOverrides[i]; bool superseded = ovP.Param != null && overridesContainsParam(externalOverrides.Values, ovP.Param); bool requestRemove = ImGui.Button("X"); ImGui.SameLine(); bool requestUp = ImGui.Button("▲"); ImGui.SameLine(); bool requestDown = ImGui.Button("▼"); ImGui.SameLine(); setOrderChanged |= requestRemove || requestUp || requestDown; if (requestDown && i < setOverrides.Count - 1) { (setOverrides[i], setOverrides[i + 1]) = (setOverrides[i + 1], setOverrides[i]); } ovP.Draw(true, setOverrides, stageOverrides, globalOverrides, superseded, width); if (requestUp && i > 0) { (setOverrides[i], setOverrides[i - 1]) = (setOverrides[i - 1], setOverrides[i]); } if (requestRemove) { if (!superseded) { BeginTransition(); ovP.Unset(); if (ovP.Param != null) { if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, ovP.Param))) { setIfOverridesContainsParam(globalOverrides, ovP.Param); } } EndTransition(); } setOverrides.RemoveAt(i); i--; } ImGui.PopID(); } if (ImGui.Button("Save")) { for (int i = 0; i < setOverrides.Count; i++) { Override ovP = setOverrides[i]; if (ovP.Param == null) { setOverrides.RemoveAt(i); i--; continue; } ovP.Save(); } saveConfig(config); } if (setOrderChanged) { configState |= SetNotSaved; } if ((configState & SetNotSaved) == SetNotSaved) { ImGui.SameLine(); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); ImGui.Text("(Not Saved)"); ImGui.PopStyleVar(); } } ImGui.PopID(); ImGui.Separator(); ImGui.PushID("External"); ImGui.Text("External:"); foreach (Override ovE in externalOverrides.Values) { ovE.Draw(false, null, null, null, false, width); } ImGui.PopID(); ImGui.Separator(); if (ImGui.CollapsingHeader("Patches")) { PatchConfig patches = config.Patches; #if SHADER_FEATURES if (ImGui.Checkbox("Full Resolution Screen Space Reflections (Requires Restart)", ref fullResSSLR)) { patches.FullResolutionSSLR = fullResSSLR; config.Patches = patches; saveConfig(config); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Restart Required"); ImGui.EndTooltip(); } #endif if (ImGui.Checkbox("Volume Rendering Full Resolution Blur Pass (Requires Area Change)", ref fullResVolumeBlur)) { if (fullResVolumeBlur) { fullResVolumeBlurEnable(); } else { fullResVolumeBlurDisable(); } patches.FullResolutionVolumeBlur = fullResVolumeBlur; config.Patches = patches; saveConfig(config); } if (ImGui.BeginItemTooltip()) { ImGui.Text("This can get rid of excessive aliasing when something is in front of a volumetric effect.\nMove to a different area for this to apply. It will stay applied after that."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Higher Than \"Highest\" Volume Rendering Quality", ref higherVolumeQuality)) { if (higherVolumeQuality) { value2ForVolumeQuality.Enable(); } else { value2ForVolumeQuality.Disable(); } patches.HigherThanHighestVolumeRendering = higherVolumeQuality; config.Patches = patches; saveConfig(config); } if (ImGui.BeginItemTooltip()) { ImGui.Text("*Extremely Resource Intensive* Only noticeable effect is likely in areas that had visible breakup with Volume Rendering Quality: Highest.\nThis takes mostly the same path as Highest."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Disable Player/Palico/NPC LOD Limit in Gameplay", ref disableLODLimits)) { if (disableLODLimits) { disableLODLimitsEnable(); } else { disableLODLimitsDisable(); } patches.DisableLODLimits = disableLODLimits; config.Patches = patches; saveConfig(config); } if (ImGui.BeginItemTooltip()) { ImGui.Text("This will make the default (gameplay) LOD limits equivalent to View Mode. Disabling the limit on:\n - Player Models & Shadows\n - Palico Models & Shadows\n - NPC Models & Shadows\n - Simple NPC Models & Shadows\nAlso equivalent to the settings applied in your room with the addition of disabled Simple NPC limits."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Larger Foliage Sway Range", ref largerFoliageSwayRange)) { if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Enable(); } else { addressHigherValueForFoliageSway.Disable(); } patches.LargerFoliageSwayRange = largerFoliageSwayRange; config.Patches = patches; saveConfig(config); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Increase the distance at which Foliage Sway is still applied to lower priority plants."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Disable Reduced Rate Animations", ref disableReducedRateAnimations)) { if (disableReducedRateAnimations) { zeroFrameSkip.Enable(); } else { zeroFrameSkip.Disable(); } patches.DisableReducedRateAnimations = disableReducedRateAnimations; config.Patches = patches; saveConfig(config); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Ignore the frame skip set on an animal's animation when are far from the camera."); ImGui.EndTooltip(); } } if (ImGui.CollapsingHeader("Lights")) { lights.DrawUI(width); } bool expanded = ImGui.CollapsingHeader("Parameters"); if (ImGui.BeginItemTooltip()) { ImGui.Text("(Original: X) may be inaccurate for various reasons, such as\n 1. The game set the parameter to the same value as an override from this mod.\n 2. The game is dynamically updating the parameter in a location unknown to this mod."); ImGui.EndTooltip(); } if (expanded) { hqMode.Draw(width); ImGui.PushID("LOD"); if (ImGui.CollapsingHeader("Level of Detail")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in lodParameters) { param.Draw(width); } foreach (Parameter param in snowParameters) { param.Draw(width); } } ImGui.PopID(); // MonsterHunterWorld.exe+1AB99E0 - mov rax,[MonsterHunterWorld.exe+500E180] nint shadowAddr = MemoryUtil.Read(0x14500E180); nint shadowObject = MemoryUtil.Read(shadowAddr + 0x4B0); shadowAddr += 0x5B0; ImGui.PushID("Shadows"); if (ImGui.CollapsingHeader("Sunlight and Shadows")) { shadowResParameter.Draw(width); ImGui.Separator(); ImGui.Text($"shadowObject: 0x{shadowAddr:X}, shadowAddr: 0x{shadowObject:X}"); /* if (ImGui.CollapsingHeader("Pass 1")) { ImGui.PushID("Pass1"); foreach (Parameter param in shadowParameters1) { param.Draw(width); } ImGui.PopID(); } if (ImGui.CollapsingHeader("Pass 2")) { ImGui.PushID("Pass2"); */ foreach (Parameter param in shadowParameters2) { param.Draw(width); } /* ImGui.PopID(); } */ ImGui.Separator(); ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in shadowCascadeParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("BroadAreaShadows"); if (ImGui.CollapsingHeader("Broad Area Shadows")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in broadAreaShadowParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("ContactLight"); if (ImGui.CollapsingHeader("Contact Light")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in contactShadowParameters) { param.Draw(width); } foreach (Parameter param in capsuleLightParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("SSAO"); if (ImGui.CollapsingHeader("Ambient Occlusion")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in ssaoParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("SSLR"); if (ImGui.CollapsingHeader("Reflections")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in sslrParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("Passthrough"); if (ImGui.CollapsingHeader("Passthrough")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in passthroughParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("Lighting"); if (ImGui.CollapsingHeader("Lighting")) { ImGui.Text($"Address: 0x{lightingObject:X}"); if (lightingObject != 0x0) { if (ImGui.CollapsingHeader("LUTs")) { nint lutBlendTexture = MemoryUtil.Read(lightingObject + 0x1C0); ImGui.Text($"LUT Blend: 0x{lutBlendTexture:X}"); nint lutMap0 = MemoryUtil.Read(lightingObject + 0x1A8); if (lutMap0 != 0x0) { luts.DrawUI(lutMap0, lightingObject, 0, width); } nint lutMap1 = MemoryUtil.Read(lightingObject + 0x1B0); if (lutMap1 != 0x0) { luts.DrawUI(lutMap1, lightingObject, 1, width); } } foreach (Parameter param in lightingParameters) { param.Draw(width); } } ImGui.Separator(); ImGui.Text($"Address: 0x{sMhRender!.Instance:X}"); foreach (Parameter param in brightnessParameters) { param.Draw(width); } ImGui.Separator(); ImGui.Text($"Address: 0x{sLightProbes!.Instance:X}"); foreach (Parameter param in sLightProbesParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("Bloom"); if (ImGui.CollapsingHeader("Bloom")) { ImGui.Text($"Address: 0x{bloomObject:X}"); if (bloomObject != 0x0) { foreach (Parameter param in bloomParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("DepthOfField"); if (ImGui.CollapsingHeader("Depth of Field")) { ImGui.Text($"Address: 0x{dofObject:X}"); if (dofObject != 0x0) { foreach (Parameter param in dofParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("MotionBlur"); if (ImGui.CollapsingHeader("Motion Blur")) { ImGui.Text($"Address: 0x{motionBlurObject:X}"); if (motionBlurObject != 0x0) { foreach (Parameter param in motionBlurParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("Sky"); if (ImGui.CollapsingHeader("Sky")) { ImGui.Text($"Address: 0x{simpleSkyObject:X}"); if (simpleSkyObject != 0x0) { foreach (Parameter param in simpleSkyParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("Fog"); if (ImGui.CollapsingHeader("Fog")) { ImGui.Text($"Address: 0x{fogObject:X}"); if (fogObject != 0x0) { foreach (Parameter param in fogParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("Water"); if (ImGui.CollapsingHeader("Water")) { ImGui.Text($"Address: 0x{waterWaveObject:X}"); if (waterWaveObject != 0x0) { foreach (Parameter param in waterWaveParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("FXAA"); if (ImGui.CollapsingHeader("FXAA")) { ImGui.Text($"Address: 0x{fxaaObject:X}"); if (fxaaObject != 0x0) { foreach (Parameter param in fxaaParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("TAA"); if (ImGui.CollapsingHeader("TAA")) { ImGui.Text($"Address: 0x{taaObject:X}"); if (taaObject != 0x0) { foreach (Parameter param in taaParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.Separator(); } if (ImGui.CollapsingHeader("DEBUG")) { ImGui.Checkbox("Globally Ignore Parameter Min/Max", ref ignoreMinMax); if (ImGui.BeginItemTooltip()) { ImGui.Text("Some min/max values are set to avoid crashes, so be warned."); ImGui.EndTooltip(); } if (ImGui.CollapsingHeader("Tests")) { if (ImGui.Checkbox("Skip Mip Mapping for SSLR", ref skipSSLRMipMapping)) { if (skipSSLRMipMapping) { ssrRes3.Enable(); } else { ssrRes3.Disable(); } } if (ImGui.Checkbox("Continous SSLR Temporal Reset", ref continousSSLRTemporalReset)) { if (continousSSLRTemporalReset) { forceSSLRTemporalReset.Enable(); } else { forceSSLRTemporalReset.Disable(); } } if (ImGui.BeginItemTooltip()) { ImGui.Text("Don't reference tBlendMap in SSLR resolve."); ImGui.EndTooltip(); } ImGui.Separator(); } ImGui.PushItemWidth(width * 0.15f); ImGui.Text($"FPS: {MemoryUtil.GetRef(sMhMain!.Instance + 0x68)}"); ImGui.Text($"Delta Time: {MemoryUtil.GetRef(sMhMain!.Instance + 0x94)}"); // Locations taken from MHW-DTI-Dumps/wip_dump_15_20_00.h. ImGui.InputFloat("Simulation FPS", ref MemoryUtil.GetRef(sMhMain!.Instance + 0x58)); if (ImGui.BeginItemTooltip()) { ImGui.Text("This does not apply to jiggle physics."); ImGui.EndTooltip(); } ImGui.InputFloat("Max FPS", ref MemoryUtil.GetRef(sMhMain!.Instance + 0x5C)); ImGui.PopItemWidth(); } } #if SHADER_FEATURES private static bool replaceShaderFromFile(ShaderInfo *info, string path) { if (!System.IO.File.Exists(path)) { Log.Warn($"File not found: {path}."); return false; } byte[] shaderData; try { shaderData = System.IO.File.ReadAllBytes(path); } catch (Exception) { Log.Error($"Failed to read: {path}."); return false; } info->Replacement.Length = shaderData.Length; info->Replacement.Source = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(shaderData, 0); return true; } private const string shaderPath = "nativePC/plugins/CSharp/Shaders"; private bool shadersLoadedOnce = false; public unsafe void OnCreateShader(ShaderInfo *info) { string hash = new string(info->DxbcHash); if (hash == "0c3dacbd-32c175d5-10d80d04-cce7e689") { if (replaceShaderFromFile(info, $"{shaderPath}/alpha_as_luma.hlsl")) { info->Replacement.Type = ShaderSourceType.HLSL; Log.Info("Alpha as luma shader replaced."); } } else if (hash == "9ff245fb-4fd555e0-04d0ef15-84609fe1") { if (replaceShaderFromFile(info, $"{shaderPath}/fxaa_max_quality.hlsl")) { info->Replacement.Type = ShaderSourceType.HLSL; Log.Info("FXAA shader replaced."); } } else if (!shadersLoadedOnce && hash == "2e4e18fd-6ea5eeae-2de3d65c-26ab36dd") { if (replaceShaderFromFile(info, $"{shaderPath}/volume_upsample1.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("Volume upsample shader 1 replaced."); } } else if (!shadersLoadedOnce && hash == "e2c8c13e-45bc99b2-4c3d7699-3d66188e") { if (replaceShaderFromFile(info, $"{shaderPath}/volume_upsample2.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("Volume upsample shader 2 replaced."); } // This allows us to A/B using the volume rendering value 2 switch. //shadersLoadedOnce = true; } /* else if (hash == "8cf2a107-ecde214b-9939b350-7b2af1a7") { if (replaceShaderFromFile(info, $"{shaderPath}/cube_only.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("Cube Only shader replaced."); } } */ else if (hash == "2eed1f00-e24d1b19-9d521064-17b9a586") { if (replaceShaderFromFile(info, $"{shaderPath}/body_skin.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("Body skin shader replaced."); } } else if (hash == "8e5e3c09-ebe77ee4-ba1cb659-d25bc7bf") { if (replaceShaderFromFile(info, $"{shaderPath}/face.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("Face shader replaced."); } } /* else if (hash == "18a797dc-9458d333-59e6dff0-9790fcea") { if (replaceShaderFromFile(info, $"{shaderPath}/sssss.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSSSS shader replaced."); } } */ /* else if (hash == "ca33bab6-2119df84-41786a69-404471cd") { if (replaceShaderFromFile(info, $"{shaderPath}/color_grading.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("Color grading shader replaced."); } } */ if (fullResSSLR) { if (hash == "4b722c0e-8e787cd5-5eaf5e6c-09782f91") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_depth_mips.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSLR depth mips shader replaced."); } } else if (hash == "5abc7a51-f182e7d6-14d2f897-6f851c0b") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_trace.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSLR trace shader replaced."); } } else if (hash == "d7e47ffe-82572c64-795c4698-341e5b91") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_mips.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSLR mips shader replaced."); } } else if (hash == "b21dd223-98ab56ae-918264e6-eb6e8ee4") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_resolve.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSLR resolve shader replaced."); } } else if (hash == "8703f55f-6c22fba7-1ef876f1-4767379e") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_resolve_no_jitter.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSLR resolve no jitter shader replaced."); } } else if (hash == "a9372f22-05154267-13f583e1-b08ffd75") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_resolve_no_dither.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSLR resolve no dither shader replaced."); } } else if (hash == "77e20ce4-0538a5f1-908ba0a3-55821b0f") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_resolve_no_jitter_no_dither.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSLR resolve no jitter or dither shader replaced."); } } } else { if (hash == "8703f55f-6c22fba7-1ef876f1-4767379e") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_resolve_no_jitter_min_only.shdr")) { info->Replacement.Type = ShaderSourceType.Binary; Log.Info("SSLR resolve no jitter shader replaced."); } } } } #endif } }