Subversion Repositories public

Rev

Rev 93 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "garmin.h"


#define GETU16(x) do { x = get_uint16(*pos);  *pos += 2; } while ( 0 )
#define GETS16(x) do { x = get_sint16(*pos);  *pos += 2; } while ( 0 )
#define GETU32(x) do { x = get_uint32(*pos);  *pos += 4; } while ( 0 )
#define GETS32(x) do { x = get_sint32(*pos);  *pos += 4; } while ( 0 )
#define GETF32(x) do { x = get_float32(*pos); *pos += 4; } while ( 0 )
#define GETF64(x) do { x = get_float64(*pos); *pos += 8; } while ( 0 )
#define GETPOS(x) do { GETS32((x).lat); GETS32((x).lon); } while ( 0 )
#define GETRPT(x) do { GETF64((x).lat); GETF64((x).lon); } while ( 0 )
#define GETVST(x) x = get_vstring(pos)
#define GETU8(x)  x = *(*pos)++
#define SKIP(x)   do { memset(*pos,0,x); *pos += x; }      while ( 0 )

#define GETSTR(x)                                                      \
  do {                                                                 \
    memcpy(x,*pos,sizeof(x)-1); x[sizeof(x)-1] = 0; *pos += sizeof(x); \
  } while ( 0 )


