#include <iostream>#include <iomanip>#include <fstream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstring>#include <cmath> typedef unsigned char u8;typedef unsigned long long u64;typedef signed long long s64; const int nTypes = 5; const char *typeNames[] = { "binary", "dec", "hex", "float", "string"}; enum Field_Type { BIN, DEC, HEX, FLOAT, STRING, INVALID}; struct Field { int bytes_per_elem; // multiply by n_elems to get the full size of this field Field_Type type; // variable type bool use_caps = false; // determines whether to capitalise digits, eg. 0x4A vs 0x4a int dgs_int = -1; // number of digits for the integral part. -1 means fewest digits possible without truncation int dgs_frac = -1; // number of digits for the fractional part char name[64]; // variable name int n_elems = 1; // array size bool is_signed = false; // controls signedness if the variable is an integer type bool big_endian = false; // controls endianness if the variable is not a string}; bool parse_field(Field& fld, std::vector<std::string>& args) { if (args.size() < 3) { std::cout << "Not enough information to create a field"; return false; } fld.bytes_per_elem = std::stoi(args[0]); if (fld.bytes_per_elem < 1) { std::cout << "Invalid element size " << fld.bytes_per_elem; return false; } const char *type_str = args[1].c_str(); const char *p = type_str; int i; for (i = 0; *p; i++, p++) { if (*p >= 'A' && *p <= 'Z') { args[1][i] += 0x20; fld.use_caps = true; } } int tok_len = i; fld.type = INVALID; for (i = 0; i < nTypes; i++) { const char *a = type_str; const char *b = typeNames[i]; bool match = true; for (int j = 0; *a && *b; a++, b++, j++) { if (*a != *b) { match = false; break; } } if (match) { fld.type = (Field_Type)i; break; } } if (fld.type == INVALID) { std::cout << "Invalid field type \"" << args[1] << "\""; return false; } if (fld.type == FLOAT && fld.bytes_per_elem != 4 && fld.bytes_per_elem != 8) { std::cout << "Unsupported floating-point size " << fld.bytes_per_elem; return false; } if (fld.type != STRING && fld.bytes_per_elem > 8) { std::cout << "Unsupported size " << fld.bytes_per_elem << " for non-string type"; return false; } int cur = 2; // Check to see if this argument is a format specifier if ((args[2][0] >= '0' && args[2][0] <= '9') || args[2][0] == '.') { int dot_idx = args[2].find('.'); if (dot_idx >= 0 && dot_idx < args[2].size() - 1) fld.dgs_frac = std::stoi(args[2].substr(dot_idx + 1)); if (dot_idx != 0) fld.dgs_int = std::stoi(args[2]); cur++; } if (fld.type == STRING && (fld.dgs_int >= 0 || fld.dgs_frac >= 0)) { std::cout << "Format specifier for string type is not supported"; return false; } if (fld.type != FLOAT && fld.dgs_frac >= 0) { std::cout << "Fractional digits for non-float types is not supported"; return false; } strncpy(fld.name, args[cur++].c_str(), 63); fld.name[63] = 0; // Optional args at the end for (int i = cur; i < args.size(); i++) { // if it's a number, treat it as the number of elements if (args[i][0] >= '0' && args[i][0] <= '9') { fld.n_elems = std::stoi(args[i]); if (fld.n_elems < 1) { std::cout << "Invalid array size " << fld.n_elems; return false; } } switch (args[i][0]) { case 'b': case 'B': fld.big_endian = true; break; case 'l': case 'L': fld.big_endian = false; break; case 's': case 'S': fld.is_signed = true; break; case 'u': case 'U': fld.is_signed = false; break; } } if (fld.big_endian && fld.type == STRING) { std::cout << "Big-endian format for string type is not supported"; } return true;} void print_field(Field& fld, u8 *elem) { if (fld.type == STRING) { char *str = (char*)elem; std::cout << std::string(str, fld.bytes_per_elem); return; } int n = 1; const bool sys_be = *((char*)&n) != 1; u8 temp[8] = {0}; if (fld.big_endian != sys_be) { int n = fld.bytes_per_elem; for (int j = 0; j < n; j++) temp[n-1-j] = elem[j]; } else memcpy(&temp[0], elem, fld.bytes_per_elem); // TODO: support digit counts, integral and fractional if (fld.type == FLOAT) { char format[16] = {0}; if (fld.dgs_int <= 0 && fld.dgs_frac <= 0) strcpy(format, "%g"); else if (fld.dgs_int > 0 && fld.dgs_frac > 0) sprintf(format, "%%%d.%df", fld.dgs_int, fld.dgs_frac); else if (fld.dgs_int > 0) sprintf(format, "%%%d.f", fld.dgs_int); else sprintf(format, "%%.%df", fld.dgs_frac); if (fld.bytes_per_elem == 8) printf(format, *(double*)(&temp[0])); else if (fld.bytes_per_elem == 4) printf(format, *(float*)(&temp[0])); return; } u64 uvar = *(u64*)(&temp[0]); if (!uvar && fld.dgs_int <= 0) { std::cout << "0"; return; } int base = 0; switch (fld.type) { case BIN: base = 2; std::cout << "0b"; break; case DEC: base = 10; break; case HEX: base = 16; std::cout << "0x"; break; } bool neg = (uvar & (1 << 63)); s64 var = *(s64*)(&temp[0]); char num[68] = {0}; char *p = &num[0]; if (neg && fld.is_signed) { *p++ = '-'; var = -var; } int i = 0; while (true) { if (fld.dgs_int < 0) { if (!var) break; } else if (i >= fld.dgs_int) break; int d = (int)(var % (s64)base); var /= (s64)base; if (d >= 10) { if (fld.use_caps) *p++ = d - 10 + 'A'; else *p++ = d - 10 + 'a'; } else *p++ = d + '0'; i++; } std::string var_str(num); std::reverse(var_str.begin(), var_str.end()); std::cout << var_str;} void read_data(FILE *f, std::vector<u8>& buf) { u8 temp[256]; while (1) { int len = fread(temp, 1, 256, f); if (len <= 0) break; buf.insert(buf.end(), temp, temp+len); }} int main(int argc, char **argv) { if (argc < 2) { std::cout << "Invalid arguments\n" "Usage: " << argv[0] << " <struct file> [input data file]\n" "The default source for input data is stdin\n" "The format for each line in the struct file goes as such:\n" " <bytes> <type> [print format] <name> [array length] [signedness] [endianness]\n" " where <type> can be float, string, dec, hex or binary.\n" "Properties marked by square brackets are optional.\n" ; return 1; } std::ifstream in(argv[1]); if (!in) { std::cout << "Could not open struct \"" << argv[1] << "\""; return 2; } std::vector<Field> model; std::string str; int line_no = 0; bool failed = false; while (std::getline(in, str)) { line_no++; if (str.size() <= 0) continue; std::vector<std::string> tokens; int span = 0; const char *p = str.c_str(); while (*p) { if (*p == ' ') { if (span) tokens.push_back(std::string(p-span, p)); span = 0; } else span++; p++; } if (span) tokens.push_back(std::string(p-span, p)); Field fld; if (!parse_field(fld, tokens)) { std::cout << " (line " << line_no << ")\n"; failed = true; break; } model.push_back(fld); } in.close(); if (failed) return 3; FILE *f = NULL; if (argc > 2) { f = fopen(argv[2], "rb"); if (!f) { std::cout << "Could not open data file \"" << argv[2] << "\""; return 4; } } if (!f) f = stdin; std::vector<u8> data; read_data(f, data); if (f != stdin) fclose(f); int struct_size = 0; int name_width = 0; for (Field& fld : model) { struct_size += fld.bytes_per_elem * fld.n_elems; int len = strlen(fld.name); if (len > name_width) name_width = len; } if (data.size() < struct_size) { std::cout << "Not enough data to fill the struct\n" " required = " << struct_size << " bytes, given = " << data.size() << " bytes\n" ; return 5; } u8 *elem = &data[0]; for (Field& fld : model) { std::cout << std::setw(name_width) << fld.name << " = "; for (int i = 0; i < fld.n_elems; i++) { if (i > 0) std::cout << ", "; print_field(fld, elem); elem += fld.bytes_per_elem; } std::cout << "\n"; } return 0;}