mirror of
https://github.com/Dadido3/noita-mapcap.git
synced 2024-12-22 02:17:33 +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 = { ... },
|
||||
}
|
||||
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 Coords = require("coordinates")
|
||||
local ffi = require("ffi")
|
||||
local Memory = require("memory")
|
||||
local NXML = require("luanxml.nxml")
|
||||
local Utils = require("noita-api.utils")
|
||||
local Vec2 = require("noita-api.vec2")
|
||||
@ -92,38 +93,40 @@ end
|
||||
---@param memory table
|
||||
function Modification.SetMemoryOptions(memory)
|
||||
-- Lookup table with the following hierarchy:
|
||||
-- DevBuild -> OS -> BuildDate -> Option -> Address.
|
||||
-- DevBuild -> OS -> BuildDate -> Option -> ModFunc.
|
||||
local lookup = {
|
||||
[true] = {
|
||||
Windows = {
|
||||
[0x00F77B0C] = { _BuildString = "Build Apr 23 2021 18:36:55", -- GOG dev build.
|
||||
mPostFxDisabled = 0x010E3B6C,
|
||||
mGuiDisabled = 0x010E3B6D,
|
||||
mGuiHalfSize = 0x010E3B6E,
|
||||
mFogOfWarOpenEverywhere = 0x010E3B6F,
|
||||
mTrailerMode = 0x010E3B70,
|
||||
mDayTimeRotationPause = 0x010E3B71,
|
||||
mPlayerNeverDies = 0x010E3B72,
|
||||
mFreezeAI = 0x010E3B73,
|
||||
mPostFxDisabled = function(value) ffi.cast("char*", 0x010E3B6C)[0] = value end,
|
||||
mGuiDisabled = function(value) ffi.cast("char*", 0x010E3B6D)[0] = value end,
|
||||
mGuiHalfSize = function(value) ffi.cast("char*", 0x010E3B6E)[0] = value end,
|
||||
mFogOfWarOpenEverywhere = function(value) ffi.cast("char*", 0x010E3B6F)[0] = value end,
|
||||
mTrailerMode = function(value) ffi.cast("char*", 0x010E3B70)[0] = value end,
|
||||
mDayTimeRotationPause = function(value) ffi.cast("char*", 0x010E3B71)[0] = value end,
|
||||
mPlayerNeverDies = function(value) ffi.cast("char*", 0x010E3B72)[0] = value end,
|
||||
mFreezeAI = function(value) ffi.cast("char*", 0x010E3B73)[0] = value end,
|
||||
},
|
||||
[0x00F80384] = { _BuildString = "Build Apr 23 2021 18:40:40", -- Steam dev build.
|
||||
mPostFxDisabled = 0x010EDEBC,
|
||||
mGuiDisabled = 0x010EDEBD,
|
||||
mGuiHalfSize = 0x010EDEBE,
|
||||
mFogOfWarOpenEverywhere = 0x010EDEBF,
|
||||
mTrailerMode = 0x010EDEC0,
|
||||
mDayTimeRotationPause = 0x010EDEC1,
|
||||
mPlayerNeverDies = 0x010EDEC2,
|
||||
mFreezeAI = 0x010EDEC3,
|
||||
mPostFxDisabled = function(value) ffi.cast("char*", 0x010EDEBC)[0] = value end,
|
||||
mGuiDisabled = function(value) ffi.cast("char*", 0x010EDEBD)[0] = value end,
|
||||
mGuiHalfSize = function(value) ffi.cast("char*", 0x010EDEBE)[0] = value end,
|
||||
mFogOfWarOpenEverywhere = function(value) ffi.cast("char*", 0x010EDEBF)[0] = value end,
|
||||
mTrailerMode = function(value) ffi.cast("char*", 0x010EDEC0)[0] = value end,
|
||||
mDayTimeRotationPause = function(value) ffi.cast("char*", 0x010EDEC1)[0] = value end,
|
||||
mPlayerNeverDies = function(value) ffi.cast("char*", 0x010EDEC2)[0] = value end,
|
||||
mFreezeAI = function(value) ffi.cast("char*", 0x010EDEC3)[0] = value end,
|
||||
},
|
||||
},
|
||||
},
|
||||
[false] = {
|
||||
Windows = {
|
||||
[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.
|
||||
-- The page this is in is not writable, so this will crash Noita.
|
||||
-- This modification can be applied manually with some other tool that changes the permission prior to writing, like Cheat Engine.
|
||||
enableModDetection = function(value)
|
||||
local ptr = ffi.cast("char*", 0x0063D8AD)
|
||||
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.
|
||||
|
||||
local level1 = lookup[DebugGetIsDevBuild()]
|
||||
if level1 == nil then return end
|
||||
level1 = level1 or {}
|
||||
|
||||
local level2 = level1[ffi.os]
|
||||
if level2 == nil then return end
|
||||
level2 = level2 or {}
|
||||
|
||||
local level3
|
||||
for k, v in pairs(level2) do
|
||||
@ -146,9 +149,11 @@ function Modification.SetMemoryOptions(memory)
|
||||
end
|
||||
|
||||
for k, v in pairs(memory) do
|
||||
local address = level3[k]
|
||||
if address ~= nil then
|
||||
ffi.cast("char*", address)[0] = v
|
||||
local modFunc = level3[k]
|
||||
if modFunc ~= nil then
|
||||
modFunc(v)
|
||||
else
|
||||
Message:ShowModificationUnsupported("processMemory", k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -243,7 +248,8 @@ function Modification.RequiredChanges()
|
||||
if ModSettingGet("noita-mapcap.disable-mod-detection") then
|
||||
memory["enableModDetection"] = 0
|
||||
else
|
||||
memory["enableModDetection"] = 1
|
||||
-- Don't actively (re)enable mod detection.
|
||||
--memory["enableModDetection"] = 1
|
||||
end
|
||||
|
||||
-- Disables or hides most of the UI.
|
||||
|
11
init.lua
11
init.lua
@ -150,10 +150,13 @@ end
|
||||
---@param isPaused boolean
|
||||
---@param isInventoryPause boolean
|
||||
function OnPausedChanged(isPaused, isInventoryPause)
|
||||
-- Set some stuff based on mod settings.
|
||||
-- Normally this would be in `OnModSettingsChanged`, but that doesn't seem to be called.
|
||||
local config, magic, memory, patches = Modification.RequiredChanges()
|
||||
Modification.SetMemoryOptions(memory)
|
||||
Message:CatchException("OnPausedChanged", function()
|
||||
-- Set some stuff based on mod settings.
|
||||
-- Normally this would be in `OnModSettingsChanged`, but that doesn't seem to be called.
|
||||
local config, magic, memory, patches = Modification.RequiredChanges()
|
||||
Modification.SetMemoryOptions(memory)
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
---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",
|
||||
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.",
|
||||
hidden = true,
|
||||
value_default = false,
|
||||
scope = MOD_SETTING_SCOPE_RUNTIME,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user