/* --------------------------------------------------------------------------*/
/* 7.4.1   D100                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d100 ( D100 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
}


/* --------------------------------------------------------------------------*/
/* 7.4.2   D101                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d101 ( D101 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETF32(wpt->dst);
  GETU8(wpt->smbl);
}


/* --------------------------------------------------------------------------*/
/* 7.4.3   D102                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d102 ( D102 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETF32(wpt->dst);
  GETU16(wpt->smbl);
}


/* --------------------------------------------------------------------------*/
/* 7.4.4   D103                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d103 ( D103 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETU8(wpt->smbl);
  GETU8(wpt->dspl);
}


/* --------------------------------------------------------------------------*/
/* 7.4.5   D104                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d104 ( D104 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETF32(wpt->dst);
  GETU16(wpt->smbl);
  GETU8(wpt->dspl);
}


/* --------------------------------------------------------------------------*/
/* 7.4.6   D105                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d105 ( D105 * wpt, uint8 ** pos )
{
  GETPOS(wpt->posn);
  GETU16(wpt->smbl);
  GETVST(wpt->wpt_ident);
}


/* --------------------------------------------------------------------------*/
/* 7.4.7   D106                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d106 ( D106 * wpt, uint8 ** pos )
{
  GETU8(wpt->wpt_class);
  GETSTR(wpt->subclass);
  GETPOS(wpt->posn);
  GETU16(wpt->smbl);
  GETVST(wpt->wpt_ident);
  GETVST(wpt->lnk_ident);
}


/* --------------------------------------------------------------------------*/
/* 7.4.8   D107                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d107 ( D107 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETU8(wpt->smbl);
  GETU8(wpt->dspl);
  GETF32(wpt->dst);
  GETU8(wpt->color);
}


/* --------------------------------------------------------------------------*/
/* 7.4.9   D108                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d108 ( D108 * wpt, uint8 ** pos )
{
  GETU8(wpt->wpt_class);
  GETU8(wpt->color);
  GETU8(wpt->dspl);
  GETU8(wpt->attr);
  GETU16(wpt->smbl);
  GETSTR(wpt->subclass);
  GETPOS(wpt->posn);
  GETF32(wpt->alt);
  GETF32(wpt->dpth);
  GETF32(wpt->dist);
  GETSTR(wpt->state);
  GETSTR(wpt->cc);
  GETVST(wpt->ident);
  GETVST(wpt->comment);
  GETVST(wpt->facility);
  GETVST(wpt->city);
  GETVST(wpt->addr);
  GETVST(wpt->cross_road);
}


/* --------------------------------------------------------------------------*/
/* 7.4.10  D109                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d109 ( D109 * wpt, uint8 ** pos )
{
  GETU8(wpt->dtyp);
  GETU8(wpt->wpt_class);
  GETU8(wpt->dspl_color);
  GETU8(wpt->attr);
  GETU16(wpt->smbl);
  GETSTR(wpt->subclass);
  GETPOS(wpt->posn);
  GETF32(wpt->alt);
  GETF32(wpt->dpth);
  GETF32(wpt->dist);
  GETSTR(wpt->state);
  GETSTR(wpt->cc);
  GETU32(wpt->ete);
  GETVST(wpt->ident);
  GETVST(wpt->comment);
  GETVST(wpt->facility);
  GETVST(wpt->city);
  GETVST(wpt->addr);
  GETVST(wpt->cross_road);
}


/* --------------------------------------------------------------------------*/
/* 7.4.11  D110                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d110 ( D110 * wpt, uint8 ** pos )
{
  GETU8(wpt->dtyp);
  GETU8(wpt->wpt_class);
  GETU8(wpt->dspl_color);
  GETU8(wpt->attr);
  GETU16(wpt->smbl);
  GETSTR(wpt->subclass);
  GETPOS(wpt->posn);
  GETF32(wpt->alt);
  GETF32(wpt->dpth);
  GETF32(wpt->dist);
  GETSTR(wpt->state);
  GETSTR(wpt->cc);
  GETU32(wpt->ete);
  GETF32(wpt->temp);
  GETU32(wpt->time);
  GETU16(wpt->wpt_cat);
  GETVST(wpt->ident);
  GETVST(wpt->comment);
  GETVST(wpt->facility);
  GETVST(wpt->city);
  GETVST(wpt->addr);
  GETVST(wpt->cross_road);
}


/* --------------------------------------------------------------------------*/
/* 7.4.12  D120                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d120 ( D120 * cat, uint8 ** pos )
{
  GETSTR(cat->name);
}


/* --------------------------------------------------------------------------*/
/* 7.4.13  D150                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d150 ( D150 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETSTR(wpt->cc);
  GETU8(wpt->wpt_class);
  GETPOS(wpt->posn);
  GETS16(wpt->alt);
  GETSTR(wpt->city);
  GETSTR(wpt->state);
  GETSTR(wpt->name);
  GETSTR(wpt->cmnt);
}


/* --------------------------------------------------------------------------*/
/* 7.4.14  D151                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d151 ( D151 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETF32(wpt->dst);
  GETSTR(wpt->name);
  GETSTR(wpt->city);
  GETSTR(wpt->state);
  GETS16(wpt->alt);
  GETSTR(wpt->cc);
  SKIP(1);
  GETU8(wpt->wpt_class);
}


/* --------------------------------------------------------------------------*/
/* 7.4.15  D152                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d152 ( D152 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETF32(wpt->dst);
  GETSTR(wpt->name);
  GETSTR(wpt->city);
  GETSTR(wpt->state);
  GETS16(wpt->alt);
  GETSTR(wpt->cc);
  SKIP(1);
  GETU8(wpt->wpt_class);
}


/* --------------------------------------------------------------------------*/
/* 7.4.16  D154                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d154 ( D154 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETF32(wpt->dst);
  GETSTR(wpt->name);
  GETSTR(wpt->city);
  GETSTR(wpt->state);
  GETS16(wpt->alt);
  GETSTR(wpt->cc);
  SKIP(1);
  GETU8(wpt->wpt_class);
  GETU16(wpt->smbl);
}


/* --------------------------------------------------------------------------*/
/* 7.4.17  D155                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d155 ( D155 * wpt, uint8 ** pos )
{
  GETSTR(wpt->ident);
  GETPOS(wpt->posn);
  SKIP(4);
  GETSTR(wpt->cmnt);
  GETF32(wpt->dst);
  GETSTR(wpt->name);
  GETSTR(wpt->city);
  GETSTR(wpt->state);
  GETS16(wpt->alt);
  GETSTR(wpt->cc);
  SKIP(1);
  GETU8(wpt->wpt_class);
  GETU16(wpt->smbl);  
  GETU8(wpt->dspl);
}


/* --------------------------------------------------------------------------*/
/* 7.4.18  D200                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d200 ( D200 * hdr, uint8 ** pos )
{
  GETU8(*hdr);
}


/* --------------------------------------------------------------------------*/
/* 7.4.19  D201                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d201 ( D201 * hdr, uint8 ** pos )
{
  GETU8(hdr->nmbr);
  GETSTR(hdr->cmnt);
}


/* --------------------------------------------------------------------------*/
/* 7.4.20  D202                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d202 ( D202 * hdr, uint8 ** pos )
{
  GETVST(hdr->rte_ident);
}


/* --------------------------------------------------------------------------*/
/* 7.4.21  D210                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d210 ( D210 * link, uint8 ** pos )
{
  GETU16(link->class);
  GETSTR(link->subclass);
  GETVST(link->ident);
}


/* --------------------------------------------------------------------------*/
/* 7.4.22  D300                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d300 ( D300 * point, uint8 ** pos )
{
  GETPOS(point->posn);
  GETU32(point->time);
  GETU8(point->new_trk);
}


/* --------------------------------------------------------------------------*/
/* 7.4.23  D301                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d301 ( D301 * point, uint8 ** pos )
{ 
  GETPOS(point->posn);
  GETU32(point->time);
  GETF32(point->alt);
  GETF32(point->dpth);
  GETU8(point->new_trk);
}


/* --------------------------------------------------------------------------*/
/* 7.4.24  D302                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d302 ( D302 * point, uint8 ** pos )
{
  GETPOS(point->posn);
  GETU32(point->time);
  GETF32(point->alt);
  GETF32(point->dpth);
  GETF32(point->temp);
  GETU8(point->new_trk);
}


/* --------------------------------------------------------------------------*/
/* 7.4.25  D303                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d303 ( D303 * point, uint8 ** pos )
{
  GETPOS(point->posn);
  GETU32(point->time);
  GETF32(point->alt);
  GETU8(point->heart_rate);
}


/* --------------------------------------------------------------------------*/
/* 7.4.26  D304                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d304 ( D304 * point, uint8 ** pos )
{
  GETPOS(point->posn);
  GETU32(point->time);
  GETF32(point->alt);
  GETF32(point->distance);
  GETU8(point->heart_rate);
  GETU8(point->cadence);
  GETU8(point->sensor);
}


/* --------------------------------------------------------------------------*/
/* 7.4.27  D310                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d310 ( D310 * hdr, uint8 ** pos )
{
  GETU8(hdr->dspl);
  GETU8(hdr->color);
  GETVST(hdr->trk_ident);
}


/* --------------------------------------------------------------------------*/
/* 7.4.28  D311                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d311 ( D311 * hdr, uint8 ** pos )
{
  GETU16(hdr->index);
}


/* --------------------------------------------------------------------------*/
/* 7.4.29  D312                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d312 ( D312 * hdr, uint8 ** pos )
{
  GETU8(hdr->dspl);
  GETU8(hdr->color);
  GETVST(hdr->trk_ident);
}


/* ------------------------------------------------------------------------- */
/* 7.4.30  D400                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d400 ( D400 * prx, uint8 ** pos )
{
  garmin_unpack_d100(&prx->wpt,pos);
  SKIP(sizeof(D100));
  GETF32(prx->dst);
}


/* ------------------------------------------------------------------------- */
/* 7.4.31  D403                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d403 ( D403 * prx, uint8 ** pos )
{
  garmin_unpack_d103(&prx->wpt,pos);
  SKIP(sizeof(D103));
  GETF32(prx->dst);
}


/* ------------------------------------------------------------------------- */
/* 7.4.32  D450                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d450 ( D450 * prx, uint8 ** pos )
{
  GETU32(prx->idx);
  garmin_unpack_d150(&prx->wpt,pos);
  SKIP(sizeof(D150));
  GETF32(prx->dst);
}


/* ------------------------------------------------------------------------- */
/* 7.4.33  D500                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d500 ( D500 * alm, uint8 ** pos )
{
  GETU16(alm->wn);
  GETF32(alm->toa);
  GETF32(alm->af0);
  GETF32(alm->af1);
  GETF32(alm->e);
  GETF32(alm->sqrta);
  GETF32(alm->m0);
  GETF32(alm->w);
  GETF32(alm->omg0);
  GETF32(alm->odot);
  GETF32(alm->i);
}


/* ------------------------------------------------------------------------- */
/* 7.4.34  D501                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d501 ( D501 * alm, uint8 ** pos )
{
  GETU16(alm->wn);
  GETF32(alm->toa);
  GETF32(alm->af0);
  GETF32(alm->af1);
  GETF32(alm->e);
  GETF32(alm->sqrta);
  GETF32(alm->m0);
  GETF32(alm->w);
  GETF32(alm->omg0);
  GETF32(alm->odot);
  GETF32(alm->i);
  GETU8(alm->hlth);
}


/* ------------------------------------------------------------------------- */
/* 7.4.35  D550                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d550 ( D550 * alm, uint8 ** pos )
{
  GETU8(alm->svid);
  GETU16(alm->wn);
  GETF32(alm->toa);
  GETF32(alm->af0);
  GETF32(alm->af1);
  GETF32(alm->e);
  GETF32(alm->sqrta);
  GETF32(alm->m0);
  GETF32(alm->w);
  GETF32(alm->omg0);
  GETF32(alm->odot);
  GETF32(alm->i);
}


/* ------------------------------------------------------------------------- */
/* 7.4.36  D551                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d551 ( D551 * alm, uint8 ** pos )
{
  GETU8(alm->svid);
  GETU16(alm->wn);
  GETF32(alm->toa);
  GETF32(alm->af0);
  GETF32(alm->af1);
  GETF32(alm->e);
  GETF32(alm->sqrta);
  GETF32(alm->m0);
  GETF32(alm->w);
  GETF32(alm->omg0);
  GETF32(alm->odot);
  GETF32(alm->i);
  GETU8(alm->hlth);
}


/* ------------------------------------------------------------------------- */
/* 7.4.37  D600                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d600 ( D600 * dt, uint8 ** pos )
{
  GETU8(dt->month);
  GETU8(dt->day);
  GETU16(dt->year);
  GETU16(dt->hour);
  GETU8(dt->minute);
  GETU8(dt->second);
}


/* ------------------------------------------------------------------------- */
/* 7.4.38  D650                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d650 ( D650 * fbr, uint8 ** pos )
{
  GETU32(fbr->takeoff_time);
  GETU32(fbr->landing_time);
  GETPOS(fbr->takeoff_posn);
  GETPOS(fbr->landing_posn);
  GETU32(fbr->night_time);
  GETU32(fbr->num_landings);
  GETF32(fbr->max_speed);
  GETF32(fbr->max_alt);
  GETF32(fbr->distance);
  GETU8(fbr->cross_country_flag);
  GETVST(fbr->departure_name);
  GETVST(fbr->departure_ident);
  GETVST(fbr->arrival_name);
  GETVST(fbr->arrival_ident);
  GETVST(fbr->ac_id);
}


/* ------------------------------------------------------------------------- */
/* 7.4.39  D700                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d700 ( D700 * pt, uint8 ** pos )
{
  GETRPT(*pt);
}


/* ------------------------------------------------------------------------- */
/* 7.4.40  D800                                                              */
/* ------------------------------------------------------------------------- */

