911 lines
20 KiB
C++
911 lines
20 KiB
C++
// scas.cpp - config file assembler for Soarer's Keyboard Converter.
|
|
|
|
#include "../common/global.h"
|
|
#include "../common/hid_tokens.h"
|
|
#include "../common/macro_tokens.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <vector>
|
|
#include <utility>
|
|
#include <string>
|
|
|
|
using namespace std;
|
|
|
|
typedef unsigned char uint8_t;
|
|
typedef unsigned short uint16_t;
|
|
|
|
#define SETTINGS_VERSION_MAJOR 1
|
|
#define SETTINGS_VERSION_MINOR 1
|
|
|
|
#define COMMENT_CHAR '#'
|
|
|
|
#define ERR_FILE_NOT_FOUND 1
|
|
#define ERR_INVALID_COMMAND 2
|
|
#define ERR_INVALID_ARGS 3
|
|
#define ERR_NOT_IMPLEMENTED 4
|
|
#define ERR_BLOCK_TOO_LARGE 5
|
|
#define ERR_MACRO_TOO_LONG 6
|
|
#define ERR_FILE_WRITE 7
|
|
|
|
void print_error(int err)
|
|
{
|
|
switch ( err ) {
|
|
case ERR_FILE_NOT_FOUND: fprintf(stderr, "file not found\n"); break;
|
|
case ERR_INVALID_COMMAND: fprintf(stderr, "invalid command\n"); break;
|
|
case ERR_INVALID_ARGS: fprintf(stderr, "invalid argument\n"); break;
|
|
case ERR_NOT_IMPLEMENTED: fprintf(stderr, "not implemented\n"); break;
|
|
case ERR_BLOCK_TOO_LARGE: fprintf(stderr, "block too large\n"); break;
|
|
case ERR_MACRO_TOO_LONG: fprintf(stderr, "macro too long\n"); break;
|
|
default: fprintf(stderr, "unknown error\n"); break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
const char* skip_whitespace(const char* p)
|
|
{
|
|
while ( isspace(*p) ) {
|
|
++p;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
const char* skip_non_whitespace(const char* p)
|
|
{
|
|
while ( *p && !isspace(*p) ) {
|
|
++p;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
const char* skip_digit(const char* p)
|
|
{
|
|
while ( isdigit(*p) ) {
|
|
++p;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
const char* match(const char* s1, const char* s2)
|
|
{
|
|
size_t len = strlen(s1);
|
|
if ( 0 == strnicmp(s1, s2, len) ) {
|
|
return s2 + len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char* skip_to_end_quote(const char* p)
|
|
{
|
|
if ( *p == '\"' ) {
|
|
return p;
|
|
}
|
|
while ( *p ) {
|
|
if ( *p == '\"' && *(p-1) != '\\' ) {
|
|
break;
|
|
}
|
|
++p;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
const char* skip_token(const char* p)
|
|
{
|
|
p = skip_whitespace(p);
|
|
if ( *p == '\"' ) {
|
|
++p;
|
|
p = skip_to_end_quote(p);
|
|
if ( *p == '\"' ) {
|
|
++p;
|
|
}
|
|
} else {
|
|
p = skip_non_whitespace(p);
|
|
}
|
|
p = skip_whitespace(p);
|
|
return p;
|
|
}
|
|
|
|
string get_token(const char* p)
|
|
{
|
|
p = skip_whitespace(p);
|
|
const char* p2;
|
|
if ( *p == '\"' ) {
|
|
++p;
|
|
p2 = skip_to_end_quote(p);
|
|
} else {
|
|
p2 = skip_non_whitespace(p);
|
|
}
|
|
return string(p, p2);
|
|
}
|
|
|
|
int parse_int(const char* args)
|
|
{
|
|
args = skip_whitespace(args);
|
|
if ( isdigit(*args) ) {
|
|
return atoi(args);
|
|
}
|
|
return INVALID_NUMBER;
|
|
}
|
|
|
|
int parse_int(const char* args, int minval, int maxval)
|
|
{
|
|
int v = parse_int(args);
|
|
if ( minval <= v && v <= maxval ) {
|
|
return v;
|
|
}
|
|
return INVALID_NUMBER;
|
|
}
|
|
|
|
int parse_hid(const char* args)
|
|
{
|
|
string t = get_token(args);
|
|
return lookup_hid_token(t.c_str());
|
|
/*
|
|
|
|
const char* p2 = skip_non_whitespace(args);
|
|
char c = *p2;
|
|
if ( c ) {
|
|
*p2 = 0;
|
|
}
|
|
int hid = lookup_hid_token(args);
|
|
if ( c ) {
|
|
*p2 = c;
|
|
}
|
|
return hid;
|
|
*/
|
|
}
|
|
|
|
bool parse_meta_match(const char* args, int& desired_meta, int& matched_meta)
|
|
{
|
|
desired_meta = 0;
|
|
matched_meta = 0;
|
|
const char* p = args;
|
|
while ( *p ) {
|
|
bool inverted = false;
|
|
if ( *p == '-' ) {
|
|
inverted = true;
|
|
++p;
|
|
}
|
|
string t = get_token(p);
|
|
int meta = lookup_meta_token(t.c_str());
|
|
if ( meta == INVALID_NUMBER ) {
|
|
return false;
|
|
}
|
|
if ( inverted ) {
|
|
desired_meta &= ~meta;
|
|
matched_meta |= meta;
|
|
} else {
|
|
desired_meta |= meta;
|
|
if ( is_meta_handed(meta) ) {
|
|
matched_meta |= meta;
|
|
} else {
|
|
matched_meta |= (meta & 0x0F);
|
|
}
|
|
}
|
|
p = skip_token(p);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int parse_meta_handed(const char* p)
|
|
{
|
|
int val = 0;
|
|
while ( *p ) {
|
|
string t = get_token(p);
|
|
int meta = lookup_meta_token(t.c_str());
|
|
if ( meta == INVALID_NUMBER /*|| !is_meta_handed(meta)*/ ) {
|
|
return INVALID_NUMBER;
|
|
}
|
|
val |= meta;
|
|
p = skip_token(p);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
bool parse_macro_cmd(const char* args, int& cmd, int& val)
|
|
{
|
|
string t = get_token(args);
|
|
cmd = lookup_macro_token(t.c_str());
|
|
if ( cmd == INVALID_NUMBER ) {
|
|
return false;
|
|
}
|
|
const char* p = skip_token(args);
|
|
// todo: Q_PLAY
|
|
if ( cmd == Q_PUSH_META ) {
|
|
t = get_token(p);
|
|
int q = lookup_macro_token(t.c_str());
|
|
if ( q == INVALID_NUMBER ) {
|
|
return false;
|
|
}
|
|
cmd |= q;
|
|
p = skip_token(p);
|
|
}
|
|
t = get_token(p);
|
|
//char* p2 = p;
|
|
int argtype = get_macro_arg_type(cmd);
|
|
switch ( argtype ) {
|
|
case MACRO_ARG_HID:
|
|
val = lookup_hid_token(t.c_str());
|
|
//p2 = skip_token(p);
|
|
break;
|
|
case MACRO_ARG_META:
|
|
val = parse_meta_handed(p);
|
|
////p2 = next_token(p);
|
|
break;
|
|
case MACRO_ARG_DELAY:
|
|
val = parse_int(t.c_str(), 0, 255);
|
|
//p2 = skip_token(p);
|
|
break;
|
|
case MACRO_ARG_NONE:
|
|
val = 0;
|
|
break;
|
|
}
|
|
return val != INVALID_NUMBER;
|
|
}
|
|
|
|
int lookup_set_token(const char* t)
|
|
{
|
|
if ( 0 == stricmp(t, "set1") ) {
|
|
return 1;
|
|
} else if ( 0 == stricmp(t, "set2") ) {
|
|
return 2;
|
|
} else if ( 0 == stricmp(t, "set3") ) {
|
|
return 3;
|
|
} else if ( 0 == stricmp(t, "set2ext") ) {
|
|
return 4;
|
|
} else if ( 0 == stricmp(t, "any") ) {
|
|
return 0;
|
|
}
|
|
return INVALID_NUMBER;
|
|
}
|
|
|
|
int parse_single_set(const char* p)
|
|
{
|
|
string t = get_token(p);
|
|
int s = lookup_set_token(t.c_str());
|
|
if ( s == INVALID_NUMBER ) {
|
|
return INVALID_NUMBER;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
int parse_multi_set(const char* p)
|
|
{
|
|
int val = 0;
|
|
while ( *p ) {
|
|
string t = get_token(p);
|
|
int s = lookup_set_token(t.c_str());
|
|
if ( s == INVALID_NUMBER ) {
|
|
return INVALID_NUMBER;
|
|
}
|
|
if ( s ) {
|
|
val |= 1 << (s - 1);
|
|
} else {
|
|
val = 0;
|
|
}
|
|
p = skip_token(p);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
int parse_function_n(const char* p)
|
|
{
|
|
if ( 0 == strnicmp("fn", p, 2) ) {
|
|
p += 2;
|
|
if ( isdigit(*p) ) {
|
|
int v = atoi(p);
|
|
if ( 1 <= v && v <= 8 ) {
|
|
return v;
|
|
}
|
|
}
|
|
}
|
|
return INVALID_NUMBER;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
typedef int (*command_fn)(const char* args);
|
|
|
|
uint8_t current_force_flags = 0;
|
|
int cmd_force(const char* args)
|
|
{
|
|
int set = parse_single_set(args);
|
|
if ( set != INVALID_NUMBER ) {
|
|
current_force_flags = (current_force_flags & 0xF0) | (uint8_t)set;
|
|
return 0;
|
|
}
|
|
// todo: XT/AT force?
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
|
|
uint8_t current_select = 0;
|
|
int cmd_select(const char* args)
|
|
{
|
|
string t = get_token(args);
|
|
int s;
|
|
if ( 0 == stricmp(t.c_str(), "any") ) {
|
|
s = 0;
|
|
} else {
|
|
s = parse_int(args, 1, 7);
|
|
}
|
|
if ( s != INVALID_NUMBER ) {
|
|
current_select = (uint8_t)s;
|
|
return 0;
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
|
|
uint8_t current_scanset = 0;
|
|
int cmd_scanset(const char* args)
|
|
{
|
|
int s = parse_multi_set(args);
|
|
if ( s != INVALID_NUMBER ) {
|
|
current_scanset = (uint8_t)s;
|
|
return 0;
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
|
|
bool is_hex(char c)
|
|
{
|
|
c = (char)toupper(c);
|
|
return isdigit(c) || ('A' <= c && c <= 'F');
|
|
}
|
|
|
|
int parse_hex(char c)
|
|
{
|
|
c = (char)toupper(c);
|
|
if ( isdigit(c) ) {
|
|
return c - '0';
|
|
} else if ( 'A' <= c && c <= 'F' ) {
|
|
return c - 'A' + 10;
|
|
} else {
|
|
return INVALID_NUMBER;
|
|
}
|
|
}
|
|
|
|
int parse_hex(const char* p)
|
|
{
|
|
p = skip_whitespace(p);
|
|
int val = 0;
|
|
int digit = parse_hex(*p++);
|
|
if ( digit == INVALID_NUMBER ) {
|
|
return INVALID_NUMBER;
|
|
}
|
|
while ( digit != INVALID_NUMBER ) {
|
|
val = (val << 4) | digit;
|
|
digit = parse_hex(*p++);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
int parse_hex(const char* p, int minval, int maxval)
|
|
{
|
|
int v = parse_hex(p);
|
|
if ( minval <= v && v <= maxval ) {
|
|
return v;
|
|
}
|
|
return INVALID_NUMBER;
|
|
}
|
|
|
|
uint16_t current_keyboard_id = 0;
|
|
int cmd_keyboard_id(const char* args)
|
|
{
|
|
string t = get_token(args);
|
|
if ( 0 == stricmp(t.c_str(), "any") ) {
|
|
current_keyboard_id = 0;
|
|
} else {
|
|
int v = parse_hex(args, 0, 0xFFFF);
|
|
if ( v == INVALID_NUMBER ) {
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
current_keyboard_id = (uint16_t)v;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint8_t current_layer = 0;
|
|
int cmd_layer(const char* args)
|
|
{
|
|
int v = parse_int(args, 0, 255);
|
|
if ( v != INVALID_NUMBER ) {
|
|
current_layer = (uint8_t)v;
|
|
return 0;
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
|
|
typedef pair<uint8_t, uint8_t> layerdef_pair;
|
|
typedef vector<layerdef_pair> layerdef_list_t;
|
|
layerdef_list_t layerdef_list;
|
|
int cmd_layerdef(const char* args)
|
|
{
|
|
int fn_combo = 0;
|
|
const char* p = skip_whitespace(args);
|
|
for ( ; *p; p = skip_token(p) ) {
|
|
int fn = parse_function_n(p);
|
|
if ( fn == INVALID_NUMBER ) {
|
|
break;
|
|
}
|
|
fn_combo |= 1 << (fn - 1);
|
|
}
|
|
if ( !fn_combo ) {
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
int layer = parse_int(p, 1, 255); // layer id
|
|
if ( layer == INVALID_NUMBER ) {
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
layerdef_list.push_back(layerdef_pair((uint8_t)fn_combo, (uint8_t)layer));
|
|
return 0;
|
|
}
|
|
|
|
typedef pair<uint8_t, uint8_t> remap_pair;
|
|
typedef vector<remap_pair> remap_list_t;
|
|
remap_list_t remap_list;
|
|
int cmd_remap(const char* args)
|
|
{
|
|
args = skip_whitespace(args);
|
|
int v1 = parse_hid(args);
|
|
if ( v1 != INVALID_NUMBER ) {
|
|
args = skip_token(args);
|
|
int v2 = parse_hid(args);
|
|
if ( v2 != INVALID_NUMBER ) {
|
|
remap_list.push_back(remap_pair((uint8_t)v1, (uint8_t)v2));
|
|
return 0;
|
|
}
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
|
|
int current_macro_phase = -1; // -1 = invalid, 0 = make, 1 = break
|
|
uint8_t current_macro_release_meta = 1;
|
|
uint8_t current_hid_code = 0;
|
|
uint8_t current_desired_meta = 0;
|
|
uint8_t current_matched_meta = 0;
|
|
int cmd_macro(const char* args)
|
|
{
|
|
string t = get_token(args);
|
|
int hid_code = lookup_hid_token(t.c_str());
|
|
if ( hid_code == INVALID_NUMBER ) {
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
int desired_meta, matched_meta;
|
|
if ( !parse_meta_match(skip_token(args), desired_meta, matched_meta) ) {
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
current_macro_phase = 0;
|
|
current_macro_release_meta = 1;
|
|
current_hid_code = (uint8_t)hid_code;
|
|
current_desired_meta = (uint8_t)desired_meta;
|
|
current_matched_meta = (uint8_t)matched_meta;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
int cmd_meta(char* args)
|
|
{
|
|
int v = parse_int(args, 0, 255);
|
|
if ( v != INVALID_NUMBER ) {
|
|
current_desired_meta = (uint8_t)v;
|
|
return 0;
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
int cmd_ignore(char* args)
|
|
{
|
|
int v = parse_int(args, 0, 255);
|
|
if ( v != INVALID_NUMBER ) {
|
|
current_matched_meta = (uint8_t)v;
|
|
return 0;
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
*/
|
|
|
|
int cmd_onbreak(const char* args)
|
|
{
|
|
if ( current_macro_phase != 0 ) {
|
|
return ERR_INVALID_COMMAND;
|
|
}
|
|
current_macro_phase = 1;
|
|
string t = get_token(args);
|
|
if ( t == "" ) {
|
|
current_macro_release_meta = 1;
|
|
} else if ( 0 == stricmp(t.c_str(), "norestoremeta") ) {
|
|
current_macro_release_meta = 0;
|
|
} else {
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
typedef pair<uint8_t, uint8_t> mcommand_pair;
|
|
typedef vector<mcommand_pair> mcommand_list_t;
|
|
mcommand_list_t press_mcommand_list;
|
|
mcommand_list_t release_mcommand_list;
|
|
/*
|
|
int cmd_press(char* args)
|
|
{
|
|
int cmd, val;
|
|
if ( parse_macro_cmd(args, cmd, val) ) {
|
|
press_mcommand_list.push_back(mcommand_pair((uint8_t)cmd, (uint8_t)val));
|
|
return 0;
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
|
|
int cmd_release(char* args)
|
|
{
|
|
int cmd, val;
|
|
if ( parse_macro_cmd(args, cmd, val) ) {
|
|
release_mcommand_list.push_back(mcommand_pair((uint8_t)cmd, (uint8_t)val));
|
|
return 0;
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
*/
|
|
|
|
int cmd_macrostep(const char* args)
|
|
{
|
|
int cmd, val;
|
|
if ( parse_macro_cmd(args, cmd, val) ) {
|
|
mcommand_pair mcp((uint8_t)cmd, (uint8_t)val);
|
|
if ( current_macro_phase == 0 ) {
|
|
press_mcommand_list.push_back(mcp);
|
|
} else {
|
|
release_mcommand_list.push_back(mcp);
|
|
}
|
|
return 0;
|
|
}
|
|
return ERR_INVALID_ARGS;
|
|
}
|
|
|
|
|
|
|
|
struct macro_t
|
|
{
|
|
uint8_t hid_code;
|
|
uint8_t desired_meta;
|
|
uint8_t matched_meta;
|
|
uint8_t press_flags;
|
|
uint8_t release_flags;
|
|
mcommand_list_t commands;
|
|
};
|
|
typedef vector<macro_t> macro_list_t;
|
|
macro_list_t macro_list;
|
|
int cmd_endmacro(const char* /*args*/)
|
|
{
|
|
if ( current_macro_phase == -1 ) {
|
|
return ERR_INVALID_COMMAND;
|
|
}
|
|
current_macro_phase = -1;
|
|
macro_t macro;
|
|
macro.hid_code = current_hid_code;
|
|
macro.desired_meta = current_desired_meta;
|
|
macro.matched_meta = current_matched_meta;
|
|
macro.press_flags = (uint8_t)press_mcommand_list.size();
|
|
macro.release_flags = (uint8_t)release_mcommand_list.size();
|
|
macro.release_flags |= current_macro_release_meta << 7;
|
|
if ( press_mcommand_list.size() > 63 || release_mcommand_list.size() > 63 ) {
|
|
return ERR_MACRO_TOO_LONG;
|
|
}
|
|
//macro.commands.push_back(press_mcommand_list);
|
|
macro.commands.insert(macro.commands.end(), press_mcommand_list.begin(), press_mcommand_list.end());
|
|
//macro.commands.push_back(release_mcommand_list);
|
|
macro.commands.insert(macro.commands.end(), release_mcommand_list.begin(), release_mcommand_list.end());
|
|
macro_list.push_back(macro);
|
|
press_mcommand_list.clear();
|
|
release_mcommand_list.clear();
|
|
return 0;
|
|
}
|
|
|
|
int block_type = BLOCK_NONE;
|
|
|
|
int cmd_layerdefblock(const char* /*args*/)
|
|
{
|
|
if ( block_type != BLOCK_NONE ) {
|
|
return ERR_INVALID_COMMAND;
|
|
}
|
|
block_type = BLOCK_LAYERDEF;
|
|
return 0;
|
|
}
|
|
|
|
int cmd_remapblock(const char* /*args*/)
|
|
{
|
|
if ( block_type != BLOCK_NONE ) {
|
|
return ERR_INVALID_COMMAND;
|
|
}
|
|
block_type = BLOCK_REMAP;
|
|
return 0;
|
|
}
|
|
|
|
int cmd_macroblock(const char* /*args*/)
|
|
{
|
|
if ( block_type != BLOCK_NONE ) {
|
|
return ERR_INVALID_COMMAND;
|
|
}
|
|
block_type = BLOCK_MACRO;
|
|
return 0;
|
|
}
|
|
|
|
int cmd_invalid(const char* args)
|
|
{
|
|
switch ( block_type ) {
|
|
case BLOCK_LAYERDEF: return cmd_layerdef(args);
|
|
case BLOCK_REMAP: return cmd_remap(args);
|
|
case BLOCK_MACRO: return cmd_macrostep(args);
|
|
default: return ERR_INVALID_COMMAND;
|
|
}
|
|
}
|
|
|
|
int process_file(const char* fname);
|
|
|
|
int cmd_include(const char* args)
|
|
{
|
|
string fname = get_token(args);
|
|
return process_file(fname.c_str());
|
|
}
|
|
|
|
|
|
typedef vector<uint8_t> block_t;
|
|
typedef vector<block_t> block_list_t;
|
|
block_list_t block_list;
|
|
|
|
void fill_block_header(block_t& block)
|
|
{
|
|
// common block header stuff...
|
|
block.push_back(0); // placeholder for size
|
|
uint8_t flags = (uint8_t)block_type;
|
|
flags |= current_select << 3;
|
|
flags |= (current_scanset != 0) << 6;
|
|
flags |= (current_keyboard_id != 0) << 7;
|
|
block.push_back(flags);
|
|
if ( current_scanset ) {
|
|
block.push_back(current_scanset);
|
|
}
|
|
if ( current_keyboard_id ) {
|
|
block.push_back((uint8_t)(current_keyboard_id & 0xFF));
|
|
block.push_back((uint8_t)((current_keyboard_id >> 8) & 0xFF));
|
|
}
|
|
}
|
|
|
|
int cmd_endlayerdefblock(const char* /*args*/)
|
|
{
|
|
block_t block;
|
|
fill_block_header(block);
|
|
block.push_back((uint8_t)layerdef_list.size());
|
|
for ( int i = 0; i < (int)layerdef_list.size(); ++i ) {
|
|
block.push_back(layerdef_list[i].first);
|
|
block.push_back(layerdef_list[i].second);
|
|
}
|
|
if ( block.size() > 255 ) {
|
|
return ERR_BLOCK_TOO_LARGE;
|
|
}
|
|
block[0] = (uint8_t)block.size();
|
|
block_list.push_back(block);
|
|
layerdef_list.clear();
|
|
block_type = BLOCK_NONE;
|
|
return 0;
|
|
}
|
|
|
|
int cmd_endremapblock(const char* /*args*/)
|
|
{
|
|
block_t block;
|
|
fill_block_header(block);
|
|
block.push_back(current_layer);
|
|
block.push_back((uint8_t)remap_list.size());
|
|
for ( int i = 0; i < (int)remap_list.size(); ++i ) {
|
|
block.push_back(remap_list[i].first);
|
|
block.push_back(remap_list[i].second);
|
|
}
|
|
if ( block.size() > 255 ) {
|
|
return ERR_BLOCK_TOO_LARGE;
|
|
}
|
|
block[0] = (uint8_t)block.size();
|
|
block_list.push_back(block);
|
|
remap_list.clear();
|
|
current_layer = 0;
|
|
block_type = BLOCK_NONE;
|
|
return 0;
|
|
}
|
|
|
|
int cmd_endmacroblock(const char* /*args*/)
|
|
{
|
|
block_t block;
|
|
fill_block_header(block);
|
|
block.push_back((uint8_t)macro_list.size());
|
|
for ( int i = 0; i < (int)macro_list.size(); ++i ) {
|
|
block.push_back(macro_list[i].hid_code);
|
|
block.push_back(macro_list[i].desired_meta);
|
|
block.push_back(macro_list[i].matched_meta);
|
|
block.push_back(macro_list[i].press_flags);
|
|
block.push_back(macro_list[i].release_flags);
|
|
for ( int j = 0; j < (int)macro_list[i].commands.size(); ++j ) {
|
|
block.push_back(macro_list[i].commands[j].first);
|
|
block.push_back(macro_list[i].commands[j].second);
|
|
}
|
|
}
|
|
if ( block.size() > 255 ) {
|
|
return ERR_BLOCK_TOO_LARGE;
|
|
}
|
|
block[0] = (uint8_t)block.size();
|
|
block_list.push_back(block);
|
|
macro_list.clear();
|
|
block_type = BLOCK_NONE;
|
|
return 0;
|
|
}
|
|
|
|
int cmd_endblock(const char* args)
|
|
{
|
|
switch ( block_type ) {
|
|
case BLOCK_LAYERDEF: return cmd_endlayerdefblock(args);
|
|
case BLOCK_REMAP: return cmd_endremapblock(args);
|
|
case BLOCK_MACRO: return cmd_endmacroblock(args);
|
|
default: return ERR_INVALID_COMMAND;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
struct command_map_t
|
|
{
|
|
const char* cmd;
|
|
command_fn fn;
|
|
};
|
|
|
|
command_map_t command_map[] =
|
|
{
|
|
{ "force", cmd_force },
|
|
|
|
{ "ifselect", cmd_select },
|
|
{ "ifset", cmd_scanset },
|
|
{ "ifkeyboard", cmd_keyboard_id },
|
|
|
|
{ "layerblock", cmd_layerdefblock },
|
|
|
|
{ "remapblock", cmd_remapblock },
|
|
{ "layer", cmd_layer },
|
|
|
|
{ "macroblock", cmd_macroblock },
|
|
{ "macro", cmd_macro },
|
|
{ "onbreak", cmd_onbreak },
|
|
{ "endmacro", cmd_endmacro },
|
|
|
|
{ "endblock", cmd_endblock },
|
|
|
|
{ "include", cmd_include },
|
|
};
|
|
|
|
command_fn find_command(const char* cmd)
|
|
{
|
|
int n = sizeof(command_map) / sizeof(command_map_t);
|
|
for ( int i = 0; i < n; ++i ) {
|
|
if ( 0 == stricmp(cmd, command_map[i].cmd) ) {
|
|
return command_map[i].fn;
|
|
}
|
|
}
|
|
return cmd_invalid;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
int process_line(char* linebuf)
|
|
{
|
|
char* pcomment = strchr(linebuf, COMMENT_CHAR);
|
|
if ( pcomment ) {
|
|
*pcomment = 0;
|
|
}
|
|
string t = get_token(linebuf);
|
|
if ( t.size() ) {
|
|
const char* p;
|
|
command_fn fn = find_command(t.c_str());
|
|
if ( fn != cmd_invalid ) {
|
|
p = skip_token(linebuf);
|
|
} else {
|
|
p = linebuf;
|
|
}
|
|
if ( int err = fn(p) ) {
|
|
return err;
|
|
}
|
|
}
|
|
// p = skip_whitespace(linebuf);
|
|
// char* p2 = skip_non_whitespace(p);
|
|
// if ( *p2 ) {
|
|
// *p2++ = 0;
|
|
// p2 = skip_whitespace(p2);
|
|
// if ( int err = find_command(p)(p2) ) {
|
|
// return err;
|
|
// }
|
|
// }
|
|
return 0;
|
|
}
|
|
|
|
int process_file(FILE* f)
|
|
{
|
|
int linenum = 0;
|
|
char linebuf[256];
|
|
while ( fgets(linebuf, sizeof(linebuf), f) ) {
|
|
++linenum;
|
|
if ( int err = process_line(linebuf) ) {
|
|
fprintf(stderr, "error at line %d: ", linenum);
|
|
return err;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int process_file(const char* fname)
|
|
{
|
|
FILE* f = fopen(fname, "rt");
|
|
if ( f ) {
|
|
int retval = process_file(f);
|
|
fclose(f);
|
|
return retval;
|
|
}
|
|
return ERR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
int write_target(FILE* f)
|
|
{
|
|
// Header...
|
|
fputc('S', f); // signature...
|
|
fputc('C', f);
|
|
fputc(SETTINGS_VERSION_MAJOR, f);
|
|
fputc(SETTINGS_VERSION_MINOR, f);
|
|
fputc(current_force_flags, f);
|
|
fputc(0, f); // reserved
|
|
|
|
// Blocks...
|
|
for ( int i = 0; i < (int)block_list.size(); ++i ) {
|
|
if ( !fwrite(&(block_list[i][0]), block_list[i].size(), 1, f) ) {
|
|
return ERR_FILE_WRITE;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int write_target(const char* fname)
|
|
{
|
|
FILE* f = fopen(fname, "wb");
|
|
if ( f ) {
|
|
int retval = write_target(f);
|
|
fclose(f);
|
|
return retval;
|
|
}
|
|
return ERR_FILE_WRITE;
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
printf("scas v1.10\n");
|
|
|
|
int err = 0;
|
|
if ( argc < 3 ) {
|
|
fprintf(stderr, "usage: scas <text_config> [<text_config> ...] <binary_config>\n");
|
|
err = 1;
|
|
}
|
|
for ( int i = 1; i < argc - 1; ++i ) {
|
|
err = process_file(argv[i]);
|
|
if ( err ) {
|
|
print_error(err);
|
|
}
|
|
}
|
|
if ( !err ) {
|
|
err = write_target(argv[argc - 1]);
|
|
if ( err ) {
|
|
fprintf(stderr, "unable to write to file: %s\n", argv[argc - 1]);
|
|
}
|
|
}
|
|
if ( !err ) {
|
|
fprintf(stderr, "No errors. Wrote: %s\n", argv[argc - 1]);
|
|
}
|
|
return err ? 1 : 0;
|
|
}
|