#!/usr/bin/env splrun /* * The QUAFFLER library * Copyright (c) 2006 Clifford Wolf * * 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. */ load "file"; function to_upper(str) { return str =~ e/[a-z]/Rg chr(ord($0) + (ord('A') - ord('a'))); } // Parse protocol specs from QUAFFLER.txt var specs; { var specs_current; foreach[] line (file_read("QUAFFLER.txt") =~ /[^\n]*/Ag) { if (line =~ /^\*\*\s+(\S+)\s+(\S+)/) { specs_current = [ opcode: $1, name: $2, fields: undef ]; push specs, specs_current; continue; } if (line =~ /^\s+\$(\S+):(\S+)/) { var field = [ type: $1, name: $2 ]; push specs_current.fields, field; continue; } } } // Create structs for all package types var header_opcodes; var header_structs; foreach[] p (specs) { header_structs ~= "struct qfl_pkg_$p.name {\n"; foreach[] f (p.fields) switch { case f.type ~== "string": header_structs ~= <:> : unsigned char ${f.name}_len; : char *${f.name}; ; case f.type ~== "data": header_structs ~= <:> : unsigned int ${f.name}_len; : unsigned char *${f.name}; ; case f.type ~== "byte": header_structs ~= <:> : unsigned char ${f.name}; ; case f.type ~== "word": header_structs ~= <:> : unsigned short ${f.name}; ; case f.type ~== "dword": header_structs ~= <:> : unsigned int ${f.name}; ; default: panic "Unknown protocol data type $f.type!"; } header_structs ~= "};\n\n"; header_opcodes ~= fmt("#define QFL_PKG_%-20s %s\n", to_upper(p.name), p.opcode); } // Create pkgfree code (getlen part) var code_pkgfree; foreach[] p (specs) { code_pkgfree ~= "\tcase QFL_PKG_${to_upper(p.name)}:\n"; foreach[] f (p.fields) if (f.type ~== "string" or f.type ~== "data") code_pkgfree ~= <:> : if (pkg->payload.${p.name}.${f.name}) : free(pkg->payload.${p.name}.${f.name}); ; code_pkgfree ~= "\t\tbreak;\n"; } // Create shiftdata code (getlen part) var code_shiftdata_getlen; foreach[] p (specs) { var static_fields = 1; code_shiftdata_getlen ~= "\tcase QFL_PKG_${to_upper(p.name)}:\n"; foreach[] f (p.fields) switch { case f.type ~== "string": code_shiftdata_getlen ~= "\t\t*len_p += pkg->payload.${p.name}.${f.name}_len + 1;\n"; case f.type ~== "data": code_shiftdata_getlen ~= "\t\t*len_p += pkg->payload.${p.name}.${f.name}_len + 3;\n"; case f.type ~== "byte": static_fields += 1; case f.type ~== "word": static_fields += 2; case f.type ~== "dword": static_fields += 4; default: panic "Unknown protocol data type $f.type!"; } code_shiftdata_getlen ~= "\t\t*len_p += $static_fields;\n"; code_shiftdata_getlen ~= "\t\tbreak;\n"; } // Create shiftdata code (getdata part) var code_shiftdata_getdata; foreach[] p (specs) { code_shiftdata_getdata ~= "\tcase QFL_PKG_${to_upper(p.name)}:\n"; foreach[] f (p.fields) switch { case f.type ~== "string": code_shiftdata_getdata ~= <:> : data[offset++] = pkg->payload.${p.name}.${f.name}_len; : memcpy(data+offset, pkg->payload.${p.name}.${f.name}, : pkg->payload.${p.name}.${f.name}_len); : offset += pkg->payload.${p.name}.${f.name}_len; ; case f.type ~== "data": code_shiftdata_getdata ~= <:> : data[offset++] = (pkg->payload.${p.name}.${f.name}_len & 0xff0000) >> 16; : data[offset++] = (pkg->payload.${p.name}.${f.name}_len & 0x00ff00) >> 8; : data[offset++] = (pkg->payload.${p.name}.${f.name}_len & 0x0000ff) >> 0; : memcpy(data+offset, pkg->payload.${p.name}.${f.name}, : pkg->payload.${p.name}.${f.name}_len); : offset += pkg->payload.${p.name}.${f.name}_len; ; case f.type ~== "byte": code_shiftdata_getdata ~= <:> : data[offset++] = pkg->payload.${p.name}.${f.name}; ; case f.type ~== "word": code_shiftdata_getdata ~= <:> : data[offset++] = (pkg->payload.${p.name}.${f.name} & 0xff00) >> 8; : data[offset++] = (pkg->payload.${p.name}.${f.name} & 0x00ff) >> 0; ; case f.type ~== "dword": code_shiftdata_getdata ~= <:> : data[offset++] = (pkg->payload.${p.name}.${f.name} & 0xff000000) >> 24; : data[offset++] = (pkg->payload.${p.name}.${f.name} & 0x00ff0000) >> 16; : data[offset++] = (pkg->payload.${p.name}.${f.name} & 0x0000ff00) >> 8; : data[offset++] = (pkg->payload.${p.name}.${f.name} & 0x000000ff) >> 0; ; default: panic "Unknown protocol data type $f.type!"; } code_shiftdata_getdata ~= "\t\tbreak;\n"; } // Create shiftdata code var code_pushdata; var state_counter = 1; foreach[] p (specs) { if (elementsof p.fields == 0) continue; var tt = "\t\t"; function next_state() { code_pushdata ~= "STATE(${state_counter++})\n"; code_pushdata ~= "\t\t\tpkg = &fifo->parser_current->payload.${p.name};\n"; } code_pushdata ~= <:> : case QFL_PKG_${to_upper(p.name)}: : { : struct qfl_pkg_${p.name} *pkg; ; foreach[] f (p.fields) switch { case f.type ~== "string": next_state(); code_pushdata ~= <:> :$tt GET_BYTE_OR_RETURN(); :$tt pkg->${f.name}_len = b; :$tt pkg->${f.name} = calloc(1, pkg->${f.name}_len + 1); ; next_state(); code_pushdata ~= <:> :$tt COPY_OR_RETURN(pkg->${f.name}, pkg->${f.name}_len); ; case f.type ~== "data": foreach[] bits ([ 16, 8, 0 ]) { next_state(); code_pushdata ~= <:> :$tt GET_BYTE_OR_RETURN(); :$tt pkg->${f.name}_len |= b << $bits; ; } code_pushdata ~= <:> :$tt pkg->${f.name} = calloc(1, pkg->${f.name}_len); ; next_state(); code_pushdata ~= <:> :$tt COPY_OR_RETURN(pkg->${f.name}, pkg->${f.name}_len); ; case f.type ~== "byte": next_state(); code_pushdata ~= <:> :$tt GET_BYTE_OR_RETURN(); :$tt pkg->$f.name = b; ; case f.type ~== "word": foreach[] bits ([ 8, 0 ]) { next_state(); code_pushdata ~= <:> :$tt GET_BYTE_OR_RETURN(); :$tt pkg->$f.name |= b << $bits; ; } case f.type ~== "dword": foreach[] bits ([ 24, 16, 8, 0 ]) { next_state(); code_pushdata ~= <:> :$tt GET_BYTE_OR_RETURN(); :$tt pkg->$f.name |= b << $bits; ; } default: panic "Unknown protocol data type $f.type!"; } code_pushdata ~= <:> : break; : } ; } // Create create code var header_mkpkg; var code_mkpkg; foreach[] p (specs) { var proto = "struct qfl_pkg *qfl_mkpkg_$p.name("; var argseperator = ""; foreach[] f (p.fields) { proto ~= argseperator; argseperator = ", "; switch { case f.type ~== "string": proto ~= "const char *$f.name"; case f.type ~== "data": proto ~= "unsigned char *$f.name, unsigned int ${f.name}_len"; case f.type ~== "byte": proto ~= "unsigned char $f.name"; case f.type ~== "word": proto ~= "unsigned short $f.name"; case f.type ~== "dword": proto ~= "unsigned int $f.name"; default: panic "Unknown protocol data type $f.type!"; } } proto ~= ")"; header_mkpkg ~= "extern $proto;\n"; code_mkpkg ~= <:> : $proto : { : struct qfl_pkg *pkg = qfl_pkg_create(); : struct qfl_pkg_$p.name *p = &pkg->payload.$p.name; : pkg->opcode = QFL_PKG_${to_upper(p.name)}; ; foreach[] f (p.fields) switch { case f.type ~== "string": code_mkpkg ~= <:> : p->$f.name = strdup($f.name); : p->${f.name}_len = strlen($f.name); ; case f.type ~== "data": code_mkpkg ~= <:> : p->$f.name = $f.name; : p->${f.name}_len = ${f.name}_len; ; case f.type ~== "byte" or f.type ~== "word" or f.type ~== "dword": code_mkpkg ~= <:> : p->$f.name = $f.name; ; default: panic "Unknown protocol data type $f.type!"; } code_mkpkg ~= <:> : return pkg; : } : ; } // Create dump code var code_dump; foreach[] p (specs) { code_dump ~= <:> : case QFL_PKG_${to_upper(p.name)}: : printf("[0x%02x] %s:\n", QFL_PKG_${to_upper(p.name)}, "${to_upper(p.name)}"); ; foreach[] f (p.fields) switch { case f.type ~== "string": code_dump ~= <:> : printf("-- $f.name ($f.type): %s\n", : pkg->payload.${p.name}.${f.name}); ; case f.type ~== "data": code_dump ~= <:> : printf("-- $f.name ($f.type): %d bytes\n", : pkg->payload.${p.name}.${f.name}_len); ; case f.type ~== "byte" or f.type ~== "word" or f.type ~== "dword": code_dump ~= <:> : printf("-- $f.name ($f.type): %u\n", : (unsigned int)pkg->payload.${p.name}.${f.name}); ; default: panic "Unknown protocol data type $f.type!"; } code_dump ~= <:> : break; ; } // Generate protocol.h file_write("protocol.h", <:> : // This file is auto-generated by protocol.spl from QUAFFLER.txt. : : #ifndef _QUAFFLER_PROTOCOL_H : #define _QUAFFLER_PROTOCOL_H : : ${header_opcodes =~ s/\n*$//R} : : ${header_structs =~ s/\n*$//R} : : struct qfl_pkg { : unsigned char opcode; : struct qfl_pkg *next; : union { : struct qfl_pkg_$p.name $p.name; : } payload; : }; : : ${header_mkpkg =~ s/\n*$//R} : : struct qfl_pkgfifo { : struct qfl_pkg *first; : struct qfl_pkg *last; : : struct qfl_pkg *parser_current; : unsigned int parser_offset; : unsigned int parser_state; : }; : : extern struct qfl_pkg *qfl_pkg_create(); : extern void qfl_pkg_destroy(struct qfl_pkg *pkg); : : extern struct qfl_pkgfifo *qfl_pkgfifo_create(); : extern void qfl_pkgfifo_destroy(struct qfl_pkgfifo *fifo); : : extern struct qfl_pkg *qfl_pkgfifo_shift(struct qfl_pkgfifo *fifo); : extern void qfl_pkgfifo_push(struct qfl_pkgfifo *fifo, struct qfl_pkg *pkg); : : extern void qfl_pkgfifo_shiftdata(struct qfl_pkgfifo *fifo, unsigned char **data_p, unsigned int *len_p); : extern void qfl_pkgfifo_pushdata(struct qfl_pkgfifo *fifo, unsigned char *data, unsigned int len); : : extern void qfl_pkg_dump(const struct qfl_pkg *pkg); : : #endif ); // Generate protocol.c file_write("protocol.c", <:> : // This file is auto-generated by protocol.spl from QUAFFLER.txt. : : #include : #include : #include : #include : : #include "quaffler.h" : : struct qfl_pkg *qfl_pkg_create() : { : return calloc(1, sizeof(struct qfl_pkg)); : } : : void qfl_pkg_destroy(struct qfl_pkg *pkg) : { : switch (pkg->opcode) : { : ${code_pkgfree =~ s/\n*$//R} : } : : free(pkg); : } : : struct qfl_pkgfifo *qfl_pkgfifo_create() : { : return calloc(1, sizeof(struct qfl_pkgfifo)); : } : : void qfl_pkgfifo_destroy(struct qfl_pkgfifo *fifo) : { : struct qfl_pkg *p; : : if (fifo->parser_current) : qfl_pkg_destroy(fifo->parser_current); : : while ((p = qfl_pkgfifo_shift(fifo)) != 0) : qfl_pkg_destroy(p); : : free(fifo); : } : : struct qfl_pkg *qfl_pkgfifo_shift(struct qfl_pkgfifo *fifo) : { : struct qfl_pkg *ret; : : if (!fifo->first) : return 0; : : ret = fifo->first; : fifo->first = ret->next; : : if (!fifo->first) : fifo->last = 0; : : ret->next = 0; : return ret; : } : : void qfl_pkgfifo_push(struct qfl_pkgfifo *fifo, struct qfl_pkg *pkg) : { : if (!fifo->last) { : fifo->first = pkg; : fifo->last = pkg; : return; : } : : fifo->last->next = pkg; : fifo->last = pkg; : } : : void qfl_pkgfifo_shiftdata(struct qfl_pkgfifo *fifo, unsigned char **data_p, unsigned int *len_p) : { : unsigned int offset = 0; : unsigned char *data; : *len_p = 0; : : struct qfl_pkg *pkg = qfl_pkgfifo_shift(fifo); : if (!pkg) return; : : switch (pkg->opcode) : { : ${code_shiftdata_getlen =~ s/\n*$//R} : } : : if (*len_p == 0) return; : data = malloc(*len_p); : data[offset++] = pkg->opcode; : : switch (pkg->opcode) : { : ${code_shiftdata_getdata =~ s/\n*$//R} : } : : qfl_pkg_destroy(pkg); : : assert(offset == *len_p); : *data_p = data; : } : : void qfl_pkgfifo_pushdata(struct qfl_pkgfifo *fifo, unsigned char *data, unsigned int len) : { : unsigned int i = 0; : unsigned char *d = data; : unsigned char b; : : #define GET_BYTE_OR_RETURN() do { \ : if (i == len) return; \ : else b = d[i++]; \ : } while (0) : : #define COPY_OR_RETURN(_dest, _size) do { \ : char *dest = (char*)(_dest) + fifo->parser_offset; \ : int size = (_size) - fifo->parser_offset; \ : if (size > len-i) { \ : memcpy(dest, d+i, len-i); \ : fifo->parser_offset += len-i; \ : return; \ : } else { \ : memcpy(dest, d+i, size); \ : i += size; \ : } \ : } while (0) : : #define STATE(statenumber) \ : fifo->parser_offset = 0; fifo->parser_state = statenumber; state_ ## statenumber: : : parse_next_package: : if (!fifo->parser_current) { : GET_BYTE_OR_RETURN(); : fifo->parser_current = qfl_pkg_create(); : fifo->parser_current->opcode = b; : fifo->parser_offset = 0; : fifo->parser_state = 0; : } : : switch (fifo->parser_state) : { : case $i: : goto state_$i; : } : : state_0: : switch (fifo->parser_current->opcode) : { : ${code_pushdata =~ s/\n*$//R} : } : : qfl_pkgfifo_push(fifo, fifo->parser_current); : fifo->parser_current = 0; : : goto parse_next_package; : } : : void qfl_pkg_dump(const struct qfl_pkg *pkg) : { : switch (pkg->opcode) : { : ${code_dump =~ s/\n*$//R} : } : } : : ${code_mkpkg =~ s/\n*$//R} );