develop
Timothy Collett 2012-06-19 14:48:40 -04:00
commit 4ca3aa878a
6 changed files with 345 additions and 82 deletions

@ -1172,9 +1172,9 @@ and are only documented here for completeness:
Returns the pre-extracted vtable address ``name``, or *nil*. Returns the pre-extracted vtable address ``name``, or *nil*.
* ``dfhack.internal.getBase()`` * ``dfhack.internal.getRebaseDelta()``
Returns the base address of the process. Returns the ASLR rebase offset of the DF executable.
* ``dfhack.internal.getMemRanges()`` * ``dfhack.internal.getMemRanges()``

@ -1329,8 +1329,8 @@ global environment, persistent between calls to the script.</p>
<li><p class="first"><tt class="docutils literal">dfhack.internal.getVTable(name)</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.internal.getVTable(name)</tt></p>
<p>Returns the pre-extracted vtable address <tt class="docutils literal">name</tt>, or <em>nil</em>.</p> <p>Returns the pre-extracted vtable address <tt class="docutils literal">name</tt>, or <em>nil</em>.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.getBase()</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.internal.getRebaseDelta()</tt></p>
<p>Returns the base address of the process.</p> <p>Returns the ASLR rebase offset of the DF executable.</p>
</li> </li>
<li><p class="first"><tt class="docutils literal">dfhack.internal.getMemRanges()</tt></p> <li><p class="first"><tt class="docutils literal">dfhack.internal.getMemRanges()</tt></p>
<p>Returns a sequence of tables describing virtual memory ranges of the process.</p> <p>Returns a sequence of tables describing virtual memory ranges of the process.</p>

@ -1036,10 +1036,10 @@ static void *checkaddr(lua_State *L, int idx, bool allow_null = false)
return rv; return rv;
} }
static uint32_t getBase() { return Core::getInstance().p->getBase(); } static int getRebaseDelta() { return Core::getInstance().vinfo->getRebaseDelta(); }
static const LuaWrapper::FunctionReg dfhack_internal_module[] = { static const LuaWrapper::FunctionReg dfhack_internal_module[] = {
WRAP(getBase), WRAP(getRebaseDelta),
{ NULL, NULL } { NULL, NULL }
}; };
@ -1074,6 +1074,7 @@ static int internal_setAddress(lua_State *L)
} }
// Print via printerr, so that it is definitely logged to stderr.log. // Print via printerr, so that it is definitely logged to stderr.log.
addr -= Core::getInstance().vinfo->getRebaseDelta();
std::string msg = stl_sprintf("<global-address name='%s' value='0x%x'/>", name.c_str(), addr); std::string msg = stl_sprintf("<global-address name='%s' value='0x%x'/>", name.c_str(), addr);
dfhack_printerr(L, msg); dfhack_printerr(L, msg);

