develop
Quietust 2014-02-27 15:49:18 -06:00
commit dd268d43d9
9 changed files with 50 additions and 235 deletions

@ -9,6 +9,7 @@ DFHack future
Misc improvements: Misc improvements:
- digfort: improved csv parsing, add start() comment handling - digfort: improved csv parsing, add start() comment handling
- exterminate: allow specifying a caste (exterminate gob:male)
DFHack v0.34.11-r4 DFHack v0.34.11-r4

@ -2125,6 +2125,9 @@ With the special argument ``him``, targets only the selected creature.
With the special argument ``undead``, targets all undeads on the map, With the special argument ``undead``, targets all undeads on the map,
regardless of their race. regardless of their race.
When specifying a race, a caste can be specified to further restrict the
targeting. To do that, append and colon and the caste name after the race.
Any non-dead non-caged unit of the specified race gets its ``blood_count`` Any non-dead non-caged unit of the specified race gets its ``blood_count``
set to 0, which means immediate death at the next game tick. For creatures set to 0, which means immediate death at the next game tick. For creatures
such as vampires, it also sets animal.vanish_countdown to 2. such as vampires, it also sets animal.vanish_countdown to 2.
@ -2140,6 +2143,7 @@ but ignore caged/chained creatures.
Ex:: Ex::
exterminate gob exterminate gob
exterminate gob:male
To kill a single creature, select the unit with the 'v' cursor and:: To kill a single creature, select the unit with the 'v' cursor and::

