You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tomtom.cc 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. /*
  2. Read and write TomTom .ov2 files.
  3. Copyright (C) 2005 Ronald Parker (babeltomtom@parkrrrr.com) and
  4. Robert Lipe (robertlipe+source@gpsbabel.org)
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  16. */
  17. /*
  18. This module is based on my reverse-engineering of the .ov2 format, so
  19. it might not be aware of all record types. In particular, I've seen
  20. a type-3 record that may contain additional strings, but since I haven't
  21. seen any of those from a legitimate source, I don't know what they are
  22. supposed to contain. Thus, they are not currently supported. (The one
  23. I saw was due to an errant pair of double-quotes in the input to
  24. makeov2.exe.) -- Ron Parker, 28 April 2005
  25. Because they've been seen in the wild, I have updated the reader to
  26. deal with type 3 as if they were type 2. I still haven't seen any
  27. records that fill in the other two strings, so until I know for sure
  28. that they are indeed strings, I'm just putting them on the end of the
  29. description string beyond the NUL terminator. -- Ron Parker, 17 July 2006
  30. */
  31. #include "defs.h"
  32. #include <stdio.h> // sprintf
  33. #include <stdlib.h> // qsort
  34. #include <QtCore/QTextCodec>
  35. #define MYNAME "TomTom"
  36. static gbfile* file_in;
  37. static gbfile* file_out;
  38. static
  39. arglist_t tomtom_args[] = {
  40. ARG_TERMINATOR
  41. };
  42. static void
  43. rd_init(const QString& fname)
  44. {
  45. file_in = gbfopen_le(fname, "rb", MYNAME);
  46. }
  47. static void
  48. rd_deinit(void)
  49. {
  50. gbfclose(file_in);
  51. }
  52. static void
  53. wr_init(const QString& fname)
  54. {
  55. file_out = gbfopen_le(fname, "wb", MYNAME);
  56. }
  57. static void
  58. wr_deinit(void)
  59. {
  60. gbfclose(file_out);
  61. }
  62. #define read_long(f) gbfgetint32((f))
  63. #define read_char(f) gbfgetc((f))
  64. /*
  65. * Decode a type 8 compressed record
  66. */
  67. char*
  68. decode_8(int sz, const unsigned char* inbuf)
  69. {
  70. static const char encoding_8[] = "X. SaerionstldchumgpbkfzvACBMPG-";
  71. static const int encoding_8_high[8] = {0x2,0x3,0x4,0x5,0x6,0x7,0xe,0xf};
  72. // Maximally sized for laziness.
  73. char* rval = (char*) xmalloc(sz * 3 + 1);
  74. char* out = rval;
  75. int i;
  76. for (i = 0; i < sz;) {
  77. if (inbuf[0] & 0x80) {
  78. int idx;
  79. int res;
  80. idx = (inbuf[0] & 0x70) >> 4;
  81. res = inbuf[0] & 0x0f;
  82. res |= encoding_8_high[idx] << 4;
  83. *out++ = res;
  84. inbuf++;
  85. i++;
  86. } else {
  87. int c1 = (inbuf[0] & 0x7c) >> 2;
  88. int c2 = ((inbuf[0] & 3) << 3) | (inbuf[1] & 0xe0) >> 5;
  89. int c3 = inbuf[1] & 0x1f;
  90. if ((c1 | c2 | c3) > 0x1f) {
  91. fatal("bit unpacking error");
  92. }
  93. *out++ = encoding_8[c1];
  94. *out++ = encoding_8[c2];
  95. *out++ = encoding_8[c3];
  96. inbuf+=2;
  97. i+=2;
  98. }
  99. }
  100. return rval;
  101. }
  102. void
  103. decode_latlon(double* lat, double* lon)
  104. {
  105. unsigned char latbuf[3];
  106. unsigned char lonbuf[3];
  107. double rlat, rlon;
  108. gbfread(&lonbuf, 3, 1, file_in);
  109. gbfread(&latbuf, 3, 1, file_in);
  110. rlat = ((latbuf[2] << 16) + (latbuf[1] << 8) + latbuf[0]) / 1000000.0;
  111. *lat = 80 - rlat;
  112. *lon = rlon = 123.456;
  113. }
  114. static void
  115. check_recsize(int sz)
  116. {
  117. if ((sz > 100000) || (sz < 0)) {
  118. fatal(MYNAME ":malformed file. Bad record size.");
  119. }
  120. }
  121. static void
  122. data_read(void)
  123. {
  124. int rectype;
  125. long recsize;
  126. long x;
  127. long y;
  128. char* desc;
  129. Waypoint* wpt_tmp;
  130. while (!gbfeof(file_in)) {
  131. rectype = read_char(file_in);
  132. if (rectype == EOF) {
  133. fatal(MYNAME ":Unexpected EOF.");
  134. }
  135. if (global_opts.debug_level >= 5) {
  136. printf("Reading record type %d\n", rectype);
  137. }
  138. switch (rectype) {
  139. case 0:
  140. case 100:
  141. if (global_opts.debug_level >= 5) {
  142. printf("Skipping deleted record\n");
  143. }
  144. recsize = read_long(file_in) - 5;
  145. check_recsize(recsize);
  146. if (global_opts.debug_level >= 5) {
  147. printf("Skipping %li bytes\n", recsize);
  148. }
  149. gbfseek(file_in, recsize, SEEK_CUR);
  150. break;
  151. case 1:
  152. /* a block header; ignored on read */
  153. read_long(file_in);
  154. read_long(file_in);
  155. read_long(file_in);
  156. read_long(file_in);
  157. read_long(file_in);
  158. break;
  159. case 2:
  160. case 3:
  161. recsize = read_long(file_in);
  162. check_recsize(recsize);
  163. x = read_long(file_in);
  164. y = read_long(file_in);
  165. desc = (char*)xmalloc(recsize - 13);
  166. gbfread(desc, recsize-13, 1, file_in);
  167. wpt_tmp = new Waypoint;
  168. wpt_tmp->longitude = x/100000.0;
  169. wpt_tmp->latitude = y/100000.0;
  170. wpt_tmp->description = STRTOUNICODE(desc);
  171. xfree(desc);
  172. desc = NULL;
  173. // TODO:: description in rectype 3 contains two zero-terminated strings
  174. // First is same as rectype 2, second apparently contains the unique ID of the waypoint
  175. // See http://www.tomtom.com/lib/doc/PRO/TTN6_SDK_documentation.zip
  176. if (rectype == 3) {
  177. warning("Unexpected waypoint record type %d encountered.\nThe unique ID of the POI may have been dropped.\n", rectype);
  178. }
  179. waypt_add(wpt_tmp);
  180. break;
  181. case 8:
  182. case 24:
  183. #if 0 // Fallthrough for now to silently ignore these until this is done.
  184. recsize = read_char(file_in) ;
  185. check_recsize(recsize);
  186. wpt_tmp = new Waypoint;
  187. decode_latlon(&wpt_tmp->latitude, &wpt_tmp->longitude);
  188. gbfread(tbuf, 3, 1, file_in);
  189. gbfread(tbuf, 3, 1, file_in);
  190. gbfread(tbuf, recsize, 1, file_in);
  191. wpt_tmp->shortname = decode_8(recsize, tbuf);
  192. waypt_add(wpt_tmp);
  193. break;
  194. #else
  195. #endif
  196. case 9:
  197. case 25:
  198. recsize = read_char(file_in) + 6;
  199. check_recsize(recsize);
  200. if (global_opts.debug_level >= 5)
  201. warning("Unknown record type 0x%x; skipping %ld bytes.\n",
  202. rectype, recsize);
  203. gbfseek(file_in, recsize, SEEK_CUR);
  204. break;
  205. default:
  206. if (global_opts.debug_level >= 1) {
  207. warning("Unexpected waypoint record type: %d at offset 0x%x\n", rectype, gbftell(file_in));
  208. }
  209. }
  210. }
  211. }
  212. struct hdr {
  213. Waypoint* wpt;
  214. };
  215. static int compare_lon(const void* a, const void* b);
  216. static
  217. int
  218. compare_lat(const void* a, const void* b)
  219. {
  220. const struct hdr* wa = (const struct hdr*) a;
  221. const struct hdr* wb = (const struct hdr*) b;
  222. double difference = wa->wpt->latitude - wb->wpt->latitude;
  223. if (difference < 0) {
  224. return -1;
  225. }
  226. if (difference) {
  227. return 1;
  228. }
  229. if (wa->wpt->longitude - wb->wpt->longitude == 0) {
  230. return wa->wpt->shortname.compare(wb->wpt->shortname);
  231. }
  232. return compare_lon(a,b);
  233. }
  234. static
  235. int
  236. compare_lon(const void* a, const void* b)
  237. {
  238. const struct hdr* wa = (const struct hdr*)a;
  239. const struct hdr* wb = (const struct hdr*)b;
  240. double difference = wa->wpt->longitude - wb->wpt->longitude;
  241. if (difference < 0) {
  242. return -1;
  243. }
  244. if (difference) {
  245. return 1;
  246. }
  247. if (wa->wpt->latitude - wb->wpt->latitude == 0) {
  248. return wa->wpt->shortname.compare(wb->wpt->shortname);
  249. }
  250. return compare_lat(a,b);
  251. }
  252. #define write_long(f,v) gbfputint32((v),f)
  253. static void
  254. write_float_as_long(gbfile* file, double value)
  255. {
  256. long tmp = (value + 0.500000000001);
  257. write_long(file, tmp);
  258. }
  259. #define write_char(f,c) gbfputc((c),f)
  260. #define write_string(f,s) gbfwrite((s),1,strlen(s)+1,f)
  261. struct blockheader {
  262. struct hdr* start;
  263. long count;
  264. long size;
  265. double minlat;
  266. double maxlat;
  267. double minlon;
  268. double maxlon;
  269. struct blockheader* ch1;
  270. struct blockheader* ch2;
  271. };
  272. static void
  273. write_blocks(gbfile* f, struct blockheader* blocks)
  274. {
  275. int i;
  276. write_char(f, 1);
  277. write_long(f, blocks->size);
  278. write_float_as_long(f, blocks->maxlon*100000);
  279. write_float_as_long(f, blocks->maxlat*100000);
  280. write_float_as_long(f, blocks->minlon*100000);
  281. write_float_as_long(f, blocks->minlat*100000);
  282. if (blocks->ch1) {
  283. write_blocks(f, blocks->ch1);
  284. }
  285. if (blocks->ch2) {
  286. write_blocks(f, blocks->ch2);
  287. }
  288. if (!blocks->ch1 && !blocks->ch2) {
  289. for (i = 0; i < blocks->count; i++) {
  290. char desc_field [256];
  291. write_char(f, 2);
  292. if (global_opts.smart_names &&
  293. blocks->start[i].wpt->gc_data->diff &&
  294. blocks->start[i].wpt->gc_data->terr) {
  295. snprintf(desc_field,sizeof(desc_field),"%s(t%ud%u)%s(type%dcont%d)",STRFROMUNICODE(blocks->start[i].wpt->description),
  296. blocks->start[i].wpt->gc_data->terr/10,
  297. blocks->start[i].wpt->gc_data->diff/10,
  298. STRFROMUNICODE(blocks->start[i].wpt->shortname),
  299. (int) blocks->start[i].wpt->gc_data->type,
  300. (int) blocks->start[i].wpt->gc_data->container);
  301. //Unfortunately enums mean we get numbers for cache type and container.
  302. } else {
  303. snprintf(desc_field, sizeof(desc_field), "%s",
  304. STRFROMUNICODE(blocks->start[i].wpt->description));
  305. }
  306. write_long(f, strlen(desc_field) + 14);
  307. write_float_as_long(f, blocks->start[i].wpt->longitude*100000);
  308. write_float_as_long(f, blocks->start[i].wpt->latitude*100000);
  309. write_string(f, desc_field);
  310. }
  311. }
  312. }
  313. static struct blockheader*
  314. compute_blocks(struct hdr* start, int count,
  315. double minlon, double maxlon, double minlat, double maxlat)
  316. {
  317. struct blockheader* newblock;
  318. newblock = (struct blockheader*)xcalloc(sizeof(*newblock), 1);
  319. newblock->start = start;
  320. newblock->count = count;
  321. newblock->minlon = minlon;
  322. newblock->maxlon = maxlon;
  323. newblock->minlat = minlat;
  324. newblock->maxlat = maxlat;
  325. newblock->size = 4 * 5 + 1; /* hdr is 5 longs, 1 char */
  326. if (count < 20) {
  327. int i;
  328. Waypoint* wpt = NULL;
  329. for (i = 0; i < count; i++) {
  330. newblock->size += 4 * 3 + 1;
  331. /* wpt const part 3 longs, 1 char */
  332. wpt = start[i].wpt;
  333. newblock->size += wpt->description.length() + 1;
  334. }
  335. } else {
  336. if ((maxlat-minlat)>(maxlon-minlon)) {
  337. /* split along lats */
  338. qsort(start, count, sizeof(*start), compare_lat);
  339. newblock->ch1 = compute_blocks(start, count/2,
  340. minlon, maxlon, minlat,
  341. start[count/2].wpt->latitude);
  342. newblock->ch2 = compute_blocks(start+count/2,
  343. count-count/2, minlon, maxlon,
  344. start[count/2].wpt->latitude, maxlat);
  345. } else {
  346. /* split along lons */
  347. qsort(start, count, sizeof(*start), compare_lon);
  348. newblock->ch1 = compute_blocks(start, count/2,
  349. minlon, start[count/2].wpt->longitude,
  350. minlat, maxlat);
  351. newblock->ch2 = compute_blocks(start+count/2,
  352. count-count/2, start[count/2].wpt->longitude,
  353. maxlon, minlat, maxlat);
  354. }
  355. if (newblock->ch1) {
  356. newblock->size += newblock->ch1->size;
  357. }
  358. if (newblock->ch2) {
  359. newblock->size += newblock->ch2->size;
  360. }
  361. }
  362. return newblock;
  363. }
  364. static void
  365. free_blocks(struct blockheader* block)
  366. {
  367. if (block->ch1) {
  368. free_blocks(block->ch1);
  369. }
  370. if (block->ch2) {
  371. free_blocks(block->ch2);
  372. }
  373. xfree(block);
  374. }
  375. static void
  376. data_write(void)
  377. {
  378. int ct = waypt_count();
  379. struct hdr* htable, *bh;
  380. #if NEWQ
  381. extern QList<Waypoint*> waypt_list;
  382. #else
  383. queue* elem, *tmp;
  384. extern queue waypt_head;
  385. #endif
  386. double minlon = 200;
  387. double maxlon = -200;
  388. double minlat = 200;
  389. double maxlat = -200;
  390. struct blockheader* blocks = NULL;
  391. htable = (struct hdr*) xmalloc(ct * sizeof(*htable));
  392. bh = htable;
  393. #if NEWQ
  394. // Iterate with waypt_disp_all?
  395. foreach(Waypoint* waypointp, waypt_list) {
  396. #else
  397. QUEUE_FOR_EACH(&waypt_head, elem, tmp) {
  398. Waypoint* waypointp = (Waypoint*) elem;
  399. #endif
  400. bh->wpt = waypointp;
  401. if (waypointp->longitude > maxlon) {
  402. maxlon = waypointp->longitude;
  403. }
  404. if (waypointp->longitude < minlon) {
  405. minlon = waypointp->longitude;
  406. }
  407. if (waypointp->latitude > maxlat) {
  408. maxlat = waypointp->latitude;
  409. }
  410. if (waypointp->latitude < minlat) {
  411. minlat = waypointp->latitude;
  412. }
  413. bh ++;
  414. }
  415. blocks = compute_blocks(htable, ct, minlon, maxlon, minlat, maxlat);
  416. write_blocks(file_out, blocks);
  417. free_blocks(blocks);
  418. xfree(htable);
  419. }
  420. ff_vecs_t tomtom_vecs = {
  421. ff_type_file,
  422. FF_CAP_RW_WPT,
  423. rd_init,
  424. wr_init,
  425. rd_deinit,
  426. wr_deinit,
  427. data_read,
  428. data_write,
  429. NULL,
  430. tomtom_args,
  431. CET_CHARSET_MS_ANSI, 0 /* CET-REVIEW */
  432. };