/* Copyright (c) 2003-2005 Gianni Tedesco * Released under the terms of the GNU GPL v2 * * Reads Symantec Act! databases */ #include #include #include #include #include #include #include #include #include #include #include "bytesex.h" #define DB3_VER 0x3 #define DB3_TYPE_CHAR 'C' #define DB3_TYPE_NUM 'N' #define DB3_TYPE_LOGIC 'L' #define DB3_TYPE_MEMO 'M' #define DB3_TYPE_DATA 'D' struct db3_hdr { u_int8_t ver; u_int8_t l_update[3]; /* yymmdd update time */ u_int32_t count; /* num records */ u_int16_t flen; /* length of field descriptor block */ u_int16_t rec_len; /* record len */ u_int8_t pad[20]; }; struct db3_field { char name[11]; char type; u_int32_t ptr; u_int8_t len; char decimal; char fill[14]; }; int mapfile(const char *fn, char **begin, size_t *sz) { struct stat st; int fd; fd = open(fn, O_RDONLY); if ( fd < 0 ) { perror("open()"); return -1; } if ( fstat(fd, &st) ) { perror("fstat()"); close(fd); return -1; } *begin = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if ( *begin == MAP_FAILED ) { close(fd); return -1; } *sz = st.st_size; close(fd); return 0; } void do_db3(const char *fn) { char *begin, *end; struct db3_hdr *h; struct db3_field *f; int n, r, i, j; size_t sz; char *chunk; if ( mapfile(fn, &begin, &sz) < 0 ) return; end = begin + sz; if ( sz < sizeof(*h) ) goto done; h = (struct db3_hdr *)begin; if ( h->ver != DB3_VER ) goto done; n = ((le_16(h->flen)-sizeof(*h))/sizeof(struct db3_field)); r = le_16(h->rec_len); chunk = begin + le_16(h->flen); f = (struct db3_field *)(begin + sizeof(*h)); printf("## Filename: %s\n", fn); printf("## Schema:"); for(i=0; i < n; i++,f++) { const char *tn; switch(f->type) { case 'C': tn = "char"; break; case 'N': tn = "number"; break; case 'L': tn = "bool"; break; case 'M': tn = "memo"; break; case 'D': tn = "data"; break; default: tn = "unknown"; break; } printf(" %s %s[%i]", tn, f->name, f->len); if ( i+1 != n ) printf(","); } printf("\n\n"); for(j=0; j < le_32(h->count); j++) { chunk = begin + 1 + le_16(h->flen) + (j*r); f = (struct db3_field *)(begin + sizeof(*h)); for(i=0; i < n; i++,f++) { char buf[f->len]; switch( f->type ) { case DB3_TYPE_CHAR: memcpy(buf, chunk, f->len); buf[f->len]=0; if ( buf[0] != ' ' ) printf("%s = %s\n", f->name, buf); chunk += f->len; break; case DB3_TYPE_NUM: if ( *chunk != ' ' ) printf("%s = %c\n", f->name, *chunk); chunk += f->len; break; default: printf("Type %c\n", f->type); } } printf("\n"); } done: munmap(begin, sz); } int main(int argc, char **argv) { /* tables marked ??? look like many-many relationships */ do_db3("act-db/masterdatabase.adb"); /* task */ /* act-db/masterdatabase.adx */ do_db3("act-db/masterdatabase.ddb"); /* products */ /* act-db/masterdatabase.ddx */ do_db3("act-db/masterdatabase.edb"); /* ??? */ /* act-db/masterdatabase.edx */ do_db3("act-db/masterdatabase.gdb"); /* groups */ /* act-db/masterdatabase.gdx */ do_db3("act-db/masterdatabase.hdb"); /* ??? */ /* act-db/masterdatabase.hdx */ do_db3("act-db/masterdatabase.sdb"); /* status */ /* act-db/masterdatabase.sdx */ do_db3("act-db/masterdatabase.tdb"); /* ??? */ /* act-db/masterdatabase.tdx */ do_db3("act-db/masterdatabase.dbf"); /* customers */ /* act-db/masterdatabase.mdx */ do_db3("act-db/masterdatabase.rel"); /* ??? */ /* act-db/masterdatabase.rex */ /* follwing files are not yet reverse engineered, * they are listed in decreasing order of importance */ /* masterdatabase.blb - crazy binary format */ /* masterdatabase.dbf - stored queries? */ /* masterdatabase.rem - strange ini file */ /* masterdatabase.lck - stupid 5 byte lock file */ /* phone.fmt - telephone number format specs */ return 0; }