static void
garmin_unpack_d800 ( D800 * pvt, uint8 ** pos )
{
  GETF32(pvt->alt);
  GETF32(pvt->epe);
  GETF32(pvt->eph);
  GETF32(pvt->epv);
  GETU16(pvt->fix);
  GETF64(pvt->tow);
  GETRPT(pvt->posn);
  GETF32(pvt->east);
  GETF32(pvt->north);
  GETF32(pvt->up);
  GETF32(pvt->msl_hght);
  GETS16(pvt->leap_scnds);
  GETU32(pvt->wn_days);
}


/* --------------------------------------------------------------------------*/
/* 7.4.41  D906                                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d906 ( D906 * lap, uint8 ** pos )
{
  GETU32(lap->start_time);
  GETU32(lap->total_time);
  GETF32(lap->total_distance);
  GETPOS(lap->begin);
  GETPOS(lap->end);
  GETU16(lap->calories);
  GETU8(lap->track_index);
}


/* --------------------------------------------------------------------------*/
/* 7.4.42  D1000                                                             */
/* --------------------------------------------------------------------------*/


static void garmin_unpack_d1002 ( D1002 * wkt, uint8 ** pos );


static void
garmin_unpack_d1000 ( D1000 * run, uint8 ** pos )
{
  GETU32(run->track_index);
  GETU32(run->first_lap_index);
  GETU32(run->last_lap_index);
  GETU8(run->sport_type);
  GETU8(run->program_type);
  SKIP(2);
  GETU32(run->virtual_partner.time);
  GETF32(run->virtual_partner.distance);
  garmin_unpack_d1002(&run->workout,pos);
}


