-
-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
unpackdat: Update algorithm #8
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,16 @@ | ||
/* (c) fenugrec 2015-2016 | ||
* GPLv3 | ||
* unpack nissan .dat repro files. | ||
* currently recognizes only one chunk format: "FMT SRC TGT LEN 34 82 AH AM AL CL <CL bytes of data> <CRC1> <CRC2> <CKS>" | ||
* not very clever about possible variants, plenty of hardcoded junk. Also doesn't check CRC and cks. | ||
* unpack nissan .dat, .kwp and other repro files. | ||
* currently recognizes two chunk formats: | ||
* extended "FMT SRC TGT LEN 34 82 AH AM AL CL <CL bytes of data> <CRC1> <CRC2> <CKS>" | ||
* short "FMT_LEN SRC TGT 34 82 AH AM AL CL <CL bytes of data> <CRC1> <CRC2> <CKS>" | ||
* Also doesn't check CRC and cks. | ||
* | ||
*/ | ||
|
||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <stddef.h> | ||
#include <stdlib.h> //malloc etc | ||
#include <string.h> //memcpy | ||
|
||
|
@@ -16,15 +19,60 @@ | |
|
||
FILE *dbg_stream; | ||
|
||
#define FMT_CHUNK_EXT 0x80 | ||
#define CRC_TAIL_LEN 3 | ||
#define MAX_DAT_OFFSET 0xffffff | ||
|
||
struct datfile { | ||
FILE *hf; | ||
u32 siz; //size of .dat, in bytes | ||
u32 buf_size; //size of .dat, in bytes | ||
uint8_t *buf; //copied here | ||
|
||
u32 dbuf_size; //size of .bin, in bytes | ||
uint8_t *dbuf; //unpacked data | ||
}; | ||
|
||
typedef struct { | ||
uint8_t sign[3]; // 0xA1 XX XX | ||
|
||
char part_number[5]; | ||
uint32_t date; //? | ||
char part_number2[5]; | ||
|
||
char ver_str[6]; // e.g. LBC ZE1/ VCM | ||
|
||
uint32_t off17; // 00 00 00 80 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I count properly, this field is not 4-byte aligned in the (packed) struct, correct ? |
||
char zeroes[5]; | ||
|
||
uint32_t off20; // 82 00 00 0x | ||
uint8_t cks; //? | ||
} dat_header_t; | ||
|
||
typedef struct { | ||
uint8_t fmt; // 0x80 if exist extra byte ckslen | ||
// or 0x80 + len (ckslen missed) | ||
uint8_t src; | ||
uint8_t tgt; | ||
|
||
uint8_t ckslen; // data for cks from cmd upto cks. | ||
// byte missed if fmt != 0x80 | ||
} chunk_header_t; | ||
|
||
typedef struct { | ||
uint16_t cmd; // 34 82 | ||
|
||
uint8_t offset_hi; | ||
uint8_t offset_mid; | ||
uint8_t offset_low; | ||
|
||
uint8_t chunklen; | ||
uint8_t payload[]; // chunklen size | ||
|
||
// CRC at the end | ||
//uint16_t crc; | ||
//uint8_t cks; | ||
} chunk_t; | ||
|
||
|
||
//load ROM to a new buffer | ||
//ret 0 if OK | ||
|
@@ -45,7 +93,7 @@ static int open_dat(struct datfile *rf, const char *fname) { | |
if ((!file_len) || (file_len > 3*1024*1024L)) { | ||
printf("empty, or huge file (length %lu)\n", (unsigned long) file_len); | ||
} | ||
rf->siz = file_len; | ||
rf->buf_size = file_len; | ||
|
||
buf = malloc(file_len); | ||
if (!buf) { | ||
|
@@ -54,7 +102,9 @@ static int open_dat(struct datfile *rf, const char *fname) { | |
return -1; | ||
} | ||
|
||
rf->dbuf = malloc(file_len); | ||
// Allocate max size for output according to offset field dimension | ||
rf->dbuf_size = MAX_DAT_OFFSET; | ||
rf->dbuf = malloc(rf->dbuf_size); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, but maybe the size should be +1 : if the last chunk has len=0x80 starting at 0xff ff80 then the last byte would overflow. |
||
if (!rf->dbuf) { | ||
printf("malloc choke\n"); | ||
free(buf); | ||
|
@@ -90,47 +140,105 @@ void close_dat(struct datfile *rf) { | |
return; | ||
} | ||
|
||
#define DCHUNK_SIZE 0x8D //chunk size is very happy | ||
#define DCHUNK_DLEN 0x80 //bytes of payload | ||
// "FMT SRC TGT LEN 34 82 AH AM AL CL <CL bytes of data> <CRC1> <CRC2> <CKS>" | ||
void unpack_dat(FILE *outf, uint8_t *dest, const uint8_t *src, u32 siz) { | ||
// 1- search for "88 34 82" | ||
const uint8_t pm[]={0x88, 0x34, 0x82}; | ||
void print_hex(const void *src, int len) { | ||
const uint8_t *ptr = src; | ||
for (int i = 0; i < len; i++) { | ||
printf(" %02x", ptr[i]); | ||
} | ||
printf("\n"); | ||
} | ||
|
||
void unpack_dat(FILE *outf, struct datfile *rf, bool trim) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, passing in a |
||
const uint8_t *src = rf->buf; | ||
const u32 src_size = rf->buf_size; | ||
const uint8_t pm[] = {0x34, 0x82}; | ||
const uint8_t *psrc; //misc pointer inside src buf | ||
uint8_t *orig_dest = dest; | ||
uint32_t curaddr = 0; | ||
uint8_t *dest = NULL; | ||
|
||
const chunk_header_t *ch_header = NULL; | ||
|
||
unsigned long plsiz = 0; //total payload bytes | ||
uint8_t chklen = 0; | ||
uint32_t offset = 0; | ||
uint32_t firstchaddr = 0; | ||
unsigned long outsize = 0; | ||
unsigned pl_numc = 0; //# of chunks | ||
|
||
psrc = u8memstr(src, siz, pm, 3); | ||
if (!psrc) return; | ||
|
||
//2- point to "AH AM AL..." and start | ||
psrc += 3; | ||
while ( psrc < &src[siz - 1] ) { | ||
// parse AH:AM:AL | ||
uint32_t temp_addr = reconst_32(psrc); | ||
temp_addr >>= 8; | ||
if (temp_addr != (curaddr + DCHUNK_DLEN)) { | ||
printf("addr skip @ %lX\n", (unsigned long) temp_addr); | ||
const dat_header_t *dath = (const dat_header_t *)src; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like casting a raw buffer to a struct like this, especially if the struct has unaligned bytes. It also leads to assuming host endianness. |
||
|
||
printf("SIGN:"); | ||
print_hex(dath->sign, 3); | ||
printf("PART1: %.*s\n", 5, dath->part_number); | ||
printf("DATE:"); | ||
print_hex(&dath->date, sizeof(dath->date)); | ||
printf("PART2: %.*s", 5, dath->part_number2); | ||
print_hex(dath->part_number2, 5); | ||
printf("VER: %.*s", 6, dath->ver_str); | ||
print_hex(dath->ver_str, 6); | ||
|
||
// TODO get rid of search. The first chunk is located at a constant offset 0x25 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, we need still to need search : I have at least 9HA7D.dat where the first chunk is at offset 0x73. |
||
psrc = u8memstr(src, src_size, pm, 2); | ||
if (!psrc) { | ||
printf("Pattern 34 82 not found\n"); | ||
return; | ||
} | ||
|
||
ch_header = (const chunk_header_t *)(psrc - sizeof(chunk_header_t)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here too, casting directly to struct. |
||
if (ch_header->fmt != FMT_CHUNK_EXT) { | ||
// fallback to short chunk | ||
ch_header = (const chunk_header_t *)(psrc - offsetof(chunk_header_t, ckslen)); | ||
} | ||
|
||
printf("SRC: %#x\n", ch_header->src); | ||
printf("TGT: %#x\n", ch_header->tgt); | ||
|
||
while (true) { | ||
const chunk_t *ch = NULL; | ||
if (ch_header->fmt == FMT_CHUNK_EXT) { | ||
// 80 SRC TGT LEN 34 82 | ||
ch = (chunk_t*)((uint8_t*)ch_header + sizeof(chunk_header_t)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. casting to struct |
||
} else { | ||
// XX SRC TGT 34 82 | ||
ch = (chunk_t*)((uint8_t*)ch_header + offsetof(chunk_header_t, ckslen)); | ||
} | ||
// TODO check whether ch buffer is not go beyond the end of file | ||
|
||
if (ch->cmd != 0x8234) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, you're assuming host endianness. Why not memcmp() with pm[] ? |
||
printf("Unexpected cmd pattern %#x at offset %#llx\n", | ||
ch->cmd, ((uint8_t*)ch - src + offsetof(chunk_t, cmd))); | ||
break; | ||
} | ||
|
||
offset = ch->offset_hi << 16 | ch->offset_mid << 8 | ch->offset_low; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer either using a truncated |
||
if (pl_numc == 0) { | ||
if (trim) firstchaddr = offset; | ||
printf("Trim gap in .bin before first chunk offset 0x%06x\n", offset); | ||
|
||
printf("Chunks:\n"); | ||
printf("# : offset @ size\n"); | ||
} | ||
curaddr = temp_addr; | ||
psrc += 3; //seek to "CL" | ||
if (*psrc != DCHUNK_DLEN) { | ||
printf("bad chunk len ?\n"); | ||
return; | ||
|
||
pl_numc++; | ||
chklen = ch->chunklen; | ||
printf("#%04d: 0x%06x @ %d\n", pl_numc, offset, chklen); | ||
dest = rf->dbuf + offset; | ||
memcpy(dest, ch->payload, chklen); | ||
|
||
ch_header = (const chunk_header_t *)((uint8_t*)ch + offsetof(chunk_t, payload) + chklen + CRC_TAIL_LEN); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. casting to struct |
||
if (((uint8_t*)ch_header - src) >= src_size) { | ||
// This is the end of file | ||
break; | ||
} | ||
psrc += 1; //seek to payload data | ||
memcpy(dest, psrc, DCHUNK_DLEN); | ||
pl_numc += 1; | ||
dest += DCHUNK_DLEN; | ||
plsiz += DCHUNK_DLEN; | ||
psrc += (DCHUNK_SIZE - 4); //realign to AH | ||
} | ||
|
||
outsize = offset + chklen - firstchaddr; | ||
dest = rf->dbuf + firstchaddr; | ||
printf("total: %u chunks; %lu (0x%06lX) bytes in PL.\n", | ||
pl_numc, plsiz, plsiz); | ||
fwrite(orig_dest, 1, plsiz, outf); | ||
pl_numc, outsize, outsize); | ||
|
||
if (pl_numc > 0) | ||
fwrite(dest, outsize, 1, outf); | ||
else | ||
printf("No recognized chunks to save in file\n"); | ||
return; | ||
} | ||
|
||
|
@@ -140,9 +248,10 @@ int main(int argc, char *argv[]) | |
bool dbg_file; //flag if dbgstream is a real file | ||
FILE *pl0; | ||
struct datfile rf = {0}; | ||
bool trim = true; | ||
|
||
if (argc !=3) { | ||
printf("%s <file.dat> <out.dat> : unpack payload to <out.dat> etc\n",argv[0]); | ||
if (argc < 3) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what was wrong with |
||
printf("%s <file.dat> <out.dat>: unpack payload to <out.dat>\n",argv[0]); | ||
return 0; | ||
} | ||
|
||
|
@@ -167,7 +276,7 @@ int main(int argc, char *argv[]) | |
|
||
/* add header to dbg log */ | ||
fprintf(dbg_stream, "\n********************\n**** Started analyzing %s\n", argv[1]); | ||
unpack_dat(pl0, rf.dbuf, rf.buf, rf.siz); | ||
unpack_dat(pl0, &rf, trim); | ||
|
||
printf("\n"); | ||
close_dat(&rf); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume MAX_DAT_OFFSET is to cover the maximum "AH:AM:AL" value ?