#!/usr/bin/perl # Original Version by "LitteWarrior" (forums.eagle.ru) # License: "MIT" # [Begin license text] # # Copyright 2020 "LitteWarrior" (forums.eagle.ru) # # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and # associated documentation files (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all copies or # substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR # THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # [End license text] use strict; use warnings; # Install these two with "cpan install HTML::TableExtract" and "cpan install Data::UUID" # or add the precompiled perl library with apt or alike, where they *might* be called # libhtml-tableextract-perl and libdata-uuid-perl. use HTML::TableExtract; use Data::UUID; # You should have List::MoreUtils already. use List::MoreUtils qw(first_index); my ($input_file_name, $output_file_name, $keyboard_html_name) = @ARGV; if (not defined $input_file_name) { $input_file_name = "Untitled.pr0"; } if (not defined $output_file_name) { $output_file_name = "Output.pr0"; } if (not defined $keyboard_html_name) { $keyboard_html_name = "Keyboard.html"; } # Mapping from DCS text to USB-HID codes for Saitek. # https://www.freebsddiary.org/APC/usb_hid_usages.php has been used for this. my %code_hlist = ( LCtrl => 0xE0, # LeftControl LShift => 0xE1, # LeftShift LAlt => 0xE2, # LeftAlt LWin => 0xE3, # Left GUI RCtrl => 0xE4, # RightControl RShift => 0xE5, # RightShift RAlt => 0xE6, # RightAlt #??? => 0xE7, # Right GUI RWin => 0x65, # Application "A" => 0x04, "B" => 0x05, "C" => 0x06, "D" => 0x07, "E" => 0x08, "F" => 0x09, "G" => 0x0A, "H" => 0x0B, "I" => 0x0C, "J" => 0x0D, "K" => 0x0E, "L" => 0x0F, "M" => 0x10, "N" => 0x11, "O" => 0x12, "P" => 0x13, "Q" => 0x14, "R" => 0x15, "S" => 0x16, "T" => 0x17, "U" => 0x18, "V" => 0x19, "W" => 0x1A, "X" => 0x1B, "Y" => 0x1C, "Z" => 0x1D, "1" => 0x1E, "2" => 0x1F, "3" => 0x20, "4" => 0x21, "5" => 0x22, "6" => 0x23, "7" => 0x24, "8" => 0x25, "9" => 0x26, "0" => 0x27, "!" => 0x1E, "@" => 0x1F, "#" => 0x20, "\$" => 0x21, "%" => 0x22, "^" => 0x23, "&" => 0x24, "*" => 0x25, "(" => 0x26, ")" => 0x27, "Enter" => 0x28, "Esc" => 0x29, "Back" => 0x2A, "Tab" => 0x2B, "Space" => 0x2C, "-" => 0x2D, "=" => 0x2E, "[" => 0x2F, "]" => 0x30, "\\" => 0x31, "OEM3" => 0x32, # Non-US # and ~ TODO: correct key? ";" => 0x33, "'" => 0x34, "`" => 0x35, # Grave Accent and Tilde TODO: correct key? "," => 0x36, "." => 0x37, "/" => 0x38, #"CapsLock" => 0x39, "F1" => 0x3A, "F2" => 0x3B, "F3" => 0x3C, "F4" => 0x3D, "F5" => 0x3E, "F6" => 0x3F, "F7" => 0x40, "F8" => 0x41, "F9" => 0x42, "F10" => 0x43, "F11" => 0x44, "F12" => 0x45, #"PrintScreen" => 0x46, "Scroll" => 0x47, # Scroll Lock "Pause" => 0x48, "Insert" => 0x49, "Home" => 0x4A, "PageUp" => 0x4B, "Delete" => 0x4C, "End" => 0x4D, "PageDown" => 0x4E, "Right" => 0x4F, "Left" => 0x50, "Down" => 0x51, "Up" => 0x52, "NumLock" => 0x53, "Num/" => 0x54, "Num*" => 0x55, "Num-" => 0x56, "Num+" => 0x57, "NumEnter" => 0x58, "Num1" => 0x59, "Num2" => 0x5A, "Num3" => 0x5B, "Num4" => 0x5C, "Num5" => 0x5D, "Num6" => 0x5E, "Num7" => 0x5F, "Num8" => 0x60, "Num9" => 0x61, "Num0" => 0x62, "Num." => 0x63, "OEM102" => 0x64, # Non-US \ and | "SysRQ" => 0x9A, ); # Check if input files exist if (not -e $keyboard_html_name) { print "Usage: $0 [Input profile] [Output profile] [Keyboard HTML file]\n"; print "Defaults: Input profile=Untitled.pr0, Output profile=Output.pr0, Keyboard HTML file=Keyboard.html\n"; die "\nHTML file not found: $keyboard_html_name (Generate it with DCS)\n"; } if (not -e $input_file_name) { print "Usage: $0 [Input profile] [Output profile] [Keyboard HTML file]\n"; print "Defaults: Input profile=Untitled.pr0, Output profile=Output.pr0, Keyboard HTML file=Keyboard.html\n"; die "\nInput profile not found: $input_file_name\n"; } # Print what we'll do now print "Reading $keyboard_html_name and merging it with $input_file_name.\n"; if (-e $output_file_name) { print "Output profile $output_file_name will be overwritten now!\n"; } else { print "Resulting profile is $output_file_name.\n"; } # Load and parse Keyboard.html from DCS my $te = HTML::TableExtract->new( depth=>0, count=>0, keep_html=>1 ); $te->parse_file($keyboard_html_name) or die "Error with $keyboard_html_name.\n"; my $table = $te->first_table_found(); defined $table or die "No table in $keyboard_html_name?\n"; my @rows = $table->rows(); # Saitek needs UUIDs my $ug = Data::UUID->new; # Open and create files open(my $f_in, '<:raw:encoding(UTF-16)', $input_file_name); open(my $f_out, '>:raw:encoding(UTF-16)', $output_file_name); # First copy contents... while(<$f_in>) { my $txt = $_; #remove ]] from last line (tr and s don't work) if( index( $txt, "[action" ) > 0 and index( $txt, "]]]]]" ) > 0 ) { chop $txt; chop $txt; } print $f_out $txt; } # ...then add the stuff from html my @rows_tmp; foreach my $row (@rows) { my ($keyseq_, $event_, $cat_) = @$row; # Debug #print "'$key_' : '$event_' : '$cat_'\n"; # Remove html stuff (my $keyseq) = ( $keyseq_ =~ / \"(.*)\"<\/font>/ ); (my $event) = ( $event_ =~ / (.*)<\/b>/ ); (my $cat) = ( $cat_ =~ / (.*)<\/font>/ ); # Ignore lines without keyboard shortcuts like axis if( not defined $keyseq ) { next; } push(@rows_tmp,[($keyseq, $event, $cat)]); } # sort by cat(-egory) and event my @rows_sorted = sort { $a->[2] cmp $b->[2] || $a->[1] cmp $b->[1] } @rows_tmp; # output to f_out foreach my $row (@rows_sorted) { my ($keyseq, $event, $cat) = @$row; # Some have multiple shortcuts assigned my @subseqs = split /"; "/, $keyseq; my @codes; # For all shortcuts foreach my $subseq (@subseqs) { # Split into individual keys my @keys = split / - /, $subseq; my $missing = ""; foreach my $key (@keys) { # Lookup the HID codes if( defined $code_hlist{$key} ) { push (@codes, sprintf( "0x%08X", $code_hlist{$key} ) ); } else { $missing = $missing . " '$key'"; } } # Unknown key name? if( not ($missing eq "" ) ) { @codes = (); print "'$subseq': missing code for$missing ($event)\n"; last; } # only add first of multiple short cuts last; } # Have mapped all keys? if( @codes > 0 ) { # Sanitize $event =~ tr/'/"/; print $f_out "\r\n [actioncommand=" . $ug->create_str() . " name='" . $cat . ": " .$event . "'\r\n"; print $f_out " [actionblock"; foreach my $code (@codes) { print $f_out "\r\n [action device=keyboard usage=" . $code . " page=0x00000007 value=0x00000001]"; } print $f_out "]]"; } # Debug #last; } # Re-add the removed ]] from above print $f_out "]]"; # Done close $f_out; # Bye print "Just added " . @rows . " shortcuts. Have fun!\n";