/* --------------------------------------------------------------------------*/
/* 7.4.43  D1001                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1001 ( D1001 * lap, uint8 ** pos )
{
  GETU32(lap->index);
  GETU32(lap->start_time);
  GETU32(lap->total_time);
  GETF32(lap->total_dist);
  GETF32(lap->max_speed);
  GETPOS(lap->begin);
  GETPOS(lap->end);
  GETU16(lap->calories);
  GETU8(lap->avg_heart_rate);
  GETU8(lap->max_heart_rate);
  GETU8(lap->intensity);
}


/* --------------------------------------------------------------------------*/
/* 7.4.44  D1002                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1002 ( D1002 * wkt, uint8 ** pos )
{
  int i;

  GETU32(wkt->num_valid_steps);
  for ( i = 0; i < 20; i++ ) {
    GETSTR(wkt->steps[i].custom_name);
    GETF32(wkt->steps[i].target_custom_zone_low);
    GETF32(wkt->steps[i].target_custom_zone_high);
    GETU16(wkt->steps[i].duration_value);
    GETU8(wkt->steps[i].intensity);
    GETU8(wkt->steps[i].duration_type);
    GETU8(wkt->steps[i].target_type);
    GETU8(wkt->steps[i].target_value);
    SKIP(2);
  }
  GETSTR(wkt->name);
  GETU8(wkt->sport_type);
}


/* --------------------------------------------------------------------------*/
/* 7.4.45  D1003                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1003 ( D1003 * occ, uint8 ** pos )
{
  GETSTR(occ->workout_name);
  GETU32(occ->day);
}


/* --------------------------------------------------------------------------*/
/* 7.4.46  D1004                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1004 ( D1004 * prof, uint8 ** pos )
{
  int i;
  int j;

  for ( i = 0; i < 3; i++ ) {
    for ( j = 0; j < 5; j++ ) {
      GETU8(prof->activities[i].heart_rate_zones[j].low_heart_rate);
      GETU8(prof->activities[i].heart_rate_zones[j].high_heart_rate);
      SKIP(2);
    }
    for ( j = 0; j < 10; j++ ) {
      GETF32(prof->activities[i].speed_zones[j].low_speed);
      GETF32(prof->activities[i].speed_zones[j].high_speed);
      GETSTR(prof->activities[i].speed_zones[j].name);
    }
    GETF32(prof->activities[i].gear_weight);
    GETU8(prof->activities[i].max_heart_rate);
    SKIP(3);
  }
  GETF32(prof->weight);
  GETU16(prof->birth_year);
  GETU8(prof->birth_month);
  GETU8(prof->birth_day);
  GETU8(prof->gender);
}


/* --------------------------------------------------------------------------*/
/* 7.4.47  D1005                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1005 ( D1005 * limits, uint8 ** pos )
{
  GETU32(limits->max_workouts);
  GETU32(limits->max_unscheduled_workouts);
  GETU32(limits->max_occurrences);
}


/* --------------------------------------------------------------------------*/
/* 7.4.48  D1006                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1006 ( D1006 * course, uint8 ** pos )
{
  GETU16(course->index);
  SKIP(2);
  GETSTR(course->course_name);
  GETU16(course->track_index);
}


/* --------------------------------------------------------------------------*/
/* 7.4.49  D1007                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1007 ( D1007 * lap, uint8 ** pos )
{
  GETU16(lap->course_index);
  GETU16(lap->lap_index);
  GETU32(lap->total_time);
  GETF32(lap->total_dist);
  GETPOS(lap->begin);
  GETPOS(lap->end);
  GETU8(lap->avg_heart_rate);
  GETU8(lap->max_heart_rate);
  GETU8(lap->intensity);
  GETU8(lap->avg_cadence);
}


/* --------------------------------------------------------------------------*/
/* 7.4.50  D1008                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1008 ( D1008 * wkt, uint8 ** pos )
{
  garmin_unpack_d1002((D1002 *)wkt,pos);
}


/* --------------------------------------------------------------------------*/
/* 7.4.51  D1009                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1009 ( D1009 * run, uint8 ** pos )
{
  GETU16(run->track_index);
  GETU16(run->first_lap_index);
  GETU16(run->last_lap_index);
  GETU8(run->sport_type);
  GETU8(run->program_type);
  GETU8(run->multisport);
  SKIP(3);
  GETU32(run->quick_workout.time);
  GETF32(run->quick_workout.distance);
  garmin_unpack_d1008(&run->workout,pos);  
}


/* --------------------------------------------------------------------------*/
/* 7.4.52  D1010                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1010 ( D1010 * run, uint8 ** pos )
{
  GETU32(run->track_index);
  GETU32(run->first_lap_index);
  GETU32(run->last_lap_index);
  GETU8(run->sport_type);
  GETU8(run->program_type);
  GETU8(run->multisport);
  SKIP(1);
  GETU32(run->virtual_partner.time);
  GETF32(run->virtual_partner.distance);
  garmin_unpack_d1002(&run->workout,pos);
}


/* --------------------------------------------------------------------------*/
/* 7.4.53  D1011                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1011 ( D1011 * lap, uint8 ** pos )
{
  GETU16(lap->index);
  SKIP(2);
  GETU32(lap->start_time);
  GETU32(lap->total_time);
  GETF32(lap->total_dist);
  GETF32(lap->max_speed);
  GETPOS(lap->begin);
  GETPOS(lap->end);
  GETU16(lap->calories);
  GETU8(lap->avg_heart_rate);
  GETU8(lap->max_heart_rate);
  GETU8(lap->intensity);
  GETU8(lap->avg_cadence);
  GETU8(lap->trigger_method);
}


/* --------------------------------------------------------------------------*/
/* 7.4.54  D1012                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1012 ( D1012 * point, uint8 ** pos )
{
  GETSTR(point->name);
  SKIP(1);
  GETU16(point->course_index);
  SKIP(2);
  GETU32(point->track_point_time);
  GETU8(point->point_type);
}


/* --------------------------------------------------------------------------*/
/* 7.4.55  D1013                                                             */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1013 ( D1013 * limits, uint8 ** pos )
{
  GETU32(limits->max_courses);
  GETU32(limits->max_course_laps);
  GETU32(limits->max_course_pnt);
  GETU32(limits->max_course_trk_pnt);
}


