377 lines
9.1 KiB
C++
377 lines
9.1 KiB
C++
// scdis.cpp - config file disassembler 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;
|
|
|
|
FILE* fout = stdout;
|
|
|
|
const char* get_force_set(uint8_t force)
|
|
{
|
|
static const char* sets[] = { "set1", "set2", "set3", "set2ext" };
|
|
uint8_t force_set = force & 0x0F;
|
|
if ( 1 <= force_set && force_set <= 4 ) {
|
|
return sets[force_set - 1];
|
|
}
|
|
return "ERROR";
|
|
}
|
|
|
|
const char* get_force_protocol(uint8_t force)
|
|
{
|
|
static const char* protocols[] = { "xt", "at" };
|
|
uint8_t force_protocol = (force & 0xF0) >> 4;
|
|
if ( 1 <= force_protocol && force_protocol <= 2 ) {
|
|
return protocols[force_protocol - 1];
|
|
}
|
|
return "ERROR";
|
|
}
|
|
|
|
const char* get_block_type(uint8_t blk_type)
|
|
{
|
|
static const char* blk_types[] = { "layerblock", "remapblock", "macroblock" };
|
|
if ( 0 <= blk_type && blk_type <= 2 ) {
|
|
return blk_types[blk_type];
|
|
}
|
|
return "ERROR";
|
|
}
|
|
|
|
string get_ifset(uint8_t ifset)
|
|
{
|
|
static const char* sets[8] = { "set1", "set2", "set3", "set2ext", "INVALIDSET", "INVALIDSET", "INVALIDSET", "INVALIDSET" };
|
|
if ( !ifset ) {
|
|
return string("any");
|
|
} else {
|
|
string s;
|
|
for ( int i = 0; i < 8; ++i ) {
|
|
if ( ifset & 1 ) {
|
|
s += sets[i];
|
|
s += " ";
|
|
}
|
|
ifset = ifset >> 1;
|
|
}
|
|
return s;
|
|
}
|
|
}
|
|
|
|
string get_macro_match_metas(uint8_t desired_meta, uint8_t matched_meta)
|
|
{
|
|
static const char* metas[4] = { "control", "shift", "alt", "gui" };
|
|
static const char* hmetas[8] = { "lcontrol", "lshift", "lalt", "lgui", "rcontrol", "rshift", "ralt", "rgui" };
|
|
uint8_t unhanded_meta = (desired_meta & ~matched_meta) & 0xF0;
|
|
string s;
|
|
for ( int i = 0; i < 4; ++i ) {
|
|
uint8_t mask = (uint8_t)((1 << (i + 4)) | (1 << i));
|
|
if ( unhanded_meta & mask ) {
|
|
s += metas[i];
|
|
s += " ";
|
|
desired_meta &= ~mask;
|
|
matched_meta &= ~mask;
|
|
}
|
|
}
|
|
for ( int i = 0; i < 8; ++i ) {
|
|
uint8_t mask = (uint8_t)(1 << i);
|
|
if ( matched_meta & mask ) {
|
|
if ( !(desired_meta & mask) ) {
|
|
s += "-";
|
|
}
|
|
s += hmetas[i];
|
|
s += " ";
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
string get_macrostep_metas(int val)
|
|
{
|
|
static const char* metas[8] = { "lcontrol", "lshift", "lalt", "lgui", "rcontrol", "rshift", "ralt", "rgui" };
|
|
string s;
|
|
for ( int i = 0; i < 8; ++i ) {
|
|
if ( val & 1 ) {
|
|
s += metas[i];
|
|
s += " ";
|
|
}
|
|
val = val >> 1;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
string get_macrostep(int cmd, int val)
|
|
{
|
|
//fprintf(fout, "\t%s %d\n", lookup_macro_token(cmd), val);
|
|
string s;
|
|
if ( cmd & Q_PUSH_META ) {
|
|
s = "PUSH_META ";
|
|
}
|
|
s += lookup_macro_token(cmd & ~Q_PUSH_META);
|
|
s += " ";
|
|
int argtype = get_macro_arg_type(cmd);
|
|
char buffer[64];
|
|
switch ( argtype ) {
|
|
case MACRO_ARG_NONE:
|
|
break;
|
|
case MACRO_ARG_HID:
|
|
s += lookup_hid_token(val);
|
|
break;
|
|
case MACRO_ARG_META:
|
|
s += get_macrostep_metas(val);
|
|
break;
|
|
case MACRO_ARG_DELAY:
|
|
//s += itoa(val, buffer, 10);
|
|
sprintf(buffer, "%d", val);
|
|
s += string(buffer);
|
|
break;
|
|
default:
|
|
s += "INVALID";
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
int process_layerblock(const uint8_t* buf, const uint8_t* bufend)
|
|
{
|
|
const uint8_t* p = buf;
|
|
fprintf(fout, "layerblock\n");
|
|
if ( bufend - p < 1 ) {
|
|
fprintf(fout, "# ERROR: block truncated\n");
|
|
return 1;
|
|
}
|
|
uint8_t n = *p++;
|
|
fprintf(fout, "# count: %d\n", n);
|
|
if ( bufend - p != (int)n * 2 ) {
|
|
fprintf(fout, "# ERROR: block size mismatch\n");
|
|
return 1;
|
|
}
|
|
for ( int i = 0; i < n; ++i ) {
|
|
uint8_t fn = *p++;
|
|
uint8_t layer = *p++;
|
|
fprintf(fout, "\t");
|
|
int b = 1;
|
|
while ( fn ) {
|
|
if ( fn & 1 ) {
|
|
fprintf(fout, "fn%d ", b);
|
|
}
|
|
++b;
|
|
fn = fn >> 1;
|
|
}
|
|
fprintf(fout, "%d\n", layer);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int process_remapblock(const uint8_t* buf, const uint8_t* bufend)
|
|
{
|
|
const uint8_t* p = buf;
|
|
fprintf(fout, "remapblock\n");
|
|
if ( bufend - p < 2 ) {
|
|
fprintf(fout, "# ERROR: block truncated\n");
|
|
return 1;
|
|
}
|
|
uint8_t layer = *p++;
|
|
fprintf(fout, "layer %d\n", layer);
|
|
uint8_t n = *p++;
|
|
fprintf(fout, "# count: %d\n", n);
|
|
if ( bufend - p != (int)n * 2 ) {
|
|
fprintf(fout, "# ERROR: block size mismatch\n");
|
|
return 1;
|
|
}
|
|
for ( int i = 0; i < n; ++i ) {
|
|
uint8_t from_hid = *p++;
|
|
uint8_t to_hid = *p++;
|
|
fprintf(fout, "\t%s %s\n", lookup_hid_token(from_hid), lookup_hid_token(to_hid));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int process_macro(const uint8_t* buf, const uint8_t* /*bufend*/)
|
|
{
|
|
// todo: use bufend to check length
|
|
const uint8_t* p = buf;
|
|
uint8_t hid_code = *p++;
|
|
uint8_t desired_meta = *p++;
|
|
uint8_t matched_meta = *p++;
|
|
uint8_t press_flags = *p++;
|
|
uint8_t release_flags = *p++;
|
|
size_t press_length = press_flags & 0x3F;
|
|
size_t release_length = release_flags & 0x3F;
|
|
string s = get_macro_match_metas(desired_meta, matched_meta);
|
|
fprintf(fout, "macro %s %s # %02X %02X\n", lookup_hid_token(hid_code), s.c_str(), desired_meta, matched_meta);
|
|
for ( int i = 0; i < (int)press_length; ++i ) {
|
|
uint8_t cmd = *p++;
|
|
uint8_t val = *p++;
|
|
fprintf(fout, "\t%s\n", get_macrostep(cmd, val).c_str());
|
|
}
|
|
if ( release_length ) {
|
|
fprintf(fout, "onbreak%s\n", (release_flags & 0x40) ? "" : " norestoremeta");
|
|
for ( int i = 0; i < (int)release_length; ++i ) {
|
|
uint8_t cmd = *p++;
|
|
uint8_t val = *p++;
|
|
fprintf(fout, "\t%s\n", get_macrostep(cmd, val).c_str());
|
|
}
|
|
}
|
|
fprintf(fout, "endmacro\n");
|
|
return 0;
|
|
}
|
|
|
|
int process_macroblock(const uint8_t* buf, const uint8_t* bufend)
|
|
{
|
|
const uint8_t* p = buf;
|
|
fprintf(fout, "macroblock\n");
|
|
if ( bufend - p < 1 ) {
|
|
fprintf(fout, "# ERROR: block truncated\n");
|
|
return 1;
|
|
}
|
|
uint8_t n = *p++;
|
|
fprintf(fout, "# macro count: %d\n", n);
|
|
for ( int i = 0; i < n; ++i ) {
|
|
if ( bufend - p < 5 ) {
|
|
fprintf(fout, "# ERROR: block truncated\n");
|
|
return 1;
|
|
}
|
|
int press_length = p[3] & 0x3F;
|
|
int release_length = p[4] & 0x3F;
|
|
int macro_length = 5 + 2 * (press_length + release_length);
|
|
if ( bufend - p < macro_length ) {
|
|
fprintf(fout, "# ERROR: block truncated\n");
|
|
return 1;
|
|
}
|
|
process_macro(p, p + macro_length);
|
|
p += macro_length;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int process_block(const uint8_t* buf, size_t buflen)
|
|
{
|
|
const uint8_t* p = buf;
|
|
const uint8_t* bufend = buf + buflen;
|
|
if ( bufend - p < 2 ) {
|
|
fprintf(fout, "# ERROR: block truncated\n");
|
|
return 1;
|
|
}
|
|
fprintf(fout, "\n# block length: %d\n", *p++);
|
|
uint8_t blktype = *p & 0x07;
|
|
uint8_t blksel = (*p >> 3) & 0x07;
|
|
uint8_t has_set = (*p & 0x40) ? 1 : 0;
|
|
uint8_t has_id = (*p & 0x80) ? 2 : 0;
|
|
++p;
|
|
if ( bufend - p < has_set + has_id ) {
|
|
fprintf(fout, "# ERROR: block truncated\n");
|
|
return 1;
|
|
}
|
|
uint8_t blkset = 0;
|
|
if ( has_set ) {
|
|
blkset = *p++;
|
|
}
|
|
uint16_t blkid = 0;
|
|
if ( has_id ) {
|
|
blkid = (uint16_t)(*p++);
|
|
blkid |= ((uint16_t)(*p++) << 8);
|
|
}
|
|
fprintf(fout, "ifset %s\n", get_ifset(blkset).c_str());
|
|
if ( has_id ) {
|
|
fprintf(fout, "ifkeyboard %04X\n", blkid);
|
|
} else {
|
|
fprintf(fout, "ifkeyboard any\n");
|
|
}
|
|
if ( blksel ) {
|
|
fprintf(fout, "ifselect %d\n", blksel);
|
|
} else {
|
|
fprintf(fout, "ifselect any\n");
|
|
}
|
|
// fprintf(fout, "%s\n", get_block_type(blktype));
|
|
int retval = 1;
|
|
switch ( blktype ) {
|
|
case BLOCK_LAYERDEF: retval = process_layerblock(p, bufend); break;
|
|
case BLOCK_REMAP: retval = process_remapblock(p, bufend); break;
|
|
case BLOCK_MACRO: retval = process_macroblock(p, bufend); break;
|
|
}
|
|
fprintf(fout, "endblock\n");
|
|
return retval;
|
|
}
|
|
|
|
int process_file(const uint8_t* buf, size_t buflen)
|
|
{
|
|
const uint8_t* p = buf;
|
|
|
|
fprintf(fout, "# length: %u\n", buflen);
|
|
|
|
// header...
|
|
uint8_t sig1 = *p++;
|
|
uint8_t sig2 = *p++;
|
|
fprintf(fout, "# signature: %c %c\n", sig1, sig2);
|
|
uint8_t ver1 = *p++;
|
|
uint8_t ver2 = *p++;
|
|
fprintf(fout, "# version: %d %d\n", ver1, ver2);
|
|
if ( *p & 0x0F ) {
|
|
fprintf(fout, "force %s\n", get_force_set(*p));
|
|
}
|
|
if ( *p & 0xF0 ) {
|
|
fprintf(fout, "force %s\n", get_force_protocol(*p));
|
|
}
|
|
++p; // force
|
|
++p; // reserved
|
|
|
|
// blocks...
|
|
int err = 0;
|
|
const uint8_t* pend = buf + buflen;
|
|
while ( p < pend ) {
|
|
uint8_t blklen = *p;
|
|
if ( !blklen ) {
|
|
fprintf(fout, "ERROR: block length is zero!\n");
|
|
return 1;
|
|
}
|
|
err |= process_block(p, blklen);
|
|
p += blklen;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
printf("scdis v1.10\n");
|
|
|
|
if ( argc != 2 && argc != 3 ) {
|
|
fprintf(stderr, "usage: scdis <binary_config> [<text_config>]\n");
|
|
return 1;
|
|
}
|
|
const size_t bufsize = 16384;
|
|
static uint8_t buf[bufsize];
|
|
FILE* f = fopen(argv[1], "rb");
|
|
if ( !f ) {
|
|
fprintf(stderr, "error: could not open input file %s\n", argv[1]);
|
|
return 1;
|
|
}
|
|
size_t buflen = fread(buf, 1, bufsize, f);
|
|
fclose(f);
|
|
if ( argc == 3 ) {
|
|
fout = fopen(argv[2], "wt");
|
|
if ( !fout ) {
|
|
fprintf(stderr, "error: could not open output file %s\n", argv[2]);
|
|
return 1;
|
|
}
|
|
}
|
|
int err = process_file(buf, buflen);
|
|
if ( fout != stdout ) {
|
|
fclose(fout);
|
|
}
|
|
if ( err ) {
|
|
fprintf(stderr, "errors encountered, see output file\n");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|