panraven

ce_csharp_decimal.lua

Dec 18th, 2020 (edited)
195
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- 64bit CE only,
  2. -- the value-to-bytes in the custom type asm script is not implement,
  3. -- instead the value change function is implemented by AddressList.OnValueChange, it allow more precision input of the decimal value.
  4.  
  5. local cs_decimal_typeName = 'CSharp_Decimal_ASM'
  6.  
  7. cs_decimal_typeName = cs_decimal_typeName:gsub('%W','_')
  8.  
  9. local carry = 0x100000000
  10.  
  11. local function QNAdd(me, rhs)
  12.   local ret = {}
  13.   for i=1,4 do ret[i]=me[i]end
  14.   for i=1,4 do
  15.     ret[i] = ret[i]+rhs[i]
  16.     if ret[i] >= carry then
  17.       ret[i], ret[i+1] = ret[i] - carry, i==4 and ret[i+1] or 1+ret[i+1]
  18.     end
  19.   end
  20.   return ret
  21. end
  22.  
  23. local function QNDump(me)
  24.   local r = {}
  25.   for i=1,#me do r[i]=string.format('%X',me[i]) end
  26.   return 'QN:hex<'..table.concat(me,', ')..'>'
  27. end
  28.  
  29. local QN
  30. QN = setmetatable({[0]={0,0,0,0},[1]={1,0,0,0}},{
  31. __call = function(me,digit,power)local key = digit + (power<<8) return me[key]end,
  32. __index = function(me,key)
  33.   local digit, power, ret = key & 0xf, key >> 8, {0,0,0,0}
  34.   if digit == 0 then
  35.     return ret
  36.   elseif digit==1 then
  37.     local lower = QN(1,power-1)
  38.     for i=1,10 do ret = QNAdd(ret,lower) end
  39.   else
  40.     local lower = QN(1,power)
  41.     for i=1,digit do ret = QNAdd(ret,lower) end
  42.   end
  43.   rawset(me, key, ret)
  44.   return ret
  45. end
  46. })
  47.  
  48. local al = getAddressList()
  49. al.OnValueChange = function(me, mr)
  50.   if mr.Type ~= vtCustom or mr.CustomTypeName ~= cs_decimal_typeName then
  51.     return false
  52.   else
  53.     local addr = mr.currentAddress
  54.     local val = mr.Value
  55.     local ins,inp, sign, lhs, decPos, decimal, rhs, lastMant,hasExp, expSign, exp, mantissa, power
  56.     repeat
  57.       ins = inputQuery(cs_decimal_typeName, 'Enter decimal value to change:', val)
  58.       if not ins then print(cs_decimal_typeName..' abort input for change.')return true end
  59.       inp = ins:gsub('[ _,]','')
  60.       sign, lhs, decimal,decPos, rhs, lastMant, hasExp,expSign, exp = inp:match'^([%+%-]?)(%d+)(%.?)()(%d*)()([eE]?)([%+%-]?)(%d-)$'
  61.       if not sign then print(cs_decimal_typeName..' invalid format : '..ins)end
  62.     until sign
  63.     sign, expSign, power = sign=='-'and -1 or 1, expSign=='-'and -1 or 1,#exp>0 and #hasExp>0 and tonumber(exp)or 0
  64.     power =  lastMant - decPos - power * expSign
  65.     mantissa = #hasExp>0 and lhs..rhs or lhs..rhs..exp
  66.     local expected = sign*tonumber(mantissa)*math.pow(10,power)
  67. --    print(sign, power, decPos, mantissa, expected)
  68.  
  69.     while mantissa:sub(1,1)=='0' do mantissa=mantissa:sub(2)  end
  70.     expected = sign*tonumber(mantissa)*math.pow(10,power)
  71. --    print(sign, power, decPos, mantissa, expected)
  72.     while mantissa:sub(-1)=='0' and power<0 do power, mantissa=power+1, mantissa:sub(1,-2) end
  73.     expected = sign*tonumber(mantissa)*math.pow(10,power)
  74. --    print(sign, power, decPos, mantissa, expected)
  75.     while power>28 and #mantissa<28 do power, mantissa=power-1, mantissa..'0'   end
  76.  
  77.     expected = sign*tonumber(mantissa)*math.pow(10,power)
  78. --    print(sign, power, decPos, mantissa, expected)
  79.  
  80.  
  81.  
  82.     local ret = QN[0]
  83.     for i=1,math.min(28,#mantissa) do
  84.       local e = tonumber(mantissa:sub(-i,-i))
  85.       ret = QNAdd(ret ,QN(e,i-1))
  86.     end
  87. --    print(QNDump(ret))
  88.  
  89.     if ret[4]~=0 or #mantissa>28 or power>28 then print(cs_decimal_typeName..' digits overflow :'..ins)return true end
  90.  
  91.     local flags = ( (sign <0 and (1 << 15) or 0 )+ power)<<16
  92.  
  93.     if not mantissa:find'[1-9]'then writeQword(addr,0);writeQword(addr+8,0);return true end -- zero case
  94.  
  95.     local bt = stringToByteTable(string.pack('I4I4I4',table.unpack(ret,1,3)))
  96.     writeBytes(addr,bt)
  97. --    writeQword(addr, tonumber(mantissa))
  98.     writeInteger(addr+0xc,flags)
  99.     return true
  100.   end
  101. end
  102.  
  103.  
  104. local script = string.format([[
  105. alloc(ConvertRoutine,$4000)
  106. alloc(ConvertBackRoutine,1024)
  107. alloc(TypeName,256)
  108. alloc(ByteSize,4)
  109. alloc(UsesFloat,1)
  110. alloc(CallMethod,1)
  111.  
  112. TypeName:
  113. db '%s',0
  114.  
  115. ByteSize:
  116. dd 10
  117.  
  118. UsesFloat:
  119. db 1 //Change to 1 if this custom type should be treated as a float
  120.  
  121. CallMethod:
  122. db 1 //Remove or change to 0 for legacy call mechanism
  123.  
  124. label(ds2to64)
  125. label(s_doublePowers10)
  126. label(NaN)
  127.  
  128. ConvertRoutine+3000:
  129. NaN:
  130. dd 0ffc00000,(float)0.5
  131. ds2to64:
  132. dq (double)1.8446744073709552e+019
  133. s_doublePowers10:
  134. dq (double)1e+00, (double)1e+01, (double)1e+02, (double)1e+03,
  135. dq (double)1e+04, (double)1e+05, (double)1e+06, (double)1e+07,
  136. dq (double)1e+08, (double)1e+09, (double)1e+10, (double)1e+11,
  137. dq (double)1e+12, (double)1e+13, (double)1e+14, (double)1e+15,
  138. dq (double)1e+16, (double)1e+17, (double)1e+18, (double)1e+19,
  139. dq (double)1e+20, (double)1e+21, (double)1e+22, (double)1e+23,
  140. dq (double)1e+24, (double)1e+25, (double)1e+26, (double)1e+27,
  141. dq (double)1e+28, (double)1e+29, (double)1e+30, (double)1e+31,
  142. dq (double)1e+32, (double)1e+33, (double)1e+34, (double)1e+35,
  143. dq (double)1e+36, (double)1e+37, (double)1e+38, (double)1e+39,
  144. dq (double)1e+40, (double)1e+41, (double)1e+42, (double)1e+43,
  145. dq (double)1e+44, (double)1e+45, (double)1e+46, (double)1e+47,
  146. dq (double)1e+48, (double)1e+49, (double)1e+50, (double)1e+51,
  147. dq (double)1e+52, (double)1e+53, (double)1e+54, (double)1e+55,
  148. dq (double)1e+56, (double)1e+57, (double)1e+58, (double)1e+59,
  149. dq (double)1e+60, (double)1e+61, (double)1e+62, (double)1e+63,
  150. dq (double)1e+64, (double)1e+65, (double)1e+66, (double)1e+67,
  151. dq (double)1e+68, (double)1e+69, (double)1e+70, (double)1e+71,
  152. dq (double)1e+72, (double)1e+73, (double)1e+74, (double)1e+75,
  153. dq (double)1e+76, (double)1e+77, (double)1e+78, (double)1e+79,
  154. dq (double)1e+80
  155.  
  156. label(skipDone)
  157. //The convert routine should hold a routine that converts the data to an integer (in eax)
  158. //function declared as: cdecl int ConvertRoutine(unsigned char *input, PTR_UINT address);
  159. //Note: Keep in mind that this routine can be called by multiple threads at the same time.
  160. ConvertRoutine:
  161. //jmp dllname.functionname
  162. [64-bit]
  163. //or manual:
  164. //parameters: (64-bit)
  165. //rcx=address of input
  166. //rdx=address
  167. //mov eax,[rcx] //eax now contains the bytes 'input' pointed to
  168. ////////////////////////////
  169. push   rsi
  170. push   rdi
  171. push   rdx
  172. push   rcx
  173. //  mov    edi,[rcx+04]
  174. //  jmp    skipDone
  175.   mov    rsi,rcx
  176.   mov    edi,[NaN]
  177.  
  178.   mov    edx,[rsi+0c]
  179.   test    edx,7fe0ffff
  180.   jne    skipDone
  181.   shr    edx,10
  182.   and    edx,ff
  183.   cmp    edx,1c
  184.   ja     skipDone
  185.   mov    ecx,edx
  186.  
  187.   movsd      xmm0,[ds2to64]
  188.   cvtsi2sd   xmm1,dword ptr[rsi+08]
  189.   mulsd      xmm0,xmm1
  190.   cvtsi2sd   xmm1,qword ptr[rsi]
  191.   addsd      xmm0,xmm1
  192.   movsd      xmm1,[rcx*8+s_doublePowers10]
  193.   divsd      xmm0,xmm1
  194.  
  195.   test       byte ptr[rsi+0f],80
  196.   je         short @f
  197.     xorpd    xmm1,xmm1
  198.     subsd    xmm1,xmm0
  199.     movsd    xmm0,xmm1
  200. @@:
  201.   cvtsd2ss   xmm0,xmm0
  202.   movd       edi,xmm0
  203.  
  204. skipDone:
  205.   mov    eax,edi
  206. pop    rcx
  207. pop    rdx
  208. pop    rdi
  209. pop    rsi
  210.  
  211. ////////////////////////////
  212. ret
  213. [/64-bit]
  214.  
  215. [32-bit]
  216. //jmp dllname.functionname
  217. //or manual:
  218. //parameters: (32-bit)
  219. push ebp
  220. mov ebp,esp
  221. //[ebp+8]=address of input
  222. //[ebp+c]=address
  223. //example:
  224. mov eax,[ebp+8] //place the address that contains the bytes into eax
  225. mov eax,[eax] //place the bytes into eax so it's handled as a normal 4 byte value
  226.  
  227. pop ebp
  228. ret
  229. [/32-bit]
  230.  
  231. //The convert back routine should hold a routine that converts the given integer back to a row of bytes (e.g when the user wats to write a new value)
  232. //function declared as: cdecl void ConvertBackRoutine(int i, PTR_UINT address, unsigned char *output);
  233. ConvertBackRoutine:
  234. //jmp dllname.functionname
  235. //or manual:
  236. [64-bit]
  237. //parameters: (64-bit)
  238. //ecx=input
  239. //rdx=address
  240. //r8=address of output
  241. //example:
  242.  
  243. ///  do no change
  244. ///mov [r8],ecx //place the integer at the 4 bytes pointed to by r8
  245.  
  246. ret
  247. [/64-bit]
  248.  
  249. [32-bit]
  250. //parameters: (32-bit)
  251. push ebp
  252. mov ebp,esp
  253. //[ebp+8]=input
  254. //[ebp+c]=address
  255. //[ebp+10]=address of output
  256. //example:
  257. push eax
  258. push ebx
  259. mov eax,[ebp+8] //load the value into eax
  260. mov ebx,[ebp+10] //load the output address into ebx
  261. mov [ebx],eax //write the value into the address
  262. pop ebx
  263. pop eax
  264.  
  265. pop ebp
  266. ret
  267. [/32-bit]
  268.  
  269. ]],cs_decimal_typeName)
  270. --print(script)
  271. registerCustomTypeAutoAssembler(script)
  272. --local ct = getCustomType(cs_decimal_typeName)
  273. --print(ct and ct.name)
  274.  
RAW Paste Data