/* --------------------------------------------------------------------------*/
/* 7.4.XX  D1015 (Undocumented)                                              */
/* --------------------------------------------------------------------------*/

static void
garmin_unpack_d1015 ( D1015 * lap, uint8 ** pos )
{
  GETU16(lap->index);
  SKIP(2);
  GETU32(lap->start_time);
  GETU32(lap->total_time);
  GETF32(lap->total_dist);
  GETF32(lap->max_speed);
  GETPOS(lap->begin);
  GETPOS(lap->end);
  GETU16(lap->calories);
  GETU8(lap->avg_heart_rate);
  GETU8(lap->max_heart_rate);
  GETU8(lap->intensity);
  GETU8(lap->avg_cadence);
  GETU8(lap->trigger_method);

  /* 
     Garmin has not gotten back to me about what these fields mean, and
     whether all of the bytes are needed or just, say, three of them.
     This is annoying, because it means we may end up with .gmn files
     that have oversized D1015 elements, but it shouldn't affect our
     ability to read those files.  We don't make any assumptions about
     the size of each element.
  */

  GETU8(lap->unknown[0]);
  GETU8(lap->unknown[1]);
  GETU8(lap->unknown[2]);
  GETU8(lap->unknown[3]);
  GETU8(lap->unknown[4]);
}