@ -233,19 +233,55 @@ struct HeapBlock
ULONG reserved; ULONG reserved;
}; };
*/ */
// FIXME: NEEDS TESTING!
// FIXME: <warmist> i noticed that if you enumerate it twice, second time it returns wrong .text region size static void GetDosNames(std::map<string, string> &table)
{
// Partially based on example from msdn:
// Translate path with device name to drive letters.
TCHAR szTemp[512];
szTemp[0] = '\0';
if (GetLogicalDriveStrings(sizeof(szTemp)-1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = " :";
BOOL bFound = FALSE;
TCHAR* p = szTemp;
do
{
// Copy the drive letter to the template string
*szDrive = *p;
// Look up each device name
if (QueryDosDevice(szDrive, szName, MAX_PATH))
table[szName] = szDrive;
// Go to the next NULL character.
while (*p++);
} while (*p); // end of string
}
}
void Process::getMemRanges( vector<t_memrange> & ranges ) void Process::getMemRanges( vector<t_memrange> & ranges )
{ {
MEMORY_BASIC_INFORMATION MBI; MEMORY_BASIC_INFORMATION MBI;
//map<char *, unsigned int> heaps; //map<char *, unsigned int> heaps;
uint64_t movingStart = 0; uint64_t movingStart = 0;
PVOID LastAllocationBase = 0;
map <char *, string> nameMap; map <char *, string> nameMap;
map <string,string> dosDrives;
// get page size // get page size
SYSTEM_INFO si; SYSTEM_INFO si;
GetSystemInfo(&si); GetSystemInfo(&si);
uint64_t PageSize = si.dwPageSize; uint64_t PageSize = si.dwPageSize;
// get dos drive names
GetDosNames(dosDrives);
ranges.clear();
// enumerate heaps // enumerate heaps
// HeapNodes(d->my_pid, heaps); // HeapNodes(d->my_pid, heaps);
// go through all the VM regions, convert them to our internal format // go through all the VM regions, convert them to our internal format
@ -254,52 +290,106 @@ void Process::getMemRanges( vector<t_memrange> & ranges )
movingStart = ((uint64_t)MBI.BaseAddress + MBI.RegionSize); movingStart = ((uint64_t)MBI.BaseAddress + MBI.RegionSize);
if(movingStart % PageSize != 0) if(movingStart % PageSize != 0)
movingStart = (movingStart / PageSize + 1) * PageSize; movingStart = (movingStart / PageSize + 1) * PageSize;
// skip empty regions and regions we share with other processes (DLLs)
if( !(MBI.State & MEM_COMMIT) /*|| !(MBI.Type & MEM_PRIVATE)*/ ) // Skip unallocated address space
if (MBI.State & MEM_FREE)
continue; continue;
// Find range and permissions
t_memrange temp; t_memrange temp;
memset(&temp, 0, sizeof(temp));
temp.start = (char *) MBI.BaseAddress; temp.start = (char *) MBI.BaseAddress;
temp.end = ((char *)MBI.BaseAddress + (uint64_t)MBI.RegionSize); temp.end = ((char *)MBI.BaseAddress + (uint64_t)MBI.RegionSize);
temp.read = MBI.Protect & PAGE_EXECUTE_READ || MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_READONLY || MBI.Protect & PAGE_READWRITE;
temp.write = MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_READWRITE;
temp.execute = MBI.Protect & PAGE_EXECUTE_READ || MBI.Protect & PAGE_EXECUTE_READWRITE || MBI.Protect & PAGE_EXECUTE;
temp.valid = true; temp.valid = true;
if(!GetModuleBaseName(d->my_handle, (HMODULE) temp.start, temp.name, 1024))
if (!(MBI.State & MEM_COMMIT))
temp.valid = false; // reserved address space
else if (MBI.Protect & PAGE_EXECUTE)
temp.execute = true;
else if (MBI.Protect & PAGE_EXECUTE_READ)
temp.execute = temp.read = true;
else if (MBI.Protect & PAGE_EXECUTE_READWRITE)
temp.execute = temp.read = temp.write = true;
else if (MBI.Protect & PAGE_EXECUTE_WRITECOPY)
temp.execute = temp.read = temp.write = true;
else if (MBI.Protect & PAGE_READONLY)
temp.read = true;
else if (MBI.Protect & PAGE_READWRITE)
temp.read = temp.write = true;
else if (MBI.Protect & PAGE_WRITECOPY)
temp.read = temp.write = true;
// Merge areas with the same properties
if (!ranges.empty() && LastAllocationBase == MBI.AllocationBase)
{ {
if(nameMap.count((char *)temp.start)) auto &last = ranges.back();
if (last.end == temp.start &&
last.valid == temp.valid && last.execute == temp.execute &&
last.read == temp.read && last.write == temp.write)
{ {
// potential buffer overflow... last.end = temp.end;
strcpy(temp.name, nameMap[(char *)temp.start].c_str()); continue;
} }
else }
#if 1
// Find the mapped file name
if (GetMappedFileName(d->my_handle, temp.start, temp.name, 1024))
{
int vsize = strlen(temp.name);
// Translate NT name to DOS name
for (auto it = dosDrives.begin(); it != dosDrives.end(); ++it)
{ {
// filter away shared segments without a name. int ksize = it->first.size();
if( !(MBI.Type & MEM_PRIVATE) ) if (strncmp(temp.name, it->first.data(), ksize) != 0)
continue; continue;
else
temp.name[0]=0; memcpy(temp.name, it->second.data(), it->second.size());
memmove(temp.name + it->second.size(), temp.name + ksize, vsize + 1 - ksize);
break;
} }
} }
else else
temp.name[0] = 0;
#else
// Find the executable name
char *base = (char*)MBI.AllocationBase;
if(nameMap.count(base))
{
strncpy(temp.name, nameMap[base].c_str(), 1023);
}
else if(GetModuleBaseName(d->my_handle, (HMODULE)base, temp.name, 1024))
{ {
std::string nm(temp.name);
nameMap[base] = nm;
// this is our executable! (could be generalized to pull segments from libs, but whatever) // this is our executable! (could be generalized to pull segments from libs, but whatever)
if(d->base == temp.start) if(d->base == base)
{ {
for(int i = 0; i < d->pe_header.FileHeader.NumberOfSections; i++) for(int i = 0; i < d->pe_header.FileHeader.NumberOfSections; i++)
{ {
char sectionName[9]; /*char sectionName[9];
memcpy(sectionName,d->sections[i].Name,8); memcpy(sectionName,d->sections[i].Name,8);
sectionName[8] = 0; sectionName[8] = 0;
string nm; string nm;
nm.append(temp.name); nm.append(temp.name);
nm.append(" : "); nm.append(" : ");
nm.append(sectionName); nm.append(sectionName);*/
nameMap[(char *)temp.start + d->sections[i].VirtualAddress] = nm; nameMap[base + d->sections[i].VirtualAddress] = nm;
} }
} }
else
continue;
} }
else
temp.name[0] = 0;
#endif
// Push the entry
LastAllocationBase = MBI.AllocationBase;
ranges.push_back(temp); ranges.push_back(temp);
} }
} }

