Merge branch 'master' of https://github.com/danaris/dfhack
Conflicts: depends/clsocket package/darwin/dfhack package/darwin/dfhack-run Fixed.develop
commit
d5ae1fc4f2
@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
SDL - Simple DirectMedia Layer
|
||||||
|
Copyright (C) 1997-2009 Sam Lantinga
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Sam Lantinga
|
||||||
|
slouken@libsdl.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Fake - only structs. Shamelessly pilfered from the SDL library.
|
||||||
|
// Needed for processing its event types without polluting our namespaces with C garbage
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "SDL_keyboard.h"
|
||||||
|
|
||||||
|
namespace SDL
|
||||||
|
{
|
||||||
|
enum ButtonState
|
||||||
|
{
|
||||||
|
BTN_RELEASED = 0,
|
||||||
|
BTN_PRESSED = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Event enumerations */
|
||||||
|
enum EventType
|
||||||
|
{
|
||||||
|
ET_NOEVENT = 0, /**< Unused (do not remove) */
|
||||||
|
ET_ACTIVEEVENT, /**< Application loses/gains visibility */
|
||||||
|
ET_KEYDOWN, /**< Keys pressed */
|
||||||
|
ET_KEYUP, /**< Keys released */
|
||||||
|
ET_MOUSEMOTION, /**< Mouse moved */
|
||||||
|
ET_MOUSEBUTTONDOWN, /**< Mouse button pressed */
|
||||||
|
ET_MOUSEBUTTONUP, /**< Mouse button released */
|
||||||
|
ET_JOYAXISMOTION, /**< Joystick axis motion */
|
||||||
|
ET_JOYBALLMOTION, /**< Joystick trackball motion */
|
||||||
|
ET_JOYHATMOTION, /**< Joystick hat position change */
|
||||||
|
ET_JOYBUTTONDOWN, /**< Joystick button pressed */
|
||||||
|
ET_JOYBUTTONUP, /**< Joystick button released */
|
||||||
|
ET_QUIT, /**< User-requested quit */
|
||||||
|
ET_SYSWMEVENT, /**< System specific event */
|
||||||
|
ET_EVENT_RESERVEDA, /**< Reserved for future use.. */
|
||||||
|
ET_EVENT_RESERVEDB, /**< Reserved for future use.. */
|
||||||
|
ET_VIDEORESIZE, /**< User resized video mode */
|
||||||
|
ET_VIDEOEXPOSE, /**< Screen needs to be redrawn */
|
||||||
|
ET_EVENT_RESERVED2, /**< Reserved for future use.. */
|
||||||
|
ET_EVENT_RESERVED3, /**< Reserved for future use.. */
|
||||||
|
ET_EVENT_RESERVED4, /**< Reserved for future use.. */
|
||||||
|
ET_EVENT_RESERVED5, /**< Reserved for future use.. */
|
||||||
|
ET_EVENT_RESERVED6, /**< Reserved for future use.. */
|
||||||
|
ET_EVENT_RESERVED7, /**< Reserved for future use.. */
|
||||||
|
/** Events ET_USEREVENT through ET_MAXEVENTS-1 are for your use */
|
||||||
|
ET_USEREVENT = 24,
|
||||||
|
/** This last event is only for bounding internal arrays
|
||||||
|
* It is the number of bits in the event mask datatype -- Uint32
|
||||||
|
*/
|
||||||
|
ET_NUMEVENTS = 32
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Application visibility event structure */
|
||||||
|
struct ActiveEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_ACTIVEEVENT */
|
||||||
|
uint8_t gain; /**< Whether given states were gained or lost (1/0) */
|
||||||
|
uint8_t state; /**< A mask of the focus states */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Keyboard event structure */
|
||||||
|
struct KeyboardEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_KEYDOWN or ET_KEYUP */
|
||||||
|
uint8_t which; /**< The keyboard device index */
|
||||||
|
uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
|
||||||
|
keysym ksym;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Mouse motion event structure */
|
||||||
|
struct MouseMotionEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_MOUSEMOTION */
|
||||||
|
uint8_t which; /**< The mouse device index */
|
||||||
|
uint8_t state; /**< The current button state */
|
||||||
|
uint16_t x, y; /**< The X/Y coordinates of the mouse */
|
||||||
|
int16_t xrel; /**< The relative motion in the X direction */
|
||||||
|
int16_t yrel; /**< The relative motion in the Y direction */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Mouse button event structure */
|
||||||
|
struct MouseButtonEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_MOUSEBUTTONDOWN or ET_MOUSEBUTTONUP */
|
||||||
|
uint8_t which; /**< The mouse device index */
|
||||||
|
uint8_t button; /**< The mouse button index */
|
||||||
|
uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
|
||||||
|
uint16_t x, y; /**< The X/Y coordinates of the mouse at press time */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Joystick axis motion event structure */
|
||||||
|
struct JoyAxisEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_JOYAXISMOTION */
|
||||||
|
uint8_t which; /**< The joystick device index */
|
||||||
|
uint8_t axis; /**< The joystick axis index */
|
||||||
|
int16_t value; /**< The axis value (range: -32768 to 32767) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Joystick trackball motion event structure */
|
||||||
|
struct JoyBallEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_JOYBALLMOTION */
|
||||||
|
uint8_t which; /**< The joystick device index */
|
||||||
|
uint8_t ball; /**< The joystick trackball index */
|
||||||
|
int16_t xrel; /**< The relative motion in the X direction */
|
||||||
|
int16_t yrel; /**< The relative motion in the Y direction */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Joystick hat position change event structure */
|
||||||
|
struct JoyHatEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_JOYHATMOTION */
|
||||||
|
uint8_t which; /**< The joystick device index */
|
||||||
|
uint8_t hat; /**< The joystick hat index */
|
||||||
|
uint8_t value; /**< The hat position value:
|
||||||
|
* SDL_HAT_LEFTUP SDL_HAT_UP SDL_HAT_RIGHTUP
|
||||||
|
* SDL_HAT_LEFT SDL_HAT_CENTERED SDL_HAT_RIGHT
|
||||||
|
* SDL_HAT_LEFTDOWN SDL_HAT_DOWN SDL_HAT_RIGHTDOWN
|
||||||
|
* Note that zero means the POV is centered.
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Joystick button event structure */
|
||||||
|
struct JoyButtonEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_JOYBUTTONDOWN or ET_JOYBUTTONUP */
|
||||||
|
uint8_t which; /**< The joystick device index */
|
||||||
|
uint8_t button; /**< The joystick button index */
|
||||||
|
uint8_t state; /**< BTN_PRESSED or BTN_RELEASED */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The "window resized" event
|
||||||
|
* When you get this event, you are responsible for setting a new video
|
||||||
|
* mode with the new width and height.
|
||||||
|
*/
|
||||||
|
struct ResizeEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_VIDEORESIZE */
|
||||||
|
int w; /**< New width */
|
||||||
|
int h; /**< New height */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The "screen redraw" event */
|
||||||
|
struct ExposeEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_VIDEOEXPOSE */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The "quit requested" event */
|
||||||
|
struct QuitEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ET_QUIT */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A user-defined event type */
|
||||||
|
struct UserEvent
|
||||||
|
{
|
||||||
|
uint8_t type; /**< ETL_USEREVENT through ET_NUMEVENTS-1 */
|
||||||
|
int code; /**< User defined event code */
|
||||||
|
void *data1; /**< User defined data pointer */
|
||||||
|
void *data2; /**< User defined data pointer */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** If you want to use this event, you should include SDL_syswm.h */
|
||||||
|
struct SysWMmsg;
|
||||||
|
struct SysWMEvent
|
||||||
|
{
|
||||||
|
uint8_t type;
|
||||||
|
SysWMmsg *msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** General event structure */
|
||||||
|
union Event
|
||||||
|
{
|
||||||
|
uint8_t type;
|
||||||
|
ActiveEvent active;
|
||||||
|
KeyboardEvent key;
|
||||||
|
MouseMotionEvent motion;
|
||||||
|
MouseButtonEvent button;
|
||||||
|
JoyAxisEvent jaxis;
|
||||||
|
JoyBallEvent jball;
|
||||||
|
JoyHatEvent jhat;
|
||||||
|
JoyButtonEvent jbutton;
|
||||||
|
ResizeEvent resize;
|
||||||
|
ExposeEvent expose;
|
||||||
|
QuitEvent quit;
|
||||||
|
UserEvent user;
|
||||||
|
SysWMEvent syswm;
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
SDL - Simple DirectMedia Layer
|
||||||
|
Copyright (C) 1997-2009 Sam Lantinga
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Sam Lantinga
|
||||||
|
slouken@libsdl.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Fake - only structs. Shamelessly pilfered from the SDL library.
|
||||||
|
// Needed for processing its event types without polluting our namespaces with C garbage
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "SDL_keysym.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace SDL
|
||||||
|
{
|
||||||
|
/** Keysym structure
|
||||||
|
*
|
||||||
|
* - The scancode is hardware dependent, and should not be used by general
|
||||||
|
* applications. If no hardware scancode is available, it will be 0.
|
||||||
|
*
|
||||||
|
* - The 'unicode' translated character is only available when character
|
||||||
|
* translation is enabled by the SDL_EnableUNICODE() API. If non-zero,
|
||||||
|
* this is a UNICODE character corresponding to the keypress. If the
|
||||||
|
* high 9 bits of the character are 0, then this maps to the equivalent
|
||||||
|
* ASCII character:
|
||||||
|
* @code
|
||||||
|
* char ch;
|
||||||
|
* if ( (keysym.unicode & 0xFF80) == 0 ) {
|
||||||
|
* ch = keysym.unicode & 0x7F;
|
||||||
|
* } else {
|
||||||
|
* An international character..
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
typedef struct keysym
|
||||||
|
{
|
||||||
|
uint8_t scancode; /**< hardware specific scancode */
|
||||||
|
Key sym; /**< SDL virtual keysym */
|
||||||
|
Mod mod; /**< current key modifiers */
|
||||||
|
uint16_t unicode; /**< translated character */
|
||||||
|
} keysym;
|
||||||
|
|
||||||
|
/** This is the mask which refers to all hotkey bindings */
|
||||||
|
#define ALL_HOTKEYS 0xFFFFFFFF
|
||||||
|
}
|
@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
SDL - Simple DirectMedia Layer
|
||||||
|
Copyright (C) 1997-2009 Sam Lantinga
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Sam Lantinga
|
||||||
|
slouken@libsdl.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Fake - only structs. Shamelessly pilfered from the SDL library.
|
||||||
|
// Needed for processing its event types without polluting our namespaces with C garbage
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace SDL
|
||||||
|
{
|
||||||
|
/** What we really want is a mapping of every raw key on the keyboard.
|
||||||
|
* To support international keyboards, we use the range 0xA1 - 0xFF
|
||||||
|
* as international virtual keycodes. We'll follow in the footsteps of X11...
|
||||||
|
* @brief The names of the keys
|
||||||
|
*/
|
||||||
|
enum Key
|
||||||
|
{
|
||||||
|
/** @name ASCII mapped keysyms
|
||||||
|
* The keyboard syms have been cleverly chosen to map to ASCII
|
||||||
|
*/
|
||||||
|
/*@{*/
|
||||||
|
K_UNKNOWN = 0,
|
||||||
|
K_FIRST = 0,
|
||||||
|
K_BACKSPACE = 8,
|
||||||
|
K_TAB = 9,
|
||||||
|
K_CLEAR = 12,
|
||||||
|
K_RETURN = 13,
|
||||||
|
K_PAUSE = 19,
|
||||||
|
K_ESCAPE = 27,
|
||||||
|
K_SPACE = 32,
|
||||||
|
K_EXCLAIM = 33,
|
||||||
|
K_QUOTEDBL = 34,
|
||||||
|
K_HASH = 35,
|
||||||
|
K_DOLLAR = 36,
|
||||||
|
K_AMPERSAND = 38,
|
||||||
|
K_QUOTE = 39,
|
||||||
|
K_LEFTPAREN = 40,
|
||||||
|
K_RIGHTPAREN = 41,
|
||||||
|
K_ASTERISK = 42,
|
||||||
|
K_PLUS = 43,
|
||||||
|
K_COMMA = 44,
|
||||||
|
K_MINUS = 45,
|
||||||
|
K_PERIOD = 46,
|
||||||
|
K_SLASH = 47,
|
||||||
|
K_0 = 48,
|
||||||
|
K_1 = 49,
|
||||||
|
K_2 = 50,
|
||||||
|
K_3 = 51,
|
||||||
|
K_4 = 52,
|
||||||
|
K_5 = 53,
|
||||||
|
K_6 = 54,
|
||||||
|
K_7 = 55,
|
||||||
|
K_8 = 56,
|
||||||
|
K_9 = 57,
|
||||||
|
K_COLON = 58,
|
||||||
|
K_SEMICOLON = 59,
|
||||||
|
K_LESS = 60,
|
||||||
|
K_EQUALS = 61,
|
||||||
|
K_GREATER = 62,
|
||||||
|
K_QUESTION = 63,
|
||||||
|
K_AT = 64,
|
||||||
|
/*
|
||||||
|
Skip uppercase letters
|
||||||
|
*/
|
||||||
|
K_LEFTBRACKET = 91,
|
||||||
|
K_BACKSLASH = 92,
|
||||||
|
K_RIGHTBRACKET = 93,
|
||||||
|
K_CARET = 94,
|
||||||
|
K_UNDERSCORE = 95,
|
||||||
|
K_BACKQUOTE = 96,
|
||||||
|
K_a = 97,
|
||||||
|
K_b = 98,
|
||||||
|
K_c = 99,
|
||||||
|
K_d = 100,
|
||||||
|
K_e = 101,
|
||||||
|
K_f = 102,
|
||||||
|
K_g = 103,
|
||||||
|
K_h = 104,
|
||||||
|
K_i = 105,
|
||||||
|
K_j = 106,
|
||||||
|
K_k = 107,
|
||||||
|
K_l = 108,
|
||||||
|
K_m = 109,
|
||||||
|
K_n = 110,
|
||||||
|
K_o = 111,
|
||||||
|
K_p = 112,
|
||||||
|
K_q = 113,
|
||||||
|
K_r = 114,
|
||||||
|
K_s = 115,
|
||||||
|
K_t = 116,
|
||||||
|
K_u = 117,
|
||||||
|
K_v = 118,
|
||||||
|
K_w = 119,
|
||||||
|
K_x = 120,
|
||||||
|
K_y = 121,
|
||||||
|
K_z = 122,
|
||||||
|
K_DELETE = 127,
|
||||||
|
/* End of ASCII mapped keysyms */
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
/** @name International keyboard syms */
|
||||||
|
/*@{*/
|
||||||
|
K_WORLD_0 = 160, /* 0xA0 */
|
||||||
|
K_WORLD_1 = 161,
|
||||||
|
K_WORLD_2 = 162,
|
||||||
|
K_WORLD_3 = 163,
|
||||||
|
K_WORLD_4 = 164,
|
||||||
|
K_WORLD_5 = 165,
|
||||||
|
K_WORLD_6 = 166,
|
||||||
|
K_WORLD_7 = 167,
|
||||||
|
K_WORLD_8 = 168,
|
||||||
|
K_WORLD_9 = 169,
|
||||||
|
K_WORLD_10 = 170,
|
||||||
|
K_WORLD_11 = 171,
|
||||||
|
K_WORLD_12 = 172,
|
||||||
|
K_WORLD_13 = 173,
|
||||||
|
K_WORLD_14 = 174,
|
||||||
|
K_WORLD_15 = 175,
|
||||||
|
K_WORLD_16 = 176,
|
||||||
|
K_WORLD_17 = 177,
|
||||||
|
K_WORLD_18 = 178,
|
||||||
|
K_WORLD_19 = 179,
|
||||||
|
K_WORLD_20 = 180,
|
||||||
|
K_WORLD_21 = 181,
|
||||||
|
K_WORLD_22 = 182,
|
||||||
|
K_WORLD_23 = 183,
|
||||||
|
K_WORLD_24 = 184,
|
||||||
|
K_WORLD_25 = 185,
|
||||||
|
K_WORLD_26 = 186,
|
||||||
|
K_WORLD_27 = 187,
|
||||||
|
K_WORLD_28 = 188,
|
||||||
|
K_WORLD_29 = 189,
|
||||||
|
K_WORLD_30 = 190,
|
||||||
|
K_WORLD_31 = 191,
|
||||||
|
K_WORLD_32 = 192,
|
||||||
|
K_WORLD_33 = 193,
|
||||||
|
K_WORLD_34 = 194,
|
||||||
|
K_WORLD_35 = 195,
|
||||||
|
K_WORLD_36 = 196,
|
||||||
|
K_WORLD_37 = 197,
|
||||||
|
K_WORLD_38 = 198,
|
||||||
|
K_WORLD_39 = 199,
|
||||||
|
K_WORLD_40 = 200,
|
||||||
|
K_WORLD_41 = 201,
|
||||||
|
K_WORLD_42 = 202,
|
||||||
|
K_WORLD_43 = 203,
|
||||||
|
K_WORLD_44 = 204,
|
||||||
|
K_WORLD_45 = 205,
|
||||||
|
K_WORLD_46 = 206,
|
||||||
|
K_WORLD_47 = 207,
|
||||||
|
K_WORLD_48 = 208,
|
||||||
|
K_WORLD_49 = 209,
|
||||||
|
K_WORLD_50 = 210,
|
||||||
|
K_WORLD_51 = 211,
|
||||||
|
K_WORLD_52 = 212,
|
||||||
|
K_WORLD_53 = 213,
|
||||||
|
K_WORLD_54 = 214,
|
||||||
|
K_WORLD_55 = 215,
|
||||||
|
K_WORLD_56 = 216,
|
||||||
|
K_WORLD_57 = 217,
|
||||||
|
K_WORLD_58 = 218,
|
||||||
|
K_WORLD_59 = 219,
|
||||||
|
K_WORLD_60 = 220,
|
||||||
|
K_WORLD_61 = 221,
|
||||||
|
K_WORLD_62 = 222,
|
||||||
|
K_WORLD_63 = 223,
|
||||||
|
K_WORLD_64 = 224,
|
||||||
|
K_WORLD_65 = 225,
|
||||||
|
K_WORLD_66 = 226,
|
||||||
|
K_WORLD_67 = 227,
|
||||||
|
K_WORLD_68 = 228,
|
||||||
|
K_WORLD_69 = 229,
|
||||||
|
K_WORLD_70 = 230,
|
||||||
|
K_WORLD_71 = 231,
|
||||||
|
K_WORLD_72 = 232,
|
||||||
|
K_WORLD_73 = 233,
|
||||||
|
K_WORLD_74 = 234,
|
||||||
|
K_WORLD_75 = 235,
|
||||||
|
K_WORLD_76 = 236,
|
||||||
|
K_WORLD_77 = 237,
|
||||||
|
K_WORLD_78 = 238,
|
||||||
|
K_WORLD_79 = 239,
|
||||||
|
K_WORLD_80 = 240,
|
||||||
|
K_WORLD_81 = 241,
|
||||||
|
K_WORLD_82 = 242,
|
||||||
|
K_WORLD_83 = 243,
|
||||||
|
K_WORLD_84 = 244,
|
||||||
|
K_WORLD_85 = 245,
|
||||||
|
K_WORLD_86 = 246,
|
||||||
|
K_WORLD_87 = 247,
|
||||||
|
K_WORLD_88 = 248,
|
||||||
|
K_WORLD_89 = 249,
|
||||||
|
K_WORLD_90 = 250,
|
||||||
|
K_WORLD_91 = 251,
|
||||||
|
K_WORLD_92 = 252,
|
||||||
|
K_WORLD_93 = 253,
|
||||||
|
K_WORLD_94 = 254,
|
||||||
|
K_WORLD_95 = 255, /* 0xFF */
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
/** @name Numeric keypad */
|
||||||
|
/*@{*/
|
||||||
|
K_KP0 = 256,
|
||||||
|
K_KP1 = 257,
|
||||||
|
K_KP2 = 258,
|
||||||
|
K_KP3 = 259,
|
||||||
|
K_KP4 = 260,
|
||||||
|
K_KP5 = 261,
|
||||||
|
K_KP6 = 262,
|
||||||
|
K_KP7 = 263,
|
||||||
|
K_KP8 = 264,
|
||||||
|
K_KP9 = 265,
|
||||||
|
K_KP_PERIOD = 266,
|
||||||
|
K_KP_DIVIDE = 267,
|
||||||
|
K_KP_MULTIPLY = 268,
|
||||||
|
K_KP_MINUS = 269,
|
||||||
|
K_KP_PLUS = 270,
|
||||||
|
K_KP_ENTER = 271,
|
||||||
|
K_KP_EQUALS = 272,
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
/** @name Arrows + Home/End pad */
|
||||||
|
/*@{*/
|
||||||
|
K_UP = 273,
|
||||||
|
K_DOWN = 274,
|
||||||
|
K_RIGHT = 275,
|
||||||
|
K_LEFT = 276,
|
||||||
|
K_INSERT = 277,
|
||||||
|
K_HOME = 278,
|
||||||
|
K_END = 279,
|
||||||
|
K_PAGEUP = 280,
|
||||||
|
K_PAGEDOWN = 281,
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
/** @name Function keys */
|
||||||
|
/*@{*/
|
||||||
|
K_F1 = 282,
|
||||||
|
K_F2 = 283,
|
||||||
|
K_F3 = 284,
|
||||||
|
K_F4 = 285,
|
||||||
|
K_F5 = 286,
|
||||||
|
K_F6 = 287,
|
||||||
|
K_F7 = 288,
|
||||||
|
K_F8 = 289,
|
||||||
|
K_F9 = 290,
|
||||||
|
K_F10 = 291,
|
||||||
|
K_F11 = 292,
|
||||||
|
K_F12 = 293,
|
||||||
|
K_F13 = 294,
|
||||||
|
K_F14 = 295,
|
||||||
|
K_F15 = 296,
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
/** @name Key state modifier keys */
|
||||||
|
/*@{*/
|
||||||
|
K_NUMLOCK = 300,
|
||||||
|
K_CAPSLOCK = 301,
|
||||||
|
K_SCROLLOCK = 302,
|
||||||
|
K_RSHIFT = 303,
|
||||||
|
K_LSHIFT = 304,
|
||||||
|
K_RCTRL = 305,
|
||||||
|
K_LCTRL = 306,
|
||||||
|
K_RALT = 307,
|
||||||
|
K_LALT = 308,
|
||||||
|
K_RMETA = 309,
|
||||||
|
K_LMETA = 310,
|
||||||
|
K_LSUPER = 311, /**< Left "Windows" key */
|
||||||
|
K_RSUPER = 312, /**< Right "Windows" key */
|
||||||
|
K_MODE = 313, /**< "Alt Gr" key */
|
||||||
|
K_COMPOSE = 314, /**< Multi-key compose key */
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
/** @name Miscellaneous function keys */
|
||||||
|
/*@{*/
|
||||||
|
K_HELP = 315,
|
||||||
|
K_PRINT = 316,
|
||||||
|
K_SYSREQ = 317,
|
||||||
|
K_BREAK = 318,
|
||||||
|
K_MENU = 319,
|
||||||
|
K_POWER = 320, /**< Power Macintosh power key */
|
||||||
|
K_EURO = 321, /**< Some european keyboards */
|
||||||
|
K_UNDO = 322, /**< Atari keyboard has Undo */
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
/* Add any other keys here */
|
||||||
|
|
||||||
|
K_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Enumeration of valid key mods (possibly OR'd together) */
|
||||||
|
enum Mod {
|
||||||
|
KMOD_NONE = 0x0000,
|
||||||
|
KMOD_LSHIFT= 0x0001,
|
||||||
|
KMOD_RSHIFT= 0x0002,
|
||||||
|
KMOD_LCTRL = 0x0040,
|
||||||
|
KMOD_RCTRL = 0x0080,
|
||||||
|
KMOD_LALT = 0x0100,
|
||||||
|
KMOD_RALT = 0x0200,
|
||||||
|
KMOD_LMETA = 0x0400,
|
||||||
|
KMOD_RMETA = 0x0800,
|
||||||
|
KMOD_NUM = 0x1000,
|
||||||
|
KMOD_CAPS = 0x2000,
|
||||||
|
KMOD_MODE = 0x4000,
|
||||||
|
KMOD_RESERVED = 0x8000,
|
||||||
|
KMOD_CTRL = (KMOD_LCTRL|KMOD_RCTRL),
|
||||||
|
KMOD_SHIFT = (KMOD_LSHIFT|KMOD_RSHIFT),
|
||||||
|
KMOD_ALT = (KMOD_LALT|KMOD_RALT),
|
||||||
|
KMOD_META = (KMOD_LMETA|KMOD_RMETA)
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,455 @@
|
|||||||
|
-- Utilities for offset scan scripts.
|
||||||
|
|
||||||
|
local _ENV = mkmodule('memscan')
|
||||||
|
|
||||||
|
local utils = require('utils')
|
||||||
|
|
||||||
|
-- A length-checked view on a memory buffer
|
||||||
|
|
||||||
|
CheckedArray = CheckedArray or {}
|
||||||
|
|
||||||
|
function CheckedArray.new(type,saddr,eaddr)
|
||||||
|
local data = df.reinterpret_cast(type,saddr)
|
||||||
|
local esize = data:sizeof()
|
||||||
|
local count = math.floor((eaddr-saddr)/esize)
|
||||||
|
local obj = {
|
||||||
|
type = type, start = saddr, size = count*esize,
|
||||||
|
esize = esize, data = data, count = count
|
||||||
|
}
|
||||||
|
setmetatable(obj, CheckedArray)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
function CheckedArray:__len()
|
||||||
|
return self.count
|
||||||
|
end
|
||||||
|
function CheckedArray:__index(idx)
|
||||||
|
if type(idx) == number then
|
||||||
|
if idx >= self.count then
|
||||||
|
error('Index out of bounds: '..tostring(idx))
|
||||||
|
end
|
||||||
|
return self.data[idx]
|
||||||
|
else
|
||||||
|
return CheckedArray[idx]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function CheckedArray:__newindex(idx, val)
|
||||||
|
if idx >= self.count then
|
||||||
|
error('Index out of bounds: '..tostring(idx))
|
||||||
|
end
|
||||||
|
self.data[idx] = val
|
||||||
|
end
|
||||||
|
function CheckedArray:addr2idx(addr, round)
|
||||||
|
local off = addr - self.start
|
||||||
|
if off >= 0 and off < self.size and (round or (off % self.esize) == 0) then
|
||||||
|
return math.floor(off / self.esize), off
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function CheckedArray:idx2addr(idx)
|
||||||
|
if idx >= 0 and idx < self.count then
|
||||||
|
return self.start + idx*self.esize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Search methods
|
||||||
|
|
||||||
|
function CheckedArray:find(data,sidx,eidx,reverse)
|
||||||
|
local dcnt = #data
|
||||||
|
sidx = math.max(0, sidx or 0)
|
||||||
|
eidx = math.min(self.count, eidx or self.count)
|
||||||
|
if (eidx - sidx) >= dcnt and dcnt > 0 then
|
||||||
|
return dfhack.with_temp_object(
|
||||||
|
df.new(self.type, dcnt),
|
||||||
|
function(buffer)
|
||||||
|
for i = 1,dcnt do
|
||||||
|
buffer[i-1] = data[i]
|
||||||
|
end
|
||||||
|
local cnt = eidx - sidx - dcnt
|
||||||
|
local step = self.esize
|
||||||
|
local sptr = self.start + sidx*step
|
||||||
|
local ksize = dcnt*step
|
||||||
|
if reverse then
|
||||||
|
local idx, addr = dfhack.internal.memscan(sptr + cnt*step, cnt, -step, buffer, ksize)
|
||||||
|
if idx then
|
||||||
|
return sidx + cnt - idx, addr
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local idx, addr = dfhack.internal.memscan(sptr, cnt, step, buffer, ksize)
|
||||||
|
if idx then
|
||||||
|
return sidx + idx, addr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function CheckedArray:find_one(data,sidx,eidx,reverse)
|
||||||
|
local idx, addr = self:find(data,sidx,eidx,reverse)
|
||||||
|
if idx then
|
||||||
|
-- Verify this is the only match
|
||||||
|
if reverse then
|
||||||
|
eidx = idx+#data-1
|
||||||
|
else
|
||||||
|
sidx = idx+1
|
||||||
|
end
|
||||||
|
if self:find(data,sidx,eidx,reverse) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return idx, addr
|
||||||
|
end
|
||||||
|
function CheckedArray:list_changes(old_arr,old_val,new_val,delta)
|
||||||
|
if old_arr.type ~= self.type or old_arr.count ~= self.count then
|
||||||
|
error('Incompatible arrays')
|
||||||
|
end
|
||||||
|
local eidx = self.count
|
||||||
|
local optr = old_arr.start
|
||||||
|
local nptr = self.start
|
||||||
|
local esize = self.esize
|
||||||
|
local rv
|
||||||
|
local sidx = 0
|
||||||
|
while true do
|
||||||
|
local idx = dfhack.internal.diffscan(optr, nptr, sidx, eidx, esize, old_val, new_val, delta)
|
||||||
|
if not idx then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
rv = rv or {}
|
||||||
|
rv[#rv+1] = idx
|
||||||
|
sidx = idx+1
|
||||||
|
end
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
function CheckedArray:filter_changes(prev_list,old_arr,old_val,new_val,delta)
|
||||||
|
if old_arr.type ~= self.type or old_arr.count ~= self.count then
|
||||||
|
error('Incompatible arrays')
|
||||||
|
end
|
||||||
|
local eidx = self.count
|
||||||
|
local optr = old_arr.start
|
||||||
|
local nptr = self.start
|
||||||
|
local esize = self.esize
|
||||||
|
local rv
|
||||||
|
for i=1,#prev_list do
|
||||||
|
local idx = prev_list[i]
|
||||||
|
if idx < 0 or idx >= eidx then
|
||||||
|
error('Index out of bounds: '..idx)
|
||||||
|
end
|
||||||
|
if dfhack.internal.diffscan(optr, nptr, idx, idx+1, esize, old_val, new_val, delta) then
|
||||||
|
rv = rv or {}
|
||||||
|
rv[#rv+1] = idx
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
-- A raw memory area class
|
||||||
|
|
||||||
|
MemoryArea = MemoryArea or {}
|
||||||
|
MemoryArea.__index = MemoryArea
|
||||||
|
|
||||||
|
function MemoryArea.new(astart, aend)
|
||||||
|
local obj = {
|
||||||
|
start_addr = astart, end_addr = aend, size = aend - astart,
|
||||||
|
int8_t = CheckedArray.new('int8_t',astart,aend),
|
||||||
|
uint8_t = CheckedArray.new('uint8_t',astart,aend),
|
||||||
|
int16_t = CheckedArray.new('int16_t',astart,aend),
|
||||||
|
uint16_t = CheckedArray.new('uint16_t',astart,aend),
|
||||||
|
int32_t = CheckedArray.new('int32_t',astart,aend),
|
||||||
|
uint32_t = CheckedArray.new('uint32_t',astart,aend)
|
||||||
|
}
|
||||||
|
setmetatable(obj, MemoryArea)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
function MemoryArea:__gc()
|
||||||
|
df.delete(self.buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MemoryArea:__tostring()
|
||||||
|
return string.format('<MemoryArea: %x..%x>', self.start_addr, self.end_addr)
|
||||||
|
end
|
||||||
|
function MemoryArea:contains_range(start,size)
|
||||||
|
return start >= self.start_addr and (start+size) <= self.end_addr
|
||||||
|
end
|
||||||
|
function MemoryArea:contains_obj(obj,count)
|
||||||
|
local size, base = df.sizeof(obj)
|
||||||
|
return size and base and self:contains_range(base, size*(count or 1))
|
||||||
|
end
|
||||||
|
|
||||||
|
function MemoryArea:clone()
|
||||||
|
local buffer = df.new('int8_t', self.size)
|
||||||
|
local _, base = buffer:sizeof()
|
||||||
|
local rv = MemoryArea.new(base, base+self.size)
|
||||||
|
rv.buffer = buffer
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
function MemoryArea:copy_from(area2)
|
||||||
|
if area2.size ~= self.size then
|
||||||
|
error('Size mismatch')
|
||||||
|
end
|
||||||
|
dfhack.internal.memmove(self.start_addr, area2.start_addr, self.size)
|
||||||
|
end
|
||||||
|
function MemoryArea:delete()
|
||||||
|
setmetatable(self, nil)
|
||||||
|
df.delete(self.buffer)
|
||||||
|
for k,v in pairs(self) do self[k] = nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Static data segment search
|
||||||
|
|
||||||
|
local function find_data_segment()
|
||||||
|
local data_start, data_end
|
||||||
|
|
||||||
|
for i,mem in ipairs(dfhack.internal.getMemRanges()) do
|
||||||
|
if data_end then
|
||||||
|
if mem.start_addr == data_end and mem.read and mem.write then
|
||||||
|
data_end = mem.end_addr
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
elseif mem.read and mem.write
|
||||||
|
and (string.match(mem.name,'/dwarfort%.exe$')
|
||||||
|
or string.match(mem.name,'/Dwarf_Fortress$'))
|
||||||
|
then
|
||||||
|
data_start = mem.start_addr
|
||||||
|
data_end = mem.end_addr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return data_start, data_end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_data_segment()
|
||||||
|
local s, e = find_data_segment()
|
||||||
|
if s and e then
|
||||||
|
return MemoryArea.new(s, e)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Register a found offset, or report an error.
|
||||||
|
|
||||||
|
function found_offset(name,val)
|
||||||
|
local cval = dfhack.internal.getAddress(name)
|
||||||
|
|
||||||
|
if not val then
|
||||||
|
print('Could not find offset '..name)
|
||||||
|
if not cval and not utils.prompt_yes_no('Continue with the script?') then
|
||||||
|
error('User quit')
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if df.isvalid(val) then
|
||||||
|
_,val = val:sizeof()
|
||||||
|
end
|
||||||
|
|
||||||
|
print(string.format('Found offset %s: %x', name, val))
|
||||||
|
|
||||||
|
if cval then
|
||||||
|
if cval ~= val then
|
||||||
|
error(string.format('Mismatch with the current value: %x',val))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
dfhack.internal.setAddress(name, val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Offset of a field within struct
|
||||||
|
|
||||||
|
function field_ref(handle,...)
|
||||||
|
local items = table.pack(...)
|
||||||
|
for i=1,items.n-1 do
|
||||||
|
handle = handle[items[i]]
|
||||||
|
end
|
||||||
|
return handle:_field(items[items.n])
|
||||||
|
end
|
||||||
|
|
||||||
|
function field_offset(type,...)
|
||||||
|
local handle = df.reinterpret_cast(type,1)
|
||||||
|
local _,addr = df.sizeof(field_ref(handle,...))
|
||||||
|
return addr-1
|
||||||
|
end
|
||||||
|
|
||||||
|
function MemoryArea:object_by_field(addr,type,...)
|
||||||
|
if not addr then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local base = addr - field_offset(type,...)
|
||||||
|
local obj = df.reinterpret_cast(type, base)
|
||||||
|
if not self:contains_obj(obj) then
|
||||||
|
obj = nil
|
||||||
|
end
|
||||||
|
return obj, base
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Validation
|
||||||
|
|
||||||
|
function is_valid_vector(ref,size)
|
||||||
|
local ints = df.reinterpret_cast('uint32_t', ref)
|
||||||
|
return ints[0] <= ints[1] and ints[1] <= ints[2]
|
||||||
|
and (size == nil or (ints[1] - ints[0]) % size == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Difference search helper
|
||||||
|
|
||||||
|
DiffSearcher = DiffSearcher or {}
|
||||||
|
DiffSearcher.__index = DiffSearcher
|
||||||
|
|
||||||
|
function DiffSearcher.new(area)
|
||||||
|
local obj = { area = area }
|
||||||
|
setmetatable(obj, DiffSearcher)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
function DiffSearcher:begin_search(type)
|
||||||
|
self.type = type
|
||||||
|
self.old_value = nil
|
||||||
|
self.search_sets = nil
|
||||||
|
if not self.save_area then
|
||||||
|
self.save_area = self.area:clone()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function DiffSearcher:advance_search(new_value,delta)
|
||||||
|
if self.search_sets then
|
||||||
|
local nsets = #self.search_sets
|
||||||
|
local ovec = self.save_area[self.type]
|
||||||
|
local nvec = self.area[self.type]
|
||||||
|
local new_set
|
||||||
|
if nsets > 0 then
|
||||||
|
local last_set = self.search_sets[nsets]
|
||||||
|
new_set = nvec:filter_changes(last_set,ovec,self.old_value,new_value,delta)
|
||||||
|
else
|
||||||
|
new_set = nvec:list_changes(ovec,self.old_value,new_value,delta)
|
||||||
|
end
|
||||||
|
if new_set then
|
||||||
|
self.search_sets[nsets+1] = new_set
|
||||||
|
self.old_value = new_value
|
||||||
|
self.save_area:copy_from(self.area)
|
||||||
|
return #new_set, new_set
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.old_value = new_value
|
||||||
|
self.search_sets = {}
|
||||||
|
self.save_area:copy_from(self.area)
|
||||||
|
return #self.save_area[self.type], nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function DiffSearcher:reset()
|
||||||
|
self.search_sets = nil
|
||||||
|
if self.save_area then
|
||||||
|
self.save_area:delete()
|
||||||
|
self.save_area = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function DiffSearcher:idx2addr(idx)
|
||||||
|
return self.area[self.type]:idx2addr(idx)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Interactive search utility
|
||||||
|
|
||||||
|
function DiffSearcher:find_interactive(prompt,data_type,condition_cb)
|
||||||
|
enum = enum or {}
|
||||||
|
|
||||||
|
-- Loop for restarting search from scratch
|
||||||
|
while true do
|
||||||
|
print('\n'..prompt)
|
||||||
|
|
||||||
|
self:begin_search(data_type)
|
||||||
|
|
||||||
|
local found = false
|
||||||
|
local ccursor = 0
|
||||||
|
|
||||||
|
-- Loop through choices
|
||||||
|
while true do
|
||||||
|
print('')
|
||||||
|
|
||||||
|
local ok, value, delta = condition_cb(ccursor)
|
||||||
|
|
||||||
|
ccursor = ccursor + 1
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Search for it in the memory
|
||||||
|
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 = self:idx2addr(set[1])
|
||||||
|
print(string.format(' Confirmed address: %x\n', addr))
|
||||||
|
return addr, set[1]
|
||||||
|
else
|
||||||
|
found = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print(' '..cnt..' candidates remaining.')
|
||||||
|
end
|
||||||
|
|
||||||
|
if not utils.prompt_yes_no('\nRetry search from the start?') then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
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
|
@ -1,29 +1,33 @@
|
|||||||
find_package(Ruby)
|
OPTION(DL_RUBY "download libruby from the internet" ON)
|
||||||
if(RUBY_FOUND)
|
IF (DL_RUBY)
|
||||||
ADD_CUSTOM_COMMAND(
|
IF (UNIX)
|
||||||
OUTPUT ruby-autogen.cpp
|
FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/libruby187.tar.gz
|
||||||
COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.cpp
|
EXPECTED_MD5 eb2adea59911f68e6066966c1352f291)
|
||||||
DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl
|
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf libruby187.tar.gz
|
||||||
)
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
ADD_EXECUTABLE(ruby-autogen ruby-autogen.cpp)
|
FILE(RENAME libruby1.8.so.1.8.7 libruby.so)
|
||||||
if(CMAKE_COMPILER_IS_GNUCC)
|
SET(RUBYLIB libruby.so)
|
||||||
set_target_properties (ruby-autogen PROPERTIES COMPILE_FLAGS "-Wno-invalid-offsetof")
|
ELSE (UNIX)
|
||||||
endif(CMAKE_COMPILER_IS_GNUCC)
|
FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/msvcrtruby187.tar.gz
|
||||||
ADD_CUSTOM_COMMAND(
|
EXPECTED_MD5 9f4a1659ac3a5308f32d3a1937bbeeae)
|
||||||
OUTPUT ruby-autogen.offsets
|
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf msvcrtruby187.tar.gz
|
||||||
COMMAND ruby-autogen ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
DEPENDS ruby-autogen
|
FILE(RENAME msvcrt-ruby18.dll libruby.dll)
|
||||||
)
|
SET(RUBYLIB libruby.dll)
|
||||||
ADD_CUSTOM_COMMAND(
|
ENDIF(UNIX)
|
||||||
OUTPUT ruby-autogen.rb
|
ENDIF(DL_RUBY)
|
||||||
COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets ${CMAKE_CURRENT_SOURCE_DIR}/ruby-memstruct.rb
|
|
||||||
DEPENDS ruby-autogen.offsets ruby-memstruct.rb
|
ADD_CUSTOM_COMMAND(
|
||||||
)
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb
|
||||||
ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb)
|
COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb
|
||||||
include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH})
|
DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl
|
||||||
DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread)
|
COMMENT ruby-autogen.rb
|
||||||
target_link_libraries(ruby ${RUBY_LIBRARY})
|
)
|
||||||
install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION})
|
ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb)
|
||||||
else(RUBY_FOUND)
|
|
||||||
MESSAGE(STATUS "Required library (ruby) not found - ruby plugin can't be built.")
|
INCLUDE_DIRECTORIES("${dfhack_SOURCE_DIR}/depends/tthread")
|
||||||
endif(RUBY_FOUND)
|
|
||||||
|
DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread)
|
||||||
|
ADD_DEPENDENCIES(ruby ruby-autogen-rb)
|
||||||
|
|
||||||
|
INSTALL(FILES ruby.rb ruby-autogen.rb ${RUBYLIB} DESTINATION ${DFHACK_LIBRARY_DESTINATION})
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,747 +0,0 @@
|
|||||||
module DFHack
|
|
||||||
module MemHack
|
|
||||||
INSPECT_SIZE_LIMIT=16384
|
|
||||||
class MemStruct
|
|
||||||
attr_accessor :_memaddr
|
|
||||||
def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end
|
|
||||||
def _get ; self ; end
|
|
||||||
def _cpp_init ; end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Compound < MemStruct
|
|
||||||
class << self
|
|
||||||
attr_accessor :_fields, :_rtti_classname, :_sizeof
|
|
||||||
def field(name, offset)
|
|
||||||
struct = yield
|
|
||||||
return if not struct
|
|
||||||
@_fields ||= []
|
|
||||||
@_fields << [name, offset, struct]
|
|
||||||
define_method(name) { struct._at(@_memaddr+offset)._get }
|
|
||||||
define_method("#{name}=") { |v| struct._at(@_memaddr+offset)._set(v) }
|
|
||||||
end
|
|
||||||
def _fields_ancestors
|
|
||||||
if superclass.respond_to?(:_fields_ancestors)
|
|
||||||
superclass._fields_ancestors + _fields.to_a
|
|
||||||
else
|
|
||||||
_fields.to_a
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def number(bits, signed, initvalue=nil, enum=nil)
|
|
||||||
Number.new(bits, signed, initvalue, enum)
|
|
||||||
end
|
|
||||||
def float
|
|
||||||
Float.new
|
|
||||||
end
|
|
||||||
def bit(shift)
|
|
||||||
BitField.new(shift, 1)
|
|
||||||
end
|
|
||||||
def bits(shift, len, enum=nil)
|
|
||||||
BitField.new(shift, len, enum)
|
|
||||||
end
|
|
||||||
def pointer
|
|
||||||
Pointer.new((yield if block_given?))
|
|
||||||
end
|
|
||||||
def pointer_ary(tglen)
|
|
||||||
PointerAry.new(tglen, yield)
|
|
||||||
end
|
|
||||||
def static_array(len, tglen, indexenum=nil)
|
|
||||||
StaticArray.new(tglen, len, indexenum, yield)
|
|
||||||
end
|
|
||||||
def static_string(len)
|
|
||||||
StaticString.new(len)
|
|
||||||
end
|
|
||||||
|
|
||||||
def stl_vector(tglen=nil)
|
|
||||||
tg = yield if tglen
|
|
||||||
case tglen
|
|
||||||
when 1; StlVector8.new(tg)
|
|
||||||
when 2; StlVector16.new(tg)
|
|
||||||
else StlVector32.new(tg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def stl_string
|
|
||||||
StlString.new
|
|
||||||
end
|
|
||||||
def stl_bit_vector
|
|
||||||
StlBitVector.new
|
|
||||||
end
|
|
||||||
def stl_deque(tglen)
|
|
||||||
StlDeque.new(tglen, yield)
|
|
||||||
end
|
|
||||||
|
|
||||||
def df_flagarray(indexenum=nil)
|
|
||||||
DfFlagarray.new(indexenum)
|
|
||||||
end
|
|
||||||
def df_array(tglen)
|
|
||||||
DfArray.new(tglen, yield)
|
|
||||||
end
|
|
||||||
def df_linked_list
|
|
||||||
DfLinkedList.new(yield)
|
|
||||||
end
|
|
||||||
|
|
||||||
def global(glob)
|
|
||||||
Global.new(glob)
|
|
||||||
end
|
|
||||||
def compound(name=nil, &b)
|
|
||||||
m = Class.new(Compound)
|
|
||||||
DFHack.const_set(name, m) if name
|
|
||||||
m.instance_eval(&b)
|
|
||||||
m.new
|
|
||||||
end
|
|
||||||
def rtti_classname(n)
|
|
||||||
DFHack.rtti_register(n, self)
|
|
||||||
@_rtti_classname = n
|
|
||||||
end
|
|
||||||
def sizeof(n)
|
|
||||||
@_sizeof = n
|
|
||||||
end
|
|
||||||
|
|
||||||
# allocate a new c++ object, return its ruby wrapper
|
|
||||||
def cpp_new
|
|
||||||
ptr = DFHack.malloc(_sizeof)
|
|
||||||
if _rtti_classname and vt = DFHack.rtti_getvtable(_rtti_classname)
|
|
||||||
DFHack.memory_write_int32(ptr, vt)
|
|
||||||
# TODO call constructor
|
|
||||||
end
|
|
||||||
o = new._at(ptr)
|
|
||||||
o._cpp_init
|
|
||||||
o
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def _cpp_init
|
|
||||||
_fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init }
|
|
||||||
end
|
|
||||||
def _set(h)
|
|
||||||
case h
|
|
||||||
when Hash; h.each { |k, v| send("_#{k}=", v) }
|
|
||||||
when Array; names = _field_names ; raise 'bad size' if names.length != h.length ; names.zip(h).each { |n, a| send("#{n}=", a) }
|
|
||||||
when Compound; _field_names.each { |n| send("#{n}=", h.send(n)) }
|
|
||||||
else raise 'wut?'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def _fields ; self.class._fields.to_a ; end
|
|
||||||
def _fields_ancestors ; self.class._fields_ancestors.to_a ; end
|
|
||||||
def _field_names ; _fields_ancestors.map { |n, o, s| n } ; end
|
|
||||||
def _rtti_classname ; self.class._rtti_classname ; end
|
|
||||||
def _sizeof ; self.class._sizeof ; end
|
|
||||||
@@inspecting = {} # avoid infinite recursion on mutually-referenced objects
|
|
||||||
def inspect
|
|
||||||
cn = self.class.name.sub(/^DFHack::/, '')
|
|
||||||
cn << ' @' << ('0x%X' % _memaddr) if cn != ''
|
|
||||||
out = "#<#{cn}"
|
|
||||||
return out << ' ...>' if @@inspecting[_memaddr]
|
|
||||||
@@inspecting[_memaddr] = true
|
|
||||||
_fields_ancestors.each { |n, o, s|
|
|
||||||
out << ' ' if out.length != 0 and out[-1, 1] != ' '
|
|
||||||
if out.length > INSPECT_SIZE_LIMIT
|
|
||||||
out << '...'
|
|
||||||
break
|
|
||||||
end
|
|
||||||
out << inspect_field(n, o, s)
|
|
||||||
}
|
|
||||||
out.chomp!(' ')
|
|
||||||
@@inspecting.delete _memaddr
|
|
||||||
out << '>'
|
|
||||||
end
|
|
||||||
def inspect_field(n, o, s)
|
|
||||||
if s.kind_of?(BitField) and s._len == 1
|
|
||||||
send(n) ? n.to_s : ''
|
|
||||||
elsif s.kind_of?(Pointer)
|
|
||||||
"#{n}=#{s._at(@_memaddr+o).inspect}"
|
|
||||||
elsif n == :_whole
|
|
||||||
"_whole=0x#{_whole.to_s(16)}"
|
|
||||||
else
|
|
||||||
v = send(n).inspect
|
|
||||||
"#{n}=#{v}"
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
"#{n}=ERR(#{$!})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Enum
|
|
||||||
# number -> symbol
|
|
||||||
def self.enum
|
|
||||||
# ruby weirdness, needed to make the constants 'virtual'
|
|
||||||
@enum ||= const_get(:ENUM)
|
|
||||||
end
|
|
||||||
# symbol -> number
|
|
||||||
def self.nume
|
|
||||||
@nume ||= const_get(:NUME)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.to_i(i)
|
|
||||||
nume[i] || i
|
|
||||||
end
|
|
||||||
def self.to_sym(i)
|
|
||||||
enum[i] || i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Number < MemStruct
|
|
||||||
attr_accessor :_bits, :_signed, :_initvalue, :_enum
|
|
||||||
def initialize(bits, signed, initvalue, enum)
|
|
||||||
@_bits = bits
|
|
||||||
@_signed = signed
|
|
||||||
@_initvalue = initvalue
|
|
||||||
@_enum = enum
|
|
||||||
end
|
|
||||||
|
|
||||||
def _get
|
|
||||||
v = case @_bits
|
|
||||||
when 32; DFHack.memory_read_int32(@_memaddr)
|
|
||||||
when 16; DFHack.memory_read_int16(@_memaddr)
|
|
||||||
when 8; DFHack.memory_read_int8( @_memaddr)
|
|
||||||
when 64;(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + (DFHack.memory_read_int32(@_memaddr+4) << 32)
|
|
||||||
end
|
|
||||||
v &= (1 << @_bits) - 1 if not @_signed
|
|
||||||
v = @_enum.to_sym(v) if @_enum
|
|
||||||
v
|
|
||||||
end
|
|
||||||
|
|
||||||
def _set(v)
|
|
||||||
v = @_enum.to_i(v) if @_enum
|
|
||||||
case @_bits
|
|
||||||
when 32; DFHack.memory_write_int32(@_memaddr, v)
|
|
||||||
when 16; DFHack.memory_write_int16(@_memaddr, v)
|
|
||||||
when 8; DFHack.memory_write_int8( @_memaddr, v)
|
|
||||||
when 64; DFHack.memory_write_int32(@_memaddr, v & 0xffffffff) ; DFHack.memory_write_int32(@memaddr+4, v>>32)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def _cpp_init
|
|
||||||
_set(@_initvalue) if @_initvalue
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class Float < MemStruct
|
|
||||||
def _get
|
|
||||||
DFHack.memory_read_float(@_memaddr)
|
|
||||||
end
|
|
||||||
|
|
||||||
def _set(v)
|
|
||||||
DFHack.memory_write_float(@_memaddr, v)
|
|
||||||
end
|
|
||||||
|
|
||||||
def _cpp_init
|
|
||||||
_set(0.0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class BitField < MemStruct
|
|
||||||
attr_accessor :_shift, :_len, :_enum
|
|
||||||
def initialize(shift, len, enum=nil)
|
|
||||||
@_shift = shift
|
|
||||||
@_len = len
|
|
||||||
@_enum = enum
|
|
||||||
end
|
|
||||||
def _mask
|
|
||||||
(1 << @_len) - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def _get
|
|
||||||
v = DFHack.memory_read_int32(@_memaddr) >> @_shift
|
|
||||||
if @_len == 1
|
|
||||||
((v & 1) == 0) ? false : true
|
|
||||||
else
|
|
||||||
v &= _mask
|
|
||||||
v = @_enum.to_sym(v) if @_enum
|
|
||||||
v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def _set(v)
|
|
||||||
if @_len == 1
|
|
||||||
# allow 'bit = 0'
|
|
||||||
v = (v && v != 0 ? 1 : 0)
|
|
||||||
end
|
|
||||||
v = @_enum.to_i(v) if @_enum
|
|
||||||
v = (v & _mask) << @_shift
|
|
||||||
|
|
||||||
ori = DFHack.memory_read_int32(@_memaddr) & 0xffffffff
|
|
||||||
DFHack.memory_write_int32(@_memaddr, ori - (ori & ((-1 & _mask) << @_shift)) + v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Pointer < MemStruct
|
|
||||||
attr_accessor :_tg
|
|
||||||
def initialize(tg)
|
|
||||||
@_tg = tg
|
|
||||||
end
|
|
||||||
|
|
||||||
def _getp
|
|
||||||
DFHack.memory_read_int32(@_memaddr) & 0xffffffff
|
|
||||||
end
|
|
||||||
|
|
||||||
def _get
|
|
||||||
addr = _getp
|
|
||||||
return if addr == 0
|
|
||||||
@_tg._at(addr)._get
|
|
||||||
end
|
|
||||||
|
|
||||||
# XXX shaky...
|
|
||||||
def _set(v)
|
|
||||||
if v.kind_of?(Pointer)
|
|
||||||
DFHack.memory_write_int32(@_memaddr, v._getp)
|
|
||||||
elsif v.kind_of?(MemStruct)
|
|
||||||
DFHack.memory_write_int32(@_memaddr, v._memaddr)
|
|
||||||
else
|
|
||||||
_get._set(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def inspect
|
|
||||||
ptr = _getp
|
|
||||||
if ptr == 0
|
|
||||||
'NULL'
|
|
||||||
else
|
|
||||||
cn = ''
|
|
||||||
cn = @_tg.class.name.sub(/^DFHack::/, '').sub(/^MemHack::/, '') if @_tg
|
|
||||||
cn = @_tg._glob if cn == 'Global'
|
|
||||||
"#<Pointer #{cn} #{'0x%X' % _getp}>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class PointerAry < MemStruct
|
|
||||||
attr_accessor :_tglen, :_tg
|
|
||||||
def initialize(tglen, tg)
|
|
||||||
@_tglen = tglen
|
|
||||||
@_tg = tg
|
|
||||||
end
|
|
||||||
|
|
||||||
def _getp(i=0)
|
|
||||||
delta = (i != 0 ? i*@_tglen : 0)
|
|
||||||
(DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta
|
|
||||||
end
|
|
||||||
|
|
||||||
def _get
|
|
||||||
addr = _getp
|
|
||||||
return if addr == 0
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
def [](i)
|
|
||||||
addr = _getp(i)
|
|
||||||
return if addr == 0
|
|
||||||
@_tg._at(addr)._get
|
|
||||||
end
|
|
||||||
def []=(i, v)
|
|
||||||
addr = _getp(i)
|
|
||||||
raise 'null pointer' if addr == 0
|
|
||||||
@_tg._at(addr)._set(v)
|
|
||||||
end
|
|
||||||
|
|
||||||
def inspect ; ptr = _getp ; (ptr == 0) ? 'NULL' : "#<PointerAry #{'0x%X' % ptr}>" ; end
|
|
||||||
end
|
|
||||||
module Enumerable
|
|
||||||
include ::Enumerable
|
|
||||||
attr_accessor :_indexenum
|
|
||||||
def each ; (0...length).each { |i| yield self[i] } ; end
|
|
||||||
def inspect
|
|
||||||
out = '['
|
|
||||||
each_with_index { |e, idx|
|
|
||||||
out << ', ' if out.length > 1
|
|
||||||
if out.length > INSPECT_SIZE_LIMIT
|
|
||||||
out << '...'
|
|
||||||
break
|
|
||||||
end
|
|
||||||
out << "#{_indexenum.to_sym(idx)}=" if _indexenum
|
|
||||||
out << e.inspect
|
|
||||||
}
|
|
||||||
out << ']'
|
|
||||||
end
|
|
||||||
def empty? ; length == 0 ; end
|
|
||||||
def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end
|
|
||||||
end
|
|
||||||
class StaticArray < MemStruct
|
|
||||||
attr_accessor :_tglen, :_length, :_indexenum, :_tg
|
|
||||||
def initialize(tglen, length, indexenum, tg)
|
|
||||||
@_tglen = tglen
|
|
||||||
@_length = length
|
|
||||||
@_indexenum = indexenum
|
|
||||||
@_tg = tg
|
|
||||||
end
|
|
||||||
def _set(a)
|
|
||||||
a.each_with_index { |v, i| self[i] = v }
|
|
||||||
end
|
|
||||||
def _cpp_init
|
|
||||||
_length.times { |i| _tgat(i)._cpp_init }
|
|
||||||
end
|
|
||||||
alias length _length
|
|
||||||
alias size _length
|
|
||||||
def _tgat(i)
|
|
||||||
@_tg._at(@_memaddr + i*@_tglen) if i >= 0 and i < @_length
|
|
||||||
end
|
|
||||||
def [](i)
|
|
||||||
i = _indexenum.to_i(i) if _indexenum
|
|
||||||
i += @_length if i < 0
|
|
||||||
_tgat(i)._get
|
|
||||||
end
|
|
||||||
def []=(i, v)
|
|
||||||
i = _indexenum.to_i(i) if _indexenum
|
|
||||||
i += @_length if i < 0
|
|
||||||
_tgat(i)._set(v)
|
|
||||||
end
|
|
||||||
|
|
||||||
include Enumerable
|
|
||||||
end
|
|
||||||
class StaticString < MemStruct
|
|
||||||
attr_accessor :_length
|
|
||||||
def initialize(length)
|
|
||||||
@_length = length
|
|
||||||
end
|
|
||||||
def _get
|
|
||||||
DFHack.memory_read(@_memaddr, @_length)
|
|
||||||
end
|
|
||||||
def _set(v)
|
|
||||||
DFHack.memory_write(@_memaddr, v[0, @_length])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class StlVector32 < MemStruct
|
|
||||||
attr_accessor :_tg
|
|
||||||
def initialize(tg)
|
|
||||||
@_tg = tg
|
|
||||||
end
|
|
||||||
|
|
||||||
def length
|
|
||||||
DFHack.memory_vector32_length(@_memaddr)
|
|
||||||
end
|
|
||||||
def size ; length ; end # alias wouldnt work for subclasses
|
|
||||||
def valueptr_at(idx)
|
|
||||||
DFHack.memory_vector32_ptrat(@_memaddr, idx)
|
|
||||||
end
|
|
||||||
def insert_at(idx, val)
|
|
||||||
DFHack.memory_vector32_insert(@_memaddr, idx, val)
|
|
||||||
end
|
|
||||||
def delete_at(idx)
|
|
||||||
DFHack.memory_vector32_delete(@_memaddr, idx)
|
|
||||||
end
|
|
||||||
|
|
||||||
def _set(v)
|
|
||||||
delete_at(length-1) while length > v.length # match lengthes
|
|
||||||
v.each_with_index { |e, i| self[i] = e } # patch entries
|
|
||||||
end
|
|
||||||
|
|
||||||
def _cpp_init
|
|
||||||
DFHack.memory_vector_init(@_memaddr)
|
|
||||||
end
|
|
||||||
|
|
||||||
def clear
|
|
||||||
delete_at(length-1) while length > 0
|
|
||||||
end
|
|
||||||
def [](idx)
|
|
||||||
idx += length if idx < 0
|
|
||||||
@_tg._at(valueptr_at(idx))._get if idx >= 0 and idx < length
|
|
||||||
end
|
|
||||||
def []=(idx, v)
|
|
||||||
idx += length if idx < 0
|
|
||||||
if idx >= length
|
|
||||||
insert_at(idx, 0)
|
|
||||||
elsif idx < 0
|
|
||||||
raise 'invalid idx'
|
|
||||||
end
|
|
||||||
@_tg._at(valueptr_at(idx))._set(v)
|
|
||||||
end
|
|
||||||
def push(v)
|
|
||||||
self[length] = v
|
|
||||||
self
|
|
||||||
end
|
|
||||||
def <<(v) ; push(v) ; end
|
|
||||||
def pop
|
|
||||||
l = length
|
|
||||||
if l > 0
|
|
||||||
v = self[l-1]
|
|
||||||
delete_at(l-1)
|
|
||||||
end
|
|
||||||
v
|
|
||||||
end
|
|
||||||
|
|
||||||
include Enumerable
|
|
||||||
# do a binary search in an ordered vector for a specific target attribute
|
|
||||||
# ex: world.history.figures.binsearch(unit.hist_figure_id)
|
|
||||||
def binsearch(target, field=:id)
|
|
||||||
o_start = 0
|
|
||||||
o_end = length - 1
|
|
||||||
while o_end >= o_start
|
|
||||||
o_half = o_start + (o_end-o_start)/2
|
|
||||||
obj = self[o_half]
|
|
||||||
oval = obj.send(field)
|
|
||||||
if oval == target
|
|
||||||
return obj
|
|
||||||
elsif oval < target
|
|
||||||
o_start = o_half+1
|
|
||||||
else
|
|
||||||
o_end = o_half-1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class StlVector16 < StlVector32
|
|
||||||
def length
|
|
||||||
DFHack.memory_vector16_length(@_memaddr)
|
|
||||||
end
|
|
||||||
def valueptr_at(idx)
|
|
||||||
DFHack.memory_vector16_ptrat(@_memaddr, idx)
|
|
||||||
end
|
|
||||||
def insert_at(idx, val)
|
|
||||||
DFHack.memory_vector16_insert(@_memaddr, idx, val)
|
|
||||||
end
|
|
||||||
def delete_at(idx)
|
|
||||||
DFHack.memory_vector16_delete(@_memaddr, idx)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class StlVector8 < StlVector32
|
|
||||||
def length
|
|
||||||
DFHack.memory_vector8_length(@_memaddr)
|
|
||||||
end
|
|
||||||
def valueptr_at(idx)
|
|
||||||
DFHack.memory_vector8_ptrat(@_memaddr, idx)
|
|
||||||
end
|
|
||||||
def insert_at(idx, val)
|
|
||||||
DFHack.memory_vector8_insert(@_memaddr, idx, val)
|
|
||||||
end
|
|
||||||
def delete_at(idx)
|
|
||||||
DFHack.memory_vector8_delete(@_memaddr, idx)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class StlBitVector < StlVector32
|
|
||||||
def initialize ; end
|
|
||||||
def length
|
|
||||||
DFHack.memory_vectorbool_length(@_memaddr)
|
|
||||||
end
|
|
||||||
def insert_at(idx, val)
|
|
||||||
DFHack.memory_vectorbool_insert(@_memaddr, idx, val)
|
|
||||||
end
|
|
||||||
def delete_at(idx)
|
|
||||||
DFHack.memory_vectorbool_delete(@_memaddr, idx)
|
|
||||||
end
|
|
||||||
def [](idx)
|
|
||||||
idx += length if idx < 0
|
|
||||||
DFHack.memory_vectorbool_at(@_memaddr, idx) if idx >= 0 and idx < length
|
|
||||||
end
|
|
||||||
def []=(idx, v)
|
|
||||||
idx += length if idx < 0
|
|
||||||
if idx >= length
|
|
||||||
insert_at(idx, v)
|
|
||||||
elsif idx < 0
|
|
||||||
raise 'invalid idx'
|
|
||||||
else
|
|
||||||
DFHack.memory_vectorbool_setat(@_memaddr, idx, v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class StlString < MemStruct
|
|
||||||
def _get
|
|
||||||
DFHack.memory_read_stlstring(@_memaddr)
|
|
||||||
end
|
|
||||||
|
|
||||||
def _set(v)
|
|
||||||
DFHack.memory_write_stlstring(@_memaddr, v)
|
|
||||||
end
|
|
||||||
|
|
||||||
def _cpp_init
|
|
||||||
DFHack.memory_stlstring_init(@_memaddr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
class StlDeque < MemStruct
|
|
||||||
attr_accessor :_tglen, :_tg
|
|
||||||
def initialize(tglen, tg)
|
|
||||||
@_tglen = tglen
|
|
||||||
@_tg = tg
|
|
||||||
end
|
|
||||||
# XXX DF uses stl::deque<some_struct>, so to have a C binding we'd need to single-case every
|
|
||||||
# possible struct size, like for StlVector. Just ignore it for now, deque are rare enough.
|
|
||||||
def inspect ; "#<StlDeque>" ; end
|
|
||||||
end
|
|
||||||
|
|
||||||
class DfFlagarray < MemStruct
|
|
||||||
attr_accessor :_indexenum
|
|
||||||
def initialize(indexenum)
|
|
||||||
@_indexenum = indexenum
|
|
||||||
end
|
|
||||||
def length
|
|
||||||
DFHack.memory_bitarray_length(@_memaddr)
|
|
||||||
end
|
|
||||||
# TODO _cpp_init
|
|
||||||
def size ; length ; end
|
|
||||||
def resize(len)
|
|
||||||
DFHack.memory_bitarray_resize(@_memaddr, len)
|
|
||||||
end
|
|
||||||
def [](idx)
|
|
||||||
idx = _indexenum.to_i(idx) if _indexenum
|
|
||||||
idx += length if idx < 0
|
|
||||||
DFHack.memory_bitarray_isset(@_memaddr, idx) if idx >= 0 and idx < length
|
|
||||||
end
|
|
||||||
def []=(idx, v)
|
|
||||||
idx = _indexenum.to_i(idx) if _indexenum
|
|
||||||
idx += length if idx < 0
|
|
||||||
if idx >= length or idx < 0
|
|
||||||
raise 'invalid idx'
|
|
||||||
else
|
|
||||||
DFHack.memory_bitarray_set(@_memaddr, idx, v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
include Enumerable
|
|
||||||
end
|
|
||||||
class DfArray < Compound
|
|
||||||
attr_accessor :_tglen, :_tg
|
|
||||||
def initialize(tglen, tg)
|
|
||||||
@_tglen = tglen
|
|
||||||
@_tg = tg
|
|
||||||
end
|
|
||||||
|
|
||||||
field(:_ptr, 0) { number 32, false }
|
|
||||||
field(:_length, 4) { number 16, false }
|
|
||||||
|
|
||||||
def length ; _length ; end
|
|
||||||
def size ; _length ; end
|
|
||||||
# TODO _cpp_init
|
|
||||||
def _tgat(i)
|
|
||||||
@_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length
|
|
||||||
end
|
|
||||||
def [](i)
|
|
||||||
i += _length if i < 0
|
|
||||||
_tgat(i)._get
|
|
||||||
end
|
|
||||||
def []=(i, v)
|
|
||||||
i += _length if i < 0
|
|
||||||
_tgat(i)._set(v)
|
|
||||||
end
|
|
||||||
def _set(a)
|
|
||||||
a.each_with_index { |v, i| self[i] = v }
|
|
||||||
end
|
|
||||||
|
|
||||||
include Enumerable
|
|
||||||
end
|
|
||||||
class DfLinkedList < Compound
|
|
||||||
attr_accessor :_tg
|
|
||||||
def initialize(tg)
|
|
||||||
@_tg = tg
|
|
||||||
end
|
|
||||||
|
|
||||||
field(:_ptr, 0) { number 32, false }
|
|
||||||
field(:_prev, 4) { number 32, false }
|
|
||||||
field(:_next, 8) { number 32, false }
|
|
||||||
|
|
||||||
def item
|
|
||||||
# With the current xml structure, currently _tg designate
|
|
||||||
# the type of the 'next' and 'prev' fields, not 'item'.
|
|
||||||
# List head has item == NULL, so we can safely return nil.
|
|
||||||
|
|
||||||
#addr = _ptr
|
|
||||||
#return if addr == 0
|
|
||||||
#@_tg._at(addr)._get
|
|
||||||
end
|
|
||||||
|
|
||||||
def item=(v)
|
|
||||||
#addr = _ptr
|
|
||||||
#raise 'null pointer' if addr == 0
|
|
||||||
#@_tg.at(addr)._set(v)
|
|
||||||
raise 'null pointer'
|
|
||||||
end
|
|
||||||
|
|
||||||
def prev
|
|
||||||
addr = _prev
|
|
||||||
return if addr == 0
|
|
||||||
@_tg._at(addr)._get
|
|
||||||
end
|
|
||||||
|
|
||||||
def next
|
|
||||||
addr = _next
|
|
||||||
return if addr == 0
|
|
||||||
@_tg._at(addr)._get
|
|
||||||
end
|
|
||||||
|
|
||||||
include Enumerable
|
|
||||||
def each
|
|
||||||
o = self
|
|
||||||
while o
|
|
||||||
yield o.item if o.item
|
|
||||||
o = o.next
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def inspect ; "#<DfLinkedList #{item.inspect} prev=#{'0x%X' % _prev} next=#{'0x%X' % _next}>" ; end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Global < MemStruct
|
|
||||||
attr_accessor :_glob
|
|
||||||
def initialize(glob)
|
|
||||||
@_glob = glob
|
|
||||||
end
|
|
||||||
def _at(addr)
|
|
||||||
g = DFHack.const_get(@_glob)
|
|
||||||
g = DFHack.rtti_getclassat(g, addr)
|
|
||||||
g.new._at(addr)
|
|
||||||
end
|
|
||||||
def inspect ; "#<#{@_glob}>" ; end
|
|
||||||
end
|
|
||||||
end # module MemHack
|
|
||||||
|
|
||||||
|
|
||||||
# cpp rtti name -> rb class
|
|
||||||
@rtti_n2c = {}
|
|
||||||
@rtti_c2n = {}
|
|
||||||
|
|
||||||
# cpp rtti name -> vtable ptr
|
|
||||||
@rtti_n2v = {}
|
|
||||||
@rtti_v2n = {}
|
|
||||||
|
|
||||||
def self.rtti_n2c ; @rtti_n2c ; end
|
|
||||||
def self.rtti_c2n ; @rtti_c2n ; end
|
|
||||||
def self.rtti_n2v ; @rtti_n2v ; end
|
|
||||||
def self.rtti_v2n ; @rtti_v2n ; end
|
|
||||||
|
|
||||||
# register a ruby class with a cpp rtti class name
|
|
||||||
def self.rtti_register(cppname, cls)
|
|
||||||
@rtti_n2c[cppname] = cls
|
|
||||||
@rtti_c2n[cls] = cppname
|
|
||||||
end
|
|
||||||
|
|
||||||
# return the ruby class to use for the cpp object at address if rtti info is available
|
|
||||||
def self.rtti_getclassat(cls, addr)
|
|
||||||
if addr != 0 and @rtti_c2n[cls]
|
|
||||||
# rtti info exist for class => cpp object has a vtable
|
|
||||||
@rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls
|
|
||||||
else
|
|
||||||
cls
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# try to read the rtti classname from an object vtable pointer
|
|
||||||
def self.rtti_readclassname(vptr)
|
|
||||||
unless n = @rtti_v2n[vptr]
|
|
||||||
n = @rtti_v2n[vptr] = get_rtti_classname(vptr).to_sym
|
|
||||||
@rtti_n2v[n] = vptr
|
|
||||||
end
|
|
||||||
n
|
|
||||||
end
|
|
||||||
|
|
||||||
# return the vtable pointer from the cpp rtti name
|
|
||||||
def self.rtti_getvtable(cppname)
|
|
||||||
unless v = @rtti_n2v[cppname]
|
|
||||||
v = get_vtable(cppname.to_s)
|
|
||||||
@rtti_n2v[cppname] = v
|
|
||||||
@rtti_v2n[v] = cppname if v != 0
|
|
||||||
end
|
|
||||||
v if v != 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.vmethod_call(obj, voff, a0=0, a1=0, a2=0, a3=0, a4=0)
|
|
||||||
vmethod_do_call(obj._memaddr, voff, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2), vmethod_arg(a3))
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.vmethod_arg(arg)
|
|
||||||
case arg
|
|
||||||
when nil, false; 0
|
|
||||||
when true; 1
|
|
||||||
when Integer; arg
|
|
||||||
#when String; [arg].pack('p').unpack('L')[0] # raw pointer to buffer
|
|
||||||
when MemHack::Compound; arg._memaddr
|
|
||||||
else raise "bad vmethod arg #{arg.class}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,601 @@
|
|||||||
|
-- Find some offsets for linux.
|
||||||
|
|
||||||
|
local utils = require 'utils'
|
||||||
|
local ms = require 'memscan'
|
||||||
|
|
||||||
|
local is_known = dfhack.internal.getAddress
|
||||||
|
|
||||||
|
local force_scan = {}
|
||||||
|
for _,v in ipairs({...}) do
|
||||||
|
force_scan[v] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
collectgarbage()
|
||||||
|
|
||||||
|
print[[
|
||||||
|
WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS.
|
||||||
|
|
||||||
|
Running this script on a new DF version will NOT
|
||||||
|
MAKE IT RUN CORRECTLY if any data structures
|
||||||
|
changed, thus possibly leading to CRASHES AND/OR
|
||||||
|
PERMANENT SAVE CORRUPTION.
|
||||||
|
|
||||||
|
This script should be initially started immediately
|
||||||
|
after loading the game, WITHOUT first loading a world.
|
||||||
|
It expects vanilla game configuration, without any
|
||||||
|
custom tilesets or init file changes.
|
||||||
|
]]
|
||||||
|
|
||||||
|
if not utils.prompt_yes_no('Proceed?') then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Data segment location
|
||||||
|
|
||||||
|
local data = ms.get_data_segment()
|
||||||
|
if not data then
|
||||||
|
error('Could not find data segment')
|
||||||
|
end
|
||||||
|
|
||||||
|
print('\nData section: '..tostring(data))
|
||||||
|
if data.size < 5000000 then
|
||||||
|
error('Data segment too short.')
|
||||||
|
end
|
||||||
|
|
||||||
|
local searcher = ms.DiffSearcher.new(data)
|
||||||
|
|
||||||
|
local function validate_offset(name,validator,addr,tname,...)
|
||||||
|
local obj = data:object_by_field(addr,tname,...)
|
||||||
|
if obj and not validator(obj) then
|
||||||
|
obj = nil
|
||||||
|
end
|
||||||
|
ms.found_offset(name,obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function exec_finder(finder, names)
|
||||||
|
if type(names) ~= 'table' then
|
||||||
|
names = { names }
|
||||||
|
end
|
||||||
|
local search = force_scan['all']
|
||||||
|
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
|
||||||
|
--
|
||||||
|
|
||||||
|
local function find_cursor()
|
||||||
|
print('\nPlease navigate to the title screen to find cursor.')
|
||||||
|
if not utils.prompt_yes_no('Proceed?', true) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Unpadded version
|
||||||
|
local idx, addr = data.int32_t:find_one{
|
||||||
|
-30000, -30000, -30000,
|
||||||
|
-30000, -30000, -30000, -30000, -30000, -30000,
|
||||||
|
df.game_mode.NONE, df.game_type.NONE
|
||||||
|
}
|
||||||
|
if idx then
|
||||||
|
ms.found_offset('cursor', addr)
|
||||||
|
ms.found_offset('selection_rect', addr + 12)
|
||||||
|
ms.found_offset('gamemode', addr + 12 + 24)
|
||||||
|
ms.found_offset('gametype', addr + 12 + 24 + 4)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Padded version
|
||||||
|
idx, addr = data.int32_t:find_one{
|
||||||
|
-30000, -30000, -30000, 0,
|
||||||
|
-30000, -30000, -30000, -30000, -30000, -30000, 0, 0,
|
||||||
|
df.game_mode.NONE, 0, 0, 0, df.game_type.NONE
|
||||||
|
}
|
||||||
|
if idx then
|
||||||
|
ms.found_offset('cursor', addr)
|
||||||
|
ms.found_offset('selection_rect', addr + 0x10)
|
||||||
|
ms.found_offset('gamemode', addr + 0x30)
|
||||||
|
ms.found_offset('gametype', addr + 0x40)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
dfhack.printerr('Could not find cursor.')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' })
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Announcements
|
||||||
|
--
|
||||||
|
|
||||||
|
local function find_announcements()
|
||||||
|
local idx, addr = data.int32_t:find_one{
|
||||||
|
25, 25, 31, 31, 24, 24, 40, 40, 40, 40, 40, 40, 40
|
||||||
|
}
|
||||||
|
if idx then
|
||||||
|
ms.found_offset('announcements', addr)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
dfhack.printerr('Could not find announcements.')
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_announcements, 'announcements')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- d_init
|
||||||
|
--
|
||||||
|
|
||||||
|
local function is_valid_d_init(di)
|
||||||
|
if di.sky_tile ~= 178 then
|
||||||
|
print('Sky tile expected 178, found: '..di.sky_tile)
|
||||||
|
if not utils.prompt_yes_no('Ignore?') then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local ann = is_known 'announcements'
|
||||||
|
local size,ptr = di:sizeof()
|
||||||
|
if ann and ptr+size ~= ann then
|
||||||
|
print('Announcements not immediately after d_init.')
|
||||||
|
if not utils.prompt_yes_no('Ignore?') then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_d_init()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if idx then
|
||||||
|
validate_offset('d_init', is_valid_d_init, addr, df.d_init, 'path_cost')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
dfhack.printerr('Could not find d_init')
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
--
|
||||||
|
|
||||||
|
local function is_valid_world(world)
|
||||||
|
if not ms.is_valid_vector(world.units.all, 4)
|
||||||
|
or not ms.is_valid_vector(world.units.bad, 4)
|
||||||
|
or not ms.is_valid_vector(world.history.figures, 4)
|
||||||
|
or not ms.is_valid_vector(world.cur_savegame.map_features, 4)
|
||||||
|
then
|
||||||
|
dfhack.printerr('Vector layout check failed.')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if #world.units.all == 0 or #world.units.all ~= #world.units.bad then
|
||||||
|
print('Different or zero size of units.all and units.bad:'..#world.units.all..' vs '..#world.units.bad)
|
||||||
|
if not utils.prompt_yes_no('Ignore?') then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_world()
|
||||||
|
local addr = searcher:find_menu_cursor([[
|
||||||
|
Searching for world. Please open the stockpile creation
|
||||||
|
menu, and select different types as instructed below:]],
|
||||||
|
'int32_t',
|
||||||
|
{ 'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins' },
|
||||||
|
df.stockpile_category
|
||||||
|
)
|
||||||
|
validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type')
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_world, 'world')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- UI
|
||||||
|
--
|
||||||
|
|
||||||
|
local function is_valid_ui(ui)
|
||||||
|
if not ms.is_valid_vector(ui.economic_stone, 1)
|
||||||
|
or not ms.is_valid_vector(ui.dipscripts, 4)
|
||||||
|
then
|
||||||
|
dfhack.printerr('Vector layout check failed.')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if ui.follow_item ~= -1 or ui.follow_unit ~= -1 then
|
||||||
|
print('Invalid follow state: '..ui.follow_item..', '..ui.follow_unit)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_ui()
|
||||||
|
local addr = searcher:find_menu_cursor([[
|
||||||
|
Searching for ui. Please open the designation
|
||||||
|
menu, and switch modes as instructed below:]],
|
||||||
|
'int16_t',
|
||||||
|
{ 'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps', 'DesignateUpStair',
|
||||||
|
'DesignateDownStair', 'DesignateUpDownStair', 'DesignateUpRamp', 'DesignateChopTrees' },
|
||||||
|
df.ui_sidebar_mode
|
||||||
|
)
|
||||||
|
validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode')
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_ui, 'ui')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ui_sidebar_menus
|
||||||
|
--
|
||||||
|
|
||||||
|
local function is_valid_ui_sidebar_menus(usm)
|
||||||
|
if not ms.is_valid_vector(usm.workshop_job.choices_all, 4)
|
||||||
|
or not ms.is_valid_vector(usm.workshop_job.choices_visible, 4)
|
||||||
|
then
|
||||||
|
dfhack.printerr('Vector layout check failed.')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if #usm.workshop_job.choices_all == 0
|
||||||
|
or #usm.workshop_job.choices_all ~= #usm.workshop_job.choices_visible then
|
||||||
|
print('Different or zero size of visible and all choices:'..
|
||||||
|
#usm.workshop_job.choices_all..' vs '..#usm.workshop_job.choices_visible)
|
||||||
|
if not utils.prompt_yes_no('Ignore?') then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_ui_sidebar_menus()
|
||||||
|
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 },
|
||||||
|
ordinal_names
|
||||||
|
)
|
||||||
|
validate_offset('ui_sidebar_menus', is_valid_ui_sidebar_menus,
|
||||||
|
addr, df.ui_sidebar_menus, 'workshop_job', 'cursor')
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ui_build_selector
|
||||||
|
--
|
||||||
|
|
||||||
|
local function is_valid_ui_build_selector(ubs)
|
||||||
|
if not ms.is_valid_vector(ubs.requirements, 4)
|
||||||
|
or not ms.is_valid_vector(ubs.choices, 4)
|
||||||
|
then
|
||||||
|
dfhack.printerr('Vector layout check failed.')
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if ubs.building_type ~= df.building_type.Trap
|
||||||
|
or ubs.building_subtype ~= df.trap_type.PressurePlate then
|
||||||
|
print('Invalid building type and subtype:'..ubs.building_type..','..ubs.building_subtype)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_ui_build_selector()
|
||||||
|
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
|
||||||
|
number, so when it shows "Min (5000df", it means 50000:]],
|
||||||
|
'int32_t',
|
||||||
|
{ 50000, 49000, 48000, 47000, 46000, 45000, 44000 }
|
||||||
|
)
|
||||||
|
validate_offset('ui_build_selector', is_valid_ui_build_selector,
|
||||||
|
addr, df.ui_build_selector, 'plate_info', 'unit_min')
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_ui_build_selector, 'ui_build_selector')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ui_selected_unit
|
||||||
|
--
|
||||||
|
|
||||||
|
local function find_ui_selected_unit()
|
||||||
|
if not is_known 'world' then
|
||||||
|
dfhack.printerr('Cannot search for ui_selected_unit: no world')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,unit in ipairs(df.global.world.units.active) do
|
||||||
|
dfhack.units.setNickname(unit, i)
|
||||||
|
end
|
||||||
|
|
||||||
|
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,
|
||||||
|
'noprompt'
|
||||||
|
)
|
||||||
|
ms.found_offset('ui_selected_unit', addr)
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_ui_selected_unit, 'ui_selected_unit')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ui_unit_view_mode
|
||||||
|
--
|
||||||
|
|
||||||
|
local function find_ui_unit_view_mode()
|
||||||
|
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',
|
||||||
|
{ 'General', 'Inventory', 'Preferences', 'Wounds' },
|
||||||
|
df.ui_unit_view_mode.T_value
|
||||||
|
)
|
||||||
|
ms.found_offset('ui_unit_view_mode', addr)
|
||||||
|
end
|
||||||
|
|
||||||
|
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 below.
|
||||||
|
|
||||||
|
NOTE: After first 3 steps resize the game window.]],
|
||||||
|
'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')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ui_building_in_assign
|
||||||
|
--
|
||||||
|
|
||||||
|
local function find_ui_building_in_assign()
|
||||||
|
local addr = searcher:find_menu_cursor([[
|
||||||
|
Searching for ui_building_in_assign. Please activate
|
||||||
|
the 'q' mode, select a room building (e.g. a bedroom)
|
||||||
|
and do as instructed below.
|
||||||
|
|
||||||
|
NOTE: After first 3 steps resize the game window.]],
|
||||||
|
'int8_t',
|
||||||
|
{ 1, 0 },
|
||||||
|
{ [1] = 'enter the Assign owner menu',
|
||||||
|
[0] = 'press Esc to exit assign' }
|
||||||
|
)
|
||||||
|
ms.found_offset('ui_building_in_assign', addr)
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_ui_building_in_assign, 'ui_building_in_assign')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ui_building_in_resize
|
||||||
|
--
|
||||||
|
|
||||||
|
local function find_ui_building_in_resize()
|
||||||
|
local addr = searcher:find_menu_cursor([[
|
||||||
|
Searching for ui_building_in_resize. Please activate
|
||||||
|
the 'q' mode, select a room building (e.g. a bedroom)
|
||||||
|
and do as instructed below.
|
||||||
|
|
||||||
|
NOTE: After first 3 steps resize the game window.]],
|
||||||
|
'int8_t',
|
||||||
|
{ 1, 0 },
|
||||||
|
{ [1] = 'enter the Resize room mode',
|
||||||
|
[0] = 'press Esc to exit resize' }
|
||||||
|
)
|
||||||
|
ms.found_offset('ui_building_in_resize', addr)
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_ui_building_in_resize, 'ui_building_in_resize')
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- 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 below.
|
||||||
|
|
||||||
|
NOTE: After first 3 steps resize the game window.]],
|
||||||
|
'int32_t', -1,
|
||||||
|
"Please press '>' to scroll one Z level down."
|
||||||
|
)
|
||||||
|
ms.found_offset('window_z', addr)
|
||||||
|
end
|
||||||
|
|
||||||
|
exec_finder(find_window_z, 'window_z')
|
||||||
|
|
||||||
|
--
|
||||||
|
-- THE END
|
||||||
|
--
|
||||||
|
|
||||||
|
print('Done.')
|
||||||
|
searcher:reset()
|
@ -0,0 +1,16 @@
|
|||||||
|
-- Deletes ALL items not held by units, buildings or jobs.
|
||||||
|
--
|
||||||
|
-- Intended solely for lag investigation.
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
|
||||||
|
for _,v in ipairs(df.global.world.items.all) do
|
||||||
|
if not (v.flags.in_building or v.flags.construction or v.flags.in_job
|
||||||
|
or dfhack.items.getGeneralRef(v,df.general_ref_type.UNIT_HOLDER)) then
|
||||||
|
count = count + 1
|
||||||
|
v.flags.forbid = true
|
||||||
|
v.flags.garbage_collect = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print('Deletion requested: '..count)
|
@ -0,0 +1,29 @@
|
|||||||
|
-- Remove uninteresting dead units from the unit list.
|
||||||
|
|
||||||
|
local units = df.global.world.units.active
|
||||||
|
local dwarf_race = df.global.ui.race_id
|
||||||
|
local dwarf_civ = df.global.ui.civ_id
|
||||||
|
local count = 0
|
||||||
|
|
||||||
|
for i=#units-1,0,-1 do
|
||||||
|
local unit = units[i]
|
||||||
|
local flags1 = unit.flags1
|
||||||
|
local flags2 = unit.flags2
|
||||||
|
if flags1.dead and unit.race ~= dwarf_race then
|
||||||
|
local remove = false
|
||||||
|
if flags2.slaughter then
|
||||||
|
remove = true
|
||||||
|
elseif not unit.name.has_name then
|
||||||
|
remove = true
|
||||||
|
elseif unit.civ_id ~= dwarf_civ and
|
||||||
|
not (flags1.merchant or flags1.diplomat) then
|
||||||
|
remove = true
|
||||||
|
end
|
||||||
|
if remove then
|
||||||
|
count = count + 1
|
||||||
|
units:erase(i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print('Units removed from active: '..count)
|
@ -0,0 +1,24 @@
|
|||||||
|
-- Makes fat dwarves non-fat.
|
||||||
|
--
|
||||||
|
-- See for more info:
|
||||||
|
-- http://www.bay12games.com/dwarves/mantisbt/view.php?id=5971
|
||||||
|
|
||||||
|
local num_fat = 0
|
||||||
|
local num_lagging = 0
|
||||||
|
|
||||||
|
for _,v in ipairs(df.global.world.units.all) do
|
||||||
|
local fat = v.counters2.stored_fat
|
||||||
|
if fat > 850000 then
|
||||||
|
v.counters2.stored_fat = 500000
|
||||||
|
if v.race == df.global.ui.race_id then
|
||||||
|
print(fat,dfhack.TranslateName(dfhack.units.getVisibleName(v)))
|
||||||
|
num_fat = num_fat + 1
|
||||||
|
if fat > 999990 then
|
||||||
|
num_lagging = num_lagging + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print("Fat dwarves cured: "..num_fat)
|
||||||
|
print("Lag sources: "..num_lagging)
|
@ -0,0 +1,49 @@
|
|||||||
|
-- Reset item temperature to the value of their tile.
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
local types = {}
|
||||||
|
|
||||||
|
local function update_temp(item,btemp)
|
||||||
|
if item.temperature ~= btemp then
|
||||||
|
count = count + 1
|
||||||
|
local tid = item:getType()
|
||||||
|
types[tid] = (types[tid] or 0) + 1
|
||||||
|
end
|
||||||
|
item.temperature = btemp
|
||||||
|
item.temperature_fraction = 0
|
||||||
|
|
||||||
|
if item.contaminants then
|
||||||
|
for _,c in ipairs(item.contaminants) do
|
||||||
|
c.temperature = btemp
|
||||||
|
c.temperature_fraction = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,sub in ipairs(dfhack.items.getContainedItems(item)) do
|
||||||
|
update_temp(sub,btemp)
|
||||||
|
end
|
||||||
|
|
||||||
|
item:checkTemperatureDamage()
|
||||||
|
end
|
||||||
|
|
||||||
|
local last_frame = df.global.world.frame_counter-1
|
||||||
|
|
||||||
|
for _,item in ipairs(df.global.world.items.all) do
|
||||||
|
if item.flags.on_ground and df.item_actual:is_instance(item) and
|
||||||
|
item.temp_updated_frame == last_frame then
|
||||||
|
local pos = item.pos
|
||||||
|
local block = dfhack.maps.getTileBlock(pos)
|
||||||
|
if block then
|
||||||
|
update_temp(item, block.temperature_1[pos.x%16][pos.y%16])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print('Items updated: '..count)
|
||||||
|
|
||||||
|
local tlist = {}
|
||||||
|
for k,_ in pairs(types) do tlist[#tlist+1] = k end
|
||||||
|
table.sort(tlist, function(a,b) return types[a] > types[b] end)
|
||||||
|
for _,k in ipairs(tlist) do
|
||||||
|
print(' '..df.item_type[k]..':', types[k])
|
||||||
|
end
|
Loading…
Reference in New Issue