aboutsummaryrefslogtreecommitdiff
path: root/src/parsec/con_kmap.cpp
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
committerFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
commitc068f22329d5cc722622a2183bbb22eef2093df7 (patch)
tree12d56c1aede67988a55e241364606bfbb4dba933 /src/parsec/con_kmap.cpp
downloadopenparsec-c068f22329d5cc722622a2183bbb22eef2093df7.tar.xz
openparsec-c068f22329d5cc722622a2183bbb22eef2093df7.zip
initial importHEADmain
Diffstat (limited to 'src/parsec/con_kmap.cpp')
-rw-r--r--src/parsec/con_kmap.cpp976
1 files changed, 976 insertions, 0 deletions
diff --git a/src/parsec/con_kmap.cpp b/src/parsec/con_kmap.cpp
new file mode 100644
index 0000000..c0ac528
--- /dev/null
+++ b/src/parsec/con_kmap.cpp
@@ -0,0 +1,976 @@
+/*
+ * PARSEC - Keymapping Commands
+ *
+ * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:34 $
+ *
+ * Orginally written by:
+ * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-1999
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// C library
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// compilation flags/debug support
+#include "config.h"
+#include "debug.h"
+
+// general definitions
+#include "general.h"
+#include "objstruc.h"
+
+// global externals
+#include "globals.h"
+
+// subsystem headers
+#include "inp_defs.h"
+
+// local module header
+#include "con_kmap.h"
+
+// proprietary module headers
+#include "con_ext.h"
+#include "con_list.h"
+#include "con_main.h"
+#include "con_std.h"
+#include "keycodes.h"
+
+
+
+// generic string paste area --------------------------------------------------
+//
+#define PASTE_STR_LEN 255
+static char paste_str[ PASTE_STR_LEN + 1 ];
+
+
+// string constants -----------------------------------------------------------
+//
+static char keymap_syntax[] = "syntax: key <keyname> <command>";
+static char bind_syntax[] = "syntax: bind <keyname> <command>";
+static char invalid_key[] = "invalid keyname.";
+static char invalid_func[] = "invalid key function specifier.";
+static char invalid_mkc[] = "invalid make code specifier.";
+static char invalid_akc[] = "invalid key code specifier.";
+static char too_many_keyadds[] = "too many additional key assignments.";
+
+
+// current keyboard command mappings (console commands) -----------------------
+//
+keycom_s key_com_mappings[ KEY_ADDITIONS_MAX ];
+
+
+// key names for additional mappings ------------------------------------------
+//
+textkeymap_s key_com_names[] = {
+
+ { AKC_ESCAPE, "akc_escape" },
+ { AKC_SPACE, "akc_space" },
+ { AKC_ENTER, "akc_enter" },
+ { AKC_CURSORUP, "akc_cursorup" },
+ { AKC_CURSORDOWN, "akc_cursordown" },
+ { AKC_CURSORLEFT, "akc_cursorleft" },
+ { AKC_CURSORRIGHT, "akc_cursorright" },
+ { AKC_BACKSPACE, "akc_backspace" },
+ { AKC_INSERT, "akc_insert" },
+ { AKC_DELETE, "akc_delete" },
+ { AKC_HOME, "akc_home" },
+ { AKC_END, "akc_end" },
+ { AKC_PAGEUP, "akc_pageup" },
+ { AKC_PAGEDOWN, "akc_pagedown" },
+ { AKC_CAPSLOCK, "akc_capslock" },
+ { AKC_TILDE, "akc_tilde" },
+
+ { AKC_MINUS, "akc_minus" },
+ { AKC_EQUALS, "akc_equals" },
+ { AKC_TAB, "akc_tab" },
+ { AKC_LBRACKET, "akc_lbracket" },
+ { AKC_RBRACKET, "akc_rbracket" },
+ { AKC_SEMICOLON, "akc_semicolon" },
+ { AKC_APOSTROPHE, "akc_apostrophe" },
+ { AKC_GRAVE, "akc_grave" },
+
+ { AKC_LSHIFT, "akc_lshift" },
+ { AKC_RSHIFT, "akc_rshift" },
+ { AKC_LCONTROL, "akc_lcontrol" },
+ { AKC_RCONTROL, "akc_rcontrol" },
+ { AKC_LALT, "akc_lalt" },
+ { AKC_RALT, "akc_ralt" },
+
+ { AKC_COMMA, "akc_comma" },
+ { AKC_PERIOD, "akc_period" },
+ { AKC_SLASH, "akc_slash" },
+
+ { AKC_BACKSLASH, "akc_backslash" },
+ { AKC_GERARROWS, "akc_gerarrows" },
+
+ { AKC_NUMPADSLASH, "akc_numpadslash" },
+ { AKC_NUMPADSTAR, "akc_numpadstar" },
+ { AKC_NUMPADMINUS, "akc_numpadminus" },
+ { AKC_NUMPADPLUS, "akc_numpadplus" },
+ { AKC_NUMPADPERIOD, "akc_numpadperiod" },
+ { AKC_NUMLOCK, "akc_numlock" },
+ { AKC_SCROLL, "akc_scroll" },
+
+ { AKC_NUMPAD0, "akc_numpad0" },
+ { AKC_NUMPAD1, "akc_numpad1" },
+ { AKC_NUMPAD2, "akc_numpad2" },
+ { AKC_NUMPAD3, "akc_numpad3" },
+ { AKC_NUMPAD4, "akc_numpad4" },
+ { AKC_NUMPAD5, "akc_numpad5" },
+ { AKC_NUMPAD6, "akc_numpad6" },
+ { AKC_NUMPAD7, "akc_numpad7" },
+ { AKC_NUMPAD8, "akc_numpad8" },
+ { AKC_NUMPAD9, "akc_numpad9" },
+
+ { AKC_LWIN, "akc_lwin" },
+ { AKC_RWIN, "akc_rwin" },
+ { AKC_APPS, "akc_apps" },
+
+ { AKC_ENTER_GRAY, "akc_enter_gray" },
+ { AKC_CURSORUP_GRAY, "akc_cursorup_gray" },
+ { AKC_CURSORDOWN_GRAY, "akc_cursordown_gray" },
+ { AKC_CURSORLEFT_GRAY, "akc_cursorleft_gray" },
+ { AKC_CURSORRIGHT_GRAY, "akc_cursorright_gray" },
+ { AKC_BACKSPACE_GRAY, "akc_backspace_gray" },
+ { AKC_INSERT_GRAY, "akc_insert_gray" },
+ { AKC_DELETE_GRAY, "akc_delete_gray" },
+ { AKC_HOME_GRAY, "akc_home_gray" },
+ { AKC_END_GRAY, "akc_end_gray" },
+ { AKC_PAGEUP_GRAY, "akc_pageup_gray" },
+ { AKC_PAGEDOWN_GRAY, "akc_pagedown_gray" },
+
+ { AKC_A, "akc_a" },
+ { AKC_B, "akc_b" },
+ { AKC_C, "akc_c" },
+ { AKC_D, "akc_d" },
+ { AKC_E, "akc_e" },
+ { AKC_F, "akc_f" },
+ { AKC_G, "akc_g" },
+ { AKC_H, "akc_h" },
+ { AKC_I, "akc_i" },
+ { AKC_J, "akc_j" },
+ { AKC_K, "akc_k" },
+ { AKC_L, "akc_l" },
+ { AKC_M, "akc_m" },
+ { AKC_N, "akc_n" },
+ { AKC_O, "akc_o" },
+ { AKC_P, "akc_p" },
+ { AKC_Q, "akc_q" },
+ { AKC_R, "akc_r" },
+ { AKC_S, "akc_s" },
+ { AKC_T, "akc_t" },
+ { AKC_U, "akc_u" },
+ { AKC_V, "akc_v" },
+ { AKC_W, "akc_w" },
+ { AKC_X, "akc_x" },
+ { AKC_Y, "akc_y" },
+ { AKC_Z, "akc_z" },
+
+ { AKC_A_SHIFTED, "akc_a_shifted" },
+ { AKC_B_SHIFTED, "akc_b_shifted" },
+ { AKC_C_SHIFTED, "akc_c_shifted" },
+ { AKC_D_SHIFTED, "akc_d_shifted" },
+ { AKC_E_SHIFTED, "akc_e_shifted" },
+ { AKC_F_SHIFTED, "akc_f_shifted" },
+ { AKC_G_SHIFTED, "akc_g_shifted" },
+ { AKC_H_SHIFTED, "akc_h_shifted" },
+ { AKC_I_SHIFTED, "akc_i_shifted" },
+ { AKC_J_SHIFTED, "akc_j_shifted" },
+ { AKC_K_SHIFTED, "akc_k_shifted" },
+ { AKC_L_SHIFTED, "akc_l_shifted" },
+ { AKC_M_SHIFTED, "akc_m_shifted" },
+ { AKC_N_SHIFTED, "akc_n_shifted" },
+ { AKC_O_SHIFTED, "akc_o_shifted" },
+ { AKC_P_SHIFTED, "akc_p_shifted" },
+ { AKC_Q_SHIFTED, "akc_q_shifted" },
+ { AKC_R_SHIFTED, "akc_r_shifted" },
+ { AKC_S_SHIFTED, "akc_s_shifted" },
+ { AKC_T_SHIFTED, "akc_t_shifted" },
+ { AKC_U_SHIFTED, "akc_u_shifted" },
+ { AKC_V_SHIFTED, "akc_v_shifted" },
+ { AKC_W_SHIFTED, "akc_w_shifted" },
+ { AKC_X_SHIFTED, "akc_x_shifted" },
+ { AKC_Y_SHIFTED, "akc_y_shifted" },
+ { AKC_Z_SHIFTED, "akc_z_shifted" },
+
+ { AKC_0, "akc_0" },
+ { AKC_1, "akc_1" },
+ { AKC_2, "akc_2" },
+ { AKC_3, "akc_3" },
+ { AKC_4, "akc_4" },
+ { AKC_5, "akc_5" },
+ { AKC_6, "akc_6" },
+ { AKC_7, "akc_7" },
+ { AKC_8, "akc_8" },
+ { AKC_9, "akc_9" },
+
+ { AKC_0_SHIFTED, "akc_0_shifted" },
+ { AKC_1_SHIFTED, "akc_1_shifted" },
+ { AKC_2_SHIFTED, "akc_2_shifted" },
+ { AKC_3_SHIFTED, "akc_3_shifted" },
+ { AKC_4_SHIFTED, "akc_4_shifted" },
+ { AKC_5_SHIFTED, "akc_5_shifted" },
+ { AKC_6_SHIFTED, "akc_6_shifted" },
+ { AKC_7_SHIFTED, "akc_7_shifted" },
+ { AKC_8_SHIFTED, "akc_8_shifted" },
+ { AKC_9_SHIFTED, "akc_9_shifted" },
+
+ { AKC_F1, "akc_f1" },
+ { AKC_F2, "akc_f2" },
+ { AKC_F3, "akc_f3" },
+ { AKC_F4, "akc_f4" },
+ { AKC_F5, "akc_f5" },
+ { AKC_F6, "akc_f6" },
+ { AKC_F7, "akc_f7" },
+ { AKC_F8, "akc_f8" },
+ { AKC_F9, "akc_f9" },
+ { AKC_F10, "akc_f10" },
+ { AKC_F11, "akc_f11" },
+ { AKC_F12, "akc_f12" },
+
+ { AKC_F1_SHIFTED, "akc_f1_shifted" },
+ { AKC_F2_SHIFTED, "akc_f2_shifted" },
+ { AKC_F3_SHIFTED, "akc_f3_shifted" },
+ { AKC_F4_SHIFTED, "akc_f4_shifted" },
+ { AKC_F5_SHIFTED, "akc_f5_shifted" },
+ { AKC_F6_SHIFTED, "akc_f6_shifted" },
+ { AKC_F7_SHIFTED, "akc_f7_shifted" },
+ { AKC_F8_SHIFTED, "akc_f8_shifted" },
+ { AKC_F9_SHIFTED, "akc_f9_shifted" },
+ { AKC_F10_SHIFTED, "akc_f10_shifted" },
+ { AKC_F11_SHIFTED, "akc_f11_shifted" },
+ { AKC_F12_SHIFTED, "akc_f12_shifted" },
+
+ { AKC_JOY_BUTTON1, "akc_joy_button1" },
+ { AKC_JOY_BUTTON2, "akc_joy_button2" },
+ { AKC_JOY_BUTTON3, "akc_joy_button3" },
+ { AKC_JOY_BUTTON4, "akc_joy_button4" },
+ { AKC_JOY_BUTTON5, "akc_joy_button5" },
+ { AKC_JOY_BUTTON6, "akc_joy_button6" },
+ { AKC_JOY_BUTTON7, "akc_joy_button7" },
+ { AKC_JOY_BUTTON8, "akc_joy_button8" },
+
+};
+
+#define AKC_SIGNATURE "akc_"
+#define AKC_SIGNATURE_LEN 4
+#define NUM_AKC_CODES CALC_NUM_ARRAY_ENTRIES( key_com_names )
+
+
+// key names for functional mappings (make codes) -----------------------------
+//
+textkeymap_s make_codes[] = {
+
+ { MKC_ESCAPE, "mkc_escape" },
+ { MKC_SPACE, "mkc_space" },
+ { MKC_ENTER, "mkc_enter" },
+ { MKC_CURSORUP, "mkc_cursorup" },
+ { MKC_CURSORDOWN, "mkc_cursordown" },
+ { MKC_CURSORLEFT, "mkc_cursorleft" },
+ { MKC_CURSORRIGHT, "mkc_cursorright" },
+ { MKC_BACKSPACE, "mkc_backspace" },
+ { MKC_INSERT, "mkc_insert" },
+ { MKC_DELETE, "mkc_delete" },
+ { MKC_HOME, "mkc_home" },
+ { MKC_END, "mkc_end" },
+ { MKC_PAGEUP, "mkc_pageup" },
+ { MKC_PAGEDOWN, "mkc_pagedown" },
+ { MKC_CAPSLOCK, "mkc_capslock" },
+ { MKC_TILDE, "mkc_tilde" },
+
+ { MKC_MINUS, "mkc_minus" },
+ { MKC_EQUALS, "mkc_equals" },
+ { MKC_TAB, "mkc_tab" },
+ { MKC_LBRACKET, "mkc_lbracket" },
+ { MKC_RBRACKET, "mkc_rbracket" },
+ { MKC_SEMICOLON, "mkc_semicolon" },
+ { MKC_APOSTROPHE, "mkc_apostrophe" },
+ { MKC_GRAVE, "mkc_grave" },
+
+ { MKC_LSHIFT, "mkc_lshift" },
+ { MKC_RSHIFT, "mkc_rshift" },
+ { MKC_LCONTROL, "mkc_lcontrol" },
+ { MKC_RCONTROL, "mkc_rcontrol" },
+ { MKC_LALT, "mkc_lalt" },
+ { MKC_RALT, "mkc_ralt" },
+
+ { MKC_COMMA, "mkc_comma" },
+ { MKC_PERIOD, "mkc_period" },
+ { MKC_SLASH, "mkc_slash" },
+
+ { MKC_BACKSLASH, "mkc_backslash" },
+ { MKC_GERARROWS, "mkc_gerarrows" },
+
+ { MKC_NUMPADSLASH, "mkc_numpadslash" },
+ { MKC_NUMPADSTAR, "mkc_numpadstar" },
+ { MKC_NUMPADMINUS, "mkc_numpadminus" },
+ { MKC_NUMPADPLUS, "mkc_numpadplus" },
+ { MKC_NUMPADPERIOD, "mkc_numpadperiod" },
+ { MKC_NUMLOCK, "mkc_numlock" },
+ { MKC_SCROLL, "mkc_scroll" },
+
+ { MKC_NUMPAD0, "mkc_numpad0" },
+ { MKC_NUMPAD1, "mkc_numpad1" },
+ { MKC_NUMPAD2, "mkc_numpad2" },
+ { MKC_NUMPAD3, "mkc_numpad3" },
+ { MKC_NUMPAD4, "mkc_numpad4" },
+ { MKC_NUMPAD5, "mkc_numpad5" },
+ { MKC_NUMPAD6, "mkc_numpad6" },
+ { MKC_NUMPAD7, "mkc_numpad7" },
+ { MKC_NUMPAD8, "mkc_numpad8" },
+ { MKC_NUMPAD9, "mkc_numpad9" },
+
+ { MKC_LWIN, "mkc_lwin" },
+ { MKC_RWIN, "mkc_rwin" },
+ { MKC_APPS, "mkc_apps" },
+
+ { MKC_ENTER_GRAY, "mkc_enter_gray" },
+ { MKC_CURSORUP_GRAY, "mkc_cursorup_gray" },
+ { MKC_CURSORDOWN_GRAY, "mkc_cursordown_gray" },
+ { MKC_CURSORLEFT_GRAY, "mkc_cursorleft_gray" },
+ { MKC_CURSORRIGHT_GRAY, "mkc_cursorright_gray" },
+ { MKC_BACKSPACE_GRAY, "mkc_backspace_gray" },
+ { MKC_INSERT_GRAY, "mkc_insert_gray" },
+ { MKC_DELETE_GRAY, "mkc_delete_gray" },
+ { MKC_HOME_GRAY, "mkc_home_gray" },
+ { MKC_END_GRAY, "mkc_end_gray" },
+ { MKC_PAGEUP_GRAY, "mkc_pageup_gray" },
+ { MKC_PAGEDOWN_GRAY, "mkc_pagedown_gray" },
+
+ { MKC_A, "mkc_a" },
+ { MKC_B, "mkc_b" },
+ { MKC_C, "mkc_c" },
+ { MKC_D, "mkc_d" },
+ { MKC_E, "mkc_e" },
+ { MKC_F, "mkc_f" },
+ { MKC_G, "mkc_g" },
+ { MKC_H, "mkc_h" },
+ { MKC_I, "mkc_i" },
+ { MKC_J, "mkc_j" },
+ { MKC_K, "mkc_k" },
+ { MKC_L, "mkc_l" },
+ { MKC_M, "mkc_m" },
+ { MKC_N, "mkc_n" },
+ { MKC_O, "mkc_o" },
+ { MKC_P, "mkc_p" },
+ { MKC_Q, "mkc_q" },
+ { MKC_R, "mkc_r" },
+ { MKC_S, "mkc_s" },
+ { MKC_T, "mkc_t" },
+ { MKC_U, "mkc_u" },
+ { MKC_V, "mkc_v" },
+ { MKC_W, "mkc_w" },
+ { MKC_X, "mkc_x" },
+ { MKC_Y, "mkc_y" },
+ { MKC_Z, "mkc_z" },
+
+ { MKC_0, "mkc_0" },
+ { MKC_1, "mkc_1" },
+ { MKC_2, "mkc_2" },
+ { MKC_3, "mkc_3" },
+ { MKC_4, "mkc_4" },
+ { MKC_5, "mkc_5" },
+ { MKC_6, "mkc_6" },
+ { MKC_7, "mkc_7" },
+ { MKC_8, "mkc_8" },
+ { MKC_9, "mkc_9" },
+
+ { MKC_F1, "mkc_f1" },
+ { MKC_F2, "mkc_f2" },
+ { MKC_F3, "mkc_f3" },
+ { MKC_F4, "mkc_f4" },
+ { MKC_F5, "mkc_f5" },
+ { MKC_F6, "mkc_f6" },
+ { MKC_F7, "mkc_f7" },
+ { MKC_F8, "mkc_f8" },
+ { MKC_F9, "mkc_f9" },
+ { MKC_F10, "mkc_f10" },
+ { MKC_F11, "mkc_f11" },
+ { MKC_F12, "mkc_f12" },
+
+};
+
+#define NUM_MKC_CODES CALC_NUM_ARRAY_ENTRIES( make_codes )
+
+
+// functional keyboard function names -----------------------------------------
+//
+const char *const functional_keys[] = {
+
+ "func_", // signature (prefix) for all game function specs
+
+ "escape",
+ "turnleft",
+ "turnright",
+ "divedown",
+ "pullup",
+ "rollleft",
+ "rollright",
+ "shootweapon",
+ "launchmissile",
+ "nextweapon",
+ "nextmissile",
+ "accelerate",
+ "decelerate",
+ "slideleft",
+ "slideright",
+ "slideup",
+ "slidedown",
+ "nexttarget",
+ "toggleframerate",
+ "toggleobjcamera",
+ "togglehelp",
+ "toggleconsole",
+ "savescreenshot",
+ "showkillstats",
+ "speedzero",
+ "targetspeed",
+ "afterburner",
+ "fronttarget",
+ "select",
+ "cursorleft",
+ "cursorright",
+ "cursorup",
+ "cursordown",
+};
+
+#define NUM_FUNCTIONAL_KEYS CALC_NUM_ARRAY_ENTRIES( functional_keys )
+#define FUNC_SIG_LEN 5 //strlen(functional_keys[0])
+
+
+// table of keys for which a gray equivalent exists ---------------------------
+//
+dword dup_gray_keys[] = {
+
+ MKC_ENTER, MKC_ENTER_GRAY,
+ MKC_CURSORUP, MKC_CURSORUP_GRAY,
+ MKC_CURSORDOWN, MKC_CURSORDOWN_GRAY,
+ MKC_CURSORLEFT, MKC_CURSORLEFT_GRAY,
+ MKC_CURSORRIGHT, MKC_CURSORRIGHT_GRAY,
+ MKC_BACKSPACE, MKC_BACKSPACE_GRAY,
+ MKC_INSERT, MKC_INSERT_GRAY,
+ MKC_DELETE, MKC_DELETE_GRAY,
+ MKC_HOME, MKC_HOME_GRAY,
+ MKC_END, MKC_END_GRAY,
+ MKC_PAGEUP, MKC_PAGEUP_GRAY,
+ MKC_PAGEDOWN, MKC_PAGEDOWN_GRAY,
+
+ MKC_NIL, MKC_NIL,
+};
+
+
+// get text description for AKC_ code -----------------------------------------
+//
+char *GetAKCDescription( int num )
+{
+ ASSERT( num >= 0 );
+ ASSERT( ( num < KEY_ADDITIONS_MAX ) && ( num < KeyAdditional->size ) );
+
+ //NOTE:
+ // this function is used by CON_LIST::DispKeyBinding()
+ // in order to display a text description of an
+ // assignable key-code (AKC_ code).
+ // (command "listbindings".)
+
+ keyaddition_s *kap = &KeyAdditional->table[num];
+
+ for ( dword i = 0; i < NUM_AKC_CODES; i++ ) {
+ if ( kap->code == (dword)key_com_names[ i ].code ) {
+ return (char *) key_com_names[ i ].text;
+ }
+ }
+
+ // if code not found in table display it in hex
+ sprintf( paste_str, "0x%06x", kap->code );
+ return paste_str;
+}
+
+
+// get text description of game function --------------------------------------
+//
+char *GetGameFuncDescription( int num )
+{
+ ASSERT( ( num >= 0 ) && ( (dword)num < NUM_FUNCTIONAL_KEYS - 1 ) );
+
+ //NOTE:
+ // this function is used by CON_LIST::DispKeyFunction()
+ // in order to display a text description of a
+ // game function.
+ // (command "listgamefunckeys".)
+
+ return (char *) functional_keys[ num + 1 ];
+}
+
+
+// get text description for MKC_ codes of function layers 1 and 2 -------------
+//
+char *GetMKCDescription( int num )
+{
+ ASSERT( ( num >= 0 ) && ( (dword)num < NUM_GAMEFUNC_KEYS ) );
+
+ //NOTE:
+ // this function is used by CON_LIST::DispKeyFunction()
+ // in order to display a text description of a key
+ // mapped to a game function.
+ // (command "listgamefunckeys".)
+
+ int *assigns1 = (int *) KeyAssignments;
+ int *assigns2 = (int *)&KeyAssignments[1];
+
+ const char *desc1 = NULL;
+ const char *desc2 = NULL;
+
+ for ( unsigned int i = 0; i < NUM_MKC_CODES; i++ ) {
+ if ( desc1 == NULL && assigns1[ num ] == make_codes[ i ].code )
+ desc1 = make_codes[ i ].text; // assume it's the first available name that matches the keycode
+ if ( assigns2[ num ] == make_codes[ i ].code )
+ desc2 = make_codes[ i ].text; // assume secondary assignment is the last available name that matches the keycode
+ }
+
+ //NOTE:
+ // if a MKC_ code has been specified as byte rather than
+ // a string constant and this byte has no corresponding
+ // string constant, then the byte-code will be displayed
+ // instead of a key specifier.
+
+ char bytecode1[ 8 ];
+ char bytecode2[ 8 ];
+
+ if ( ( desc1 == NULL ) && ( assigns1[ num ] != MKC_NIL ) ) {
+ sprintf( bytecode1, "0x%02x", assigns1[ num ] );
+ desc1 = bytecode1;
+ }
+ if ( ( desc2 == NULL ) && ( assigns2[ num ] != MKC_NIL ) ) {
+ sprintf( bytecode2, "0x%02x", assigns2[ num ] );
+ desc2 = bytecode2;
+ }
+
+ // field-width for layer1 description
+ #define LAYER1_DESC_PAD_SIZE 14
+
+ if ( ( desc1 != NULL ) && ( desc2 != NULL ) ) {
+ sprintf( paste_str, "l1: %s ", desc1 );
+ int descpad = LAYER1_DESC_PAD_SIZE - strlen( desc1 );
+ char *ppad = paste_str + strlen( desc1 ) + 5;
+ while ( descpad-- > 0 )
+ *ppad++ = ' ';
+ sprintf( ppad, "l2: %s", desc2 );
+ } else if ( desc1 != NULL ) {
+ sprintf( paste_str, "l1: %s ", desc1 );
+ int descpad = LAYER1_DESC_PAD_SIZE - strlen( desc1 );
+ char *ppad = paste_str + strlen( desc1 ) + 5;
+ while ( descpad-- > 0 )
+ *ppad++ = ' ';
+ strcpy( ppad, "l2: unassigned" );
+ } else if ( desc2 != NULL ) {
+ sprintf( paste_str, "l1: unassigned l2: %s", desc2 );
+ } else {
+ sprintf( paste_str, "function unassigned" );
+ }
+
+ return paste_str;
+}
+
+
+// get standard key for gray key (or vice versa) ------------------------------
+//
+PRIVATE
+dword GrayEquiv( dword kcode )
+{
+ //NOTE:
+ // e.g., this function would return MKC_ENTER_GRAY
+ // for argument MKC_ENTER, and MKC_ENTER for
+ // argument MKC_ENTER_GRAY.
+
+ //NOTE:
+ // on a pc this function would only need to invert bit 7
+ // to toggle between gray/non-gray, but this would
+ // not be portable. hence, simple table lookup is used.
+
+ for ( int i = 0; dup_gray_keys[ i ] != MKC_NIL; i+=2 ) {
+ if ( dup_gray_keys[ i ] == kcode )
+ return dup_gray_keys[ i + 1 ];
+ if ( dup_gray_keys[ i + 1 ] == kcode )
+ return dup_gray_keys[ i ];
+ }
+
+ return MKC_NIL;
+}
+
+
+// get MKC_ code for textually specified MKC_ code identifier -----------------
+//
+PRIVATE
+dword ReadMKC( char *scan )
+{
+ //NOTE:
+ // MKC_ codes may be specified via a string constant
+ // like MKC_ESCAPE, or directly via a byte-code as
+ // hex-byte, like 0x01.
+
+ if ( scan != NULL ) {
+
+ // if string starts with "0x" convert as byte
+ if ( ( scan[ 0 ] == '0' ) && ( scan[ 1 ] == 'x' ) ) {
+ char *remptr;
+ long code = strtol( scan + 2, &remptr, 16 );
+ if ( ( *remptr != 0 ) || ( code < 0 ) ) {
+ CON_AddLine( invalid_mkc );
+ return MKC_NIL;
+ }
+ return (dword) code;
+ }
+
+ // check if string is valid identifier starting with "MKC_"
+ for ( unsigned int i = 0; i < NUM_MKC_CODES; i++ ) {
+ if ( strcmp( make_codes[ i ].text, scan ) == 0 )
+ return make_codes[ i ].code;
+ }
+
+ CON_AddLine( invalid_mkc );
+
+ // if supplied code is invalid deassign mapping
+ return MKC_NIL;
+
+ } else {
+
+ // if no code is specified deassign mapping
+ return MKC_NIL;
+ }
+}
+
+
+// bind console command to specific key (via FUNC_ or AKC_ key specifier) -----
+//
+PRIVATE
+int CheckKeyMappingBase( char *scan, int echo )
+{
+ ASSERT( scan != NULL );
+ ASSERT( ( echo == TRUE ) || ( echo == FALSE ) );
+
+ // fetch keyspec and command
+ char *key = scan;
+ scan = strtok( NULL, "" );
+
+ // check functional keys (FUNC_, FUNC_1_, FUNC_2_)
+ if ( strncmp( key, functional_keys[ 0 ], FUNC_SIG_LEN ) == 0 ) {
+
+ //NOTE:
+ // the two functional layers can either be mapped
+ // independently, or simultaneously (for gray keys).
+ // e.g., the primary keyspec for escape would be
+ // FUNC_1_ESCAPE. for the alternate (second) layer
+ // it would be FUNC_2_ESCAPE. if the second layer
+ // should not be utilized, FUNC_ESCAPE may be
+ // used. then, if the specified key has a gray
+ // equivalent, this is put into the second layer,
+ // otherwise the alternate assignment will be deleted.
+
+ dword *layer = (dword *) KeyAssignments;
+ char *cont = key + FUNC_SIG_LEN;
+ int setboth = FALSE;
+
+ if ( ( key[ FUNC_SIG_LEN ] == '1' ) && ( key[ FUNC_SIG_LEN + 1 ] == '_' ) ) {
+ // select layer 1 and skip 1_ prefix
+ cont += 2;
+ } else if ( ( key[ FUNC_SIG_LEN ] == '2' ) && ( key[ FUNC_SIG_LEN + 1 ] == '_' ) ) {
+ // select layer 2 and skip 2_ prefix
+ layer += NUM_GAMEFUNC_KEYS;
+ cont += 2;
+ } else {
+ setboth = TRUE;
+ }
+
+ //NOTE:
+ // the second parameter for mapping functional keys
+ // is not a command, but a MKC_ keyspec. also, the
+ // echo flag is not used, i.e., the BIND and KEY
+ // commands are identical in this case.
+ //
+ // mapping the primary key for the escape-function
+ // to the <escape> key would be:
+ // "BIND FUNC_1_ESCAPE MKC_ESCAPE", or, equivalently,
+ // "KEY FUNC_1_ESCAPE MKC_ESCAPE".
+
+ // eat whitespace
+ while ( *scan == ' ' )
+ scan++;
+
+ // cut off trailing whitespace
+ char *rscan = scan;
+ while ( *rscan != 0 )
+ rscan++;
+ while ( *--rscan == ' ' )
+ {}
+ *( rscan + 1 ) = 0;
+
+ int j = -1;
+ for ( unsigned int i = 1; i < NUM_FUNCTIONAL_KEYS; i++ ) {
+ if ( strcmp( functional_keys[ i ], cont ) == 0 ) {
+ // set first layer (may be 1 or 2)
+ layer[ j = i - 1 ] = ReadMKC( scan );
+ // also set second layer if FUNC_ was used (always 2)
+ if ( setboth )
+ layer[ NUM_GAMEFUNC_KEYS + j ] = GrayEquiv( layer[ j ] );
+ break;
+ }
+ }
+ if ( j == -1 )
+ CON_AddLine( invalid_func );
+
+ return 1;
+ }
+
+ // check additional keys (AKC_xx)
+ if ( strncmp( key, AKC_SIGNATURE, AKC_SIGNATURE_LEN ) == 0 ) {
+ for ( dword i = 0; i < NUM_AKC_CODES; i++ ) {
+ if ( strcmp( key_com_names[ i ].text, key ) == 0 ) {
+
+ // get code and alloc command string mem
+ dword code = key_com_names[ i ].code;
+ char *com = scan ? (char *) ALLOCMEM( strlen( scan ) + 1 ) : NULL;
+
+ // copy command into floating string
+ if ( com != NULL )
+ strcpy( com, scan );
+
+ // search for already existing entry
+ int entryindx = -1;
+ keyaddition_s *kap = &KeyAdditional->table[0];
+ for ( i = 0; i < (unsigned int)KeyAdditional->size; i++, kap++ ) {
+ if ( kap->code == code ) {
+ entryindx = i;
+ break;
+ }
+ }
+ if ( entryindx == -1 ) {
+ if ( KeyAdditional->size == KEY_ADDITIONS_MAX ) {
+ // don't allow more additional key assignments than
+ // the static table can accommodate
+ CON_AddLine( too_many_keyadds );
+ return 1;
+ }
+ // create new entry
+ entryindx = KeyAdditional->size++;
+ kap->code = code;
+ kap->state = 0;
+ } else if ( key_com_mappings[ entryindx ].command != NULL ) {
+ // free command string assigned previously
+ FREEMEM( key_com_mappings[ entryindx ].command );
+ }
+
+ //NOTE:
+ // the echo flag is stored along with the command and
+ // will be used by ExecBoundKeyCommands() to determine
+ // whether the command should be displayed and entered
+ // into the history list, or not.
+
+ key_com_mappings[ entryindx ].echo = echo;
+ key_com_mappings[ entryindx ].command = com;
+
+ return 1;
+ }
+ }
+
+ CON_AddLine( invalid_akc );
+ return 1;
+ }
+
+ CON_AddLine( invalid_key );
+ return 1;
+}
+
+
+// exec key map command (command "key") ---------------------------------------
+//
+int CheckKeyMappingEcho( char *scan )
+{
+ ASSERT( scan != NULL );
+
+ //NOTE:
+ //CONCOM:
+ // key_command ::= 'key' ( <key_spec> <command> | <func_spec> <mkc_code> )
+ // key_spec ::= 'akc_*'
+ // command ::= "arbitrary string, may contain whitespace"
+ // func_spec ::= 'func_*' | 'func_1_*' | 'func_2_*'
+ // mkc_code ::= 'mkc_*' | '0x' <hex-digit> <hex-digit> | <nil>
+ // hex-digit ::= [0-9a-f]
+ // nil ::= "if no mkc_code is supplied mapping will be deassigned."
+ //
+ // * in 'akc_*' means any of the strings in key_com_names[].
+ // * in 'func_*', 'func_1_*', and 'func_2_*' means any of the
+ // strings in functional_keys[].
+ // * in 'mkc_*' means any of the strings in make_codes[].
+ //
+ // if the mkc_code is specified as hex-byte it will be
+ // interpreted directly as make-code. i.e., this is highly
+ // system-dependent!
+
+ //NOTE:
+ // commands mapped to keys via the KEY command
+ // act just like the command were typed at the
+ // console prompt. i.e., the command itself will
+ // be displayed in the console along with its
+ // text-output, if any.
+ //
+ // these commands will also be inserted into
+ // the history list.
+
+ // check if keymap command
+ if ( strcmp( scan, CMSTR( CM_KEY ) ) != 0 )
+ return FALSE;
+
+ // keyspec + command must be supplied as parameters
+ if ( ( scan = strtok( NULL, " " ) ) == NULL ) {
+ CON_AddLine( keymap_syntax );
+ return TRUE;
+ }
+
+ // store binding with echo flag on
+ return CheckKeyMappingBase( scan, TRUE );
+}
+
+
+// exec silent key map command (command "bind") -------------------------------
+//
+int CheckKeyMappingSilent( char *scan )
+{
+ ASSERT( scan != NULL );
+
+ //NOTE:
+ //CONCOM:
+ // bind_command ::= 'bind' ( <key_spec> <command> | <func_spec> <mkc_code> )
+ // key_spec ::= 'akc_*'
+ // command ::= "arbitrary string, may contain whitespace"
+ // func_spec ::= 'func_*' | 'func_1_*' | 'func_2_*'
+ // mkc_code ::= 'mkc_*' | '0x' <hex-digit> <hex-digit> | <nil>
+ // hex-digit ::= [0-9a-f]
+ // nil ::= "if no mkc_code is supplied mapping will be deassigned."
+ //
+ // * in 'akc_*' means any of the strings in key_com_names[].
+ // * in 'func_*', 'func_1_*', and 'func_2_*' means any of the
+ // strings in functional_keys[].
+ // * in 'mkc_*' means any of the strings in make_codes[].
+ //
+ // if the mkc_code is specified as hex-byte it will be
+ // interpreted directly as make-code. i.e., this is highly
+ // system-dependent!
+
+ //NOTE:
+ // commands mapped to keys via the BIND command
+ // will be executed silently. i.e., the command
+ // will not be displayed in the console if the
+ // key gets pressed and the command is executed.
+ // if the command causes any text-output, this
+ // will still be displayed, however.
+ //
+ // these commands will NOT be inserted into
+ // the history list.
+
+ // check if bind command
+ if ( strcmp( scan, CMSTR( CM_BIND ) ) != 0 )
+ return FALSE;
+
+ // keyspec + command must be supplied as parameters
+ if ( ( scan = strtok( NULL, " " ) ) == NULL ) {
+ CON_AddLine( bind_syntax );
+ return TRUE;
+ }
+
+ // store binding with echo flag off
+ return CheckKeyMappingBase( scan, FALSE );
+}
+
+
+// exec console commands mapped to additional keys ----------------------------
+//
+void ExecBoundKeyCommands()
+{
+ //NOTE:
+ // this function is called every frame by
+ // the gameloop (G_MAIN.C) to check
+ // for depression of additionally mapped keys.
+
+ //NOTE:
+ // this function will do nothing if a list command
+ // is currently in progress in the console.
+
+ // if standard prompt active execute commands mapped to AKC_xx keys
+ if ( !await_keypress ) {
+
+ // test all additional keys
+ keyaddition_s *kap = &KeyAdditional->table[0];
+ for ( int aid = 0; aid < KeyAdditional->size; aid++, kap++ ) {
+
+ // execute command if key pressed
+ if ( kap->state ) {
+ kap->state = 0;
+
+ if ( key_com_mappings[ aid ].command == NULL )
+ continue;
+
+ int echo = key_com_mappings[ aid ].echo;
+ int oldbutt = con_bottom;
+
+ // remember old login check state
+ int oldcheckstate = con_no_login_check;
+
+ if ( echo ) {
+
+ // insert prompt and command if echo on
+ EraseConLine( con_bottom );
+ strcpy( paste_str, con_prompt );
+ strcat( paste_str, key_com_mappings[ aid ].command );
+ strcpy( con_lines[ con_bottom ], paste_str );
+ cursor_x = 0;
+ InsertCommandLog( key_com_mappings[ aid ].command );
+
+ } else {
+
+ // turn off login check for command if echo off
+ con_no_login_check = 1;
+ }
+
+ // execute command
+ ExecConsoleCommand( key_com_mappings[ aid ].command, echo );
+
+ // restore login check state
+ con_no_login_check = oldcheckstate;
+
+ // check for list command
+ if ( await_keypress ) {
+ CON_ListCtdPrompt();
+ } else if ( echo || ( con_bottom != oldbutt ) ) {
+
+ //TODO:
+ //CAVEAT:
+ // if a command outputs exactly as many lines in one
+ // run as the console can contain, con_bottom will
+ // wrap around to itself and the output will not
+ // be detected!
+
+ CON_ListEndPrompt();
+ }
+ }
+ }
+ }
+}
+
+
+