mirror of
https://github.com/Dadido3/noita-mapcap.git
synced 2024-11-18 17:17:31 +00:00
Fully implement "disable-mod-detection" setting
- Catch exceptions in OnPausedChanged - Unhide "disable-mod-detection" setting - Add error message for unsupported modifications - Change memory modification lookup to contain functions - Don't (re)enable mod detection automatically - Add memory.lua library that allows to change the protection of memory regions.
This commit is contained in:
parent
28c07dfd25
commit
18682ed441
58
files/libraries/memory.lua
Normal file
58
files/libraries/memory.lua
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
-- Copyright (c) 2022 David Vogel
|
||||||
|
--
|
||||||
|
-- This software is released under the MIT License.
|
||||||
|
-- https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
local ffi = require("ffi")
|
||||||
|
|
||||||
|
local Memory = {}
|
||||||
|
|
||||||
|
if ffi.abi'64bit' then
|
||||||
|
ffi.cdef([[
|
||||||
|
typedef uint64_t __uint3264;
|
||||||
|
]])
|
||||||
|
else
|
||||||
|
ffi.cdef([[
|
||||||
|
typedef uint32_t __uint3264;
|
||||||
|
]])
|
||||||
|
end
|
||||||
|
|
||||||
|
ffi.cdef([[
|
||||||
|
typedef void VOID;
|
||||||
|
typedef VOID *LPVOID;
|
||||||
|
typedef int BOOL;
|
||||||
|
typedef __uint3264 ULONG_PTR, *PULONG_PTR;
|
||||||
|
typedef ULONG_PTR SIZE_T, *PSIZE_T;
|
||||||
|
typedef unsigned long DWORD;
|
||||||
|
typedef DWORD *PDWORD;
|
||||||
|
|
||||||
|
BOOL VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||||
|
]])
|
||||||
|
|
||||||
|
Memory.PAGE_NOACCESS = 0x01
|
||||||
|
Memory.PAGE_READONLY = 0x02
|
||||||
|
Memory.PAGE_READWRITE = 0x04
|
||||||
|
Memory.PAGE_WRITECOPY = 0x08
|
||||||
|
Memory.PAGE_EXECUTE = 0x10
|
||||||
|
Memory.PAGE_EXECUTE_READ = 0x20
|
||||||
|
Memory.PAGE_EXECUTE_READWRITE = 0x40
|
||||||
|
Memory.PAGE_EXECUTE_WRITECOPY = 0x80
|
||||||
|
Memory.PAGE_GUARD = 0x100
|
||||||
|
Memory.PAGE_NOCACHE = 0x200
|
||||||
|
Memory.PAGE_WRITECOMBINE = 0x400
|
||||||
|
|
||||||
|
---Changes the protection on a region of committed pages in the virtual address space of the calling process.
|
||||||
|
---@param addr ffi.cdata*
|
||||||
|
---@param size integer
|
||||||
|
---@param newProtect integer
|
||||||
|
---@return ffi.cdata* oldProtect
|
||||||
|
function Memory.VirtualProtect(addr, size, newProtect)
|
||||||
|
local oldprotect = ffi.new('DWORD[1]')
|
||||||
|
if not ffi.C.VirtualProtect(addr, size, newProtect, oldprotect) then
|
||||||
|
error(string.format("failed to call VirtualProtect(%s, %s, %s)", addr, size, newProtect))
|
||||||
|
end
|
||||||
|
|
||||||
|
return oldprotect
|
||||||
|
end
|
||||||
|
|
||||||
|
return Memory
|
@ -176,3 +176,21 @@ function Message:ShowGeneralInstallationProblem(...)
|
|||||||
Lines = { ... },
|
Lines = { ... },
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Tell the user that some modifications couldn't be applied because it is unsupported.
|
||||||
|
---@param realm "config"|"magicNumbers"|"processMemory"|"filePatches"
|
||||||
|
---@param name string
|
||||||
|
---@param value any
|
||||||
|
function Message:ShowModificationUnsupported(realm, name, value)
|
||||||
|
self.List = self.List or {}
|
||||||
|
|
||||||
|
self.List["ModificationFailed"] = {
|
||||||
|
Type = "warning",
|
||||||
|
Lines = {
|
||||||
|
string.format("Couldn't modify %q in %q realm.", name, realm),
|
||||||
|
" ",
|
||||||
|
"This simply means that this modification is not supported for the Noita version you are using.",
|
||||||
|
"Feel free to open an issue at https://github.com/Dadido3/noita-mapcap.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
local CameraAPI = require("noita-api.camera")
|
local CameraAPI = require("noita-api.camera")
|
||||||
local Coords = require("coordinates")
|
local Coords = require("coordinates")
|
||||||
local ffi = require("ffi")
|
local ffi = require("ffi")
|
||||||
|
local Memory = require("memory")
|
||||||
local NXML = require("luanxml.nxml")
|
local NXML = require("luanxml.nxml")
|
||||||
local Utils = require("noita-api.utils")
|
local Utils = require("noita-api.utils")
|
||||||
local Vec2 = require("noita-api.vec2")
|
local Vec2 = require("noita-api.vec2")
|
||||||
@ -92,38 +93,40 @@ end
|
|||||||
---@param memory table
|
---@param memory table
|
||||||
function Modification.SetMemoryOptions(memory)
|
function Modification.SetMemoryOptions(memory)
|
||||||
-- Lookup table with the following hierarchy:
|
-- Lookup table with the following hierarchy:
|
||||||
-- DevBuild -> OS -> BuildDate -> Option -> Address.
|
-- DevBuild -> OS -> BuildDate -> Option -> ModFunc.
|
||||||
local lookup = {
|
local lookup = {
|
||||||
[true] = {
|
[true] = {
|
||||||
Windows = {
|
Windows = {
|
||||||
[0x00F77B0C] = { _BuildString = "Build Apr 23 2021 18:36:55", -- GOG dev build.
|
[0x00F77B0C] = { _BuildString = "Build Apr 23 2021 18:36:55", -- GOG dev build.
|
||||||
mPostFxDisabled = 0x010E3B6C,
|
mPostFxDisabled = function(value) ffi.cast("char*", 0x010E3B6C)[0] = value end,
|
||||||
mGuiDisabled = 0x010E3B6D,
|
mGuiDisabled = function(value) ffi.cast("char*", 0x010E3B6D)[0] = value end,
|
||||||
mGuiHalfSize = 0x010E3B6E,
|
mGuiHalfSize = function(value) ffi.cast("char*", 0x010E3B6E)[0] = value end,
|
||||||
mFogOfWarOpenEverywhere = 0x010E3B6F,
|
mFogOfWarOpenEverywhere = function(value) ffi.cast("char*", 0x010E3B6F)[0] = value end,
|
||||||
mTrailerMode = 0x010E3B70,
|
mTrailerMode = function(value) ffi.cast("char*", 0x010E3B70)[0] = value end,
|
||||||
mDayTimeRotationPause = 0x010E3B71,
|
mDayTimeRotationPause = function(value) ffi.cast("char*", 0x010E3B71)[0] = value end,
|
||||||
mPlayerNeverDies = 0x010E3B72,
|
mPlayerNeverDies = function(value) ffi.cast("char*", 0x010E3B72)[0] = value end,
|
||||||
mFreezeAI = 0x010E3B73,
|
mFreezeAI = function(value) ffi.cast("char*", 0x010E3B73)[0] = value end,
|
||||||
},
|
},
|
||||||
[0x00F80384] = { _BuildString = "Build Apr 23 2021 18:40:40", -- Steam dev build.
|
[0x00F80384] = { _BuildString = "Build Apr 23 2021 18:40:40", -- Steam dev build.
|
||||||
mPostFxDisabled = 0x010EDEBC,
|
mPostFxDisabled = function(value) ffi.cast("char*", 0x010EDEBC)[0] = value end,
|
||||||
mGuiDisabled = 0x010EDEBD,
|
mGuiDisabled = function(value) ffi.cast("char*", 0x010EDEBD)[0] = value end,
|
||||||
mGuiHalfSize = 0x010EDEBE,
|
mGuiHalfSize = function(value) ffi.cast("char*", 0x010EDEBE)[0] = value end,
|
||||||
mFogOfWarOpenEverywhere = 0x010EDEBF,
|
mFogOfWarOpenEverywhere = function(value) ffi.cast("char*", 0x010EDEBF)[0] = value end,
|
||||||
mTrailerMode = 0x010EDEC0,
|
mTrailerMode = function(value) ffi.cast("char*", 0x010EDEC0)[0] = value end,
|
||||||
mDayTimeRotationPause = 0x010EDEC1,
|
mDayTimeRotationPause = function(value) ffi.cast("char*", 0x010EDEC1)[0] = value end,
|
||||||
mPlayerNeverDies = 0x010EDEC2,
|
mPlayerNeverDies = function(value) ffi.cast("char*", 0x010EDEC2)[0] = value end,
|
||||||
mFreezeAI = 0x010EDEC3,
|
mFreezeAI = function(value) ffi.cast("char*", 0x010EDEC3)[0] = value end,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[false] = {
|
[false] = {
|
||||||
Windows = {
|
Windows = {
|
||||||
[0x00E1C550] = { _BuildString = "Build Apr 23 2021 18:44:24", -- Steam build.
|
[0x00E1C550] = { _BuildString = "Build Apr 23 2021 18:44:24", -- Steam build.
|
||||||
--enableModDetection = 0x0063D8AD, -- This basically just changes the value that Noita forces to the "mods_have_been_active_during_this_run" member of the WorldStateComponent when any mod is enabled.
|
enableModDetection = function(value)
|
||||||
-- The page this is in is not writable, so this will crash Noita.
|
local ptr = ffi.cast("char*", 0x0063D8AD)
|
||||||
-- This modification can be applied manually with some other tool that changes the permission prior to writing, like Cheat Engine.
|
Memory.VirtualProtect(ptr, 1, Memory.PAGE_EXECUTE_READWRITE)
|
||||||
|
ptr[0] = value -- This basically just changes the value that Noita forces to the "mods_have_been_active_during_this_run" member of the WorldStateComponent when any mod is enabled.
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -132,10 +135,10 @@ function Modification.SetMemoryOptions(memory)
|
|||||||
-- Look up the tree and set options accordingly.
|
-- Look up the tree and set options accordingly.
|
||||||
|
|
||||||
local level1 = lookup[DebugGetIsDevBuild()]
|
local level1 = lookup[DebugGetIsDevBuild()]
|
||||||
if level1 == nil then return end
|
level1 = level1 or {}
|
||||||
|
|
||||||
local level2 = level1[ffi.os]
|
local level2 = level1[ffi.os]
|
||||||
if level2 == nil then return end
|
level2 = level2 or {}
|
||||||
|
|
||||||
local level3
|
local level3
|
||||||
for k, v in pairs(level2) do
|
for k, v in pairs(level2) do
|
||||||
@ -146,9 +149,11 @@ function Modification.SetMemoryOptions(memory)
|
|||||||
end
|
end
|
||||||
|
|
||||||
for k, v in pairs(memory) do
|
for k, v in pairs(memory) do
|
||||||
local address = level3[k]
|
local modFunc = level3[k]
|
||||||
if address ~= nil then
|
if modFunc ~= nil then
|
||||||
ffi.cast("char*", address)[0] = v
|
modFunc(v)
|
||||||
|
else
|
||||||
|
Message:ShowModificationUnsupported("processMemory", k, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -243,7 +248,8 @@ function Modification.RequiredChanges()
|
|||||||
if ModSettingGet("noita-mapcap.disable-mod-detection") then
|
if ModSettingGet("noita-mapcap.disable-mod-detection") then
|
||||||
memory["enableModDetection"] = 0
|
memory["enableModDetection"] = 0
|
||||||
else
|
else
|
||||||
memory["enableModDetection"] = 1
|
-- Don't actively (re)enable mod detection.
|
||||||
|
--memory["enableModDetection"] = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Disables or hides most of the UI.
|
-- Disables or hides most of the UI.
|
||||||
|
11
init.lua
11
init.lua
@ -150,10 +150,13 @@ end
|
|||||||
---@param isPaused boolean
|
---@param isPaused boolean
|
||||||
---@param isInventoryPause boolean
|
---@param isInventoryPause boolean
|
||||||
function OnPausedChanged(isPaused, isInventoryPause)
|
function OnPausedChanged(isPaused, isInventoryPause)
|
||||||
-- Set some stuff based on mod settings.
|
Message:CatchException("OnPausedChanged", function()
|
||||||
-- Normally this would be in `OnModSettingsChanged`, but that doesn't seem to be called.
|
-- Set some stuff based on mod settings.
|
||||||
local config, magic, memory, patches = Modification.RequiredChanges()
|
-- Normally this would be in `OnModSettingsChanged`, but that doesn't seem to be called.
|
||||||
Modification.SetMemoryOptions(memory)
|
local config, magic, memory, patches = Modification.RequiredChanges()
|
||||||
|
Modification.SetMemoryOptions(memory)
|
||||||
|
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Will be called when the game is unpaused, if player changed any mod settings while the game was paused.
|
---Will be called when the game is unpaused, if player changed any mod settings while the game was paused.
|
||||||
|
@ -318,7 +318,6 @@ modSettings = {
|
|||||||
id = "disable-mod-detection",
|
id = "disable-mod-detection",
|
||||||
ui_name = " Disable mod detection",
|
ui_name = " Disable mod detection",
|
||||||
ui_description = "If enabled, Noita will behave as if no mods are enabled.\nTherefore secrets like the cauldron will be generated.",
|
ui_description = "If enabled, Noita will behave as if no mods are enabled.\nTherefore secrets like the cauldron will be generated.",
|
||||||
hidden = true,
|
|
||||||
value_default = false,
|
value_default = false,
|
||||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user