Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Custom Command Line Parser

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting
View previous topic :: View next topic  
Author Message
RandName
Newbie cheater
Reputation: 0

Joined: 19 Jun 2015
Posts: 22

PostPosted: Fri Apr 08, 2016 5:08 pm    Post subject: Custom Command Line Parser Reply with quote

Hey,

I tried myself at writing a custom command line parser in Lua.
Putting the ui aside, there were a couple difficulties I encountered.
I have finally decided to choose a class based command system.
You will see in the code what I mean with that Razz

The advantages of using classes is, that the command structure is more flexible and also more organized.

Here is the example code:
Code:

-- some globals
local script = {}
script.name = "Script"
script.ceversion = 6.5
script.state = false

-- ui stuff
function script.toggleCE(sender)
  if not script.state and not script.debugging then
    hideAllCEWindows()
    script.state = true
  elseif script.state then
    ui = nil
    script.state = false
    print = _print
    object_destroy(sender)
    unhideMainCEwindow()
  else
    script.state = true
  end
end

script.toggleCE()
-- Creating basic ui elements
local ui = {}
ui.form = createForm(true)
ui.output = createMemo(ui.form)
ui.input = createEdit(ui.form)

-- Defining the properties
-- -- form
form_centerScreen(ui.form)
form_onClose(ui.form, script.toggleCE)

setProperty(ui.form, "BiDiMode", "bdLeftToRight")
setProperty(ui.form, "Caption", script.name)

ui.form.setSize(450, 350)

-- -- output
ui.output.setWordWrap(false)
ui.output.setScrollbars(6)
setProperty(ui.output, "BorderStyle", "bsSingle")

ui.output.setPosition(5, 5)
local xp, yp = ui.output.getPosition()
local formxs, formys = ui.form.getSize()
ui.output.setSize(formxs - xp * 2, formys * 0.9)