/* List */

static void
garmin_unpack_dlist ( garmin_list * list, uint8 ** pos )
{
  uint32             id;
  uint32             elements;
  uint32             type;
  uint32             size;
  uint32             i;

  GETU32(list->id);
  GETU32(elements);

  for ( i = 0; i < elements; i++ ) {
    GETU32(id);
    GETU32(type);
    GETU32(size);
    if ( id == list->id ) {
      garmin_list_append(list,garmin_unpack(pos,type));
    } else {
      /* list element has wrong list ID */
      char hv0[256];
      sprintf(hv0, "garmin_unpack_dlist: list element had ID %d, expected ID %d!",
             id,list->id);
      garmin_queue_error(hv0, err_error);
    }
  }
}


/* Unpack a chunk of data. */

static garmin_data *
garmin_unpack_chunk ( uint8 ** pos )
{
  garmin_data * data = NULL;
  uint8 *       start;
  uint32        unpacked;
  uint32        version;
  uint32        size;
  uint32        type;
  uint32        chunk;
  char          hv0[256];

  /* First, read the header and check that it's satisfactory. */
  
  if ( memcmp(*pos,GARMIN_MAGIC,strlen(GARMIN_MAGIC)) == 0 ) {
    SKIP(12);
    GETU32(version);
    
    if ( version > GARMIN_VERSION ) {
      /* warning: version is more recent than supported. */
      sprintf(hv0, "garmin_unpack_chunk: version %.2f supported, %.2f found!",
             GARMIN_VERSION/100.0, version/100.0);
      garmin_queue_error(hv0, err_warning);
    }
    
    /* This is the size of the packed data (not including the header) */
    
    GETU32(size);

    /* Now let's get the type of the data, and the size of the chunk. */

    GETU32(type);
    GETU32(chunk);

    /* Unpack from here. */

    start    = *pos;
    data     = garmin_unpack(pos,type);
    unpacked = *pos - start;

    /* Double check - did we unpack the number of bytes we were supposed to? */

    if ( unpacked != chunk ) {
      /* unpacked the wrong number of bytes! */
      sprintf(hv0, "garmin_unpack_chunk: unpacked %d bytes (expecting %d)!",
             unpacked,chunk);
      garmin_queue_error(hv0, err_error);
    }
  } else {
    /* unknown file format */
    sprintf(hv0, "garmin_unpack_chunk: not a .gmn file!");
    garmin_queue_error(hv0, err_fatal);
  }

  return data;
}

  
/* ========================================================================= */
/* garmin_load                                                               */
/* ========================================================================= */

