[Clientside]Custom key commands
-
The following code provides a means to override or extend the key commands in freelancer. In the example below I’ve added a USER_HUD to toggle the hud visibility.
In KeyMap.ini
[KeyCmd] nickname = USER_HUD ids_name = 1983 ; replace these with new ids ids_info = 3217 ; replace these with new ids state = keydown key = "L" [KeyMap] nickname = IDR_ALWAYS_PRESENT key = USER_HUD
In keylist.ini
[key] id = USER_HUD
And in the client side DLL
void __stdcall HkCb_HandleKeyCmd(int keycmd, int dunno) { if (keycmd == 0x0000008d) // key index of USER_STEALTH which is renamed USER_HUD. { // do stuff here } } __declspec(naked) void HkCb_HandleKeyCmdNaked(void) { __asm { push [esp+8] push [esp+8] call HkCb_HandleKeyCmd sub esp, 80h push ebx mov ecx, 0x00576417 jmp ecx } } Patch() { // Patch keynames { char patch[] = "USER_HUD\x00\x00\x00\x00"; WriteProcMem((char*)0x5E376C, &patch, 13); } // Patch keycmd processor { BYTE patch[] = { 0xB9, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE1 }; // mov ecx HkCb_HandleKeyCmdNaked, jmp ecx DWORD *iAddr = (DWORD*)((char*)&patch + 1); *iAddr = reinterpret_cast<dword>((void*)HkCb_HandleKeyCmdNaked); WriteProcMem((char*)0x576410, &patch, 7); } }</dword>
With a patch a on the keyname list it should be possible to add any number of new key command but in the above example I’ve just renamed an existing but unused command (USER_STEALTH).
EDIT: it turns out that there are heaps of unused commands, i’ve added 8 new commands so far.
-
Very nice find
-
You may also be interested in this:
uint iState = *(int*)0x067C290; // iState == 0 -> keyup | iState == 1 -> keydown
There are indeed a lot of unused keys, btw. here is an enum:
enum INPUT_COMMANDS { USER_NONE, USER_KEY, USER_CHAR, USER_BUTTON, USER_WHEEL, USER_MOVE, USER_BUTTON0, USER_BUTTON1, USER_BUTTON2, USER_BUTTON3, USER_BUTTON4, USER_BUTTON5, USER_BUTTON6, USER_BUTTON7, USER_EVENTS, USER_CANCEL, USER_STACKABLE_CANCEL, USER_ENTER, USER_NO, USER_YES, USER_PAUSE, USER_RESET, USER_SAVE_GAME, USER_FULLSCREEN, USER_USE_ITEM, USER_ACTIVATE_ITEM, USER_NEXT_ITEM, USER_PREV_ITEM, USER_SET_TARGET, USER_NEXT_TARGET, USER_PREV_TARGET, USER_NEXT_WEAPON_SUBTARGET, USER_PREV_WEAPON_SUBTARGET, USER_NEXT_SUBTARGET, USER_PREV_SUBTARGET, USER_CLEAR_TARGET, USER_NEXT_ENEMY, USER_PREV_ENEMY, USER_CLOSEST_ENEMY, USER_ZOOM_IN, USER_ZOOM_OUT, USER_ZOOM_TOGGLE, USER_X_ROTATE, USER_X_UNROTATE, USER_Y_ROTATE, USER_Y_UNROTATE, USER_Z_ROTATE, USER_Z_UNROTATE, USER_VIEW_RESET, USER_COCKPIT_CAMERA_MODE, USER_LOOK_ROTATE_CAMERA_LEFT, USER_LOOK_ROTATE_CAMERA_RIGHT, USER_LOOK_ROTATE_CAMERA_UP, USER_LOOK_ROTATE_CAMERA_DOWN, USER_LOOK_ROTATE_CAMERA_RESET, USER_CHASE_CAMERA_MODE, USER_REARVIEW_CAMERA_MODE, USER_REARVIEW_CAMERA_MODE_OFF, USER_AFTERBURN, USER_AFTERBURN_OFF, USER_AFTERBURN_ON, USER_TOGGLE_AUTO_AVOIDANCE, USER_TOGGLE_AUTO_LEVEL, USER_TOGGLE_LEVEL_CAMERA, USER_WARP, USER_ENGINE_TOGGLE, USER_SET_THROTTLE, USER_INC_THROTTLE, USER_DEC_THROTTLE, USER_CRUISE, USER_THROTTLE_0, USER_THROTTLE_10, USER_THROTTLE_20, USER_THROTTLE_25, USER_THROTTLE_30, USER_THROTTLE_40, USER_THROTTLE_50, USER_THROTTLE_60, USER_THROTTLE_70, USER_THROTTLE_75, USER_THROTTLE_80, USER_THROTTLE_90, USER_THROTTLE_100, USER_RADAR, USER_CONTACT_LIST, USER_RADAR_ZOOM, USER_SWITCH_TO_PLAYER_SHIP, USER_SWITCH_TO_WEAPON_LIST, USER_SWITCH_TO_TARGET, USER_FORMATION_LIST, USER_RADAR_ZOOM_IN, USER_RADAR_ZOOM_OUT, USER_RADIO, USER_CHAT, USER_STATUS, USER_TARGET, USER_STATUS_MODE, USER_TOGGLE_MAINFRAME, USER_NN, USER_NAV_MAP, USER_NAVMAP, USER_PLAYER_STATS, USER_INVENTORY, USER_STORY_STAR, USER_CHAT_WINDOW, USER_GROUP_WINDOW, USER_HELP, USER_INVENTORY_CLOSE, NN_TOGGLE_OPEN, USER_MINIMIZE_HUD, USER_DISPLAY_LAST_OBJECTIVE, USER_COLLECT_LOOT, USER_ACTIONS, USER_MANEUVER, USER_SET_MANEUVER_DIRECTION, USER_MANEUVER_WINDOW, USER_MANEUVER_TRAIL, USER_MANEUVER_AFTERBURNER, USER_MANEUVER_EVADE, USER_MANEUVER_ENGINEKILL, USER_MANEUVER_BRAKE_REVERSE, USER_MANEUVER_DOCK, USER_MANEUVER_GOTO, USER_MANEUVER_FACE, USER_MANEUVER_CRUISE, USER_MANEUVER_TRADE_LANE, USER_MANEUVER_DRASTIC_EVADE, USER_MANEUVER_FORMATION, USER_MANEUVER_STRAFE, USER_MANEUVER_TRAIL_CLOSER, USER_MANEUVER_TRAIL_FARTHER, USER_MANEUVER_CORKSCREW_EVADE, USER_MANEUVER_SLIDE_EVADE, USER_MANEUVER_SLIDE_EVADE_LEFT, USER_MANEUVER_SLIDE_EVADE_RIGHT, USER_MANEUVER_SLIDE_EVADE_UP, USER_MANEUVER_SLIDE_EVADE_DOWN, USER_ACTIVATE_MANEUVER, USER_SCAN_CARGO, USER_TRACTOR_BEAM, USER_JAMMER, USER_STEALTH, USER_CLOAK, USER_REPAIR_HEALTH, USER_REPAIR_SHIELD, USER_WEAPON_GROUP1, USER_WEAPON_GROUP2, USER_WEAPON_GROUP3, USER_WEAPON_GROUP4, USER_WEAPON_GROUP5, USER_WEAPON_GROUP6, USER_TOGGLE_WEAPON1, USER_TOGGLE_WEAPON2, USER_TOGGLE_WEAPON3, USER_TOGGLE_WEAPON4, USER_TOGGLE_WEAPON5, USER_TOGGLE_WEAPON6, USER_TOGGLE_WEAPON7, USER_TOGGLE_WEAPON8, USER_TOGGLE_WEAPON9, USER_TOGGLE_WEAPON10, USER_FIRE_WEAPON1, USER_FIRE_WEAPON2, USER_FIRE_WEAPON3, USER_FIRE_WEAPON4, USER_FIRE_WEAPON5, USER_FIRE_WEAPON6, USER_FIRE_WEAPON7, USER_FIRE_WEAPON8, USER_FIRE_WEAPON9, USER_FIRE_WEAPON10, USER_FIRE_WEAPON_GROUP1, USER_FIRE_WEAPON_GROUP2, USER_FIRE_WEAPON_GROUP3, USER_FIRE_WEAPON_GROUP4, USER_FIRE_WEAPON_GROUP5, USER_FIRE_WEAPON_GROUP6, USER_ASSIGN_WEAPON_GROUP1, USER_ASSIGN_WEAPON_GROUP2, USER_ASSIGN_WEAPON_GROUP3, USER_ASSIGN_WEAPON_GROUP4, USER_ASSIGN_WEAPON_GROUP5, USER_ASSIGN_WEAPON_GROUP6, USER_REMAPPABLE_LEFT, USER_REMAPPABLE_RIGHT, USER_REMAPPABLE_UP, USER_REMAPPABLE_DOWN, USER_FIRE_FORWARD, USER_LAUNCH_MISSILES, USER_LAUNCH_MINES, USER_LAUNCH_COUNTERMEASURES, USER_AUTO_TURRET, USER_LAUNCH_TORPEDOS, USER_LAUNCH_CRUISE_DISRUPTORS, USER_TURN, USER_NEXT_OBJECT, USER_PREV_OBJECT, USER_EXIT_GAME, USER_MANEUVER_FREE_FLIGHT, USER_FIRE_WEAPONS, USER_TURN_SHIP, USER_GROUP_INVITE, USER_TRADE_REQUEST, USER_SCREEN_SHOT };
-
Checking if the chat input window is open:
typedef void* (*_GetObjByStr)(char*); _GetObjByStr GetObjByStr = (_GetObjByStr)0x59DA10;
void __stdcall HkCb_HandleKeyCmd(int keycmd, int dunno) { ... void* Obj = GetObjByStr("CTID"); byte byIsChatOpen; ReadProcMem((char*)Obj+1337, &byIsChatOpen, 1); if(byIsChatOpen) return; ...
-
I don’t understand all this use of ReadProcMem. Plugins are loaded into the address space of the process, so there’s no need for it. What’s wrong with just:
struct SomeObjectType { byte todo[0x1337]; bool bIsOpen; // byte more_todo[]; }; typedef SomeObjectType* (*_GetObjByStr)(const char*); SomeObjectType* obj = _GetObjByStr("CTID"); if (obj->bIsOpen) return;
-
An update to the code so that a keypress that is processed by a custom command will not be printed into the chat box, if the chat box is open.
bool __stdcall HkCb_HandleKeyCmd(int keycmd, int dunno) { switch (keycmd) case blah: return true; // if you process it default: return false; // if you don't } __declspec(naked) void HkCb_HandleKeyCmdNaked(void) { __asm { push [esp+8] push [esp+8] call HkCb_HandleKeyCmd test al, al jz cmd_not_processed ret cmd_not_processed: sub esp, 80h push ebx mov ecx, 0x00576417 jmp ecx } }
I guess there’s no reason to have this in the private section. I’m not sure why I put it here so if a site admin wants to move it then do so please….
-
A bit of a grave dig, but I was wondering if anybody knows the location of the function for handling the release of input.
From what I can tell, any key defined inside of keymap.ini with a state of “keydown, keyup” will only fire an event in the above function for the initial keydown state. This is great for intercepting calls, but sometimes I want to supress the cancelation.