diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 9f972e8d4..71f8f0d4a 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -79,13 +79,16 @@ CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/library/config.h.cmake ${CMAKE_SOURCE_DIR}/l IF(UNIX) add_definitions(-DLINUX_BUILD) add_definitions(-DUSE_CONFIG_H) + find_library(X11_LIBRARY X11) + find_library(XTEST_LIBRARY Xtst) + SET(PROJECT_LIBS ${X11_LIBRARY} ${XTEST_LIBRARY}) ELSE(UNIX) SET(PROJECT_LIBS psapi) ENDIF(UNIX) ADD_LIBRARY(dfhack SHARED ${PROJECT_SRCS}) -SET_TARGET_PROPERTIES( dfhack PROPERTIES DEBUG_POSTFIX "-debug" ) +SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" ) TARGET_LINK_LIBRARIES(dfhack ${PROJECT_LIBS}) diff --git a/library/DFCommonInternal.h b/library/DFCommonInternal.h index bbb002a7d..e3a5dea13 100644 --- a/library/DFCommonInternal.h +++ b/library/DFCommonInternal.h @@ -62,7 +62,11 @@ using namespace std; #include #include #include + #include //need for X11 functions + #include //need for Xtest + #include //for the atom stuff #else + #define _WIN32_WINNT 0x0500 // needed for INPUT struct #define WINVER 0x0500 // OpenThread(), PSAPI, Toolhelp32 #define WIN32_LEAN_AND_MEAN #include diff --git a/library/DFHackAPI.cpp b/library/DFHackAPI.cpp index ca9ef141c..93f56c707 100644 --- a/library/DFHackAPI.cpp +++ b/library/DFHackAPI.cpp @@ -50,6 +50,9 @@ class API::Private uint32_t window_z_offset; uint32_t cursor_xyz_offset; uint32_t window_dims_offset; + uint32_t current_cursor_creature_offset; + uint32_t pause_state_offset; + uint32_t view_screen_offset; uint32_t creature_pos_offset; uint32_t creature_type_offset; @@ -103,9 +106,9 @@ class API::Private DfVector *p_bld; DfVector *p_veg; - DfVector *p_trans; - DfVector *p_generic; - DfVector *p_dwarf_names; +// DfVector *p_trans; +// DfVector *p_generic; +// DfVector *p_dwarf_names; DfVector *p_itm; /* @@ -271,6 +274,919 @@ bool API::WriteTileTypes(uint32_t x, uint32_t y, uint32_t z, uint16_t *buffer) return false; } +bool API::getCurrentCursorCreatures(vector &addresses) +{ + assert(d->cursorWindowInited); + DfVector creUnderCursor = d->dm->readVector(d->current_cursor_creature_offset,4); + if(creUnderCursor.getSize() == 0){ + return false; + } + addresses.clear(); + for(int i =0;i= Success) && (win_name) && strcmp(win_name,searchString) == 0){ + return rootWindow; + } + + level++; + + status = XQueryTree (display, rootWindow, &rootWindow, &parent, &children, &noOfChildren); + + if (status == 0) + { + return BadWindow; + } + + if (noOfChildren == 0) + { + return BadWindow; + } + for (i=0; i < noOfChildren; i++) + { + Window tempWindow = EnumerateWindows (display, children[i], searchString); + if(tempWindow != BadWindow){ + retWindow = tempWindow; + break; + } + } + + XFree ((char*) children); + return retWindow; +} +// END ENUMERATE WINDOWS +Window getDFWindow(Display *dpy){ + // int numScreeens = ScreenCount(dpy); + for(int i = 0;i < ScreenCount(dpy);i++){ + Window currWin = RootWindow(dpy, i); + Window retWindow = EnumerateWindows(dpy,currWin,"Dwarf Fortress"); + if(retWindow != BadWindow){ + return (retWindow); + } + //I would ideally like to find the dfwindow using the PID, but X11 Windows only know their processes pid if the _NET_WM_PID attribute is set, which it is not for SDL 1.2. Supposedly SDL 1.3 will set this, but who knows when that will occur. + } + return(BadWindow); +} +bool setWMClientLeaderProperty(Display *dpy, Window &dfWin, Window ¤tFocus) +{ + static bool propertySet; + if(propertySet) + { + return true; + } + Atom leaderAtom; + Atom type; + int format, status; + unsigned long nitems = 0; + unsigned long extra = 0; + unsigned char *data = 0; + Window result = 0; + leaderAtom = XInternAtom(dpy, "WM_CLIENT_LEADER", false); + status = XGetWindowProperty(dpy,currentFocus,leaderAtom,0L,1L,false,XA_WINDOW,&type,&format,&nitems,&extra,(unsigned char **) &data); + if(status == Success) + { + if(data != 0) + { + result = *((Window*) data); + XFree(data); + } + else + { + Window curr = currentFocus; + while(data == 0){ + Window parent; + Window root; + Window *children; + uint numChildren; + XQueryTree(dpy,curr, &root,&parent,&children,&numChildren); + XGetWindowProperty(dpy,parent,leaderAtom,0L,1L,false,XA_WINDOW,&type,&format,&nitems,&extra,(unsigned char **) &data); + } + result = *((Window*) data); + XFree(data); + } + } + XChangeProperty(dpy,dfWin,leaderAtom,XA_WINDOW,32,PropModeReplace, (const unsigned char *)&result,1); + propertySet = true; + return true; +} +void API::TypeStr(const char *lpszString,int delay,bool useShift) +{ + ForceResume(); + Display *dpy = XOpenDisplay(NULL); // null opens the display in $DISPLAY + Window dfWin = getDFWindow(dpy); + if(dfWin != BadWindow) + { + + XWindowAttributes currAttr; + Window currentFocus; + int currentRevert; + XGetInputFocus(dpy, ¤tFocus,¤tRevert); //get current focus + setWMClientLeaderProperty(dpy,dfWin,currentFocus); + XGetWindowAttributes(dpy,dfWin,&currAttr); + if(currAttr.map_state == IsUnmapped){ + XMapRaised(dpy,dfWin); + } + if(currAttr.map_state == IsUnviewable){ + XRaiseWindow(dpy,dfWin); + } + XSync(dpy,false); + XSetInputFocus(dpy,dfWin,RevertToNone,CurrentTime); + XSync(dpy,false); + + char cChar; + KeyCode xkeycode; + char prevKey = 0; + int sleepAmnt = 0; + while((cChar=*lpszString++)) // loops through chars + { + xkeycode = XKeysymToKeycode(dpy,cChar); + //HACK add an extra shift up event, this fixes the problem of the same character twice in a row being displayed in df + XTestFakeKeyEvent(dpy,XKeysymToKeycode(dpy,XStringToKeysym("Shift_L")),false,CurrentTime); + if(useShift || cChar >= 'A' && cChar <= 'Z'){ + XTestFakeKeyEvent(dpy,XKeysymToKeycode(dpy,XStringToKeysym("Shift_L")),true,CurrentTime); + XSync(dpy,false); + } + XTestFakeKeyEvent(dpy,xkeycode,true,CurrentTime); + XSync(dpy,false); + XTestFakeKeyEvent(dpy,xkeycode,false,CurrentTime); + XSync(dpy,false); + if(useShift || cChar >= 'A' && cChar <= 'Z'){ + XTestFakeKeyEvent(dpy,XKeysymToKeycode(dpy,XStringToKeysym("Shift_L")),false,CurrentTime); + XSync(dpy,false); + } + + } + if(currAttr.map_state == IsUnmapped){ + // XUnmapWindow(dpy,dfWin); if I unmap the window, it is no longer on the task bar, so just lower it instead + XLowerWindow(dpy,dfWin); + } + XSetInputFocus(dpy,currentFocus,currentRevert,CurrentTime); + XSync(dpy,true); + } + usleep(delay*1000); +} + +void API::TypeSpecial(t_special command,int count,int delay) +{ + ForceResume(); + if(command != WAIT){ + KeySym mykeysym; + KeyCode xkeycode; + Display *dpy = XOpenDisplay(NULL); // null opens the display in $DISPLAY + Window dfWin = getDFWindow(dpy); + if(dfWin != BadWindow) + { + XWindowAttributes currAttr; + Window currentFocus; + int currentRevert; + XGetInputFocus(dpy, ¤tFocus,¤tRevert); //get current focus + setWMClientLeaderProperty(dpy,dfWin,currentFocus); + XGetWindowAttributes(dpy,dfWin,&currAttr); + if(currAttr.map_state == IsUnmapped){ + XMapRaised(dpy,dfWin); + } + if(currAttr.map_state == IsUnviewable){ + XRaiseWindow(dpy,dfWin); + } + XSync(dpy,false); + XSetInputFocus(dpy,dfWin,RevertToParent,CurrentTime); + + for(int i =0;ipid){ + ((window *) lParam)->windowHandle = hwnd; + return FALSE; + } + return TRUE; +} +void API::TypeStr(const char *lpszString,int delay, bool useShift) +{ + //Resume(); + ForceResume(); + + //sendmessage needs a window handle HWND, so have to get that from the process HANDLE + HWND currentWindow = GetForegroundWindow(); + window myWindow; + myWindow.pid = GetProcessId(DFHack::g_ProcessHandle); + EnumWindows(EnumWindowsProc, (LPARAM) &myWindow); + + HWND nextWindow =GetWindow(myWindow.windowHandle,GW_HWNDNEXT); + + SetActiveWindow(myWindow.windowHandle); + SetForegroundWindow(myWindow.windowHandle); + + char cChar; + + while((cChar=*lpszString++)) // loops through chars + { + short vk=VkKeyScan(cChar); // keycode of char + if(useShift || (vk>>8)&1){ // char is capital, so need to hold down shift + //shift down + INPUT input[4] = {0}; + input[0].type = INPUT_KEYBOARD; + input[0].ki.wVk = VK_SHIFT; + + input[1].type = INPUT_KEYBOARD; + input[1].ki.wVk = vk; + + input[2].type = INPUT_KEYBOARD; + input[2].ki.wVk = vk; + input[2].ki.dwFlags = KEYEVENTF_KEYUP; + + // shift up + input[3].type = INPUT_KEYBOARD; + input[3].ki.wVk = VK_SHIFT; + input[3].ki.dwFlags = KEYEVENTF_KEYUP; + + SendInput(4, input, sizeof(input[0])); + } + else{ + INPUT input[2] = {0}; + input[0].type = INPUT_KEYBOARD; + input[0].ki.wVk = vk; + + input[1].type = INPUT_KEYBOARD; + input[1].ki.wVk = vk; + input[1].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(2, input, sizeof(input[0])); + } + } + SetForegroundWindow(currentWindow); + SetActiveWindow(currentWindow); + SetWindowPos(myWindow.windowHandle,nextWindow,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); + Sleep(delay); + +} +void API::TypeSpecial(t_special command,int count,int delay) +{ + ForceResume(); + if(command != WAIT) + { + HWND currentWindow = GetForegroundWindow(); + window myWindow; + myWindow.pid = GetProcessId(DFHack::g_ProcessHandle); + EnumWindows(EnumWindowsProc, (LPARAM) &myWindow); + + HWND nextWindow =GetWindow(myWindow.windowHandle,GW_HWNDNEXT); + SetForegroundWindow(myWindow.windowHandle); + SetActiveWindow(myWindow.windowHandle); + INPUT shift; + shift.type = INPUT_KEYBOARD; + shift.ki.wVk = VK_SHIFT; + shift.ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(1,&shift,sizeof(shift)); + INPUT input[2] = {0}; + input[0].type = INPUT_KEYBOARD; + input[1].type = INPUT_KEYBOARD; + input[1].ki.dwFlags = KEYEVENTF_KEYUP; + switch(command) + { + case ENTER: + input[0].ki.wVk = VK_RETURN; + input[1].ki.wVk = VK_RETURN; + break; + case SPACE: + input[0].ki.wVk = VK_SPACE; + input[1].ki.wVk = VK_SPACE; + break; + case BACK_SPACE: + input[0].ki.wVk = VK_BACK; + input[1].ki.wVk = VK_BACK; + break; + case TAB: + input[0].ki.wVk = VK_TAB; + input[1].ki.wVk = VK_TAB; + break; + case CAPS_LOCK: + input[0].ki.wVk = VK_CAPITAL; + input[1].ki.wVk = VK_CAPITAL; + break; + // These are only for pressing the modifier key itself, you can't do key combinations with them, like ctrl+C + case LEFT_SHIFT: // I am not positive that this will work to distinguish the left and right.. + input[0].ki.wVk = VK_LSHIFT; + input[1].ki.wVk = VK_LSHIFT; + break; + case RIGHT_SHIFT: + input[0].ki.wVk = VK_RSHIFT; + input[1].ki.wVk = VK_RSHIFT; + break; + case LEFT_CONTROL: + input[0].ki.wVk = VK_LCONTROL; + input[1].ki.wVk = VK_LCONTROL; + break; + case RIGHT_CONTROL: + input[0].ki.wVk = VK_RCONTROL; + input[1].ki.wVk = VK_RCONTROL; + break; + case ALT: + input[0].ki.wVk = VK_MENU; + input[1].ki.wVk = VK_MENU; + break; + case ESCAPE: + input[0].ki.wVk = VK_ESCAPE; + input[1].ki.wVk = VK_ESCAPE; + break; + case UP: + input[0].ki.wVk = VK_UP; + input[1].ki.wVk = VK_UP; + break; + case DOWN: + input[0].ki.wVk = VK_DOWN; + input[1].ki.wVk = VK_DOWN; + break; + case LEFT: + input[0].ki.wVk = VK_LEFT; + input[1].ki.wVk = VK_LEFT; + break; + case RIGHT: + input[0].ki.wVk = VK_RIGHT; + input[1].ki.wVk = VK_RIGHT; + break; + case F1: + input[0].ki.wVk = VK_F1; + input[1].ki.wVk = VK_F1; + break; + case F2: + input[0].ki.wVk = VK_F2; + input[1].ki.wVk = VK_F2; + break; + case F3: + input[0].ki.wVk = VK_F3; + input[1].ki.wVk = VK_F3; + break; + case F4: + input[0].ki.wVk = VK_F4; + input[1].ki.wVk = VK_F4; + break; + case F5: + input[0].ki.wVk = VK_F5; + input[1].ki.wVk = VK_F5; + break; + case F6: + input[0].ki.wVk = VK_F6; + input[1].ki.wVk = VK_F6; + break; + case F7: + input[0].ki.wVk = VK_F7; + input[1].ki.wVk = VK_F7; + break; + case F8: + input[0].ki.wVk = VK_F8; + input[1].ki.wVk = VK_F8; + break; + case F9: + input[0].ki.wVk = VK_F9; + input[1].ki.wVk = VK_F9; + break; + case F10: + input[0].ki.wVk = VK_F10; + input[1].ki.wVk = VK_F10; + break; + case F11: + input[0].ki.wVk = VK_F11; + input[1].ki.wVk = VK_F11; + break; + case F12: + input[0].ki.wVk = VK_F12; + input[1].ki.wVk = VK_F12; + break; + case PAGE_UP: + input[0].ki.wVk = VK_PRIOR; + input[1].ki.wVk = VK_PRIOR; + break; + case PAGE_DOWN: + input[0].ki.wVk = VK_NEXT; + input[1].ki.wVk = VK_NEXT; + break; + case INSERT: + input[0].ki.wVk = VK_INSERT; + input[1].ki.wVk = VK_INSERT; + break; + case KEY_DELETE: + input[0].ki.wVk = VK_DELETE; + input[1].ki.wVk = VK_DELETE; + break; + case HOME: + input[0].ki.wVk = VK_HOME; + input[1].ki.wVk = VK_HOME; + break; + case END: + input[0].ki.wVk = VK_END; + input[1].ki.wVk = VK_END; + break; + case KEYPAD_DIVIDE: + input[0].ki.wVk = VK_DIVIDE; + input[1].ki.wVk = VK_DIVIDE; + break; + case KEYPAD_MULTIPLY: + input[0].ki.wVk = VK_MULTIPLY; + input[1].ki.wVk = VK_MULTIPLY; + break; + case KEYPAD_SUBTRACT: + input[0].ki.wVk = VK_SUBTRACT; + input[1].ki.wVk = VK_SUBTRACT; + break; + case KEYPAD_ADD: + input[0].ki.wVk = VK_ADD; + input[1].ki.wVk = VK_ADD; + break; + case KEYPAD_ENTER: + input[0].ki.wVk = VK_RETURN; + input[1].ki.wVk = VK_RETURN; + break; + case KEYPAD_0: + input[0].ki.wVk = VK_NUMPAD0; + input[1].ki.wVk = VK_NUMPAD0; + break; + case KEYPAD_1: + input[0].ki.wVk = VK_NUMPAD1; + input[1].ki.wVk = VK_NUMPAD1; + break; + case KEYPAD_2: + input[0].ki.wVk = VK_NUMPAD2; + input[1].ki.wVk = VK_NUMPAD2; + break; + case KEYPAD_3: + input[0].ki.wVk = VK_NUMPAD3; + input[1].ki.wVk = VK_NUMPAD3; + break; + case KEYPAD_4: + input[0].ki.wVk = VK_NUMPAD4; + input[1].ki.wVk = VK_NUMPAD4; + break; + case KEYPAD_5: + input[0].ki.wVk = VK_NUMPAD5; + input[1].ki.wVk = VK_NUMPAD5; + break; + case KEYPAD_6: + input[0].ki.wVk = VK_NUMPAD6; + input[1].ki.wVk = VK_NUMPAD6; + break; + case KEYPAD_7: + input[0].ki.wVk = VK_NUMPAD7; + input[1].ki.wVk = VK_NUMPAD7; + break; + case KEYPAD_8: + input[0].ki.wVk = VK_NUMPAD8; + input[1].ki.wVk = VK_NUMPAD8; + break; + case KEYPAD_9: + input[0].ki.wVk = VK_NUMPAD9; + input[1].ki.wVk = VK_NUMPAD9; + break; + case KEYPAD_DECIMAL_POINT: + input[0].ki.wVk = VK_SEPARATOR; + input[1].ki.wVk = VK_SEPARATOR; + break; + } + for(int i = 0; icreaturesInited); // read pointer from vector at position uint32_t temp = *(uint32_t *) d->p_cre->at(index); + furball.origin = temp; //read creature from memory Mread(temp + d->creature_pos_offset, 3 * sizeof(uint16_t), (uint8_t *) &(furball.x)); // xyz really MreadDWord(temp + d->creature_type_offset, furball.type); @@ -1054,42 +1970,80 @@ bool API::ReadCreature(const int32_t &index, t_creature & furball) } //FIXME: this just isn't enough -void API::InitReadNameTables() +void API::InitReadNameTables(map< string, vector > & nameTable) { int genericAddress = d->offset_descriptor->getAddress("language_vector"); int transAddress = d->offset_descriptor->getAddress("translation_vector"); int word_table_offset = d->offset_descriptor->getOffset("word_table"); - d->p_generic = new DfVector(d->dm->readVector(genericAddress, 4)); - d->p_trans = new DfVector(d->dm->readVector(transAddress, 4)); + DfVector genericVec(d->dm->readVector(genericAddress, 4)); + DfVector transVec(d->dm->readVector(transAddress, 4)); uint32_t dwarf_entry = 0; + + for(int32_t i =0;i < genericVec.getSize();i++){ + uint32_t genericNamePtr = *(uint32_t *) genericVec.at(i); + string genericName = d->dm->readSTLString(genericNamePtr); + nameTable["GENERIC"].push_back(genericName); + } - for(uint32_t i = 0; i < d->p_trans->getSize();i++) + for(uint32_t i = 0; i < transVec.getSize();i++) { -/* uint32_t namePtr; - d->p_trans->read(i,(uint8_t *)&namePtr);*/ - uint32_t namePtr = *(uint32_t *) d->p_trans->at(i); - - string raceName = d->dm->readSTLString(namePtr); - - if(raceName == "DWARF") - { - dwarf_entry = namePtr; + uint32_t transPtr = *(uint32_t *) transVec.at(i); + string transName = d->dm->readSTLString(transPtr); + DfVector trans_names_vec(d->dm->readVector(transPtr+word_table_offset,4)); + for(uint32_t j =0;jdm->readSTLString(transNamePtr); + nameTable[transName].push_back(name); } } - - d->dwarf_lang_table_offset = dwarf_entry + word_table_offset; - d->p_dwarf_names = new DfVector(d->dm->readVector(d->dwarf_lang_table_offset,4)); d->nameTablesInited = true; } +string API::TranslateName(const t_lastname & last, const map > & nameTable,const string & language) +{ + string trans_last; + assert(d->nameTablesInited); + map >::const_iterator it; + it=nameTable.find(language); + if(it != nameTable.end()){ + for(int i =0;i<7;i++){ + if(last.names[i] == -1){ + break; + } + trans_last.append(it->second[last.names[i]]); + } + } + return(trans_last); +} +string API::TranslateName(const t_squadname & squad, const map > & nameTable,const string & language) +{ + string trans_squad; + assert(d->nameTablesInited); + map >::const_iterator it; + it=nameTable.find(language); + if(it != nameTable.end()){ + for(int i =0;i<7;i++){ + if(squad.names[i] == 0xFFFFFFFF) + { + continue; + } + if(squad.names[i] == 0) + { + break; + } + if(i ==4){ + trans_squad.append(" "); + } + trans_squad.append(it->second[squad.names[i]]); + } + } + return(trans_squad); +} + void API::FinishReadNameTables() { - delete d->p_trans; - delete d->p_generic; - delete d->p_dwarf_names; - d->p_trans=d->p_generic=d->p_dwarf_names=NULL; - d->nameTablesInited=false; + d->nameTablesInited=false; } void API::FinishReadCreatures() @@ -1176,11 +2130,14 @@ void API::WriteRaw (const uint32_t &offset, const uint32_t &size, uint8_t *sourc bool API::InitViewAndCursor() { - d->window_x_offset = d->offset_descriptor->getAddress("window_x"); d->window_y_offset = d->offset_descriptor->getAddress("window_y"); d->window_z_offset = d->offset_descriptor->getAddress("window_z"); d->cursor_xyz_offset = d->offset_descriptor->getAddress("cursor_xyz"); + d->current_cursor_creature_offset = d->offset_descriptor->getAddress("current_cursor_creature"); + + d->pause_state_offset = d->offset_descriptor->getAddress("pause_state"); + d->view_screen_offset = d->offset_descriptor->getAddress("view_screen"); if(d->window_x_offset && d->window_y_offset && d->window_z_offset) { @@ -1275,6 +2232,8 @@ uint32_t API::InitReadItems() { int items = d->offset_descriptor->getAddress("items"); assert(items); + + cerr << hex << items; d->item_material_offset = d->offset_descriptor->getOffset("item_materials"); assert(d->item_material_offset); @@ -1319,4 +2278,26 @@ void API::FinishReadItems() delete d->p_itm; d->p_itm = NULL; d->itemsInited = false; -} \ No newline at end of file +} + +bool API::ReadPauseState() +{ + assert(d->cursorWindowInited); + + uint32_t pauseState = MreadDWord(d->pause_state_offset); + return(pauseState); +} + +bool API::ReadViewScreen(t_viewscreen &screen) +{ + assert(d->cursorWindowInited); + uint32_t last = MreadDWord(d->view_screen_offset); + uint32_t screenAddr = MreadDWord(last); + uint32_t nextScreenPtr = MreadDWord(last+4); + while(nextScreenPtr != 0){ + last = nextScreenPtr; + screenAddr = MreadDWord(nextScreenPtr); + nextScreenPtr = MreadDWord(nextScreenPtr+4); + } + return d->offset_descriptor->resolveClassId(last, screen.type); +} diff --git a/library/DFHackAPI.h b/library/DFHackAPI.h index 538ecfc3d..dc6d03e0e 100644 --- a/library/DFHackAPI.h +++ b/library/DFHackAPI.h @@ -28,6 +28,7 @@ distribution. #include "Export.h" #include #include +#include #include "integers.h" #include "DFTileTypes.h" @@ -45,6 +46,14 @@ namespace DFHack bool Attach(); bool Detach(); bool isAttached(); + + void TypeStr(const char *lpszString,int delay = 0,bool useShift = false); //Capitals are shifted automatically, other keys !@# ect need to have useShift set for them + void TypeSpecial(t_special command,int count=1,int delay = 0); + + bool ReadPauseState(); //true if paused, false if not + + bool ReadViewScreen(t_viewscreen &); + // stop DF from executing bool Suspend(); @@ -163,6 +172,8 @@ namespace DFHack bool getCursorCoords (int32_t &x, int32_t &y, int32_t &z); bool setCursorCoords (const int32_t &x, const int32_t &y, const int32_t &z); + bool getCurrentCursorCreatures(vector &addresses); // This returns false if there is nothing under the cursor, it puts the addresses in a the vector if there is + bool InitViewSize(); bool getWindowSize(int32_t & width, int32_t & height); bool setWindowSize(const int32_t & width, const int32_t & height); @@ -177,10 +188,12 @@ namespace DFHack vector getTraits(const uint32_t &index); vector getLabors(const uint32_t &index); */ - - void InitReadNameTables(); + void InitReadNameTables(map< string, vector< string > > & nameTable); void FinishReadNameTables(); + string TranslateName(const t_lastname & last, const map< string, vector< string > > &nameTable,const string & language="GENERIC"); + string TranslateName(const t_squadname & squad, const map< string, vector< string > > &nameTable,const string & language="GENERIC"); + uint32_t InitReadItems(); bool ReadItem(const uint32_t &index, t_item & item); void FinishReadItems(); diff --git a/library/DFTypes.h b/library/DFTypes.h index ccea57383..e37be19a6 100644 --- a/library/DFTypes.h +++ b/library/DFTypes.h @@ -478,6 +478,7 @@ struct t_job #define NUM_CREATURE_LABORS 102 struct t_creature { + uint32_t origin; uint16_t x; uint16_t y; uint16_t z; @@ -641,4 +642,63 @@ union t_occupancy } unibits; }; +enum t_special +{ + ENTER, + SPACE, + BACK_SPACE, + TAB, + CAPS_LOCK, + LEFT_SHIFT, + RIGHT_SHIFT, + LEFT_CONTROL, + RIGHT_CONTROL, + ALT, + WAIT, + ESCAPE, + UP, + DOWN, + LEFT, + RIGHT, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + PAGE_UP, + PAGE_DOWN, + INSERT, + KEY_DELETE, // DELETE is already defined in winnt.h, so can't redefine it here + HOME, + END, + KEYPAD_DIVIDE, + KEYPAD_MULTIPLY, + KEYPAD_SUBTRACT, + KEYPAD_ADD, + KEYPAD_ENTER, + KEYPAD_0, + KEYPAD_1, + KEYPAD_2, + KEYPAD_3, + KEYPAD_4, + KEYPAD_5, + KEYPAD_6, + KEYPAD_7, + KEYPAD_8, + KEYPAD_9, + KEYPAD_DECIMAL_POINT +}; + +struct t_viewscreen +{ + int32_t type; + //There is more info in these objects, but I don't know what it is yet +}; #endif // TYPES_H_INCLUDED diff --git a/output/Memory.xml b/output/Memory.xml index ea3977c38..a6e8207ad 100644 --- a/output/Memory.xml +++ b/output/Memory.xml @@ -924,8 +924,11 @@ 59ab29021aca9f3c66b1ab102fb3ceea -
15BDF50
+
0x01757F38
+
0x15BDF50
0x68 +
0x13DC2EB
+
0x013EF970
@@ -1016,6 +1019,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .-"""-. @@ -1297,6 +1371,173 @@ 022b933926e08da49c6df8649295f2b7 +
0x91ab420
+
0x8F5A2EC
+ 0x50 +
0x8F35800
+
0x878493c
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 09e7ca212..e44511dcc 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -57,6 +57,10 @@ TARGET_LINK_LIBRARIES(dfsuspend dfhack) ADD_EXECUTABLE(dfitemdump dfitemdump.cpp) TARGET_LINK_LIBRARIES(dfitemdump dfhack) +# customCtreatureNameProf - change the custom names and professions of creatures, sends keys to df directly +ADD_EXECUTABLE(dfcustomCreatureNameProf customCreatureNameProf.cpp) +TARGET_LINK_LIBRARIES(dfcustomCreatureNameProf dfhack) + IF(UNIX) install(TARGETS dfexpbench diff --git a/tools/creaturedump.cpp b/tools/creaturedump.cpp index 50cd24ee8..7e37175a3 100644 --- a/tools/creaturedump.cpp +++ b/tools/creaturedump.cpp @@ -41,12 +41,14 @@ int main (void) return 1; } + map > names; + DF.InitReadNameTables(names); uint32_t numCreatures = DF.InitReadCreatures(); for(uint32_t i = 0; i < numCreatures; i++) { t_creature temp; DF.ReadCreature(i, temp); - cout << "creature type: " << creaturestypes[temp.type].id << ", position: " << temp.x << "x " << temp.y << "y "<< temp.z << "z" << endl; + cout << "address: " << temp.origin << " creature type: " << creaturestypes[temp.type].id << ", position: " << temp.x << "x " << temp.y << "y "<< temp.z << "z" << endl; bool addendl = false; if(temp.first_name[0]) { @@ -58,6 +60,12 @@ int main (void) cout << ", nick name: " << temp.nick_name; addendl = true; } + string transName = DF.TranslateName(temp.last_name,names,creaturestypes[temp.type].id); + if(!transName.empty()){ + cout << ", trans name: " << transName; + addendl=true; + } + //cout << ", generic name: " << DF.TranslateName(temp.last_name,names,"GENERIC"); /* if(!temp.trans_name.empty()){ cout << ", trans name: " << temp.trans_name; diff --git a/tools/customCreatureNameProf.cpp b/tools/customCreatureNameProf.cpp new file mode 100644 index 000000000..bff652e2c --- /dev/null +++ b/tools/customCreatureNameProf.cpp @@ -0,0 +1,312 @@ +// Creature dump + +#include +#include +#include +#include +#include +using namespace std; + +#include +#include +#include + +template +void print_bits ( T val, std::ostream& out ) +{ + T n_bits = sizeof ( val ) * CHAR_BIT; + + for ( unsigned i = 0; i < n_bits; ++i ) { + out<< !!( val & 1 ) << " "; + val >>= 1; + } +} +vector buildingtypes; +map > names; +uint32_t numCreatures; +vector creaturestypes; +void printDwarves(DFHack::API & DF) +{ + int dwarfCounter = 0; + for(uint32_t i = 0; i < numCreatures; i++) + { + t_creature temp; + DF.ReadCreature(i, temp); + string type = creaturestypes[temp.type].id; + if(type == "DWARF" && !temp.flags1.bits.dead && !temp.flags2.bits.killed){ + cout << i << ":"; + if(temp.nick_name[0]) + { + cout << temp.nick_name; + } + else{ + cout << temp.first_name; + } + string transName = DF.TranslateName(temp.last_name,names,creaturestypes[temp.type].id); + cout << " " << temp.custom_profession; //transName; + if(dwarfCounter%3 != 2){ + cout << '\t'; + } + else{ + cout << endl; + } + dwarfCounter++; + } + } +} + +bool getDwarfSelection(DFHack::API & DF, t_creature & toChange,string & changeString, string & commandString,int & eraseAmount,int &dwarfNum,bool &isName) +{ + DF.ForceResume(); + static string lastText; + bool dwarfSuccess = false; + + while(!dwarfSuccess) + { + string input; + cout << "\nSelect Dwarf to Change or q to Quit" << endl; + getline (cin, input); + if(input == "q") + { + return false; + } + else if(input == "r") + { + DF.Suspend(); + printDwarves(DF); + DF.Resume(); + } + else if(!input.empty()) + { + int num; + stringstream(input) >> num;//= atol(input.c_str()); + DF.Suspend(); + dwarfSuccess = DF.ReadCreature(num,toChange); + DF.Resume(); + string type = creaturestypes[toChange.type].id; + if(type != "DWARF"){ + dwarfSuccess = false; + } + else + { + dwarfNum = num; + } + } + } + bool changeType = false; + while(!changeType) + { + string input; + cout << "\n(n)ickname or (p)rofession?" << endl; + getline(cin, input); + if(input == "q"){ + return false; + } + if(input == "n"){ + commandString = "pzyn"; + eraseAmount = string(toChange.nick_name).length(); + changeType = true; + isName = true; + } + else if(input == "p"){ + commandString = "pzyp"; + eraseAmount = string(toChange.custom_profession).length(); + changeType = true; + isName = false; + } + } + bool changeValue = false; + while(!changeValue) + { + string input; + cout << "value to change to?" << endl; + getline(cin, input); + if(input == "q"){ + return false; + } + if(!lastText.empty() && input.empty()){ + changeValue = true; + } + else if( !input.empty()){ + lastText = input; + changeValue = true; + } + } + changeString = lastText; + return true; +} +bool waitTillChanged(DFHack::API &DF, int creatureToCheck, string changeValue, bool isName) +{ + DF.Suspend(); + t_creature testCre; + DF.ReadCreature(creatureToCheck,testCre); + int tryCount = 0; + if(isName) + { + while(testCre.nick_name != changeValue && tryCount <50) + { + DF.TypeSpecial(WAIT,1,100); + DF.Suspend(); + DF.ReadCreature(creatureToCheck,testCre); + tryCount++; + } + } + else + { + while(testCre.custom_profession != changeValue && tryCount < 50) + { + DF.TypeSpecial(WAIT,1,100); + DF.Suspend(); + DF.ReadCreature(creatureToCheck,testCre); + tryCount++; + } + } + if(tryCount >= 50){ + cerr << "Something went wrong, make sure that DF is at the correct screen"; + return false; + } + return true; +} +bool waitTillScreenState(DFHack::API &DF, string screenState) +{ + t_viewscreen current; + DF.Suspend(); + DF.ReadViewScreen(current); + int tryCount = 0; + while(buildingtypes[current.type] != screenState && tryCount < 50){ + DF.TypeSpecial(WAIT,1,100); + DF.Suspend(); + DF.ReadViewScreen(current); + tryCount++; + } + if(tryCount >= 50){ + cerr << "Something went wrong, DF at " << buildingtypes[current.type] << endl; + return false; + } + return true; +} + +bool waitTillCursorState(DFHack::API &DF, bool On) +{ + int32_t x,y,z; + int tryCount = 0; + DF.Suspend(); + bool cursorResult = DF.getCursorCoords(x,y,z); + while(tryCount < 50 && On && !cursorResult || !On && cursorResult) + { + DF.TypeSpecial(WAIT,1,100); + tryCount++; + DF.Suspend(); + cursorResult = DF.getCursorCoords(x,y,z); + } + if(tryCount >= 50) + { + cerr << "Something went wrong, cursor at x: " << x << " y: " << y << " z: " << z << endl; + return false; + } + return true; +} +int main (void) +{ + DFHack::API DF("Memory.xml"); + if(!DF.Attach()) + { + cerr << "DF not found" << endl; + return 1; + } + //Need buildingtypes for the viewscreen vtables, this really should not be just buildings, as viewscreen and items both use the same interface + uint32_t numBuildings = DF.InitReadBuildings(buildingtypes); + + DFHack::memory_info mem = DF.getMemoryInfo(); + // get stone matgloss mapping + if(!DF.ReadCreatureMatgloss(creaturestypes)) + { + cerr << "Can't get the creature types." << endl; + return 1; + } + + DF.InitReadNameTables(names); + numCreatures = DF.InitReadCreatures(); + DF.InitViewAndCursor(); + DF.Suspend(); + printDwarves(DF); + t_creature toChange; + string changeString,commandString; + int eraseAmount; + int toChangeNum; + bool isName; + while(getDwarfSelection(DF,toChange,changeString,commandString,eraseAmount,toChangeNum,isName)) + { + bool completed = false; + int32_t x,y,z; + DF.Suspend(); + DF.getCursorCoords(x,y,z); + if(x == -30000)// cursor not displayed + { + DF.TypeStr("v"); + } + if(waitTillCursorState(DF,true)) + { + DF.Suspend(); + DF.setCursorCoords(toChange.x, toChange.y,toChange.z); + vector underCursor; + while(!DF.getCurrentCursorCreatures(underCursor)) + { + DF.TypeSpecial(WAIT,1,100); + DF.Suspend(); + DF.setCursorCoords(toChange.x, toChange.y,toChange.z); + } + //CurrentCursorCreatures gives the creatures in the order that you see them with the 'k' cursor. + //The 'v' cursor displays them in the order of last, then first,second,third and so on + //Pretty weird, but it works + //The only place that seems to display which creature is currently selected is on the stack, whose location is likely not static, so not usable + if(underCursor[underCursor.size()-1] != toChange.origin) + { + for(int i = 0;i