Last active
December 3, 2024 15:56
-
-
Save yne/21648c9e48d9e9ef84f3d303d1185c96 to your computer and use it in GitHub Desktop.
615A 8002/8004 LUH JSON dumper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// BUILD: | |
// cc luh.c -o luh | |
// USAGE: | |
// luh < EXAMPLE.LUH > EXAMPLE.json | |
#include <stdint.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <errno.h> | |
#include <string.h> | |
uint16_t crc16_tab[256]; | |
void crc16_init() { | |
uint16_t i,j,c;// to please c89 gods | |
for (i = 0; i < sizeof(crc16_tab) / sizeof(*crc16_tab); i++) | |
for (j = 0, c = i << 8; j < 8; j++, c = c << 1) | |
crc16_tab[i] = ( crc16_tab[i] << 1 ) ^ ( (crc16_tab[i] ^ c) & 0x8000 ? 0x1021 : 0); | |
} | |
uint16_t crc16 = 0xFFFF; | |
void crc16_digest(uint8_t data) { | |
crc16 = (crc16 << 8) ^ crc16_tab[((crc16 >> 8) ^ data) & 0xFF]; | |
} | |
uint32_t crc32_tab[256]; | |
void crc32_init() { | |
uint32_t i,j;// to please c89 gods | |
for (i = 0; i < sizeof(crc32_tab) / sizeof(*crc32_tab); i++) | |
for (j = 0, crc32_tab[i] = i << 24; j < 8; j++) | |
crc32_tab[i] = crc32_tab[i] & 0x80000000 ? crc32_tab[i] << 1 ^ 0x04C11DB7 : crc32_tab[i] << 1; | |
} | |
uint32_t crc32 = 0xFFFFFFFF; | |
void crc32_digest(uint8_t data) { | |
crc32 = (crc32 << 8) ^ crc32_tab[(( crc32 >> 24) ^ data) & 0xFF]; | |
} | |
char* hash(char* path, void (*digest)(uint8_t)) { | |
uint8_t c; | |
int fd = open(path,0); | |
if(fd < 0)return path; | |
while(read(fd,&c,sizeof(c)) > 0)digest(c); | |
close(fd); | |
return NULL; | |
} | |
size_t get_total = 0;//total read | |
uint8_t get() { | |
uint8_t c; | |
if(read(0, &c, sizeof(c)) == EOF) exit(c); | |
get_total += sizeof(c); | |
crc16_digest(c); | |
crc32_digest(c); | |
return c; | |
} | |
int indent=0, iter[10]={0}; | |
void attr(char*name){printf("%s\n%*s%s%s%s", iter[indent]++?",":"",4*indent,"", *name?"\"":"",name, *name?"\": ":"");} | |
void obj_init(char*name){attr(name);printf("{");indent++;} | |
void arr_init(char*name){attr(name);printf("[");indent++;} | |
void obj_fini(){iter[indent]=0;printf("\n%*s}",4*--indent,"");} | |
void arr_fini(){iter[indent]=0;printf("\n%*s]",4*--indent,"");} | |
size_t hex(size_t bit, char*name) { | |
size_t acc=0; | |
attr(name); | |
for(; bit >= 8; bit -= 8) { | |
uint8_t c = get(); | |
// printf("%02X", c); | |
acc = c | (acc << 8); | |
} | |
printf("%i",acc); | |
return acc; | |
} | |
typedef struct {char str[4096];size_t len;} String; | |
String last_str; | |
void str(size_t byte, char*name) { | |
attr(name); printf("\""); | |
size_t pad = byte & 0x01; | |
for(last_str.len=0;byte--;last_str.len++) { | |
char c = get(); | |
printf("%c", c); | |
last_str.str[last_str.len]=c; | |
} | |
last_str.str[last_str.len] = '\0'; | |
printf("\""); | |
while(pad--) { | |
get();//fprintf(stderr," # 0x%02X", get()); | |
} | |
} | |
size_t jump(size_t to) { | |
if (to) { | |
//printf("\n-- jump %i Bytes to 0x%X (%08X)\n", to - get_total, to, to/sizeof(uint16_t)); | |
while(get_total<to)get();//fprintf(stderr," # 0x%02X",get()); | |
} | |
return to; | |
} | |
int next(size_t*pointers, size_t count) { | |
int i,min=~0,ret = 0; | |
for (i = 0; i < count; i++) { | |
if ((pointers[i]*2) >= get_total && pointers[i] < min) { | |
min = pointers[i]; | |
ret = i; | |
} | |
} | |
// printf("\n-- next section from %04X is [%i] %04X\n",get_total, ret, pointers[ret]*2); | |
return ret; | |
} | |
String paths[256]; | |
size_t paths_total; | |
void parse8004(){ | |
size_t s,t,u, type; | |
hex(16, "partFlags"); | |
obj_init("pointers"); | |
size_t pointers[] = { | |
0, // use section[0] as special case to break the while | |
hex(32, "loadPnLength"), | |
hex(32, "numberOfTargetHWID"), | |
hex(32, "numberOfDataFiles"), | |
hex(32, "numberOfSupportFiles"), | |
hex(32, "userDefinedData"), | |
hex(32, "loadTypeDescriptionLength"), | |
hex(32, "numberOfTargetHWIDWithPositions"), | |
hex(32, "loadCheckValueLength"), | |
}; | |
obj_fini(); | |
while((type=next(pointers, sizeof(pointers)/sizeof(*pointers)))){ | |
jump(pointers[type]*2); | |
switch (type){ | |
case 1: | |
s=hex(16,"loadPnLength"); | |
str(s?:1,"loadPn");/* s?:1 because "One or more 16-bit words" (Note1) */ | |
break; | |
case 6://pointer order are different than field definition order in the spec | |
s=hex(16,"loadTypeDescriptionLength"); | |
str(s, "loadTypeDescription"); | |
hex(16, "loadTypeID"); | |
break; | |
case 2: | |
s=hex(16,"targetHWIDTotal"); | |
arr_init("targetHWID"); | |
while(s--) { | |
obj_init(""); | |
t=hex(16, "length"); | |
str(t?:1, "value"); | |
obj_fini(); | |
} | |
arr_fini(); | |
break; | |
case 7://strange definition order | |
s=hex(16, "targetHWIDWithPositionsTotal"); | |
arr_init("targetHWIDWithPositions"); | |
while(s--) { | |
obj_init(""); | |
t=hex(16, "targetHWIDLength"); | |
str(t?:1, "targetHWID"); | |
t=hex(16, "positionsTotal"); | |
arr_init( "positions"); | |
while(t--) { | |
obj_init(""); | |
u=hex(16, "length"); | |
str(u?:1, "value"); | |
obj_fini(); | |
} | |
arr_fini(); | |
obj_fini(); | |
} | |
arr_fini(); | |
break; | |
case 3: | |
s=hex(16, "dataFileTotal"); | |
arr_init("dataFile"); | |
while(s--) { | |
obj_init(""); | |
hex(16, "pointer"); | |
t=hex(16,"nameLength"); | |
str(t?:1,"name"); | |
paths[paths_total++] = last_str; // printf("[%.*s]\n", last_str.len, last_str.str); | |
t=hex(16,"pnLength"); | |
str(t?:1,"pn"); | |
hex(32, "length"); | |
hex(16, "crc"); | |
{ | |
uint16_t crc16_bkp = crc16; | |
crc16 = 0xFFFF; // need a fresh start on this file | |
attr("crc__expected"); | |
char* missing = hash(paths[paths_total-1].str, crc16_digest); | |
if (missing) { | |
printf("\"%s:%s\"", missing, strerror(errno)); | |
} else { | |
printf("\"0x%04X\"", crc16); | |
} | |
crc16 = crc16_bkp; | |
} | |
hex(64, "lengthBytes"); | |
t=hex(16,"checkValueLength"); | |
if (t) { // "Omit if Data File Check Value Length is set to 0x0000" | |
hex(16, "checkValueType"); | |
str(t?:1,"checkValue"); | |
} | |
obj_fini(); | |
} | |
arr_fini(); | |
break; | |
case 4: | |
s=hex(16, "supportFilesTotal"); | |
arr_init("supportFiles"); | |
while(s--) { | |
obj_init(""); | |
hex(16, "pointer"); | |
t=hex(16,"nameLength"); | |
str(t, "name"); // 0 | |
t=hex(16,"pnLength"); | |
str(t, "pn"); // 0 | |
hex(32, "length"); | |
hex(16, "crc"); | |
t=hex(16,"checkValueLength"); | |
if (t) { // "Omit if Support File Check Value Length is set to 0x0000" | |
hex(16, "checkValueType"); | |
str(t?:1,"checkValue"); | |
} | |
obj_fini(); | |
} | |
arr_fini(); | |
break; | |
case 5: | |
hex(16, "userDefinedData"); | |
break; | |
case 8: | |
t=hex(16, "loadCheckValueLength"); | |
if (t) { // "Omit if Load Check Value Length is set to 0x0000" | |
hex(16, "loadCheckValueType"); | |
hex(16, "loadCheckValue"); | |
} | |
uint16_t expected16 = crc16; | |
hex(16, "headerFileCrc"); | |
attr("headerFileCrc__expected"); | |
printf("%i", expected16); | |
char* missing = NULL; | |
for (s = 0; s < paths_total && missing==NULL; s++) { | |
missing = hash(paths[s].str, crc32_digest); | |
} | |
uint32_t expected32 = crc32; // save it before reading anymore data ! | |
hex(32, "loadCrc"); | |
attr("loadCrc__expected"); | |
if (missing) { | |
printf("\"%s:%s\"", missing, strerror(errno)); | |
} else { | |
printf("%i", ~expected32); | |
} | |
break; | |
} | |
} | |
attr("__read_bytes");printf("%zu", get_total); | |
attr("__read_hword");printf("%zu", get_total/2); | |
// fprintf(stderr,"\n# total read: %zuBytes (0x%lXh)\n", get_total, get_total/2); | |
} | |
// I can't find the ARINC spec for this version, let's blind guess every field | |
void parse8002(){ | |
size_t s,t,type; | |
obj_init("pointers"); | |
size_t pointers[] = { | |
0, // use section[0] as special case to break the while | |
hex(32, "loadPnLength"), | |
hex(32, "numberOfTargetHWID"), | |
hex(32, "numberOfDataFiles"), | |
hex(32, "numberOfSupportFiles"), | |
hex(32, "userDefinedData"), | |
}; | |
obj_fini(); | |
while((type=next(pointers, sizeof(pointers)/sizeof(*pointers)))){ | |
jump(pointers[type]*2); | |
switch (type){ | |
case 1: | |
s=hex(16,"loadPnLength"); | |
str(s?:1,"loadPn");/* s?:1 because "One or more 16-bit words" (Note1) */ | |
break; | |
case 2: | |
s=hex(16,"targetHWIDTotal"); | |
arr_init("targetHWID"); | |
while(s--) { | |
t=hex(16, "length"); | |
str(t?:1, "value"); | |
} | |
arr_fini(); | |
break; | |
case 3: | |
s=hex(16, "fataFilesTotal"); | |
//attr("DataFiles") | |
arr_init("fataFiles"); | |
while(s--) { | |
obj_init(""); | |
hex(16, "pointer"); | |
t=hex(16,"nameLength"); | |
str(t?:1,"name"); | |
t=hex(16,"pnLength"); | |
str(t?:1,"pn"); | |
hex(32, "length"); | |
hex(16, "crc"); | |
obj_fini(); | |
} | |
arr_fini(); | |
break; | |
default: | |
fprintf(stderr,"\n# unhandled pointer[%zu]@0x%lX\n",type,get_total); | |
return; | |
} | |
} | |
hex(16, "userDataPointer:"); | |
hex(16, "crc1:"); | |
hex(16, "crc2:"); | |
hex(16, "crc3:"); | |
fprintf(stderr,"\n# total read: %zuBytes (0x%lXh)\n", get_total, get_total/2); | |
} | |
int main(int argc, char**argv) { | |
if (isatty(0)) { | |
return fprintf(stderr, "USAGE:\n\t%s < sample.LUH\n", argv[0]); | |
} | |
crc16_init(); | |
crc32_init(); | |
obj_init(""); | |
hex(32, "headerFileLength"); | |
uint32_t version = hex(16, "loadFileFormatVersion"); | |
if (version==0x8004) parse8004(); | |
else if (version==0x8002) parse8002(); | |
else printf("\"error\":\"unsupported LUH version %04X\"", version); | |
obj_fini(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment