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.

mmo.cc 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527
  1. /*
  2. Support for Memory-Map Navigator Overlay Files (.mmo)
  3. Copyright (C) 2008 Olaf Klein, o.b.klein@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 <QtCore/QHash>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <errno.h>
  21. #define MYNAME "mmo"
  22. // #define MMO_DBG
  23. static char* opt_locked, *opt_visible, *opt_version;
  24. static
  25. arglist_t mmo_args[] = {
  26. {
  27. "locked", &opt_locked, "Write items 'locked' [default no]", "0",
  28. ARGTYPE_BOOL, ARG_NOMINMAX
  29. },
  30. {
  31. "visible", &opt_visible, "Write items 'visible' [default yes]", "1",
  32. ARGTYPE_BOOL, ARG_NOMINMAX
  33. },
  34. {
  35. "ver", &opt_version, "Write files with internal version [n]", NULL,
  36. ARGTYPE_INT, "17", "18"
  37. },
  38. ARG_TERMINATOR
  39. };
  40. typedef struct mmo_data_s {
  41. int objid; /* internal object id */
  42. char* name;
  43. const char* category; /* currently not handled */
  44. gpsdata_type type; /* type of "data" */
  45. time_t ctime;
  46. time_t mtime;
  47. int left; /* number of un-readed route points */
  48. void* data; /* can be a waypoint, a route or a track */
  49. int refct;
  50. struct mmo_data_s** members;
  51. unsigned char visible:1;
  52. unsigned char locked:1;
  53. unsigned char loaded:1;
  54. } mmo_data_t;
  55. static gbfile* fin, *fout;
  56. static int mmo_version;
  57. static int mmo_obj_ct;
  58. static int mmo_object_id;
  59. static uint32_t mmo_filemark;
  60. static uint16_t wpt_object_id;
  61. static uint16_t rte_object_id;
  62. static uint16_t trk_object_id;
  63. static uint16_t cat_object_id;
  64. static uint16_t ico_object_id;
  65. static uint16_t pos_object_id;
  66. static uint16_t txt_object_id;
  67. static gpsdata_type mmo_datatype;
  68. static route_head* mmo_rte;
  69. static QHash<QString, int> category_names;
  70. static QHash<int, QString> icons;
  71. static QHash<int, mmo_data_t*> objects;
  72. static QHash<QString, unsigned> mmobjects;
  73. typedef struct mmo_icon_mapping_s {
  74. const int value;
  75. const char* icon;
  76. } mmo_icon_mapping_t;
  77. /* standard icons; no bitmaps in file */
  78. static const mmo_icon_mapping_t mmo_icon_value_table[] = {
  79. { 0x00, "Dot" },
  80. { 0x01, "House" },
  81. { 0x02, "Fuel" },
  82. { 0x03, "Car" },
  83. { 0x04, "Fish" },
  84. { 0x05, "Boat" },
  85. { 0x06, "Anchor" },
  86. { 0x07, "Wreck" },
  87. { 0x08, "Exit" },
  88. { 0x09, "Skull" },
  89. { 0x0A, "Flag" },
  90. { 0x0B, "Camp" },
  91. { 0x0C, "Man Overboard" },
  92. { 0x0D, "Deer" },
  93. { 0x0E, "First Aid" },
  94. { 0x0F, "Trackback" },
  95. { 0x10, "Tiny dot" },
  96. { 0x11, "Triangle" },
  97. { 0x12, "Square" },
  98. { 0x13, "Circle" },
  99. { 0x14, "Green bouy" },
  100. { 0x15, "Red bouy" },
  101. { 0x16, "Yellow bouy" },
  102. { 0x17, "Geocache" },
  103. { -1, NULL }
  104. };
  105. static const uint32_t obj_type_ico = 0x00;
  106. static const uint32_t obj_type_rte = 0x14;
  107. static const uint32_t obj_type_trk = 0x1E;
  108. #ifdef MMO_DBG
  109. static const uint32_t obj_type_txt = 0x32;
  110. #endif
  111. static const uint32_t obj_type_wpt = 0x3C;
  112. /* helpers */
  113. #ifdef MMO_DBG
  114. static void
  115. dbgprintf(const char* sobj, const char* fmt, ...)
  116. {
  117. va_list args;
  118. va_start(args, fmt);
  119. printf(MYNAME "-%s: ", sobj);
  120. vprintf(fmt, args);
  121. va_end(args);
  122. }
  123. # define DBG(args) dbgprintf args
  124. #else
  125. # define DBG(args) do {} while (0) ;
  126. #endif
  127. static char*
  128. mmo_readstr(void)
  129. {
  130. char* res;
  131. signed int len;
  132. len = (unsigned)gbfgetc(fin);
  133. if (len == 0xFF) {
  134. // Next two bytes are either the length (strings longer than 254 chars)
  135. // or FE then FF (which is -2) meaning a UTF-16 string
  136. len = gbfgetint16(fin);
  137. if (len == -2) {
  138. // read the new length (single byte)
  139. // length is number of "characters" not number of bytes
  140. len = (unsigned)gbfgetc(fin);
  141. if (len > 0) {
  142. unsigned int ch, resbytes=0;
  143. res = (char*) xmalloc(len*2 + 1); // bigger to allow for utf-8 expansion
  144. for (signed int ii = 0; ii<len; ii++) {
  145. char utf8buf[8];
  146. int utf8len;
  147. ch = gbfgetint16(fin);
  148. // convert to utf-8, possibly multiple bytes
  149. utf8len = cet_ucs4_to_utf8(utf8buf, sizeof(utf8buf), ch);
  150. for (signed int jj = 0; jj < utf8len; jj++) {
  151. res[resbytes++] = utf8buf[jj];
  152. }
  153. }
  154. res[resbytes] = '\0';
  155. return res;
  156. }
  157. // length zero is handled below: returns an empty string
  158. } else if (len < 0) {
  159. fatal(MYNAME ": Invalid string length (%d)!\n", len);
  160. }
  161. // positive values of len are for strings longer than 254, handled below:
  162. }
  163. // length zero returns an empty string
  164. res = (char*) xmalloc(len + 1);
  165. res[len] = '\0';
  166. if (len) {
  167. gbfread(res, len, 1, fin);
  168. if (static_cast<size_t>(len) != strlen(res)) {
  169. // strlen requires a size_t, but Microsoft's stupid compiler doesn't
  170. // do C99 %zd. Thanx, Microsoft.
  171. fprintf(stdout, "got len %d but str is '%s' (strlen %d)\n", len, res, (int) strlen(res));
  172. fatal(MYNAME ": Error in file structure!\n");
  173. }
  174. }
  175. return res;
  176. }
  177. static int
  178. mmo_fillbuf2(void* buf, const gbsize_t bufsz, const gbsize_t count, const int need_all)
  179. {
  180. gbsize_t res;
  181. if (count > (unsigned int)bufsz) {
  182. fatal(MYNAME ": Internal error (bufsz too small)!\n");
  183. }
  184. memset(buf, 0xFF, count);
  185. res = gbfread(buf, 1, count, fin);
  186. if (need_all && (res < count)) {
  187. fatal(MYNAME ": Unexpected end of file!\n");
  188. }
  189. return res;
  190. }
  191. #define mmo_fillbuf(a,b,c) mmo_fillbuf2((a),sizeof((a)),(b),(c))
  192. static void
  193. mmo_printbuf(const char* buf, int count, const char* comment)
  194. {
  195. #ifdef MMO_DBG
  196. int i;
  197. printf("%s", comment);
  198. for (i = 0; i < count; i++) {
  199. printf("%02X ", buf[i] & 0xFF);
  200. }
  201. printf("- ");
  202. for (i = 0; i < count; i++)
  203. if (isprint(buf[i])) {
  204. printf("%c", buf[i] & 0xFF);
  205. } else {
  206. printf(".");
  207. }
  208. printf("\n");
  209. fflush(stdout);
  210. #endif
  211. }
  212. /******************************************************************************/
  213. static mmo_data_t*
  214. mmo_register_object(const int objid, const void* ptr, const gpsdata_type type)
  215. {
  216. mmo_data_t* data;
  217. data = (mmo_data_t*) xcalloc(1, sizeof(*data));
  218. data->data = (void*)ptr;
  219. data->visible = 1;
  220. data->locked = 0;
  221. data->type = type;
  222. data->objid = objid;
  223. objects.insert(objid, data);
  224. return data;
  225. }
  226. static int
  227. mmo_get_objid(const void* ptr)
  228. {
  229. foreach(int key, objects.keys()) {
  230. if (objects.value(key)->data == ptr) {
  231. return key;
  232. }
  233. }
  234. return 0;
  235. }
  236. static mmo_data_t*
  237. mmo_get_object(const uint16_t objid)
  238. {
  239. int key;
  240. key = objid | 0x8000;
  241. if (!objects.contains(key)) {
  242. #ifdef MMO_DBG
  243. gbfseek(fin, -2, SEEK_CUR);
  244. int ni, n;
  245. for (ni = 0; (n = gbfgetc(fin)) != EOF; ni++) {
  246. DBG(("mmo_get_object", "%04X %02X %c (%d)\n",
  247. ni, n, n >= 32 && n <= 126 ? (char)n : '.', n));
  248. }
  249. #endif
  250. fatal(MYNAME ": Unregistered object id 0x%04X!\n", objid | 0x8000);
  251. }
  252. return objects.value(key);
  253. }
  254. static Waypoint*
  255. mmo_get_waypt(mmo_data_t* data)
  256. {
  257. data->refct++;
  258. if (data->refct == 1) {
  259. return (Waypoint*)data->data;
  260. } else {
  261. return new Waypoint(*(Waypoint*)data->data);
  262. }
  263. }
  264. static void
  265. mmo_free_object(mmo_data_t* data)
  266. {
  267. if (data->name) {
  268. xfree(data->name);
  269. }
  270. if ((data->type == wptdata) && (data->refct == 0)) {
  271. delete(Waypoint*)data->data;
  272. }
  273. xfree(data);
  274. }
  275. static void
  276. mmo_register_icon(const int id, const char* name)
  277. {
  278. icons.insert(id, QString::fromUtf8(name));
  279. }
  280. static mmo_data_t* mmo_read_object(void);
  281. static void
  282. mmo_end_of_route(mmo_data_t* data)
  283. {
  284. #ifdef MMO_DBG
  285. const char* sobj = "CObjRoute";
  286. #endif
  287. route_head* rte = (route_head*) data->data;
  288. char buf[7];
  289. if (mmo_version >= 0x12) {
  290. mmo_fillbuf(buf, 7, 1);
  291. DBG((sobj, "route data (since 0x12): "));
  292. mmo_printbuf(buf, 7, "");
  293. rte->line_color.bbggrr = le_read32(&buf[0]);
  294. rte->line_color.opacity = 255 - (buf[6] * 51);
  295. DBG((sobj, "color = 0x%06X\n", rte->line_color.bbggrr));
  296. DBG((sobj, "transparency = %d (-> %d)\n", buf[6], rte->line_color.opacity));
  297. DBG((sobj, "for \"%s\" \n", data->name));
  298. }
  299. if (rte->rte_waypt_ct == 0) { /* don't keep empty routes */
  300. route_del_head(rte);
  301. data->data = NULL;
  302. }
  303. }
  304. static void
  305. mmo_read_category(mmo_data_t* data)
  306. {
  307. int marker = gbfgetuint16(fin);
  308. if (marker & 0x8000) {
  309. mmo_data_t* tmp;
  310. DBG(("mmo_read_category", "reading category object\n"));
  311. gbfseek(fin, -2, SEEK_CUR);
  312. tmp = mmo_read_object();
  313. if (data) {
  314. data->category = tmp->name;
  315. }
  316. }
  317. }
  318. static void
  319. mmo_read_CObjIcons(mmo_data_t* data)
  320. {
  321. #ifdef MMO_DBG
  322. const char* sobj = "CObjIcons";
  323. #endif
  324. int icon_id;
  325. uint16_t u16;
  326. DBG((sobj, ":-----------------------------------------------------\n"));
  327. DBG((sobj, "name = \"%s\" [ visible=%s, id=0x%04X ]\n",
  328. data->name, data->visible ? "yes" : "NO", data->objid));
  329. if (mmo_version >= 0x18) {
  330. u16 = gbfgetuint16(fin);
  331. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  332. u16 = gbfgetuint16(fin);
  333. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  334. u16 = gbfgetuint16(fin);
  335. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  336. u16 = gbfgetuint16(fin);
  337. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  338. }
  339. u16 = gbfgetuint16(fin);
  340. (void) u16;
  341. DBG((sobj, "unknown value = 0x%04X\n", u16));
  342. u16 = gbfgetuint16(fin);
  343. DBG((sobj, "unknown value = 0x%04X\n", u16));
  344. u16 = gbfgetuint16(fin);
  345. DBG((sobj, "unknown value = 0x%04X\n", u16));
  346. while ((icon_id = gbfgetuint32(fin))) {
  347. char* name;
  348. (void) gbfgetuint32(fin);
  349. (void) gbfgetuint32(fin);
  350. name = mmo_readstr();
  351. DBG((sobj, "bitmap(0x%08X) = \"%s\"\n", icon_id, name));
  352. mmo_register_icon(icon_id, name);
  353. xfree(name);
  354. // The next four bytes hold the length of the image,
  355. // read them and then skip the image data.
  356. gbfseek(fin, gbfgetuint32(fin), SEEK_CUR);
  357. }
  358. }
  359. static void
  360. mmo_read_CObjWaypoint(mmo_data_t* data)
  361. {
  362. #ifdef MMO_DBG
  363. const char* sobj = "CObjWaypoint";
  364. #endif
  365. Waypoint* wpt;
  366. time_t time;
  367. int rtelinks;
  368. mmo_data_t** rtelink = NULL;
  369. char* str;
  370. char buf[16];
  371. int i, ux;
  372. DBG((sobj, ":-----------------------------------------------------\n"));
  373. DBG((sobj, "name = \"%s\" [ visible=%s, id=0x%04X ]\n",
  374. data->name, data->visible ? "yes" : "NO", data->objid));
  375. data->data = wpt = new Waypoint;
  376. wpt->shortname = QString::fromLatin1(data->name);
  377. time = data->mtime;
  378. if (! time) {
  379. time = data->ctime;
  380. }
  381. if (time > 0) {
  382. wpt->SetCreationTime(time);
  383. }
  384. if (mmo_version >= 0x18) {
  385. uint16_t u16;
  386. u16 = gbfgetuint16(fin);
  387. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  388. u16 = gbfgetuint16(fin);
  389. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  390. u16 = gbfgetuint16(fin);
  391. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  392. u16 = gbfgetuint16(fin);
  393. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  394. (void) u16;
  395. }
  396. wpt->latitude = gbfgetdbl(fin);
  397. wpt->longitude = gbfgetdbl(fin);
  398. DBG((sobj, "trackpoint %d/%d coordinates = %f / %f\n", ctp+1,tp, wpt->latitude, wpt->longitude));
  399. rtelinks = gbfgetuint16(fin);
  400. if (rtelinks > 0) {
  401. rtelink = (mmo_data_t**) xcalloc(sizeof(*rtelink), rtelinks);
  402. DBG((sobj, "rtelinks = %d\n", rtelinks));
  403. for (i = 0; i < rtelinks; i++) {
  404. DBG((sobj, "read rtelink number %d\n", i + 1));
  405. rtelink[i] = mmo_read_object();
  406. }
  407. }
  408. str = mmo_readstr(); /* descr + url */
  409. if (strncmp(str, "_FILE_ ", 7) == 0) {
  410. char* cx, *cend;
  411. cx = lrtrim(str + 7);
  412. cend = strchr(cx, '\n');
  413. if (cend == NULL) {
  414. cend = cx + strlen(cx);
  415. }
  416. {
  417. QString url = QString::fromUtf8(cx, cend-cx).trimmed();
  418. if (!url.isEmpty()) {
  419. wpt->AddUrlLink(url);
  420. }
  421. }
  422. if (*cend++) {
  423. wpt->notes = QString::fromLatin1(cend);
  424. }
  425. if (wpt->HasUrlLink()) {
  426. DBG((sobj, "url = \"%s\"\n", wpt->url));
  427. }
  428. } else if (*str) {
  429. wpt->notes = QString::fromLatin1(str);
  430. }
  431. xfree(str);
  432. if (!wpt->notes.isEmpty()) {
  433. DBG((sobj, "notes = \"%s\"\n", wpt->notes));
  434. }
  435. mmo_fillbuf(buf, 12, 1);
  436. i = le_read32(&buf[8]); /* icon */
  437. if (i != -1) {
  438. if (icons.contains(i)) {
  439. wpt->icon_descr = icons.value(i);
  440. DBG((sobj, "icon = \"%s\"\n", qPrintable(wpt->icon_descr)));
  441. }
  442. #ifdef MMO_DBG
  443. else {
  444. DBG((sobj, "icon not found for 0x%08X\n", i));
  445. }
  446. #endif
  447. }
  448. wpt->proximity = le_read_float(&buf[4]);
  449. if (wpt->proximity) {
  450. wpt->wpt_flags.proximity = 1;
  451. DBG((sobj, "proximity = %f\n", wpt->proximity));
  452. }
  453. str = mmo_readstr(); /* name on gps ??? option ??? */
  454. if (*str) {
  455. wpt->description = wpt->shortname;
  456. wpt->shortname = str;
  457. DBG((sobj, "name on gps = %s\n", str));
  458. } else {
  459. xfree(str);
  460. }
  461. ux = gbfgetuint32(fin);
  462. DBG((sobj, "proximity type = %d\n", ux));
  463. (void) ux;
  464. data->loaded = 1;
  465. if (rtelink) {
  466. xfree(rtelink);
  467. } else {
  468. waypt_add(mmo_get_waypt(data));
  469. }
  470. }
  471. static void
  472. mmo_read_CObjRoute(mmo_data_t* data)
  473. {
  474. #ifdef MMO_DBG
  475. const char* sobj = "CObjRoute";
  476. #endif
  477. int rtept;
  478. route_head* rte;
  479. char buf[16];
  480. int ux;
  481. DBG((sobj, ":-----------------------------------------------------\n"));
  482. DBG((sobj, "name = \"%s\" [ visible=%s, id=0x%04X ]\n",
  483. data->name, data->visible ? "yes" : "NO", data->objid));
  484. data->data = rte = route_head_alloc();
  485. rte->rte_name = data->name;
  486. route_add_head(rte);
  487. if (mmo_version >= 0x18) {
  488. uint16_t u16;
  489. u16 = gbfgetuint16(fin);
  490. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  491. u16 = gbfgetuint16(fin);
  492. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  493. u16 = gbfgetuint16(fin);
  494. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  495. u16 = gbfgetuint16(fin);
  496. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  497. (void) u16;
  498. }
  499. ux = gbfgetc(fin); /* line label */
  500. DBG((sobj, "line label = %d\n", ux));
  501. (void) ux;
  502. data->left = rtept = gbfgetint16(fin);
  503. DBG((sobj, "route has %d point(s)\n", rtept));
  504. if (data->left <= 0) {
  505. if (mmo_version >= 0x12) {
  506. mmo_fillbuf(buf, 7, 1);
  507. }
  508. route_del_head(rte);
  509. data->data = NULL;
  510. return;
  511. }
  512. while (data->left > 0) {
  513. mmo_data_t* tmp;
  514. DBG((sobj, "read next waypoint\n"));
  515. tmp = mmo_read_object();
  516. if (tmp && tmp->data && (tmp->type == wptdata)) {
  517. Waypoint* wpt;
  518. /* FIXME: At this point this waypoint maybe not fully loaded (initialized) !!!
  519. We need a final procedure to handle this !!! */
  520. if (! tmp->loaded) {
  521. wpt = new Waypoint;
  522. wpt->latitude = 0;
  523. wpt->longitude = 0;
  524. xasprintf(&wpt->shortname, "\01%p", tmp);
  525. } else {
  526. wpt = mmo_get_waypt(tmp);
  527. }
  528. route_add_wpt(rte, wpt);
  529. data->left--;
  530. }
  531. }
  532. if (mmo_version > 0x11) {
  533. mmo_end_of_route(data);
  534. }
  535. }
  536. static void
  537. mmo_read_CObjTrack(mmo_data_t* data)
  538. {
  539. #ifdef MMO_DBG
  540. const char* sobj = "CObjTrack";
  541. #endif
  542. int tp, ctp;
  543. route_head* trk;
  544. DBG((sobj, ":-----------------------------------------------------\n"));
  545. DBG((sobj, "name = \"%s\" [ visible=%s, id=0x%04X ]\n",
  546. data->name, data->visible ? "yes" : "NO", data->objid));
  547. trk = route_head_alloc();
  548. trk->rte_name = data->name;
  549. track_add_head(trk);
  550. if (mmo_version >= 0x18) {
  551. uint16_t u16;
  552. u16 = gbfgetuint16(fin);
  553. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  554. u16 = gbfgetuint16(fin);
  555. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  556. u16 = gbfgetuint16(fin);
  557. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  558. u16 = gbfgetuint16(fin);
  559. DBG((sobj, "unknown value = 0x%04X (since 0x18)\n", u16));
  560. (void) u16;
  561. }
  562. tp = gbfgetint16(fin);
  563. DBG((sobj, "track has %d point(s)\n", tp));
  564. for (ctp = 0; ctp < tp; ctp++) {
  565. Waypoint* wpt;
  566. char unk;
  567. wpt = new Waypoint;
  568. wpt->latitude = gbfgetdbl(fin);
  569. wpt->longitude = gbfgetdbl(fin);
  570. DBG((sobj, "coordinates = %f / %f\n", wpt->latitude, wpt->longitude));
  571. unk = gbfgetc(fin);
  572. DBG((sobj, "Unknown = 0x%02X (%d)\n", unk, unk));
  573. wpt->SetCreationTime(gbfgetint32(fin));
  574. wpt->altitude = gbfgetflt(fin);
  575. if (unk != 0) {
  576. uint16_t ux;
  577. ux = gbfgetuint16(fin);
  578. DBG((sobj, "unknown value = 0x%04X (%d)\n", ux, ux));
  579. (void) ux;
  580. if (unk > 1) {
  581. uint16_t ux;
  582. ux = gbfgetuint16(fin);
  583. DBG((sobj, "unknown value = 0x%04X (%d)\n", ux, ux));
  584. (void) ux;
  585. }
  586. }
  587. track_add_wpt(trk, wpt);
  588. }
  589. if (mmo_version > 0) {
  590. uint32_t u32;
  591. u32 = gbfgetuint32(fin); /* Min. update interval */
  592. DBG((sobj, "min. update interval = %d\n", u32));
  593. u32 = gbfgetuint32(fin); /* unknown */
  594. DBG((sobj, "unknown value = 0x%08X (%d)\n", u32, u32));
  595. u32 = gbfgetuint32(fin); /* unknown */
  596. DBG((sobj, "unknown value = 0x%08X (%d)\n", u32, u32));
  597. u32 = gbfgetuint32(fin); /* unknown */
  598. DBG((sobj, "min. update distance = %d\n", u32));
  599. u32 = gbfgetuint32(fin); /* unknown */
  600. DBG((sobj, "track partition interval = %d\n", u32 / 60));
  601. u32 = gbfgetuint32(fin); /* unknown */
  602. DBG((sobj, "unknown value = 0x%08X (%d)\n", u32, u32));
  603. u32 = gbfgetuint32(fin); /* unknown */
  604. DBG((sobj, "tick interval = %d\n", u32 / 60));
  605. trk->line_color.bbggrr = gbfgetuint32(fin); /* rgb color */
  606. trk->line_color.opacity = 255;
  607. DBG((sobj, "color = 0x%06X\n", trk->line_color.bbggrr));
  608. (void) u32;
  609. }
  610. if (mmo_version >= 0x12) {
  611. char u8;
  612. u8 = gbfgetc(fin);
  613. DBG((sobj, "line width = %d - (since 0x12)\n", u8));
  614. u8 = gbfgetc(fin);
  615. DBG((sobj, "line style = %d - (since 0x12)\n", u8));
  616. u8 = gbfgetc(fin);
  617. DBG((sobj, "transparency = %d - (since 0x12)\n", u8));
  618. trk->line_color.opacity = 255 - (u8 * 51);
  619. if (mmo_version >= 0x16) {
  620. uint16_t u16;
  621. char* text;
  622. // XXX ARB was u8 = gbfgetc(fin); but actually a string
  623. text = mmo_readstr();
  624. DBG((sobj, "text = \"%s\"\n", text));
  625. xfree(text);
  626. u16 = gbfgetuint16(fin);
  627. DBG((sobj, "unknown value = 0x%04X (since 0x16)\n", u16));
  628. u16 = gbfgetuint16(fin);
  629. DBG((sobj, "unknown value = 0x%04X (since 0x16)\n", u16));
  630. (void) u16;
  631. }
  632. }
  633. if (trk->rte_waypt_ct == 0) {
  634. track_del_head(trk);
  635. data->data = NULL;
  636. }
  637. }
  638. static void
  639. mmo_read_CObjText(mmo_data_t* data)
  640. {
  641. #ifdef MMO_DBG
  642. const char* sobj = "CObjText";
  643. #endif
  644. char buf[28];
  645. double lat, lon;
  646. char* text, *font;
  647. DBG((sobj, ":-----------------------------------------------------\n"));
  648. DBG((sobj, "name = \"%s\" [ visible=%s, id=0x%04X ]\n",
  649. data->name, data->visible ? "yes" : "NO", data->objid));
  650. lat = gbfgetdbl(fin);
  651. lon = gbfgetdbl(fin);
  652. DBG((sobj, "coordinates = %f / %f\n", lat, lon));
  653. (void) lat;
  654. (void) lon;
  655. text = mmo_readstr();
  656. DBG((sobj, "text = \"%s\"\n", text));
  657. xfree(text);
  658. mmo_fillbuf(buf, 28, 1);
  659. font = mmo_readstr();
  660. DBG((sobj, "font = \"%s\"\n", font));
  661. xfree(font);
  662. mmo_fillbuf(buf, 25, 1);
  663. }
  664. static void
  665. mmo_read_CObjCurrentPosition(mmo_data_t* data)
  666. {
  667. #ifdef MMO_DBG
  668. const char* sobj = "CObjCurrentPosition";
  669. #endif
  670. char buf[24];
  671. double lat, lon;
  672. DBG((sobj, ":-----------------------------------------------------\n"));
  673. DBG((sobj, "name = \"%s\" [ visible=%s, id=0x%04X ]\n",
  674. data->name, data->visible ? "yes" : "NO", data->objid));
  675. lat = gbfgetdbl(fin);
  676. lon = gbfgetdbl(fin);
  677. DBG((sobj, "coordinates = %f / %f\n", lat, lon));
  678. (void) lat;
  679. (void) lon;
  680. mmo_fillbuf(buf, 24, 1);
  681. if (mmo_version >= 0x18) {
  682. mmo_fillbuf(buf, 8, 1); // XXX ARB read an extra 8
  683. }
  684. if (mmo_version >= 0x14) {
  685. char* name;
  686. name = mmo_readstr();
  687. DBG((sobj, "name = \"%s\"\n", name));
  688. xfree(name);
  689. // XXX ARB was just: mmo_fillbuf(buf, 13, 1);
  690. // but actually it's string/long/string/long/long
  691. (void) gbfgetuint32(fin);
  692. name = mmo_readstr();
  693. DBG((sobj, "name = \"%s\"\n", name));
  694. xfree(name);
  695. (void) gbfgetuint32(fin);
  696. (void) gbfgetuint32(fin);
  697. }
  698. }
  699. static mmo_data_t*
  700. mmo_read_object(void)
  701. {
  702. int objid;
  703. mmo_data_t* data = NULL;
  704. // There are three cases:
  705. // a new object of a type that has not occurred previously in this file;
  706. // a new object; or
  707. // a back reference to an object that appears earlier in the file.
  708. objid = gbfgetuint16(fin);
  709. if (objid == 0xFFFF) {
  710. uint16_t version;
  711. char* sobj;
  712. int len;
  713. DBG(("mmo_read_object", "Registering new object type\n"));
  714. objid = mmo_object_id++;
  715. version = gbfgetuint16(fin);
  716. is_fatal(version != mmo_version, MYNAME ": Invalid version identifier!\n");
  717. len = gbfgetint16(fin);
  718. sobj = (char*) xmalloc(len + 1);
  719. sobj[len] = '\0';
  720. gbfread(sobj, len, 1, fin);
  721. DBG(("mmo_read_object", "%s\n", sobj));
  722. if (strcmp(sobj, "CObjIcons") == 0) {
  723. ico_object_id = objid;
  724. } else if (strcmp(sobj, "CCategory") == 0) {
  725. cat_object_id = objid;
  726. } else if (strcmp(sobj, "CObjWaypoint") == 0) {
  727. wpt_object_id = objid;
  728. } else if (strcmp(sobj, "CObjRoute") == 0) {
  729. rte_object_id = objid;
  730. } else if (strcmp(sobj, "CObjTrack") == 0) {
  731. trk_object_id = objid;
  732. } else if (strcmp(sobj, "CObjCurrentPosition") == 0) {
  733. pos_object_id = objid;
  734. } else if (strcmp(sobj, "CObjText") == 0) {
  735. txt_object_id = objid;
  736. } else {
  737. fatal(MYNAME ": Unknown Object \"%s\"!\n", sobj);
  738. }
  739. xfree(sobj);
  740. }
  741. DBG(("mmo_read_object", "objid = 0x%04X\n", objid));
  742. if (objid & 0x8000) {
  743. data = mmo_register_object(mmo_object_id++, NULL, (gpsdata_type)0);
  744. data->name = mmo_readstr();
  745. if (objid != cat_object_id) {
  746. uint32_t obj_type;
  747. data->ctime = gbfgetuint32(fin);
  748. data->mtime = gbfgetuint32(fin);
  749. data->locked = gbfgetc(fin);
  750. data->visible = gbfgetc(fin);
  751. obj_type = gbfgetuint32(fin);
  752. (void) obj_type;
  753. #ifdef MMO_DBG
  754. uint32_t expected_type = 0xFFFFFFFF;
  755. if (objid == ico_object_id) {
  756. expected_type = obj_type_ico;
  757. } else if (objid == trk_object_id) {
  758. expected_type = obj_type_trk;
  759. } else if (objid == wpt_object_id) {
  760. expected_type = obj_type_wpt;
  761. } else if (objid == rte_object_id) {
  762. expected_type = obj_type_rte;
  763. } else if (objid == txt_object_id) {
  764. expected_type = obj_type_txt;
  765. }
  766. if (mmo_version >= 0x18) {
  767. expected_type <<= 24;
  768. }
  769. DBG(("mmo_read_object", "object type = 0x%08X\n", obj_type));
  770. if (obj_type != expected_type) {
  771. DBG(("mmo_read_object", " expected 0x%08X\n", expected_type));
  772. }
  773. #endif
  774. if (objid != ico_object_id) {
  775. mmo_read_category(data);
  776. }
  777. DBG(("mmo_read_object", "Category : %s\n",
  778. data->category ? data->category : "[No category]"));
  779. }
  780. if (objid == cat_object_id) ; /* do nothing */
  781. else if (objid == ico_object_id) {
  782. mmo_read_CObjIcons(data);
  783. } else if (objid == trk_object_id) {
  784. data->type = trkdata;
  785. mmo_read_CObjTrack(data);
  786. } else if (objid == wpt_object_id) {
  787. data->type = wptdata;
  788. mmo_read_CObjWaypoint(data);
  789. } else if (objid == rte_object_id) {
  790. data->type = rtedata;
  791. mmo_read_CObjRoute(data);
  792. } else if (objid == pos_object_id) {
  793. mmo_read_CObjCurrentPosition(data);
  794. } else if (objid == txt_object_id) {
  795. mmo_read_CObjText(data);
  796. } else {
  797. fatal(MYNAME ": Unregistered Object-ID 0x%04X\n", objid);
  798. }
  799. } else {
  800. data = mmo_get_object(objid);
  801. }
  802. return data;
  803. }
  804. static void
  805. mmo_finalize_rtept_cb(const Waypoint* wptref)
  806. {
  807. Waypoint* wpt = (Waypoint*)wptref;
  808. if ((wpt->shortname[0] == 1) && (wpt->latitude == 0) && (wpt->longitude == 0)) {
  809. mmo_data_t* data;
  810. Waypoint* wpt2;
  811. // This code path isn't tested in anything we have and I have No Idea
  812. // what it was trying to do. Throw a hard error to force the hand of
  813. // getting a sample file.
  814. abort();
  815. #if OLD
  816. sscanf(wpt->shortname + 1, "%p", &data);
  817. #endif
  818. wpt2 = (Waypoint*)data->data;
  819. wpt->latitude = wpt2->latitude;
  820. wpt->longitude = wpt2->longitude;
  821. wpt->shortname = wpt2->shortname;
  822. wpt->description = wpt2->description;
  823. wpt->notes = (wpt2->notes);
  824. if (wpt2->HasUrlLink()) {
  825. UrlLink l = wpt2->GetUrlLink();
  826. wpt->notes = l.url_;
  827. }
  828. wpt->proximity = wpt2->proximity;
  829. wpt->wpt_flags.proximity = wpt2->wpt_flags.proximity;
  830. if (!wpt2->icon_descr.isNull()) {
  831. wpt->icon_descr = wpt2->icon_descr;
  832. }
  833. }
  834. }
  835. /*******************************************************************************
  836. * %%% global callbacks called by gpsbabel main process %%% *
  837. *******************************************************************************/
  838. static void
  839. mmo_rd_init(const QString& fname)
  840. {
  841. int i;
  842. fin = gbfopen_le(fname, "rb", MYNAME);
  843. ico_object_id = pos_object_id = txt_object_id = cat_object_id = 0;
  844. wpt_object_id = rte_object_id = trk_object_id = 0;
  845. mmo_object_id = 0x8001;
  846. i = 0;
  847. while (mmo_icon_value_table[i].icon) {
  848. mmo_register_icon(mmo_icon_value_table[i].value, mmo_icon_value_table[i].icon);
  849. i++;
  850. }
  851. }
  852. static void
  853. mmo_rd_deinit(void)
  854. {
  855. route_disp_session(curr_session(), NULL, NULL, mmo_finalize_rtept_cb);
  856. icons.clear();
  857. foreach(int k, objects.keys()) {
  858. mmo_free_object(objects.value(k));
  859. }
  860. objects.clear();
  861. gbfclose(fin);
  862. }
  863. static void
  864. mmo_read(void)
  865. {
  866. #ifdef MMO_DBG
  867. const char* sobj = "main";
  868. #endif
  869. gbfile* fx;
  870. int i;
  871. /* copy file to memory stream (needed for seek-ops and piped commands) */
  872. DBG(("main", "loading file \"%s\".\n", fin->name));
  873. fx = gbfopen(NULL, "wb", MYNAME);
  874. gbfcopyfrom(fx, fin, 0x7FFFFFFF);
  875. gbfrewind(fx);
  876. gbfclose(fin);
  877. fin = fx;
  878. mmo_obj_ct = gbfgetuint16(fin);
  879. DBG((sobj, "number of objects = %d\n", mmo_obj_ct));
  880. i = gbfgetuint16(fin);
  881. if (i != 0xFFFF) {
  882. fatal(MYNAME ": Marker not equal to 0xFFFF!\n");
  883. }
  884. mmo_version = gbfgetuint16(fin);
  885. DBG((sobj, "version = 0x%02X\n", mmo_version));
  886. mmo_filemark = 0xFFFF0000UL | be_read16(&mmo_version);
  887. DBG((sobj, "filemark = 0x%08X\n", mmo_filemark));
  888. gbfseek(fin, -4, SEEK_CUR);
  889. while (! gbfeof(fin)) { /* main read loop */
  890. (void) mmo_read_object();
  891. }
  892. #ifdef MMO_DBG
  893. printf("\n" MYNAME ":---------------------------------------\n");
  894. printf(MYNAME ": EOF reached, nice!!!\n");
  895. printf(MYNAME ": =======================================\n\n");
  896. #endif
  897. }
  898. /**************************************************************************/
  899. static void
  900. mmo_register_category_names(const QString& name)
  901. {
  902. category_names.insert(name, mmo_object_id);
  903. }
  904. static void
  905. mmo_writestr(const char* str)
  906. {
  907. int ii, topbitset = 0;
  908. int len = strlen(str);
  909. // see if there's any utf-8 multi-byte chars
  910. for (ii = 0; ii < len; ii++) {
  911. if (str[ii] & 0x80) {
  912. topbitset = 1;
  913. break;
  914. }
  915. }
  916. // Old version can't handle utf-16
  917. // XXX ARB check which version number can, just guessed at 0x12
  918. if (mmo_version < 0x12) {
  919. topbitset = 0;
  920. }
  921. // XXX ARB need to convert UTF-8 into UTF-16
  922. if (topbitset) {
  923. gbfputc(0xFF, fout); // means two-byte length follows
  924. gbfputc(0xFE, fout); // means utf-16 little-endian string follows
  925. gbfputc(0xFF, fout); // ditto
  926. gbfputc(len, fout);
  927. } else if (len > 254) {
  928. len = len & 0x7FFF;
  929. gbfputc(0xFF, fout); // means two-byte length follows
  930. gbfputint16(len, fout);
  931. } else {
  932. gbfputc(len, fout);
  933. }
  934. if (len) {
  935. if (topbitset) {
  936. int utf16val;
  937. int utf16len;
  938. for (ii=0; ii<len; ii++) {
  939. cet_utf8_to_ucs4(str+ii, &utf16len, &utf16val);
  940. // this format only handles two-byte encoding
  941. // so only write the lower two bytes
  942. gbfputint16(utf16val & 0xffff, fout);
  943. // if utf8 char was multi-byte then skip them
  944. ii += (utf16len - 1);
  945. }
  946. } else {
  947. gbfwrite(str, len, 1, fout);
  948. }
  949. }
  950. }
  951. static void
  952. mmo_writestr(const QString& str)
  953. {
  954. // If UTF-8 is used instgead of Latin1, we fail in weird ways.
  955. mmo_writestr(str.toLatin1().constData());
  956. }
  957. static void
  958. mmo_enum_waypt_cb(const Waypoint* wpt)
  959. {
  960. mmo_obj_ct++;
  961. }
  962. static void
  963. mmo_enum_route_cb(const route_head* rte)
  964. {
  965. if (rte->rte_waypt_ct > 0) {
  966. mmo_obj_ct++;
  967. }
  968. }
  969. static int
  970. mmo_write_obj_mark(const char* sobj, const char* name)
  971. {
  972. QString key = QString::fromUtf8(sobj);
  973. uint16_t nr;
  974. int res;
  975. if (mmobjects.contains(key)) {
  976. nr = mmobjects.value(key);
  977. gbfputuint16(nr, fout);
  978. } else {
  979. mmo_object_id++;
  980. DBG(("write", "object \"%s\", registered type \"%s\" (id = 0x%04X)\n",
  981. name, sobj, mmo_object_id));
  982. mmobjects.insert(key, mmo_object_id);
  983. gbfputuint32(mmo_filemark, fout);
  984. gbfputuint16(strlen(sobj), fout);
  985. gbfwrite(sobj, strlen(sobj), 1, fout);
  986. }
  987. mmo_object_id++;
  988. res = mmo_object_id;
  989. mmo_writestr(name);
  990. return res;
  991. }
  992. static void
  993. mmo_write_category(const char* sobj, const char* name)
  994. {
  995. uint16_t nr;
  996. QString key = QString::fromUtf8(name);
  997. if (category_names.contains(key)) {
  998. nr = category_names.value(key);
  999. gbfputuint16(nr & 0x7FFF, fout);
  1000. } else {
  1001. mmo_write_obj_mark(sobj, name);
  1002. mmo_register_category_names(key);
  1003. }
  1004. }
  1005. static int
  1006. mmo_write_obj_head(const char* sobj, const char* name, const time_t ctime,
  1007. const uint32_t obj_type)
  1008. {
  1009. int res;
  1010. res = mmo_write_obj_mark(sobj, name);
  1011. gbfputuint32(ctime, fout);
  1012. gbfputuint32(ctime, fout);
  1013. gbfputc(*opt_locked, fout);
  1014. gbfputc(*opt_visible, fout);
  1015. gbfputuint32(obj_type, fout);
  1016. return res;
  1017. }
  1018. static void
  1019. mmo_write_wpt_cb(const Waypoint* wpt)
  1020. {
  1021. QString str;
  1022. QString cx;
  1023. int objid;
  1024. time_t time;
  1025. int icon = 0;
  1026. mmo_data_t* data;
  1027. time = wpt->GetCreationTime().toTime_t();
  1028. if (time < 0) {
  1029. time = 0;
  1030. }
  1031. if (mmo_datatype == trkdata) {
  1032. gbfputdbl(wpt->latitude, fout);
  1033. gbfputdbl(wpt->longitude, fout);
  1034. gbfputc(0, fout);
  1035. gbfputuint32(time, fout);
  1036. if (wpt->altitude != unknown_alt) {
  1037. gbfputflt(wpt->altitude, fout);
  1038. } else {
  1039. gbfputflt(0, fout);
  1040. }
  1041. return;
  1042. }
  1043. DBG(("write", "waypoint \"%s\"\n", wpt->shortname ? wpt->shortname : "Mark"));
  1044. objid = mmo_write_obj_head("CObjWaypoint",
  1045. wpt->shortname.isEmpty() ? "Mark" : CSTRc(wpt->shortname), time, obj_type_wpt);
  1046. data = mmo_register_object(objid, wpt, wptdata);
  1047. data->refct = 1;
  1048. mmo_write_category("CCategory", (mmo_datatype == rtedata) ? "Waypoints" : "Marks");
  1049. gbfputdbl(wpt->latitude, fout);
  1050. gbfputdbl(wpt->longitude, fout);
  1051. if (mmo_datatype == rtedata) {
  1052. int i = mmo_get_objid(mmo_rte);
  1053. gbfputuint16(1, fout); /* two extra bytes */
  1054. gbfputuint16(i & 0x7FFF, fout);
  1055. } else {
  1056. gbfputuint16(0, fout); /* extra bytes */
  1057. }
  1058. if (wpt->HasUrlLink()) {
  1059. str = "_FILE_ ";
  1060. UrlLink l = wpt->GetUrlLink();
  1061. str += l.url_;
  1062. str += "\n";
  1063. }
  1064. cx = wpt->notes;
  1065. if (cx == NULL) {
  1066. cx = wpt->description;
  1067. }
  1068. if (cx != NULL) {
  1069. char* kml = NULL;
  1070. if (strcmp(wpt->session->name, "kml") == 0) {
  1071. utf_string tmp;
  1072. tmp.utfstring = cx;
  1073. tmp.is_html = 1;
  1074. cx = kml = strip_html(&tmp);
  1075. }
  1076. str += cx;
  1077. if (kml) {
  1078. xfree(kml);
  1079. }
  1080. }
  1081. mmo_writestr(str);
  1082. gbfputuint32(0x01, fout);
  1083. if WAYPT_HAS(wpt, proximity) {
  1084. gbfputflt((int) wpt->proximity, fout);
  1085. } else {
  1086. gbfputflt(0, fout);
  1087. }
  1088. if (!wpt->icon_descr.isNull()) {
  1089. int i = 0;
  1090. while (mmo_icon_value_table[i].icon) {
  1091. if (wpt->icon_descr.compare(mmo_icon_value_table[i].icon, Qt::CaseInsensitive) == 0) {
  1092. icon = mmo_icon_value_table[i].value;
  1093. break;
  1094. }
  1095. i++;
  1096. }
  1097. }
  1098. gbfputuint32(icon, fout);
  1099. mmo_writestr(""); /* name on gps */
  1100. gbfputuint32(0x00, fout);
  1101. }
  1102. static void
  1103. mmo_write_rte_head_cb(const route_head* rte)
  1104. {
  1105. int objid;
  1106. queue* elem, *tmp;
  1107. time_t time = 0x7FFFFFFF;
  1108. if (rte->rte_waypt_ct <= 0) {
  1109. return;
  1110. }
  1111. mmo_rte = (route_head*)rte;
  1112. QUEUE_FOR_EACH(&rte->waypoint_list, elem, tmp) {
  1113. Waypoint* wpt = (Waypoint*)elem;
  1114. QDateTime t = wpt->GetCreationTime();
  1115. if ((t.isValid()) && (t.toTime_t() < time)) {
  1116. time = t.toTime_t();
  1117. }
  1118. }
  1119. if (time == 0x7FFFFFFF) {
  1120. time = gpsbabel_time;
  1121. }
  1122. objid = mmo_write_obj_head("CObjRoute",
  1123. rte->rte_name.isEmpty() ? "Route" : CSTRc(rte->rte_name), time, obj_type_rte);
  1124. mmo_register_object(objid, rte, rtedata);
  1125. mmo_write_category("CCategory", "Route");
  1126. gbfputc(0, fout); /* unknown */
  1127. gbfputuint16(rte->rte_waypt_ct, fout);
  1128. }
  1129. static void
  1130. mmo_write_rte_tail_cb(const route_head* rte)
  1131. {
  1132. queue* elem, *tmp;
  1133. if (rte->rte_waypt_ct <= 0) {
  1134. return;
  1135. }
  1136. DBG(("write", "route with %d point(s).\n", rte->rte_waypt_ct));
  1137. if (mmo_version >= 0x12) {
  1138. if (rte->line_color.bbggrr < 0) {
  1139. gbfputuint32(0xFF, fout); /* color; default red */
  1140. gbfputc(0x01, fout); /* Line width "normal" */
  1141. gbfputc(0x00, fout); /* Line style "solid"*/
  1142. gbfputc(0x00, fout); /* Transparency "Opaque" */
  1143. } else {
  1144. gbfputuint32(rte->line_color.bbggrr, fout); /* color */
  1145. gbfputc(0x01, fout); /* Line width "normal" */
  1146. gbfputc(0x00, fout); /* Line style "solid"*/
  1147. gbfputc((255 - rte->line_color.opacity) / 51, fout); /* Transparency "Opaque" */
  1148. }
  1149. }
  1150. QUEUE_FOR_EACH(&rte->waypoint_list, elem, tmp) {
  1151. Waypoint* wpt = (Waypoint*)elem;
  1152. int objid = mmo_get_objid(wpt);
  1153. gbfputuint16(objid & 0x7FFF, fout);
  1154. }
  1155. }
  1156. static void
  1157. mmo_write_trk_head_cb(const route_head* trk)
  1158. {
  1159. int objid;
  1160. if (trk->rte_waypt_ct <= 0) {
  1161. return;
  1162. }
  1163. objid = mmo_write_obj_head("CObjTrack",
  1164. trk->rte_name.isEmpty() ? "Track" : CSTRc(trk->rte_name), gpsbabel_time, obj_type_trk);
  1165. mmo_write_category("CCategory", "Track");
  1166. gbfputuint16(trk->rte_waypt_ct, fout);
  1167. mmo_register_object(objid, trk, trkdata);
  1168. }
  1169. static void
  1170. mmo_write_trk_tail_cb(const route_head* trk)
  1171. {
  1172. if (trk->rte_waypt_ct <= 0) {
  1173. return;
  1174. }
  1175. gbfputuint32(0x0A, fout); /* Min. update interval */
  1176. gbfputflt(0, fout);
  1177. gbfputflt(0, fout);
  1178. gbfputuint32(0x0F, fout); /* Min. update distance */
  1179. gbfputuint32(0xE10, fout); /* Track partition interval */
  1180. gbfputuint32(0x00, fout); /* ??? */
  1181. gbfputuint32(0x12C, fout);
  1182. if (trk->line_color.bbggrr < 0) {
  1183. gbfputuint32(0xFF0000, fout); /* color; default blue */
  1184. if (mmo_version >= 0x12) {
  1185. gbfputc(0x01, fout); /* Line width "normal" */
  1186. gbfputc(0x00, fout); /* Line style "solid"*/
  1187. gbfputc(0x00, fout); /* Transparency "Opaque" */
  1188. }
  1189. } else {
  1190. gbfputuint32(trk->line_color.bbggrr, fout); /* color */
  1191. if (mmo_version >= 0x12) {
  1192. gbfputc(0x01, fout); /* Line width "normal" */
  1193. gbfputc(0x00, fout); /* Line style "solid"*/
  1194. gbfputc((255 - trk->line_color.opacity) / 51, fout); /* Transparency "Opaque" */
  1195. }
  1196. }
  1197. }
  1198. /**************************************************************************/
  1199. static void
  1200. mmo_wr_init(const QString& fname)
  1201. {
  1202. fout = gbfopen_le(fname, "wb", MYNAME);
  1203. mmo_object_id = 0x8000;
  1204. mmo_obj_ct = 1; /* ObjIcons always present */
  1205. mmo_version = 0x12; /* by default we write as version 0x12 */
  1206. if (opt_version) {
  1207. while (isspace(*opt_version)) {
  1208. opt_version++;
  1209. }
  1210. errno = 0;
  1211. mmo_version = strtol(opt_version, NULL, 0);
  1212. if (errno || ((mmo_version != 0x11) && (mmo_version != 0x12))) {
  1213. fatal(MYNAME ": Unsupported version identifier (%s)!\n", opt_version);
  1214. }
  1215. }
  1216. DBG(("write", "version = 0x%02X\n", mmo_version));
  1217. mmo_filemark = 0xFFFFUL | (mmo_version << 16);
  1218. }
  1219. static void
  1220. mmo_wr_deinit(void)
  1221. {
  1222. mmobjects.clear();
  1223. category_names.clear();
  1224. foreach(int k, objects.keys()) {
  1225. mmo_free_object(objects.value(k));
  1226. }
  1227. objects.clear();
  1228. gbfclose(fout);
  1229. }
  1230. static void
  1231. mmo_write(void)
  1232. {
  1233. int i;
  1234. /* find out number of objects we have to write */
  1235. waypt_disp_all(mmo_enum_waypt_cb);
  1236. route_disp_all(mmo_enum_route_cb, NULL, mmo_enum_waypt_cb);
  1237. track_disp_all(mmo_enum_route_cb, NULL, NULL);
  1238. gbfputuint16(mmo_obj_ct, fout);
  1239. mmo_write_obj_head("CObjIcons", "Unnamed object", gpsbabel_time, obj_type_ico);
  1240. for (i = 0; i < 5; i++) {
  1241. gbfputuint16(0, fout);
  1242. }
  1243. mmo_datatype = wptdata;
  1244. waypt_disp_all(mmo_write_wpt_cb);
  1245. mmo_datatype = rtedata;
  1246. route_disp_all(mmo_write_rte_head_cb, mmo_write_rte_tail_cb, mmo_write_wpt_cb);
  1247. mmo_datatype = trkdata;
  1248. track_disp_all(mmo_write_trk_head_cb, mmo_write_trk_tail_cb, mmo_write_wpt_cb);
  1249. }
  1250. /**************************************************************************/
  1251. ff_vecs_t mmo_vecs = {
  1252. ff_type_file,
  1253. FF_CAP_RW_ALL, /* read and write waypoints, tracks and routes*/
  1254. mmo_rd_init,
  1255. mmo_wr_init,
  1256. mmo_rd_deinit,
  1257. mmo_wr_deinit,
  1258. mmo_read,
  1259. mmo_write,
  1260. NULL,
  1261. mmo_args,
  1262. CET_CHARSET_MS_ANSI, 0
  1263. };
  1264. /**************************************************************************/