@ -246,28 +246,6 @@ static int getdir (string dir, vector<string> &files)
return 0; return 0;
} }
bool Process::getThreadIDs(vector<uint32_t> & threads )
{
stringstream ss;
vector<string> subdirs;
if(getdir("/proc/self/task/",subdirs) != 0)
{
//FIXME: needs exceptions. this is a fatal error
cerr << "unable to enumerate threads. This is BAD!" << endl;
return false;
}
threads.clear();
for(size_t i = 0; i < subdirs.size();i++)
{
uint32_t tid;
if(sscanf(subdirs[i].c_str(),"%d", &tid))
{
threads.push_back(tid);
}
}
return true;
}
uint32_t Process::getTickCount() uint32_t Process::getTickCount()
{ {
struct timeval tp; struct timeval tp;

@ -181,28 +181,6 @@ static int getdir (string dir, vector<string> &files)
return 0; return 0;
} }
bool Process::getThreadIDs(vector<uint32_t> & threads )
{
stringstream ss;
vector<string> subdirs;
if(getdir("/proc/self/task/",subdirs) != 0)
{
//FIXME: needs exceptions. this is a fatal error
cerr << "unable to enumerate threads. This is BAD!" << endl;
return false;
}
threads.clear();
for(size_t i = 0; i < subdirs.size();i++)
{
uint32_t tid;
if(sscanf(subdirs[i].c_str(),"%d", &tid))
{
threads.push_back(tid);
}
}
return true;
}
uint32_t Process::getTickCount() uint32_t Process::getTickCount()
{ {
struct timeval tp; struct timeval tp;

@ -24,63 +24,8 @@ distribution.
#include "Internal.h" #include "Internal.h"
#define _WIN32_WINNT 0x0501 // needed for INPUT struct
#define WINVER 0x0501 // OpenThread(), PSAPI, Toolhelp32
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <psapi.h>
#include <tlhelp32.h>
typedef LONG NTSTATUS;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
// FIXME: it is uncertain how these map to 64bit
typedef struct _DEBUG_BUFFER
{
HANDLE SectionHandle;
PVOID SectionBase;
PVOID RemoteSectionBase;
ULONG SectionBaseDelta;
HANDLE EventPairHandle;
ULONG Unknown[2];
HANDLE RemoteThreadHandle;
ULONG InfoClassMask;
ULONG SizeOfInfo;
ULONG AllocatedSize;
ULONG SectionSize;
PVOID ModuleInformation;
PVOID BackTraceInformation;
PVOID HeapInformation;
PVOID LockInformation;
PVOID Reserved[8];
} DEBUG_BUFFER, *PDEBUG_BUFFER;
typedef struct _DEBUG_HEAP_INFORMATION
{
ULONG Base; // 0×00
ULONG Flags; // 0×04
USHORT Granularity; // 0×08
USHORT Unknown; // 0x0A
ULONG Allocated; // 0x0C
ULONG Committed; // 0×10
ULONG TagCount; // 0×14
ULONG BlockCount; // 0×18
ULONG Reserved[7]; // 0x1C
PVOID Tags; // 0×38
PVOID Blocks; // 0x3C
} DEBUG_HEAP_INFORMATION, *PDEBUG_HEAP_INFORMATION;
// RtlQueryProcessDebugInformation.DebugInfoClassMask constants
#define PDI_MODULES 0x01
#define PDI_BACKTRACE 0x02
#define PDI_HEAPS 0x04
#define PDI_HEAP_TAGS 0x08
#define PDI_HEAP_BLOCKS 0x10
#define PDI_LOCKS 0x20
extern "C" __declspec(dllimport) NTSTATUS __stdcall RtlQueryProcessDebugInformation( IN ULONG ProcessId, IN ULONG DebugInfoClassMask, IN OUT PDEBUG_BUFFER DebugBuffer);
extern "C" __declspec(dllimport) PDEBUG_BUFFER __stdcall RtlCreateQueryDebugBuffer( IN ULONG Size, IN BOOLEAN EventPair);
extern "C" __declspec(dllimport) NTSTATUS __stdcall RtlDestroyQueryDebugBuffer( IN PDEBUG_BUFFER DebugBuffer);
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
@ -106,8 +51,6 @@ namespace DFHack
sections = 0; sections = 0;
}; };
HANDLE my_handle; HANDLE my_handle;
vector <HANDLE> threads;
vector <HANDLE> stoppedthreads;
uint32_t my_pid; uint32_t my_pid;
IMAGE_NT_HEADERS pe_header; IMAGE_NT_HEADERS pe_header;
IMAGE_SECTION_HEADER * sections; IMAGE_SECTION_HEADER * sections;
@ -138,7 +81,7 @@ Process::Process(VersionInfoFactory * factory)
// read from this process // read from this process
try try
{ {
uint32_t pe_offset = Process::readDWord(d->base+0x3C); uint32_t pe_offset = readDWord(d->base+0x3C);
read(d->base + pe_offset, sizeof(d->pe_header), (uint8_t *)&(d->pe_header)); read(d->base + pe_offset, sizeof(d->pe_header), (uint8_t *)&(d->pe_header));
const size_t sectionsSize = sizeof(IMAGE_SECTION_HEADER) * d->pe_header.FileHeader.NumberOfSections; const size_t sectionsSize = sizeof(IMAGE_SECTION_HEADER) * d->pe_header.FileHeader.NumberOfSections;
d->sections = (IMAGE_SECTION_HEADER *) malloc(sectionsSize); d->sections = (IMAGE_SECTION_HEADER *) malloc(sectionsSize);
@ -151,24 +94,10 @@ Process::Process(VersionInfoFactory * factory)
VersionInfo* vinfo = factory->getVersionInfoByPETimestamp(d->pe_header.FileHeader.TimeDateStamp); VersionInfo* vinfo = factory->getVersionInfoByPETimestamp(d->pe_header.FileHeader.TimeDateStamp);
if(vinfo) if(vinfo)
{ {
vector<uint32_t> threads_ids;
if(!getThreadIDs( threads_ids ))
{
// thread enumeration failed.
return;
}
identified = true; identified = true;
// give the process a data model and memory layout fixed for the base of first module // give the process a data model and memory layout fixed for the base of first module
my_descriptor = new VersionInfo(*vinfo); my_descriptor = new VersionInfo(*vinfo);
my_descriptor->rebaseTo(getBase()); my_descriptor->rebaseTo(getBase());
for(size_t i = 0; i < threads_ids.size();i++)
{
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD) threads_ids[i]);
if(hThread)
d->threads.push_back(hThread);
else
cerr << "Unable to open thread :" << hex << (DWORD) threads_ids[i] << endl;
}
} }
} }
@ -176,41 +105,10 @@ Process::~Process()
{ {
// destroy our rebased copy of the memory descriptor // destroy our rebased copy of the memory descriptor
delete my_descriptor; delete my_descriptor;
for(size_t i = 0; i < d->threads.size(); i++)
CloseHandle(d->threads[i]);
if(d->sections != NULL) if(d->sections != NULL)
free(d->sections); free(d->sections);
} }
bool Process::getThreadIDs(vector<uint32_t> & threads )
{
HANDLE AllThreads = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
AllThreads = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
if( AllThreads == INVALID_HANDLE_VALUE )
{
return false;
}
te32.dwSize = sizeof(THREADENTRY32 );
if( !Thread32First( AllThreads, &te32 ) )
{
CloseHandle( AllThreads );
return false;
}
do
{
if( te32.th32OwnerProcessID == d->my_pid )
{
threads.push_back(te32.th32ThreadID);
}
} while( Thread32Next(AllThreads, &te32 ) );
CloseHandle( AllThreads );
return true;
}
/* /*
typedef struct _MEMORY_BASIC_INFORMATION typedef struct _MEMORY_BASIC_INFORMATION
{ {

@ -43,29 +43,6 @@ namespace DFHack
class VersionInfoFactory; class VersionInfoFactory;
class PlatformSpecific; class PlatformSpecific;
/**
* A type for storing an extended OS Process ID (combines PID and the time the process was started for unique identification)
* \ingroup grp_context
*/
struct ProcessID
{
ProcessID(const uint64_t _time, const uint64_t _pid): time(_time), pid(_pid){};
bool operator==(const ProcessID &other) const
{
return (other.time == time && other.pid == pid);
}
bool operator< (const ProcessID& ms) const
{
if (time < ms.time)
return true;
else if(time == ms.time)
return pid < ms.pid ;
return false;
}
uint64_t time;
uint64_t pid;
};
/** /**
* Structure describing a section of virtual memory inside a process * Structure describing a section of virtual memory inside a process
* \ingroup grp_context * \ingroup grp_context
@ -265,8 +242,6 @@ namespace DFHack
{ {
return identified; return identified;
}; };
/// find the thread IDs of the process
bool getThreadIDs(std::vector<uint32_t> & threads );
/// get virtual memory ranges of the process (what is mapped where) /// get virtual memory ranges of the process (what is mapped where)
void getMemRanges(std::vector<t_memrange> & ranges ); void getMemRanges(std::vector<t_memrange> & ranges );

@ -144,21 +144,6 @@ static int lua_Process_isIdentified(lua_State *S)
st.push(c->isIdentified()); st.push(c->isIdentified());
return 1; return 1;
} }
static int lua_Process_getThreadIDs(lua_State *S)
{
lua::state st(S);
DFHack::Process* c=GetProcessPtr(st);
std::vector<uint32_t> threads;
c->getThreadIDs(threads);
st.newtable();
for(size_t i=0;i<threads.size();i++)
{
st.push(i);
st.push(threads[i]);
st.settable();
}
return 1;
}
static int lua_Process_getMemRanges(lua_State *S) static int lua_Process_getMemRanges(lua_State *S)
{ {
lua::state st(S); lua::state st(S);
@ -261,7 +246,6 @@ const luaL_Reg lua_process_func[]=
PROC_FUNC(readCString ), PROC_FUNC(readCString ),
PROC_FUNC(isSuspended), PROC_FUNC(isSuspended),
PROC_FUNC(isIdentified), PROC_FUNC(isIdentified),
PROC_FUNC(getThreadIDs),
PROC_FUNC(getMemRanges), PROC_FUNC(getMemRanges),
PROC_FUNC(getBase), PROC_FUNC(getBase),
//PROC_FUNC(getPID), //not implemented //PROC_FUNC(getPID), //not implemented

@ -1,58 +1,39 @@
class AutoUnsuspend class AutoUnsuspend
attr_accessor :running
def initialize
end
def process def process
return false unless @running
joblist = df.world.job_list.next
count = 0 count = 0
df.world.job_list.each { |job|
while joblist if job.job_type == :ConstructBuilding and job.flags.suspend and df.map_tile_at(job).designation.flow_size <= 1
job = joblist.item
joblist = joblist.next
if job.job_type == :ConstructBuilding
if (job.flags.suspend)
item = job.items[0].item
job.flags.suspend = false job.flags.suspend = false
count += 1 count += 1
end end
}
if count > 0
puts "Unsuspended #{count} job(s)."
df.process_jobs = true
end end
end end
puts "Unsuspended #{count} job(s)." unless count == 0
end
def start def start
@onupdate = df.onupdate_register('autounsuspend', 5) { process }
@running = true @running = true
@onupdate = df.onupdate_register('autounsuspend', 5) { process if @running }
end end
def stop def stop
df.onupdate_unregister(@onupdate)
@running = false @running = false
df.onupdate_unregister(@onupdate)
end end
def status
@running ? 'Running.' : 'Stopped.'
end
end end
case $script_args[0] case $script_args[0]
when 'start' when 'start'
$AutoUnsuspend = AutoUnsuspend.new unless $AutoUnsuspend $AutoUnsuspend ||= AutoUnsuspend.new
$AutoUnsuspend.start $AutoUnsuspend.start
when 'end', 'stop' when 'end', 'stop'
$AutoUnsuspend.stop $AutoUnsuspend.stop
else else
if $AutoUnsuspend puts $AutoUnsuspend && $AutoUnsuspend.running ? 'Running.' : 'Stopped.'
puts $AutoUnsuspend.status
else
puts 'Not loaded.'
end
end end

@ -84,6 +84,7 @@ The special final argument 'magma' will make magma rain on the targets instead.
The special final argument 'butcher' will mark the targets for butchering instead. The special final argument 'butcher' will mark the targets for butchering instead.
Ex: exterminate gob Ex: exterminate gob
exterminate gob:male
exterminate elve magma exterminate elve magma
exterminate him exterminate him
exterminate pig butcher exterminate pig butcher
@ -115,6 +116,10 @@ when /^undead/i
puts "#{slain} #{count} undeads" puts "#{slain} #{count} undeads"
else else
if race.index(':')
race, caste = race.split(':')
end
raw_race = df.match_rawname(race, all_races.keys) raw_race = df.match_rawname(race, all_races.keys)
if not raw_race if not raw_race
puts "Invalid race, use one of #{all_races.keys.sort.join(' ')}" puts "Invalid race, use one of #{all_races.keys.sort.join(' ')}"
@ -123,13 +128,24 @@ else
race_nr = df.world.raws.creatures.all.index { |cr| cr.creature_id == raw_race } race_nr = df.world.raws.creatures.all.index { |cr| cr.creature_id == raw_race }
if caste
all_castes = df.world.raws.creatures.all[race_nr].caste.map { |c| c.caste_id }
raw_caste = df.match_rawname(caste, all_castes)
if not raw_caste
puts "Invalid caste, use one of #{all_castes.sort.join(' ')}"
throw :script_finished
end
caste_nr = all_castes.index(raw_caste)
end
count = 0 count = 0
df.world.units.active.each { |u| df.world.units.active.each { |u|
if u.race == race_nr and checkunit[u] if u.race == race_nr and checkunit[u]
next if caste_nr and u.caste != caste_nr
slayit[u] slayit[u]
count += 1 count += 1
end end
} }
puts "#{slain} #{count} #{raw_race}" puts "#{slain} #{count} #{raw_caste} #{raw_race}"
end end