From 1d4c1ca900a50be96593ea224c252dbae67b76f4 Mon Sep 17 00:00:00 2001 From: neru Date: Sat, 10 Jan 2026 01:50:36 -0300 Subject: [PATCH] feat: add Supermarket Together script --- lua/Cheat Engine/Supermarket.lua | 260 +++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 lua/Cheat Engine/Supermarket.lua diff --git a/lua/Cheat Engine/Supermarket.lua b/lua/Cheat Engine/Supermarket.lua new file mode 100644 index 0000000..a9219f1 --- /dev/null +++ b/lua/Cheat Engine/Supermarket.lua @@ -0,0 +1,260 @@ +if (mono_enumDomains() == nil) then LaunchMonoDataCollector() end + +local is64Bit = targetIs64Bit() +local ARRAY_SIZE_OFF = is64Bit and 0x18 or 0x10 +local ARRAY_DATA_OFF = is64Bit and 0x20 or 0x14 +local PTR_SIZE = is64Bit and 8 or 4 + +local monoTypeToVartypeLookup = { + [0x02] = vtByte, -- BOOLEAN + [0x03] = vtWord, -- CHAR + [0x04] = vtByte, -- I1 + [0x05] = vtByte, -- U1 + [0x06] = vtWord, -- I2 + [0x07] = vtWord, -- U2 + [0x08] = vtDword, -- I4 (Int32) + [0x09] = vtDword, -- U4 + [0x0a] = vtQword, -- I8 + [0x0b] = vtQword, -- U8 + [0x0c] = vtSingle, -- R4 + [0x0d] = vtDouble, -- R8 + [0x0e] = vtString, -- STRING + [0x0f] = vtPointer, -- PTR + [0x12] = vtPointer, -- CLASS + [0x14] = vtPointer -- ARRAY +} + +--[[ + generic helper functions +]] +function printf(fmt, ...) print(fmt:format(...)) end + +function splitByComma(str) + if str == nil or str == "" then return {} end + local result = {} + for word in string.gmatch(str, "([^,]+)") do table.insert(result, word) end + return result +end + +--[[ + mono helper functions +]] +function dumpFields(class) + print("class fields:") + for i, v in pairs(mono_class_enumFields(class)) do + printf("\tname: %s - static: %s", v.name, tostring(v.isStatic)) + end +end + +function getFieldFromName(class, name, shouldBeStatic) + local fields = mono_class_enumFields(class) + if not fields then return nil end + for i, v in pairs(fields) do + if v.isStatic == (shouldBeStatic == true) and v.name == name then + return v + end + end + print(("could not find field: %s"):format(name)) + return nil +end + +--[[ + mono wrappers +]] +-- UNUSED +function MonoArray(addr) + local classId, className = mono_object_getClass(addr) + if not className then error("address did not point to valid object") end + if className:sub(-2) ~= "[]" then error("address did not point to array") end + + local array = { + _address = addr, + getLength = function(self) + return readInteger(self._address + ARRAY_SIZE_OFF) + end, + getElement = function(self, idx) + if idx < 0 or idx >= self:getLength() then + error("invalid index") + end + return + readPointer(self._address + ARRAY_DATA_OFF + (idx * PTR_SIZE)) + end + } + + setmetatable(array, { + __tostring = function() return "Mono array instance" end, + __index = function(self, k) + local idx = tonumber(k) + if idx ~= nil then return self:getElement(idx) end + return rawget(self, k) + end + }) + return array +end + +-- UNUSED +local stringClass = mono_findClass("System", "String") +local string_Length = getFieldFromName(stringClass, "_stringLength") +local string_FirstChar = getFieldFromName(stringClass, "_firstChar") + +function MonoStringReader(addr) + if not addr or addr == 0 then return nil end + local classId, className = mono_object_getClass(addr) + if className ~= "String" then error("address did not point to string") end + + local str = { + _address = addr, + getLength = function(self) + return readInteger(self._address + string_Length.offset) + end, + getValue = function(self) + return readString(self._address + string_FirstChar.offset, + self:getLength() * 2, true) + end + } + + setmetatable(str, { + __tostring = function(self) return self:getValue() end, + __index = str + }) + return str +end + +function MonoFunctionHelper(fnName, fnId, isStatic) + local fn = { + _name = fnName, + _id = fnId, + _signatures = {}, + _static = isStatic + } + + if type(fnId) == "table" then + for i, v in ipairs(fnId) do + local sig = mono_method_getSignature(v) + table.insert(fn._signatures, {id = v, params = splitByComma(sig)}) + end + end + + setmetatable(fn, { + __call = function(self, ...) + local args = {...} + local methodArgs = args + local instancePtr = nil + + if not self._static then + if #args == 0 then + error("Instance method " .. self._name .. + " requires instance pointer") + end + instancePtr = table.remove(methodArgs, 1) + end + + local activeId = nil + if type(self._id) == "table" then + for _, candidate in ipairs(self._signatures) do + if #candidate.params == #methodArgs then + activeId = candidate.id + break + end + end + if not activeId then + error("No overload for " .. self._name .. + " matches arg count " .. #methodArgs) + end + else + activeId = self._id + end + + local paramsInfo = mono_method_get_parameters(activeId) + local monoArgs = {} + for i = 1, #methodArgs do + local pType = paramsInfo.parameters[i].type + table.insert(monoArgs, { + type = monoTypeToVartypeLookup[pType] or vtPointer, + value = methodArgs[i] + }) + end + + return mono_invoke_method(mono_enumDomains()[1], activeId, + instancePtr, monoArgs) + end, + __tostring = function(self) return "MonoFunction: " .. self._name end + }) + return fn +end + +function MonoClass(namespace, name) + local classId = mono_findClass(namespace, name) + if not classId then error("Could not find class " .. name) end + + local domain = mono_enumDomains()[1] + local staticAddr = mono_class_getStaticFieldAddress(domain, classId) + local methodsRaw = mono_class_enumMethods(classId) + + local methodsSorted = {} + for _, v in pairs(methodsRaw) do + if methodsSorted[v.name] then + if type(methodsSorted[v.name]) ~= "table" then + methodsSorted[v.name] = {methodsSorted[v.name]} + end + table.insert(methodsSorted[v.name], v.method) + else + methodsSorted[v.name] = v.method + end + end + + local methodsWrapped = {} + for mName, mId in pairs(methodsSorted) do + local checkId = type(mId) == "table" and mId[1] or mId + local flags = mono_method_getFlags(checkId) + local isStatic = bAnd(flags, 0x0010) ~= 0 + methodsWrapped[mName] = MonoFunctionHelper(mName, mId, isStatic) + end + + local classObj = { + _id = classId, + _name = name, + _ns = namespace, + _fieldInfo = {}, + _staticAddr = staticAddr, + _methods = methodsWrapped, + getStaticFieldOffset = function(self, name) + local field = getFieldFromName(self._id, name, true) + return field and field.offset or nil + end, + getStaticField = function(self, offset) + return readPointer(self._staticAddr + offset) + end, + getMethod = function(self, methodName) + local m = self._methods[methodName] + if not m then + error("Method " .. methodName .. " not found!") + end + return m + end + } + + return setmetatable(classObj, { + __index = function(self, key) + return self._methods[key] or rawget(self, key) + end + }) +end + +--[[ + game +]] +local GameDataClass = MonoClass("", "GameData") +local instanceOffset = GameDataClass:getStaticFieldOffset("Instance") + +printf("GameData class id: %s", tostring(GameDataClass)) +printf("GameData.Instance field offset: %x", instanceOffset or 0) + +local gameDataInstance = GameDataClass:getStaticField(instanceOffset) +printf("GameData: %x", gameDataInstance or 0) + +local moneySpentHelper = GameDataClass:getMethod("CmdAlterFundsWithoutExperience") + +printf("GameData.CmdAlterFundsWithoutExperience: %x", moneySpentHelper._id or 0) + +moneySpentHelper(gameDataInstance, -51170)