garmin_data *
garmin_load ( const char * filename )
{
  garmin_data * data   = NULL;
  garmin_data * data_l = NULL;
  garmin_list * list;
  uint32        bytes;
  uint8 *       buf;
  uint8 *       pos;
  uint8 *       start;
  struct stat   sb;
  int           fd;
  char          hv0[1024];

  if ( (fd = open(filename,O_RDONLY)) != -1 ) {
    if ( fstat(fd,&sb) != -1 ) {
      if ( (buf = malloc(sb.st_size)) != NULL ) {
        if ( (bytes = read(fd,buf,sb.st_size)) == sb.st_size ) {
          data_l = garmin_alloc_data(data_Dlist);
          list   = data_l->data;
          pos    = buf;
          while ( pos - buf < bytes ) {
            start = pos;
            garmin_list_append(list,garmin_unpack_chunk(&pos));
            if ( pos == start ) {
              /* did not unpack anything! */
              sprintf(hv0, "garmin_load:  %s: nothing unpacked!",filename);
              garmin_queue_error(hv0, err_error);
              break;
            }
          }

          /* 
             If we unpacked only a single element, return it.  Otherwise,
             return the list.
          */

          if ( list->elements == 1 ) {
            data = list->head->data;
            list->head->data = NULL;
            garmin_free_data(data_l);
          } else {
            data = data_l;
          }

        } else {
          /* read failed */
          sprintf(hv0, "%s: read: %s!",filename,strerror(errno));
          garmin_queue_error(hv0, err_error);
        }
        free(buf);
      } else {
        /* malloc failed */
        sprintf(hv0, "%s: malloc: %s!",filename,strerror(errno));
        garmin_queue_error(hv0, err_fatal);
      }
    } else {
      /* fstat failed */
      sprintf(hv0, "%s: fstat: %s!",filename,strerror(errno));
      garmin_queue_error(hv0, err_error);
    }
    close(fd);
  } else {
    /* open failed */
    sprintf(hv0, "%s: open: %s!",filename,strerror(errno));
    garmin_queue_error(hv0, err_error);
  }

  return data;
}


