#include <stdio.h>
#include <strings.h>
/*
this program will generate a keyboard macro file that can be uploaded to the maxkeybaord nighthawk x8.
The file can be uploaded to the keyboard by importing via the maxkeyboard "Gaming keyboard config" software.
*
*
usage: binaryname < inputfile of text you want to type
Maxkeyboard nighthawk x8 Macro generator
Todo..
* figure out the upload protocol and validation
* write a linux program to upload the macro
* figure out how to upload data to the flash memory in the 8 bit micro controller Freescale MC9S08JM16
http://cache.freescale.com/files/microcontrollers/doc/data_sheet/MC9S08JM16.pdf
USB-LITE Stack by CMX and the USB-MINI Stack by Freescale
* capture and analyse the usbdata using usbpcap http://desowin.org/usbpcap/ and wireshark //done
* write a http://desowin.org/usbpcap/dissectors.html
http://www.maxkeyboard.com/download.html
Details regarding the file format that the Maykeyboard software uses.
The config file is a plain text file
each macro is stored as a series of bytes
sample blank config with the 11202 zeros redacted...
[Setting]
MacroLength=0
DiableWinKey=0
[BUTTONCONFIG0]
FunID=255
Mappinkey=255
[MACROG0]
RepeatOption=0
Macro={hex values for the macro + a checksum}
MacroLength=0
[BUTTONCONFIG1]
FunID=255
Mappinkey=255
[MACROG1]
RepeatOption=0
Macro={hex values for the macro + a checksum}
MacroLength=0
[BUTTONCONFIG2]
FunID=255
Mappinkey=255
[MACROG2]
RepeatOption=0
Macro={hex values for the macro + a checksum}
MacroLength=0
[BUTTONCONFIG0] the macro number/id
FunID=32 (seems to indicate keyboard macro)
Mappinkey= this is the physical key on the keyboard that is assigned to this macro, 1 = F1 , 2 = F2 etc
[MACROG0] the macro group this macro belongs to ( I haven't used this yet)
RepeatOption=1 this needs to be set to 1 or the macro will not run
Macro= a single line with 11204 chars, seems to be divided up into 700 x 16 byte words.. the last 2 chars on this line seem to be a checksum that isn't validated (it changes when anything else changes)
MacroLength= a count of the 16 char words,this needs to be set based on the line above
# The Macro format.........
each char that requires a single key press takes 4 x 16 byte words
.1 key value press
.2 key time down
.3 key value release
.4 key time release
## if the inputed key requires 2 key presses eg.. upper case or special chars !@#...... then 8 x 16 byte words are needed
.1 shift key value press
.2 shift time down
.3 key value press
.4 key time down
.5 key value release
.6 key time release
.7 shift key release
.8 shift time release
the timing is based on milliseconds and stored in hex
the largest timing value seems to be 6375 milliseconds or 18E7
eg
50 milliseconds = 32
100 milliseconds = 64
125 milliseconds = 7D
the values are stored in the file in little endian notation ( least significant byte first )
eg... the hex value 18E7 is stored as E718
Decimal = 6375
Hex = 18E7
In File = FF000000E718000000
example press of 'a' for 100 milliseconds with a 100 millisecond gap at end
X = value that can be changed
____________ ____________ ____________ ____________
/ \ / \ / \ / \
| Key to press || Time to press||Key to release|| Time after |
1400000001000000FF000000640000001400000000000000FF00000064000000
Key Value XX XX
Static value 0000000 000000 0000000 000000
Press/Release X X
Static value FF000000 FF000000
Time Value XXXX XXXX
example press of 'a' for 1000 milliseconds with a 100 millisecond gap at end
____________ ____________ ____________ ____________
/ \ / \ / \ / \
| Key to press || Time to press||Key to release|| Time after |
1400000001000000FF000000E60300001400000000000000FF00000064000000
Key Value XX XX
Static value 0000000 000000 0000000 000000
Press/Release X X
Static value FF000000 FF000000
Time Value XXXX XXXX
100mSec FF00000064000000
Key_press 00000001000000
key_release 00000000000000
to enter the exclamation mark ! on the US keyboard...requires that the shift key is pressed whilst the 1 key is pressed
shift key on, 8300000001000000FF00000012020000 //this time has to extend past the next key
1 key on and off, 1100000001000000FF0000005E0000001100000000000000FF00000048010000
shiftkey off, 8300000000000000FF00000000000000
below is a list of the key values for the US keyboard layout
key value
Incomplete list of the control keys
shift 83
capslk 1B
space 57
TAB 13
L CTRL 88
L ALT 7D
Enter 56
BKSPC 53
List of the keys that only require one key to the pressed
a 14
b 2F
c 26
d 24
e 22
f 2C
g 2D
h 35
i 3A
j 34
k 3C
l 44
m 36
n 37
o 42
p 4A
q 12
r 2A
s 1C
t 2B
u 32
v 2E
w 1A
x 1E
y 33
z 16
1 11
2 19
3 21
4 29
5 28
6 30
7 31
8 39
9 41
0 49
- 48
= 38
[ 4B
] 3B
\ 54
; 4C
' 4D
, 3E
. 46
/ 4F
` 10
57
Keys that require the shift key to be pressed simultaneously
~ 10
! 11
@ 19
# 21
$ 29
% 28
^ 30
& 31
* 39
( 41
) 49
_ 48
+ 38
{ 4B
} 3B
| 54
: 4C
" 4D
< 3E
> 46
? 4F
A 14
B 2F
C 26
D 24
E 22
F 2C
G 2D
H 35
I 3A
J 34
K 3C
L 44
M 36
N 37
O 42
P 4A
Q 12
R 2A
S 1C
T 2B
U 32
V 2E
W 1A
X 1E
Y 33
Z 16
each macro will be an array of 11204 chars, initialized with zeros,
monitoring the usb bus whilst uploading a config that contains mostly zeros
not seems to be transferred to the keyboard i assume that are only used my the maxkeyboard software
Macro length limit...
we are limited to 700 16Byte words in each macro ,
#1 key value press = 16 Byte word
#2 key time down = 16 Byte word
#3 key value release = 16 Byte word
#4 key time release = 16 Byte word
this equates to 175 key press and key release actions.
Note if you require multiple keys to be pressed simultaneously it will use more bytes eg the shift + key stated above
make sure you do not go over the 700 limit, leave a few bytes free.
If you enter a invalid macro config...sometimes all the keyboard macros stop working
performing a reset from the app or uploading an empty config does not reset the key board.
Strangely overwriting/applying a valid (previously exported) config containing multiple macros will restore the keyboards macro functions
*/
int main()
{
int c, char_tally, MacroLength;
unsigned char key_value;
int shift_status; //for future use if i decided to extend the shift press for sequential upper keys
char* key_press = "00000001000000";
char* key_release = "00000000000000";
// there are 4 different timings 25 milliseconds doesn't seem to work
// 50 milliseconds seems like a good mix between speed and consistency
// 100 milliseconds may be needed when working through multiple remote nested RDP citrix vnx etc..
// 150 milliseconds is to hold the shift down past the timing of the upper keys
char* key_time_down_25 = "FF00000019000000";
char* key_time_down_50 = "FF00000032000000";
char* key_time_down_100 = "FF00000064000000";
char* key_time_down_150 = "FF00000096000000";
char* key_time_release_25 = "FF00000019000000";
char* key_time_release_50 = "FF00000032000000";
char* key_time_release_100 = "FF00000064000000";
char* key_time_release_150 = "FF00000096000000";
char* shift_key_press = "8300000001000000";
char* shift_key_release = "8300000000000000";
char* zero_fill = "0000000000000000";
key_value = 0;
char_tally = 0;
MacroLength = 0;
shift_status = 0;
char single_lowwer(char c)
{
//printf("%hhX%s%s%hhX%s%s",key_value, key_press, key_time_down_100, key_value,key_release, key_time_release_100 );
printf("%hhX%s%s%hhX%s%s",key_value, key_press, key_time_down_50, key_value,key_release, key_time_release_50 );
MacroLength = MacroLength + 4;
}
char single_upper(char c)
{
printf("%s%s",shift_key_press, key_time_down_150 );
printf("%hhX%s%s%hhX%s%s", key_value, key_press, key_time_down_100, key_value,key_release, key_time_release_100 );
printf("%s%s", shift_key_release, key_time_release_50 );
MacroLength = MacroLength + 8;
}
/*I acknowledge that there are more efficient ways to organize this data,
such as using arrays, matrices, or other data structures.
I am also aware that a lengthy if/else statement are visually unappealing.
I generated the if/else statements from a bash script.
*/
while ((c= getchar()) != EOF) {
++char_tally;
if (c == 'a' ){
key_value = 0x14;
single_lowwer(c);
}
else if ( c == 'b' ){
key_value = 0x2F;
single_lowwer(c);
}
else if ( c == 'c' ){
key_value = 0x26;
single_lowwer(c);
}
else if ( c == 'd' ){
key_value = 0x24;
single_lowwer(c);
}
else if ( c == 'e' ){
key_value = 0x22;
single_lowwer(c);
}
else if ( c == 'f' ){
key_value = 0x2C;
single_lowwer(c);
}
else if ( c == 'g' ){
key_value = 0x2D;
single_lowwer(c);
}
else if ( c == 'h' ){
key_value = 0x35;
single_lowwer(c);
}
else if ( c == 'i' ){
key_value = 0x3A;
single_lowwer(c);
}
else if ( c == 'j' ){
key_value = 0x34;
single_lowwer(c);
}
else if ( c == 'k' ){
key_value = 0x3C;
single_lowwer(c);
}
else if ( c == 'l' ){
key_value = 0x44;
single_lowwer(c);
}
else if ( c == 'm' ){
key_value = 0x36;
single_lowwer(c);
}
else if ( c == 'n' ){
key_value = 0x37;
single_lowwer(c);
}
else if ( c == 'o' ){
key_value = 0x42;
single_lowwer(c);
}
else if ( c == 'p' ){
key_value = 0x4A;
single_lowwer(c);
}
else if ( c == 'q' ){
key_value = 0x12;
single_lowwer(c);
}
else if ( c == 'r' ){
key_value = 0x2A;
single_lowwer(c);
}
else if ( c == 's' ){
key_value = 0x1C;
single_lowwer(c);
}
else if ( c == 't' ){
key_value = 0x2B;
single_lowwer(c);
}
else if ( c == 'u' ){
key_value = 0x32;
single_lowwer(c);
}
else if ( c == 'v' ){
key_value = 0x2E;
single_lowwer(c);
}
else if ( c == 'w' ){
key_value = 0x1A;
single_lowwer(c);
}
else if ( c == 'x' ){
key_value = 0x1E;
single_lowwer(c);
}
else if ( c == 'y' ){
key_value = 0x33;
single_lowwer(c);
}
else if ( c == 'z' ){
key_value = 0x16;
single_lowwer(c);
}
else if ( c == '1' ){
key_value = 0x11;
single_lowwer(c);
}
else if ( c == '2' ){
key_value = 0x19;
single_lowwer(c);
}
else if ( c == '3' ){
key_value = 0x21;
single_lowwer(c);
}
else if ( c == '4' ){
key_value = 0x29;
single_lowwer(c);
}
else if ( c == '5' ){
key_value = 0x28;
single_lowwer(c);
}
else if ( c == '6' ){
key_value = 0x30;
single_lowwer(c);
}
else if ( c == '7' ){
key_value = 0x31;
single_lowwer(c);
}
else if ( c == '8' ){
key_value = 0x39;
single_lowwer(c);
}
else if ( c == '9' ){
key_value = 0x41;
single_lowwer(c);
}
else if ( c == '0' ){
key_value = 0x49;
single_lowwer(c);
}
else if ( c == '-' ){
key_value = 0x48;
single_lowwer(c);
}
else if ( c == '=' ){
key_value = 0x38;
single_lowwer(c);
}
else if ( c == '[' ){
key_value = 0x4B;
single_lowwer(c);
}
else if ( c == ']' ){
key_value = 0x3B;
single_lowwer(c);
}
else if ( c == '\\' ){
key_value = 0x54;
single_lowwer(c);
}
else if ( c == ';' ){
key_value = 0x4C;
single_lowwer(c);
}
else if ( c == '8' ){
key_value = 0x39;
single_lowwer(c);
}
else if ( c == '9' ){
key_value = 0x41;
single_lowwer(c);
}
else if ( c == '0' ){
key_value = 0x49;
single_lowwer(c);
}
else if ( c == '-' ){
key_value = 0x48;
single_lowwer(c);
}
else if ( c == '=' ){
key_value = 0x38;
single_lowwer(c);
}
else if ( c == '[' ){
key_value = 0x4B;
single_lowwer(c);
}
else if ( c == ']' ){
key_value = 0x3B;
single_lowwer(c);
}
else if ( c == '\\' ){
key_value = 0x54;
single_lowwer(c);
}
else if ( c == '\'' ){
key_value = 0x4D;
single_lowwer(c);
}
else if ( c == ',' ){
key_value = 0x3E;
single_lowwer(c);
}
else if ( c == '.' ){
key_value = 0x46;
single_lowwer(c);
}
else if ( c == '/' ){
key_value = 0x4F;
single_lowwer(c);
}
else if ( c == '`' ){
key_value = 0x10;
single_lowwer(c);
}
else if ( c == ' ' ){
key_value = 0x57;
single_lowwer(c);
}
//start of shift key
else if ( c == '~' ){
key_value = 0x10;
single_upper(c);
}
else if ( c == '!' ){
key_value = 0x11;
single_upper(c);
}
else if ( c == '@' ){
key_value = 0x19;
single_upper(c);
}
else if ( c == '#' ){
key_value = 0x21;
single_upper(c);
}
else if ( c == '$' ){
key_value = 0x29;
single_upper(c);
}
else if ( c == '%' ){
key_value = 0x28;
single_upper(c);
}
else if ( c == '^' ){
key_value = 0x30;
single_upper(c);
}
else if ( c == '&' ){
key_value = 0x31;
single_upper(c);
}
else if ( c == '*' ){
key_value = 0x39;
single_upper(c);
}
else if ( c == '(' ){
key_value = 0x41;
single_upper(c);
}
else if ( c == ')' ){
key_value = 0x49;
single_upper(c);
}
else if ( c == '_' ){
key_value = 0x48;
single_upper(c);
}
else if ( c == '+' ){
key_value = 0x38;
single_upper(c);
}
else if ( c == '{' ){
key_value = 0x4B;
single_upper(c);
}
else if ( c == '}' ){
key_value = 0x3B;
single_upper(c);
}
else if ( c == '|' ){
key_value = 0x54;
single_upper(c);
}
else if ( c == ':' ){
key_value = 0x4C;
single_upper(c);
}
else if ( c == '"' ){
key_value = 0x4D;
single_upper(c);
}
else if ( c == '<' ){
key_value = 0x3E;
single_upper(c);
}
else if ( c == '>' ){
key_value = 0x46;
single_upper(c);
}
else if ( c == '?' ){
key_value = 0x4F;
single_upper(c);
}
else if ( c == 'A' ){
key_value = 0x14;
single_upper(c);
}
else if ( c == 'B' ){
key_value = 0x2F;
single_upper(c);
}
else if ( c == 'C' ){
key_value = 0x26;
single_upper(c);
}
else if ( c == 'D' ){
key_value = 0x24;
single_upper(c);
}
else if ( c == 'E' ){
key_value = 0x22;
single_upper(c);
}
else if ( c == 'F' ){
key_value = 0x2C;
single_upper(c);
}
else if ( c == 'G' ){
key_value = 0x2D;
single_upper(c);
}
else if ( c == 'H' ){
key_value = 0x35;
single_upper(c);
}
else if ( c == 'I' ){
key_value = 0x3A;
single_upper(c);
}
else if ( c == 'J' ){
key_value = 0x34;
single_upper(c);
}
else if ( c == 'K' ){
key_value = 0x3C;
single_upper(c);
}
else if ( c == 'L' ){
key_value = 0x44;
single_upper(c);
}
else if ( c == 'M' ){
key_value = 0x36;
single_upper(c);
}
else if ( c == 'N' ){
key_value = 0x37;
single_upper(c);
}
else if ( c == 'O' ){
key_value = 0x42;
single_upper(c);
}
else if ( c == 'P' ){
key_value = 0x4A;
single_upper(c);
}
else if ( c == 'Q' ){
key_value = 0x12;
single_upper(c);
}
else if ( c == 'R' ){
key_value = 0x2A;
single_upper(c);
}
else if ( c == 'S' ){
key_value = 0x1C;
single_upper(c);
}
else if ( c == 'T' ){
key_value = 0x2B;
single_upper(c);
}
else if ( c == 'U' ){
key_value = 0x32;
single_upper(c);
}
else if ( c == 'V' ){
key_value = 0x2E;
single_upper(c);
}
else if ( c == 'W' ){
key_value = 0x1A;
single_upper(c);
}
else if ( c == 'X' ){
key_value = 0x1E;
single_upper(c);
}
else if ( c == 'Y' ){
key_value = 0x33;
single_upper(c);
}
else if ( c == 'Z' ){
key_value = 0x16;
single_upper(c);
}
else if ( c == '\n' ){
// printf("new line\n");
//write something so that an empty line equates to an enter maybe use get line
}
else
printf("Invalid input char\n");
}
if ( MacroLength > 700 )
printf("\n\n\nWARNING ####### Macro length too long\n");
int i;
for(i = (700 - MacroLength) ; i > 0 ; i--)
printf("%s",zero_fill);
printf("AB\n");//fake checksum
printf("MacroLength=%d\n" , MacroLength);
printf("\nzero fill bytes %d\n", (700 - MacroLength ));
printf("\n");
return 0;
}