@ -208,7 +208,8 @@ local function find_data_segment()
end end
elseif mem.read and mem.write elseif mem.read and mem.write
and (string.match(mem.name,'/dwarfort%.exe$') and (string.match(mem.name,'/dwarfort%.exe$')
or string.match(mem.name,'/Dwarf_Fortress$')) or string.match(mem.name,'/Dwarf_Fortress$')
or string.match(mem.name,'Dwarf Fortress%.exe'))
then then
data_start = mem.start_addr data_start = mem.start_addr
data_end = mem.end_addr data_end = mem.end_addr

@ -5,6 +5,8 @@ local ms = require 'memscan'
local is_known = dfhack.internal.getAddress local is_known = dfhack.internal.getAddress
local os_type = dfhack.getOSType()
local force_scan = {} local force_scan = {}
for _,v in ipairs({...}) do for _,v in ipairs({...}) do
force_scan[v] = true force_scan[v] = true
@ -20,10 +22,14 @@ MAKE IT RUN CORRECTLY if any data structures
changed, thus possibly leading to CRASHES AND/OR changed, thus possibly leading to CRASHES AND/OR
PERMANENT SAVE CORRUPTION. PERMANENT SAVE CORRUPTION.
This script should be initially started immediately Finding the first few globals requires this script to be
after loading the game, WITHOUT first loading a world. started immediately after loading the game, WITHOUT
It expects vanilla game configuration, without any first loading a world.
custom tilesets or init file changes.
The script expects vanilla game configuration, without
any custom tilesets or init file changes. Never unpause
the game unless instructed. When done, quit the game
without saving using 'die'.
]] ]]
if not utils.prompt_yes_no('Proceed?') then if not utils.prompt_yes_no('Proceed?') then
@ -52,6 +58,34 @@ local function validate_offset(name,validator,addr,tname,...)
ms.found_offset(name,obj) ms.found_offset(name,obj)
end end
local function zoomed_searcher(startn, end_or_sz)
if force_scan.nozoom then
return nil
end
local sv = is_known(startn)
if not sv then
return nil
end
local ev
if type(end_or_sz) == 'number' then
ev = sv + end_or_sz
if end_or_sz < 0 then
sv, ev = ev, sv
end
else
ev = is_known(end_or_sz)
if not ev then
return nil
end
end
sv = sv - (sv % 4)
ev = ev + 3
ev = ev - (ev % 4)
if data:contains_range(sv, ev-sv) then
return ms.DiffSearcher.new(ms.MemoryArea.new(sv,ev))
end
end
local function exec_finder(finder, names) local function exec_finder(finder, names)
if type(names) ~= 'table' then if type(names) ~= 'table' then
names = { names } names = { names }
@ -76,7 +110,8 @@ end
local ordinal_names = { local ordinal_names = {
[0] = '1st entry', [0] = '1st entry',
[1] = '2nd entry' [1] = '2nd entry',
[2] = '3rd entry'
} }
setmetatable(ordinal_names, { setmetatable(ordinal_names, {
__index = function(self,idx) return (idx+1)..'th entry' end __index = function(self,idx) return (idx+1)..'th entry' end
@ -140,8 +175,6 @@ local function find_cursor()
return false return false
end end
exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
-- --
-- Announcements -- Announcements
-- --
@ -158,8 +191,6 @@ local function find_announcements()
dfhack.printerr('Could not find announcements.') dfhack.printerr('Could not find announcements.')
end end
exec_finder(find_announcements, 'announcements')
-- --
-- d_init -- d_init
-- --
@ -198,8 +229,6 @@ local function find_d_init()
dfhack.printerr('Could not find d_init') dfhack.printerr('Could not find d_init')
end end
exec_finder(find_d_init, 'd_init')
-- --
-- gview -- gview
-- --
@ -220,8 +249,6 @@ local function find_gview()
dfhack.printerr('Could not find gview') dfhack.printerr('Could not find gview')
end end
exec_finder(find_gview, 'gview')
-- --
-- World -- World
-- --
@ -257,8 +284,6 @@ menu, and select different types as instructed below:]],
validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type') validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type')
end end
exec_finder(find_world, 'world')
-- --
-- UI -- UI
-- --
@ -291,8 +316,6 @@ menu, and switch modes as instructed below:]],
validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode') validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode')
end end
exec_finder(find_ui, 'ui')
-- --
-- ui_sidebar_menus -- ui_sidebar_menus
-- --
@ -319,9 +342,9 @@ end
local function find_ui_sidebar_menus() local function find_ui_sidebar_menus()
local addr = searcher:find_menu_cursor([[ local addr = searcher:find_menu_cursor([[
Searching for ui_sidebar_menus. Please open the add job Searching for ui_sidebar_menus. Please switch to 'q' mode,
ui of Mason, Craftsdwarfs, or Carpenters workshop, and select a Mason, Craftsdwarfs, or Carpenters workshop, open
select entries in the list:]], the Add Job menu, and move the cursor within:]],
'int32_t', 'int32_t',
{ 0, 1, 2, 3, 4, 5, 6 }, { 0, 1, 2, 3, 4, 5, 6 },
ordinal_names ordinal_names
@ -330,8 +353,6 @@ select entries in the list:]],
addr, df.ui_sidebar_menus, 'workshop_job', 'cursor') addr, df.ui_sidebar_menus, 'workshop_job', 'cursor')
end end
exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus')
-- --
-- ui_build_selector -- ui_build_selector
-- --
@ -366,7 +387,25 @@ number, so when it shows "Min (5000df", it means 50000:]],
addr, df.ui_build_selector, 'plate_info', 'unit_min') addr, df.ui_build_selector, 'plate_info', 'unit_min')
end end
exec_finder(find_ui_build_selector, 'ui_build_selector') --
-- ui_menu_width
--
local function find_ui_menu_width()
local addr = searcher:find_menu_cursor([[
Searching for ui_menu_width. Please exit to the main
dwarfmode menu, then use Tab to do as instructed below:]],
'int8_t',
{ 2, 3, 1 },
{ [2] = 'switch to the most usual [mapmap][menu] layout',
[3] = 'hide the menu completely',
[1] = 'switch to the default [map][menu][map] layout' }
)
ms.found_offset('ui_menu_width', addr)
-- NOTE: Assume that the vars are adjacent, as always
ms.found_offset('ui_area_map_width', addr+1)
end
-- --
-- ui_selected_unit -- ui_selected_unit
@ -395,8 +434,6 @@ into the prompts below:]],
ms.found_offset('ui_selected_unit', addr) ms.found_offset('ui_selected_unit', addr)
end end
exec_finder(find_ui_selected_unit, 'ui_selected_unit')
-- --
-- ui_unit_view_mode -- ui_unit_view_mode
-- --
@ -412,8 +449,6 @@ with 'v', switch the pages as requested:]],
ms.found_offset('ui_unit_view_mode', addr) ms.found_offset('ui_unit_view_mode', addr)
end end
exec_finder(find_ui_unit_view_mode, 'ui_unit_view_mode')
-- --
-- ui_look_cursor -- ui_look_cursor
-- --
@ -434,8 +469,6 @@ and select list entries as instructed:]],
ms.found_offset('ui_look_cursor', addr) ms.found_offset('ui_look_cursor', addr)
end end
exec_finder(find_ui_look_cursor, 'ui_look_cursor')
-- --
-- ui_building_item_cursor -- ui_building_item_cursor
-- --
@ -456,8 +489,6 @@ with many contained items, and select as instructed:]],
ms.found_offset('ui_building_item_cursor', addr) ms.found_offset('ui_building_item_cursor', addr)
end end
exec_finder(find_ui_building_item_cursor, 'ui_building_item_cursor')
-- --
-- ui_workshop_in_add -- ui_workshop_in_add
-- --
@ -468,7 +499,7 @@ Searching for ui_workshop_in_add. Please activate the 'q'
mode, find a workshop without jobs (or delete jobs), mode, find a workshop without jobs (or delete jobs),
and do as instructed below. and do as instructed below.
NOTE: After first 3 steps resize the game window.]], NOTE: If not done after first 3-4 steps, resize the game window.]],
'int8_t', 'int8_t',
{ 1, 0 }, { 1, 0 },
{ [1] = 'enter the add job menu', { [1] = 'enter the add job menu',
@ -477,8 +508,6 @@ NOTE: After first 3 steps resize the game window.]],
ms.found_offset('ui_workshop_in_add', addr) ms.found_offset('ui_workshop_in_add', addr)
end end
exec_finder(find_ui_workshop_in_add, 'ui_workshop_in_add')
-- --
-- ui_workshop_job_cursor -- ui_workshop_job_cursor
-- --
@ -498,8 +527,6 @@ mode, find a workshop with many jobs, and select as instructed:]],
ms.found_offset('ui_workshop_job_cursor', addr) ms.found_offset('ui_workshop_job_cursor', addr)
end end
exec_finder(find_ui_workshop_job_cursor, 'ui_workshop_job_cursor')
-- --
-- ui_building_in_assign -- ui_building_in_assign
-- --
@ -510,7 +537,7 @@ Searching for ui_building_in_assign. Please activate
the 'q' mode, select a room building (e.g. a bedroom) the 'q' mode, select a room building (e.g. a bedroom)
and do as instructed below. and do as instructed below.
NOTE: After first 3 steps resize the game window.]], NOTE: If not done after first 3-4 steps, resize the game window.]],
'int8_t', 'int8_t',
{ 1, 0 }, { 1, 0 },
{ [1] = 'enter the Assign owner menu', { [1] = 'enter the Assign owner menu',
@ -519,8 +546,6 @@ NOTE: After first 3 steps resize the game window.]],
ms.found_offset('ui_building_in_assign', addr) ms.found_offset('ui_building_in_assign', addr)
end end
exec_finder(find_ui_building_in_assign, 'ui_building_in_assign')
-- --
-- ui_building_in_resize -- ui_building_in_resize
-- --
@ -531,7 +556,7 @@ Searching for ui_building_in_resize. Please activate
the 'q' mode, select a room building (e.g. a bedroom) the 'q' mode, select a room building (e.g. a bedroom)
and do as instructed below. and do as instructed below.
NOTE: After first 3 steps resize the game window.]], NOTE: If not done after first 3-4 steps, resize the game window.]],
'int8_t', 'int8_t',
{ 1, 0 }, { 1, 0 },
{ [1] = 'enter the Resize room mode', { [1] = 'enter the Resize room mode',
@ -540,9 +565,6 @@ NOTE: After first 3 steps resize the game window.]],
ms.found_offset('ui_building_in_resize', addr) ms.found_offset('ui_building_in_resize', addr)
end end
exec_finder(find_ui_building_in_resize, 'ui_building_in_resize')
-- --
-- window_x -- window_x
-- --
@ -557,8 +579,6 @@ scroll to the LEFT edge, then do as instructed:]],
ms.found_offset('window_x', addr) ms.found_offset('window_x', addr)
end end
exec_finder(find_window_x, 'window_x')
-- --
-- window_y -- window_y
-- --
@ -573,8 +593,6 @@ scroll to the TOP edge, then do as instructed:]],
ms.found_offset('window_y', addr) ms.found_offset('window_y', addr)
end end
exec_finder(find_window_y, 'window_y')
-- --
-- window_z -- window_z
-- --
@ -582,20 +600,173 @@ exec_finder(find_window_y, 'window_y')
local function find_window_z() local function find_window_z()
local addr = searcher:find_counter([[ local addr = searcher:find_counter([[
Searching for window_z. Please exit to main dwarfmode menu, Searching for window_z. Please exit to main dwarfmode menu,
scroll to ground level, then do as instructed below. scroll to a Z level near surface, then do as instructed below.
NOTE: After first 3 steps resize the game window.]], NOTE: If not done after first 3-4 steps, resize the game window.]],
'int32_t', -1, 'int32_t', -1,
"Please press '>' to scroll one Z level down." "Please press '>' to scroll one Z level down."
) )
ms.found_offset('window_z', addr) ms.found_offset('window_z', addr)
end end
exec_finder(find_window_z, 'window_z') --
-- cur_year
--
local function find_cur_year()
local zone
if os_type == 'windows' then
zone = zoomed_searcher('formation_next_id', 32)
elseif os_type == 'darwin' then
zone = zoomed_searcher('cursor', -32)
elseif os_type == 'linux' then
zone = zoomed_searcher('ui_building_assign_type', -512)
end
if not zone then
dfhack.printerr('Cannot search for cur_year - prerequisites missing.')
return
end
local yvalue = utils.prompt_input('Please enter current in-game year: ', utils.check_number)
local idx, addr = zone.area.int32_t:find_one{yvalue}
if idx then
ms.found_offset('cur_year', addr)
return
end
dfhack.printerr('Could not find cur_year')
end
--
-- cur_year_tick
--
local function find_cur_year_tick()
local zone
if os_type == 'windows' then
zone = zoomed_searcher('artifact_next_id', -32)
else
zone = zoomed_searcher('cur_year', 128)
end
if not zone then
dfhack.printerr('Cannot search for cur_year_tick - prerequisites missing.')
return
end
local addr = zone:find_counter([[
Searching for cur_year_tick. Please exit to main dwarfmode
menu, then do as instructed below:]],
'int32_t', 1,
"Please press '.' to step the game one frame."
)
ms.found_offset('cur_year_tick', addr)
end
-- --
-- THE END -- process_jobs
-- --
print('Done.') local function get_process_zone()
if os_type == 'windows' then
return zoomed_searcher('ui_workshop_job_cursor', 'ui_building_in_resize')
else
return zoomed_searcher('cur_year', 'cur_year_tick')
end
end
local function find_process_jobs()
local zone = get_process_zone() or searcher
local addr = zone:find_menu_cursor([[
Searching for process_jobs. Please do as instructed below:]],
'int8_t',
{ 1, 0 },
{ [1] = 'designate a building to be constructed, e.g a bed',
[0] = 'step or unpause the game to reset the flag' }
)
ms.found_offset('process_jobs', addr)
end
--
-- process_dig
--
local function find_process_dig()
local zone = get_process_zone() or searcher
local addr = zone:find_menu_cursor([[
Searching for process_dig. Please do as instructed below:]],
'int8_t',
{ 1, 0 },
{ [1] = 'designate a tile to be mined out',
[0] = 'step or unpause the game to reset the flag' }
)
ms.found_offset('process_dig', addr)
end
--
-- pause_state
--
local function find_pause_state()
local zone
if os_type == 'linux' then
zone = zoomed_searcher('ui_look_cursor', 32)
elseif os_type == 'windows' then
zone = zoomed_searcher('ui_workshop_job_cursor', 64)
end
zone = zone or searcher
local addr = zone:find_menu_cursor([[
Searching for pause_state. Please do as instructed below:]],
'int8_t',
{ 1, 0 },
{ [1] = 'PAUSE the game',
[0] = 'UNPAUSE the game' }
)
ms.found_offset('pause_state', addr)
end
--
-- MAIN FLOW
--
print('\nInitial globals (need title screen):\n')
exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
exec_finder(find_announcements, 'announcements')
exec_finder(find_d_init, 'd_init')
exec_finder(find_gview, 'gview')
print('\nCompound globals (need loaded world):\n')
exec_finder(find_world, 'world')
exec_finder(find_ui, 'ui')
exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus')
exec_finder(find_ui_build_selector, 'ui_build_selector')
print('\nPrimitive globals:\n')
exec_finder(find_ui_menu_width, { 'ui_menu_width', 'ui_area_map_width' })
exec_finder(find_ui_selected_unit, 'ui_selected_unit')
exec_finder(find_ui_unit_view_mode, 'ui_unit_view_mode')
exec_finder(find_ui_look_cursor, 'ui_look_cursor')
exec_finder(find_ui_building_item_cursor, 'ui_building_item_cursor')
exec_finder(find_ui_workshop_in_add, 'ui_workshop_in_add')
exec_finder(find_ui_workshop_job_cursor, 'ui_workshop_job_cursor')
exec_finder(find_ui_building_in_assign, 'ui_building_in_assign')
exec_finder(find_ui_building_in_resize, 'ui_building_in_resize')
exec_finder(find_window_x, 'window_x')
exec_finder(find_window_y, 'window_y')
exec_finder(find_window_z, 'window_z')
print('\nUnpausing globals:\n')
exec_finder(find_cur_year, 'cur_year')
exec_finder(find_cur_year_tick, 'cur_year_tick')
exec_finder(find_process_jobs, 'process_jobs')
exec_finder(find_process_dig, 'process_dig')
exec_finder(find_pause_state, 'pause_state')
print('\nDone. Now add newly-found globals to symbols.xml.')
searcher:reset() searcher:reset()