/* ========================================================================= */
/* garmin_unpack_packet                                                      */
/* ========================================================================= */

garmin_data *
garmin_unpack_packet ( garmin_packet * p, garmin_datatype type )
{
  uint8 * pos = p->packet.data;

  return garmin_unpack(&pos,type);
}


/* ========================================================================= */
/* garmin_unpack                                                             */
/* ========================================================================= */

garmin_data *
garmin_unpack ( uint8 **         pos, 
                garmin_datatype  type )
{
  garmin_data * d = garmin_alloc_data(type);
  char hv0[256];

  /* Early exit if we were asked to allocate an unknown data type. */

  if ( d->data == NULL ) {
    free(d);
    return NULL;
  }

  /* Now do the actual unpacking. */

#define CASE_DATA(x) \
  case data_D##x: garmin_unpack_d##x(d->data,pos); break

  switch ( type ) {
  CASE_DATA(list);
  CASE_DATA(100);
  CASE_DATA(101);
  CASE_DATA(102);
  CASE_DATA(103);
  CASE_DATA(104);
  CASE_DATA(105);
  CASE_DATA(106);
  CASE_DATA(107);
  CASE_DATA(108);
  CASE_DATA(109);
  CASE_DATA(110);
  CASE_DATA(120);
  CASE_DATA(150);
  CASE_DATA(151);
  CASE_DATA(152);
  CASE_DATA(154);
  CASE_DATA(155);
  CASE_DATA(200);
  CASE_DATA(201);
  CASE_DATA(202);
  CASE_DATA(210);
  CASE_DATA(300);
  CASE_DATA(301);
  CASE_DATA(302);
  CASE_DATA(303);
  CASE_DATA(304);
  CASE_DATA(310);
  CASE_DATA(311);
  CASE_DATA(312);
  CASE_DATA(400);
  CASE_DATA(403);
  CASE_DATA(450);
  CASE_DATA(500);
  CASE_DATA(501);
  CASE_DATA(550);
  CASE_DATA(551);
  CASE_DATA(600);
  CASE_DATA(650);
  CASE_DATA(700);
  CASE_DATA(800);
  CASE_DATA(906);
  CASE_DATA(1000);
  CASE_DATA(1001);
  CASE_DATA(1002);
  CASE_DATA(1003);
  CASE_DATA(1004);
  CASE_DATA(1005);
  CASE_DATA(1006);
  CASE_DATA(1007);
  CASE_DATA(1008);
  CASE_DATA(1009);
  CASE_DATA(1010);
  CASE_DATA(1011);
  CASE_DATA(1012);
  CASE_DATA(1013);
  CASE_DATA(1015);
  default: 
    sprintf(hv0, "garmin_unpack: data type %d not supported!",type);
    garmin_queue_error(hv0, err_warning);
    break;
  }

  return d;

#undef CASE_DATA
}