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)
|
||||
if(RUBY_FOUND)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ruby-autogen.cpp
|
||||
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
|
||||
DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl
|
||||
)
|
||||
ADD_EXECUTABLE(ruby-autogen ruby-autogen.cpp)
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
set_target_properties (ruby-autogen PROPERTIES COMPILE_FLAGS "-Wno-invalid-offsetof")
|
||||
endif(CMAKE_COMPILER_IS_GNUCC)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ruby-autogen.offsets
|
||||
COMMAND ruby-autogen ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets
|
||||
DEPENDS ruby-autogen
|
||||
)
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT 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 ${CMAKE_CURRENT_BINARY_DIR}/ruby-autogen.offsets ${CMAKE_CURRENT_SOURCE_DIR}/ruby-memstruct.rb
|
||||
DEPENDS ruby-autogen.offsets ruby-memstruct.rb
|
||||
)
|
||||
ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb)
|
||||
include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH})
|
||||
DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread)
|
||||
target_link_libraries(ruby ${RUBY_LIBRARY})
|
||||
install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION})
|
||||
else(RUBY_FOUND)
|
||||
MESSAGE(STATUS "Required library (ruby) not found - ruby plugin can't be built.")
|
||||
endif(RUBY_FOUND)
|
||||
OPTION(DL_RUBY "download libruby from the internet" ON)
|
||||
IF (DL_RUBY)
|
||||
IF (UNIX)
|
||||
FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/libruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/libruby187.tar.gz
|
||||
EXPECTED_MD5 eb2adea59911f68e6066966c1352f291)
|
||||
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf libruby187.tar.gz
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
FILE(RENAME libruby1.8.so.1.8.7 libruby.so)
|
||||
SET(RUBYLIB libruby.so)
|
||||
ELSE (UNIX)
|
||||
FILE(DOWNLOAD http://cloud.github.com/downloads/jjyg/dfhack/msvcrtruby187.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/msvcrtruby187.tar.gz
|
||||
EXPECTED_MD5 9f4a1659ac3a5308f32d3a1937bbeeae)
|
||||
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xzf msvcrtruby187.tar.gz
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
FILE(RENAME msvcrt-ruby18.dll libruby.dll)
|
||||
SET(RUBYLIB libruby.dll)
|
||||
ENDIF(UNIX)
|
||||
ENDIF(DL_RUBY)
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/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
|
||||
DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl
|
||||
COMMENT ruby-autogen.rb
|
||||
)
|
||||
ADD_CUSTOM_TARGET(ruby-autogen-rb DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb)
|
||||
|
||||
INCLUDE_DIRECTORIES("${dfhack_SOURCE_DIR}/depends/tthread")
|
||||
|
||||
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