[REQUEST] Tap Ninja
Re: [REQUEST] Tap Ninja
april 6 update, not working
Re: [REQUEST] Tap Ninja
Same here. Not working.
Re: [REQUEST] Tap Ninja
Mine is already set to VEH Debugger before and running on Cheat Engine 7.4. Still no luck
viewtopic.php?f=4&t=19544
I found this one is working.
Re: [REQUEST] Tap Ninja
I don't know what happened, you can see my video, it's a cheat table that I downloaded from the forum and used (without editing the cheat table). the latest version of the game at this time.
Re: [REQUEST] Tap Ninja
CE Version : 7.4ReskyAW wrote: ↑Sat Mar 19, 2022 5:10 pmCode: Select all
Game Name : Tap Ninja - Idle Game Game Vendor : Steam Game Version/BuildID : v2.6.5 / 8447898 Game Last Update release : 28 March 2022 Game Process : Tap Ninja.exe CE Version : 7.4 Game Release date : 17 Mar, 2022
We have not tested with all values, but we have confirmed the operation of Gold, Elixir, Amber, Key, and Star values.
Game version is v2.7.0 which was updated today
Build 8566850.
It has perfectly worked.
Amazing table!
Thanks!
Last edited by tssj on Thu Apr 21, 2022 1:09 pm, edited 1 time in total.
Re: [REQUEST] Tap Ninja
i have the same issue with player data cant find this one "UpgradeManager:Update+1c: 0F B6 80 69 04 00 00 - movzx eax,byte ptr [rax+00000469]"
v2.7.2
Re: [REQUEST] Tap Ninja
First showing me this
//Here is full text
//Later back on when i launch it up as on the screen here
//When i select game i cannot launch any of scripts image down below.
// I CANT CHECK ANY OF BOXES OR CHANGE VALUES
+ maybe that helps //The first line shows up some error, idk what is goin on
//IM USING NEW .CT version
//Here is full text
Spoiler
--[[--------------------------------------------------------------------------------
-- Forms - Save strings as temp files, then load using createFormFromFile()
--------------------------------------------------------------------------------]]
local stringFormMonoClass = [==========[<?xml version="1.0" encoding="utf-8"?>
<FormData>
<formMonoClass Class="TCEForm" Encoding="Ascii85">/yTK^8${c!?7msfX.9L%MdIQc2%!i#a/iSfwG[w3Ysw8Y[Kxk5cW0K{aT8zrG5QUV]XxAk]V}0BB[_{Vs2AqX#xv[:OOyz4.If^1_L48mym]Ld2VgSleeO*UPkS@:9=bb=Knd-{Fwg6!XwK_6R]WT,izTxk+!F}][G^4i7DsGpKdC!im?-UB)-%rDskPUQF)Rzc]O6B[^z_1jKxFN;3I7ftCcx#cPvI2B]=2@,6K60%kK1Inq+.g}B+HlEQo:GCJB^HvLD=hC/st1bW!#ougl#/+QCofy@d(g#(?4h)n8Fz+i)?5xP$o*@=dcxr)_y%yGnV1$nxVZO[j:r2!YoA0+:wJL^qMWa,vD*+B#@Aa^}w.+i9/DmKABOH)-/:7OY#V[,0t=5X%AnZNZI@*4P)qUochB[2IovkhHMj}pgGSwG;$,pf+DV1m(P:6eYFu^E_4i5L4sm06/=9(J7sgHB8O!u?#{an(pMPbnmp0mu^idQi:$okj^1k1uL#v47W@Fp/*YhfEm]%Ci;ljoM.XY223(DY6Rw1U16MK{7p_0/,oR*QnI[Fx^!y8#j2ckVB]M#Nx8-_c+WC%64uvTTI4@LHr!jItz$,W392+Q{fnI+0ZtCx}tJD9YPG3]{w.DidKFjDj=a(KvF(!Ybq*}ChFDvxykE{+J#tY*OFjmoR(+2mnbeOfW6L(G}VH6iWtST43*e09*7_ViOIcO=vZQlX[;]P:XP:O6M/=H[=ynj_%^B#R%zdd94B?*STJO,#0_F?tQwd/1zU0A=]i8tY!aETBz0@KQl]EvYNz8vptiL[Ikvlkeh]!QcAwJIEFM-@Dm0,of/B6lzLjd8rM.Gnrpfh[pd*BDc8ZD{3.LaY.#?7HvWhx2*km@.8k@?mT2K5-1RP/@sV)KQkKCt%8mdvjf.RWoE7?]4qdK5r791pEnq06lk%U@FE0#]DJXJVY@=G0$jxo,z).[8j,RK1M0c81DiaKt9iB)TJ^frZR-yyca?(fKZ]Vg)1UN1(9oZN*#C}cZbo/q1+}b-]OP]=bq+tqn,{9S:jv5gov0-xD/b,@6QeXDuMD?87SU{rt4Hrj)e$Es?Lbf0CIUI(nQD0J]K73r}V?b/@f8mm,,6(sDjPbLj]EI^7T4dCGHNGn{Yav,s/u:/3H}_92;K+QF?o7A!IP*hw:FG6p.l-9ssiZVrCF%)tkrZjW7EQC0;Gu5)-93DF{wtVS,.rrp]v9wuiG@k1h]dr]6f0e,)[j1cvUB)p3+ZXQR5Y+IqN*U832[*Bii9Atfj5DM^1R2i),;p7,TTWtgGpCMyR#t=?cSt+w-4Ay5^,$?%1PleRCoEy*I@UNgbZD5JU:h,R1^LE((gIpcuQ6Y!Q}nmbV9Cn%pG5/$YWLSPU?]TWVY8/d*XGbEDX9GFx_.#z,L</formMonoClass>
</FormData>
]==========]
local stringFormMonoImage = [==========[<?xml version="1.0" encoding="utf-8"?>
<FormData>
<formMonoImage Class="TCEForm" Encoding="Ascii85">#goBu):ZT+Wtl^dlQI).N^(5n(hQF{X^MQJn@1eo5fn$ZHIXe;RtGr4190F.cqtCN1hVAXjWcD*0l3*K^?z^hx4+qE0Ksbh3I+QToQw1+QkP4f?kNSb@5lX/)jLR@IiYbP=eCTy?ZA0ZI(bMlCiZ8DJe+6*v,mF+jD,(E%sth}1CLRX$vU-f(i9$XRV@:QA!q!f%hX+b6)]dyOt_prhLp]G9=zuU40V/tG9sQgs]],Y[?WW7%bqQH*RxO9*We0CaVdE@Zgdl/@M}s?b/WI$4H*R(bICOq:vG*bM5b2J?8GRcx[DbAAiUL]9lSLizG[D0JP0B(qo^2%G=}m+ra!0MJv,XL@_ZnSM_HxTV)cJhf9I9+4KLxJqx*H(z;36.k,8JMD^Qkiz#h)TZim?DT^LGY6G3@@j/pDI11cbh0/UjyJR^0G:vZE5TzyieiSvQC;a(0j_I1#Y1q]28AO^@P9DA)8H^GkPRD[5Z%y%t^HKF?{v6g?tUv!E3cP_:l]R+yV4f4v4-{wf!3Qc8fU38JaQy9#^U-{8!JCVHkhj,CSHPtPb3.lul(V3=_gdgp9Os[Rb%_?;C,WXNw,f/6f.*]R#b*wa-.kPtDstrDfs3NGb%qJ6y3fSIFkJp7HU$DGcFw09Lg7,2RjA700</formMonoImage>
</FormData>
]==========]
local stringFormMonoSearch = [==========[<?xml version="1.0" encoding="utf-8"?>
<FormData>
<formMonoSearch Class="TCEForm" Encoding="Ascii85">%}#Ei,cprUq+#;fbZc+SoAl.WE)L-l:ODn@%^B_H;xFG;($Pw03Dcw=)cVojK[8=K)V+xt2#OX6(]Y18()T9kQCj]j@g2!t@,BL!hBKq?n}$;oZUQj6IDG.}-Iu34S^z*-OOG:=#3-7}=w#W,.XJY2SC}v@!ui8=Mm%*;0k!W+AZE[22D;N{gfq9HcD41k](}+f6X}X)=S^V3[T1{XwuIh7UVDfza3G6cpegQhCL(I}P[C8Z%M(*4?n{c[tonGI]eChq(2V([du2{+:Y!liP*$[aM@)34K[A6YhYP[M4_BgZm{^s0N]eG9yPo0pQ-YgaPhlI=;L)LHR#-hX@aUDg,?MYyH*=1u/6SgYX(^.;O=qgqlY[$!j^,PGLo@_5]dHU$m5oWgFUqzgja?$}TXj.q=v2p,j0O0^25Q,=F+X4HNZt%+7;h!q8SCyQ!hb9ieqIA:yj{*G9AJ6TTSoXsm/Y{b8A#3p%0Sx;N.v/uJ5pYsb!Ui4I+!UcYHI-yZ.0toph)!jm(/d@rebD.mI^7Fm@hU@b}*t^gcI7KXfiLkhT:8F)7lQ}oCZK5t!AHL,6dKE+WDYJ3ciQssLG5QHIkRvUnd)_-KG[gY2^LD/=C8DJ-e!66Rq[QUSWF1q-LQvUJVe5aiER.rI1EAUa1W?KgS9?l+jtAI?bA6SfC,!^5Imtu*{XYpQ=pUp._T3SuABBv;0OKs}EIDV{?=FaRxKa2AVTF/=MH;c0$RNK@M]4tBgT#3p4Ce6vsF(L.Ru*?q4Ry5GjbyNh}f2zHU+u)7mRKOdwauFaTW+GlE=W,#m2Qo$Vx*7aH^JUHsnKRVYv%Ps.[153E{^;IsVN?6ZB!J#=cpwjHlWShBW+w8{$Ez7djl.5SGh-y2^Q@cj8a]vZP0k3(2Wet_S!k*?A3Zxs*4$RpwQnc54F+Em$;y7Z2zoPx/5ezhKvx[{.sVMvLPGH%9dp)DjdU{/k2U@[o@QV;Dmq.3_3k_3-9CH-j]*bPeh*!-qjr,b4Q*^;TmF)=b.jgw/,m)HG6VW?f{2q?kW3ODQiLU)Va-T!vCmfcf:X.6^!@K!?Uj8bmqB/nmTC2xJSqL$YKjcQ(G-CxgpLw}F2:Ve8_]xHgj5-[*m]4/2R8xo17o*1NjEC22YGWpMhe@g}$S,@*UdDtDXv@J0NMnxBO):3pe*VpgAcI)IaU{)ts*iR$7-pNw%A@8FO]TMem#f.w{vbR=CSlIBsOcR,?wg!YjWNUFojeRt/W^GWUZXboc^S/_^8p.2q#E#eaPBo?{^z!dYtB;(f8)iAt?:V_bT#(Pc]Yyfg=NM?H2suncbWi2z(mL*OTW@xT]sjj)%TR5.AF5UQE^A:VIAE*2O-zMaeztg.H;7UcPr.[BXO+aDs*K)4</formMonoSearch>
</FormData>
]==========]
local function saveForm(text)
local path = os.tmpname() -- get temp file name
local f, err = io.open(path, "w")
if f == nil then return nil, err end
f:write(text)
f:close()
return path
end
local function createFormFromString(text)
local path = saveForm(text)
local form = createFormFromFile(path)
pcall(os.remove, path)
return form
end
formMonoClassB = createFormFromString(stringFormMonoClass)
formMonoImage = createFormFromString(stringFormMonoImage)
formMonoSearch = createFormFromString(stringFormMonoSearch)
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/bootstrap.lua
--------------------------------------------------------------------------------]]
-- loadstring(loadTextFile('src/lua/util.lua'))()
-- loadstring(loadTextFile('../temp/notes.lua'))()
-- loadstring(loadTextFile('src/lua/mono.lua'))()
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/util.lua
--------------------------------------------------------------------------------]]
util = util or {}
util.loadTextFile = function (name, useTableFile)
if useTableFile then
local tableFile = findTableFile(name)
if not tableFile then return nil, 'Unable to open table file "'..tostring(name)..'"' end
local ss = createStringStream()
ss.Position = 0 -- recommended on wiki: [Link]
ss.CopyFrom(tableFile.Stream, 0)
local text = ss.DataString
ss.destroy()
return text
else
local path = getMainForm().openDialog1.InitialDir..name
local f, err = io.open(path, "r")
-- fall back to table file if disk file error (doesn't exist)
if f == nil then return loadTextFile(name, true) end
local text = f:read("*all")
f:close()
return text
end
end
--[[
Save a string to a text file. If useTableFile is true it will be saved as
a TableFile. The directory should be where the cheat file is, it is the
initial directory for the dialog when you are saving your cheat table.
--]]
util.saveTextFile = function(name, text, useTableFile)
if useTableFile then
local tf = findTableFile(name)
if tf ~= nil then
tf.delete()
tf = nil
end
tf = createTableFile(name)
local ss = createStringStream(text)
tf.Stream.CopyFrom(ss, 0)
ss.destroy()
return true
else
local path = getMainForm().saveDialog1.InitialDir..name
local f, err = io.open(path, "w")
if f == nil then return nil, err end
f:write(text)
f:close()
return true
end
end
--[[
For mono symbols, you have to surround them in quotes for use in AA
]]
function util.safeAddress(s)
local tbl = {}
for a in string.gmatch(s, "([^+]+)[+]?") do table.insert(tbl, a) end
local result = '"'..tbl[1]..'"'
if #tbl > 1 then result = result..'+'..tbl[2] end
return result
end
function util.split(s, separator)
local tbl = {}
for str in string.gmatch(s, "([^"..separator.."]+)") do table.insert(tbl, str) end
return tbl
end
function util.trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
--[[
Pretty-print serialize the passed object and return the results
as a string. This does nice indenting and follows metatables. It
is not guaranteed to provide the same object back if the string is
loaded because it just replaces circular references with a string
among other things. It does try to keep tables/arrays on one line
if there is room (120 characters).
--]]
function util.pretty(value, funcs, indent, done, stack)
indent = indent or 0
funcs = funcs or {}
done = done or {}
stack = stack or { {value=value} } -- initialize with passed value, no key
-- if a 'userdata', use metatable
local l = value
if type(value) == "userdata" then
l = getmetatable(value)
end
if type(l) == "table" then
-- if we've already serialized the table, return string representation
-- to avoid self-referencing in recursion. Only the first serialization
-- will have the full serialized string
if done[l] then return tostring(l) end
done[l] = true
local list = {}
-- here we return "{", the values, and "}"
if (#l > 0) then
-- if we have a count ('#'), then it's just an array of values
for i,v in ipairs(l) do
table.insert(list, util.pretty(v, funcs, indent + 2, done, stack))
end
else
-- no count, so either empty or object with key/value
-- first we sort the keys
local keys = {}
for k,v in pairs(l) do table.insert(keys, k) end
table.sort(keys)
local last = {key = "UNKNOWN", value = "NONE"}
table.insert(stack, last)
for i,k in ipairs(keys) do
local propValue = value[k]
last.key = k
last.value = value[k]
for j,f in ipairs(funcs) do
local returnValue = f(value, k, value[k], stack, indent)
if returnValue ~= nil then
propValue = returnValue
break
end
end
table.insert(list, string.format("%s = %s", tostring(k), util.pretty(propValue, funcs, indent+2, done, stack)))
end
table.remove(stack, #stack)
end
-- now we find the total size, add 200 if there are line breaks
local size = 0
for i,v in ipairs(list) do
size = size + string.len(v) + 2
if string.find(v, "\r\n") then
size = 200
break;
end
end
-- if it's small enough, join with commas
if (size < 120) then
local result = {"{ "}
for i,v in ipairs(list) do
if i > 1 then table.insert(result, ", ") end
table.insert(result, v)
end
table.insert(result, " }")
return table.concat(result);
else
local indentString1 = string.rep(" ", indent)
local indentString2 = string.rep(" ", indent + 2)
local result = {"{\r\n"}
for i,v in ipairs(list) do
table.insert(result, indentString2)
table.insert(result, v)
if (i < #list) then table.insert(result, ",") end
table.insert(result, "\r\n")
end
-- table.insert(result, indentString)
table.insert(result, indentString1)
table.insert(result, "}")
return table.concat(result);
end
end
if type(value) == "string" then
return string.format("\"%s\"", value)
end
return tostring(value)
end
--[[
Values and functions used by util.serialize(t)
---]]
local oddvals = {[tostring(1/0)] = '1/0', [tostring(-1/0)] = '-1/0', [tostring(-(0/0))] = '-(0/0)', [tostring(0/0)] = '0/0'}
local kw = {['and'] = true, ['break'] = true, ['do'] = true, ['else'] = true,
['elseif'] = true, ['end'] = true, ['false'] = true, ['for'] = true,
['function'] = true, ['goto'] = true, ['if'] = true, ['in'] = true,
['local'] = true, ['nil'] = true, ['not'] = true, ['or'] = true,
['repeat'] = true, ['return'] = true, ['then'] = true, ['true'] = true,
['until'] = true, ['while'] = true}
local getchr = function(c)
return "\\" .. c:byte()
end
local make_safe = function(text)
return ("%q"):format(text):gsub('\n', 'n'):gsub("[\128-\255]", getchr)
end
local write = function(t, memo, rev_memo)
local ty = type(t)
if ty == 'number' then
t = format("%.17g", t)
return oddvals[t] or t
elseif ty == 'boolean' or ty == 'nil' then
return tostring(t)
elseif ty == 'string' then
return make_safe(t)
elseif ty == 'table' or ty == 'function' then
if not memo[t] then
local index = #rev_memo + 1
memo[t] = index
rev_memo[index] = t
end
return '_[' .. memo[t] .. ']'
else
error("Trying to serialize unsupported type " .. ty)
end
end
local write_key_value_pair = function(k, v, memo, rev_memo, name)
if type(k) == 'string' and k:match '^[_%a][_%w]*$' and not kw[k] then
return (name and name .. '.' or '') .. k ..'=' .. write(v, memo, rev_memo)
else
return (name or '') .. '[' .. write(k, memo, rev_memo) .. ']=' .. write(v, memo, rev_memo)
end
end
local is_cyclic = function(memo, sub, super)
local m = memo
local p = memo
return m and p and m < p
end
local write_table_ex = function(t, memo, rev_memo, srefs, name)
if type(t) == 'function' then
return '_[' .. name .. ']=loadstring' .. make_safe(string.dump(t))
end
local m = {}
local mi = 1
for i = 1, #t do -- don't use ipairs here, we need the gaps
local v = t
if v == t or is_cyclic(memo, v, t) then
srefs[#srefs + 1] = {name, i, v}
m[mi] = 'nil'
mi = mi + 1
else
m[mi] = write(v, memo, rev_memo)
mi = mi + 1
end
end
for k,v in pairs(t) do
if type(k) ~= 'number' or math.floor(k) ~= k or k < 1 or k > #t then
if v == t or k == t or is_cyclic(memo, v, t) or is_cyclic(memo, k, t) then
srefs[#srefs + 1] = {name, k, v}
else
m[mi] = write_key_value_pair(k, v, memo, rev_memo)
mi = mi + 1
end
end
end
return '_[' .. name .. ']={' .. table.concat(m, ',') .. '}'
end
--[[
A function more appropriate for serializing objects in a reproduceable way.
--]]
function util.serialize(t)
local memo = {[t] = 0}
local rev_memo = {[0] = t}
local srefs = {}
local result = {}
-- phase 1: recursively descend the table structure
local n = 0
while rev_memo[n] do
result[n + 1] = write_table_ex(rev_memo[n], memo, rev_memo, srefs, n)
n = n + 1
end
-- phase 2: reverse order
for i = 1, n*.5 do
local j = n - i + 1
result, result[j] = result[j], result
end
-- phase 3: add all the tricky cyclic stuff
for i, v in ipairs(srefs) do
n = n + 1
result[n] = write_key_value_pair(v[2], v[3], memo, rev_memo, '_[' .. v[1] .. ']')
end
-- phase 4: add something about returning the main table
if result[n]:sub(1, 5) == '_[0]=' then
result[n] = 'return ' .. result[n]:sub(6)
else
result[n + 1] = 'return _[0]'
end
-- phase 5: just concatenate everything
result = table.concat(result, '\n')
return n > 1 and 'local _={}\n' .. result or result
end
function util.map(t, f)
local results = {}
local func = f or tostring
for i,v in ipairs(t) do
table.insert(results, func(v))
end
return results
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/temp/notes.lua
--------------------------------------------------------------------------------]]
--[[
A note has 'key' and 'text' fields, lists are sorted by key.
--]]
notes = notes or {}
notesmt = notesmt or {}
note = note or {}
notemt = notemt or {}
notemt.__index = note
notemt.__lt = function(a,b) return a.key < b.key end
function notes.new(filename, useTableFile)
local obj = {}
setmetatable(obj, {__index = notes})
obj:load(filename, useTableFile)
return obj
end
function notes:load(filename, useTableFile)
self.filename = filename
self.useTableFile = useTableFile
local s = loadTextFile(self.filename, self.useTableFile)
self.dict = {}
if s then self.dict = loadstring(s)() end
self.lastSave = os.clock()
self.lastChange = 0
end
function notes:save()
local text = util.serialize(self.dict)
saveTextFile(self.filename, text, self.useTableFile)
self.lastSave = os.clock()
end
function notes:saveAs(filename, useTableFile)
self.filename = filename
self.useTableFile = useTableFile
self:save()
end
function notes:update(key, text)
if text == nil or string.len(text) == 0 then
if self.dict[key] then self.dict[key] = nil end
else
self.dict[key] = {key = key, text = text}
end
self.lastUpdate = os.clock()
end
function notes:getList()
local keys = {}
for k,n in pairs(self.dict) do table.insert(keys, n) end
return keys
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono.lua
--------------------------------------------------------------------------------]]
-- moduled
mono = mono or {}
-- classes
mono.MonoClass = mono.MonoClass or {}
mono.MonoField = mono.MonoField or {}
mono.MonoMethod = mono.MonoMethod or {}
mono.MonoImage = mono.MonoImage or {}
mono.menu = mono.menu or {}
mono.popups = mono.popups or {}
mono.formSelectImage = mono.formSelectImage or {}
mono.formSearch = mono.formSearch or {}
mono.formClass = mono.formClass or {}
-- defines
mono.TYPE_NAMES = {
[0] = 'END',
[1] = 'void',
[2] = 'bool',
[3] = 'char',
[4] = 'sbyte',
[5] = 'byte',
[6] = 'short',
[7] = 'ushort',
[8]= 'int',
[9]= 'uint',
[10] = 'long',
[11] = 'ulong',
[12] = 'float',
[13] = 'double',
[14] = 'string',
[15] = 'ptr',
[16] = 'byref',
[17] = 'valuetype',
[18] = 'class',
[19] = 'var',
[20] = 'array',
[21] = 'genericinst',
[22] = 'typedbyref',
[24] = 'I',
[25] = 'U',
[0x1b] = 'FNPTR',
[0x1c] = 'object',
[0x1d] = 'szarray', -- 0-based one-dim-array
[0x1e] = 'mvar',
[0x1f] = 'CMOD_REQD', -- typedef or typeref token
[0x20] = 'CMOD_OPT', -- optional arg: typedef or typeref token
[0x21] = 'INTERNAL',
[0x40] = 'MODIFIER', -- Or with the following types
[0x41] = 'SENTINEL', -- sentinel for varargs method signature
[0x45] = 'PINNED', -- local var that points to pinned object
[0x55] = 'ENUM', -- an enumeration
}
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono/monofield.lua
--------------------------------------------------------------------------------]]
local MonoField = mono.MonoField
MonoField.mt = {
__index = MonoField,
__tostring = function(t)
return 'MonoField '..tostring(t.id)..' "'..tostring(t.name)..'"'
end,
__lt = function(a, b)
return a.lowerName < b.lowerName
end
}
-- class is a monoclass table, f is a table with results from
-- mono_class_enumFields
function MonoField.new(class, f)
local obj = {
class = class,
id = f.field,
name = f.name,
isStatic = f.isStatic,
isConst = f.isConst,
offset = f.offset,
monoType = f.monotype,
lowerName = string.lower(f.name),
foundTypeName = f.typeName,
typeName = mono.TYPE_NAMES[f.monotype] or 'UnknownType'..tostring(f.monotype)
}
-- should be types where we might have a 'Class'
--if f.monotype == 15 or f.monotype == 16 or f.monotype == 18 or f.monotype then
--end
obj.typeClassId = mono_type_getClass(obj.id)
if obj.typeClassId ~= nil then
-- this is the 'type' of the field
obj.typeClass = class.image.classesById[obj.typeClassId]
if obj.typeClass ~= nil then
obj.typeName = obj.typeClass.name
else
if class.image.missingNames[obj.typeClassId] then
obj.typeName = class.image.missingNames[obj.typeClassId]
else
obj.typeName = mono_class_getFullName(obj.typeClassId)
class.image.missingNames[obj.typeClassId] = obj.typeName
end
end
end
setmetatable(obj, MonoField.mt)
return obj
end
function MonoField:getFullName()
local s = ""
if self.class.namespace ~= nil and self.class.namespace:len() > 0 then s = self.class.namespace..':' end
s = s..self.class.name..'.'
s = s..self.name
return s
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono/monomethod.lua
--------------------------------------------------------------------------------]]
local MonoMethod = mono.MonoMethod
MonoMethod.mt = {
__index = MonoMethod,
__tostring = function(t)
return 'MonoMethod '..tostring(t.id)..' "'..tostring(t.name)..'"'
end,
__lt = function(a, b)
return a.lowerName < b.lowerName
end
}
function MonoMethod.new(class, m)
local obj = {
class = class,
id = m.method,
name = m.name,
lowerName = string.lower(m.name),
parameters = {}
}
setmetatable(obj, MonoMethod.mt)
lastmethod = obj
local types, parameternames, returntype = mono_method_getSignature(obj.id)
local typenames={}
local tn
if types ~= nil then
for tn in string.gmatch(types, '([^,]+)') do
table.insert(typenames, tn)
end
end
for i=1,#typenames do
table.insert(obj.parameters, { name = parameternames, type = typenames })
end
obj.returnType = returntype
return obj
end
--[[
Get parameters for method
--]]
function MonoMethod:fetchParms()
if self.parms ~= nil then return nil end
if self.class.image.ignoredClassesByName[self.class.name] ~= nil then return nil end
local status, parms = pcall(function()
local result = mono_method_get_parameters(self.id)
return result
end)
if status then
-- success!
self.parms = parms
else
self.class.image.ignoredClassesByName[self.class.name] = true
print('Error with class '..tostring(self.class.name)..' method '..tostring(self.name))
error('Error fetching parameters for '..tostring(self.class.name)..'.'..tostring(self.name))
end
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono/monoclass.lua
--------------------------------------------------------------------------------]]
local MonoClass = mono.MonoClass
MonoClass.mt = {
__index = MonoClass,
__tostring = function(t)
return 'MonoClass '..tostring(t.id)..' "'..tostring(t.name)..'"'
end,
__lt = function(a, b)
return a.lowerName < b.lowerName
end
}
local MonoField = mono.MonoField
local MonoMethod = mono.MonoMethod
--[[
Called from MonoImage:_init, 'c' is what is returned from enumerating classes.
--]]
function MonoClass.new(image, c)
local obj = {
name = c.classname,
namespace = c.namespace,
id = c.class,
lowerName = string.lower(c.classname),
image = image
}
setmetatable(obj, MonoClass.mt)
return obj
end
function MonoClass:initFields()
self.fields = {}
self.fieldsByName = {}
self.fieldsByLowerName = {}
local constFieldCount = 0
local temp = mono_class_enumFields(self.id)
for i,f in ipairs(temp) do
local field = MonoField.new(self, f)
table.insert(self.fields, field)
self.fieldsByLowerName[field.lowerName] = field;
self.fieldsByName[field.name] = field
if field.isStatic and field.isConst then
field.constValue = constFieldCount
constFieldCount = constFieldCount + 1
end
end
table.sort(self.fields)
end
function MonoClass:initMethods()
self.methods = {}
self.methodsByName = {}
self.methodsByLowerName = {}
local temp = mono_class_enumMethods(self.id)
for i,m in ipairs(temp) do
local method = MonoMethod.new(self, m)
table.insert(self.methods, method)
self.methodsByName[method.name] = method
self.methodsByLowerName[method.lowerName] = method
end
table.sort(self.methods)
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono/monoimage.lua
--------------------------------------------------------------------------------]]
local MonoImage = mono.MonoImage
MonoImage.mt = {
__index = MonoImage,
__tostring = function(t)
return 'MonoImage '..tostring(t.name)
end
}
local MonoClass = mono.MonoClass
--[[
List names of images, a name can be passed to MonoImage.new()
or MonoImage(name) to create a MonoImage instance.
--]]
function MonoImage.enumerate()
local names = {}
mono_enumImages(function(img)
local name = mono_image_get_name(img)
table.insert(names, name)
end)
table.sort(names)
return names
end
--[[
Constructor doesn't do much, call :init(name, progress)
--]]
function MonoImage.new()
local obj = { }
setmetatable(obj, MonoImage.mt)
return obj
end
--[[
init() takes the image name and an optional callback for reporting progress
and when it is complete. The signature for this function is:
function progress(complete, image, message, processed, total)
This callback should be executed on the main CE thread.
--]]
function MonoImage:init(name, progress)
if monopipe == nil then
print('Launching mono data collector...')
LaunchMonoDataCollector()
end
self.domains = mono_enumDomains()
self.domain = self.domains[1]
self.classes = {} -- straight list of all classes
self.classesByName = {} -- dictionary for access by name
self.classesByLowerName = {} -- for access by lower case name
self.classesById = {} -- for access by id
self.ignoredClassesByName = {} -- for ignoring classes in searches, etc.
self.progress = progress
self.name = name or 'Assembly-CSharp' -- intelligent default for unity
-- get id
self.id = nil
mono_enumImages(function(img)
local foundName = mono_image_get_name(img)
if foundName == self.name then self.id = img end
end)
if not self.id then
print('NO ID!')
print('name is', name, self.name)
self:report(false, 'Error finding image named '..tostring(name))
self.progress = nil
self.total = nil
self.count = nil
return false
end
createThread(function(thread)
self.thread = thread
self:_init(thread)
end)
end
--[[
This is the function called by init() on another thread
--]]
function MonoImage:_init(thread)
--print('MonoImage:_init thread is', thread, 'self is', tostring(self))
self.thread = thread
local temp = mono_image_enumClasses(self.id)
-- thread.synchronize(function(th)
-- print('Image '..tostring(self.id)..' has classes: '..tostring(temp))
-- end)
self.classes = {}
self.classesByName = {}
self.classesByLowerName = {}
self.classesById = {}
self.missingNames = {}
self.total = #temp
self.count = 0
self.message = "Getting classes"
self:report(false, "Getting classes")
-- populate classes without much information first so we can
-- access our definitions when filling in parents, properties, etc.
for i,c in ipairs(temp) do
local class = MonoClass.new(self, c)
if class.lowerName ~= nil then
table.insert(self.classes, class)
self.classesByName[class.name] = class
self.classesByLowerName[class.lowerName] = class
self.classesById[class.id] = class
else
--print("Nil lowername for class", c.class)
end
self.count = i
if (i % 100 == 0 or i == #temp) then
self:report(false, 'Fetching classes')
end
end
table.sort(self.classes)
for i,class in ipairs(self.classes) do
class.parentId = mono_class_getParent(class.id)
class.parent = self.classesById[class.parentId]
self.count = i
if (i % 100 == 0 or i == #temp) then
self:report(false, 'Fetching parents')
end
end
self.count = 0
for i,class in ipairs(self.classes) do
class:initFields()
class:initMethods()
self.count = i
if (i % 100 == 0 or i == #temp) then
self:report(false, 'Initializing fields and methods')
end
end
self:report(true, 'Done')
end
--[[
Report progress on the main CE thread to do gui updates
--]]
function MonoImage:report(done, message)
if self.progress ~= nil and self.thread ~= nil then
self.thread.synchronize(function(thread)
self.progress(done, self, message, self.count, self.total)
end)
end
if done then
-- do cleanup here
self.count = nil
self.total = nil
self.progress = nil
self.thread = nil
end
end
--[[ Sample code... weird
--return util.pretty(mono.MonoImage.enumerate())
mi = mono.MonoImage.new()
print('mi is "'..tostring(mi)..'"')
mi:init('Assembly-CSharp', function(c, i, m, p, t)
print(m, p, t)
end)
--LaunchMonoDataCollector()
local thread = createThread(function(th)
TESTIMAGES = {}
mono_enumImages(function(img)
th.synchronize(function() print(img) end)
end)
end)
--]]
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/monomenu.lua
--------------------------------------------------------------------------------]]
function mono.menu:init()
--if self.menuSearch or self.timer then return end
self.timer = createTimer()
self.timer.Interval = 1000
self.timer.OnTimer = function(timer)
-- wait for normal mono script to create mono menu, check every 5 seconds
if not miMonoTopMenuItem then return end
self.timer.destroy()
self.timer = nil
self.menuSearch = createMenuItem(miMonoTopMenuItem)
self.menuSearch.Caption = 'Search'
self.menuSearch.Name = 'miMonoSearch'
self.menuSearch.OnClick = function(sender) self:OnSearchClick() end
miMonoTopMenuItem.add(self.menuSearch)
end
end
function mono.menu:OnSearchClick()
if mono.selectedImage then
mono.formSearch:show()
formMonoSearch:centerScreen()
else
mono.formSelectImage:show()
end
end
mono.menu:init()
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/forms/formSelectImage.lua
--------------------------------------------------------------------------------]]
local DEFAULT_ASSEMBLY_NAME = "Assembly-CSharp"
local imageHasBeenSelected = false
function mono.formSelectImage:show()
LaunchMonoDataCollector()
formMonoImage.listImages.Visible = true
formMonoImage.buttonSelectImage.Visible = true
formMonoImage.progressImage.Visible = false
formMonoImage.labelMessage.Visible = false
self.imageNames = mono.MonoImage.enumerate()
table.sort(self.imageNames)
local items = formMonoImage.listImages.Items
items.Clear()
local foundIndex = 0
for i,name in ipairs(self.imageNames) do
items.add(name)
if name == DEFAULT_ASSEMBLY_NAME then foundIndex = i end
end
local handler = function(sender)
mono.formSelectImage.OnSelectImage(self)
end
formMonoImage.buttonSelectImage.OnClick = handler
formMonoImage.listImages.OnDblClick = handler
formMonoImage.show()
if foundIndex ~= 0 and imageHasBeenSelected == false then
formMonoImage.listImages.ItemIndex = foundIndex - 1
mono.formSelectImage:OnSelectImage()
end
end
function mono.formSelectImage:OnSelectImage()
imageHasBeenSelected = true
local index = formMonoImage.listImages.ItemIndex + 1
if self.imageNames == nil or index < 1 or index > #self.imageNames then return end
local imageName = self.imageNames[index]
formMonoImage.listImages.Visible = false
formMonoImage.buttonSelectImage.Visible = false
formMonoImage.progressImage.Visible = true
formMonoImage.labelMessage.Visible = true
formMonoImage.labelMessage.Caption = 'Finding classes...'
local image = mono.MonoImage.new()
formMonoImage.progressImage.Min = 0
local progressHandler = function(done, image, message, count, total)
if total ~= nil and total > 0 and count ~= nil and count >= 0 then
message = string.format('%s %d/%d', message, count, total)
formMonoImage.progressImage.Max = total
formMonoImage.progressImage.Position = count
end
formMonoImage.labelMessage.Caption = message
if done then self:OnImageComplete(image) end
end
image:init(imageName, progressHandler)
end
function mono.formSelectImage:OnImageComplete(image)
self.image = image
mono.selectedImage = image
formMonoImage.Close()
mono.formSearch:show()
formMonoSearch:centerScreen()
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/forms/formSearch.lua
--------------------------------------------------------------------------------]]
--[[
Form for searching an image.
Controls:
pageMain.tabSearch - main tab
editSearchText: TCEEdit
listSearchClasses: TCEListView
listSearchFields: TCEListView
listSearchMethods: TCEListView
miImage: TMenuItem - main menu Image entry
miImageSelectImage - entry to re-open select image form
--]]
mono.formSearch.found = mono.formSearch.found or {}
mono.formSearch.found.classes = mono.formSearch.found.classes or {}
mono.formSearch.found.fields = mono.formSearch.found.fields or {}
mono.formSearch.found.methods = mono.formSearch.found.methods or {}
--[[
Show the search form, mono.selectedImage should be set to the
image to search.
--]]
function mono.formSearch:show()
if mono.selectedImage == nil then return end
self.image = mono.selectedImage
-- setup event handlers
formMonoSearch.miImageSelectImage.OnClick = function(sender) self:SelectImageClick() end
formMonoSearch.listSearchClasses.OnData = function(sender, listitem)
self:listSearchClasses_OnData(sender, listitem)
end
formMonoSearch.listSearchClasses.OnDblClick = function(sender)
local class = self.found.classes[sender.ItemIndex + 1]
mono.formClass:show(class, nil, nil)
end
formMonoSearch.listSearchFields.OnData = function(sender, listitem)
self:listSearchFields_OnData(sender, listitem)
end
formMonoSearch.listSearchFields.OnDblClick = function(sender)
local field = self.found.fields[sender.ItemIndex + 1]
if field then
mono.formClass:show(field.class, field, nil)
end
end
formMonoSearch.listSearchMethods.OnData = function(sender, listitem)
self:listSearchMethods_OnData(sender, listitem)
end
formMonoSearch.listSearchMethods.OnDblClick = function(sender)
local method = self.found.methods[sender.ItemIndex + 1]
if method then
mono.formClass:show(method.class, nil, method)
end
end
formMonoSearch.editSearchText.OnChange = function(sender)
self:search(sender.text)
end
-- popups
formMonoSearch.listSearchClasses.PopupMenu = formMonoSearch.popupClasses
local count = 0
formMonoSearch.popupClasses.OnPopup = function(sender)
print('formMonoSearch.popupClasses.OnPopup "'..tostring(sender.name)..'" count '..tostring(count))
count = count + 1
end
-- perform initial search to set 'found' results, probably empty text
self:search(formMonoSearch.editSearchText.Text)
-- show form
formMonoSearch.show()
end
--[[
When menu item to select an image is clicked, hide this form and
show the image select form
--]]
function mono.formSearch:SelectImageClick()
formMonoSearch.close()
mono.formSelectImage:show()
end
--[[
When typing in the edit box to filter results, filter the lists of
classes, fields, and methods with the lower case of the text
--]]
function mono.formSearch:search(text)
local lower = nil
if text ~= nil then lower = text:lower() end
local classes = {}
local methods = {}
local fields = {}
for i,class in ipairs(self.image.classes) do
if lower == nil or class.lowerName:find(lower, 1, true) ~= nil then table.insert(classes, class) end
for j,method in ipairs(class.methods) do
if lower == nil or method.lowerName:find(lower, 1, true) ~= nil then table.insert(methods, method) end
end
for j,field in ipairs(class.fields) do
if lower == nil or field.lowerName:find(lower, 1, true) ~= nil then table.insert(fields, field) end
end
end
self.found.classes = classes
self.found.fields = fields
self.found.methods = methods
formMonoSearch.listSearchClasses.Items.Count = #classes
formMonoSearch.listSearchFields.Items.Count = #fields
formMonoSearch.listSearchMethods.Items.Count = #methods
end
-- handler to display classes in list view
function mono.formSearch:listSearchClasses_OnData(sender, listitem)
local index = listitem.Index + 1
local class = self.found.classes[index]
listitem.Caption = class.name
end
-- handler to display fields in list view
function mono.formSearch:listSearchFields_OnData(sender, listitem)
local field = self.found.fields[listitem.Index + 1]
if field == nil then
listitem.Caption = 'nil index '..tostring(index)
else
listitem.Caption = field.name
listitem.SubItems.text = field.class.name
end
end
-- handler to display methods in list view
function mono.formSearch:listSearchMethods_OnData(sender, listitem)
local method = self.found.methods[listitem.Index + 1]
if method == nil then
listitem.Caption = 'nil index '..tostring(index)
else
listitem.Caption = method.name
listitem.SubItems.text = method.class.name
end
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/forms/formClass.lua
--------------------------------------------------------------------------------]]
--[[
Form for viewing a class
Controls:
listFields: TCEListView
listMethods: TCEListView
miSortFieldsByOffset - if Checked, sort by offset, otherwise by name
miShowInherited - if Checked, add in methods and fields of parent(s)
miShowUsage - if Checked, funcs and methods will be on other classes that use the type
miFindFields - When clicked, find fields in all classes that have this class as a type
miFindMethodCode - When clicked, find methods in all classes that have this class as a type
Standard Mode:
listFields (can sort by name (default) or offset)
Offset
Type
Name
Class - class it is defined on, blank if not inherited
listMethods (sorted by name)
Name
Signature (return type, parameter types and names)
Class - class it is defined on, blank if not inherited
Usage Mode:
listFields (sorted by Class then Name)
Offset
Type
Class
--]]
local sortByClassThenName = function(a, b)
if a.isConst and not b.isConst then return true end
if b.isConst and not a.isConst then return false end
if a.isConst and b.isConst then return a.constValue < b.constValue end
if a.class.lowerName < b.class.lowerName then return true end
if b.class.lowerName < a.class.lowerName then return false end
return a.name < b.name
end
local sortByClassThenOffset = function(a, b)
if a.isConst and not b.isConst then return true end
if b.isConst and not a.isConst then return false end
if a.isConst and b.isConst then return a.constValue < b.constValue end
if a.class.lowerName < b.class.lowerName then return true end
if b.class.lowerName < a.class.lowerName then return false end
return a.offset < b.offset
end
local sortByName = function(a, b)
if a.isConst and not b.isConst then return true end
if b.isConst and not a.isConst then return false end
if a.isConst and b.isConst then return a.constValue < b.constValue end
return a.lowerName < b.lowerName
end
local sortByOffset = function(a, b)
if a.isConst and not b.isConst then return true end
if b.isConst and not a.isConst then return false end
if a.isConst and b.isConst then return a.constValue < b.constValue end
return a.offset < b.offset
end
--[[
Show the search form, mono.selectedImage should be set to the
image to search.
--]]
function mono.formClass:show(class, field, method)
if mono.selectedImage == nil or class == nil then return end
self.image = mono.selectedImage
self.class = class
-- function to update lists, for setting on menu items that
-- make other changes
local funcUpdate = function(sender) self:setFieldsAndMethods() end
formMonoClass.listFields.OnData = function(sender, listitem)
self:listFields_OnData(sender, listitem)
end
formMonoClass.listFields.OnDblClick = function(sender)
self:listFields_OnDblClick(sender)
end
formMonoClass.listMethods.OnData = function(sender, listitem)
self:listMethods_OnData(sender, listitem)
end
formMonoClass.listMethods.OnDblClick = function(sender)
self:listMethods_OnDblClick(sender)
end
formMonoClass.miSortFieldsByOffset.OnClick = funcUpdate
formMonoClass.miShowInherited.OnClick = funcUpdate
formMonoClass.miShowUsage.OnClick = funcUpdate
-- create our own lists of fields and methods that we can sort
-- and filter
self:setFieldsAndMethods()
-- show form
formMonoClass.show()
end
--[[
Set fields and methods arrays, sorted appropriately and
with extra fields, including possibly parent fields and methods
--]]
function mono.formClass:setFieldsAndMethods()
local other = ''
if formMonoClass.miShowUsage.Checked then other = ' (usage by other classes)' end
formMonoClass.labelClassName.Caption = string.format('Mono Class: %s:%s%s', self.class.namespace, self.class.name, other)
local fields = {}
local methods = {}
if formMonoClass.miShowUsage.Checked then
---------- show where class is used in other classes
for i,class in ipairs(self.image.classes) do
for j,method in ipairs(class.methods) do
local found = false
if method.returnType == self.class.name then
found = true
else
for k,p in ipairs(method.parameters) do
if p.typeName == self.class.name then found = true end
end
end
if found then table.insert(methods, method) end
end
for j,field in ipairs(class.fields) do
if field.typeName == self.class.name then table.insert(fields, field) end
end
end
else
---------- basic class fields and methods
local c = self.class
while c ~= nil do
for i,field in ipairs(c.fields) do
table.insert(fields, field)
end
for i,method in ipairs(c.methods) do
table.insert(methods, method)
end
if formMonoClass.miShowInherited.Checked then
c = c.parent
else
c = nil
end
end
end
if formMonoClass.miSortByClassFirst.Checked then
table.sort(fields, formMonoClass.miSortFieldsByOffset.Checked and sortByClassThenOffset or sortByClassThenName)
table.sort(methods, sortByClassThenName)
else
table.sort(fields, formMonoClass.miSortFieldsByOffset.Checked and sortByOffset or sortByName)
table.sort(methods, sortByName)
end
self.fields = fields
self.methods = methods
formMonoClass.listFields.Items.Count = 0
formMonoClass.listFields.Items.Count = #fields
formMonoClass.listMethods.Items.Count = 0
formMonoClass.listMethods.Items.Count = #methods
end
-- handler to display fields in list view
function mono.formClass:listFields_OnData(sender, listitem)
-- columns are offset (or 'STATIC'), Type, Name
local index = listitem.Index + 1
local field = self.fields[index]
-- columns are Offset, Type, Name
if field.isStatic then
if field.isConst then
listitem.Caption = 'Const:'..string.format('%2X', field.constValue or 0)
else
listitem.Caption = 'Static:'..string.format('%2X', field.offset or 0)
end
else
listitem.Caption = string.format('%02X', field.offset or 0)
end
local className = ''
if field.class.name ~= self.class.name then className = field.class.name end
listitem.SubItems.text = table.concat({field.typeName or '??', field.name, className}, '\n')
end
-- handler to display methods in list view
function mono.formClass:listMethods_OnData(sender, listitem)
local method = self.methods[listitem.Index + 1]
if method == nil then
listitem.Caption = 'nil index '..tostring(listitem.Index + 1)
else
listitem.Caption = method.name
local className = ''
if method.class.name ~= self.class.name then className = method.class.name end
local ps = {}
for i,p in ipairs(method.parameters) do
table.insert(ps, string.format('%s %s', p.type, p.name))
end
local parms = method.returnType..' ('..table.concat(ps, ', ')..')'
--print('className, method.class.name', className, method.class.name)
listitem.SubItems.text = table.concat({ parms, className }, '\n')
end
end
local parameters = { 'RCX', 'RDX', 'R8', 'R9', '[RBP+30]', '[RBP+38]', '[RBP+40]', '[RBP+48]', '[RBP+50]' }
local floatParameters = { 'XMM0', 'XMM1', 'XMM2', 'XMM3', 'XMM4', 'XMM5', 'XMM6'}
function mono.formClass:popupMethod_OnPopup(sender)
end
function mono.formClass:listMethods_OnDblClick(sender)
local method = self.methods[sender.ItemIndex + 1]
--print("method: "..tostring(method.id))
if method then
local address = mono_compile_method(method.id)
--print("address: "..tostring(address))
getMemoryViewForm().DisassemblerView.SelectedAddress = address
getMemoryViewForm().show()
local hookInfo = hookAt(address)
-- have aobString, hookString, returnString, instructions
--[[ how to get method signature
local ps = {}
for i,p in ipairs(method.parameters) do
table.insert(ps, string.format('%s %s', p.type, p.name))
end
local parms = method.returnType..' ('..table.concat(ps, ', ')..')'
]]
local lines = {}
table.insert(lines, "define(hook,"..hookInfo.hookString..")")
table.insert(lines, "define(bytes,"..hookInfo.aobString..")")
table.insert(lines, "")
table.insert(lines, "[enable]")
table.insert(lines, "")
table.insert(lines, "assert(hook, bytes)")
table.insert(lines, "alloc(newmem,$1000, hook)")
table.insert(lines, "{")
-- note: per x64 calling convention, RCX might actually be space for
-- a pre-allocated structure for the return value and other parameters
-- might be moved one further down the list
table.insert(lines, " RCX: "..method.class.name.." (this)")
for i,p in ipairs(method.parameters) do
local param = parameters[i + 1]
if (p.type == "System.Single" or p.type == "System.Double") then param = floatParameters[i + 1] end
table.insert(lines, " "..param..": "..tostring(p.type).." "..tostring(p.name))
end
table.insert(lines, "")
table.insert(lines, " Returns (RAX) "..method.returnType)
table.insert(lines, "}")
table.insert(lines, "")
table.insert(lines, "newmem:")
table.insert(lines, " // original code")
for i,c in ipairs(hookInfo.instructions) do
table.insert(lines, " "..c)
end
table.insert(lines, " jmp hook+"..string.format("%X", hookInfo.returnOffset))
table.insert(lines, "")
table.insert(lines, "hook:")
table.insert(lines, " jmp newmem")
table.insert(lines, "")
table.insert(lines, "[disable]")
table.insert(lines, "")
table.insert(lines, "hook:")
table.insert(lines, " db bytes")
table.insert(lines, "")
table.insert(lines, "dealloc(newmem)")
local t = {}
for i,v in ipairs(lines) do
table.insert(t, v);
table.insert(t, "\r\n")
end
local aa = table.concat(t)
getMemoryViewForm().AutoInject1.DoClick()
for i=0,getFormCount()-1 do --this is sorted from z-level. 0 is top
local f=getForm(i)
if getForm(i).ClassName == 'TfrmAutoInject' then
f.assemblescreen.Lines.Text = aa
break
end
end
end
end
--[[
When double-clicking a field, print out the base address for the statics of the
class and the address of the clicked-on static field.
--]]
function mono.formClass:listFields_OnDblClick(sender)
local field = self.fields[sender.ItemIndex + 1]
if field then
local class = field.class
local image = class.image
local domainId = image.domain
print('double-clicked on class '..tostring(field.class.name)..' field '..tostring(field.name)..' domain '..tostring(domainId))
local address = mono_class_getStaticFieldAddress(domainId, class.id)
print('statics base address: '..string.format("%x", address))
print(class.name..'.'..field.name..': '..string.format("%x", address + field.offset))
end
end
--[[
IN PROGRESS - take an address and create an AA script to hook at that address
expecting MONO code. Will process instructions until 5 bytes (for jmp) are
processed. Basic format is like this for address 'CryingSuns.PlayerStatus:BattleshipState:HasAuxiliarySystemType+28'
[enable]
assert("CryingSuns.PlayerStatus:BattleshipState:HasAuxiliarySystemType":+28, )
]]
function hookAt(address)
local pos = string.find(address, "+", 1, true)
local name = address
local offset = 0
if (pos ~= nil) then
name = string.substring(1, pos - 1)
offset = tonumber(string.sub(pos + 1), 16)
end
local actualAddress = getAddress(name) + offset
local data = {
hookString = util.safeAddress(getNameFromAddress(actualAddress)), -- used for injection, etc
instructions = {},
aobString = ""
}
local aobs = {}
while (offset < 5) do
local parsed = disassembleAndParse(actualAddress + offset)
if #aobs > 0 then table.insert(aobs, " ") end
table.insert(aobs, parsed.bytesString)
table.insert(data.instructions, parsed.instructionString)
offset = offset + parsed.length
end
data.aobString = table.concat(aobs)
data.returnString = util.safeAddress(getNameFromAddress(actualAddress + offset))
data.returnOffset = offset
return data
end
function hookMethod(method)
end
--[[
Expects address to be a number
]]
function disassembleAndParse(address)
local disassembly = disassemble(address)
local parts = util.split(disassembly, "-")
for i = 1,#parts do
if i == 2 then
parts = parts:gsub("%s+", "") -- remove ALL whitespace from bytes
else
parts = parts:gsub("^%s*(.-)%s*$", "%1") -- remove whitespace from ends
end
end
local aob = {}
local i = 1
while i < string.len(parts[2]) do
if (i ~= 1) then table.insert(aob, " ") end
table.insert(aob, string.sub(parts[2], i, i+1))
i = i + 2
end
local instructionString = parts[3]
for k,v in parts[3]:gmatch("[0-9a-fA-F]+") do
if k:len() == 8 or k:len() == 16 then
instructionString = instructionString:gsub(k, getNameFromAddress(k))
end
end
local result = {
address = getAddress(address),
addressString = util.safeAddress(getNameFromAddress(address)),
aob = aob,
bytesString = table.concat(aob),
disassembly = disassembly,
instructionString = instructionString,
length = getInstructionSize(address),
originalInstructionString = parts[3]
}
return result
end
-- Forms - Save strings as temp files, then load using createFormFromFile()
--------------------------------------------------------------------------------]]
local stringFormMonoClass = [==========[<?xml version="1.0" encoding="utf-8"?>
<FormData>
<formMonoClass Class="TCEForm" Encoding="Ascii85">/yTK^8${c!?7msfX.9L%MdIQc2%!i#a/iSfwG[w3Ysw8Y[Kxk5cW0K{aT8zrG5QUV]XxAk]V}0BB[_{Vs2AqX#xv[:OOyz4.If^1_L48mym]Ld2VgSleeO*UPkS@:9=bb=Knd-{Fwg6!XwK_6R]WT,izTxk+!F}][G^4i7DsGpKdC!im?-UB)-%rDskPUQF)Rzc]O6B[^z_1jKxFN;3I7ftCcx#cPvI2B]=2@,6K60%kK1Inq+.g}B+HlEQo:GCJB^HvLD=hC/st1bW!#ougl#/+QCofy@d(g#(?4h)n8Fz+i)?5xP$o*@=dcxr)_y%yGnV1$nxVZO[j:r2!YoA0+:wJL^qMWa,vD*+B#@Aa^}w.+i9/DmKABOH)-/:7OY#V[,0t=5X%AnZNZI@*4P)qUochB[2IovkhHMj}pgGSwG;$,pf+DV1m(P:6eYFu^E_4i5L4sm06/=9(J7sgHB8O!u?#{an(pMPbnmp0mu^idQi:$okj^1k1uL#v47W@Fp/*YhfEm]%Ci;ljoM.XY223(DY6Rw1U16MK{7p_0/,oR*QnI[Fx^!y8#j2ckVB]M#Nx8-_c+WC%64uvTTI4@LHr!jItz$,W392+Q{fnI+0ZtCx}tJD9YPG3]{w.DidKFjDj=a(KvF(!Ybq*}ChFDvxykE{+J#tY*OFjmoR(+2mnbeOfW6L(G}VH6iWtST43*e09*7_ViOIcO=vZQlX[;]P:XP:O6M/=H[=ynj_%^B#R%zdd94B?*STJO,#0_F?tQwd/1zU0A=]i8tY!aETBz0@KQl]EvYNz8vptiL[Ikvlkeh]!QcAwJIEFM-@Dm0,of/B6lzLjd8rM.Gnrpfh[pd*BDc8ZD{3.LaY.#?7HvWhx2*km@.8k@?mT2K5-1RP/@sV)KQkKCt%8mdvjf.RWoE7?]4qdK5r791pEnq06lk%U@FE0#]DJXJVY@=G0$jxo,z).[8j,RK1M0c81DiaKt9iB)TJ^frZR-yyca?(fKZ]Vg)1UN1(9oZN*#C}cZbo/q1+}b-]OP]=bq+tqn,{9S:jv5gov0-xD/b,@6QeXDuMD?87SU{rt4Hrj)e$Es?Lbf0CIUI(nQD0J]K73r}V?b/@f8mm,,6(sDjPbLj]EI^7T4dCGHNGn{Yav,s/u:/3H}_92;K+QF?o7A!IP*hw:FG6p.l-9ssiZVrCF%)tkrZjW7EQC0;Gu5)-93DF{wtVS,.rrp]v9wuiG@k1h]dr]6f0e,)[j1cvUB)p3+ZXQR5Y+IqN*U832[*Bii9Atfj5DM^1R2i),;p7,TTWtgGpCMyR#t=?cSt+w-4Ay5^,$?%1PleRCoEy*I@UNgbZD5JU:h,R1^LE((gIpcuQ6Y!Q}nmbV9Cn%pG5/$YWLSPU?]TWVY8/d*XGbEDX9GFx_.#z,L</formMonoClass>
</FormData>
]==========]
local stringFormMonoImage = [==========[<?xml version="1.0" encoding="utf-8"?>
<FormData>
<formMonoImage Class="TCEForm" Encoding="Ascii85">#goBu):ZT+Wtl^dlQI).N^(5n(hQF{X^MQJn@1eo5fn$ZHIXe;RtGr4190F.cqtCN1hVAXjWcD*0l3*K^?z^hx4+qE0Ksbh3I+QToQw1+QkP4f?kNSb@5lX/)jLR@IiYbP=eCTy?ZA0ZI(bMlCiZ8DJe+6*v,mF+jD,(E%sth}1CLRX$vU-f(i9$XRV@:QA!q!f%hX+b6)]dyOt_prhLp]G9=zuU40V/tG9sQgs]],Y[?WW7%bqQH*RxO9*We0CaVdE@Zgdl/@M}s?b/WI$4H*R(bICOq:vG*bM5b2J?8GRcx[DbAAiUL]9lSLizG[D0JP0B(qo^2%G=}m+ra!0MJv,XL@_ZnSM_HxTV)cJhf9I9+4KLxJqx*H(z;36.k,8JMD^Qkiz#h)TZim?DT^LGY6G3@@j/pDI11cbh0/UjyJR^0G:vZE5TzyieiSvQC;a(0j_I1#Y1q]28AO^@P9DA)8H^GkPRD[5Z%y%t^HKF?{v6g?tUv!E3cP_:l]R+yV4f4v4-{wf!3Qc8fU38JaQy9#^U-{8!JCVHkhj,CSHPtPb3.lul(V3=_gdgp9Os[Rb%_?;C,WXNw,f/6f.*]R#b*wa-.kPtDstrDfs3NGb%qJ6y3fSIFkJp7HU$DGcFw09Lg7,2RjA700</formMonoImage>
</FormData>
]==========]
local stringFormMonoSearch = [==========[<?xml version="1.0" encoding="utf-8"?>
<FormData>
<formMonoSearch Class="TCEForm" Encoding="Ascii85">%}#Ei,cprUq+#;fbZc+SoAl.WE)L-l:ODn@%^B_H;xFG;($Pw03Dcw=)cVojK[8=K)V+xt2#OX6(]Y18()T9kQCj]j@g2!t@,BL!hBKq?n}$;oZUQj6IDG.}-Iu34S^z*-OOG:=#3-7}=w#W,.XJY2SC}v@!ui8=Mm%*;0k!W+AZE[22D;N{gfq9HcD41k](}+f6X}X)=S^V3[T1{XwuIh7UVDfza3G6cpegQhCL(I}P[C8Z%M(*4?n{c[tonGI]eChq(2V([du2{+:Y!liP*$[aM@)34K[A6YhYP[M4_BgZm{^s0N]eG9yPo0pQ-YgaPhlI=;L)LHR#-hX@aUDg,?MYyH*=1u/6SgYX(^.;O=qgqlY[$!j^,PGLo@_5]dHU$m5oWgFUqzgja?$}TXj.q=v2p,j0O0^25Q,=F+X4HNZt%+7;h!q8SCyQ!hb9ieqIA:yj{*G9AJ6TTSoXsm/Y{b8A#3p%0Sx;N.v/uJ5pYsb!Ui4I+!UcYHI-yZ.0toph)!jm(/d@rebD.mI^7Fm@hU@b}*t^gcI7KXfiLkhT:8F)7lQ}oCZK5t!AHL,6dKE+WDYJ3ciQssLG5QHIkRvUnd)_-KG[gY2^LD/=C8DJ-e!66Rq[QUSWF1q-LQvUJVe5aiER.rI1EAUa1W?KgS9?l+jtAI?bA6SfC,!^5Imtu*{XYpQ=pUp._T3SuABBv;0OKs}EIDV{?=FaRxKa2AVTF/=MH;c0$RNK@M]4tBgT#3p4Ce6vsF(L.Ru*?q4Ry5GjbyNh}f2zHU+u)7mRKOdwauFaTW+GlE=W,#m2Qo$Vx*7aH^JUHsnKRVYv%Ps.[153E{^;IsVN?6ZB!J#=cpwjHlWShBW+w8{$Ez7djl.5SGh-y2^Q@cj8a]vZP0k3(2Wet_S!k*?A3Zxs*4$RpwQnc54F+Em$;y7Z2zoPx/5ezhKvx[{.sVMvLPGH%9dp)DjdU{/k2U@[o@QV;Dmq.3_3k_3-9CH-j]*bPeh*!-qjr,b4Q*^;TmF)=b.jgw/,m)HG6VW?f{2q?kW3ODQiLU)Va-T!vCmfcf:X.6^!@K!?Uj8bmqB/nmTC2xJSqL$YKjcQ(G-CxgpLw}F2:Ve8_]xHgj5-[*m]4/2R8xo17o*1NjEC22YGWpMhe@g}$S,@*UdDtDXv@J0NMnxBO):3pe*VpgAcI)IaU{)ts*iR$7-pNw%A@8FO]TMem#f.w{vbR=CSlIBsOcR,?wg!YjWNUFojeRt/W^GWUZXboc^S/_^8p.2q#E#eaPBo?{^z!dYtB;(f8)iAt?:V_bT#(Pc]Yyfg=NM?H2suncbWi2z(mL*OTW@xT]sjj)%TR5.AF5UQE^A:VIAE*2O-zMaeztg.H;7UcPr.[BXO+aDs*K)4</formMonoSearch>
</FormData>
]==========]
local function saveForm(text)
local path = os.tmpname() -- get temp file name
local f, err = io.open(path, "w")
if f == nil then return nil, err end
f:write(text)
f:close()
return path
end
local function createFormFromString(text)
local path = saveForm(text)
local form = createFormFromFile(path)
pcall(os.remove, path)
return form
end
formMonoClassB = createFormFromString(stringFormMonoClass)
formMonoImage = createFormFromString(stringFormMonoImage)
formMonoSearch = createFormFromString(stringFormMonoSearch)
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/bootstrap.lua
--------------------------------------------------------------------------------]]
-- loadstring(loadTextFile('src/lua/util.lua'))()
-- loadstring(loadTextFile('../temp/notes.lua'))()
-- loadstring(loadTextFile('src/lua/mono.lua'))()
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/util.lua
--------------------------------------------------------------------------------]]
util = util or {}
util.loadTextFile = function (name, useTableFile)
if useTableFile then
local tableFile = findTableFile(name)
if not tableFile then return nil, 'Unable to open table file "'..tostring(name)..'"' end
local ss = createStringStream()
ss.Position = 0 -- recommended on wiki: [Link]
ss.CopyFrom(tableFile.Stream, 0)
local text = ss.DataString
ss.destroy()
return text
else
local path = getMainForm().openDialog1.InitialDir..name
local f, err = io.open(path, "r")
-- fall back to table file if disk file error (doesn't exist)
if f == nil then return loadTextFile(name, true) end
local text = f:read("*all")
f:close()
return text
end
end
--[[
Save a string to a text file. If useTableFile is true it will be saved as
a TableFile. The directory should be where the cheat file is, it is the
initial directory for the dialog when you are saving your cheat table.
--]]
util.saveTextFile = function(name, text, useTableFile)
if useTableFile then
local tf = findTableFile(name)
if tf ~= nil then
tf.delete()
tf = nil
end
tf = createTableFile(name)
local ss = createStringStream(text)
tf.Stream.CopyFrom(ss, 0)
ss.destroy()
return true
else
local path = getMainForm().saveDialog1.InitialDir..name
local f, err = io.open(path, "w")
if f == nil then return nil, err end
f:write(text)
f:close()
return true
end
end
--[[
For mono symbols, you have to surround them in quotes for use in AA
]]
function util.safeAddress(s)
local tbl = {}
for a in string.gmatch(s, "([^+]+)[+]?") do table.insert(tbl, a) end
local result = '"'..tbl[1]..'"'
if #tbl > 1 then result = result..'+'..tbl[2] end
return result
end
function util.split(s, separator)
local tbl = {}
for str in string.gmatch(s, "([^"..separator.."]+)") do table.insert(tbl, str) end
return tbl
end
function util.trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
--[[
Pretty-print serialize the passed object and return the results
as a string. This does nice indenting and follows metatables. It
is not guaranteed to provide the same object back if the string is
loaded because it just replaces circular references with a string
among other things. It does try to keep tables/arrays on one line
if there is room (120 characters).
--]]
function util.pretty(value, funcs, indent, done, stack)
indent = indent or 0
funcs = funcs or {}
done = done or {}
stack = stack or { {value=value} } -- initialize with passed value, no key
-- if a 'userdata', use metatable
local l = value
if type(value) == "userdata" then
l = getmetatable(value)
end
if type(l) == "table" then
-- if we've already serialized the table, return string representation
-- to avoid self-referencing in recursion. Only the first serialization
-- will have the full serialized string
if done[l] then return tostring(l) end
done[l] = true
local list = {}
-- here we return "{", the values, and "}"
if (#l > 0) then
-- if we have a count ('#'), then it's just an array of values
for i,v in ipairs(l) do
table.insert(list, util.pretty(v, funcs, indent + 2, done, stack))
end
else
-- no count, so either empty or object with key/value
-- first we sort the keys
local keys = {}
for k,v in pairs(l) do table.insert(keys, k) end
table.sort(keys)
local last = {key = "UNKNOWN", value = "NONE"}
table.insert(stack, last)
for i,k in ipairs(keys) do
local propValue = value[k]
last.key = k
last.value = value[k]
for j,f in ipairs(funcs) do
local returnValue = f(value, k, value[k], stack, indent)
if returnValue ~= nil then
propValue = returnValue
break
end
end
table.insert(list, string.format("%s = %s", tostring(k), util.pretty(propValue, funcs, indent+2, done, stack)))
end
table.remove(stack, #stack)
end
-- now we find the total size, add 200 if there are line breaks
local size = 0
for i,v in ipairs(list) do
size = size + string.len(v) + 2
if string.find(v, "\r\n") then
size = 200
break;
end
end
-- if it's small enough, join with commas
if (size < 120) then
local result = {"{ "}
for i,v in ipairs(list) do
if i > 1 then table.insert(result, ", ") end
table.insert(result, v)
end
table.insert(result, " }")
return table.concat(result);
else
local indentString1 = string.rep(" ", indent)
local indentString2 = string.rep(" ", indent + 2)
local result = {"{\r\n"}
for i,v in ipairs(list) do
table.insert(result, indentString2)
table.insert(result, v)
if (i < #list) then table.insert(result, ",") end
table.insert(result, "\r\n")
end
-- table.insert(result, indentString)
table.insert(result, indentString1)
table.insert(result, "}")
return table.concat(result);
end
end
if type(value) == "string" then
return string.format("\"%s\"", value)
end
return tostring(value)
end
--[[
Values and functions used by util.serialize(t)
---]]
local oddvals = {[tostring(1/0)] = '1/0', [tostring(-1/0)] = '-1/0', [tostring(-(0/0))] = '-(0/0)', [tostring(0/0)] = '0/0'}
local kw = {['and'] = true, ['break'] = true, ['do'] = true, ['else'] = true,
['elseif'] = true, ['end'] = true, ['false'] = true, ['for'] = true,
['function'] = true, ['goto'] = true, ['if'] = true, ['in'] = true,
['local'] = true, ['nil'] = true, ['not'] = true, ['or'] = true,
['repeat'] = true, ['return'] = true, ['then'] = true, ['true'] = true,
['until'] = true, ['while'] = true}
local getchr = function(c)
return "\\" .. c:byte()
end
local make_safe = function(text)
return ("%q"):format(text):gsub('\n', 'n'):gsub("[\128-\255]", getchr)
end
local write = function(t, memo, rev_memo)
local ty = type(t)
if ty == 'number' then
t = format("%.17g", t)
return oddvals[t] or t
elseif ty == 'boolean' or ty == 'nil' then
return tostring(t)
elseif ty == 'string' then
return make_safe(t)
elseif ty == 'table' or ty == 'function' then
if not memo[t] then
local index = #rev_memo + 1
memo[t] = index
rev_memo[index] = t
end
return '_[' .. memo[t] .. ']'
else
error("Trying to serialize unsupported type " .. ty)
end
end
local write_key_value_pair = function(k, v, memo, rev_memo, name)
if type(k) == 'string' and k:match '^[_%a][_%w]*$' and not kw[k] then
return (name and name .. '.' or '') .. k ..'=' .. write(v, memo, rev_memo)
else
return (name or '') .. '[' .. write(k, memo, rev_memo) .. ']=' .. write(v, memo, rev_memo)
end
end
local is_cyclic = function(memo, sub, super)
local m = memo
local p = memo
return m and p and m < p
end
local write_table_ex = function(t, memo, rev_memo, srefs, name)
if type(t) == 'function' then
return '_[' .. name .. ']=loadstring' .. make_safe(string.dump(t))
end
local m = {}
local mi = 1
for i = 1, #t do -- don't use ipairs here, we need the gaps
local v = t
if v == t or is_cyclic(memo, v, t) then
srefs[#srefs + 1] = {name, i, v}
m[mi] = 'nil'
mi = mi + 1
else
m[mi] = write(v, memo, rev_memo)
mi = mi + 1
end
end
for k,v in pairs(t) do
if type(k) ~= 'number' or math.floor(k) ~= k or k < 1 or k > #t then
if v == t or k == t or is_cyclic(memo, v, t) or is_cyclic(memo, k, t) then
srefs[#srefs + 1] = {name, k, v}
else
m[mi] = write_key_value_pair(k, v, memo, rev_memo)
mi = mi + 1
end
end
end
return '_[' .. name .. ']={' .. table.concat(m, ',') .. '}'
end
--[[
A function more appropriate for serializing objects in a reproduceable way.
--]]
function util.serialize(t)
local memo = {[t] = 0}
local rev_memo = {[0] = t}
local srefs = {}
local result = {}
-- phase 1: recursively descend the table structure
local n = 0
while rev_memo[n] do
result[n + 1] = write_table_ex(rev_memo[n], memo, rev_memo, srefs, n)
n = n + 1
end
-- phase 2: reverse order
for i = 1, n*.5 do
local j = n - i + 1
result, result[j] = result[j], result
end
-- phase 3: add all the tricky cyclic stuff
for i, v in ipairs(srefs) do
n = n + 1
result[n] = write_key_value_pair(v[2], v[3], memo, rev_memo, '_[' .. v[1] .. ']')
end
-- phase 4: add something about returning the main table
if result[n]:sub(1, 5) == '_[0]=' then
result[n] = 'return ' .. result[n]:sub(6)
else
result[n + 1] = 'return _[0]'
end
-- phase 5: just concatenate everything
result = table.concat(result, '\n')
return n > 1 and 'local _={}\n' .. result or result
end
function util.map(t, f)
local results = {}
local func = f or tostring
for i,v in ipairs(t) do
table.insert(results, func(v))
end
return results
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/temp/notes.lua
--------------------------------------------------------------------------------]]
--[[
A note has 'key' and 'text' fields, lists are sorted by key.
--]]
notes = notes or {}
notesmt = notesmt or {}
note = note or {}
notemt = notemt or {}
notemt.__index = note
notemt.__lt = function(a,b) return a.key < b.key end
function notes.new(filename, useTableFile)
local obj = {}
setmetatable(obj, {__index = notes})
obj:load(filename, useTableFile)
return obj
end
function notes:load(filename, useTableFile)
self.filename = filename
self.useTableFile = useTableFile
local s = loadTextFile(self.filename, self.useTableFile)
self.dict = {}
if s then self.dict = loadstring(s)() end
self.lastSave = os.clock()
self.lastChange = 0
end
function notes:save()
local text = util.serialize(self.dict)
saveTextFile(self.filename, text, self.useTableFile)
self.lastSave = os.clock()
end
function notes:saveAs(filename, useTableFile)
self.filename = filename
self.useTableFile = useTableFile
self:save()
end
function notes:update(key, text)
if text == nil or string.len(text) == 0 then
if self.dict[key] then self.dict[key] = nil end
else
self.dict[key] = {key = key, text = text}
end
self.lastUpdate = os.clock()
end
function notes:getList()
local keys = {}
for k,n in pairs(self.dict) do table.insert(keys, n) end
return keys
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono.lua
--------------------------------------------------------------------------------]]
-- moduled
mono = mono or {}
-- classes
mono.MonoClass = mono.MonoClass or {}
mono.MonoField = mono.MonoField or {}
mono.MonoMethod = mono.MonoMethod or {}
mono.MonoImage = mono.MonoImage or {}
mono.menu = mono.menu or {}
mono.popups = mono.popups or {}
mono.formSelectImage = mono.formSelectImage or {}
mono.formSearch = mono.formSearch or {}
mono.formClass = mono.formClass or {}
-- defines
mono.TYPE_NAMES = {
[0] = 'END',
[1] = 'void',
[2] = 'bool',
[3] = 'char',
[4] = 'sbyte',
[5] = 'byte',
[6] = 'short',
[7] = 'ushort',
[8]= 'int',
[9]= 'uint',
[10] = 'long',
[11] = 'ulong',
[12] = 'float',
[13] = 'double',
[14] = 'string',
[15] = 'ptr',
[16] = 'byref',
[17] = 'valuetype',
[18] = 'class',
[19] = 'var',
[20] = 'array',
[21] = 'genericinst',
[22] = 'typedbyref',
[24] = 'I',
[25] = 'U',
[0x1b] = 'FNPTR',
[0x1c] = 'object',
[0x1d] = 'szarray', -- 0-based one-dim-array
[0x1e] = 'mvar',
[0x1f] = 'CMOD_REQD', -- typedef or typeref token
[0x20] = 'CMOD_OPT', -- optional arg: typedef or typeref token
[0x21] = 'INTERNAL',
[0x40] = 'MODIFIER', -- Or with the following types
[0x41] = 'SENTINEL', -- sentinel for varargs method signature
[0x45] = 'PINNED', -- local var that points to pinned object
[0x55] = 'ENUM', -- an enumeration
}
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono/monofield.lua
--------------------------------------------------------------------------------]]
local MonoField = mono.MonoField
MonoField.mt = {
__index = MonoField,
__tostring = function(t)
return 'MonoField '..tostring(t.id)..' "'..tostring(t.name)..'"'
end,
__lt = function(a, b)
return a.lowerName < b.lowerName
end
}
-- class is a monoclass table, f is a table with results from
-- mono_class_enumFields
function MonoField.new(class, f)
local obj = {
class = class,
id = f.field,
name = f.name,
isStatic = f.isStatic,
isConst = f.isConst,
offset = f.offset,
monoType = f.monotype,
lowerName = string.lower(f.name),
foundTypeName = f.typeName,
typeName = mono.TYPE_NAMES[f.monotype] or 'UnknownType'..tostring(f.monotype)
}
-- should be types where we might have a 'Class'
--if f.monotype == 15 or f.monotype == 16 or f.monotype == 18 or f.monotype then
--end
obj.typeClassId = mono_type_getClass(obj.id)
if obj.typeClassId ~= nil then
-- this is the 'type' of the field
obj.typeClass = class.image.classesById[obj.typeClassId]
if obj.typeClass ~= nil then
obj.typeName = obj.typeClass.name
else
if class.image.missingNames[obj.typeClassId] then
obj.typeName = class.image.missingNames[obj.typeClassId]
else
obj.typeName = mono_class_getFullName(obj.typeClassId)
class.image.missingNames[obj.typeClassId] = obj.typeName
end
end
end
setmetatable(obj, MonoField.mt)
return obj
end
function MonoField:getFullName()
local s = ""
if self.class.namespace ~= nil and self.class.namespace:len() > 0 then s = self.class.namespace..':' end
s = s..self.class.name..'.'
s = s..self.name
return s
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono/monomethod.lua
--------------------------------------------------------------------------------]]
local MonoMethod = mono.MonoMethod
MonoMethod.mt = {
__index = MonoMethod,
__tostring = function(t)
return 'MonoMethod '..tostring(t.id)..' "'..tostring(t.name)..'"'
end,
__lt = function(a, b)
return a.lowerName < b.lowerName
end
}
function MonoMethod.new(class, m)
local obj = {
class = class,
id = m.method,
name = m.name,
lowerName = string.lower(m.name),
parameters = {}
}
setmetatable(obj, MonoMethod.mt)
lastmethod = obj
local types, parameternames, returntype = mono_method_getSignature(obj.id)
local typenames={}
local tn
if types ~= nil then
for tn in string.gmatch(types, '([^,]+)') do
table.insert(typenames, tn)
end
end
for i=1,#typenames do
table.insert(obj.parameters, { name = parameternames, type = typenames })
end
obj.returnType = returntype
return obj
end
--[[
Get parameters for method
--]]
function MonoMethod:fetchParms()
if self.parms ~= nil then return nil end
if self.class.image.ignoredClassesByName[self.class.name] ~= nil then return nil end
local status, parms = pcall(function()
local result = mono_method_get_parameters(self.id)
return result
end)
if status then
-- success!
self.parms = parms
else
self.class.image.ignoredClassesByName[self.class.name] = true
print('Error with class '..tostring(self.class.name)..' method '..tostring(self.name))
error('Error fetching parameters for '..tostring(self.class.name)..'.'..tostring(self.name))
end
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono/monoclass.lua
--------------------------------------------------------------------------------]]
local MonoClass = mono.MonoClass
MonoClass.mt = {
__index = MonoClass,
__tostring = function(t)
return 'MonoClass '..tostring(t.id)..' "'..tostring(t.name)..'"'
end,
__lt = function(a, b)
return a.lowerName < b.lowerName
end
}
local MonoField = mono.MonoField
local MonoMethod = mono.MonoMethod
--[[
Called from MonoImage:_init, 'c' is what is returned from enumerating classes.
--]]
function MonoClass.new(image, c)
local obj = {
name = c.classname,
namespace = c.namespace,
id = c.class,
lowerName = string.lower(c.classname),
image = image
}
setmetatable(obj, MonoClass.mt)
return obj
end
function MonoClass:initFields()
self.fields = {}
self.fieldsByName = {}
self.fieldsByLowerName = {}
local constFieldCount = 0
local temp = mono_class_enumFields(self.id)
for i,f in ipairs(temp) do
local field = MonoField.new(self, f)
table.insert(self.fields, field)
self.fieldsByLowerName[field.lowerName] = field;
self.fieldsByName[field.name] = field
if field.isStatic and field.isConst then
field.constValue = constFieldCount
constFieldCount = constFieldCount + 1
end
end
table.sort(self.fields)
end
function MonoClass:initMethods()
self.methods = {}
self.methodsByName = {}
self.methodsByLowerName = {}
local temp = mono_class_enumMethods(self.id)
for i,m in ipairs(temp) do
local method = MonoMethod.new(self, m)
table.insert(self.methods, method)
self.methodsByName[method.name] = method
self.methodsByLowerName[method.lowerName] = method
end
table.sort(self.methods)
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/mono/monoimage.lua
--------------------------------------------------------------------------------]]
local MonoImage = mono.MonoImage
MonoImage.mt = {
__index = MonoImage,
__tostring = function(t)
return 'MonoImage '..tostring(t.name)
end
}
local MonoClass = mono.MonoClass
--[[
List names of images, a name can be passed to MonoImage.new()
or MonoImage(name) to create a MonoImage instance.
--]]
function MonoImage.enumerate()
local names = {}
mono_enumImages(function(img)
local name = mono_image_get_name(img)
table.insert(names, name)
end)
table.sort(names)
return names
end
--[[
Constructor doesn't do much, call :init(name, progress)
--]]
function MonoImage.new()
local obj = { }
setmetatable(obj, MonoImage.mt)
return obj
end
--[[
init() takes the image name and an optional callback for reporting progress
and when it is complete. The signature for this function is:
function progress(complete, image, message, processed, total)
This callback should be executed on the main CE thread.
--]]
function MonoImage:init(name, progress)
if monopipe == nil then
print('Launching mono data collector...')
LaunchMonoDataCollector()
end
self.domains = mono_enumDomains()
self.domain = self.domains[1]
self.classes = {} -- straight list of all classes
self.classesByName = {} -- dictionary for access by name
self.classesByLowerName = {} -- for access by lower case name
self.classesById = {} -- for access by id
self.ignoredClassesByName = {} -- for ignoring classes in searches, etc.
self.progress = progress
self.name = name or 'Assembly-CSharp' -- intelligent default for unity
-- get id
self.id = nil
mono_enumImages(function(img)
local foundName = mono_image_get_name(img)
if foundName == self.name then self.id = img end
end)
if not self.id then
print('NO ID!')
print('name is', name, self.name)
self:report(false, 'Error finding image named '..tostring(name))
self.progress = nil
self.total = nil
self.count = nil
return false
end
createThread(function(thread)
self.thread = thread
self:_init(thread)
end)
end
--[[
This is the function called by init() on another thread
--]]
function MonoImage:_init(thread)
--print('MonoImage:_init thread is', thread, 'self is', tostring(self))
self.thread = thread
local temp = mono_image_enumClasses(self.id)
-- thread.synchronize(function(th)
-- print('Image '..tostring(self.id)..' has classes: '..tostring(temp))
-- end)
self.classes = {}
self.classesByName = {}
self.classesByLowerName = {}
self.classesById = {}
self.missingNames = {}
self.total = #temp
self.count = 0
self.message = "Getting classes"
self:report(false, "Getting classes")
-- populate classes without much information first so we can
-- access our definitions when filling in parents, properties, etc.
for i,c in ipairs(temp) do
local class = MonoClass.new(self, c)
if class.lowerName ~= nil then
table.insert(self.classes, class)
self.classesByName[class.name] = class
self.classesByLowerName[class.lowerName] = class
self.classesById[class.id] = class
else
--print("Nil lowername for class", c.class)
end
self.count = i
if (i % 100 == 0 or i == #temp) then
self:report(false, 'Fetching classes')
end
end
table.sort(self.classes)
for i,class in ipairs(self.classes) do
class.parentId = mono_class_getParent(class.id)
class.parent = self.classesById[class.parentId]
self.count = i
if (i % 100 == 0 or i == #temp) then
self:report(false, 'Fetching parents')
end
end
self.count = 0
for i,class in ipairs(self.classes) do
class:initFields()
class:initMethods()
self.count = i
if (i % 100 == 0 or i == #temp) then
self:report(false, 'Initializing fields and methods')
end
end
self:report(true, 'Done')
end
--[[
Report progress on the main CE thread to do gui updates
--]]
function MonoImage:report(done, message)
if self.progress ~= nil and self.thread ~= nil then
self.thread.synchronize(function(thread)
self.progress(done, self, message, self.count, self.total)
end)
end
if done then
-- do cleanup here
self.count = nil
self.total = nil
self.progress = nil
self.thread = nil
end
end
--[[ Sample code... weird
--return util.pretty(mono.MonoImage.enumerate())
mi = mono.MonoImage.new()
print('mi is "'..tostring(mi)..'"')
mi:init('Assembly-CSharp', function(c, i, m, p, t)
print(m, p, t)
end)
--LaunchMonoDataCollector()
local thread = createThread(function(th)
TESTIMAGES = {}
mono_enumImages(function(img)
th.synchronize(function() print(img) end)
end)
end)
--]]
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/monomenu.lua
--------------------------------------------------------------------------------]]
function mono.menu:init()
--if self.menuSearch or self.timer then return end
self.timer = createTimer()
self.timer.Interval = 1000
self.timer.OnTimer = function(timer)
-- wait for normal mono script to create mono menu, check every 5 seconds
if not miMonoTopMenuItem then return end
self.timer.destroy()
self.timer = nil
self.menuSearch = createMenuItem(miMonoTopMenuItem)
self.menuSearch.Caption = 'Search'
self.menuSearch.Name = 'miMonoSearch'
self.menuSearch.OnClick = function(sender) self:OnSearchClick() end
miMonoTopMenuItem.add(self.menuSearch)
end
end
function mono.menu:OnSearchClick()
if mono.selectedImage then
mono.formSearch:show()
formMonoSearch:centerScreen()
else
mono.formSelectImage:show()
end
end
mono.menu:init()
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/forms/formSelectImage.lua
--------------------------------------------------------------------------------]]
local DEFAULT_ASSEMBLY_NAME = "Assembly-CSharp"
local imageHasBeenSelected = false
function mono.formSelectImage:show()
LaunchMonoDataCollector()
formMonoImage.listImages.Visible = true
formMonoImage.buttonSelectImage.Visible = true
formMonoImage.progressImage.Visible = false
formMonoImage.labelMessage.Visible = false
self.imageNames = mono.MonoImage.enumerate()
table.sort(self.imageNames)
local items = formMonoImage.listImages.Items
items.Clear()
local foundIndex = 0
for i,name in ipairs(self.imageNames) do
items.add(name)
if name == DEFAULT_ASSEMBLY_NAME then foundIndex = i end
end
local handler = function(sender)
mono.formSelectImage.OnSelectImage(self)
end
formMonoImage.buttonSelectImage.OnClick = handler
formMonoImage.listImages.OnDblClick = handler
formMonoImage.show()
if foundIndex ~= 0 and imageHasBeenSelected == false then
formMonoImage.listImages.ItemIndex = foundIndex - 1
mono.formSelectImage:OnSelectImage()
end
end
function mono.formSelectImage:OnSelectImage()
imageHasBeenSelected = true
local index = formMonoImage.listImages.ItemIndex + 1
if self.imageNames == nil or index < 1 or index > #self.imageNames then return end
local imageName = self.imageNames[index]
formMonoImage.listImages.Visible = false
formMonoImage.buttonSelectImage.Visible = false
formMonoImage.progressImage.Visible = true
formMonoImage.labelMessage.Visible = true
formMonoImage.labelMessage.Caption = 'Finding classes...'
local image = mono.MonoImage.new()
formMonoImage.progressImage.Min = 0
local progressHandler = function(done, image, message, count, total)
if total ~= nil and total > 0 and count ~= nil and count >= 0 then
message = string.format('%s %d/%d', message, count, total)
formMonoImage.progressImage.Max = total
formMonoImage.progressImage.Position = count
end
formMonoImage.labelMessage.Caption = message
if done then self:OnImageComplete(image) end
end
image:init(imageName, progressHandler)
end
function mono.formSelectImage:OnImageComplete(image)
self.image = image
mono.selectedImage = image
formMonoImage.Close()
mono.formSearch:show()
formMonoSearch:centerScreen()
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/forms/formSearch.lua
--------------------------------------------------------------------------------]]
--[[
Form for searching an image.
Controls:
pageMain.tabSearch - main tab
editSearchText: TCEEdit
listSearchClasses: TCEListView
listSearchFields: TCEListView
listSearchMethods: TCEListView
miImage: TMenuItem - main menu Image entry
miImageSelectImage - entry to re-open select image form
--]]
mono.formSearch.found = mono.formSearch.found or {}
mono.formSearch.found.classes = mono.formSearch.found.classes or {}
mono.formSearch.found.fields = mono.formSearch.found.fields or {}
mono.formSearch.found.methods = mono.formSearch.found.methods or {}
--[[
Show the search form, mono.selectedImage should be set to the
image to search.
--]]
function mono.formSearch:show()
if mono.selectedImage == nil then return end
self.image = mono.selectedImage
-- setup event handlers
formMonoSearch.miImageSelectImage.OnClick = function(sender) self:SelectImageClick() end
formMonoSearch.listSearchClasses.OnData = function(sender, listitem)
self:listSearchClasses_OnData(sender, listitem)
end
formMonoSearch.listSearchClasses.OnDblClick = function(sender)
local class = self.found.classes[sender.ItemIndex + 1]
mono.formClass:show(class, nil, nil)
end
formMonoSearch.listSearchFields.OnData = function(sender, listitem)
self:listSearchFields_OnData(sender, listitem)
end
formMonoSearch.listSearchFields.OnDblClick = function(sender)
local field = self.found.fields[sender.ItemIndex + 1]
if field then
mono.formClass:show(field.class, field, nil)
end
end
formMonoSearch.listSearchMethods.OnData = function(sender, listitem)
self:listSearchMethods_OnData(sender, listitem)
end
formMonoSearch.listSearchMethods.OnDblClick = function(sender)
local method = self.found.methods[sender.ItemIndex + 1]
if method then
mono.formClass:show(method.class, nil, method)
end
end
formMonoSearch.editSearchText.OnChange = function(sender)
self:search(sender.text)
end
-- popups
formMonoSearch.listSearchClasses.PopupMenu = formMonoSearch.popupClasses
local count = 0
formMonoSearch.popupClasses.OnPopup = function(sender)
print('formMonoSearch.popupClasses.OnPopup "'..tostring(sender.name)..'" count '..tostring(count))
count = count + 1
end
-- perform initial search to set 'found' results, probably empty text
self:search(formMonoSearch.editSearchText.Text)
-- show form
formMonoSearch.show()
end
--[[
When menu item to select an image is clicked, hide this form and
show the image select form
--]]
function mono.formSearch:SelectImageClick()
formMonoSearch.close()
mono.formSelectImage:show()
end
--[[
When typing in the edit box to filter results, filter the lists of
classes, fields, and methods with the lower case of the text
--]]
function mono.formSearch:search(text)
local lower = nil
if text ~= nil then lower = text:lower() end
local classes = {}
local methods = {}
local fields = {}
for i,class in ipairs(self.image.classes) do
if lower == nil or class.lowerName:find(lower, 1, true) ~= nil then table.insert(classes, class) end
for j,method in ipairs(class.methods) do
if lower == nil or method.lowerName:find(lower, 1, true) ~= nil then table.insert(methods, method) end
end
for j,field in ipairs(class.fields) do
if lower == nil or field.lowerName:find(lower, 1, true) ~= nil then table.insert(fields, field) end
end
end
self.found.classes = classes
self.found.fields = fields
self.found.methods = methods
formMonoSearch.listSearchClasses.Items.Count = #classes
formMonoSearch.listSearchFields.Items.Count = #fields
formMonoSearch.listSearchMethods.Items.Count = #methods
end
-- handler to display classes in list view
function mono.formSearch:listSearchClasses_OnData(sender, listitem)
local index = listitem.Index + 1
local class = self.found.classes[index]
listitem.Caption = class.name
end
-- handler to display fields in list view
function mono.formSearch:listSearchFields_OnData(sender, listitem)
local field = self.found.fields[listitem.Index + 1]
if field == nil then
listitem.Caption = 'nil index '..tostring(index)
else
listitem.Caption = field.name
listitem.SubItems.text = field.class.name
end
end
-- handler to display methods in list view
function mono.formSearch:listSearchMethods_OnData(sender, listitem)
local method = self.found.methods[listitem.Index + 1]
if method == nil then
listitem.Caption = 'nil index '..tostring(index)
else
listitem.Caption = method.name
listitem.SubItems.text = method.class.name
end
end
--[[--------------------------------------------------------------------------------
-- Included File: src/lua/forms/formClass.lua
--------------------------------------------------------------------------------]]
--[[
Form for viewing a class
Controls:
listFields: TCEListView
listMethods: TCEListView
miSortFieldsByOffset - if Checked, sort by offset, otherwise by name
miShowInherited - if Checked, add in methods and fields of parent(s)
miShowUsage - if Checked, funcs and methods will be on other classes that use the type
miFindFields - When clicked, find fields in all classes that have this class as a type
miFindMethodCode - When clicked, find methods in all classes that have this class as a type
Standard Mode:
listFields (can sort by name (default) or offset)
Offset
Type
Name
Class - class it is defined on, blank if not inherited
listMethods (sorted by name)
Name
Signature (return type, parameter types and names)
Class - class it is defined on, blank if not inherited
Usage Mode:
listFields (sorted by Class then Name)
Offset
Type
Class
--]]
local sortByClassThenName = function(a, b)
if a.isConst and not b.isConst then return true end
if b.isConst and not a.isConst then return false end
if a.isConst and b.isConst then return a.constValue < b.constValue end
if a.class.lowerName < b.class.lowerName then return true end
if b.class.lowerName < a.class.lowerName then return false end
return a.name < b.name
end
local sortByClassThenOffset = function(a, b)
if a.isConst and not b.isConst then return true end
if b.isConst and not a.isConst then return false end
if a.isConst and b.isConst then return a.constValue < b.constValue end
if a.class.lowerName < b.class.lowerName then return true end
if b.class.lowerName < a.class.lowerName then return false end
return a.offset < b.offset
end
local sortByName = function(a, b)
if a.isConst and not b.isConst then return true end
if b.isConst and not a.isConst then return false end
if a.isConst and b.isConst then return a.constValue < b.constValue end
return a.lowerName < b.lowerName
end
local sortByOffset = function(a, b)
if a.isConst and not b.isConst then return true end
if b.isConst and not a.isConst then return false end
if a.isConst and b.isConst then return a.constValue < b.constValue end
return a.offset < b.offset
end
--[[
Show the search form, mono.selectedImage should be set to the
image to search.
--]]
function mono.formClass:show(class, field, method)
if mono.selectedImage == nil or class == nil then return end
self.image = mono.selectedImage
self.class = class
-- function to update lists, for setting on menu items that
-- make other changes
local funcUpdate = function(sender) self:setFieldsAndMethods() end
formMonoClass.listFields.OnData = function(sender, listitem)
self:listFields_OnData(sender, listitem)
end
formMonoClass.listFields.OnDblClick = function(sender)
self:listFields_OnDblClick(sender)
end
formMonoClass.listMethods.OnData = function(sender, listitem)
self:listMethods_OnData(sender, listitem)
end
formMonoClass.listMethods.OnDblClick = function(sender)
self:listMethods_OnDblClick(sender)
end
formMonoClass.miSortFieldsByOffset.OnClick = funcUpdate
formMonoClass.miShowInherited.OnClick = funcUpdate
formMonoClass.miShowUsage.OnClick = funcUpdate
-- create our own lists of fields and methods that we can sort
-- and filter
self:setFieldsAndMethods()
-- show form
formMonoClass.show()
end
--[[
Set fields and methods arrays, sorted appropriately and
with extra fields, including possibly parent fields and methods
--]]
function mono.formClass:setFieldsAndMethods()
local other = ''
if formMonoClass.miShowUsage.Checked then other = ' (usage by other classes)' end
formMonoClass.labelClassName.Caption = string.format('Mono Class: %s:%s%s', self.class.namespace, self.class.name, other)
local fields = {}
local methods = {}
if formMonoClass.miShowUsage.Checked then
---------- show where class is used in other classes
for i,class in ipairs(self.image.classes) do
for j,method in ipairs(class.methods) do
local found = false
if method.returnType == self.class.name then
found = true
else
for k,p in ipairs(method.parameters) do
if p.typeName == self.class.name then found = true end
end
end
if found then table.insert(methods, method) end
end
for j,field in ipairs(class.fields) do
if field.typeName == self.class.name then table.insert(fields, field) end
end
end
else
---------- basic class fields and methods
local c = self.class
while c ~= nil do
for i,field in ipairs(c.fields) do
table.insert(fields, field)
end
for i,method in ipairs(c.methods) do
table.insert(methods, method)
end
if formMonoClass.miShowInherited.Checked then
c = c.parent
else
c = nil
end
end
end
if formMonoClass.miSortByClassFirst.Checked then
table.sort(fields, formMonoClass.miSortFieldsByOffset.Checked and sortByClassThenOffset or sortByClassThenName)
table.sort(methods, sortByClassThenName)
else
table.sort(fields, formMonoClass.miSortFieldsByOffset.Checked and sortByOffset or sortByName)
table.sort(methods, sortByName)
end
self.fields = fields
self.methods = methods
formMonoClass.listFields.Items.Count = 0
formMonoClass.listFields.Items.Count = #fields
formMonoClass.listMethods.Items.Count = 0
formMonoClass.listMethods.Items.Count = #methods
end
-- handler to display fields in list view
function mono.formClass:listFields_OnData(sender, listitem)
-- columns are offset (or 'STATIC'), Type, Name
local index = listitem.Index + 1
local field = self.fields[index]
-- columns are Offset, Type, Name
if field.isStatic then
if field.isConst then
listitem.Caption = 'Const:'..string.format('%2X', field.constValue or 0)
else
listitem.Caption = 'Static:'..string.format('%2X', field.offset or 0)
end
else
listitem.Caption = string.format('%02X', field.offset or 0)
end
local className = ''
if field.class.name ~= self.class.name then className = field.class.name end
listitem.SubItems.text = table.concat({field.typeName or '??', field.name, className}, '\n')
end
-- handler to display methods in list view
function mono.formClass:listMethods_OnData(sender, listitem)
local method = self.methods[listitem.Index + 1]
if method == nil then
listitem.Caption = 'nil index '..tostring(listitem.Index + 1)
else
listitem.Caption = method.name
local className = ''
if method.class.name ~= self.class.name then className = method.class.name end
local ps = {}
for i,p in ipairs(method.parameters) do
table.insert(ps, string.format('%s %s', p.type, p.name))
end
local parms = method.returnType..' ('..table.concat(ps, ', ')..')'
--print('className, method.class.name', className, method.class.name)
listitem.SubItems.text = table.concat({ parms, className }, '\n')
end
end
local parameters = { 'RCX', 'RDX', 'R8', 'R9', '[RBP+30]', '[RBP+38]', '[RBP+40]', '[RBP+48]', '[RBP+50]' }
local floatParameters = { 'XMM0', 'XMM1', 'XMM2', 'XMM3', 'XMM4', 'XMM5', 'XMM6'}
function mono.formClass:popupMethod_OnPopup(sender)
end
function mono.formClass:listMethods_OnDblClick(sender)
local method = self.methods[sender.ItemIndex + 1]
--print("method: "..tostring(method.id))
if method then
local address = mono_compile_method(method.id)
--print("address: "..tostring(address))
getMemoryViewForm().DisassemblerView.SelectedAddress = address
getMemoryViewForm().show()
local hookInfo = hookAt(address)
-- have aobString, hookString, returnString, instructions
--[[ how to get method signature
local ps = {}
for i,p in ipairs(method.parameters) do
table.insert(ps, string.format('%s %s', p.type, p.name))
end
local parms = method.returnType..' ('..table.concat(ps, ', ')..')'
]]
local lines = {}
table.insert(lines, "define(hook,"..hookInfo.hookString..")")
table.insert(lines, "define(bytes,"..hookInfo.aobString..")")
table.insert(lines, "")
table.insert(lines, "[enable]")
table.insert(lines, "")
table.insert(lines, "assert(hook, bytes)")
table.insert(lines, "alloc(newmem,$1000, hook)")
table.insert(lines, "{")
-- note: per x64 calling convention, RCX might actually be space for
-- a pre-allocated structure for the return value and other parameters
-- might be moved one further down the list
table.insert(lines, " RCX: "..method.class.name.." (this)")
for i,p in ipairs(method.parameters) do
local param = parameters[i + 1]
if (p.type == "System.Single" or p.type == "System.Double") then param = floatParameters[i + 1] end
table.insert(lines, " "..param..": "..tostring(p.type).." "..tostring(p.name))
end
table.insert(lines, "")
table.insert(lines, " Returns (RAX) "..method.returnType)
table.insert(lines, "}")
table.insert(lines, "")
table.insert(lines, "newmem:")
table.insert(lines, " // original code")
for i,c in ipairs(hookInfo.instructions) do
table.insert(lines, " "..c)
end
table.insert(lines, " jmp hook+"..string.format("%X", hookInfo.returnOffset))
table.insert(lines, "")
table.insert(lines, "hook:")
table.insert(lines, " jmp newmem")
table.insert(lines, "")
table.insert(lines, "[disable]")
table.insert(lines, "")
table.insert(lines, "hook:")
table.insert(lines, " db bytes")
table.insert(lines, "")
table.insert(lines, "dealloc(newmem)")
local t = {}
for i,v in ipairs(lines) do
table.insert(t, v);
table.insert(t, "\r\n")
end
local aa = table.concat(t)
getMemoryViewForm().AutoInject1.DoClick()
for i=0,getFormCount()-1 do --this is sorted from z-level. 0 is top
local f=getForm(i)
if getForm(i).ClassName == 'TfrmAutoInject' then
f.assemblescreen.Lines.Text = aa
break
end
end
end
end
--[[
When double-clicking a field, print out the base address for the statics of the
class and the address of the clicked-on static field.
--]]
function mono.formClass:listFields_OnDblClick(sender)
local field = self.fields[sender.ItemIndex + 1]
if field then
local class = field.class
local image = class.image
local domainId = image.domain
print('double-clicked on class '..tostring(field.class.name)..' field '..tostring(field.name)..' domain '..tostring(domainId))
local address = mono_class_getStaticFieldAddress(domainId, class.id)
print('statics base address: '..string.format("%x", address))
print(class.name..'.'..field.name..': '..string.format("%x", address + field.offset))
end
end
--[[
IN PROGRESS - take an address and create an AA script to hook at that address
expecting MONO code. Will process instructions until 5 bytes (for jmp) are
processed. Basic format is like this for address 'CryingSuns.PlayerStatus:BattleshipState:HasAuxiliarySystemType+28'
[enable]
assert("CryingSuns.PlayerStatus:BattleshipState:HasAuxiliarySystemType":+28, )
]]
function hookAt(address)
local pos = string.find(address, "+", 1, true)
local name = address
local offset = 0
if (pos ~= nil) then
name = string.substring(1, pos - 1)
offset = tonumber(string.sub(pos + 1), 16)
end
local actualAddress = getAddress(name) + offset
local data = {
hookString = util.safeAddress(getNameFromAddress(actualAddress)), -- used for injection, etc
instructions = {},
aobString = ""
}
local aobs = {}
while (offset < 5) do
local parsed = disassembleAndParse(actualAddress + offset)
if #aobs > 0 then table.insert(aobs, " ") end
table.insert(aobs, parsed.bytesString)
table.insert(data.instructions, parsed.instructionString)
offset = offset + parsed.length
end
data.aobString = table.concat(aobs)
data.returnString = util.safeAddress(getNameFromAddress(actualAddress + offset))
data.returnOffset = offset
return data
end
function hookMethod(method)
end
--[[
Expects address to be a number
]]
function disassembleAndParse(address)
local disassembly = disassemble(address)
local parts = util.split(disassembly, "-")
for i = 1,#parts do
if i == 2 then
parts = parts:gsub("%s+", "") -- remove ALL whitespace from bytes
else
parts = parts:gsub("^%s*(.-)%s*$", "%1") -- remove whitespace from ends
end
end
local aob = {}
local i = 1
while i < string.len(parts[2]) do
if (i ~= 1) then table.insert(aob, " ") end
table.insert(aob, string.sub(parts[2], i, i+1))
i = i + 2
end
local instructionString = parts[3]
for k,v in parts[3]:gmatch("[0-9a-fA-F]+") do
if k:len() == 8 or k:len() == 16 then
instructionString = instructionString:gsub(k, getNameFromAddress(k))
end
end
local result = {
address = getAddress(address),
addressString = util.safeAddress(getNameFromAddress(address)),
aob = aob,
bytesString = table.concat(aob),
disassembly = disassembly,
instructionString = instructionString,
length = getInstructionSize(address),
originalInstructionString = parts[3]
}
return result
end
//Later back on when i launch it up as on the screen here
//When i select game i cannot launch any of scripts image down below.
// I CANT CHECK ANY OF BOXES OR CHANGE VALUES
+ maybe that helps //The first line shows up some error, idk what is goin on
//IM USING NEW .CT version
Re: [REQUEST] Tap Ninja
There is a new update and the Player data does not work
Re: [REQUEST] Tap Ninja
Cheat table Update
v2.7.2 build ID : 8605273
v2.7.2 build ID : 8605273
Check build ID here
[Link]
Who is online
Users browsing this forum: djpowe, DotBot, Fahry, jenya.khaki, Jsdf1995