Add more offset finders to the script.

develop
Alexander Gavrilov 2012-06-17 18:44:59 +04:00
parent 16ae6cb1fb
commit dc6cb61979
3 changed files with 311 additions and 81 deletions

@ -344,62 +344,41 @@ function DiffSearcher:idx2addr(idx)
return self.area[self.type]:idx2addr(idx)
end
-- Menu search utility
-- Interactive search utility
function find_menu_cursor(searcher,prompt,data_type,choices,enum)
function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
enum = enum or {}
-- Loop for restarting search from scratch
while true do
print('\n'..prompt)
searcher:begin_search(data_type)
self:begin_search(data_type)
local found = false
local ccursor = 0
-- Loop through choices
while true do
local choice
print('')
-- Select the next value to search for
if type(choices) == 'function' then
print('')
local ok, value, delta = condition_cb(ccursor)
choice = choices(ccursor)
ccursor = ccursor + 1
if not choice then
break
end
else
choice = choices[ccursor+1]
ccursor = (ccursor+1) % #choices
ccursor = ccursor + 1
local cname = enum[choice] or choice
if type(choice) == 'string' and type(cname) == 'number' then
choice, cname = cname, choice
end
if cname ~= choice then
cname = cname..' ('..choice..')'
end
-- Ask the user to select it
print('\n Please select: '..cname)
if not utils.prompt_yes_no(' Continue?', true) then
break
end
if not ok then
break
end
-- Search for it in the memory
local cnt, set = searcher:advance_search(choice)
local cnt, set = self:advance_search(value, delta)
if not cnt then
dfhack.printerr(' Converged to zero candidates; probably a mistake somewhere.')
break
elseif set and cnt == 1 then
-- To confirm, wait for two 1-candidate results in a row
if found then
local addr = searcher:idx2addr(set[1])
local addr = self:idx2addr(set[1])
print(string.format(' Confirmed address: %x\n', addr))
return addr, set[1]
else
@ -416,4 +395,61 @@ function find_menu_cursor(searcher,prompt,data_type,choices,enum)
end
end
function DiffSearcher:find_menu_cursor(prompt,data_type,choices,enum)
enum = enum or {}
return self:find_interactive(
prompt, data_type,
function(ccursor)
local choice
-- Select the next value to search for
if type(choices) == 'function' then
choice = choices(ccursor)
if not choice then
return false
end
else
choice = choices[(ccursor % #choices) + 1]
end
-- Ask the user to select it
if enum ~= 'noprompt' then
local cname = enum[choice] or choice
if type(choice) == 'string' and type(cname) == 'number' then
choice, cname = cname, choice
end
if cname ~= choice then
cname = cname..' ('..choice..')'
end
print(' Please select: '..cname)
if not utils.prompt_yes_no(' Continue?', true) then
return false
end
end
return true, choice
end
)
end
function DiffSearcher:find_counter(prompt,data_type,delta,action_prompt)
delta = delta or 1
return self:find_interactive(
prompt, data_type,
function(ccursor)
if ccursor > 0 then
print(" "..(action_prompt or 'Please do the action.'))
end
if not utils.prompt_yes_no(' Continue?', true) then
return false
end
return true, nil, delta
end
)
end
return _ENV

@ -367,9 +367,9 @@ function prompt_yes_no(msg,default)
if default == nil then
prompt = prompt..' (y/n): '
elseif default then
prompt = prompt..' (y/n/enter=y): '
prompt = prompt..' (y/n)[y]: '
else
prompt = prompt..' (y/n/enter=n): '
prompt = prompt..' (y/n)[n]: '
end
while true do
local rv = dfhack.lineedit(prompt)
@ -378,6 +378,8 @@ function prompt_yes_no(msg,default)
return true
elseif string.match(rv,'^[Nn]') then
return false
elseif rv == 'abort' then
error('User abort in utils.prompt_yes_no()')
elseif rv == '' and default ~= nil then
return default
end

@ -3,9 +3,19 @@
local utils = require 'utils'
local ms = require 'memscan'
local scan_all = false
local is_known = dfhack.internal.getAddress
local scan_all = false
local force_scan = {}
for _,v in ipairs({...}) do
if v == 'all' then
scan_all = true
else
force_scan[v] = true
end
end
collectgarbage()
print[[
@ -48,6 +58,52 @@ local function validate_offset(name,validator,addr,tname,...)
ms.found_offset(name,obj)
end
local function exec_finder(finder, names)
local search = scan_all
if type(names) ~= 'table' then
names = { names }
end
for _,v in ipairs(names) do
if force_scan[v] or not is_known(v) then
search = true
end
end
if search then
if not dfhack.safecall(finder) then
if not utils.prompt_yes_no('Proceed with the rest of the script?') then
searcher:reset()
error('Quit')
end
end
else
print('Already known: '..table.concat(names,', '))
end
end
local ordinal_names = {
[0] = '1st entry',
[1] = '2nd entry'
}
setmetatable(ordinal_names, {
__index = function(self,idx) return (idx+1)..'th entry' end
})
local function list_index_choices(length_func)
return function(id)
if id > 0 then
local ok, len = pcall(length_func)
if not ok then
len = 5
elseif len > 10 then
len = 10
end
return id % len
else
return 0
end
end
end
--
-- Cursor group
--
@ -90,19 +146,14 @@ local function find_cursor()
return false
end
if scan_all or not (
is_known 'cursor' and is_known 'selection_rect' and
is_known 'gamemode' and is_known 'gametype'
) then
find_cursor()
end
exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
--
-- Announcements
--
local function find_announcements()
idx, addr = data.int32_t:find_one{
local idx, addr = data.int32_t:find_one{
25, 25, 31, 31, 24, 24, 40, 40, 40, 40, 40, 40, 40
}
if idx then
@ -113,9 +164,7 @@ local function find_announcements()
dfhack.printerr('Could not find announcements.')
end
if scan_all or not is_known 'announcements' then
find_announcements()
end
exec_finder(find_announcements, 'announcements')
--
-- d_init
@ -142,7 +191,7 @@ local function is_valid_d_init(di)
end
local function find_d_init()
idx, addr = data.int16_t:find_one{
local idx, addr = data.int16_t:find_one{
1,0, 2,0, 5,0, 25,0, -- path_cost
4,4, -- embark_rect
20,1000,1000,1000,1000 -- store_dist
@ -155,10 +204,30 @@ local function find_d_init()
dfhack.printerr('Could not find d_init')
end
if scan_all or not is_known 'd_init' then
find_d_init()
exec_finder(find_d_init, 'd_init')
--
-- gview
--
local function find_gview()
local vs_vtable = dfhack.internal.getVTable('viewscreenst')
if not vs_vtable then
dfhack.printerr('Cannot search for gview - no viewscreenst vtable.')
return
end
local idx, addr = data.uint32_t:find_one{0, vs_vtable}
if idx then
ms.found_offset('gview', addr)
return
end
dfhack.printerr('Could not find gview')
end
exec_finder(find_gview, 'gview')
--
-- World
--
@ -184,10 +253,9 @@ local function is_valid_world(world)
end
local function find_world()
local addr = ms.find_menu_cursor(
searcher, [[
local addr = searcher:find_menu_cursor([[
Searching for world. Please open the stockpile creation
menu, and follow instructions below:]],
menu, and select different types as instructed below:]],
'int32_t',
{ 'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins' },
df.stockpile_category
@ -195,9 +263,7 @@ menu, and follow instructions below:]],
validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type')
end
if scan_all or not is_known 'world' then
find_world()
end
exec_finder(find_world, 'world')
--
-- UI
@ -220,10 +286,9 @@ local function is_valid_ui(ui)
end
local function find_ui()
local addr = ms.find_menu_cursor(
searcher, [[
local addr = searcher:find_menu_cursor([[
Searching for ui. Please open the designation
menu, and follow instructions below:]],
menu, and switch modes as instructed below:]],
'int16_t',
{ 'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps', 'DesignateUpStair',
'DesignateDownStair', 'DesignateUpDownStair', 'DesignateUpRamp', 'DesignateChopTrees' },
@ -232,9 +297,7 @@ menu, and follow instructions below:]],
validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode')
end
if scan_all or not is_known 'ui' then
find_ui()
end
exec_finder(find_ui, 'ui')
--
-- ui_sidebar_menus
@ -261,21 +324,19 @@ local function is_valid_ui_sidebar_menus(usm)
end
local function find_ui_sidebar_menus()
local addr = ms.find_menu_cursor(
searcher, [[
local addr = searcher:find_menu_cursor([[
Searching for ui_sidebar_menus. Please open the add job
ui of Mason, Craftsdwarfs, or Carpenters workshop, and
select entries in the list:]],
'int32_t',
{ 0, 1, 2, 3, 4, 5, 6 }
{ 0, 1, 2, 3, 4, 5, 6 },
ordinal_names
)
validate_offset('ui_sidebar_menus', is_valid_ui_sidebar_menus,
addr, df.ui_sidebar_menus, 'workshop_job', 'cursor')
end
if scan_all or not is_known 'ui_sidebar_menus' then
find_ui_sidebar_menus()
end
exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus')
--
-- ui_build_selector
@ -299,8 +360,7 @@ local function is_valid_ui_build_selector(ubs)
end
local function find_ui_build_selector()
local addr = ms.find_menu_cursor(
searcher, [[
local addr = searcher:find_menu_cursor([[
Searching for ui_build_selector. Please start constructing
a pressure plate, and enable creatures. Then change the min
weight as requested, remembering that the ui truncates the
@ -312,9 +372,7 @@ number, so when it shows "Min (5000df", it means 50000:]],
addr, df.ui_build_selector, 'plate_info', 'unit_min')
end
if scan_all or not is_known 'ui_build_selector' then
find_ui_build_selector()
end
exec_finder(find_ui_build_selector, 'ui_build_selector')
--
-- ui_selected_unit
@ -322,7 +380,7 @@ end
local function find_ui_selected_unit()
if not is_known 'world' then
dfhack.printerr('Cannot find ui_selected_unit: no world')
dfhack.printerr('Cannot search for ui_selected_unit: no world')
return
end
@ -330,30 +388,27 @@ local function find_ui_selected_unit()
dfhack.units.setNickname(unit, i)
end
local addr = ms.find_menu_cursor(
searcher, [[
local addr = searcher:find_menu_cursor([[
Searching for ui_selected_unit. Please activate the 'v'
mode, point it at units, and enter their numeric nickname
into the prompts below:]],
'int32_t',
function()
return utils.prompt_input(' Enter index: ', utils.check_number)
end
end,
'noprompt'
)
ms.found_offset('ui_selected_unit', addr)
end
if scan_all or not is_known 'ui_selected_unit' then
find_ui_selected_unit()
end
exec_finder(find_ui_selected_unit, 'ui_selected_unit')
--
-- ui_unit_view_mode
--
local function find_ui_unit_view_mode()
local addr = ms.find_menu_cursor(
searcher, [[
local addr = searcher:find_menu_cursor([[
Searching for ui_unit_view_mode. Having selected a unit
with 'v', switch the pages as requested:]],
'int32_t',
@ -363,6 +418,143 @@ with 'v', switch the pages as requested:]],
ms.found_offset('ui_unit_view_mode', addr)
end
if scan_all or not is_known 'ui_unit_view_mode' then
find_ui_unit_view_mode()
exec_finder(find_ui_unit_view_mode, 'ui_unit_view_mode')
--
-- ui_look_cursor
--
local function look_item_list_count()
return #df.global.ui_look_list.items
end
local function find_ui_look_cursor()
local addr = searcher:find_menu_cursor([[
Searching for ui_look_cursor. Please activate the 'k'
mode, find a tile with many items or units on the ground,
and select list entries as instructed:]],
'int32_t',
list_index_choices(look_item_list_count),
ordinal_names
)
ms.found_offset('ui_look_cursor', addr)
end
exec_finder(find_ui_look_cursor, 'ui_look_cursor')
--
-- ui_building_item_cursor
--
local function building_item_list_count()
return #df.global.world.selected_building.contained_items
end
local function find_ui_building_item_cursor()
local addr = searcher:find_menu_cursor([[
Searching for ui_building_item_cursor. Please activate the 't'
mode, find a cluttered workshop, trade depot, or other building
with many contained items, and select as instructed:]],
'int32_t',
list_index_choices(building_item_list_count),
ordinal_names
)
ms.found_offset('ui_building_item_cursor', addr)
end
exec_finder(find_ui_building_item_cursor, 'ui_building_item_cursor')
--
-- ui_workshop_in_add
--
local function find_ui_workshop_in_add()
local addr = searcher:find_menu_cursor([[
Searching for ui_workshop_in_add. Please activate the 'q'
mode, find a workshop without jobs (or delete jobs),
and do as instructed:]],
'int8_t',
{ 1, 0 },
{ [1] = 'enter the add job menu',
[0] = 'add job, thus exiting the menu' }
)
ms.found_offset('ui_workshop_in_add', addr)
end
--exec_finder(find_ui_workshop_in_add, 'ui_workshop_in_add')
--
-- ui_workshop_job_cursor
--
local function workshop_job_list_count()
return #df.global.world.selected_building.jobs
end
local function find_ui_workshop_job_cursor()
local addr = searcher:find_menu_cursor([[
Searching for ui_workshop_job_cursor. Please activate the 'q'
mode, find a workshop with many jobs, and select as instructed:]],
'int32_t',
list_index_choices(workshop_job_list_count),
ordinal_names
)
ms.found_offset('ui_workshop_job_cursor', addr)
end
exec_finder(find_ui_workshop_job_cursor, 'ui_workshop_job_cursor')
--
-- window_x
--
local function find_window_x()
local addr = searcher:find_counter([[
Searching for window_x. Please exit to main dwarfmode menu,
scroll to the LEFT edge, then do as instructed:]],
'int32_t', 10,
'Please press Right to scroll right one step.'
)
ms.found_offset('window_x', addr)
end
exec_finder(find_window_x, 'window_x')
--
-- window_y
--
local function find_window_y()
local addr = searcher:find_counter([[
Searching for window_y. Please exit to main dwarfmode menu,
scroll to the TOP edge, then do as instructed:]],
'int32_t', 10,
'Please press Down to scroll down one step.'
)
ms.found_offset('window_y', addr)
end
exec_finder(find_window_y, 'window_y')
--
-- window_z
--
local function find_window_z()
local addr = searcher:find_counter([[
Searching for window_z. Please exit to main dwarfmode menu,
scroll to ground level, then do as instructed:]],
'int32_t', -1,
"Please press '>' to scroll 1 Z level down."
)
ms.found_offset('window_z', addr)
end
--exec_finder(find_window_z, 'window_z')
--
-- THE END
--
print('Done.')
searcher:reset()