├── .gitignore ├── sf_mar.c ├── makefile.nmake ├── util_indep.h ├── Makefile ├── sf_dem.c ├── cmdlib.h ├── sf_gmp.c ├── util_indep.c ├── gimgxor.c ├── gimginfo.c ├── sf_rgn.c ├── sf_mps.c ├── README.txt ├── sf_net.c ├── sf_typ.c ├── gimgextract.c ├── sf_nod.c ├── util.c ├── gimglib.h ├── cmdc.c ├── gimgfixcmd.c ├── gimgunlock.c ├── stdintvc.h ├── cmdlib.c ├── gimgch.c ├── sf_tre.c ├── garmin_struct.h ├── gimglib.c └── sf_lbl.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.img 2 | *.o 3 | *.obj 4 | *.exe 5 | -------------------------------------------------------------------------------- /sf_mar.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | void dump_mar (struct subfile_struct *sf) 4 | { 5 | assert(sf->typeid == ST_MAR); 6 | 7 | dump_comm(sf->header); 8 | 9 | printf("=== DEM HEADER: TODO ===\n"); 10 | } 11 | -------------------------------------------------------------------------------- /makefile.nmake: -------------------------------------------------------------------------------- 1 | CFLAGS = /nologo /W2 /O2 2 | OBJS = gimginfo.obj gimglib.obj util.obj sf_tre.obj sf_typ.obj sf_mps.obj sf_gmp.obj sf_nod.obj sf_net.obj sf_lbl.obj sf_rgn.obj 3 | 4 | all: gimginfo.exe gimgxor.exe gimgunlock.exe 5 | 6 | gimginfo.exe: $(OBJS) 7 | $(CC) $(CFLAGS) /Fegimginfo.exe $(OBJS) 8 | 9 | gimgxor.exe: gimgxor.c 10 | $(CC) $(CFLAGS) gimgxor.c 11 | 12 | gimgunlock.exe: gimgunlock.c 13 | $(CC) $(CFLAGS) gimgunlock.c util_indep.c 14 | 15 | gimgch.exe: gimgch.c 16 | $(CC) $(CFLAGS) gimgch.c util_indep.c 17 | 18 | clean: 19 | -del /F $(OBJS) gimginfo.exe 20 | -del /F gimgxor.exe gimgxor.obj 21 | -del /F gimgunlock.exe gimgunlock.obj 22 | -del /F gimgch.exe gimgch.obj 23 | -del /F util_indep.obj 24 | -------------------------------------------------------------------------------- /util_indep.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #define errexit(...) do {printf(__VA_ARGS__); exit(1);} while (0) 5 | 6 | #if defined(_MSC_VER) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) 7 | typedef __int64 off_t; 8 | #define myfseek64(fp,pos) _fseeki64(fp, pos, SEEK_SET) 9 | #else 10 | #define myfseek64(fp,pos) fseeko(fp, pos, SEEK_SET) 11 | #endif 12 | 13 | void hexdump (const unsigned char *data, int size); 14 | unsigned int read_byte_at (FILE *fp, off_t offset); 15 | unsigned int read_2byte_at (FILE *fp, off_t offset); 16 | unsigned int read_4byte_at (FILE *fp, off_t offset); 17 | void read_bytes_at (FILE *fp, off_t offset, unsigned char *buffer, int size); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 3 | GIMGLIB_SOURCES = gimglib.c util.c sf_typ.c sf_mps.c sf_tre.c sf_rgn.c sf_lbl.c sf_net.c sf_nod.c sf_dem.c sf_mar.c sf_gmp.c 4 | GIMGLIB_OBJS = $(GIMGLIB_SOURCES:.c=.o) 5 | 6 | all: gimginfo gimgfixcmd gimgxor gimgunlock gimgch gimgextract cmdc 7 | 8 | gimginfo: gimginfo.o $(GIMGLIB_OBJS) 9 | 10 | gimgfixcmd: gimgfixcmd.o cmdlib.o $(GIMGLIB_OBJS) 11 | $(CC) -o $@ $^ -lm 12 | 13 | gimgxor: gimgxor.o 14 | 15 | gimgunlock: gimgunlock.o util_indep.o 16 | 17 | gimgch: gimgch.o util_indep.o 18 | 19 | gimgextract: gimgextract.o util_indep.o 20 | 21 | cmdc: cmdc.o 22 | $(CC) -o $@ $< -lm 23 | 24 | .PHONY: clean 25 | clean: 26 | rm -f gimginfo gimgfixcmd gimgxor gimgunlock gimgch gimgextract cmdc *.o 27 | -------------------------------------------------------------------------------- /sf_dem.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | void dump_dem (struct subfile_struct *sf) 4 | { 5 | struct garmin_dem *header = (struct garmin_dem *)sf->header; 6 | 7 | assert(sf->typeid == ST_DEM); 8 | 9 | dump_comm(sf->header); 10 | 11 | printf("=== DEM HEADER ===\n"); 12 | printf("Flags: 0x%x\n", header->flags); 13 | printf("Zoom Levels: 0x%x\n", header->zoom_levels); 14 | printf("Reserved: 0x%x\n", header->reserved0); 15 | printf("Record Size: 0x%x\n", header->record_size); 16 | printf("Points to Block 3: 0x%x\n", header->points_to_block3); 17 | printf("Reserved: 0x%x\n", header->reserved2); 18 | 19 | if (header->comm.hlen > sizeof(struct garmin_gmp)) 20 | printf("from 0x%lx to 0x%x (0x%lx bytes): %s\n", 21 | sizeof(struct garmin_gmp), header->comm.hlen - 1, 22 | header->comm.hlen - sizeof(struct garmin_gmp), 23 | dump_unknown_bytes((uint8_t *)header + sizeof(struct garmin_gmp), header->comm.hlen - sizeof(struct garmin_gmp))); 24 | 25 | return; 26 | } 27 | -------------------------------------------------------------------------------- /cmdlib.h: -------------------------------------------------------------------------------- 1 | #ifndef CMDLIB_H 2 | #define CMDLIB_H 3 | 4 | /* CMD_DUPD deviation unit per degree: 5 | * Since the max deviation observed is 0.0085509, 6 | * 0.0085509 * 2500000 = 21377.25 fits will under signed short. */ 7 | #define CMD_DUPD 2500000 8 | #define CMD_X0 72 9 | #define CMD_X1 135 10 | #define CMD_Y0 18 11 | #define CMD_Y1 54 12 | 13 | #define DEVX(idx,idy) cmd_devs[((idy)*cmd_x_num + (idx)) * 2] 14 | #define DEVY(idx,idy) cmd_devs[((idy)*cmd_x_num + (idx)) * 2 + 1] 15 | 16 | extern int cmd_spd; /* sample per degree */ 17 | extern int cmd_x_num, cmd_y_num; 18 | extern short *cmd_devs; 19 | 20 | int cmd_init (const char *path); 21 | void cmd_fini (void); 22 | 23 | static inline int cmd_g24p_inchina (int x, int y) 24 | { 25 | /* 72 * 2^24 / 360 = 3355443.2 */ 26 | return (x > 3355443 && x < 6291456 && 27 | y > 838860 && y < 2516583); 28 | } 29 | 30 | /* assume x0 <= x1 && y0 <= y1 31 | * both are inclusive */ 32 | static inline int cmd_g24r_inchina (int x0, int y0, int x1, int y1) 33 | { 34 | return (x0 > 3355443 && x1 < 6291456 && 35 | y0 > 838860 && y1 < 2516583); 36 | } 37 | 38 | void cmd_g24p_fix (int *px, int *py); 39 | void cmd_g24p_dev (int *px, int *py); 40 | void cmd_g24r_fix (int *px0, int *py0, int *px1, int *py1, int avgopt); 41 | void cmd_g24r_dev (int *px0, int *py0, int *px1, int *py1, int avgopt); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /sf_gmp.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | void dump_gmp (struct subfile_struct *sf) 4 | { 5 | struct garmin_gmp *header = (struct garmin_gmp *)sf->header; 6 | 7 | assert(sf->typeid == ST_GMP); 8 | 9 | dump_comm(sf->header); 10 | 11 | assert(header->comm.hlen >= sizeof(struct garmin_gmp)); 12 | printf("=== GMP HEADER ===\n"); 13 | if (header->comm.hlen >= 0x19) printf("unknown_015: 0x%x\n", header->unknown_015); 14 | if (header->comm.hlen >= 0x1d) printf("TRE Offset: 0x%x\n", header->tre_offset); 15 | if (header->comm.hlen >= 0x21) printf("RGN Offset: 0x%x\n", header->rgn_offset); 16 | if (header->comm.hlen >= 0x25) printf("LBL Offset: 0x%x\n", header->lbl_offset); 17 | if (header->comm.hlen >= 0x29) printf("NET Offset: 0x%x\n", header->net_offset); 18 | if (header->comm.hlen >= 0x2d) printf("NOD Offset: 0x%x\n", header->nod_offset); 19 | if (header->comm.hlen >= 0x31) printf("DEM Offset: 0x%x\n", header->dem_offset); 20 | if (header->comm.hlen >= 0x35) printf("MAR Offset: 0x%x\n", header->mar_offset); 21 | 22 | if (header->comm.hlen > sizeof(struct garmin_gmp)) 23 | printf("from 0x%lx to 0x%x (0x%lx bytes): %s\n", 24 | sizeof(struct garmin_gmp), header->comm.hlen - 1, 25 | header->comm.hlen - sizeof(struct garmin_gmp), 26 | dump_unknown_bytes((uint8_t *)header + sizeof(struct garmin_gmp), header->comm.hlen - sizeof(struct garmin_gmp))); 27 | } 28 | -------------------------------------------------------------------------------- /util_indep.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "util_indep.h" 4 | 5 | void hexdump (const unsigned char *data, int size) 6 | { 7 | while (size -- > 0) 8 | printf("%02x", *(data ++)); 9 | printf("\n"); 10 | } 11 | 12 | unsigned int read_byte_at (FILE *fp, off_t offset) 13 | { 14 | int c; 15 | if (myfseek64(fp, offset)) { 16 | perror(NULL); 17 | exit(1); 18 | } 19 | c = getc(fp); 20 | if (c == EOF) 21 | errexit("Unexpected EOF\n"); 22 | return c; 23 | } 24 | 25 | unsigned int read_2byte_at (FILE *fp, off_t offset) 26 | { 27 | unsigned char buff[2]; 28 | if (myfseek64(fp, offset)) { 29 | perror(NULL); 30 | exit(1); 31 | } 32 | if (fread(&buff, 2, 1, fp) != 1) { 33 | perror(NULL); 34 | exit(1); 35 | } 36 | return (buff[1] << 8) | buff[0]; 37 | } 38 | 39 | unsigned int read_4byte_at (FILE *fp, off_t offset) 40 | { 41 | unsigned char buff[4]; 42 | if (myfseek64(fp, offset)) { 43 | perror(NULL); 44 | exit(1); 45 | } 46 | if (fread(&buff, 4, 1, fp) != 1) { 47 | perror(NULL); 48 | exit(1); 49 | } 50 | return (buff[3] << 24) | (buff[2] << 16) | (buff[1] << 8) | buff[0]; 51 | } 52 | 53 | void read_bytes_at (FILE *fp, off_t offset, unsigned char *buffer, int size) 54 | { 55 | if (myfseek64(fp, offset)) { 56 | perror(NULL); 57 | exit(1); 58 | } 59 | if (fread(buffer, size, 1, fp) != 1) { 60 | perror(NULL); 61 | exit(1); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /gimgxor.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | FILE *inf, *outf; 6 | unsigned char buffer[4096]; 7 | int buffer_size, ptr; 8 | int xorbyte; 9 | 10 | if (argc != 3) { 11 | printf("usage: %s infile outfile", argv[0]); 12 | return 1; 13 | } 14 | 15 | inf = fopen(argv[1], "rb"); 16 | if (inf == NULL) { 17 | printf("can't open %s\n", argv[1]); 18 | return 1; 19 | } 20 | 21 | buffer_size = fread(buffer, 1, sizeof(buffer), inf); 22 | if (buffer_size == 0) { 23 | printf("empty file\n"); 24 | return 1; 25 | } 26 | 27 | xorbyte = buffer[0]; 28 | if (xorbyte == 0) { 29 | printf("file is not XORed\n"); 30 | return 1; 31 | } 32 | if (buffer_size <= 600 || 33 | (buffer[0x10] ^ xorbyte) != 'D' || 34 | (buffer[0x15] ^ xorbyte) != 'G' || 35 | (buffer[0x41] ^ xorbyte) != 'G' || 36 | (buffer[0x46] ^ xorbyte) != 'N') { 37 | printf("file is not garmin img\n"); 38 | return 1; 39 | } 40 | 41 | outf = fopen(argv[2], "wb"); 42 | if (outf == NULL) { 43 | printf("can't create/write %s\n", argv[2]); 44 | return 1; 45 | } 46 | 47 | for (ptr = 0; ptr < buffer_size; ptr ++) 48 | buffer[ptr] ^= xorbyte; 49 | fwrite(buffer, 1, buffer_size, outf); 50 | 51 | while (!feof(inf)) { 52 | buffer_size = fread(buffer, 1, sizeof(buffer), inf); 53 | if (buffer_size == 0) 54 | break; 55 | for (ptr = 0; ptr < buffer_size; ptr ++) 56 | buffer[ptr] ^= xorbyte; 57 | fwrite(buffer, 1, buffer_size, outf); 58 | } 59 | 60 | fclose(inf); 61 | fclose(outf); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /gimginfo.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | static void usage (void) 4 | { 5 | printf("Usage: gimginfo [options] imgfile [subfile]\n"); 6 | printf("Example: gimginfo america-nt-2011.img\n"); 7 | printf("Example: gimginfo america-nt-2011.img MAPSOURC.MPS\n"); 8 | printf("Example: gimginfo america-nt-2011.img I07DE4E4.TRE\n"); 9 | printf("Example: gimginfo america-nt-2011.img I07DE4E4.LBL\n"); 10 | } 11 | 12 | int main (int argc, char **argv) 13 | { 14 | char *img_path = NULL; 15 | char *subfile_name = NULL; 16 | struct gimg_struct *img; 17 | int i; 18 | 19 | for (i = 1; i < argc; i ++) { 20 | if (strcmp(argv[i], "-h") == 0 || 21 | strcmp(argv[i], "--help") == 0 || 22 | strcmp(argv[i], "-?") == 0) { 23 | usage(); 24 | return 0; 25 | } else if (argv[i][0] == '-') { 26 | printf("unknown option %s\n", argv[i]); 27 | usage(); 28 | return 1; 29 | } else { 30 | if (img_path == NULL) 31 | img_path = argv[i]; 32 | else if (subfile_name == NULL) 33 | subfile_name = argv[i]; 34 | else { 35 | printf("only one subfile please.\n"); 36 | usage(); 37 | return 1; 38 | } 39 | } 40 | } 41 | 42 | if (img_path == NULL) { 43 | printf("no img file specified\n"); 44 | usage(); 45 | return 1; 46 | } 47 | 48 | img = gimg_open(img_path, 0); 49 | if (img == NULL) { 50 | printf("failed to open or parse %s\n", img_path); 51 | return 1; 52 | } 53 | 54 | if (subfile_name) { 55 | dump_subfile(img, subfile_name); 56 | } else { 57 | dump_img(img); 58 | } 59 | 60 | gimg_close(img); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /sf_rgn.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | void dump_rgn (struct subfile_struct *sf) 4 | { 5 | struct garmin_rgn *header = (struct garmin_rgn *)sf->header; 6 | int progress = 0; 7 | 8 | assert(sf->typeid == ST_RGN); 9 | 10 | dump_comm(sf->header); 11 | 12 | printf("=== RGN HEADER ===\n"); 13 | 14 | if (header->comm.hlen < 0x1d) 15 | goto headerfini; 16 | progress = 0x1d; 17 | printf("RGN1: reloff=0x%x absoff=0x%x size=0x%x\n", 18 | header->rgn1_offset, sf->offset + header->rgn1_offset, header->rgn1_length); 19 | 20 | if (header->comm.hlen < 0x7d) 21 | goto headerfini; 22 | progress = 0x7d; 23 | printf("RGN2: reloff=0x%x absoff=0x%x size=0x%x\n", 24 | header->rgn2_offset, sf->offset + header->rgn2_offset, header->rgn2_length); 25 | printf("unknown_025: %s\n", dump_unknown_bytes(header->unknown_025, sizeof(header->unknown_025))); 26 | printf("RGN3: reloff=0x%x absoff=0x%x size=0x%x\n", 27 | header->rgn3_offset, sf->offset + header->rgn3_offset, header->rgn3_length); 28 | printf("unknown_041: %s\n", dump_unknown_bytes(header->unknown_041, sizeof(header->unknown_041))); 29 | printf("RGN4: reloff=0x%x absoff=0x%x size=0x%x\n", 30 | header->rgn4_offset, sf->offset + header->rgn4_offset, header->rgn4_length); 31 | printf("unknown_05d: %s\n", dump_unknown_bytes(header->unknown_05d, sizeof(header->unknown_05d))); 32 | printf("RGN5: reloff=0x%x absoff=0x%x size=0x%x\n", 33 | header->rgn5_offset, sf->offset + header->rgn5_offset, header->rgn5_length); 34 | printf("unknown_079: 0x%x\n", header->unknown_079); 35 | 36 | headerfini: 37 | if (header->comm.hlen > progress) 38 | printf("from 0x%x to 0x%x (0x%x bytes): %s\n", 39 | progress, header->comm.hlen - 1, 40 | header->comm.hlen - progress, 41 | dump_unknown_bytes((uint8_t *)header + progress, header->comm.hlen - progress)); 42 | 43 | return; 44 | } 45 | -------------------------------------------------------------------------------- /sf_mps.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | void dump_mps (struct subfile_struct *mps) 4 | { 5 | unsigned int line; 6 | 7 | for (line = 0; line < mps->size;) { 8 | int type = *(mps->base + line + 0); 9 | int size = *(uint16_t *)(mps->base + line + 1); 10 | int ptr = line + 3; 11 | switch(type) { 12 | case 0x46: 13 | printf("0x46: pid=%d fid=%d", 14 | *(uint16_t *)(mps->base + ptr + 0), 15 | *(uint16_t *)(mps->base + ptr + 2)); 16 | ptr += 4; 17 | printf(" str1=\"%s\"\n", mps->base + ptr); 18 | ptr += strlen((char *)mps->base + ptr) + 1; 19 | break; 20 | case 0x4c: 21 | printf("0x4c: pid=%d fid=%d ?=0x%x", 22 | *(uint16_t *)(mps->base + ptr + 0), 23 | *(uint16_t *)(mps->base + ptr + 2), 24 | *(uint16_t *)(mps->base + ptr + 6)); 25 | ptr += 8; 26 | printf(" area=\"%s\"", mps->base + ptr); 27 | ptr += strlen((char *)mps->base + ptr) + 1; 28 | printf(" name=\"%s\"", mps->base + ptr); 29 | ptr += strlen((char *)mps->base + ptr) + 1; 30 | printf(" str3=\"%s\"", mps->base + ptr); 31 | ptr += strlen((char *)mps->base + ptr) + 1; 32 | printf(" id=%x ?=%x\n", *(uint32_t *)(mps->base + ptr), 33 | *(uint32_t *)(mps->base + ptr + 4)); 34 | ptr += 8; 35 | break; 36 | case 0x50: 37 | printf("0x50: pid=%d fid=%d ?=%s\n", 38 | *(uint16_t *)(mps->base + ptr + 0), 39 | *(uint16_t *)(mps->base + ptr + 2), 40 | dump_unknown_bytes(mps->base + ptr + 4, size - 4)); 41 | break; 42 | case 0x55: 43 | printf("0x55: str=\"%s\"\n", mps->base + ptr); 44 | ptr += strlen((char *)mps->base + ptr) + 1; 45 | break; 46 | case 0x56: 47 | printf("0x56: str=\"%s\"", mps->base + ptr); 48 | ptr += strlen((char *)mps->base + ptr) + 1; 49 | printf(" ?=0x%x\n", *(mps->base + ptr)); 50 | ptr += 1; 51 | break; 52 | default: 53 | printf("0x%02x: %s\n", type, dump_unknown_bytes(mps->base + ptr, size)); 54 | } 55 | line += size + 3; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | gimgtools is a set of command-line tools to examine and manipulate Garmin IMG 2 | (the map format) files. The tools are: 3 | 4 | * gimgunlock: Unlock a locked map so that it can be used on ALL devices. There 5 | is no need to specify your device ID or map keys. It works by decrypting 6 | the TRE sections, since the encryption key is also stored in the maps! 7 | Usage: gimgunlock map.img 8 | If you like Garmin, please buy their maps! 9 | * gimgxor: Some maps have been scrambled by a trival XOR algorithm. They do 10 | not work with other gimg* tools. You can use gimgxor to unscramble them. 11 | Usage: gimgxor map.img 12 | === The following tools are for reverse engineering only === 13 | * gimginfo: Print information of the map. 14 | Usage: gimginfo map.img 15 | * gimgextract: Extract the IMG sections. 16 | Usage: gimgextract map.img 17 | * gimgch: Hexdump and compare section header of two or more IMGs. Extremely 18 | useful for reverse engineering the file format. 19 | Usage: gimgch [-w columns] [-m max_sf_per_img] [-s pattern] map1.img map2.img ... 20 | 21 | Other than that, I have done some research on the deviation of China map 22 | coordinates. Though there is no concrete result, you can follow it in this 23 | article 24 | http://wuyongzheng.wordpress.com/2010/01/22/china-map-deviation-as-a-regression-problem/ 25 | My ultimate goal is to change the coordinates in place, i.e. change individual 26 | bytes without recompiling the map. The cmdc tool is to generate the deviation 27 | table. The gimgfixcmd tool fixes the deviation of a map. 28 | 29 | Bug Report: 30 | Please post it to google code http://code.google.com/p/gimgtools/. If you can 31 | fix it, you can do so in github and let me know. 32 | 33 | Credits: 34 | Most of the reverse engineering on IMG format was done by other pepole (listed 35 | below). I just read their docs and code. The reverse engineering of the 36 | unlocking algorithm and the GMP section (aka the NT format by Garmin) are my 37 | work. 38 | 39 | http://sourceforge.net/projects/garmin-img/ 40 | http://libgarmin.sourceforge.net/ 41 | http://ati.land.cz/ 42 | http://svn.parabola.me.uk/display/trunk/doc/nod.txt 43 | http://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/MDR_Subfile_Format 44 | -------------------------------------------------------------------------------- /sf_net.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | void dump_net (struct subfile_struct *sf) 4 | { 5 | struct garmin_net *header = (struct garmin_net *)sf->header; 6 | int progress = 0; 7 | 8 | assert(sf->typeid == ST_NET); 9 | 10 | dump_comm(sf->header); 11 | 12 | printf("=== NET HEADER ===\n"); 13 | 14 | if (header->comm.hlen < 0x37) 15 | goto headerfini; 16 | progress = 0x37; 17 | printf("NET1 Road Def: reloff=0x%x absoff=0x%x size=0x%x shift=%d\n", 18 | header->net1_offset, sf->offset + header->net1_offset, header->net1_length, header->net1_shift); 19 | printf("NET2 Segmented Road: reloff=0x%x absoff=0x%x size=0x%x shift=%d\n", 20 | header->net2_offset, sf->offset + header->net2_offset, header->net2_length, header->net2_shift); 21 | printf("NET3 Sorted Road: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 22 | header->net3_offset, sf->offset + header->net3_offset, header->net3_length, header->net3_recsize); 23 | printf("unknown_031: 0x%x\n", header->unknown_031); 24 | printf("unknown_035: 0x%x\n", header->unknown_035); 25 | 26 | if (header->comm.hlen < 0x3b) 27 | goto headerfini; 28 | progress = 0x3b; 29 | printf("unknown_037: 0x%x\n", header->unknown_037); 30 | 31 | if (header->comm.hlen < 0x64) 32 | goto headerfini; 33 | progress = 0x64; 34 | printf("unknown_03b: 0x%x\n", header->unknown_03b); 35 | printf("unknown_03f: 0x%x\n", header->unknown_03f); 36 | printf("NET4: reloff=0x%x absoff=0x%x size=0x%x ?04b=0x%x\n", 37 | header->net4_offset, sf->offset + header->net4_offset, header->net4_length, header->net4_u); 38 | printf("NET5: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 39 | header->net5_offset, sf->offset + header->net5_offset, header->net5_length, header->net5_recsize); 40 | printf("NET6: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 41 | header->net6_offset, sf->offset + header->net6_offset, header->net6_length, header->net6_recsize); 42 | printf("unknown_060: 0x%x\n", header->unknown_060); 43 | 44 | headerfini: 45 | if (header->comm.hlen > progress) 46 | printf("from 0x%x to 0x%x (0x%x bytes): %s\n", 47 | progress, header->comm.hlen - 1, 48 | header->comm.hlen - progress, 49 | dump_unknown_bytes((uint8_t *)header + progress, header->comm.hlen - progress)); 50 | 51 | return; 52 | } 53 | -------------------------------------------------------------------------------- /sf_typ.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | void dump_typ (struct subfile_struct *typ) 4 | { 5 | struct garmin_typ *header = (struct garmin_typ *)typ->header; 6 | 7 | dump_comm(typ->header); 8 | 9 | printf("=== TYP HEADER ===\n"); 10 | printf("Codepage: %u\n", header->codepage); 11 | printf("Point: reloff=0x%x absoff=0x%x size=0x%x\n", 12 | header->point_datoff, typ->offset + header->point_datoff, 13 | header->point_datsize); 14 | printf("Line: reloff=0x%x absoff=0x%x size=0x%x\n", 15 | header->line_datoff, typ->offset + header->line_datoff, 16 | header->line_datsize); 17 | printf("Polygon: reloff=0x%x absoff=0x%x size=0x%x\n", 18 | header->polygon_datoff, typ->offset + header->polygon_datoff, 19 | header->polygon_datsize); 20 | printf("FID: %u\n", header->fid); 21 | printf("PID: %u\n", header->pid); 22 | printf("PointArr: reloff=0x%x absoff=0x%x mod=0x%x size=0x%x\n", 23 | header->point_arroff, typ->offset + header->point_arroff, 24 | header->point_arrmod, header->point_arrsize); 25 | printf("LineArr: reloff=0x%x absoff=0x%x mod=0x%x size=0x%x\n", 26 | header->line_arroff, typ->offset + header->line_arroff, 27 | header->line_arrmod, header->line_arrsize); 28 | printf("PolygonArr: reloff=0x%x absoff=0x%x mod=0x%x size=0x%x\n", 29 | header->polygon_arroff, typ->offset + header->polygon_arroff, 30 | header->polygon_arrmod, header->polygon_arrsize); 31 | printf("DOArr: reloff=0x%x absoff=0x%x mod=0x%x size=0x%x\n", 32 | header->draworder_arroff, typ->offset + header->draworder_arroff, 33 | header->draworder_arrmod, header->draworder_arrsize); 34 | if (header->comm.hlen < 0x6e) 35 | goto headerfini; 36 | printf("NT1: reloff=0x%x absoff=0x%x size=0x%x flag=0x%x\n", 37 | header->nt1_datoff, typ->offset + header->nt1_datoff, 38 | header->nt1_datsize, header->nt1_flag); 39 | printf("NT1Arr: reloff=0x%x absoff=0x%x mod=0x%x size=0x%x\n", 40 | header->nt1_arroff, typ->offset + header->nt1_arroff, 41 | header->nt1_arrmod, header->nt1_arrsize); 42 | if (header->comm.hlen < 0x9c) 43 | goto headerfini; 44 | printf("Block0: reloff=0x%x absoff=0x%x size=0x%x x=0x%x y=0x%x\n", 45 | header->blok0_off, typ->offset + header->blok0_off, 46 | header->blok0_size, header->blok0_x, header->blok0_y); 47 | printf("Block1: reloff=0x%x absoff=0x%x size=0x%x x=0x%x y=0x%x\n", 48 | header->blok1_off, typ->offset + header->blok1_off, 49 | header->blok1_size, header->blok1_x, header->blok1_y); 50 | printf("Block2: reloff=0x%x absoff=0x%x size=0x%x x=0x%x\n", 51 | header->blok2_off, typ->offset + header->blok2_off, 52 | header->blok2_size, header->blok2_x); 53 | printf("Unknown09a: 0x%x\n", header->unknown_09a); 54 | if (header->comm.hlen > 0x9c) 55 | printf("from 0x9c to 0x%x (0x%x bytes): %s\n", 56 | header->comm.hlen - 1, header->comm.hlen - 0x9c, 57 | dump_unknown_bytes((uint8_t *)header + 0x9c, header->comm.hlen - 0x9c)); 58 | headerfini: 59 | 60 | return; 61 | } 62 | -------------------------------------------------------------------------------- /gimgextract.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "util_indep.h" 6 | 7 | int main (int argc, char *argv[]) 8 | { 9 | FILE *infp; 10 | int block_size, fatstart, fatend, fatcount; 11 | 12 | if (argc != 2) { 13 | printf("usage: %s file.img\n", argv[0]); 14 | return 1; 15 | } 16 | 17 | infp = fopen(argv[1], "rb"); 18 | if (infp == NULL) 19 | errexit("can't open %s\n", argv[1]); 20 | 21 | if ((read_byte_at(infp, 0) ^ read_byte_at(infp, 0x10)) != 'D' || 22 | (read_byte_at(infp, 0) ^ read_byte_at(infp, 0x15)) != 'G') 23 | errexit("Not a garmin img file.\n"); 24 | if (read_byte_at(infp, 0)) 25 | errexit("XOR is not 0. Fix it first.\n"); 26 | 27 | block_size = 1 << (read_byte_at(infp, 0x61) + read_byte_at(infp, 0x62)); 28 | 29 | fatstart = read_byte_at(infp, 0x40) == 0 ? 3 : read_byte_at(infp, 0x40); 30 | if (read_4byte_at(infp, 0x40c) == 0) { // use root dir. assume it's the first file 31 | off_t offset = fatstart * 512; 32 | if (read_byte_at(infp, offset) != 1 || 33 | read_byte_at(infp, offset + 0x1) != ' ' || 34 | read_byte_at(infp, offset + 0x9) != ' ') 35 | errexit("imgheader.data_offset = 0 but the first file is not root dir!\n"); 36 | fatstart ++; 37 | if (read_4byte_at(infp, offset + 0xc) % 512 != 0 || 38 | read_4byte_at(infp, offset + 0xc) <= fatstart * 512) 39 | errexit("rootdir.size = %x which is bad\n", read_4byte_at(infp, offset + 0xc)); 40 | fatend = read_4byte_at(infp, offset + 0xc) / 512; 41 | } else { 42 | fatend = read_4byte_at(infp, 0x40c) / 512; 43 | } 44 | 45 | for (fatcount = fatstart; fatcount < fatend; fatcount ++) { 46 | off_t offset = fatcount * 512, sf_offset; 47 | int sf_size; 48 | char sf_name[16]; 49 | FILE *outfp; 50 | 51 | if (read_byte_at(infp, offset) != 1) 52 | continue; 53 | if (read_byte_at(infp, offset + 0x1) == ' ') /* rootdir */ 54 | continue; 55 | if (read_2byte_at(infp, offset + 0x10) != 0) /* part != 0 */ 56 | continue; 57 | 58 | memset(sf_name, 0, sizeof(sf_name)); 59 | read_bytes_at(infp, offset + 0x1, (unsigned char *)sf_name, 8); 60 | while (strrchr(sf_name, ' ') != NULL) 61 | strrchr(sf_name, ' ')[0] = '\0'; 62 | sf_name[strlen(sf_name)] = '.'; 63 | read_bytes_at(infp, offset + 0x9, (unsigned char *)sf_name + strlen(sf_name), 3); 64 | while (strrchr(sf_name, ' ') != NULL) 65 | strrchr(sf_name, ' ')[0] = '\0'; 66 | /* a simple security check */ 67 | if (strchr(sf_name, '/') != NULL || strchr(sf_name, '\\') != NULL) 68 | errexit("invalid subfile name %s\n", sf_name); 69 | 70 | sf_offset = read_2byte_at(infp, offset + 0x20) * block_size; 71 | sf_size = read_4byte_at(infp, offset + 0xc); 72 | if (sf_offset == 0 || sf_size == 0) 73 | errexit("subfile %s has 0 offset or size: 0x%lx 0x%x\n", 74 | sf_name, (unsigned long)sf_offset, (unsigned int)sf_size); 75 | 76 | outfp = fopen(sf_name, "wb"); 77 | if (outfp == NULL) 78 | errexit("can't open %s for writing\n", sf_name); 79 | while (sf_size != 0) { 80 | unsigned char buff[4096]; 81 | int len = sf_size < sizeof(buff) ? sf_size : sizeof(buff); 82 | read_bytes_at(infp, sf_offset, buff, len); 83 | if (fwrite(buff, len, 1, outfp) != 1) 84 | errexit("can't write to %s\n", sf_name); 85 | sf_offset += len; 86 | sf_size -= len; 87 | } 88 | fclose(outfp); 89 | } 90 | 91 | fclose(infp); 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /sf_nod.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | static void dump_node_data (unsigned char *ptr, int length) 4 | { 5 | } 6 | 7 | static void dump_road_data (unsigned char *ptr, int length) 8 | { 9 | while (length >= 6) { 10 | printf("type=0x%02x, ptr=0x%02x%02x%02x, bits=%3d, ?=0x%02x", 11 | ptr[0], ptr[3], ptr[2], ptr[1], ptr[4], ptr[5]); 12 | if (ptr[4] > 0) 13 | printf(", bfield=%s", dump_unknown_bytes(ptr + 6, (ptr[4] + 7) / 8)); 14 | printf("\n"); 15 | ptr += 6 + (ptr[4] + 7) / 8; 16 | length -= 6 + (ptr[4] + 7) / 8; 17 | } 18 | } 19 | 20 | static void dump_boundary_nodes (unsigned char *ptr, int length, int recsize) 21 | { 22 | if (recsize < 9) { 23 | printf("Error: NOD3 boundry nodes's recsize %d is less than 9\n", recsize); 24 | return; 25 | } 26 | 27 | for (; length >= recsize; ptr += recsize, length -= recsize) { 28 | printf("e=%d(%s), n=%d(%s), off=0x%x", 29 | bytes_to_sint24(ptr), sint24_to_lng(bytes_to_sint24(ptr)), 30 | bytes_to_sint24(ptr+3), sint24_to_lat(bytes_to_sint24(ptr+3)), 31 | bytes_to_sint24(ptr+6)); 32 | if (recsize > 9) 33 | printf(", ?=%s", dump_unknown_bytes(ptr+9, recsize - 9)); 34 | printf("\n"); 35 | } 36 | } 37 | 38 | void dump_nod (struct subfile_struct *sf) 39 | { 40 | struct garmin_nod *header = (struct garmin_nod *)sf->header; 41 | int progress = 0; 42 | 43 | assert(sf->typeid == ST_NOD); 44 | 45 | dump_comm(sf->header); 46 | 47 | printf("=== NOD HEADER ===\n"); 48 | 49 | if (header->comm.hlen < 0x3f) 50 | goto headerfini; 51 | progress = 0x3f; 52 | printf("NOD1 Node Data: reloff=0x%x absoff=0x%x size=0x%x\n", 53 | header->nod1_offset, sf->offset + header->nod1_offset, header->nod1_length); 54 | printf("nod_bits, align: {0x%x 0x%x 0x%x 0x%x}, %d\n", 55 | header->nod_bits[0], header->nod_bits[1], header->nod_bits[2], header->nod_bits[3], header->align); 56 | printf("unknown_022: %d\n", header->unknown_022); 57 | printf("roadptrsize: %d\n", header->roadptrsize); 58 | printf("NOD2 Road Data: reloff=0x%x absoff=0x%x size=0x%x\n", 59 | header->nod2_offset, sf->offset + header->nod2_offset, header->nod2_length); 60 | printf("unknown_02d: 0x%x\n", header->unknown_02d); 61 | printf("NOD3 Boundary Node: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 62 | header->nod3_offset, sf->offset + header->nod3_offset, header->nod3_length, header->nod3_recsize); 63 | printf("unknown_03c: 0x%x\n", header->unknown_03c); 64 | 65 | if (header->comm.hlen < 0x7f) 66 | goto headerfini; 67 | progress = 0x7f; 68 | printf("NOD4: reloff=0x%x absoff=0x%x size=0x%x\n", 69 | header->nod4_offset, sf->offset + header->nod4_offset, header->nod4_length); 70 | printf("unknown_047: 0x%x\n", header->unknown_047); 71 | printf("unknown_04b: 0x%x\n", header->unknown_04b); 72 | printf("unknown_04f: 0x%x\n", header->unknown_04f); 73 | printf("unknown_053: 0x%x\n", header->unknown_053); 74 | printf("unknown_057: 0x%x\n", header->unknown_057); 75 | printf("unknown_05b: %s\n", 76 | dump_unknown_bytes(header->unknown_05b, sizeof(header->unknown_05b))); 77 | printf("NOD5: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 78 | header->nod5_offset, sf->offset + header->nod5_offset, header->nod5_length, header->nod5_recsize); 79 | printf("NOD6: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 80 | header->nod6_offset, sf->offset + header->nod6_offset, header->nod6_length, header->nod6_recsize); 81 | printf("unknown_07b: 0x%x\n", header->unknown_07b); 82 | 83 | headerfini: 84 | if (header->comm.hlen > progress) 85 | printf("from 0x%x to 0x%x (0x%x bytes): %s\n", 86 | progress, header->comm.hlen - 1, 87 | header->comm.hlen - progress, 88 | dump_unknown_bytes((uint8_t *)header + progress, header->comm.hlen - progress)); 89 | 90 | if (header->nod1_length) { 91 | printf("=== NODE DATA ===\n"); 92 | dump_node_data(sf->base + header->nod1_offset, header->nod1_length); 93 | } 94 | if (header->nod2_length) { 95 | printf("=== ROAD DATA ===\n"); 96 | dump_road_data(sf->base + header->nod2_offset, header->nod2_length); 97 | } 98 | if (header->nod3_length) { 99 | printf("=== BOUNDARY NODES ===\n"); 100 | dump_boundary_nodes(sf->base + header->nod3_offset, 101 | header->nod3_length, header->nod3_recsize); 102 | } 103 | 104 | return; 105 | } 106 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | const char *sint24_to_lat (int n) 4 | { 5 | static char buffers[8][12]; 6 | static unsigned int buffer_ptr = 0; 7 | char *buffer = buffers[buffer_ptr ++ % 8]; 8 | 9 | assert(n < 0x800000 && n >= -0x800000); 10 | if (n < 0) 11 | sprintf(buffer, "%.5fS", n * (360.0 / 0x01000000)); 12 | else 13 | sprintf(buffer, "%.5fN", n * (360.0 / 0x01000000)); 14 | return buffer; 15 | } 16 | 17 | const char *sint24_to_lng (int n) 18 | { 19 | static char buffers[8][12]; 20 | static unsigned int buffer_ptr = 0; 21 | char *buffer = buffers[buffer_ptr ++ % 8]; 22 | 23 | assert(n < 0x800000 && n >= -0x800000); 24 | if (n < 0) 25 | sprintf(buffer, "%.5fW", n * (360.0 / 0x01000000)); 26 | else 27 | sprintf(buffer, "%.5fE", n * (360.0 / 0x01000000)); 28 | return buffer; 29 | } 30 | 31 | const char *dump_unknown_bytes (uint8_t *bytes, int size) 32 | { 33 | static char buffers[4][1024]; 34 | static unsigned int currbuf = 0; 35 | char *buffer = buffers[(currbuf ++) % 4]; 36 | int ptr, outptr, repeat_count, repeat_byte; 37 | 38 | if (size == 0) 39 | return ""; 40 | 41 | if (size > sizeof(buffers[0]) / 2 - 2) 42 | size = sizeof(buffers[0]) / 2 - 2; 43 | 44 | for (repeat_byte = bytes[0], ptr = 1, repeat_count = 1, outptr = 0; ptr < size; ptr ++) { 45 | if (bytes[ptr] == repeat_byte) { 46 | repeat_count ++; 47 | } else { 48 | if (repeat_count >= 3) { 49 | outptr += sprintf(buffer + outptr, "%02x(%d)", repeat_byte, repeat_count); 50 | } else if (repeat_count == 2) { 51 | outptr += sprintf(buffer + outptr, "%02x%02x", repeat_byte, repeat_byte); 52 | } else { 53 | outptr += sprintf(buffer + outptr, "%02x", repeat_byte); 54 | } 55 | repeat_byte = bytes[ptr]; 56 | repeat_count = 1; 57 | } 58 | } 59 | if (repeat_count >= 3) { 60 | outptr += sprintf(buffer + outptr, "%02x(%d)", repeat_byte, repeat_count); 61 | } else if (repeat_count == 2) { 62 | outptr += sprintf(buffer + outptr, "%02x%02x", repeat_byte, repeat_byte); 63 | } else { 64 | outptr += sprintf(buffer + outptr, "%02x", repeat_byte); 65 | } 66 | 67 | return buffer; 68 | } 69 | 70 | void unlockml (unsigned char *dst, const unsigned char *src, 71 | int size, unsigned int key) 72 | { 73 | static const unsigned char shuf[] = { 74 | 0xb, 0xc, 0xa, 0x0, 75 | 0x8, 0xf, 0x2, 0x1, 76 | 0x6, 0x4, 0x9, 0x3, 77 | 0xd, 0x5, 0x7, 0xe}; 78 | int i, ringctr; 79 | int key_sum = shuf[((key >> 24) + (key >> 16) + (key >> 8) + key) & 0xf]; 80 | 81 | for (i = 0, ringctr = 16; i < size; i ++) { 82 | unsigned int upper = src[i] >> 4; 83 | unsigned int lower = src[i]; 84 | 85 | upper -= key_sum; 86 | upper -= key >> ringctr; 87 | upper -= shuf[(key >> ringctr) & 0xf]; 88 | ringctr = ringctr ? ringctr - 4 : 16; 89 | 90 | lower -= key_sum; 91 | lower -= key >> ringctr; 92 | lower -= shuf[(key >> ringctr) & 0xf]; 93 | ringctr = ringctr ? ringctr - 4 : 16; 94 | 95 | dst[i] = ((upper << 4) & 0xf0) | (lower & 0xf); 96 | } 97 | } 98 | 99 | enum subtype get_subtype_id (const char *str) // only use 3 chars from str 100 | { 101 | if (memcmp(str, "TRE", 3) == 0) return ST_TRE; 102 | if (memcmp(str, "RGN", 3) == 0) return ST_RGN; 103 | if (memcmp(str, "LBL", 3) == 0) return ST_LBL; 104 | if (memcmp(str, "NET", 3) == 0) return ST_NET; 105 | if (memcmp(str, "NOD", 3) == 0) return ST_NOD; 106 | if (memcmp(str, "DEM", 3) == 0) return ST_DEM; 107 | if (memcmp(str, "MAR", 3) == 0) return ST_MAR; 108 | if (memcmp(str, "SRT", 3) == 0) return ST_SRT; 109 | if (memcmp(str, "GMP", 3) == 0) return ST_GMP; 110 | if (memcmp(str, "TYP", 3) == 0) return ST_TYP; 111 | if (memcmp(str, "MDR", 3) == 0) return ST_MDR; 112 | if (memcmp(str, "TRF", 3) == 0) return ST_TRF; 113 | if (memcmp(str, "MPS", 3) == 0) return ST_MPS; 114 | if (memcmp(str, "QSI", 3) == 0) return ST_QSI; 115 | return ST_UNKNOWN; 116 | } 117 | 118 | const char *get_subtype_name (enum subtype id) 119 | { 120 | const static char *type_names[] = { 121 | "TRE", "RGN", "LBL", "NET", "NOD", "DEM", "MAR", 122 | "SRT", "GMP", "TYP", "MDR", "TRF", 123 | "MPS", "QSI"}; 124 | return id >= ST_UNKNOWN ? NULL : type_names[id]; 125 | } 126 | 127 | /* remove space characters at the end 128 | * length = -1 means using strlen() */ 129 | void string_trim (char *str, int length) 130 | { 131 | int i; 132 | if (length == -1) 133 | length = strlen(str); 134 | for (i = length - 1; i >= 0 && str[i] == ' '; i --) 135 | str[i] = '\0'; 136 | } 137 | -------------------------------------------------------------------------------- /gimglib.h: -------------------------------------------------------------------------------- 1 | #ifndef GIMGLIB_H 2 | #define GIMGLIB_H 3 | 4 | /* It's either VC in Windows or POSIX */ 5 | #if defined(_MSC_VER) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) 6 | # define GT_WINDOWS 7 | # include 8 | # include 9 | # include 10 | # include 11 | # include 12 | # include "stdintvc.h" 13 | # define inline __inline 14 | # define strcasecmp _stricmp 15 | # define strncasecmp _strnicmp 16 | #else 17 | # define GT_POSIX 18 | # include 19 | # include 20 | # include 21 | # include 22 | # include 23 | # include 24 | # include 25 | # include 26 | # include 27 | # include 28 | # include 29 | #endif 30 | 31 | #include "garmin_struct.h" 32 | #define NUMBER_SUBFILES 7 33 | 34 | enum subtype { 35 | ST_TRE, ST_RGN, ST_LBL, ST_NET, ST_NOD, ST_DEM, ST_MAR, // these 7 should match the GMP header 36 | ST_SRT, 37 | ST_GMP, 38 | ST_TYP, ST_MDR, ST_TRF, ST_MPS, ST_QSI, 39 | ST_UNKNOWN 40 | }; 41 | 42 | struct submap_struct; 43 | struct subfile_struct { 44 | struct garmin_subfile *header; 45 | unsigned char *base; // it's same as [header] if it's OF 46 | unsigned int offset; // same as base, but is abs file offset 47 | unsigned int size; 48 | char name[9]; 49 | char type[4]; 50 | char fullname[13]; /* 8.3 */ 51 | enum subtype typeid; 52 | struct submap_struct *map; 53 | struct subfile_struct *next; 54 | struct subfile_struct *orphan_next; 55 | }; 56 | 57 | struct submap_struct { 58 | char name[9]; 59 | union { 60 | struct subfile_struct *subfiles[NUMBER_SUBFILES]; 61 | struct { 62 | struct subfile_struct *tre; // always set. 63 | struct subfile_struct *rgn; 64 | struct subfile_struct *lbl; 65 | struct subfile_struct *net; 66 | struct subfile_struct *nod; 67 | struct subfile_struct *dem; 68 | struct subfile_struct *mar; 69 | }; 70 | }; 71 | struct subfile_struct *srt; 72 | struct subfile_struct *gmp; 73 | struct submap_struct *next; 74 | }; 75 | 76 | struct gimg_struct { 77 | const char *path; 78 | unsigned char *base; 79 | unsigned int size; 80 | struct subfile_struct *subfiles; 81 | struct submap_struct *submaps; 82 | struct subfile_struct *orphans; // files not belonging to any submap 83 | }; 84 | 85 | static inline unsigned int bytes_to_uint24 (unsigned char *bytes) { 86 | return (*(unsigned int *)bytes) & 0x00ffffff; 87 | } 88 | static inline int bytes_to_sint24 (const unsigned char *bytes) { 89 | int n = (*(const int *)bytes) & 0x00ffffff; 90 | return (n < 0x00800000) ? n : (n | 0xff000000); 91 | } 92 | static inline void sint24_to_bytes (int n, unsigned char *bytes) { 93 | bytes[0] = n & 0xff; 94 | bytes[1] = (n >> 8) & 0xff; 95 | bytes[2] = (n >> 16) & 0xff; 96 | } 97 | 98 | /* util.c */ 99 | const char *sint24_to_lat (int n); 100 | const char *sint24_to_lng (int n); 101 | const char *dump_unknown_bytes (uint8_t *bytes, int size); 102 | void unlockml (unsigned char *dst, const unsigned char *src, int size, unsigned int key); 103 | enum subtype get_subtype_id (const char *str); // only use 3 chars from str 104 | const char *get_subtype_name (enum subtype id); 105 | void string_trim (char *str, int length); 106 | 107 | /* sf_typ.c */ 108 | void dump_typ (struct subfile_struct *sf); 109 | 110 | /* sf_mps.c */ 111 | void dump_mps (struct subfile_struct *sf); 112 | 113 | /* sf_tre.c */ 114 | void dump_tre (struct subfile_struct *sf); 115 | 116 | /* sf_rgn.c */ 117 | void dump_rgn (struct subfile_struct *sf); 118 | 119 | /* sf_lbl.c */ 120 | void dump_lbl (struct subfile_struct *sf); 121 | 122 | /* sf_net.c */ 123 | void dump_net (struct subfile_struct *sf); 124 | 125 | /* sf_nod.c */ 126 | void dump_nod (struct subfile_struct *sf); 127 | 128 | /* sf_dem.c */ 129 | void dump_dem (struct subfile_struct *sf); 130 | 131 | /* sf_mar.c */ 132 | void dump_mar (struct subfile_struct *sf); 133 | 134 | /* sf_gmp.c */ 135 | void dump_gmp (struct subfile_struct *sf); 136 | 137 | /* gimglib.c */ 138 | void dump_comm (struct garmin_subfile *header); 139 | void dump_img (struct gimg_struct *img); 140 | void dump_subfile (struct gimg_struct *img, const char *subfile_name); 141 | struct submap_struct *get_submap (struct gimg_struct *img, const char *mapname); 142 | struct subfile_struct *get_subfile (struct gimg_struct *img, const char *subfilename); 143 | struct gimg_struct *gimg_open (const char *path, int writable); 144 | void gimg_close (struct gimg_struct *img); 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /cmdc.c: -------------------------------------------------------------------------------- 1 | /* example input: 2 | * 18.300000 109.600000 736 367 3 | * 18.300000 109.650000 746 358 4 | * 18.300000 109.700000 765 340 5 | * 18.300000 109.750000 775 330 6 | * output is binary 7 | * */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* CMD_DUPD deviation unit per degree: 15 | * Since the max deviation observed is 0.0085509, 16 | * 0.0085509 * 2500000 = 21377.25 fits will under signed short. */ 17 | #define CMD_DUPD 2500000 18 | #define CMD_X0 72 19 | #define CMD_X1 135 20 | #define CMD_Y0 18 21 | #define CMD_Y1 54 22 | 23 | 24 | int main (int argc, char *argv[]) 25 | { 26 | int spd; /* sample per degree */ 27 | short *devs; 28 | unsigned char *assign_round; 29 | int x_num, y_num; 30 | int loaded = 0, interpolated = 0; 31 | 32 | if (argc != 2 || (spd = atoi(argv[1])) == 0) { 33 | fprintf(stderr, "Usage: %s \n", argv[0]); 34 | fprintf(stderr, "Example: %s 20 out.txt\n", argv[0]); 35 | return 1; 36 | } 37 | if (spd <= 0 || spd > 100) { 38 | fprintf(stderr, "wrong spd %d\n", spd); 39 | return 1; 40 | } 41 | 42 | y_num = (CMD_Y1 - CMD_Y0) * spd; 43 | x_num = (CMD_X1 - CMD_X0) * spd; 44 | devs = malloc(y_num * x_num * 2 * sizeof(short)); 45 | memset(devs, 0, y_num * x_num * 2 * sizeof(short)); 46 | assign_round = malloc(y_num * x_num); 47 | memset(assign_round, 0, y_num * x_num); 48 | fprintf(stderr, "spd=%d, %d (EW) x %d (NS) = %d samples\n", 49 | spd, x_num, y_num, x_num * y_num); 50 | 51 | while (!feof(stdin)) { 52 | double x, y, dx, dy; 53 | int ix, iy, idx, idy; 54 | 55 | if (scanf("%lf\t%lf\t%d\t%d\n", &y, &x, &idx, &idy) != 4) 56 | break; 57 | dx = idx / ((1 << 26) / 360.0); 58 | dy = -idy * cos(y * (M_PI / 180)) / ((1 << 26) / 360.0); 59 | if (fabs(dx * CMD_DUPD) >= 32767 || fabs(dy * CMD_DUPD) >= 32767) { 60 | fprintf(stderr, "error: offset too large: %f %f %.7f %.7f\n", x, y, dx, dy); 61 | return 1; 62 | } 63 | ix = (int)lround((x - CMD_X0) * spd); 64 | iy = (int)lround((y - CMD_Y0) * spd); 65 | if (fabs((x - CMD_X0) * spd - ix) > 0.1 || fabs((y - CMD_Y0) * spd - iy) > 0.1) { 66 | fprintf(stderr, "warning: misaligned %f %f %.2f %.2f\n", 67 | x, y, (x - CMD_X0) * spd - ix, (y - CMD_X0) * spd - iy); 68 | continue; 69 | } 70 | if (ix < 0 || ix >= x_num || iy < 0 || iy >= y_num) { 71 | fprintf(stderr, "warning: OOB %f %f %d %d\n", x, y, ix, iy); 72 | continue; 73 | } 74 | devs[iy * x_num * 2 + ix * 2 + 0] = (short)lround(dx * CMD_DUPD); 75 | devs[iy * x_num * 2 + ix * 2 + 1] = (short)lround(dy * CMD_DUPD); 76 | assign_round[iy * x_num + ix] = 2; 77 | loaded ++; 78 | } 79 | fprintf(stderr, "%d samples loaded\n", loaded); 80 | 81 | /* apply interpolation */ 82 | while (1) { 83 | int x, y, this_itpd = 0; 84 | 85 | for (y = 0; y < y_num; y ++) for (x = 0; x < x_num; x ++) { 86 | int sumdx = 0, sumdy = 0, n = 0; 87 | if (assign_round[y * x_num + x] != 0) 88 | continue; 89 | if (x > 0 && assign_round[y * x_num + (x - 1)] == 2) { 90 | sumdx += devs[y * x_num * 2 + (x - 1) * 2 + 0]; 91 | sumdy += devs[y * x_num * 2 + (x - 1) * 2 + 1]; 92 | n ++; 93 | } 94 | if (x < x_num - 1 && assign_round[y * x_num + (x + 1)] == 2) { 95 | sumdx += devs[y * x_num * 2 + (x + 1) * 2 + 0]; 96 | sumdy += devs[y * x_num * 2 + (x + 1) * 2 + 1]; 97 | n ++; 98 | } 99 | if (y > 0 && assign_round[(y - 1) * x_num + x] == 2) { 100 | sumdx += devs[(y - 1) * x_num * 2 + x * 2 + 0]; 101 | sumdy += devs[(y - 1) * x_num * 2 + x * 2 + 1]; 102 | n ++; 103 | } 104 | if (y < y_num - 1 && assign_round[(y + 1) * x_num + x] == 2) { 105 | sumdx += devs[(y + 1) * x_num * 2 + x * 2 + 0]; 106 | sumdy += devs[(y + 1) * x_num * 2 + x * 2 + 1]; 107 | n ++; 108 | } 109 | if (n == 0) 110 | continue; 111 | devs[y * x_num * 2 + x * 2 + 0] = (short)lround(sumdx / (double)n); 112 | devs[y * x_num * 2 + x * 2 + 1] = (short)lround(sumdy / (double)n); 113 | assign_round[y * x_num + x] = 1; 114 | interpolated ++; 115 | this_itpd ++; 116 | } 117 | if (this_itpd == 0) 118 | break; 119 | for (y = 0; y < y_num; y ++) for (x = 0; x < x_num; x ++) { 120 | if (assign_round[y * x_num + (x - 1)] == 1) 121 | assign_round[y * x_num + (x - 1)] = 2; 122 | } 123 | } 124 | fprintf(stderr, "%d samples interpolated\n", interpolated); 125 | 126 | fwrite(&spd, 4, 1, stdout); 127 | fwrite(devs, y_num * x_num * 2 * sizeof(short), 1, stdout); 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /gimgfixcmd.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | #include "cmdlib.h" 3 | 4 | /* CMD_DUPD deviation unit per degree: 5 | * Since the max deviation observed is 0.0085509, 6 | * 0.0085509 * 2500000 = 21377.25 fits will under signed short. */ 7 | #define CMD_DUPD 2500000 8 | #define CMD_X0 72 9 | #define CMD_X1 135 10 | #define CMD_Y0 18 11 | #define CMD_Y1 54 12 | 13 | static int dryrun = 0; 14 | 15 | static int fix_subdiv (struct subfile_struct *map, struct garmin_tre_map_level *maplevel, 16 | struct garmin_tre_subdiv *div, unsigned char *div2, int div2_len) 17 | { 18 | int bitshift = 24 - maplevel->bits; 19 | int center_x, center_y; 20 | 21 | center_x = bytes_to_sint24(div->center_lng); 22 | center_y = bytes_to_sint24(div->center_lat); 23 | printf("subdiv cent orig: x=%d(%s) y=%d(%s) width=%5d height=%5d\n", 24 | center_x, sint24_to_lng(center_x), 25 | center_y, sint24_to_lat(center_y), 26 | (div->width<height<width<height<width<height<center_lng); 42 | sint24_to_bytes(center_y, div->center_lat); 43 | } 44 | 45 | return 0; 46 | } 47 | 48 | static int fix_tre (struct subfile_struct *tre) 49 | { 50 | struct garmin_tre *header = (struct garmin_tre *)tre->header; 51 | struct garmin_tre_map_level *maplevels; 52 | int x0, y0, x1, y1; 53 | int level, global_index, level_index; 54 | unsigned char *subdiv_ptr; 55 | 56 | x0 = bytes_to_sint24(header->westbound); 57 | y0 = bytes_to_sint24(header->southbound); 58 | x1 = bytes_to_sint24(header->eastbound); 59 | y1 = bytes_to_sint24(header->northbound); 60 | printf("submap bond orig: x0=%d(%s) y0=%d(%s) x1=%d(%s) y1=%d(%s)\n", 61 | x0, sint24_to_lng(x0), y0, sint24_to_lat(y0), 62 | x1, sint24_to_lng(x1), y1, sint24_to_lat(y1)); 63 | cmd_g24r_fix(&x0, &y0, &x1, &y1, 1); 64 | printf("submap bond fixd: x0=%d(%s) y0=%d(%s) x1=%d(%s) y1=%d(%s)\n", 65 | x0, sint24_to_lng(x0), y0, sint24_to_lat(y0), 66 | x1, sint24_to_lng(x1), y1, sint24_to_lat(y1)); 67 | if (!dryrun) { 68 | sint24_to_bytes(x0, header->westbound); 69 | sint24_to_bytes(y0, header->southbound); 70 | sint24_to_bytes(x1, header->eastbound); 71 | sint24_to_bytes(y1, header->northbound); 72 | } 73 | 74 | if (header->comm.locked) { 75 | maplevels = (struct garmin_tre_map_level *)malloc(header->tre1_size); 76 | unlockml((unsigned char *)maplevels, 77 | (unsigned char *)tre->base + header->tre1_offset, 78 | header->tre1_size, 79 | *(unsigned int *)(header->key+16)); 80 | //TODO some simple verification, maybe? 81 | } else { 82 | maplevels = (struct garmin_tre_map_level *)malloc(header->tre1_size); 83 | memcpy(maplevels, tre->base + header->tre1_offset, header->tre1_size); 84 | } 85 | 86 | for (subdiv_ptr = tre->base + header->tre2_offset, 87 | level = 0, global_index = 1; 88 | level < header->tre1_size / 4; level ++) { 89 | for (level_index = 0; level_index < maplevels[level].nsubdiv; 90 | level_index ++, global_index ++) { 91 | if (fix_subdiv(tre, &maplevels[level], 92 | (struct garmin_tre_subdiv *)subdiv_ptr, 93 | header->comm.hlen >= 0x86 && header->tre7_size ? 94 | tre->base + header->tre7_offset + 95 | global_index * header->tre7_rec_size : 96 | NULL, 97 | header->comm.hlen >= 0x86 && header->tre7_size ? 98 | header->tre7_rec_size : 0)) 99 | return 1; 100 | subdiv_ptr += level == header->tre1_size / 4 - 1 ? 101 | sizeof(struct garmin_tre_subdiv) - 2 : 102 | sizeof(struct garmin_tre_subdiv); 103 | } 104 | } 105 | return 0; 106 | } 107 | 108 | static int fix_nod (struct subfile_struct *nod) 109 | { 110 | struct garmin_nod *header = (struct garmin_nod *)nod->header; 111 | 112 | if (header->nod3_length) { 113 | unsigned char *ptr = nod->base + header->nod3_offset; 114 | int length = header->nod3_length; 115 | int recsize = header->nod3_recsize; 116 | 117 | if (recsize < 9) { 118 | printf("Error: NOD3 boundry nodes's recsize %d is less than 9\n", recsize); 119 | return 1; 120 | } 121 | 122 | for (; length >= recsize; ptr += recsize, length -= recsize) { 123 | int x = bytes_to_sint24(ptr); 124 | int y = bytes_to_sint24(ptr+3); 125 | printf("orig bond-node: e=%d(%s), n=%d(%s)\n", 126 | x, sint24_to_lng(x), y, sint24_to_lat(y)); 127 | cmd_g24p_fix(&x, &y); 128 | printf("fixd bond-node: e=%d(%s), n=%d(%s)\n", 129 | x, sint24_to_lng(x), y, sint24_to_lat(y)); 130 | if (!dryrun) { 131 | sint24_to_bytes(x, ptr); 132 | sint24_to_bytes(y, ptr+3); 133 | } 134 | } 135 | } 136 | return 0; 137 | } 138 | 139 | static int fix_map (struct submap_struct *map) 140 | { 141 | printf("%s\n", map->name); 142 | return fix_tre(map->tre) || fix_nod(map->nod); 143 | } 144 | 145 | static int fix_img (struct gimg_struct *img) 146 | { 147 | struct submap_struct *map; 148 | 149 | for (map = img->submaps; map != NULL; map = map->next) 150 | if (fix_map(map)) 151 | return 1; 152 | 153 | return 0; 154 | } 155 | 156 | static void usage (int code) 157 | { 158 | printf("Usage gimgfixcmd file.img\n"); 159 | exit(code); 160 | } 161 | 162 | int main (int argc, char **argv) 163 | { 164 | const char *img_path = NULL; 165 | struct gimg_struct *img; 166 | int i; 167 | 168 | for (i = 1; i < argc; i ++) { 169 | if (strcmp(argv[i], "-h") == 0 || 170 | strcmp(argv[i], "--help") == 0 || 171 | strcmp(argv[i], "-?") == 0) { 172 | usage(0); 173 | } else if (strcmp(argv[i], "-dryrun") == 0) { 174 | dryrun = 1; 175 | } else if (argv[i][0] == '-') { 176 | printf("unknown option %s\n", argv[i]); 177 | usage(1); 178 | } else { 179 | if (img_path == NULL) 180 | img_path = argv[i]; 181 | else { 182 | printf("only one subfile please.\n"); 183 | usage(1); 184 | } 185 | } 186 | } 187 | 188 | if (cmd_init("cmd.db")) { 189 | printf("failed to initialize cmd.db\n"); 190 | return 1; 191 | } 192 | 193 | if (img_path == NULL) { 194 | printf("no img file specified\n"); 195 | usage(1); 196 | } 197 | 198 | img = gimg_open(img_path, dryrun ? 0 : 1); 199 | if (img == NULL) { 200 | printf("failed to open or parse %s\n", img_path); 201 | return 1; 202 | } 203 | 204 | if (fix_img(img)) 205 | return 1; 206 | 207 | gimg_close(img); 208 | 209 | return 0; 210 | } 211 | -------------------------------------------------------------------------------- /gimgunlock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "util_indep.h" 6 | 7 | struct patch_struct { 8 | unsigned char *data; 9 | struct patch_struct *next; 10 | off_t offset; 11 | unsigned long size; 12 | }; 13 | 14 | 15 | void unlockml (unsigned char *dst, const unsigned char *src, 16 | int size, unsigned int key) 17 | { 18 | static const unsigned char shuf[] = { 19 | 0xb, 0xc, 0xa, 0x0, 20 | 0x8, 0xf, 0x2, 0x1, 21 | 0x6, 0x4, 0x9, 0x3, 22 | 0xd, 0x5, 0x7, 0xe}; 23 | int i, ringctr; 24 | int key_sum = shuf[((key >> 24) + (key >> 16) + (key >> 8) + key) & 0xf]; 25 | 26 | for (i = 0, ringctr = 16; i < size; i ++) { 27 | unsigned int upper = src[i] >> 4; 28 | unsigned int lower = src[i]; 29 | 30 | upper -= key_sum; 31 | upper -= key >> ringctr; 32 | upper -= shuf[(key >> ringctr) & 0xf]; 33 | ringctr = ringctr ? ringctr - 4 : 16; 34 | 35 | lower -= key_sum; 36 | lower -= key >> ringctr; 37 | lower -= shuf[(key >> ringctr) & 0xf]; 38 | ringctr = ringctr ? ringctr - 4 : 16; 39 | 40 | dst[i] = ((upper << 4) & 0xf0) | (lower & 0xf); 41 | } 42 | } 43 | 44 | struct patch_struct *prepend_patch (struct patch_struct *patch_list, unsigned long size) 45 | { 46 | struct patch_struct *new_patch = 47 | (struct patch_struct *)malloc(sizeof(struct patch_struct) + size); 48 | assert(new_patch != NULL); 49 | memset(new_patch, 0, sizeof(struct patch_struct) + size); 50 | new_patch->size = size; 51 | new_patch->data = (unsigned char *)new_patch + sizeof(struct patch_struct); 52 | new_patch->next = patch_list; 53 | return new_patch; 54 | } 55 | 56 | struct patch_struct *unlock_tre (FILE *fp, struct patch_struct *patch, 57 | off_t base, off_t header) 58 | { 59 | unsigned int key, mloff, mlsize; 60 | unsigned char encml[64]; /* at most 16 levels */ 61 | 62 | key = read_4byte_at(fp, header + 0xaa); 63 | printf("key: 0x%x\n", key); 64 | mloff = read_4byte_at(fp, header + 0x21); 65 | mlsize = read_4byte_at(fp, header + 0x25); 66 | if (mlsize > 64) 67 | errexit("Map level's size %d > 64\n", mlsize); 68 | read_bytes_at(fp, base + mloff, encml, mlsize); 69 | printf("encrypted ml: "); 70 | hexdump(encml, mlsize); 71 | patch = prepend_patch(patch, mlsize); 72 | patch->offset = base + mloff; 73 | unlockml(patch->data, encml, mlsize, key); 74 | printf("decrypted ml: "); 75 | hexdump(patch->data, mlsize); 76 | 77 | patch = prepend_patch(patch, 20); 78 | patch->offset = header + 0x9a; 79 | read_bytes_at(fp, header + 0x9a, patch->data, 20); 80 | printf("orig key: "); 81 | hexdump(patch->data, 20); 82 | patch->data[1] ^= 0x80; 83 | memcpy(patch->data + 8, patch->data + 12, 4); 84 | memset(patch->data + 16, 0, 4); 85 | printf("new key: "); 86 | hexdump(patch->data, 20); 87 | 88 | patch = prepend_patch(patch, 1); 89 | patch->offset = header + 0xd; 90 | patch->data[0] = read_byte_at(fp, (header + 0xd)) ^ 0x80; 91 | 92 | return patch; 93 | } 94 | 95 | struct patch_struct *create_patch (FILE *fp) 96 | { 97 | struct patch_struct *patch = NULL; 98 | int block_size, fatstart, fatend, fatcount; 99 | 100 | if ((read_byte_at(fp, 0) ^ read_byte_at(fp, 0x10)) != 'D' || 101 | (read_byte_at(fp, 0) ^ read_byte_at(fp, 0x15)) != 'G') 102 | errexit("Not a garmin img file.\n"); 103 | if (read_byte_at(fp, 0)) 104 | errexit("XOR is not 0. Fix it first.\n"); 105 | 106 | block_size = 1 << (read_byte_at(fp, 0x61) + read_byte_at(fp, 0x62)); 107 | 108 | fatstart = read_byte_at(fp, 0x40) == 0 ? 3 : read_byte_at(fp, 0x40); 109 | if (read_4byte_at(fp, 0x40c) == 0) { // use root dir. assume it's the first file 110 | off_t offset = fatstart * 512; 111 | if (read_byte_at(fp, offset) != 1 || 112 | read_byte_at(fp, offset + 0x1) != ' ' || 113 | read_byte_at(fp, offset + 0x9) != ' ') 114 | errexit("imgheader.data_offset = 0 but the first file is not root dir!\n"); 115 | fatstart ++; 116 | if (read_4byte_at(fp, offset + 0xc) % 512 != 0 || 117 | read_4byte_at(fp, offset + 0xc) <= fatstart * 512) 118 | errexit("rootdir.size = %x which is bad\n", read_4byte_at(fp, offset + 0xc)); 119 | fatend = read_4byte_at(fp, offset + 0xc) / 512; 120 | printf("Parsing fat use rootdir, fatstart=%d, fatend=%d\n", fatstart, fatend); 121 | } else { 122 | fatend = read_4byte_at(fp, 0x40c) / 512; 123 | printf("Parsing fat use data_offset, fatstart=%d, fatend=%d\n", fatstart, fatend); 124 | } 125 | 126 | for (fatcount = fatstart; fatcount < fatend; fatcount ++) { 127 | off_t offset = fatcount * 512; 128 | char subfile_name[16]; 129 | 130 | if (read_byte_at(fp, offset) != 1) 131 | continue; 132 | if (read_byte_at(fp, offset + 0x1) == ' ') /* rootdir */ 133 | continue; 134 | if (read_2byte_at(fp, offset + 0x10) != 0) 135 | continue; 136 | 137 | // TODO fix space padding 138 | read_bytes_at(fp, offset + 0x1, (unsigned char *)subfile_name, 8); 139 | subfile_name[8] = '.'; 140 | read_bytes_at(fp, offset + 0x9, (unsigned char *)subfile_name + 9, 3); 141 | subfile_name[12] = '\0'; 142 | 143 | if (read_byte_at(fp, offset + 0x9) == 'G' && 144 | read_byte_at(fp, offset + 0xa) == 'M' && 145 | read_byte_at(fp, offset + 0xb) == 'P') { 146 | off_t tre_offset; 147 | offset = read_2byte_at(fp, offset + 0x20) * block_size; 148 | if (read_byte_at(fp, offset + 0x2) != 'G' || 149 | read_byte_at(fp, offset + 0x9) != 'G' || 150 | read_byte_at(fp, offset + 0xa) != 'M' || 151 | read_byte_at(fp, offset + 0xb) != 'P') 152 | errexit("Unknown GMP file header.\n"); 153 | if (read_byte_at(fp, offset + 0xd) & 0x80) 154 | printf("WARNING: don't know how to unlock GMP file %s\n", subfile_name); 155 | tre_offset = offset + read_4byte_at(fp, offset + 0x19); 156 | if (read_byte_at(fp, tre_offset + 0x2) != 'G' || 157 | read_byte_at(fp, tre_offset + 0x9) != 'T' || 158 | read_byte_at(fp, tre_offset + 0xa) != 'R' || 159 | read_byte_at(fp, tre_offset + 0xb) != 'E') 160 | errexit("Unknown TRE file header at %x in GMP.\n", (unsigned int)tre_offset); 161 | if (read_byte_at(fp, tre_offset + 0xd) & 0x80) { 162 | printf("Unlocking subfile %s\n", subfile_name); 163 | patch = unlock_tre(fp, patch, offset, tre_offset); 164 | } else { 165 | printf("Skipping plain subfile %s\n", subfile_name); 166 | } 167 | //TODO check other subsubfiles 168 | } else if (read_byte_at(fp, offset + 0x9) == 'T' && 169 | read_byte_at(fp, offset + 0xa) == 'R' && 170 | read_byte_at(fp, offset + 0xb) == 'E') { 171 | offset = read_2byte_at(fp, offset + 0x20) * block_size; 172 | if (read_byte_at(fp, offset + 0x2) != 'G' || 173 | read_byte_at(fp, offset + 0x9) != 'T' || 174 | read_byte_at(fp, offset + 0xa) != 'R' || 175 | read_byte_at(fp, offset + 0xb) != 'E') 176 | errexit("Unknown TRE file header.\n"); 177 | if (read_byte_at(fp, offset + 0xd) & 0x80) { 178 | printf("Unlocking subfile %s\n", subfile_name); 179 | patch = unlock_tre(fp, patch, offset, offset); 180 | } else { 181 | printf("Skipping plain subfile %s\n", subfile_name); 182 | } 183 | } else if (strcmp(subfile_name, "MAPSOURC.MPS") == 0) { 184 | printf("Skipping MAPSOURC.MPS\n"); 185 | } else { 186 | offset = read_2byte_at(fp, offset + 0x20) * block_size; 187 | if (read_byte_at(fp, offset + 0xd) & 0x80) { 188 | printf("WARNING: don't know how to unlock subfile %s\n", subfile_name); 189 | } else { 190 | printf("Skipping plain subfile %s\n", subfile_name); 191 | } 192 | } 193 | } 194 | 195 | return patch; 196 | } 197 | 198 | void apply_patch (FILE *fp, struct patch_struct *patch_list) 199 | { 200 | struct patch_struct *patch; 201 | 202 | for (patch = patch_list; patch != NULL; patch = patch->next) { 203 | if (myfseek64(fp, patch->offset)) { 204 | perror(NULL); 205 | return; 206 | } 207 | if (fwrite(patch->data, patch->size, 1, fp) != 1) { 208 | perror(NULL); 209 | return; 210 | } 211 | } 212 | } 213 | 214 | int main (int argc, char *argv[]) 215 | { 216 | FILE *fp; 217 | struct patch_struct *patch; 218 | 219 | if (argc != 2) { 220 | printf("usage: %s file.img\n", argv[0]); 221 | return 1; 222 | } 223 | if (strcmp(argv[1], "-h") == 0 || 224 | strcmp(argv[1], "--help") == 0 || 225 | strcmp(argv[1], "-?") == 0) { 226 | printf("usage: %s file.img\n", argv[0]); 227 | return 1; 228 | } 229 | 230 | printf("Analyzing file.\n"); 231 | fp = fopen(argv[1], "rb"); 232 | if (fp == NULL) { 233 | printf("can't open %s\n", argv[1]); 234 | return 1; 235 | } 236 | patch = create_patch(fp); 237 | fclose(fp); 238 | if (patch == NULL) 239 | return 1; 240 | 241 | printf("Writing to file.\n"); 242 | fp = fopen(argv[1], "rb+"); 243 | if (fp == NULL) { 244 | printf("can't open %s for writing\n", argv[1]); 245 | return 1; 246 | } 247 | apply_patch(fp, patch); 248 | fclose(fp); 249 | 250 | return 0; 251 | } 252 | -------------------------------------------------------------------------------- /stdintvc.h: -------------------------------------------------------------------------------- 1 | // ISO C9x compliant stdint.h for Microsoft Visual Studio 2 | // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 3 | // 4 | // Copyright (c) 2006-2008 Alexander Chemeris 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // 1. Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // 12 | // 2. Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // 16 | // 3. The name of the author may be used to endorse or promote products 17 | // derived from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 21 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 22 | // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 | // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 28 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | /////////////////////////////////////////////////////////////////////////////// 31 | 32 | #ifndef _MSC_VER // [ 33 | #error "Use this header only with Microsoft Visual C++ compilers!" 34 | #endif // _MSC_VER ] 35 | 36 | #ifndef _MSC_STDINT_H_ // [ 37 | #define _MSC_STDINT_H_ 38 | 39 | #if _MSC_VER > 1000 40 | #pragma once 41 | #endif 42 | 43 | #include 44 | 45 | // For Visual Studio 6 in C++ mode and for many Visual Studio versions when 46 | // compiling for ARM we should wrap include with 'extern "C++" {}' 47 | // or compiler give many errors like this: 48 | // error C2733: second C linkage of overloaded function 'wmemchr' not allowed 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | # include 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | // Define _W64 macros to mark types changing their size, like intptr_t. 58 | #ifndef _W64 59 | # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 60 | # define _W64 __w64 61 | # else 62 | # define _W64 63 | # endif 64 | #endif 65 | 66 | 67 | // 7.18.1 Integer types 68 | 69 | // 7.18.1.1 Exact-width integer types 70 | 71 | // Visual Studio 6 and Embedded Visual C++ 4 doesn't 72 | // realize that, e.g. char has the same size as __int8 73 | // so we give up on __intX for them. 74 | #if (_MSC_VER < 1300) 75 | typedef signed char int8_t; 76 | typedef signed short int16_t; 77 | typedef signed int int32_t; 78 | typedef unsigned char uint8_t; 79 | typedef unsigned short uint16_t; 80 | typedef unsigned int uint32_t; 81 | #else 82 | typedef signed __int8 int8_t; 83 | typedef signed __int16 int16_t; 84 | typedef signed __int32 int32_t; 85 | typedef unsigned __int8 uint8_t; 86 | typedef unsigned __int16 uint16_t; 87 | typedef unsigned __int32 uint32_t; 88 | #endif 89 | typedef signed __int64 int64_t; 90 | typedef unsigned __int64 uint64_t; 91 | 92 | 93 | // 7.18.1.2 Minimum-width integer types 94 | typedef int8_t int_least8_t; 95 | typedef int16_t int_least16_t; 96 | typedef int32_t int_least32_t; 97 | typedef int64_t int_least64_t; 98 | typedef uint8_t uint_least8_t; 99 | typedef uint16_t uint_least16_t; 100 | typedef uint32_t uint_least32_t; 101 | typedef uint64_t uint_least64_t; 102 | 103 | // 7.18.1.3 Fastest minimum-width integer types 104 | typedef int8_t int_fast8_t; 105 | typedef int16_t int_fast16_t; 106 | typedef int32_t int_fast32_t; 107 | typedef int64_t int_fast64_t; 108 | typedef uint8_t uint_fast8_t; 109 | typedef uint16_t uint_fast16_t; 110 | typedef uint32_t uint_fast32_t; 111 | typedef uint64_t uint_fast64_t; 112 | 113 | // 7.18.1.4 Integer types capable of holding object pointers 114 | #ifdef _WIN64 // [ 115 | typedef signed __int64 intptr_t; 116 | typedef unsigned __int64 uintptr_t; 117 | #else // _WIN64 ][ 118 | typedef _W64 signed int intptr_t; 119 | typedef _W64 unsigned int uintptr_t; 120 | #endif // _WIN64 ] 121 | 122 | // 7.18.1.5 Greatest-width integer types 123 | typedef int64_t intmax_t; 124 | typedef uint64_t uintmax_t; 125 | 126 | 127 | // 7.18.2 Limits of specified-width integer types 128 | 129 | #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 130 | 131 | // 7.18.2.1 Limits of exact-width integer types 132 | #define INT8_MIN ((int8_t)_I8_MIN) 133 | #define INT8_MAX _I8_MAX 134 | #define INT16_MIN ((int16_t)_I16_MIN) 135 | #define INT16_MAX _I16_MAX 136 | #define INT32_MIN ((int32_t)_I32_MIN) 137 | #define INT32_MAX _I32_MAX 138 | #define INT64_MIN ((int64_t)_I64_MIN) 139 | #define INT64_MAX _I64_MAX 140 | #define UINT8_MAX _UI8_MAX 141 | #define UINT16_MAX _UI16_MAX 142 | #define UINT32_MAX _UI32_MAX 143 | #define UINT64_MAX _UI64_MAX 144 | 145 | // 7.18.2.2 Limits of minimum-width integer types 146 | #define INT_LEAST8_MIN INT8_MIN 147 | #define INT_LEAST8_MAX INT8_MAX 148 | #define INT_LEAST16_MIN INT16_MIN 149 | #define INT_LEAST16_MAX INT16_MAX 150 | #define INT_LEAST32_MIN INT32_MIN 151 | #define INT_LEAST32_MAX INT32_MAX 152 | #define INT_LEAST64_MIN INT64_MIN 153 | #define INT_LEAST64_MAX INT64_MAX 154 | #define UINT_LEAST8_MAX UINT8_MAX 155 | #define UINT_LEAST16_MAX UINT16_MAX 156 | #define UINT_LEAST32_MAX UINT32_MAX 157 | #define UINT_LEAST64_MAX UINT64_MAX 158 | 159 | // 7.18.2.3 Limits of fastest minimum-width integer types 160 | #define INT_FAST8_MIN INT8_MIN 161 | #define INT_FAST8_MAX INT8_MAX 162 | #define INT_FAST16_MIN INT16_MIN 163 | #define INT_FAST16_MAX INT16_MAX 164 | #define INT_FAST32_MIN INT32_MIN 165 | #define INT_FAST32_MAX INT32_MAX 166 | #define INT_FAST64_MIN INT64_MIN 167 | #define INT_FAST64_MAX INT64_MAX 168 | #define UINT_FAST8_MAX UINT8_MAX 169 | #define UINT_FAST16_MAX UINT16_MAX 170 | #define UINT_FAST32_MAX UINT32_MAX 171 | #define UINT_FAST64_MAX UINT64_MAX 172 | 173 | // 7.18.2.4 Limits of integer types capable of holding object pointers 174 | #ifdef _WIN64 // [ 175 | # define INTPTR_MIN INT64_MIN 176 | # define INTPTR_MAX INT64_MAX 177 | # define UINTPTR_MAX UINT64_MAX 178 | #else // _WIN64 ][ 179 | # define INTPTR_MIN INT32_MIN 180 | # define INTPTR_MAX INT32_MAX 181 | # define UINTPTR_MAX UINT32_MAX 182 | #endif // _WIN64 ] 183 | 184 | // 7.18.2.5 Limits of greatest-width integer types 185 | #define INTMAX_MIN INT64_MIN 186 | #define INTMAX_MAX INT64_MAX 187 | #define UINTMAX_MAX UINT64_MAX 188 | 189 | // 7.18.3 Limits of other integer types 190 | 191 | #ifdef _WIN64 // [ 192 | # define PTRDIFF_MIN _I64_MIN 193 | # define PTRDIFF_MAX _I64_MAX 194 | #else // _WIN64 ][ 195 | # define PTRDIFF_MIN _I32_MIN 196 | # define PTRDIFF_MAX _I32_MAX 197 | #endif // _WIN64 ] 198 | 199 | #define SIG_ATOMIC_MIN INT_MIN 200 | #define SIG_ATOMIC_MAX INT_MAX 201 | 202 | #ifndef SIZE_MAX // [ 203 | # ifdef _WIN64 // [ 204 | # define SIZE_MAX _UI64_MAX 205 | # else // _WIN64 ][ 206 | # define SIZE_MAX _UI32_MAX 207 | # endif // _WIN64 ] 208 | #endif // SIZE_MAX ] 209 | 210 | // WCHAR_MIN and WCHAR_MAX are also defined in 211 | #ifndef WCHAR_MIN // [ 212 | # define WCHAR_MIN 0 213 | #endif // WCHAR_MIN ] 214 | #ifndef WCHAR_MAX // [ 215 | # define WCHAR_MAX _UI16_MAX 216 | #endif // WCHAR_MAX ] 217 | 218 | #define WINT_MIN 0 219 | #define WINT_MAX _UI16_MAX 220 | 221 | #endif // __STDC_LIMIT_MACROS ] 222 | 223 | 224 | // 7.18.4 Limits of other integer types 225 | 226 | #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 227 | 228 | // 7.18.4.1 Macros for minimum-width integer constants 229 | 230 | #define INT8_C(val) val##i8 231 | #define INT16_C(val) val##i16 232 | #define INT32_C(val) val##i32 233 | #define INT64_C(val) val##i64 234 | 235 | #define UINT8_C(val) val##ui8 236 | #define UINT16_C(val) val##ui16 237 | #define UINT32_C(val) val##ui32 238 | #define UINT64_C(val) val##ui64 239 | 240 | // 7.18.4.2 Macros for greatest-width integer constants 241 | #define INTMAX_C INT64_C 242 | #define UINTMAX_C UINT64_C 243 | 244 | #endif // __STDC_CONSTANT_MACROS ] 245 | 246 | 247 | #endif // _MSC_STDINT_H_ ] 248 | -------------------------------------------------------------------------------- /cmdlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "cmdlib.h" 7 | 8 | int cmd_spd; /* sample per degree */ 9 | int cmd_x_num, cmd_y_num; 10 | short *cmd_devs = NULL; 11 | 12 | int cmd_init (const char *path) 13 | { 14 | FILE *fp = fopen(path, "rb"); 15 | if (fp == NULL) { 16 | printf("cannot open %s\n", path); 17 | goto errout; 18 | } 19 | 20 | cmd_spd = 0; 21 | if (fread(&cmd_spd, 4, 1, fp) != 1) { 22 | printf("%s is invalid cmd DB\n", path); 23 | goto errout; 24 | } 25 | if (cmd_spd < 0 || cmd_spd > 100) { 26 | printf("%s has invalid sample per degree\n", path); 27 | goto errout; 28 | } 29 | cmd_x_num = (CMD_X1 - CMD_X0) * cmd_spd; 30 | cmd_y_num = (CMD_Y1 - CMD_Y0) * cmd_spd; 31 | cmd_devs = (short *)malloc(cmd_x_num * cmd_y_num * 4); 32 | if (fread(cmd_devs, cmd_x_num * cmd_y_num * 4, 1, fp) != 1) { 33 | printf("%s is truncated\n", path); 34 | goto errout; 35 | } 36 | fclose(fp); 37 | return 0; 38 | 39 | errout: 40 | if (cmd_devs) 41 | free(cmd_devs); 42 | cmd_devs = NULL; 43 | if (fp) 44 | fclose(fp); 45 | return 1; 46 | } 47 | 48 | void cmd_fini (void) 49 | { 50 | if (cmd_devs != NULL) 51 | free(cmd_devs); 52 | cmd_devs = NULL; 53 | } 54 | 55 | /* cmd_point_dev(x, y, 0): get deviation x of a point (x,y) 56 | * cmd_point_dev(x, y, 1): get deviation y of a point (x,y) 57 | * x, y are in sample unit. 58 | * returned value is in deviation unit. */ 59 | static inline double cmd_point_dev (double x, double y, int isdy) 60 | { 61 | int ix = (int)x; 62 | int iy = (int)y; 63 | 64 | if (x <= 0) x = ix = 0; 65 | else if (ix > cmd_x_num - 2) x = ix = cmd_x_num - 2; 66 | if (y <= 0) y = iy = 0; 67 | else if (iy > cmd_y_num - 2) y = iy = cmd_y_num - 2; 68 | 69 | if (isdy) { 70 | return DEVY(ix,iy) * (ix + 1 - x) * (iy + 1 - y) + 71 | DEVY(ix+1,iy) * (x - ix) * (iy + 1 - y) + 72 | DEVY(ix,iy+1) * (ix + 1 - x) * (y - iy) + 73 | DEVY(ix+1,iy+1) * (x - ix) * (y - iy); 74 | } else { 75 | return DEVX(ix,iy) * (ix + 1 - x) * (iy + 1 - y) + 76 | DEVX(ix+1,iy) * (x - ix) * (iy + 1 - y) + 77 | DEVX(ix,iy+1) * (ix + 1 - x) * (y - iy) + 78 | DEVX(ix+1,iy+1) * (x - ix) * (y - iy); 79 | } 80 | } 81 | 82 | /* cmd_g24p_fix: given a deviated point, return the corrected point. 83 | * cmd_g24p_dev: given a correcte point, return the deviated point. 84 | * The input and output is in garmin's 24bit unit */ 85 | void cmd_g24p_fix (int *px, int *py) 86 | { 87 | double x, y; 88 | 89 | x = (*px * 360.0 / 0x1000000 - CMD_X0) * cmd_spd; 90 | y = (*py * 360.0 / 0x1000000 - CMD_Y0) * cmd_spd; 91 | 92 | *px -= (int)lround(cmd_point_dev(x, y, 0) * 0x1000000 / 360 / CMD_DUPD); 93 | *py -= (int)lround(cmd_point_dev(x, y, 1) * 0x1000000 / 360 / CMD_DUPD); 94 | } 95 | void cmd_g24p_dev (int *px, int *py) 96 | { 97 | double x, y; 98 | 99 | x = (*px * 360.0 / 0x1000000 - CMD_X0) * cmd_spd; 100 | y = (*py * 360.0 / 0x1000000 - CMD_Y0) * cmd_spd; 101 | 102 | *px += (int)lround(cmd_point_dev(x, y, 0) * 0x1000000 / 360 / CMD_DUPD); 103 | *py += (int)lround(cmd_point_dev(x, y, 1) * 0x1000000 / 360 / CMD_DUPD); 104 | } 105 | 106 | /* cmd_ls_dx: get average deviation x of a vertical line segment. 107 | * The purpose of it is to correct rectangle boundry. 108 | * x, y0, y1 are all in sample unit. 109 | * returned value is in deviation unit. 110 | * swapxy: I'm too lazy to implement two versions: cmd_ls_dx and cmd_ls_dy 111 | * swapxy = 0 means cmd_ls_dx(x, y0, y1) 112 | * swapxy = 1 means cmd_ls_dy(y, x0, x1) */ 113 | static double cmd_ls_dx (double x, double y0, double y1, int swapxy) 114 | { 115 | int n, i, ix, iy0, iy1; 116 | const int my_x_num = swapxy ? cmd_y_num : cmd_x_num; 117 | const int my_y_num = swapxy ? cmd_x_num : cmd_y_num; 118 | double sum; 119 | 120 | assert(y0 <= y1); 121 | 122 | ix = (int)x; 123 | if (ix <= 0) ix = x = 0; 124 | else if (ix > my_x_num - 2) ix = x = my_x_num - 2; 125 | 126 | iy0 = lround(y0); 127 | iy1 = lround(y1); 128 | if (iy0 < 0) iy0 = 0; 129 | if (iy0 >= my_y_num) iy0 = my_y_num - 1; 130 | if (iy1 < 0) iy1 = 0; 131 | if (iy1 >= my_y_num) iy1 = my_y_num - 1; 132 | 133 | n = iy1 - iy0; 134 | if (n <= 1) 135 | return swapxy ? 136 | cmd_point_dev((y0 + y1) / 2, x, 1) : 137 | cmd_point_dev(x, (y0 + y1) / 2, 0); 138 | if (n > 50) n = 50; /* don't want too much computation */ 139 | /* TODO: the deviation follows periodic pattern. 140 | * if our samples are aliased against the period, we can get biased average. */ 141 | for (sum = 0.0, i = 0; i <= n; i ++) { 142 | int iy = (i * (iy1 - iy0) + n * iy0) / n; 143 | if (swapxy) 144 | sum += DEVY(iy,ix) * (ix + 1 - x) + DEVY(iy,ix+1) * (x - ix); 145 | else 146 | sum += DEVX(ix,iy) * (ix + 1 - x) + DEVX(ix+1,iy) * (x - ix); 147 | } 148 | return sum / (n + 1); 149 | } 150 | 151 | /* cmd_rect_dev: get average deviation of a rectangle area. 152 | * input parameters are in sample unit. 153 | * returned value is in deviation unit. */ 154 | static double cmd_rect_dev (double x0, double x1, double y0, double y1, int isdy) 155 | { 156 | int ix0, ix1, iy0, iy1, nx, ny, countx, county; 157 | double sum; 158 | 159 | assert(x0 <= x1 && y0 <= y1); 160 | 161 | ix0 = (int)x0; 162 | if (ix0 < 0) ix0 = 0; 163 | else if (ix0 >= cmd_x_num) ix0 = cmd_x_num - 1; 164 | ix1 = (int)x1; 165 | if (ix1 < 0) ix1 = 0; 166 | else if (ix1 >= cmd_x_num) ix1 = cmd_x_num - 1; 167 | iy0 = (int)y0; 168 | if (iy0 < 0) iy0 = 0; 169 | else if (iy0 >= cmd_y_num) iy0 = cmd_y_num - 1; 170 | iy1 = (int)y1; 171 | if (iy1 < 0) iy1 = 0; 172 | else if (iy1 >= cmd_y_num) iy1 = cmd_y_num - 1; 173 | 174 | nx = ix1 - ix0; 175 | ny = iy1 - iy0; 176 | 177 | if (nx > 10) nx = 10; /* don't want too much computation */ 178 | if (ny > 10) ny = 10; 179 | if (nx <= 1 && ny <= 1) 180 | return cmd_point_dev((x0 + x1) / 2, (y0 + y1) / 2, isdy); 181 | /* TODO: the deviation follows periodic pattern. 182 | * if our samples are aliased against the period, we can get biased average. */ 183 | if (nx <= 1) { 184 | for (sum = 0.0, county = 0; county <= ny; county ++) 185 | sum += cmd_point_dev((x0 + x1) / 2, (county * (iy1 - iy0) + ny * iy0) / ny, isdy); 186 | return sum / (ny + 1); 187 | } 188 | if (ny <= 1) { 189 | for (sum = 0.0, countx = 0; countx <= nx; countx ++) 190 | sum += cmd_point_dev((countx * (ix1 - ix0) + nx * ix0) / nx, (y0 + y1) / 2, isdy); 191 | return sum / (nx + 1); 192 | } 193 | for (sum = 0.0, county = 0; county <= ny; county ++) 194 | for (countx = 0; countx <= nx; countx ++) { 195 | int ix = (countx * (ix1 - ix0) + nx * ix0) / nx; 196 | int iy = (county * (iy1 - iy0) + ny * iy0) / ny; 197 | sum += isdy ? DEVY(ix,iy) : DEVX(ix,iy); 198 | } 199 | return sum / ((nx + 1) * (ny + 1)); 200 | } 201 | 202 | /* cmd_g24r_fix: given a divated rectangle, return the corrected one. 203 | * cmd_g24r_dev: given a correct rectangle, return the divated one. 204 | * avgopt: average option. 0: average the area; 1: average the boundary. 205 | * the input and output is in garmin's 24bit unit. 206 | * assume x0 <= x1 && y0 <= y1 */ 207 | void cmd_g24r_fix (int *px0, int *py0, int *px1, int *py1, int avgopt) 208 | { 209 | double x0, y0, x1, y1; 210 | 211 | assert(*px0 <= *px1 && *py0 <= *py1); 212 | assert(avgopt == 0 || avgopt == 1); 213 | 214 | x0 = (*px0 * 360.0 / 0x1000000 - CMD_X0) * cmd_spd; 215 | x1 = (*px1 * 360.0 / 0x1000000 - CMD_X0) * cmd_spd; 216 | y0 = (*py0 * 360.0 / 0x1000000 - CMD_Y0) * cmd_spd; 217 | y1 = (*py1 * 360.0 / 0x1000000 - CMD_Y0) * cmd_spd; 218 | 219 | if (avgopt == 0) { 220 | int dx = (int)lround(cmd_rect_dev(x0, x1, y0, y1, 0) * 0x1000000 / 360 / CMD_DUPD); 221 | int dy = (int)lround(cmd_rect_dev(x0, x1, y0, y1, 1) * 0x1000000 / 360 / CMD_DUPD); 222 | *px0 -= dx; 223 | *px1 -= dx; 224 | *py0 -= dy; 225 | *py1 -= dy; 226 | } else { 227 | *px0 -= (int)lround(cmd_ls_dx(x0, y0, y1, 0) * 0x1000000 / 360 / CMD_DUPD); 228 | *px1 -= (int)lround(cmd_ls_dx(x1, y0, y1, 0) * 0x1000000 / 360 / CMD_DUPD); 229 | *py0 -= (int)lround(cmd_ls_dx(y0, x0, x1, 1) * 0x1000000 / 360 / CMD_DUPD); 230 | *py1 -= (int)lround(cmd_ls_dx(y1, x0, x1, 1) * 0x1000000 / 360 / CMD_DUPD); 231 | } 232 | } 233 | void cmd_g24r_dev (int *px0, int *py0, int *px1, int *py1, int avgopt) 234 | { 235 | double x0, y0, x1, y1; 236 | 237 | assert(*px0 <= *px1 && *py0 <= *py1); 238 | assert(avgopt == 0 || avgopt == 1); 239 | 240 | x0 = (*px0 * 360.0 / 0x1000000 - CMD_X0) * cmd_spd; 241 | x1 = (*px1 * 360.0 / 0x1000000 - CMD_X0) * cmd_spd; 242 | y0 = (*py0 * 360.0 / 0x1000000 - CMD_Y0) * cmd_spd; 243 | y1 = (*py1 * 360.0 / 0x1000000 - CMD_Y0) * cmd_spd; 244 | 245 | if (avgopt == 0) { 246 | int dx = (int)lround(cmd_rect_dev(x0, x1, y0, y1, 0) * 0x1000000 / 360 / CMD_DUPD); 247 | int dy = (int)lround(cmd_rect_dev(x0, x1, y0, y1, 1) * 0x1000000 / 360 / CMD_DUPD); 248 | *px0 += dx; 249 | *px1 += dx; 250 | *py0 += dy; 251 | *py1 += dy; 252 | } else { 253 | *px0 += (int)lround(cmd_ls_dx(x0, y0, y1, 0) * 0x1000000 / 360 / CMD_DUPD); 254 | *px1 += (int)lround(cmd_ls_dx(x1, y0, y1, 0) * 0x1000000 / 360 / CMD_DUPD); 255 | *py0 += (int)lround(cmd_ls_dx(y0, x0, x1, 1) * 0x1000000 / 360 / CMD_DUPD); 256 | *py1 += (int)lround(cmd_ls_dx(y1, x0, x1, 1) * 0x1000000 / 360 / CMD_DUPD); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /gimgch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "util_indep.h" 8 | 9 | struct header_struct { 10 | const char *imgpath; 11 | char subfile[16]; 12 | off_t subfile_offset; /* abs offset */ 13 | int subfile_size; 14 | int header_rel_offset; /* 0 if it's OF */ 15 | int header_size; 16 | unsigned char *header; 17 | char id[4]; 18 | }; 19 | 20 | #define MAX_HEADERS 8192 21 | static struct header_struct *headers[MAX_HEADERS]; 22 | static int header_num = 0; 23 | 24 | 25 | static void display_headers (int line_columns) 26 | { 27 | int i, ptr, bytes_pre_line; 28 | char emptyid[4]; 29 | 30 | for (i = 0; i < header_num; i ++) { 31 | if (header_num <= 26) { 32 | headers[i]->id[0] = 'a' + i; 33 | headers[i]->id[1] = '\0'; 34 | } else if (header_num <= 26 * 26) { 35 | headers[i]->id[0] = 'a' + i / 26; 36 | headers[i]->id[1] = 'a' + i % 26; 37 | headers[i]->id[2] = '\0'; 38 | } else { 39 | headers[i]->id[0] = 'a' + i / (26 * 26); 40 | headers[i]->id[1] = 'a' + i / 26 % 26; 41 | headers[i]->id[2] = 'a' + i % 26; 42 | headers[i]->id[3] = '\0'; 43 | } 44 | } 45 | bytes_pre_line = (line_columns - strlen(headers[0]->id) - 1) / 2; 46 | memset(emptyid, ' ', 4); 47 | emptyid[strlen(headers[0]->id)] = '\0'; 48 | 49 | for (i = 0; i < header_num; i ++) { 50 | printf("%s %8lx=foff %8x=flen %4x=hoff %4x=hlen %s %s\n", 51 | headers[i]->id, 52 | (unsigned long)headers[i]->subfile_offset, 53 | headers[i]->subfile_size, 54 | headers[i]->header_rel_offset, 55 | headers[i]->header_size, 56 | headers[i]->imgpath, 57 | headers[i]->subfile); 58 | } 59 | printf("\n"); 60 | 61 | for (ptr = 0; ; ptr += bytes_pre_line) { 62 | int more_lines = 0, bc, fc; 63 | 64 | /* print address bar */ 65 | printf("%s ", emptyid); 66 | for (bc = 0; bc <= bytes_pre_line - 2; bc += 4) { 67 | printf("%-4x", ptr + bc); 68 | if (bc < bytes_pre_line - 4) 69 | printf(" "); 70 | } 71 | printf("\n"); 72 | 73 | /* print header content */ 74 | for (fc = 0; fc < header_num; fc ++) { 75 | printf("%s ", headers[fc]->id); 76 | if (headers[fc]->header_size > ptr + bytes_pre_line) 77 | more_lines = 1; 78 | // if (headers[fc]->header_size <= ptr) { 79 | // printf("\n"); 80 | // continue; 81 | // } 82 | for (bc = 0; bc < headers[fc]->header_size - ptr && bc < bytes_pre_line; bc ++) 83 | printf("%02x", headers[fc]->header[ptr + bc]); 84 | printf("\n"); 85 | } 86 | 87 | /* print diff */ 88 | if (header_num > 1) { 89 | printf("%s ", emptyid); 90 | for (bc = 0; bc < bytes_pre_line; bc ++) { 91 | int b = -1; 92 | for (fc = 0; fc < header_num; fc ++) { 93 | if (ptr + bc >= headers[fc]->header_size) 94 | continue; 95 | if (b == -1) 96 | b = headers[fc]->header[ptr + bc]; 97 | if (b != headers[fc]->header[ptr + bc]) 98 | break; 99 | } 100 | printf(fc == header_num ? " " : "* " ); 101 | } 102 | printf("\n"); 103 | } 104 | 105 | /* print address bar again */ 106 | printf("%s ", emptyid); 107 | for (bc = 0; bc <= bytes_pre_line - 2; bc += 4) { 108 | printf("%-4x", ptr + bc); 109 | if (bc < bytes_pre_line - 4) 110 | printf(" "); 111 | } 112 | printf("\n\n"); 113 | 114 | if (!more_lines) 115 | break; 116 | } 117 | } 118 | 119 | static int add_header(FILE *fp, const char *imgpath, const char *sf_name, 120 | off_t subfile_offset, int subfile_size, int header_rel_offset) 121 | { 122 | struct header_struct *header = 123 | (struct header_struct *)malloc(sizeof(struct header_struct)); 124 | 125 | header->imgpath = imgpath; 126 | strcpy(header->subfile, sf_name); 127 | header->subfile_offset = subfile_offset; 128 | header->subfile_size = subfile_size; 129 | header->header_rel_offset = header_rel_offset; 130 | 131 | header->header_size = read_2byte_at(fp, subfile_offset + header_rel_offset); 132 | if (header->header_size < 0x15) { 133 | printf("wrong header size %d\n", header->header_size); 134 | return 1; 135 | } 136 | 137 | header->header = (unsigned char *)malloc(header->header_size); 138 | read_bytes_at(fp, subfile_offset + header_rel_offset, 139 | header->header, header->header_size); 140 | 141 | if (header_num >= MAX_HEADERS) { 142 | printf("too many files\n"); 143 | return 1; 144 | } 145 | headers[header_num ++] = header; 146 | return 0; 147 | } 148 | 149 | static int read_header (const char *imgpath, const char *subfile_name_pattern, 150 | int match_maximum) 151 | { 152 | FILE *fp = NULL; 153 | int block_size, fatstart, fatend, fatcount; 154 | int added = 0; 155 | 156 | fp = fopen(imgpath, "rb"); 157 | if (fp == NULL) 158 | errexit("can't open %s", imgpath); 159 | 160 | if ((read_byte_at(fp, 0) ^ read_byte_at(fp, 0x10)) != 'D' || 161 | (read_byte_at(fp, 0) ^ read_byte_at(fp, 0x15)) != 'G') 162 | errexit("Not a garmin img file.\n"); 163 | if (read_byte_at(fp, 0)) 164 | errexit("XOR is not 0. Fix it first.\n"); 165 | 166 | block_size = 1 << (read_byte_at(fp, 0x61) + read_byte_at(fp, 0x62)); 167 | 168 | fatstart = read_byte_at(fp, 0x40) == 0 ? 3 : read_byte_at(fp, 0x40); 169 | if (read_4byte_at(fp, 0x40c) == 0) { // use root dir. assume it's the first file 170 | off_t offset = fatstart * 512; 171 | if (read_byte_at(fp, offset) != 1 || 172 | read_byte_at(fp, offset + 0x1) != ' ' || 173 | read_byte_at(fp, offset + 0x9) != ' ') 174 | errexit("imgheader.data_offset = 0 but the first file is not root dir!\n"); 175 | fatstart ++; 176 | if (read_4byte_at(fp, offset + 0xc) % 512 != 0 || 177 | read_4byte_at(fp, offset + 0xc) <= fatstart * 512) 178 | errexit("rootdir.size = %x which is bad\n", read_4byte_at(fp, offset + 0xc)); 179 | fatend = read_4byte_at(fp, offset + 0xc) / 512; 180 | //printf("Parsing fat use rootdir, fatstart=%d, fatend=%d\n", fatstart, fatend); 181 | } else { 182 | fatend = read_4byte_at(fp, 0x40c) / 512; 183 | //printf("Parsing fat use data_offset, fatstart=%d, fatend=%d\n", fatstart, fatend); 184 | } 185 | 186 | for (fatcount = fatstart; fatcount < fatend; fatcount ++) { 187 | off_t offset = fatcount * 512, subfile_offset; 188 | int subfile_size; 189 | char sf_name[16]; 190 | 191 | if (read_byte_at(fp, offset) != 1) 192 | continue; 193 | if (read_byte_at(fp, offset + 0x1) == ' ') /* rootdir */ 194 | continue; 195 | if (read_2byte_at(fp, offset + 0x10) != 0) /* part != 0 */ 196 | continue; 197 | 198 | /* make up sf_name */ 199 | memset(sf_name, 0, sizeof(sf_name)); 200 | read_bytes_at(fp, offset + 0x1, (unsigned char *)sf_name, 8); 201 | if (strchr(sf_name, ' ') != NULL) 202 | strchr(sf_name, ' ')[0] = '\0'; 203 | strcat(sf_name, "."); 204 | read_bytes_at(fp, offset + 0x9, (unsigned char *)sf_name + strlen(sf_name), 3); 205 | if (strchr(sf_name, ' ') != NULL) 206 | strchr(sf_name, ' ')[0] = '\0'; 207 | /* a simple security check */ 208 | if (strchr(sf_name, '/') != NULL || strchr(sf_name, '\\') != NULL) 209 | errexit("invalid subfile name %s\n", sf_name); 210 | 211 | subfile_offset = read_2byte_at(fp, offset + 0x20) * block_size; 212 | subfile_size = read_4byte_at(fp, offset + 0xc); 213 | 214 | if (subfile_name_pattern == NULL || strstr(sf_name, subfile_name_pattern)) { 215 | /* header integrity test */ 216 | if (read_byte_at(fp, subfile_offset + 2) != 'G' || 217 | read_byte_at(fp, subfile_offset + 3) != 'A' || 218 | read_byte_at(fp, subfile_offset + 4) != 'R') { 219 | if (!strstr(sf_name, ".MPS")) /* I know MPS doesn't have proper header */ 220 | printf("warning: %s has invalid header\n", sf_name); 221 | continue; 222 | } 223 | if (add_header(fp, imgpath, sf_name, subfile_offset, subfile_size, 0)) 224 | errexit("add_header failed\n"); 225 | added ++; 226 | if (match_maximum && added >= match_maximum) 227 | goto out; 228 | } 229 | 230 | if (strstr(sf_name, ".GMP")) { 231 | static const char *exts[] = {".TRE", ".RGN", ".LBL", ".NET", ".NOD", ".DEM", ".MAR"}; 232 | static const int offs[] = {0x19, 0x1d, 0x21, 0x25, 0x29, 0x2d, 0x31,}; 233 | int i; 234 | for (i = 0; i < 7; i ++) { 235 | char sf_new_name[16]; 236 | int header_rel_offset; 237 | strcpy(sf_new_name, sf_name); 238 | strcpy(strstr(sf_new_name, ".GMP"), exts[i]); 239 | if (subfile_name_pattern != NULL && 240 | strstr(sf_new_name, subfile_name_pattern) == NULL) 241 | continue; 242 | header_rel_offset = read_4byte_at(fp, subfile_offset + offs[i]); 243 | if (header_rel_offset == 0) 244 | continue; 245 | if (add_header(fp, imgpath, sf_new_name, subfile_offset, 246 | subfile_size, header_rel_offset)) 247 | errexit("add_header failed\n"); 248 | added ++; 249 | if (match_maximum && added >= match_maximum) 250 | goto out; 251 | } 252 | } 253 | } 254 | 255 | if (added == 0) 256 | printf("no matching subfile found in %s\n", imgpath); 257 | out: 258 | fclose(fp); 259 | return 0; 260 | } 261 | 262 | static void usage (void) 263 | { 264 | printf("Usage: gimgch [-w columns] [-m max_sf_per_img] " 265 | "[-s subfile_name_pattern] file1.img file2.img ...\n"); 266 | printf("Example: gimgch -m 1 -s TRE china-chs-2012.img china-chs-76.img\n"); 267 | printf(" The above command compares the header of the first TRE submap in two IMG files\n"); 268 | printf(" gimgch -m 0 -s TRE malsingmaps.img\n"); 269 | printf(" The above command compares all TREs in malsingmaps.img\n"); 270 | } 271 | 272 | int main (int argc, char *argv[]) 273 | { 274 | int line_columns; 275 | #define MAX_IMGS 1024 276 | char *imgs[MAX_IMGS]; 277 | int img_num = 0; 278 | char *subfile_name_pattern = NULL; 279 | int match_maximum = 1; 280 | int i; 281 | 282 | /* default line_columns */ 283 | if (isatty(1)) { 284 | struct winsize w; 285 | ioctl(1, TIOCGWINSZ, &w); 286 | line_columns = w.ws_col; 287 | } else { 288 | line_columns = 80; 289 | } 290 | 291 | for (i = 1; i < argc; i ++) { 292 | if (strcmp("-h", argv[i]) == 0 || 293 | strcmp(argv[i], "--help") == 0 || 294 | strcmp(argv[i], "-?") == 0) { 295 | usage(); 296 | return 0; 297 | } else if (strcmp("-w", argv[i]) == 0) { 298 | if (i + 1 >= argc) { 299 | usage(); 300 | return 1; 301 | } 302 | line_columns = atoi(argv[++ i]); 303 | } else if (strcmp("-m", argv[i]) == 0) { 304 | if (i + 1 >= argc) { 305 | usage(); 306 | return 1; 307 | } 308 | match_maximum = atoi(argv[++ i]); 309 | } else if (strcmp("-s", argv[i]) == 0) { 310 | char *ptr; 311 | if (i + 1 >= argc) { 312 | usage(); 313 | return 1; 314 | } 315 | subfile_name_pattern = argv[++ i]; 316 | for (ptr = subfile_name_pattern; *ptr; ptr ++) 317 | if (*ptr >= 'a' && *ptr <= 'z') 318 | *ptr += 'A' - 'a'; 319 | } else if (argv[i][0] == '-') { 320 | printf("unknown option %s\n", argv[i]); 321 | usage(); 322 | return 1; 323 | } else { 324 | imgs[img_num ++] = argv[i]; 325 | } 326 | } 327 | 328 | if (img_num == 0) { 329 | printf("no files to examine\n"); 330 | usage(); 331 | return 1; 332 | } 333 | 334 | for (i = 0; i < img_num; i ++) 335 | if (read_header(imgs[i], subfile_name_pattern, match_maximum)) 336 | return 1; 337 | 338 | if (header_num == 0) { 339 | printf("no subfile found\n"); 340 | return 1; 341 | } 342 | 343 | display_headers(line_columns); 344 | 345 | return 0; 346 | } 347 | -------------------------------------------------------------------------------- /sf_tre.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | static void dump_poverview (uint8_t *ptr, int num, int size) 4 | { 5 | while(num -- > 0) { 6 | int i; 7 | printf("type=0x%2x", ptr[0]); 8 | printf(" maxl=%2d", ptr[1]); 9 | for (i = 2; i < size; i ++) 10 | printf(" subtype=0x%2x", ptr[i]); 11 | printf("\n"); 12 | ptr += size; 13 | } 14 | } 15 | 16 | static void dump_subdiv (uint8_t *ptr, int level_num, 17 | struct garmin_tre_map_level *levels, struct subfile_struct *rgn) 18 | { 19 | int level, global_index, level_index; 20 | 21 | for (level = 0, global_index = 1; level < level_num; level ++) { 22 | for (level_index = 0; level_index < levels[level].nsubdiv; level_index ++, global_index ++) { 23 | struct garmin_tre_subdiv *div = (struct garmin_tre_subdiv *)ptr; 24 | int bitshift = 24 - levels[level].bits; 25 | 26 | printf("%5d %d off=%06x ele=%02x+%d, lng=%8d(%s), lat=%8d(%s), w=%5d, h=%5d %c", 27 | global_index, level, 28 | bytes_to_uint24(div->rgn_offset), div->elements, div->unknownbit, 29 | bytes_to_sint24(div->center_lng), sint24_to_lng(bytes_to_sint24(div->center_lng)), 30 | bytes_to_sint24(div->center_lat), sint24_to_lat(bytes_to_sint24(div->center_lat)), 31 | div->width<height<terminate ? 'T' : ' '); 32 | if (level == level_num - 1) 33 | printf("\n"); 34 | else 35 | printf(" next=%5d\n", div->next); 36 | 37 | #if 0 38 | if (rgn && div->unknownbit) { 39 | int rgn1_offset = ((struct garmin_rgn *)rgn->header)->rgn1_offset; 40 | int offset = bytes_to_uint24(div->rgn_offset); 41 | int next_offset = level == level_num - 1 ? 42 | bytes_to_uint24(div[1].rgn_offset - 2) : 43 | bytes_to_uint24(div[1].rgn_offset); 44 | if (div->elements == 0) 45 | ; 46 | else if (div->elements == 0x10 || div->elements == 0x20) 47 | offset += *(short *)(rgn->base + rgn1_offset + offset); 48 | else if (div->elements == 0x30) 49 | offset += *(short *)(rgn->base + rgn1_offset + offset + 2); 50 | else 51 | printf("???\n"); 52 | printf("RGN: %s\n", dump_unknown_bytes(rgn->base + rgn1_offset + offset, next_offset - offset)); 53 | } 54 | #endif 55 | 56 | ptr += level == level_num - 1 ? 57 | sizeof(struct garmin_tre_subdiv) - 2 : 58 | sizeof(struct garmin_tre_subdiv); 59 | } 60 | } 61 | } 62 | 63 | static void dump_maplevels (struct garmin_tre_map_level *levels, int num) 64 | { 65 | int i; 66 | for (i = 0; i < num; i ++) { 67 | printf("level=%d bit456=0x%x %s bits=%2d nsubdiv=%d\n", 68 | levels[i].level, levels[i].bit456, 69 | levels[i].inherited ? "inher" : "noinh", 70 | levels[i].bits, levels[i].nsubdiv); 71 | } 72 | } 73 | 74 | static void dump_tre7 (uint8_t *ptr, int size, int rec_size, 75 | struct garmin_tre_map_level *levels, struct subfile_struct *rgn) 76 | { 77 | int sdidx = 1; 78 | while (size >= rec_size) { 79 | printf("%4d:", sdidx ++); 80 | printf(" rgn2_reloff=0x%x", *(uint32_t *)ptr); 81 | if (rec_size >= 8) 82 | printf(" rgn3_reloff=0x%x", *(uint32_t *)(ptr+4)); 83 | if (rec_size >= 12) 84 | printf(" rgn4_reloff=0x%x", *(uint32_t *)(ptr+8)); 85 | if (rec_size >= 16) 86 | printf(" rgn5_reloff=0x%x", *(uint32_t *)(ptr+12)); 87 | if (rec_size % 4) 88 | printf(" flag=0x%x\n", *(ptr+rec_size-1)); 89 | else 90 | printf("\n"); 91 | 92 | #if 0 93 | if (*(uint32_t *)(ptr+rec_size) - *(uint32_t *)(ptr)) 94 | printf("RGN2: %s\n", dump_unknown_bytes(rgn->base + ((struct garmin_rgn *)rgn->header)->rgn2_offset + *(uint32_t *)(ptr), *(uint32_t *)(ptr+rec_size) - *(uint32_t *)(ptr))); 95 | if (rec_size >= 8 && *(uint32_t *)(ptr+rec_size+4) - *(uint32_t *)(ptr+4)) 96 | printf("RGN3: %s\n", dump_unknown_bytes(rgn->base + ((struct garmin_rgn *)rgn->header)->rgn3_offset + *(uint32_t *)(ptr+4), *(uint32_t *)(ptr+rec_size+4) - *(uint32_t *)(ptr+4))); 97 | if (rec_size >= 12 && *(uint32_t *)(ptr+rec_size+8) - *(uint32_t *)(ptr+8)) 98 | printf("RGN4: %s\n", dump_unknown_bytes(rgn->base + ((struct garmin_rgn *)rgn->header)->rgn4_offset + *(uint32_t *)(ptr+8), *(uint32_t *)(ptr+rec_size+8) - *(uint32_t *)(ptr+8))); 99 | if (rec_size >= 16 && *(uint32_t *)(ptr+rec_size+12) - *(uint32_t *)(ptr+12)) 100 | printf("RGN5: %s\n", dump_unknown_bytes(rgn->base + ((struct garmin_rgn *)rgn->header)->rgn5_offset + *(uint32_t *)(ptr+12), *(uint32_t *)(ptr+rec_size+12) - *(uint32_t *)(ptr+12))); 101 | #endif 102 | 103 | ptr += rec_size; 104 | size -= rec_size; 105 | } 106 | } 107 | 108 | void dump_tre (struct subfile_struct *tre) 109 | { 110 | struct garmin_tre *header = (struct garmin_tre *)tre->header; 111 | struct garmin_tre_map_level *maplevels; 112 | int progress = 0; 113 | 114 | assert(tre->typeid == ST_TRE); 115 | 116 | dump_comm(tre->header); 117 | 118 | printf("=== TRE HEADER ===\n"); 119 | 120 | if (header->comm.hlen < 0x74) 121 | goto headerfini; 122 | progress = 0x74; 123 | printf("Bound: %d(%s) %d(%s) %d(%s) %d(%s)\n", 124 | bytes_to_sint24(header->northbound), sint24_to_lat(bytes_to_sint24(header->northbound)), 125 | bytes_to_sint24(header->eastbound), sint24_to_lng(bytes_to_sint24(header->eastbound)), 126 | bytes_to_sint24(header->southbound), sint24_to_lat(bytes_to_sint24(header->southbound)), 127 | bytes_to_sint24(header->westbound), sint24_to_lng(bytes_to_sint24(header->westbound))); 128 | printf("TRE1 Map Levels: reloff=0x%x absoff=0x%x size=0x%x\n", 129 | header->tre1_offset, tre->offset + header->tre1_offset, header->tre1_size); 130 | printf("TRE2 Subdivisions: reloff=0x%x absoff=0x%x size=0x%x\n", 131 | header->tre2_offset, tre->offset + header->tre2_offset, header->tre2_size); 132 | printf("TRE3 Copyright: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 133 | header->tre3_offset, tre->offset + header->tre3_offset, header->tre3_size, header->tre3_rec_size); 134 | printf("unknown_03b: %s\n", dump_unknown_bytes(header->unknown_03b, sizeof(header->unknown_03b))); 135 | printf("POI Flags: %s %s %s\n", 136 | header->POI_flags & 0x1 ? "transp" : "not-transp", 137 | header->POI_flags & 0x2 ? "street-before-num" : "num-before-street", 138 | header->POI_flags & 0x4 ? "zip-before-city" : "city-before-zip"); 139 | printf("Draw Priority: %d\n", header->drawprio); 140 | printf("unknown_041: %s\n", dump_unknown_bytes(header->unknown_041, sizeof(header->unknown_041))); 141 | printf("TRE4 Polyline Overview: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 142 | header->tre4_offset, tre->offset + header->tre4_offset, header->tre4_size, header->tre4_rec_size); 143 | printf("unknown_054: %s\n", dump_unknown_bytes(header->unknown_054, sizeof(header->unknown_054))); 144 | printf("TRE5 Polygon Overview: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 145 | header->tre5_offset, tre->offset + header->tre5_offset, header->tre5_size, header->tre5_rec_size); 146 | printf("unknown_062: %s\n", dump_unknown_bytes(header->unknown_062, sizeof(header->unknown_062))); 147 | printf("TRE6 Point Overview: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 148 | header->tre6_offset, tre->offset + header->tre6_offset, header->tre6_size, header->tre6_rec_size); 149 | printf("unknown_070: %s\n", dump_unknown_bytes(header->unknown_070, sizeof(header->unknown_070))); 150 | 151 | if (header->comm.hlen < 0x78) 152 | goto headerfini; 153 | progress = 0x78; 154 | printf("Map ID: 0x%x(%d)\n", header->mapID, header->mapID); 155 | 156 | if (header->comm.hlen < 0x9a) 157 | goto headerfini; 158 | progress = 0x9a; 159 | printf("unknown_078: %s\n", dump_unknown_bytes(header->unknown_078, sizeof(header->unknown_078))); 160 | printf("TRE7: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 161 | header->tre7_offset, tre->offset + header->tre7_offset, header->tre7_size, header->tre7_rec_size); 162 | printf("unknown_086: %s\n", dump_unknown_bytes(header->unknown_086, sizeof(header->unknown_086))); 163 | printf("TRE8: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 164 | header->tre8_offset, tre->offset + header->tre8_offset, header->tre8_size, header->tre8_rec_size); 165 | printf("unknown_094: %s\n", dump_unknown_bytes(header->unknown_094, sizeof(header->unknown_094))); 166 | 167 | if (header->comm.hlen < 0xbc) 168 | goto headerfini; 169 | progress = 0xbc; 170 | printf("Encrpytion Key: %s\n", dump_unknown_bytes(header->key, sizeof(header->key))); 171 | printf("TRE9: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 172 | header->tre9_offset, tre->offset + header->tre9_offset, header->tre9_size, header->tre9_rec_size); 173 | printf("unknown_0b8: %s\n", dump_unknown_bytes(header->unknown_0b8, sizeof(header->unknown_0b8))); 174 | 175 | if (header->comm.hlen < 0xca) 176 | goto headerfini; 177 | progress = 0xca; 178 | printf("TRE10: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x\n", 179 | header->tre10_offset, tre->offset + header->tre10_offset, header->tre10_size, header->tre10_rec_size); 180 | printf("unknown_0c6: %s\n", dump_unknown_bytes(header->unknown_0c6, sizeof(header->unknown_0c6))); 181 | 182 | if (header->comm.hlen < 0xce) 183 | goto headerfini; 184 | progress = 0xce; 185 | printf("unknown_0ca: %s\n", dump_unknown_bytes(header->unknown_0ca, sizeof(header->unknown_0ca))); 186 | 187 | if (header->comm.hlen < 0xcf) 188 | goto headerfini; 189 | progress = 0xcf; 190 | printf("unknown_0ce: 0x%x\n", header->unknown_0ce); 191 | 192 | headerfini: 193 | if (header->comm.hlen > progress) 194 | printf("from 0x%x to 0x%x (0x%x bytes): %s\n", 195 | progress, header->comm.hlen - 1, 196 | header->comm.hlen - progress, 197 | dump_unknown_bytes((uint8_t *)header + progress, header->comm.hlen - progress)); 198 | 199 | printf("=== MAP LEVELS ===\n"); 200 | if (header->comm.locked) { 201 | printf("locked: %s\n", dump_unknown_bytes(tre->base + header->tre1_offset, header->tre1_size)); 202 | maplevels = (struct garmin_tre_map_level *)malloc(header->tre1_size); 203 | unlockml((unsigned char *)maplevels, 204 | (unsigned char *)tre->base + header->tre1_offset, 205 | header->tre1_size, 206 | *(unsigned int *)(header->key+16)); 207 | //TODO some simple verification, maybe? 208 | } else { 209 | maplevels = (struct garmin_tre_map_level *)malloc(header->tre1_size); 210 | memcpy(maplevels, tre->base + header->tre1_offset, header->tre1_size); 211 | } 212 | dump_maplevels(maplevels, header->tre1_size / sizeof(struct garmin_tre_map_level)); 213 | 214 | printf("=== SUBDIVISIONS ===\n"); 215 | dump_subdiv(tre->base + header->tre2_offset, header->tre1_size / 4, 216 | maplevels, tre->map ? tre->map->rgn : NULL); 217 | 218 | //TODO copyright 219 | 220 | if (header->tre4_size) { 221 | printf("=== POLYLINE OVERVIEWS ===\n"); 222 | dump_poverview(tre->base + header->tre4_offset, 223 | header->tre4_size / header->tre4_rec_size, 224 | header->tre4_rec_size); 225 | } 226 | if (header->tre5_size) { 227 | printf("=== POLYGON OVERVIEWS ===\n"); 228 | dump_poverview(tre->base + header->tre5_offset, 229 | header->tre5_size / header->tre5_rec_size, 230 | header->tre5_rec_size); 231 | } 232 | if (header->tre6_size) { 233 | printf("=== POINT OVERVIEWS ===\n"); 234 | dump_poverview(tre->base + header->tre6_offset, 235 | header->tre6_size / header->tre6_rec_size, 236 | header->tre6_rec_size); 237 | } 238 | 239 | if (header->comm.hlen >= 0x86 && header->tre7_size) { 240 | printf("=== TRE7 ===\n"); 241 | dump_tre7(tre->base + header->tre7_offset, header->tre7_size, 242 | header->tre7_rec_size, maplevels, tre->map->rgn); 243 | } 244 | 245 | free(maplevels); 246 | } 247 | -------------------------------------------------------------------------------- /garmin_struct.h: -------------------------------------------------------------------------------- 1 | #ifndef GARMIN_STRUCT_H 2 | #define GARMIN_STRUCT_H 3 | 4 | #ifdef GT_POSIX 5 | # define PACK_STRUCT __attribute__((packed)) 6 | #else 7 | # define PACK_STRUCT 8 | # pragma pack(push, 1) 9 | #endif 10 | 11 | struct garmin_img { 12 | uint8_t xor_byte; 13 | uint8_t unknown_001[9]; 14 | uint8_t umonth; 15 | uint8_t uyear; 16 | uint8_t unknown_00c[3]; 17 | uint8_t checksum; 18 | char signature[7]; 19 | uint8_t unknown_017; 20 | uint16_t sectors; 21 | uint16_t heads; 22 | uint16_t cylinders; 23 | uint16_t unknown_01e; 24 | uint8_t unknown_020[25]; 25 | uint16_t cyear; 26 | uint8_t cmonth; 27 | uint8_t cdate; 28 | uint8_t chour; 29 | uint8_t cminute; 30 | uint8_t csecond; 31 | uint8_t fat_offset; // in blocks of 512bytes from the start 32 | char identifier[7]; 33 | uint8_t unknown_048; 34 | char desc1[20]; 35 | uint16_t heads1; 36 | uint16_t sectors1; 37 | uint8_t blockexp1; 38 | uint8_t blockexp2; 39 | uint16_t unknown_063; 40 | char desc2[30]; 41 | uint8_t unknown_083; 42 | uint8_t unknown_084[904]; 43 | uint32_t data_offset; 44 | uint8_t unknown_410[16]; 45 | uint16_t blocks[240]; 46 | } PACK_STRUCT ; 47 | 48 | struct garmin_fat { 49 | uint8_t flag; // 0x00 50 | char name[8]; // 0x01 51 | char type[3]; // 0x09 52 | uint32_t size; // 0x0c 53 | uint16_t part; // 0x10 54 | uint8_t unknown_012[14]; // 0x12 55 | uint16_t blocks[240]; // 0x20 56 | } PACK_STRUCT ; 57 | 58 | struct garmin_subfile { 59 | uint16_t hlen; // 0x00 60 | char type[10]; // 0x02 61 | uint8_t unknown_00c; // 0x0c 62 | uint8_t locked; // 0x0d 63 | uint16_t year; // 0x0e 64 | uint8_t month; // 0x10 65 | uint8_t date; // 0x11 66 | uint8_t hour; // 0x12 67 | uint8_t minute; // 0x13 68 | uint8_t second; // 0x14 69 | } PACK_STRUCT ; 70 | 71 | struct garmin_tre { 72 | struct garmin_subfile comm; 73 | uint8_t northbound[3]; ///< 0x00000015 .. 0x00000017 74 | uint8_t eastbound[3]; ///< 0x00000018 .. 0x0000001A 75 | uint8_t southbound[3]; ///< 0x0000001B .. 0x0000001D 76 | uint8_t westbound[3]; ///< 0x0000001E .. 0x00000020 77 | uint32_t tre1_offset; ///< 0x00000021 .. 0x00000024 78 | uint32_t tre1_size; ///< 0x00000025 .. 0x00000028 79 | uint32_t tre2_offset; ///< 0x00000029 .. 0x0000002C 80 | uint32_t tre2_size; ///< 0x0000002D .. 0x00000030 81 | uint32_t tre3_offset; ///< 0x00000031 .. 0x00000034 82 | uint32_t tre3_size; ///< 0x00000035 .. 0x00000038 83 | uint16_t tre3_rec_size; ///< 0x00000039 .. 0x0000003A 84 | uint8_t unknown_03b[4]; 85 | uint8_t POI_flags; ///< 0x0000003F 86 | uint8_t drawprio; // map draw priority 87 | uint8_t unknown_041[9]; 88 | uint32_t tre4_offset; ///< 0x0000004A .. 0x0000004D 89 | uint32_t tre4_size; ///< 0x0000004E .. 0x00000051 90 | uint16_t tre4_rec_size; ///< 0x00000052 .. 0x00000053 91 | uint8_t unknown_054[4]; 92 | uint32_t tre5_offset; ///< 0x00000058 .. 0x0000005B 93 | uint32_t tre5_size; ///< 0x0000005C .. 0x0000005F 94 | uint16_t tre5_rec_size; ///< 0x00000060 .. 0x00000061 95 | uint8_t unknown_062[4]; 96 | uint32_t tre6_offset; ///< 0x00000066 .. 0x00000069 97 | uint32_t tre6_size; ///< 0x0000006A .. 0x0000006D 98 | uint16_t tre6_rec_size; ///< 0x0000006E .. 0x0000006F 99 | uint8_t unknown_070[4]; ///< 0x00000070 .. 0x00000073 break 100 | uint32_t mapID; ///< 0x00000074 .. 0x00000077 break 101 | uint8_t unknown_078[4]; 102 | uint32_t tre7_offset; ///< 0x0000007C .. 0x0000007F 103 | uint32_t tre7_size; ///< 0x00000080 .. 0x00000083 104 | uint16_t tre7_rec_size; ///< 0x00000084 .. 0x00000085 105 | uint8_t unknown_086[4]; 106 | uint32_t tre8_offset; ///< 0x0000008A .. 0x0000008D 107 | uint32_t tre8_size; ///< 0x0000008E .. 0x00000091 108 | uint16_t tre8_rec_size; 109 | uint8_t unknown_094[6]; 110 | uint8_t key[20]; ///< 0x0000009A .. 0x000000AD 111 | uint32_t tre9_offset; ///< 0x000000AE .. 0x000000B1 112 | uint32_t tre9_size; ///< 0x000000B2 .. 0x000000B5 113 | uint16_t tre9_rec_size; ///< 0x000000B6 .. 0x000000B7 114 | uint8_t unknown_0b8[4]; ///< 0x000000B8 .. 0x000000BB break 115 | uint32_t tre10_offset; ///< 0x000000BC .. 0x000000BF 116 | uint32_t tre10_size; ///< 0x000000C0 .. 0x000000C3 117 | uint16_t tre10_rec_size; ///< 0x000000C4 .. 0x000000C5 break 118 | uint8_t unknown_0c6[4]; ///< 0x000000C6 .. 0x000000C9 break 119 | uint8_t unknown_0ca[4]; ///< 0x000000CA .. 0x000000CD break 120 | uint8_t unknown_0ce; ///< 0x000000CE .. 0x000000CE break 121 | } PACK_STRUCT ; 122 | 123 | struct garmin_tre_map_level { 124 | uint8_t level :4; 125 | uint8_t bit456 :3; 126 | uint8_t inherited :1; 127 | uint8_t bits; 128 | uint16_t nsubdiv; 129 | } PACK_STRUCT ; 130 | 131 | struct garmin_tre_subdiv { 132 | uint8_t rgn_offset[3]; 133 | uint8_t elements; 134 | uint8_t center_lng[3]; 135 | uint8_t center_lat[3]; 136 | uint16_t width :15; 137 | uint16_t terminate :1; 138 | uint16_t height :15; 139 | uint16_t unknownbit :1; 140 | uint16_t next; 141 | } PACK_STRUCT ; 142 | 143 | struct garmin_rgn { 144 | struct garmin_subfile comm; // 0x000 145 | uint32_t rgn1_offset; // 0x015 146 | uint32_t rgn1_length; // 0x019 147 | /* break */ 148 | uint32_t rgn2_offset; // 0x01d 149 | uint32_t rgn2_length; // 0x021 150 | uint8_t unknown_025[20]; // 0x025 151 | uint32_t rgn3_offset; // 0x039 152 | uint32_t rgn3_length; // 0x03d 153 | uint8_t unknown_041[20]; // 0x041 154 | uint32_t rgn4_offset; // 0x055 155 | uint32_t rgn4_length; // 0x059 156 | uint8_t unknown_05d[20]; // 0x05d 157 | uint32_t rgn5_offset; // 0x071 158 | uint32_t rgn5_length; // 0x075 159 | uint32_t unknown_079; // 0x079 160 | } PACK_STRUCT ; 161 | 162 | struct garmin_lbl { 163 | struct garmin_subfile comm; 164 | uint32_t lbl1_offset; ///< 0x00000015 .. 0x00000018 165 | uint32_t lbl1_length; ///< 0x00000019 .. 0x0000001C 166 | uint8_t addr_shift; ///< 0x0000001D 167 | uint8_t coding; ///< 0x0000001E 168 | uint32_t lbl2_offset; ///< 0x0000001F .. 0x00000022 169 | uint32_t lbl2_length; ///< 0x00000023 .. 0x00000026 170 | uint16_t lbl2_recsize; ///< 0x00000027 .. 0x00000028 171 | uint32_t lbl2_u; 172 | uint32_t lbl3_offset; ///< 0x0000002D .. 0x00000030 173 | uint32_t lbl3_length; ///< 0x00000031 .. 0x00000034 174 | uint16_t lbl3_recsize; ///< 0x00000035 .. 0x00000036 175 | uint32_t lbl3_u; 176 | uint32_t lbl4_offset; ///< 0x0000003B .. 0x0000003E 177 | uint32_t lbl4_length; ///< 0x0000003F .. 0x00000042 178 | uint16_t lbl4_recsize; ///< 0x00000043 .. 0x00000044 179 | uint32_t lbl4_u; 180 | uint32_t lbl5_offset; ///< 0x00000049 .. 0x0000004C 181 | uint32_t lbl5_length; ///< 0x0000004D .. 0x00000050 182 | uint16_t lbl5_recsize; ///< 0x00000051 .. 0x00000052 183 | uint32_t lbl5_u; 184 | uint32_t lbl6_offset; ///< 0x00000057 .. 0x0000005A 185 | uint32_t lbl6_length; ///< 0x0000005B .. 0x0000005E 186 | uint8_t lbl6_addr_shift; ///< 0x0000005F 187 | uint8_t lbl6_glob_mask; ///< 0x00000060 188 | uint8_t lbl6_u[3]; 189 | uint32_t lbl7_offset; ///< 0x00000064 .. 0x00000067 190 | uint32_t lbl7_length; ///< 0x00000068 .. 0x0000006B 191 | uint16_t lbl7_recsize; ///< 0x0000006C .. 0x0000006D 192 | uint32_t lbl7_u; 193 | uint32_t lbl8_offset; ///< 0x00000072 .. 0x00000075 194 | uint32_t lbl8_length; ///< 0x00000076 .. 0x00000079 195 | uint16_t lbl8_recsize; ///< 0x0000007A .. 0x0000007B 196 | uint32_t lbl8_u; 197 | uint32_t lbl9_offset; ///< 0x00000080 .. 0x00000083 198 | uint32_t lbl9_length; ///< 0x00000084 .. 0x00000087 199 | uint16_t lbl9_recsize; ///< 0x00000088 .. 0x00000089 200 | uint32_t lbl9_u; 201 | uint32_t lbl10_offset; ///< 0x0000008E .. 0x00000091 202 | uint32_t lbl10_length; ///< 0x00000092 .. 0x00000095 203 | uint16_t lbl10_recsize; ///< 0x00000096 .. 0x00000097 204 | uint32_t lbl10_u; 205 | uint32_t lbl11_offset; ///< 0x0000009C .. 0x0000009F 206 | uint32_t lbl11_length; ///< 0x000000A0 .. 0x000000A3 207 | uint16_t lbl11_recsize; ///< 0x000000A4 .. 0x000000A5 208 | uint32_t lbl11_u; ///< 0x000000A6 .. 0x000000A9 break 209 | uint16_t codepage; // 0x0aa 210 | uint16_t codepage2; // 0x0ac 211 | uint16_t codepage3; // 0x0ae 212 | uint32_t lbl12_offset; // 0x0b0 213 | uint32_t lbl12_length; // 0x0b4 214 | uint32_t lbl13_offset; // 0x0b8 215 | uint32_t lbl13_length; // 0x0bc 216 | uint16_t lbl13_recsize; // 0x0c0 217 | uint16_t lbl13_u; // 0x0c2 218 | uint32_t lbl14_offset; // 0x0c4 219 | uint32_t lbl14_length; // 0x0c8 220 | uint16_t lbl14_recsize; // 0x0cc 221 | uint16_t lbl14_u; // 0x0ce 222 | uint32_t lbl15_offset; // 0x0d0 223 | uint32_t lbl15_length; // 0x0d4 224 | uint16_t lbl15_recsize; // 0x0d8 225 | uint32_t lbl15_u; // 0x0da 226 | uint32_t lbl16_offset; // 0x0de 227 | uint32_t lbl16_length; // 0x0e2 228 | uint16_t lbl16_recsize; // 0x0e6 229 | uint32_t lbl16_u; // 0x0e8 230 | uint32_t lbl17_offset; // 0x0ec 231 | uint32_t lbl17_length; // 0x0f0 232 | uint16_t lbl17_recsize; // 0x0f4 233 | uint32_t lbl17_u; // 0x0f6 234 | uint32_t lbl18_offset; // 0x0fa 235 | uint32_t lbl18_length; // 0x0fe 236 | uint16_t lbl18_recsize; // 0x102 237 | uint32_t lbl18_u; // 0x104 238 | uint32_t lbl19_offset; // 0x108 239 | uint32_t lbl19_length; // 0x10c 240 | uint16_t lbl19_recsize; // 0x110 241 | uint32_t lbl19_u; // 0x112 242 | uint32_t lbl20_offset; // 0x116 243 | uint32_t lbl20_length; // 0x11a 244 | uint16_t lbl20_recsize; // 0x11e 245 | uint32_t lbl20_u; // 0x120 246 | uint32_t lbl21_offset; // 0x124 247 | uint32_t lbl21_length; // 0x128 248 | uint16_t lbl21_recsize; // 0x12c 249 | uint32_t lbl21_u; // 0x12e 250 | uint32_t lbl22_offset; // 0x132 251 | uint32_t lbl22_length; // 0x136 252 | uint16_t lbl22_recsize; // 0x13a 253 | uint32_t lbl22_u; // 0x13c 254 | uint32_t lbl23_offset; // 0x140 255 | uint32_t lbl23_length; // 0x144 256 | uint16_t lbl23_recsize; // 0x148 257 | uint32_t lbl23_u; // 0x14a 258 | uint32_t lbl24_offset; // 0x14e 259 | uint32_t lbl24_length; // 0x152 260 | uint16_t lbl24_recsize; // 0x156 261 | uint16_t lbl24_u; // 0x158 262 | uint32_t lbl25_offset; // 0x15a 263 | uint32_t lbl25_length; // 0x15e 264 | uint16_t lbl25_recsize; // 0x162 265 | uint32_t lbl25_u; // 0x164 266 | uint32_t lbl26_offset; // 0x168 267 | uint32_t lbl26_length; // 0x16c 268 | uint16_t lbl26_recsize; // 0x170 269 | uint32_t lbl26_u; // 0x172 270 | uint32_t lbl27_offset; // 0x176 271 | uint32_t lbl27_length; // 0x17a 272 | uint16_t lbl27_recsize; // 0x17e 273 | uint32_t lbl27_u; // 0x180 274 | uint32_t lbl28_offset; // 0x184 275 | uint32_t lbl28_length; // 0x188 276 | uint16_t lbl28_recsize; // 0x18c 277 | uint32_t lbl28_u; // 0x18e 278 | uint32_t lbl29_offset; // 0x192 279 | uint32_t lbl29_length; // 0x196 280 | uint32_t lbl30_offset; // 0x19a 281 | uint32_t lbl30_length; // 0x19e 282 | uint16_t lbl30_recsize; // 0x1a2 283 | uint16_t lbl30_u; // 0x1a4 284 | uint32_t lbl31_offset; // 0x1a6 285 | uint32_t lbl31_length; // 0x1aa 286 | uint16_t lbl31_recsize; // 0x1ae 287 | uint16_t lbl31_u; // 0x1b0 288 | uint32_t lbl32_offset; // 0x1b2 289 | uint32_t lbl32_length; // 0x1b6 290 | uint16_t lbl32_recsize; // 0x1ba 291 | uint16_t lbl32_u; // 0x1bc 292 | uint32_t lbl33_offset; // 0x1be 293 | uint32_t lbl33_length; // 0x1c2 294 | uint16_t lbl33_recsize; // 0x1c6 295 | uint16_t lbl33_u; // 0x1c8 296 | uint32_t lbl34_offset; // 0x1ca 297 | uint32_t lbl34_length; // 0x1ce 298 | uint16_t lbl34_recsize; // 0x1d2 299 | uint32_t lbl34_u; // 0x1d4 300 | uint32_t lbl35_offset; // 0x1d8 301 | uint32_t lbl35_length; // 0x1dc 302 | uint16_t lbl35_recsize; // 0x1e0 303 | uint32_t lbl35_u; // 0x1e2 304 | uint32_t lbl36_offset; // 0x1e6 305 | uint32_t lbl36_length; // 0x1ea 306 | uint16_t lbl36_recsize; // 0x1ee 307 | uint16_t lbl36_u; // 0x1f0 308 | } PACK_STRUCT ; 309 | 310 | struct garmin_net { 311 | struct garmin_subfile comm; 312 | // NET1 Road definitions 313 | uint32_t net1_offset; ///< 0x00000015 .. 0x00000018 314 | uint32_t net1_length; ///< 0x00000019 .. 0x0000001C 315 | uint8_t net1_shift; ///< 0x0000001D 316 | // Segmented roads 317 | uint32_t net2_offset; ///< 0x0000001E .. 0x00000021 318 | uint32_t net2_length; ///< 0x00000022 .. 0x00000025 319 | uint8_t net2_shift; ///< 0x00000026 320 | // Sorted Roads 321 | uint32_t net3_offset; ///< 0x00000027 .. 0x0000002A 322 | uint32_t net3_length; ///< 0x0000002B .. 0x0000002E 323 | uint16_t net3_recsize; // 0x2f 324 | uint32_t unknown_031; // 0x31 325 | uint16_t unknown_035; // 0x35 326 | /* break */ 327 | uint32_t unknown_037; // 0x37 328 | /* break */ 329 | uint32_t unknown_03b; // 0x3b 330 | uint32_t unknown_03f; // 0x3f 331 | uint32_t net4_offset; // 0x43 332 | uint32_t net4_length; // 0x47 333 | uint8_t net4_u; // 0x4b 334 | uint32_t net5_offset; // 0x4c 335 | uint32_t net5_length; // 0x50 336 | uint16_t net5_recsize; // 0x54 337 | uint32_t net6_offset; // 0x56 338 | uint32_t net6_length; // 0x5a 339 | uint16_t net6_recsize; // 0x5e 340 | uint32_t unknown_060; // 0x60 341 | } PACK_STRUCT ; 342 | 343 | struct garmin_nod 344 | { 345 | struct garmin_subfile comm; 346 | uint32_t nod1_offset; // 0x15 347 | uint32_t nod1_length; // 0x19 348 | uint8_t nod_bits[4]; // 0x1d 349 | uint8_t align; // 0x21 350 | uint8_t unknown_022; // 0x22 351 | uint16_t roadptrsize; // 0x23 352 | uint32_t nod2_offset; // 0x25 Road data 353 | uint32_t nod2_length; // 0x29 354 | uint32_t unknown_02d; // 0x2d 355 | uint32_t nod3_offset; // 0x31 Boundary nodes 356 | uint32_t nod3_length; // 0x35 357 | uint16_t nod3_recsize; // 0x39 {9, a} 358 | uint32_t unknown_03c; // 0x3c {0, 2} 359 | /* break */ 360 | uint32_t nod4_offset; // 0x3f 361 | uint32_t nod4_length; // 0x43 362 | uint32_t unknown_047; // 0x47 363 | uint32_t unknown_04b; // 0x4b 364 | uint32_t unknown_04f; // 0x4f 365 | uint32_t unknown_053; // 0x53 366 | uint32_t unknown_057; // 0x57 367 | uint8_t unknown_05b[12]; // 0x5b-0x66 368 | uint32_t nod5_offset; // 0x67 369 | uint32_t nod5_length; // 0x6b 370 | uint16_t nod5_recsize; // 0x6f 371 | uint32_t nod6_offset; // 0x71 372 | uint32_t nod6_length; // 0x75 373 | uint16_t nod6_recsize; // 0x79 374 | uint32_t unknown_07b; // 0x7b 375 | } PACK_STRUCT ; 376 | 377 | struct garmin_dem 378 | { 379 | struct garmin_subfile comm; 380 | uint32_t flags; // 0x015 381 | uint16_t zoom_levels; // 0x019 382 | uint32_t reserved0; // 0x01B 383 | uint16_t record_size; // 0x01F 384 | uint32_t points_to_block3; // 0x021 385 | // This may not be present on every map: 386 | uint32_t reserved2; // 0x025 387 | } PACK_STRUCT ; 388 | 389 | struct garmin_gmp { 390 | struct garmin_subfile comm; 391 | uint32_t unknown_015; 392 | uint32_t tre_offset; 393 | uint32_t rgn_offset; 394 | uint32_t lbl_offset; 395 | uint32_t net_offset; 396 | uint32_t nod_offset; 397 | uint32_t dem_offset; 398 | uint32_t mar_offset; 399 | } PACK_STRUCT ; 400 | 401 | /* http://ati.land.cz/ */ 402 | struct garmin_typ 403 | { 404 | struct garmin_subfile comm; 405 | uint16_t codepage; /* offset = 0x15 */ 406 | uint32_t point_datoff; 407 | uint32_t point_datsize; 408 | uint32_t line_datoff; 409 | uint32_t line_datsize; 410 | uint32_t polygon_datoff; 411 | uint32_t polygon_datsize; 412 | uint16_t fid; 413 | uint16_t pid; 414 | uint32_t point_arroff; 415 | uint16_t point_arrmod; 416 | uint32_t point_arrsize; 417 | uint32_t line_arroff; 418 | uint16_t line_arrmod; 419 | uint32_t line_arrsize; 420 | uint32_t polygon_arroff; 421 | uint16_t polygon_arrmod; 422 | uint32_t polygon_arrsize; 423 | uint32_t draworder_arroff; 424 | uint16_t draworder_arrmod; 425 | uint32_t draworder_arrsize; 426 | /* possible size limit here*/ 427 | uint32_t nt1_arroff; /* offset = 0x5B */ 428 | uint16_t nt1_arrmod; 429 | uint32_t nt1_arrsize; 430 | uint8_t nt1_flag; 431 | uint32_t nt1_datoff; 432 | uint32_t nt1_datsize; 433 | /* possible size limit here*/ 434 | uint32_t blok0_x; /* offset = 0x6E */ 435 | uint32_t blok0_off; 436 | uint32_t blok0_size; 437 | uint32_t blok0_y; 438 | uint32_t blok1_x; 439 | uint32_t blok1_off; 440 | uint32_t blok1_size; 441 | uint32_t blok1_y; 442 | uint32_t blok2_x; 443 | uint32_t blok2_off; 444 | uint32_t blok2_size; 445 | uint16_t unknown_09a; 446 | /* offset = 0x9C */ 447 | } PACK_STRUCT ; 448 | 449 | #ifdef GT_POSIX 450 | #else 451 | # pragma pack(pop) 452 | #endif 453 | 454 | #endif 455 | -------------------------------------------------------------------------------- /gimglib.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | 3 | //#define vlog(...) if (option_verbose) fprintf(stderr, "LOG: " __VA_ARGS__) 4 | //#define warn(...) fprintf(stderr, "WARNING: " __VA_ARGS__) 5 | 6 | #ifdef GT_POSIX 7 | static int map_img (const char *path, int writable, uint8_t **pbase, unsigned int *psize) 8 | { 9 | int img_fd; 10 | struct stat sb; 11 | uint8_t *base; 12 | 13 | assert((writable & 1) == writable); 14 | 15 | img_fd = open(path, writable ? O_RDWR: O_RDONLY); 16 | if (img_fd == -1) { 17 | fprintf(stderr, "cannot open file %s. err=%d\n", path, errno); 18 | return 1; 19 | } 20 | 21 | if (fstat(img_fd, &sb) == -1) { 22 | fprintf(stderr, "cannot get size of file %s. err=%d\n", path, errno); 23 | return 1; 24 | } 25 | if (sb.st_size == 0) { 26 | fprintf(stderr, "file %s is an empty file\n", path); 27 | return 1; 28 | } 29 | //vlog("file size = %u\n", img_size); 30 | 31 | base = mmap(NULL, sb.st_size, PROT_READ | (PROT_WRITE * writable), 32 | MAP_SHARED, img_fd, 0); 33 | if (base == MAP_FAILED) { 34 | fprintf(stderr, "cannot map file %s into memory. err=%d\n", path, errno); 35 | return 1; 36 | } 37 | 38 | close(img_fd); 39 | 40 | *pbase = base; 41 | *psize = sb.st_size; 42 | return 0; 43 | } 44 | 45 | static void unmap_img (uint8_t *base, unsigned int size) 46 | { 47 | munmap(base, size); 48 | } 49 | #else 50 | static int map_img (const char *path, int writable, uint8_t **pbase, unsigned int *psize) 51 | { 52 | HANDLE hfile, hmapping; 53 | uint8_t *base; 54 | unsigned int size; 55 | 56 | assert((writable & 1) == writable); 57 | 58 | hfile = CreateFile(path, GENERIC_READ | (GENERIC_WRITE * writable), 59 | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 60 | if (hfile == INVALID_HANDLE_VALUE) { 61 | fprintf(stderr, "cannot open file %s. CreateFile err=%d\n", path, GetLastError()); 62 | return 1; 63 | } 64 | 65 | size = GetFileSize(hfile, NULL); 66 | if (size == INVALID_FILE_SIZE) { 67 | fprintf(stderr, "cannot get size of file %s. GetFileSize err=%d\n", path, GetLastError()); 68 | return 1; 69 | } 70 | if (size == 0) { 71 | fprintf(stderr, "file %s is an empty file.\n", path); 72 | return 1; 73 | } 74 | //vlog("file size = %u\n", size); 75 | 76 | hmapping = CreateFileMapping(hfile, NULL, 77 | writable ? PAGE_READWRITE : PAGE_READONLY, 78 | 0, 0, NULL); 79 | if (hmapping == NULL) { 80 | fprintf(stderr, "cannot map (1) file %s into memory. CreateFileMapping err=%d\n", path, GetLastError()); 81 | return 1; 82 | } 83 | base = MapViewOfFile(hmapping, writable ? FILE_MAP_WRITE : FILE_MAP_READ, 0, 0, 0); 84 | if (base == NULL) { 85 | fprintf(stderr, "cannot map (2) file %s into memory. MapViewOfFile err=%d\n", path, GetLastError()); 86 | return 1; 87 | } 88 | 89 | CloseHandle(hmapping); 90 | CloseHandle(hfile); 91 | *pbase = base; 92 | *psize = size; 93 | return 0; 94 | } 95 | 96 | static void unmap_img (uint8_t *base, unsigned int size) 97 | { 98 | UnmapViewOfFile(base); 99 | } 100 | #endif 101 | 102 | static int parse_img (struct gimg_struct *img) 103 | { 104 | struct garmin_img *img_header = (struct garmin_img *)(img->base); 105 | struct subfile_struct *subfile, *subfiles, *subfiles_tail, *orphans, *orphans_tail; 106 | struct submap_struct *submap, *submaps, *submaps_tail; 107 | unsigned int block_size, fatstart, fatend, i, prev_block; 108 | 109 | if (img_header->xor_byte != 0) { 110 | fprintf(stderr, "XOR is not 0. Fix it first.\n"); 111 | return 1; 112 | } 113 | if (strcmp(img_header->signature, "DSKIMG") != 0) { 114 | fprintf(stderr, "not DSKIMG\n"); 115 | return 1; 116 | } 117 | 118 | block_size = 1 << (img_header->blockexp1 + img_header->blockexp2); 119 | //vlog("block size = 1 << (%d + %d) = %d\n", img_header->blockexp1, img_header->blockexp2, block_size); 120 | 121 | subfiles = subfiles_tail = NULL; 122 | fatstart = img_header->fat_offset == 0 ? 3 : img_header->fat_offset; 123 | if (img_header->data_offset == 0) { // use root dir. assume it's the first file 124 | struct garmin_fat *fat = (struct garmin_fat *)(img->base + fatstart * 512); 125 | if (fat->flag != 1 || memcmp(fat->name, " ", 8) != 0 || memcmp(fat->type, " ", 3) != 0) { 126 | fprintf(stderr, "imgheader.data_offset = 0 but the first file is not root dir!\n"); 127 | return 1; 128 | } 129 | fatstart ++; 130 | assert(fat->size % 512 == 0 && fat->size > (unsigned)fatstart * 512); 131 | fatend = fat->size / 512; 132 | //vlog("parsing fat use rootdir, fatstart=%d, fatend=%d\n", fatstart, fatend); 133 | } else { 134 | fatend = img_header->data_offset / 512; 135 | //vlog("parsing fat use data_offset, fatstart=%d, fatend=%d\n", fatstart, fatend); 136 | } 137 | 138 | for (i = fatstart; i < fatend; i ++) { 139 | struct garmin_fat *fat = (struct garmin_fat *)(img->base + i * 512); 140 | int j; 141 | 142 | if (fat->flag != 1) { 143 | //vlog("fat%d.flag = %d, skipped\n", i, fat->flag); 144 | continue; 145 | } 146 | if (memcmp(fat->name, " ", 8) == 0) { 147 | //vlog("fat%d is rootdir (size=%d), skipped\n", i, fat->size); 148 | continue; 149 | } 150 | 151 | /* scan fat table */ 152 | if (fat->part == 0) { 153 | if (memcmp(fat->type, "GMP", 3) == 0) { 154 | struct garmin_gmp *gmp = (struct garmin_gmp *)(img->base + fat->blocks[0] * block_size); 155 | int k; 156 | 157 | //vlog("fat%d: gmp 0x%x+0x%x\n", i, fat->blocks[0] * block_size, fat->size); 158 | for (k = 0; k < NUMBER_SUBFILES && 0x1d + k*4 <= gmp->comm.hlen; k ++) { 159 | uint32_t abs_offset, rel_offset = *(&gmp->tre_offset + k); 160 | if (rel_offset == 0) { 161 | //vlog("GMP has no %s\n", get_subtype_name(k)); 162 | continue; 163 | } 164 | abs_offset = rel_offset + fat->blocks[0] * block_size; 165 | 166 | subfile = (struct subfile_struct *)malloc(sizeof(struct subfile_struct)); 167 | memset(subfile, 0, sizeof(struct subfile_struct)); 168 | subfile->header = (struct garmin_subfile *)(img->base + abs_offset); 169 | subfile->base = (unsigned char *)gmp; 170 | subfile->offset = fat->blocks[0] * block_size; 171 | subfile->size = fat->size; 172 | memcpy(subfile->name, fat->name, 8); 173 | string_trim(subfile->name, -1); 174 | strncpy(subfile->type, get_subtype_name(k), 3); 175 | sprintf(subfile->fullname, "%s.%s", subfile->name, subfile->type); 176 | subfile->typeid = k; 177 | //vlog("%s.GMP.%s: reloff=0x%x, absoff=0x%x\n", 178 | // subfile->name, subfile->type, 179 | // rel_offset, abs_offset); 180 | 181 | subfile->next = NULL; 182 | if (subfiles_tail == NULL) 183 | subfiles_tail = subfiles = subfile; 184 | else 185 | subfiles_tail = subfiles_tail->next = subfile; 186 | } 187 | 188 | /* add gmp it self */ 189 | subfile = (struct subfile_struct *)malloc(sizeof(struct subfile_struct)); 190 | memset(subfile, 0, sizeof(struct subfile_struct)); 191 | subfile->header = (struct garmin_subfile *)(img->base + fat->blocks[0] * block_size); 192 | subfile->base = (unsigned char *)gmp; 193 | subfile->offset = fat->blocks[0] * block_size; 194 | subfile->size = fat->size; 195 | memcpy(subfile->name, fat->name, 8); 196 | string_trim(subfile->name, -1); 197 | memcpy(subfile->type, "GMP", 3); 198 | sprintf(subfile->fullname, "%s.GMP", subfile->name); 199 | subfile->typeid = ST_GMP; 200 | 201 | subfile->next = NULL; 202 | if (subfiles_tail == NULL) 203 | subfiles_tail = subfiles = subfile; 204 | else 205 | subfiles_tail = subfiles_tail->next = subfile; 206 | } else { 207 | subfile = (struct subfile_struct *)malloc(sizeof(struct subfile_struct)); 208 | memset(subfile, 0, sizeof(struct subfile_struct)); 209 | subfile->header = (struct garmin_subfile *)(img->base + fat->blocks[0] * block_size); 210 | subfile->base = img->base + fat->blocks[0] * block_size; 211 | subfile->offset = fat->blocks[0] * block_size; 212 | subfile->size = fat->size; 213 | memcpy(subfile->name, fat->name, 8); 214 | string_trim(subfile->name, -1); 215 | memcpy(subfile->type, fat->type, 3); 216 | string_trim(subfile->type, -1); 217 | sprintf(subfile->fullname, "%s.%s", subfile->name, subfile->type); 218 | subfile->typeid = get_subtype_id(subfile->type); 219 | //vlog("fat%d: %s %s 0x%x+0x%x\n", i, subfile->name, subfile->type, 220 | // fat->blocks[0] * block_size, subfile->size); 221 | 222 | subfile->next = NULL; 223 | if (subfiles_tail == NULL) 224 | subfiles_tail = subfiles = subfile; 225 | else 226 | subfiles_tail = subfiles_tail->next = subfile; 227 | } 228 | 229 | prev_block = fat->blocks[0] - 1; 230 | } 231 | 232 | for (j = 0; j < 240; j ++) { 233 | if (fat->blocks[j] != 0xffff && fat->blocks[j] != ++ prev_block) { 234 | fprintf(stderr, "file block number is not continuous\n"); 235 | return 1; 236 | } 237 | } 238 | } 239 | //vlog("parsing fat finished\n"); 240 | 241 | /* build submaps link list */ 242 | submaps = submaps_tail = NULL; 243 | orphans = orphans_tail = NULL; 244 | for (subfile = subfiles; subfile != NULL; subfile = subfile->next) { 245 | if (subfile->typeid != ST_TRE) 246 | continue; 247 | submap = (struct submap_struct *)malloc(sizeof(struct submap_struct)); 248 | memset(submap, 0, sizeof(struct submap_struct)); 249 | strcpy(submap->name, subfile->name); 250 | submap->next = NULL; 251 | if (submaps_tail == NULL) 252 | submaps_tail = submaps = submap; 253 | else 254 | submaps_tail = submaps_tail->next = submap; 255 | } 256 | for (subfile = subfiles; subfile != NULL; subfile = subfile->next) { 257 | for (submap = submaps; submap != NULL; submap = submap->next) 258 | if (strcmp(subfile->name, submap->name) == 0) 259 | break; 260 | subfile->map = submap; 261 | if (submap == NULL) { // orphan 262 | subfile->orphan_next = NULL; 263 | if (orphans_tail == NULL) 264 | orphans = orphans_tail = subfile; 265 | else 266 | orphans_tail = orphans_tail->orphan_next = subfile; 267 | } else { 268 | switch (subfile->typeid) { 269 | case ST_TRE: case ST_RGN: case ST_LBL: case ST_NET: case ST_NOD: case ST_DEM: case ST_MAR: 270 | if (submap->subfiles[subfile->typeid]) 271 | fprintf(stderr, "Warning: file %s has duplicate %s\n", subfile->name, subfile->type); 272 | submap->subfiles[subfile->typeid] = subfile; 273 | break; 274 | case ST_SRT: 275 | if (submap->srt) 276 | fprintf(stderr, "Warning: file %s has duplicate %s\n", subfile->name, subfile->type); 277 | submap->srt = subfile; 278 | break; 279 | case ST_GMP: 280 | if (submap->gmp) 281 | fprintf(stderr, "Warning: file %s has duplicate %s\n", subfile->name, subfile->type); 282 | submap->gmp = subfile; 283 | break; 284 | default: 285 | fprintf(stderr, "Warning: file %s's type %s not known\n", subfile->name, subfile->type); 286 | } 287 | } 288 | } 289 | 290 | img->subfiles = subfiles; 291 | img->submaps = submaps; 292 | img->orphans = orphans; 293 | return 0; 294 | } 295 | 296 | void dump_comm (struct garmin_subfile *header) 297 | { 298 | char buffer[12]; 299 | 300 | printf("=== COMMON HEADER ===\n"); 301 | printf("hlen %d (0x%x)\n", header->hlen, header->hlen); 302 | memcpy(buffer, header->type, sizeof(header->type)); 303 | buffer[sizeof(header->type)] = '\0'; 304 | printf("type %s\n", buffer); 305 | printf("unknown_00c %d\n", header->unknown_00c); 306 | printf("locked 0x%x\n", header->locked); 307 | printf("year %d\n", header->year); 308 | printf("month %d\n", header->month); 309 | printf("date %d\n", header->date); 310 | printf("hour %d\n", header->hour); 311 | printf("minute %d\n", header->minute); 312 | printf("second %d\n", header->second); 313 | } 314 | 315 | void dump_img (struct gimg_struct *img) 316 | { 317 | struct garmin_img *img_header = (struct garmin_img *)(img->base); 318 | struct subfile_struct *subfile; 319 | struct submap_struct *submap; 320 | char buffer[100]; 321 | 322 | printf("=== IMAGE HEADER ===\n"); 323 | printf("unknown_001 %s\n", dump_unknown_bytes(img_header->unknown_001, sizeof(img_header->unknown_001))); 324 | printf("umonth %d\n", img_header->umonth); 325 | printf("uyear %d\n", img_header->uyear); 326 | printf("unknown_00c %s\n", dump_unknown_bytes(img_header->unknown_00c, sizeof(img_header->unknown_00c))); 327 | printf("checksum %d\n", img_header->checksum); 328 | memcpy(buffer, img_header->signature, sizeof(img_header->signature)); 329 | buffer[sizeof(img_header->signature)] = '\0'; 330 | printf("signature \"%s\"\n", buffer); 331 | printf("unknown_017 %d\n", img_header->unknown_017); 332 | printf("sectors %d\n", img_header->sectors); 333 | printf("heads %d\n", img_header->heads); 334 | printf("cylinders %d\n", img_header->cylinders); 335 | printf("unknown_01e %d\n", img_header->unknown_01e); 336 | printf("unknown_020 %s\n", dump_unknown_bytes(img_header->unknown_020, sizeof(img_header->unknown_020))); 337 | printf("cyear %d\n", img_header->cyear); 338 | printf("cmonth %d\n", img_header->cmonth); 339 | printf("cdate %d\n", img_header->cdate); 340 | printf("chour %d\n", img_header->chour); 341 | printf("cminute %d\n", img_header->cminute); 342 | printf("csecond %d\n", img_header->csecond); 343 | printf("fat_offset %d\n", img_header->fat_offset); 344 | memcpy(buffer, img_header->identifier, sizeof(img_header->identifier)); 345 | buffer[sizeof(img_header->identifier)] = '\0'; 346 | printf("identifier \"%s\"\n", buffer); 347 | printf("unknown_048 %d\n", img_header->unknown_048); 348 | memcpy(buffer, img_header->desc1, sizeof(img_header->desc1)); 349 | memcpy(buffer + sizeof(img_header->desc1), img_header->desc2, sizeof(img_header->desc2)); 350 | buffer[sizeof(img_header->desc1) + sizeof(img_header->desc2)] = '\0'; 351 | string_trim(buffer, -1); 352 | printf("desc \"%s\"\n", buffer); 353 | printf("heads1 %d\n", img_header->heads1); 354 | printf("sectors1 %d\n", img_header->sectors1); 355 | printf("blockexp1 %d\n", img_header->blockexp1); 356 | printf("blockexp2 %d\n", img_header->blockexp2); 357 | printf("unknown_063 %d\n", img_header->unknown_063); 358 | printf("unknown_083 %d\n", img_header->unknown_083); 359 | printf("unknown_084 %s\n", dump_unknown_bytes(img_header->unknown_084, sizeof(img_header->unknown_084))); 360 | printf("data_offset %d\n", img_header->data_offset); 361 | printf("unknown_410 %s\n", dump_unknown_bytes(img_header->unknown_410, sizeof(img_header->unknown_410))); 362 | 363 | printf("=== SUBMAPS ===\n"); 364 | for (submap = img->submaps; submap != NULL; submap = submap->next) { 365 | if (submap->gmp) { 366 | int k; 367 | printf("%s: NT off=0x%x size=0x%x\n", 368 | submap->name, 369 | submap->gmp->offset, 370 | submap->gmp->size); 371 | for (k = 0; k < NUMBER_SUBFILES; k ++) { 372 | if (submap->subfiles[k]) 373 | printf(" %s abs=0x%x rel=0x%x\n", 374 | get_subtype_name(k), 375 | (int)((uint8_t *)submap->subfiles[k]->header - img->base), 376 | (int)((uint8_t *)submap->subfiles[k]->header - submap->subfiles[k]->base)); 377 | } 378 | } else { // OF 379 | int k; 380 | printf("%s: OF\n", submap->name); 381 | for (k = 0; k < NUMBER_SUBFILES; k ++) { 382 | if (submap->subfiles[k]) 383 | printf(" %s off=0x%x size=0x%x\n", 384 | get_subtype_name(k), 385 | submap->subfiles[k]->offset, 386 | submap->subfiles[k]->size); 387 | } 388 | } 389 | if (submap->srt) 390 | printf(" SRT off=0x%x size=0x%x\n", submap->srt->offset, submap->srt->size); 391 | } 392 | printf("=== OTHER SUBFILES ===\n"); 393 | for (subfile = img->orphans; subfile != NULL; subfile = subfile->orphan_next) { 394 | printf("%s: off=0x%x, size=0x%x\n", 395 | subfile->fullname, subfile->offset, subfile->size); 396 | } 397 | } 398 | 399 | void dump_subfile (struct gimg_struct *img, const char *subfile_name) 400 | { 401 | struct subfile_struct *subfile; 402 | 403 | /* no extension defailts to TRE */ 404 | if (strchr(subfile_name, '.') == NULL) { 405 | struct submap_struct *submap = get_submap(img, subfile_name); 406 | if (submap) { 407 | dump_tre(submap->tre); 408 | return; 409 | } 410 | } 411 | 412 | subfile = get_subfile(img, subfile_name); 413 | if (subfile == NULL) { 414 | printf("subfile %s not found\n", subfile_name); 415 | return; 416 | } 417 | 418 | switch(subfile->typeid) { 419 | case ST_TRE: dump_tre(subfile); break; 420 | case ST_RGN: dump_rgn(subfile); break; 421 | case ST_LBL: dump_lbl(subfile); break; 422 | case ST_NET: dump_net(subfile); break; 423 | case ST_NOD: dump_nod(subfile); break; 424 | case ST_DEM: dump_dem(subfile); break; 425 | case ST_MAR: dump_mar(subfile); break; 426 | case ST_TYP: dump_typ(subfile); break; 427 | case ST_MPS: dump_mps(subfile); break; 428 | case ST_GMP: dump_gmp(subfile); break; 429 | default: dump_comm(subfile->header); 430 | } 431 | } 432 | 433 | struct submap_struct *get_submap (struct gimg_struct *img, const char *mapname) 434 | { 435 | struct submap_struct *submap; 436 | if (strchr(mapname, '.') == NULL) { 437 | for (submap = img->submaps; submap != NULL; submap = submap->next) 438 | if (strcasecmp(mapname, submap->name) == 0) 439 | return submap; 440 | } else { 441 | int n = strchr(mapname, '.') - mapname; 442 | for (submap = img->submaps; submap != NULL; submap = submap->next) { 443 | if (strncasecmp(mapname, submap->name, n) == 0 && submap->name[n] == '\0') 444 | return submap; 445 | } 446 | } 447 | return NULL; 448 | } 449 | 450 | struct subfile_struct *get_subfile (struct gimg_struct *img, const char *subfilename) 451 | { 452 | struct subfile_struct *subfile; 453 | for (subfile = img->subfiles; subfile != NULL; subfile = subfile->next) 454 | if (strcasecmp(subfilename, subfile->fullname) == 0) 455 | return subfile; 456 | return NULL; 457 | } 458 | 459 | struct gimg_struct *gimg_open (const char *path, int writable) 460 | { 461 | struct gimg_struct *img = (struct gimg_struct *)malloc(sizeof(struct gimg_struct)); 462 | 463 | img->path = path; 464 | if (map_img(path, writable, &img->base, &img->size)) 465 | goto errout; 466 | if (parse_img(img)) 467 | goto errout; 468 | return img; 469 | 470 | errout: 471 | free(img); 472 | return NULL; 473 | } 474 | 475 | void gimg_close (struct gimg_struct *img) 476 | { 477 | unmap_img(img->base, img->size); 478 | //TODO how to free? 479 | } 480 | -------------------------------------------------------------------------------- /sf_lbl.c: -------------------------------------------------------------------------------- 1 | #include "gimglib.h" 2 | #include 3 | 4 | // 6-bit decoder cpoied from http://libgarmin.sourceforge.net/ 5 | static const char str6tbl1[] = { 6 | ' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R' 7 | ,'S','T','U','V','W','X','Y','Z' 8 | ,0,0,0,0,0 9 | ,'0','1','2','3','4','5','6','7','8','9' 10 | ,0,0,0,0,0,0 11 | }; 12 | 13 | static const char str6tbl2[] = { 14 | //@ ! " # $ % & ' ( ) * + , - . / 15 | '@','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/' 16 | ,0,0,0,0,0,0,0,0,0,0 17 | //: ; < = > ? 18 | ,':',';','<','=','>','?' 19 | ,0,0,0,0,0,0,0,0,0,0,0 20 | //[ \ ] ^ _ 21 | ,'[','\\',']','^','_' 22 | }; 23 | 24 | 25 | static const char str6tbl3[] = { 26 | '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r', 27 | 's','t','u','v','w','x','y','z' 28 | }; 29 | 30 | struct bitstream_struct { 31 | unsigned char *next; 32 | unsigned int buff; 33 | int bufflen; 34 | }; 35 | 36 | static void bitstream_init (struct bitstream_struct *bi, unsigned char *ptr) 37 | { 38 | bi->next = ptr; 39 | bi->buff = 0; 40 | bi->bufflen = 0; 41 | } 42 | 43 | static unsigned int bitstream_getint (struct bitstream_struct *bi, int bits) 44 | { 45 | unsigned int retval; 46 | 47 | assert(bits > 0 && bits < 24); 48 | while (bi->bufflen < bits) { 49 | bi->buff = (bi->buff << 8) | *(bi->next); 50 | bi->bufflen += 8; 51 | bi->next ++; 52 | } 53 | retval = bi->buff >> (bi->bufflen - bits); 54 | bi->buff &= (1 << (bi->bufflen - bits)) - 1; 55 | bi->bufflen -= bits; 56 | return retval; 57 | } 58 | 59 | // hex dump 60 | static void lbl_decoden (unsigned char *code, int bits, int count, char *out) 61 | { 62 | struct bitstream_struct bi; 63 | 64 | bitstream_init(&bi, code); 65 | while (count -- > 0) { 66 | out += sprintf(out, "%02x", bitstream_getint(&bi, bits)); 67 | if (count != 0) 68 | out += sprintf(out, "-"); 69 | } 70 | } 71 | 72 | 73 | static int lbl_decode6 (unsigned char *code, char *out, ssize_t outlen) 74 | { 75 | unsigned char ch; 76 | int sz = 0; 77 | struct bitstream_struct bi; 78 | 79 | bitstream_init(&bi, code); 80 | while (1) { 81 | int c = bitstream_getint(&bi, 6); 82 | //printf("got %d(0x%x)\n", c, c); 83 | if (c > 0x2f) 84 | break; 85 | ch = str6tbl1[c]; 86 | if (ch == 0) { 87 | if (c == 0x1C) { 88 | c = bitstream_getint(&bi, 6); 89 | if (c < 0) 90 | break; 91 | ch = str6tbl2[c]; 92 | *out++ = ch; 93 | sz++; 94 | } else if (c == 0x1B) { 95 | c = bitstream_getint(&bi, 6); 96 | if (c < 0) 97 | break; 98 | ch = str6tbl3[c]; 99 | *out++ = ch; 100 | sz++; 101 | } else if (c == 0x1D) { // delimiter for formal name 102 | *out++ = '|'; 103 | sz++; 104 | } else if (c == 0x1E) { 105 | *out++ = '_'; // hide previous symbols 106 | sz++; 107 | } else if (c == 0x1F) { 108 | *out++ = '^'; // hide next symbols 109 | sz++; 110 | } else if (c >= 0x20 && c <= 0x2F ) { 111 | *out++ = '@'; 112 | sz++; 113 | } 114 | } else { 115 | *out++ = ch; 116 | sz++; 117 | } 118 | if (sz >= outlen-1) 119 | break; 120 | } 121 | *out = '\0'; 122 | return sz; 123 | } 124 | 125 | static int lbl_decode8x (unsigned char *code, int codepage, char *out, ssize_t outlen) 126 | { 127 | if (codepage >= 37 && codepage <= 16804) { 128 | char cpstr[10]; 129 | iconv_t cd; 130 | char *inbuf = (char *)code, *outbuf = out; 131 | size_t inbytesleft = outlen, outbytesleft = outlen; 132 | 133 | sprintf(cpstr, "CP%d", codepage); 134 | cd = iconv_open("UTF-8", cpstr); //TODO: use setlocal to determine console encoding 135 | assert(cd != (iconv_t)-1); 136 | iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); //FIXME: this converts outlen bytes regardless of null-terminator. outlen is large. performance prob. 137 | outbuf[0] = '\0'; 138 | iconv_close(cd); 139 | 140 | for (outbuf = out; *outbuf; outbuf ++) { 141 | if (*outbuf == '\x1d') 142 | *outbuf = '|'; 143 | else if (*outbuf < ' ' && *outbuf > 0) 144 | *outbuf = '.'; 145 | } 146 | return strlen(out); 147 | } else { 148 | int i; 149 | for (i = 0; i < outlen - 1 && code[i]; i ++) { 150 | if (code[i] > ' ' && code[i] <= '~') 151 | out[i] = code[i]; 152 | else 153 | out[i] = '.'; 154 | } 155 | out[i] = '\0'; 156 | return i; 157 | } 158 | } 159 | 160 | static const char *lbl_decode_static (unsigned char *code, int bits, int codepage) 161 | { 162 | static char buf[4][512]; 163 | static unsigned int bufid = 0; 164 | int currid = (bufid ++) % 4; 165 | 166 | buf[currid][0] = '\0'; 167 | switch (bits) { 168 | //case 11: lbl_decoden(code, 11, 16, buf[currid]); break; 169 | case 6: lbl_decode6(code, buf[currid], sizeof(buf[0])); break; 170 | case 8: 171 | case 9: 172 | case 10: lbl_decode8x(code, codepage, buf[currid], sizeof(buf[0])); break; 173 | default: return dump_unknown_bytes(code, 16); 174 | //default: lbl_decoden(code, bits, 10, buf[currid]); 175 | } 176 | return buf[currid]; 177 | } 178 | 179 | const char *lbl_lbl_static (struct subfile_struct *lbl, int lbl_index) 180 | { 181 | struct garmin_lbl *header = (struct garmin_lbl *)lbl->header; 182 | return lbl_decode_static(lbl->base + header->lbl1_offset + 183 | lbl_index * (1 << header->addr_shift), 184 | header->coding, header->codepage); 185 | } 186 | 187 | // return number of bytes parsed. 188 | int dump_poi (struct subfile_struct *lbl, int poi_index) 189 | { 190 | struct garmin_lbl *header = (struct garmin_lbl *)lbl->header; 191 | unsigned char *lbl6_poi = lbl->base + header->lbl6_offset; 192 | int offset = poi_index * (1 << header->lbl6_addr_shift); 193 | int poi_data = lbl6_poi[offset] + (lbl6_poi[offset+1] << 8) + (lbl6_poi[offset+2] << 16); 194 | int local_mask = header->lbl6_glob_mask; 195 | 196 | printf("poi %d: %s, %s\n", poi_index, lbl_lbl_static(lbl, poi_data & 0x3fffff), dump_unknown_bytes(lbl6_poi + offset, 16)); 197 | 198 | offset += 3; 199 | if (poi_data >> 23) { 200 | int i, j; 201 | for (i = j = 0; i <= 7; i ++) { 202 | if (local_mask & (1 << i)) { 203 | if (!(lbl6_poi[offset] & (1 << j))) 204 | local_mask -= 1 << i; 205 | j ++; 206 | } 207 | } 208 | offset ++; 209 | } 210 | poi_data &= (1 << 22) - 1; 211 | 212 | printf("localmask: %x\n", local_mask); 213 | if (local_mask & 1) { // street_number 214 | if (lbl6_poi[offset] & 0x80) { 215 | char buf[32]; 216 | char *ptr = buf; 217 | int i; 218 | for (i = 0; ; i++) { 219 | int n = lbl6_poi[offset+i] & ~ 0x80; 220 | int n1 = n / 11; 221 | int n2 = n % 11; 222 | if (n2 == 10) 223 | ptr += sprintf(ptr, "%d", n1); 224 | else 225 | ptr += sprintf(ptr, "%d%d", n1, n2); 226 | if (i != 0 && lbl6_poi[offset+i] & 0x80) 227 | break; 228 | assert(buf + sizeof(buf) - ptr > 3); 229 | } 230 | printf("property_mask: %s\n", buf); 231 | offset += i; 232 | } else { 233 | int lbl_off = lbl6_poi[offset] + (lbl6_poi[offset+1] << 8) + (lbl6_poi[offset+2] << 16); 234 | printf("property_mask: 0x%x\n", lbl_off); 235 | offset += 3; 236 | } 237 | } 238 | 239 | // TODO 240 | return offset - poi_index * (1 << header->lbl6_addr_shift); 241 | } 242 | 243 | void dump_lbl (struct subfile_struct *sf) 244 | { 245 | struct garmin_lbl *header = (struct garmin_lbl *)sf->header; 246 | int progress = 0; 247 | unsigned char *lbl1_data = NULL; 248 | unsigned char *lbl2_country = NULL; 249 | unsigned char *lbl3_region = NULL; 250 | unsigned char *lbl4_city = NULL; 251 | unsigned char *lbl6_poi = NULL; 252 | 253 | assert(sf->typeid == ST_LBL); 254 | 255 | dump_comm(sf->header); 256 | 257 | printf("=== LBL HEADER ===\n"); 258 | if (header->comm.hlen < 0xaa) 259 | goto headerfini; 260 | progress = 0xaa; 261 | printf("LBL1 Data: reloff=0x%x absoff=0x%x size=0x%x\n", 262 | header->lbl1_offset, sf->offset + header->lbl1_offset, header->lbl1_length); 263 | if (header->lbl1_length) 264 | lbl1_data = sf->base + header->lbl1_offset; 265 | printf("Shift, Coding: %d, %d\n", 266 | header->addr_shift, header->coding); 267 | printf("LBL2 Country: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?029=0x%x\n", 268 | header->lbl2_offset, sf->offset + header->lbl2_offset, 269 | header->lbl2_length, header->lbl2_recsize, header->lbl2_u); 270 | if (header->lbl2_length) 271 | lbl2_country = sf->base + header->lbl2_offset; 272 | printf("LBL3 Region: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?037=0x%x\n", 273 | header->lbl3_offset, sf->offset + header->lbl3_offset, 274 | header->lbl3_length, header->lbl3_recsize, header->lbl3_u); 275 | if (header->lbl3_length) 276 | lbl3_region = sf->base + header->lbl3_offset; 277 | printf("LBL4 City: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?045=0x%x\n", 278 | header->lbl4_offset, sf->offset + header->lbl4_offset, 279 | header->lbl4_length, header->lbl4_recsize, header->lbl4_u); 280 | if (header->lbl4_length) 281 | lbl4_city = sf->base + header->lbl4_offset; 282 | printf("LBL5 POI: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?053=0x%x\n", 283 | header->lbl5_offset, sf->offset + header->lbl5_offset, 284 | header->lbl5_length, header->lbl5_recsize, header->lbl5_u); 285 | printf("LBL6 POI Prop: reloff=0x%x absoff=0x%x size=0x%x shift=%d mask=0x%x ?061=0x%02x%02x%02x\n", 286 | header->lbl6_offset, sf->offset + header->lbl6_offset, header->lbl6_length, 287 | header->lbl6_addr_shift, header->lbl6_glob_mask, 288 | header->lbl6_u[0], header->lbl6_u[1], header->lbl6_u[2]); 289 | if (header->lbl6_length) 290 | lbl6_poi = sf->base + header->lbl6_offset; 291 | printf("LBL7 POI Type: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?06e=0x%x\n", 292 | header->lbl7_offset, sf->offset + header->lbl7_offset, 293 | header->lbl7_length, header->lbl7_recsize, header->lbl7_u); 294 | printf("LBL8 Zip: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?07c=0x%x\n", 295 | header->lbl8_offset, sf->offset + header->lbl8_offset, 296 | header->lbl8_length, header->lbl8_recsize, header->lbl8_u); 297 | printf("LBL9 Highway: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?08a=0x%x\n", 298 | header->lbl9_offset, sf->offset + header->lbl9_offset, 299 | header->lbl9_length, header->lbl9_recsize, header->lbl9_u); 300 | printf("LBL10 Exit: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?098=0x%x\n", 301 | header->lbl10_offset, sf->offset + header->lbl10_offset, 302 | header->lbl10_length, header->lbl10_recsize, header->lbl10_u); 303 | printf("LBL11 Highway Data: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?0a6=0x%x\n", 304 | header->lbl11_offset, sf->offset + header->lbl11_offset, 305 | header->lbl11_length, header->lbl11_recsize, header->lbl11_u); 306 | 307 | if (header->comm.hlen < 0xb0) 308 | goto headerfini; 309 | progress = 0xb0; 310 | if (header->codepage >= 1250 && header->codepage <= 1258) 311 | printf("Codepage: Windows-%d\n", header->codepage); 312 | else if (header->codepage == 950) 313 | printf("Codepage: Big5\n"); 314 | else if (header->codepage == 936) 315 | printf("Codepage: GBK\n"); 316 | else 317 | printf("Codepage: %d\n", header->codepage); 318 | printf("Codepage2: 0x%d\n", header->codepage2); 319 | printf("Codepage3: 0x%x\n", header->codepage3); 320 | 321 | if (header->comm.hlen < 0xb8) 322 | goto headerfini; 323 | progress = 0xb8; 324 | printf("LBL12: reloff=0x%x absoff=0x%x size=0x%x\n", 325 | header->lbl12_offset, sf->offset + header->lbl12_offset, header->lbl12_length); 326 | if (header->comm.hlen < 0xc4) 327 | goto headerfini; 328 | progress = 0xc4; 329 | printf("LBL13: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 330 | header->lbl13_offset, sf->offset + header->lbl13_offset, header->lbl13_length, 331 | header->lbl13_recsize, header->lbl13_u); 332 | if (header->comm.hlen < 0xd0) 333 | goto headerfini; 334 | progress = 0xd0; 335 | printf("LBL14: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 336 | header->lbl14_offset, sf->offset + header->lbl14_offset, header->lbl14_length, 337 | header->lbl14_recsize, header->lbl14_u); 338 | if (header->comm.hlen < 0xde) 339 | goto headerfini; 340 | progress = 0xde; 341 | printf("LBL15: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 342 | header->lbl15_offset, sf->offset + header->lbl15_offset, header->lbl15_length, 343 | header->lbl15_recsize, header->lbl15_u); 344 | if (header->comm.hlen < 0xec) 345 | goto headerfini; 346 | progress = 0xec; 347 | printf("LBL16: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 348 | header->lbl16_offset, sf->offset + header->lbl16_offset, header->lbl16_length, 349 | header->lbl16_recsize, header->lbl16_u); 350 | if (header->comm.hlen < 0xfa) 351 | goto headerfini; 352 | progress = 0xfa; 353 | printf("LBL17: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 354 | header->lbl17_offset, sf->offset + header->lbl17_offset, header->lbl17_length, 355 | header->lbl17_recsize, header->lbl17_u); 356 | if (header->comm.hlen < 0x108) 357 | goto headerfini; 358 | progress = 0x108; 359 | printf("LBL18: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 360 | header->lbl18_offset, sf->offset + header->lbl18_offset, header->lbl18_length, 361 | header->lbl18_recsize, header->lbl18_u); 362 | if (header->comm.hlen < 0x116) 363 | goto headerfini; 364 | progress = 0x116; 365 | printf("LBL19: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 366 | header->lbl19_offset, sf->offset + header->lbl19_offset, header->lbl19_length, 367 | header->lbl19_recsize, header->lbl19_u); 368 | if (header->comm.hlen < 0x124) 369 | goto headerfini; 370 | progress = 0x124; 371 | printf("LBL20: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 372 | header->lbl20_offset, sf->offset + header->lbl20_offset, header->lbl20_length, 373 | header->lbl20_recsize, header->lbl20_u); 374 | if (header->comm.hlen < 0x132) 375 | goto headerfini; 376 | progress = 0x132; 377 | printf("LBL21: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 378 | header->lbl21_offset, sf->offset + header->lbl21_offset, header->lbl21_length, 379 | header->lbl21_recsize, header->lbl21_u); 380 | if (header->comm.hlen < 0x140) 381 | goto headerfini; 382 | progress = 0x140; 383 | printf("LBL22: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 384 | header->lbl22_offset, sf->offset + header->lbl22_offset, header->lbl22_length, 385 | header->lbl22_recsize, header->lbl22_u); 386 | if (header->comm.hlen < 0x14e) 387 | goto headerfini; 388 | progress = 0x14e; 389 | printf("LBL23: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 390 | header->lbl23_offset, sf->offset + header->lbl23_offset, header->lbl23_length, 391 | header->lbl23_recsize, header->lbl23_u); 392 | if (header->comm.hlen < 0x15a) 393 | goto headerfini; 394 | progress = 0x15a; 395 | printf("LBL24: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 396 | header->lbl24_offset, sf->offset + header->lbl24_offset, header->lbl24_length, 397 | header->lbl24_recsize, header->lbl24_u); 398 | if (header->comm.hlen < 0x168) 399 | goto headerfini; 400 | progress = 0x168; 401 | printf("LBL25: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 402 | header->lbl25_offset, sf->offset + header->lbl25_offset, header->lbl25_length, 403 | header->lbl25_recsize, header->lbl25_u); 404 | if (header->comm.hlen < 0x176) 405 | goto headerfini; 406 | progress = 0x176; 407 | printf("LBL26: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 408 | header->lbl26_offset, sf->offset + header->lbl26_offset, header->lbl26_length, 409 | header->lbl26_recsize, header->lbl26_u); 410 | if (header->comm.hlen < 0x184) 411 | goto headerfini; 412 | progress = 0x184; 413 | printf("LBL27: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 414 | header->lbl27_offset, sf->offset + header->lbl27_offset, header->lbl27_length, 415 | header->lbl27_recsize, header->lbl27_u); 416 | if (header->comm.hlen < 0x192) 417 | goto headerfini; 418 | progress = 0x192; 419 | printf("LBL28: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 420 | header->lbl28_offset, sf->offset + header->lbl28_offset, header->lbl28_length, 421 | header->lbl28_recsize, header->lbl28_u); 422 | if (header->comm.hlen < 0x19a) 423 | goto headerfini; 424 | progress = 0x19a; 425 | printf("LBL29: reloff=0x%x absoff=0x%x size=0x%x\n", 426 | header->lbl29_offset, sf->offset + header->lbl29_offset, header->lbl29_length); 427 | if (header->comm.hlen < 0x1a6) 428 | goto headerfini; 429 | progress = 0x1a6; 430 | printf("LBL30: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 431 | header->lbl30_offset, sf->offset + header->lbl30_offset, header->lbl30_length, 432 | header->lbl30_recsize, header->lbl30_u); 433 | if (header->comm.hlen < 0x1b2) 434 | goto headerfini; 435 | progress = 0x1b2; 436 | printf("LBL31: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 437 | header->lbl31_offset, sf->offset + header->lbl31_offset, header->lbl31_length, 438 | header->lbl31_recsize, header->lbl31_u); 439 | if (header->comm.hlen < 0x1be) 440 | goto headerfini; 441 | progress = 0x1be; 442 | printf("LBL32: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 443 | header->lbl32_offset, sf->offset + header->lbl32_offset, header->lbl32_length, 444 | header->lbl32_recsize, header->lbl32_u); 445 | if (header->comm.hlen < 0x1ca) 446 | goto headerfini; 447 | progress = 0x1ca; 448 | printf("LBL33: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 449 | header->lbl33_offset, sf->offset + header->lbl33_offset, header->lbl33_length, 450 | header->lbl33_recsize, header->lbl33_u); 451 | if (header->comm.hlen < 0x1d8) 452 | goto headerfini; 453 | progress = 0x1d8; 454 | printf("LBL34: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 455 | header->lbl34_offset, sf->offset + header->lbl34_offset, header->lbl34_length, 456 | header->lbl34_recsize, header->lbl34_u); 457 | if (header->comm.hlen < 0x1e6) 458 | goto headerfini; 459 | progress = 0x1e6; 460 | printf("LBL35: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 461 | header->lbl35_offset, sf->offset + header->lbl35_offset, header->lbl35_length, 462 | header->lbl35_recsize, header->lbl35_u); 463 | if (header->comm.hlen < 0x1f2) 464 | goto headerfini; 465 | progress = 0x1f2; 466 | printf("LBL36: reloff=0x%x absoff=0x%x size=0x%x recsize=0x%x ?=0x%x\n", 467 | header->lbl36_offset, sf->offset + header->lbl36_offset, header->lbl36_length, 468 | header->lbl36_recsize, header->lbl36_u); 469 | 470 | headerfini: 471 | if (header->comm.hlen > progress) 472 | printf("from 0x%x to 0x%x (0x%x bytes): %s\n", 473 | progress, header->comm.hlen - 1, 474 | header->comm.hlen - progress, 475 | dump_unknown_bytes((uint8_t *)header + progress, header->comm.hlen - progress)); 476 | 477 | if (lbl2_country) { 478 | int offset, id; 479 | printf("=== COUNTRY ===\n"); 480 | for (offset = 0, id = 1; offset < header->lbl2_length; offset += header->lbl2_recsize, id ++) { 481 | int dataoff = lbl2_country[offset] + (lbl2_country[offset+1] << 8) + (lbl2_country[offset+2] << 16); 482 | printf("%d: \"%s\", \"%s\"\n", id, 483 | lbl_decode_static(lbl1_data + (dataoff << header->addr_shift), header->coding, header->codepage), 484 | //dump_unknown_bytes(lbl1_data + (dataoff << header->addr_shift), 32), 485 | dump_unknown_bytes(lbl2_country + offset + 3, header->lbl2_recsize - 3)); 486 | } 487 | } 488 | 489 | if (lbl3_region) { 490 | int offset, id; 491 | printf("=== REGION ===\n"); 492 | for (offset = 0, id = 1; offset < header->lbl3_length; offset += header->lbl3_recsize, id ++) { 493 | int dataoff = lbl3_region[offset+2] + (lbl3_region[offset+3] << 8) + (lbl3_region[offset+4] << 16); 494 | printf("%d: country=%d, \"%s\", \"%s\"\n", id, 495 | (lbl3_region[offset] + (lbl3_region[offset+1] << 8)), 496 | lbl_decode_static(lbl1_data + (dataoff << header->addr_shift), header->coding, header->codepage), 497 | //dump_unknown_bytes(lbl1_data + (dataoff << header->addr_shift), 32), 498 | dump_unknown_bytes(lbl3_region + offset + 5, header->lbl3_recsize - 5)); 499 | } 500 | } 501 | 502 | if (lbl4_city) { 503 | int offset, id; 504 | printf("=== CITY ===\n"); 505 | for (offset = 0, id = 1; offset < header->lbl4_length; offset += header->lbl4_recsize, id ++) { 506 | int region = lbl4_city[offset+3] + (lbl4_city[offset+4] << 8); 507 | int pointref = region >> 15; 508 | region &= (1 << 14) - 1; 509 | if (pointref) { 510 | printf("%d: region=%d, point_index=%d, subdiv_index=%d, \"%s\"\n", id, region, 511 | lbl4_city[offset], 512 | lbl4_city[offset+1] + (lbl4_city[offset+2] << 8), 513 | dump_unknown_bytes(lbl4_city + offset + 5, header->lbl4_recsize - 5)); 514 | } else { 515 | int dataoff = lbl4_city[offset] + (lbl4_city[offset+1] << 8) + (lbl4_city[offset+2] << 16); 516 | printf("%d: region=%d, \"%s\", \"%s\"\n", id, region, 517 | lbl_decode_static(lbl1_data + (dataoff << header->addr_shift), header->coding, header->codepage), 518 | //dump_unknown_bytes(lbl1_data + (dataoff << header->addr_shift), 32), 519 | dump_unknown_bytes(lbl4_city + offset + 5, header->lbl4_recsize - 5)); 520 | } 521 | } 522 | } 523 | 524 | /* if (lbl6_poi) { 525 | int index; 526 | const int indexmul = 1 << header->lbl6_addr_shift; 527 | printf("=== PoI ===\n"); 528 | for (index = 0; index < (header->lbl6_length - 3) / indexmul; ) { 529 | int poi_size = dump_poi(sf, index); 530 | if (poi_size <= 0) { 531 | printf("poi parse error\n"); 532 | break; 533 | } 534 | index += (poi_size - 1) / indexmul + 1; 535 | } 536 | } 537 | */ 538 | return; 539 | } 540 | --------------------------------------------------------------------------------