-- -- input
ui.input.OnKeyPress = function (sender, key)
  local vk = string.byte(key)

  if (vk == VK_RETURN) then -- send input to pInput() function
    if pInput(control_getCaption(sender)) then
      sender.clear()
    end
  elseif (vk == 35) then -- (#) enable lua mode (bypass pInput)
    if not script.luamode then script.luamode = true else script.luamode = false end
    return
  elseif not script.luamode then
    if (vk == 60) then -- (<) going one back in inputHistory
      if script.inputHistory then
        if not script.inputIndex then script.inputIndex = #script.inputHistory else if script.inputIndex > 1 then script.inputIndex = script.inputIndex - 1 end end
        control_setCaption(sender, script.inputHistory[script.inputIndex])
        doKeyPress(35)
        return
      end
    elseif (vk == 62) then -- (>) going one forward in inputHistory
      if script.inputHistory then
        if not script.inputIndex then script.inputIndex = #script.inputHistory else if script.inputIndex < #script.inputHistory then script.inputIndex = script.inputIndex + 1 end end
        control_setCaption(sender, script.inputHistory[script.inputIndex])
        doKeyPress(35)
        return
      end
    end
  end
  return key
end

local xs, ys = ui.output.getSize()
setProperty(ui.input, "BorderStyle", "bsSingle")
ui.input.setPosition(xp, ys + 2 * yp - 1)
ui.input.setSize(xs, 0)

-- custom print
-- send output to new ui
if not _print then _print = print end -- backup of original print function
if not format then format = string.format end
if not fprint then fprint = function (str, ...) print(format(str, ...)) end end
function print(...)
  local function split(s, delimiter)
    local result = {};
    for match in (s..delimiter):gmatch("(.-)["..delimiter.."]") do
        table.insert(result, match);
    end
    return result;
  end
  local strTable = split(..., '\n')
  for i = 1,#strTable do
    local str = strTable[i]
    memo_append(ui.output, str)
    if script.debugging then _print(str) end
  end
end

-- command input handler
-- _ = input from user
-- returns -> true, if input was accepted, else false
function pInput(_)
  if _:find("[.].+") == 1 then assert(loadstring(_:sub(2)))() return true end -- direct lua escape
  if not script.ready then print("error, commands are not available until the script is done with initializing!") return false end -- waiting for init to end
  if not script.inputHistory then script.inputHistory = {} script.inputHistory[1] = _ else script.inputHistory[#script.inputHistory + 1] = _ end -- adding entry to inputHistory
  script.inputIndex = #script.inputHistory + 1 -- increasing inputIndex
  local input = {}
  for str in _:gmatch('([^,]+)') do -- seperating input by [, ]
    str = str:gsub("^%s+", "")
    input[#input + 1] = str
  end

  if not cmdTable then print("ERROR: No commands loaded!") return false end
  --[[
  if cmdTable['alias'].state then -- if alias function is at work
    return aliasFunc(_)
  end
  --]]

  local ret = false
  for i,v in ipairs(input) do
    local inlist = {}
    for str in v:gmatch('([^%s]+)') do -- seperating input by spaces
      inlist[#inlist + 1] = str
    end
    print('> ' .. v)
    local result, argval = cmdTable:getFunc(inlist)
    if result then result:func(argval) ret = true else print("error, [".. i .."] unknown command!") end
  end

  return ret
end

-- file handling
function script.rf(f, mode) local r = io.open(f, mode or 'rb') local ret = r:read('*all') r:close() return ret end
function script.wf(f, mode, str) local r = io.open(f, mode or 'w+') r:write(str) r:close() end

-- check CE version => has to be 6.5
if getCEVersion() == script.ceversion then

else
  showMessage('Your CE version ('.. getCEVersion() ..') is incompatible with this script.\nPlease install CE ' .. script.ceversion)
  print('Your CE version ('.. getCEVersion() ..') is incompatible with this script.\nPlease install CE ' .. script.ceversion .. '!')
end

function class(a,b)local c={}if not b and type(a)=='function'then b=a;a=nil elseif type(a)=='table'then for d,e in pairs(a)do c[d]=e end;c._base=a end;c.__index=c;local f={}f.__call=function(g,...)local h={}setmetatable(h,c)if b then b(h,...)else if a and a.init then a.init(h,...)end end;return h end;c.init=b;c.is_a=function(self,i)local j=getmetatable(self)while j do if j==i then return true end;j=j._base end;return false end;setmetatable(c,f)return c end

Command = class(function(cmd, name, kind, ...)
  cmd[1] = name
  cmd[2] = kind
  local args = table.pack(...)
  for i,v in ipairs(args) do
    if type(v) == 'table' then
      cmd[#cmd + 1] = Command(unpack(v))
    else
      cmd.func = v
    end
  end
  end)

CList = class(function(cl) end)

function CList.insert(cl, cmd)
  if type(cmd) == 'table' and getmetatable(cmd) == Command then
    cl[#cl + 1] = cmd
  end
end

function CList.getFunc( cl, tbl )
  local input = type(tbl) == 'table' and tbl or nil
  if input then
    local retTbl = nil
    for i,v in ipairs(input) do
      if type(tonumber(v)) ~= 'number' then
        retTbl = cl:getTableByName(v, retTbl)
        if not retTbl then return nil end
      else
        return retTbl, v
      end
    end
    return retTbl
  else return nil end
end

function CList.getTableByName(cl, name, tbl)
  if not tbl then
    for i,v in ipairs(cl) do
      if type(v) == 'table' then
        if v[1] == name then
          return cl[i]
        end
      end
    end
    return nil
  else
    for i,v in ipairs(tbl) do
      if v[1] == name then
        return tbl[i]
      end
    end
    return nil
  end
end

-- example command
cmdTable = CList()
cmdTable:insert(Command(
  'foo', -- name
  'cmd', -- kind (not used yet)
  -- following args are either subcommands or a execution function
  {
    'bar', -- name
    'subcmd', -- kind
    function(self) print('foo.bar()') end, -- execution function
    {
      'bor',
      'subcmd',
      function(self) print('foo.bar.bor()') end,
    },
    {
      'val',
      'argcmd',
      function(self, val) print('prev val: ' .. tostring(self.val)) self.val = val print('val: ' .. tostring(val)) end, -- note that 'val' is passed here
    }
  },
  {
    'other',
    'subcmd';
    function() print('foo.other()') end,
    {
      'toggle',
      'togcmd',
      function(self) print('prev state: ' .. tostring(self.state)) self.state = not self.state print('state: ' .. tostring(self.state)) end,
    }
  }))

script.ready = true


Commands are defined this way:
Code:

-- example command
cmdTable = CList()
cmdTable:insert(Command(
  'foo', -- name
  'cmd', -- kind (not used yet)
  -- following args are either subcommands or a execution function
  {
    'bar', -- name
    'subcmd', -- kind
    function(self) print('foo.bar()') end, -- execution function
    {
      'bor',
      'subcmd',
      function(self) print('foo.bar.bor()') end,
    },
    {
      'val',
      'argcmd',
      function(self, val) print('prev val: ' .. tostring(self.val)) self.val = val print('val: ' .. tostring(val)) end, -- note that 'val' is passed here
    }
  },
  {
    'other',
    'subcmd';
    function() print('foo.other()') end,
    {
      'toggle',
      'togcmd',
      function(self) print('prev state: ' .. tostring(self.state)) self.state = not self.state print('state: ' .. tostring(self.state)) end,
    }
  }))


Like you can see, you can have nested commands with as much sub-commands as you like.

Here is a test session:
Code:

> foo bar
foo.bar()
> foo bar bor
foo.bar.bor()
> foo bar val 10
prev val: nil
val: 10
> foo bar val 2
prev val: 10
val: 2
> foo other
foo.other()
> foo other toggle
prev state: nil
state: true
> foo other toggle
prev state: true
state: false

(Note that the input is written with a '>' character before.)

Like you can see, passing of arguments to the sub-commands is possible too.

UPDATE:

I have modified the code to support toggle values (see above).
And also added the support for multi commands in one line (separated through commas).

I hope that this can help or inspire you in some way or another.
I appreciate any kind of feedback and suggestions for improvements!

Greetings,
RandName/Randshot
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites