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.

mapsend.cc 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /*
  2. Access Magellan Mapsend files.
  3. Copyright (C) 2002-2006 Robert Lipe, robertlipe+source@gpsbabel.org
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  15. */
  16. #include "defs.h"
  17. #include "mapsend.h"
  18. #include "magellan.h"
  19. #include <cmath>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. static gbfile* mapsend_file_in;
  23. static gbfile* mapsend_file_out;
  24. static short_handle mkshort_handle;
  25. static short_handle wpt_handle;
  26. static int route_wp_count;
  27. static int mapsend_infile_version;
  28. static int trk_version = 30;
  29. #define MYNAME "mapsend"
  30. static char* mapsend_opt_trkver = NULL;
  31. #define MAPSEND_TRKVER_MIN 3
  32. #define MAPSEND_TRKVER_MAX 4
  33. static
  34. arglist_t mapsend_args[] = {
  35. {
  36. "trkver", &mapsend_opt_trkver,
  37. "MapSend version TRK file to generate (3,4)",
  38. "4", ARGTYPE_INT, "3", "4"
  39. },
  40. ARG_TERMINATOR
  41. };
  42. static void
  43. mapsend_init_opts(const char isReading) /* 1=read, 2=write */
  44. {
  45. int opt_trkver;
  46. /* read & write options here */
  47. if (isReading) {
  48. /* reading-only options here */
  49. } else {
  50. /* writing-only options here */
  51. // TRK MapSend version
  52. opt_trkver = atoi(mapsend_opt_trkver);
  53. if ((opt_trkver < MAPSEND_TRKVER_MIN) || (opt_trkver > MAPSEND_TRKVER_MAX)) {
  54. fatal(MYNAME ": Unsupported MapSend TRK version \"%s\"!\n", mapsend_opt_trkver);
  55. }
  56. trk_version = opt_trkver * 10;
  57. }
  58. }
  59. static void
  60. mapsend_rd_init(const QString& fname)
  61. {
  62. mapsend_init_opts(1);
  63. mapsend_file_in = gbfopen_le(fname, "rb", MYNAME);
  64. }
  65. static void
  66. mapsend_rd_deinit(void)
  67. {
  68. gbfclose(mapsend_file_in);
  69. }
  70. static void
  71. mapsend_wr_init(const QString& fname)
  72. {
  73. mapsend_init_opts(0);
  74. mapsend_file_out = gbfopen(fname, "wb", MYNAME);
  75. mkshort_handle = mkshort_new_handle();
  76. wpt_handle = mkshort_new_handle();
  77. setshort_whitespace_ok(wpt_handle, 1);
  78. setshort_length(wpt_handle, 8);
  79. route_wp_count = 0;
  80. }
  81. static void
  82. mapsend_wr_deinit(void)
  83. {
  84. gbfclose(mapsend_file_out);
  85. mkshort_del_handle(&mkshort_handle);
  86. mkshort_del_handle(&wpt_handle);
  87. }
  88. static void
  89. mapsend_wpt_read(void)
  90. {
  91. char tbuf[256];
  92. int wpt_count, rte_count, rte_num;
  93. int wpt_number;
  94. char wpt_icon;
  95. char wpt_status;
  96. Waypoint* wpt_tmp;
  97. route_head* rte_head;
  98. wpt_count = gbfgetint32(mapsend_file_in);
  99. while (wpt_count--) {
  100. wpt_tmp = new Waypoint;
  101. wpt_tmp->shortname = gbfgetpstr(mapsend_file_in);
  102. wpt_tmp->description = gbfgetpstr(mapsend_file_in);
  103. wpt_number = gbfgetint32(mapsend_file_in);
  104. (void) wpt_number; // hush warning.
  105. wpt_icon = gbfgetc(mapsend_file_in);
  106. wpt_status = gbfgetc(mapsend_file_in);
  107. (void) wpt_status; // hush warning.
  108. wpt_tmp->altitude = gbfgetdbl(mapsend_file_in);
  109. wpt_tmp->longitude = gbfgetdbl(mapsend_file_in);
  110. wpt_tmp->latitude = -gbfgetdbl(mapsend_file_in);
  111. if (wpt_icon < 26) {
  112. sprintf(tbuf, "%c", wpt_icon + 'a');
  113. } else {
  114. sprintf(tbuf, "a%c", wpt_icon - 26 + 'a');
  115. }
  116. wpt_tmp->icon_descr = mag_find_descr_from_token(tbuf);
  117. waypt_add(wpt_tmp);
  118. }
  119. /* now read the routes... */
  120. rte_count = gbfgetint32(mapsend_file_in);
  121. while (rte_count--) {
  122. rte_head = route_head_alloc();
  123. route_add_head(rte_head);
  124. /* route name */
  125. rte_head->rte_name = gbfgetpstr(mapsend_file_in);
  126. /* route # */
  127. rte_num = gbfgetint32(mapsend_file_in);
  128. rte_head->rte_num = rte_num;
  129. /* points this route */
  130. wpt_count = gbfgetint32(mapsend_file_in);
  131. while (wpt_count--) {
  132. wpt_tmp = new Waypoint;
  133. /* waypoint name */
  134. wpt_tmp->shortname = gbfgetpstr(mapsend_file_in);
  135. /* waypoint # */
  136. wpt_number = gbfgetint32(mapsend_file_in);
  137. wpt_tmp->longitude = gbfgetdbl(mapsend_file_in);
  138. wpt_tmp->latitude = -gbfgetdbl(mapsend_file_in);
  139. gbfread(&wpt_icon, 1, sizeof(wpt_icon), mapsend_file_in);
  140. if (wpt_icon < 26) {
  141. sprintf(tbuf, "%c", wpt_icon + 'a');
  142. } else {
  143. sprintf(tbuf, "a%c", wpt_icon - 26 + 'a');
  144. }
  145. wpt_tmp->icon_descr = mag_find_descr_from_token(tbuf);
  146. route_add_wpt(rte_head, wpt_tmp);
  147. }
  148. }
  149. }
  150. static void
  151. mapsend_track_read(void)
  152. {
  153. unsigned int trk_count;
  154. int valid;
  155. unsigned char centisecs;
  156. route_head* track_head;
  157. Waypoint* wpt_tmp;
  158. track_head = route_head_alloc();
  159. track_head->rte_name = gbfgetpstr(mapsend_file_in);
  160. track_add_head(track_head);
  161. trk_count = gbfgetuint32(mapsend_file_in);
  162. while (trk_count--) {
  163. wpt_tmp = new Waypoint;
  164. wpt_tmp->longitude = gbfgetdbl(mapsend_file_in);
  165. wpt_tmp->latitude = -gbfgetdbl(mapsend_file_in);
  166. if (mapsend_infile_version < 36) { /* < version 4.0 */
  167. wpt_tmp->altitude = gbfgetint32(mapsend_file_in);
  168. } else {
  169. wpt_tmp->altitude = gbfgetflt(mapsend_file_in);
  170. }
  171. if (wpt_tmp->altitude < unknown_alt + 1) {
  172. wpt_tmp->altitude = unknown_alt;
  173. }
  174. time_t t = gbfgetint32(mapsend_file_in);
  175. valid = gbfgetint32(mapsend_file_in);
  176. /* centiseconds only in >= version 3.0 */
  177. if (mapsend_infile_version >= 34) {
  178. gbfread(&centisecs, 1, 1, mapsend_file_in);
  179. } else {
  180. centisecs = 0;
  181. }
  182. wpt_tmp->SetCreationTime(t, 10 * centisecs);
  183. track_add_wpt(track_head, wpt_tmp);
  184. }
  185. }
  186. static void
  187. mapsend_read(void)
  188. {
  189. mapsend_hdr hdr;
  190. int type;
  191. gbsize_t len;
  192. char buf[3];
  193. /*
  194. * Because of the silly struct packing and the goofy variable-length
  195. * strings, each member has to be read in one at a time. Grrr.
  196. */
  197. len = gbfread(&hdr, 1, sizeof(hdr), mapsend_file_in);
  198. is_fatal(len < sizeof(hdr), MYNAME ": No mapsend or empty file!");
  199. type = le_read16(&hdr.ms_type);
  200. strncpy(buf, hdr.ms_version, 2);
  201. buf[2] = '\0';
  202. mapsend_infile_version = atoi(buf);
  203. switch (type) {
  204. case ms_type_wpt:
  205. mapsend_wpt_read();
  206. break;
  207. case ms_type_track:
  208. mapsend_track_read();
  209. break;
  210. case ms_type_log:
  211. fatal(MYNAME ", GPS logs not supported.\n");
  212. case ms_type_rgn:
  213. fatal(MYNAME ", GPS regions not supported.\n");
  214. default:
  215. fatal(MYNAME ", unknown file type %d\n", type);
  216. }
  217. }
  218. static void
  219. mapsend_waypt_pr(const Waypoint* waypointp)
  220. {
  221. unsigned int c = 0;
  222. double falt;
  223. static int cnt = 0;
  224. QString sn = global_opts.synthesize_shortnames ?
  225. mkshort_from_wpt(mkshort_handle, waypointp) :
  226. waypointp->shortname;
  227. /*
  228. * The format spec doesn't call out the character set of waypoint
  229. * name and description. Empirically, we can see that it's 8859-1,
  230. * but if we create mapsend files containing those, Mapsend becomes
  231. * grumpy uploading the resulting waypoints and being unable to deal
  232. * with the resulting comm errors.
  233. *
  234. * Ironically, our own Magellan serial module strips the "naughty"
  235. * characters, keeping it more in definition with their own serial
  236. * spec. :-)
  237. *
  238. * So we just decompose the utf8 strings to ascii before stuffing
  239. * them into the Mapsend file.
  240. */
  241. QString tmp1 = mkshort(wpt_handle, sn);
  242. gbfputpstr(tmp1, mapsend_file_out);
  243. // This is funny looking to ensure that no more than 30 bytes
  244. // get written to the file.
  245. c = waypointp->description.length();
  246. if (c > 30) {
  247. c = 30;
  248. }
  249. gbfputc(c, mapsend_file_out);
  250. gbfwrite(CSTR(waypointp->description), 1, c, mapsend_file_out);
  251. /* #, icon, status */
  252. gbfputint32(++cnt, mapsend_file_out);
  253. QString iconp;
  254. if (!waypointp->icon_descr.isNull()) {
  255. iconp = mag_find_token_from_descr(waypointp->icon_descr);
  256. if (1 == iconp.size()) {
  257. c = iconp[0].toLatin1() - 'a';
  258. } else {
  259. c = iconp[1].toLatin1() - 'a' + 26;
  260. }
  261. } else {
  262. c = 0;
  263. }
  264. if (get_cache_icon(waypointp)) {
  265. iconp = mag_find_token_from_descr(get_cache_icon(waypointp));
  266. if (1 == iconp.size()) {
  267. c = iconp[0].toLatin1() - 'a';
  268. } else {
  269. c = iconp[1].toLatin1() - 'a' + 26;
  270. }
  271. }
  272. gbfwrite(&c, 1, 1, mapsend_file_out);
  273. gbfputc(1, mapsend_file_out);
  274. falt = waypointp->altitude;
  275. if (falt == unknown_alt) {
  276. falt = 0;
  277. }
  278. gbfputdbl(falt, mapsend_file_out);
  279. gbfputdbl(waypointp->longitude, mapsend_file_out);
  280. gbfputdbl(-waypointp->latitude, mapsend_file_out);
  281. }
  282. static void
  283. mapsend_route_hdr(const route_head* rte)
  284. {
  285. QString rname;
  286. QString r = rte->rte_name;
  287. /* route name -- mapsend really seems to want something here.. */
  288. if (r.isEmpty()) {
  289. rname = "Route";
  290. } else {
  291. rname = CSTRc(rte->rte_name);
  292. }
  293. gbfputpstr(rname, mapsend_file_out);
  294. /* route # */
  295. gbfputint32(rte->rte_num, mapsend_file_out);
  296. /* # of waypoints to follow... */
  297. gbfputint32(rte->rte_waypt_ct, mapsend_file_out);
  298. }
  299. static void
  300. mapsend_noop(const route_head* wp)
  301. {
  302. /* no-op */
  303. }
  304. static void
  305. mapsend_route_disp(const Waypoint* waypointp)
  306. {
  307. unsigned char c;
  308. QString iconp;
  309. route_wp_count++;
  310. /* waypoint name */
  311. gbfputpstr(waypointp->shortname, mapsend_file_out);
  312. /* waypoint number */
  313. gbfputint32(route_wp_count, mapsend_file_out);
  314. gbfputdbl(waypointp->longitude, mapsend_file_out);
  315. gbfputdbl(-waypointp->latitude, mapsend_file_out);
  316. if (!waypointp->icon_descr.isNull()) {
  317. iconp = mag_find_token_from_descr(waypointp->icon_descr);
  318. if (1 == iconp.size()) {
  319. c = iconp[0].toLatin1() - 'a';
  320. } else {
  321. c = iconp[1].toLatin1() - 'a' + 26;
  322. }
  323. } else {
  324. c = 0;
  325. }
  326. gbfwrite(&c, 1, 1, mapsend_file_out);
  327. }
  328. void mapsend_track_hdr(const route_head* trk)
  329. {
  330. /*
  331. * we write mapsend v3.0 tracks as mapsend v2.0 tracks get
  332. * tremendously out of whack time/date wise.
  333. */
  334. const char* verstring = "30";
  335. queue* elem, *tmp;
  336. int i;
  337. mapsend_hdr hdr = {13, {'4','D','5','3','3','3','3','4',' ','M','S'},
  338. {'3','0'}, ms_type_track, {0, 0, 0}
  339. };
  340. switch (trk_version) {
  341. case 20:
  342. verstring = "30";
  343. break;
  344. case 30:
  345. verstring = "34";
  346. break;
  347. case 40:
  348. /* the signature seems to change with the versions, even though it
  349. * shouldn't have according to the document. MapSend V4 doesn't
  350. * like the old version.
  351. */
  352. hdr.ms_signature[7] = '6';
  353. verstring = "36";
  354. break;
  355. default:
  356. fatal("Unknown track version.\n");
  357. break;
  358. }
  359. hdr.ms_version[0] = verstring[0];
  360. hdr.ms_version[1] = verstring[1];
  361. gbfwrite(&hdr, sizeof(hdr), 1, mapsend_file_out);
  362. QString tname = trk->rte_name.isEmpty() ? "Track" : trk->rte_name;
  363. gbfputpstr(tname, mapsend_file_out);
  364. /* total nodes (waypoints) this track */
  365. i = 0;
  366. QUEUE_FOR_EACH(&trk->waypoint_list, elem, tmp) {
  367. i++;
  368. }
  369. gbfputint32(i, mapsend_file_out);
  370. }
  371. void mapsend_track_disp(const Waypoint* wpt)
  372. {
  373. unsigned char c;
  374. int32_t t;
  375. static int last_time;
  376. /*
  377. * Firmware Ver 4.06 (at least) has a defect when it's set for .01km
  378. * tracking that will sometimes result in timestamps in the track
  379. * going BACKWARDS. When mapsend sees this, it (stupidly) advances
  380. * the date by one, ignoring the date on the TRK lines. This looks
  381. * for time travel and just uses the previous time - it's better to
  382. * be thought to be standing still than to be time-travelling!
  383. *
  384. * This is rumoured (but yet unconfirmed) to be fixed in f/w 5.12.
  385. */
  386. t = wpt->GetCreationTime().toTime_t();
  387. if (t < last_time) {
  388. t = last_time;
  389. }
  390. /* x = longitude */
  391. gbfputdbl(wpt->longitude, mapsend_file_out);
  392. /* x = latitude */
  393. gbfputdbl(-wpt->latitude, mapsend_file_out);
  394. /* altitude
  395. * in V4.0+ this field is a float, it was previously an int
  396. */
  397. if (trk_version < 40) {
  398. gbfputint32((int) wpt->altitude, mapsend_file_out);
  399. } else {
  400. gbfputflt((float) wpt->altitude, mapsend_file_out);
  401. }
  402. /* time */
  403. gbfputint32(t, mapsend_file_out);
  404. last_time = t;
  405. /* validity */
  406. gbfputint32(1, mapsend_file_out);
  407. /* 0 centiseconds */
  408. if (trk_version >= 30) {
  409. c = lround(wpt->GetCreationTime().time().msec() / 10.0);
  410. gbfwrite(&c, 1, 1, mapsend_file_out);
  411. }
  412. }
  413. void
  414. mapsend_track_write(void)
  415. {
  416. track_disp_all(mapsend_track_hdr, mapsend_noop, mapsend_track_disp);
  417. }
  418. static void
  419. mapsend_wpt_write(void)
  420. {
  421. mapsend_hdr hdr = {13, {'4','D','5','3','3','3','3','0',' ','M','S'},
  422. {'3', '0'}, ms_type_wpt, {0, 0, 0}
  423. };
  424. int n = 0;
  425. int wpt_count = waypt_count();
  426. if (global_opts.objective == trkdata) {
  427. mapsend_track_write();
  428. } else {
  429. gbfwrite(&hdr, sizeof(hdr), 1, mapsend_file_out);
  430. if (global_opts.objective == wptdata) {
  431. gbfputint32(wpt_count, mapsend_file_out);
  432. waypt_disp_all(mapsend_waypt_pr);
  433. } else if (global_opts.objective == rtedata) {
  434. /* # of points - all routes */
  435. gbfputint32(route_waypt_count(), mapsend_file_out);
  436. /* write points - all routes */
  437. route_disp_all(mapsend_noop, mapsend_noop, mapsend_waypt_pr);
  438. }
  439. n = route_count();
  440. gbfputint32(n, mapsend_file_out);
  441. if (n) {
  442. route_disp_all(mapsend_route_hdr, mapsend_noop, mapsend_route_disp);
  443. }
  444. }
  445. }
  446. ff_vecs_t mapsend_vecs = {
  447. ff_type_file,
  448. FF_CAP_RW_ALL,
  449. mapsend_rd_init,
  450. mapsend_wr_init,
  451. mapsend_rd_deinit,
  452. mapsend_wr_deinit,
  453. mapsend_read,
  454. mapsend_wpt_write,
  455. NULL,
  456. mapsend_args,
  457. CET_CHARSET_ASCII, 0 /* CET-REVIEW */
  458. };