/*
 * $Id: pilot-foto.c,v 1.24 2006/10/12 14:21:21 desrod Exp $ 
 *
 * pilot-foto.c: Palm 'Foto' Image Fetcher/Converter
 *
 * This is a palm conduit to fetch Foto files from a Palm.  It can also
 * convert *.jpg.pdb files that have already been fetched from the Palm.
 *
 * Copyright (C) 2003 - 2004 by Judd Montgomery
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "popt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <netinet/in.h>

#include "pi-file.h"
#include "pi-socket.h"
#include "pi-foto.h"
#include "pi-userland.h"

/* Declare prototypes */
int get_jpg_info(FILE * in, char *type, unsigned short *version,
		 int *height, int *width);
int memcpy_tux_record(unsigned char *dest);
int install_tux_with_name(int sd, char *name, int width, int height,
			  long pdb_size, long mod_time, long file_time);

#define SIZE_OF_TUX 3335
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif



/***********************************************************************
 *
 * Function:    get_jpg_info
 *
 * Summary:     gets information about a jpeg file
 *
 * Parameters:  in - an open file that should be a jpeg
 *              type - at least 5 chars that will get written the
 *                4 digit jpeg type and a null
 *              version - high byte gets written version major,
 *                low byte gets written version minor
 *              height - height of jpeg
 *              width - width of jpeg
 *
 * Returns:     0 on success, -1 if file is not a jpeg/exif file
 *
 ***********************************************************************/
int get_jpg_info(FILE * in, char *type, unsigned short *version,
		 int *height, int *width)
{
    unsigned short word;
    unsigned short word1, word2;
    char str[65536];
    size_t len;
    unsigned char m1, m2;

    rewind(in);

    /* marker identifier, application use marker */
    fread(&word, 2, 1, in);
    word1 = htons(word);
    fread(&word, 2, 1, in);
    word2 = htons(word);
    /* printf("start of image 0x%02x 0x%02x\n", str[0], str[1]); */
    /* printf("application use marker 0x%02x 0x%02x\n", str[2], str[3]); */
    if ((word1 != 0xFFD8) && (word2 != 0xFFE0)) {
	/* fprintf(stderr, "Not a jpeg file 1\n"); */
	/* debug_fprintf(stderr, "%x\n", word1); */
	rewind(in);
	return -1;
	/* ff1e */
    }

    *height = *width = -1;
    /* length of application block */
    fread(&word, 2, 1, in);
    len = htons(word);
    /* printf("len %d\n", len); */
    if (len > 65535) {
	/* fprintf(stderr, "Not a jpeg file 2\n"); */
	rewind(in);
	return -1;
    }

    fread(str, 5, 1, in);
    str[6] = '\0';
    /* printf("%s\n", str); */
    if (strncmp(str, "JFIF", 4) && strncmp(str, "Exif", 4)
	) {
	/* fprintf(stderr, "Not a jpeg file 3\n"); */
	rewind(in);
	return -1;
    }
    strncpy(type, str, 4);
    type[4] = '\0';

    /* Version */
    fread(&word, 2, 1, in);
    *version = htons(word);
    /* printf("version %d.%02d\n", ((*version)&0xFF00)>>8, (*version)&0xFF); */

    fread(str, len - 9, 1, in);
    /* Units 1 byte
     * X and Y density, 1 byte each
     * X and Y thumbnail size, 2 bytes each
     */
    while (!feof(in)) {
	/* marker identifier */
	fread(&word, 2, 1, in);
	word1 = htons(word);
	m1 = (word1 & 0xFF00) >> 8;
	m2 = word1 & 0xFF;
	/* printf("marker 0x%02x 0x%02x\n", m1, m2); */

	if ((m1 == 0xFF) && (m2 == 0xC0)) {
	    /* The SOF0 marker (Start of Frame 0) */
	    /* length(2 bytes) and data precision(1 byte) */
	    fread(str, 3, 1, in);
	    /* Height */
	    fread(&word, 2, 1, in);
	    *height = htons(word);
	    /* printf("height %d\n", *height); */
	    /* Width */
	    fread(&word, 2, 1, in);
	    *width = htons(word);
	    /* printf("width %d\n", *width); */
	    break;
	}
	fread(&word, 2, 1, in);
	len = htons(word);
	/* printf("len %d\n", len); */
	if (len > 65535) {
	    /* fprintf(stderr, "Not a jpeg file\n"); */
	    rewind(in);
	    return -1;
	}
	fread(str, len - 2, 1, in);
    }

    rewind(in);
    return 0;
}

/***********************************************************************
 *
 * Function:    do_list
 *
 * Summary:     lists jpeg files and thumbnails found on the handheld
 *
 * Parameters:  sd - open connection to the handheld
 *
 * Returns:     0 on success, -1 if can't open PhotosDB-Foto
 *
 ***********************************************************************/
int do_list(int sd)
{
    int ret;
    char jpg_name[255];
    size_t jpg_name_size;
    size_t offset;
    int db;
    int index;
    recordid_t id_;
    int attr;
    int category;
    int start;
    struct DBInfo info;
    char creator[5];
    char type[5];
    struct pi_buffer_t *buffer;

    printf("Searching for photos\n");
    start = 0;
    buffer = pi_buffer_new(0xffff);
    while (dlp_ReadDBList(sd, 0, dlpOpenRead, start, buffer) > 0) {
	memcpy(&info, buffer->data, sizeof(struct DBInfo));
	start = info.index + 1;
	creator[0] = (info.creator & 0xFF000000) >> 24;
	creator[1] = (info.creator & 0x00FF0000) >> 16;
	creator[2] = (info.creator & 0x0000FF00) >> 8;
	creator[3] = (info.creator & 0x000000FF);
	creator[4] = '\0';
	type[0] = (info.type & 0xFF000000) >> 24;
	type[1] = (info.type & 0x00FF0000) >> 16;
	type[2] = (info.type & 0x0000FF00) >> 8;
	type[3] = (info.type & 0x000000FF);
	type[4] = '\0';
	if ((!strcmp(creator, "Foto")) &&
	    (!strcmp(type, "Foto")) && (info.flags & dlpDBFlagStream)) {
	    printf("Photo '%s' (Creator ID '%s')\n", info.name, creator);
	}
    }

    printf("\nSearching for thumbnails\n");
    ret = dlp_OpenDB(sd, 0, dlpOpenReadWrite, "PhotosDB-Foto", &db);
    if (ret < 0) {
	fprintf(stderr, "Unable to open PhotosDB-Foto\n");
	pi_buffer_free(buffer);
	return -1;
    }

    for (index = 0; index < 65535; index++) {
	ret = dlp_ReadRecordByIndex(sd, db, index, buffer,
				    &id_, &attr, &category);
	if (ret < 0)
	    break;
	if (buffer->used < 40)
	    continue;
	offset = ((buffer->data[38] << 8) | buffer->data[39]) + 40;
	jpg_name_size = buffer->used - offset;
	if (jpg_name_size >= sizeof(jpg_name))
	    jpg_name_size = sizeof(jpg_name) - 1;
	memcpy(jpg_name, buffer->data + offset, jpg_name_size);
	jpg_name[jpg_name_size] = '\0';
	fprintf(stderr, "Thumbnail %s\n", jpg_name);
    }
    pi_buffer_free(buffer);
    dlp_CloseDB(sd, db);
    return 0;
}

/***********************************************************************
 *
 * Function:    do_delete
 *
 * Summary:     deletes jpeg files and thumbnails found on the handheld
 *
 * Parameters:  sd - open connection to the handheld
 *              delete_files - an array of filenames to find and delete
 *              all - if true then delete all images and thumbnails
 *
 * Returns:     0 on success, -1 if can't open PhotosDB-Foto
 *
 ***********************************************************************/
int do_delete(int sd, const char **delete_files, int all)
{
    int i;
    int ret;
    char jpg_name[255];
    int jpg_name_size;
    size_t offset;
    int db;
    int index;
    recordid_t id_;
    int attr;
    int category;
    char log_text[256];
    struct pi_buffer_t *buffer;

    if ((!all) && (!delete_files))
	return -1;

    /* printf("Searching photos\n"); */
    ret = dlp_OpenDB(sd, 0, dlpOpenReadWrite, "PhotosDB-Foto", &db);
    if (ret < 0) {
	fprintf(stderr, "Unable to open PhotosDB-Foto\n");
	return -1;
    }

    for (i = 0; delete_files[i]; i++) {
	ret = dlp_DeleteDB(sd, 0, delete_files[i]);
	if (ret < 0) {
	    fprintf(stderr, "Unable to delete photo %s\n",
		    delete_files[i]);
	} else {
	    fprintf(stderr, "Deleted photo %s\n", delete_files[i]);
	    sprintf(log_text, "Deleted %s\n", delete_files[i]);
	    dlp_AddSyncLogEntry(sd, log_text);
	}
    }

    /* printf("Searching thumbnails\n"); */
    if (all) {
	dlp_DeleteRecord(sd, db, 1, 0);
	printf("Deleted all thumbnails\n");
	dlp_AddSyncLogEntry(sd, "Deleted all thumbnails\n");
    } else {
	buffer = pi_buffer_new(0xffff);
	for (index = 0; index < 65535; index++) {
	    ret = dlp_ReadRecordByIndex(sd, db, index, buffer,
					&id_, &attr, &category);
	    if (ret < 0)
		break;
	    if (buffer->used < 40)
		continue;
	    offset = ((buffer->data[38] << 8) | buffer->data[39]) + 40;
	    /* printf("offset = 0x%x\n", offset); */
	    jpg_name_size = buffer->used - offset;
	    if (jpg_name_size > 255)
		jpg_name_size = 255;
	    memcpy(jpg_name, buffer->data + offset, jpg_name_size);
	    jpg_name[jpg_name_size] = '\0';
	    /* printf("found jpg named = %s\n", jpg_name); */
	    for (i = 0; delete_files[i]; i++) {
		if (!strcmp(jpg_name, delete_files[i])) {
		    ret = dlp_DeleteRecord(sd, db, 0, id_);
		    if (ret < 0) {
			fprintf(stderr, "Unable to delete thumbnail %s\n",
				delete_files[i]);
		    } else {
			fprintf(stderr, "Deleted thumbnail %s\n",
				delete_files[i]);
			sprintf(log_text, "Deleted thumbnail %s\n",
				delete_files[i]);
			dlp_AddSyncLogEntry(sd, log_text);
		    }
		    /* need to decrement index because we just deleted one record */
		    index--;
		    break;
		}
	    }
	}
	pi_buffer_free(buffer);
    }

    dlp_ResetSyncFlags(sd, db);
    dlp_CleanUpDatabase(sd, db);

    dlp_CloseDB(sd, db);

    return 0;
}

/***********************************************************************
 *
 * Function:    do_fetch
 *
 * Summary:     Fetch the files given, or all foto files
 *
 * Parameters:  sd - open connection to the handheld
 *              fetch_files - an array of filenames to find and fetch
 *              all - if true then fetch all images and thumbnails
 *
 * Returns:     0 on success
 *
 ***********************************************************************/
int do_fetch(int sd, const char **fetch_files, int all)
{
    FILE *out;
    recordid_t id_;
    int i;
    int found;
    int index, db, attr, category, ret, start;

    struct DBInfo info;
    char creator[5];
    char type[5];
    pi_buffer_t *buffer;

    if ((!all) && (!fetch_files))
	return -1;

    start = 0;
    buffer = pi_buffer_new(65536);
    while (dlp_ReadDBList(sd, 0, dlpOpenRead, start, buffer) > 0) {
	memcpy(&info, buffer->data, sizeof(struct DBInfo));
	start = info.index + 1;
	creator[0] = (info.creator & 0xFF000000) >> 24;
	creator[1] = (info.creator & 0x00FF0000) >> 16;
	creator[2] = (info.creator & 0x0000FF00) >> 8;
	creator[3] = (info.creator & 0x000000FF);
	creator[4] = '\0';
	type[0] = (info.type & 0xFF000000) >> 24;
	type[1] = (info.type & 0x00FF0000) >> 16;
	type[2] = (info.type & 0x0000FF00) >> 8;
	type[3] = (info.type & 0x000000FF);
	type[4] = '\0';
	if ((!strcmp(creator, "Foto")) &&
	    (!strcmp(type, "Foto")) && (info.flags & dlpDBFlagStream)) {
	    if (!all) {
		/* Need to see if this is in the list */
		found = 0;
		for (i = 0; fetch_files[i]; i++) {
		    if (!strcmp(fetch_files[i], info.name)) {
			found = 1;
			break;
		    }
		}
		if (!found)
		    continue;
	    }

	    printf("Fetching '%s' (Creator ID '%s')...", info.name,
		   creator);

	    ret = dlp_OpenDB(sd, 0, dlpOpenRead, info.name, &db);
	    if (ret < 0) {
		fprintf(stderr, "Unable to open %s\n", info.name);
		continue;
	    }

	    out = fopen(info.name, "wb");
	    if (!out) {
		fprintf(stderr, "Failed, unable to create file %s\n",
			info.name);
		dlp_CloseDB(sd, db);
		continue;
	    }

	    index = 0;
	    do {
		ret =
		    dlp_ReadRecordByIndex(sd, db, index, buffer, &id_,
					  &attr, &category);
		index++;
		if (ret > 0 && buffer->used > 8)
		    fwrite(buffer->data + 8, buffer->used - 8, 1, out);
	    } while (ret > 0);

	    dlp_CloseDB(sd, db);
	    fclose(out);
	    printf("OK\n");
	}
    }
    pi_buffer_free(buffer);
    return 0;
}

/***********************************************************************
 *
 * Function:    install_tux_with_name
 *
 * Summary:     Installs a generic thumbnail with the given name embedded
 *
 * Parameters:  sd - open connection to the handheld
 *              name - name of jpeg being installed
 *
 * Returns:     0 on sucess, -1 on failure
 *
 ***********************************************************************/
int install_tux_with_name(int sd, char *name, int width, int height,
			  long pdb_size, long mod_time, long file_time)
{
    int ret;
    int db;
    unsigned char *record;
    unsigned long new_id;
    int rec_len;

    /* unsigned int thumb_size; */

    rec_len = SIZE_OF_TUX + strlen(name);
    record = malloc(rec_len + 4);
    memcpy_tux_record(record);
    memcpy(&record[SIZE_OF_TUX], name, strlen(name));

    /* Length of filename needs to be here */
    record[22] = (unsigned char) strlen(name);

    /*
       printf("rec_len = %d\n", rec_len);
       printf("tux = %d\n", SIZE_OF_TUX);
       printf("str = %d\n", strlen(name));
     */

    /* Here is where the image size is. */
    set_short(record + FOTO_IMAGE_WIDTH, width);
    set_short(record + FOTO_IMAGE_HEIGHT, height);
    set_long(record + FOTO_IMAGE_SIZE, pdb_size);

    /* Dates */
    set_long(record + FOTO_MODIFY_DATE, mod_time);
    set_long(record + FOTO_IMAGE_DATE, file_time);

    /* Name length */
    set_byte(record + FOTO_NAME_LEN, strlen(name));

    /* Here is where the thumbnail size is.  We won't need to change that.
       thumb_size=SIZE_OF_TUX-40;
       record[38]=(unsigned char)((thumb_size & 0xFF00) >> 8);
       record[39]=(unsigned char)(thumb_size & 0x00FF);
     */

    ret = dlp_OpenDB(sd, 0, dlpOpenReadWrite, "PhotosDB-Foto", &db);
    if (ret < 0) {
	fprintf(stderr, "Unable to open PhotosDB-Foto\n");
	free(record);
	return -1;
    }
    ret = dlp_WriteRecord(sd, db, 0x00, 0, 0x00, record, rec_len, &new_id);
    free(record);
    if (ret < 0) {
	fprintf(stderr, "Could not write to Photos-DB-Foto\n");
	fprintf(stderr, "WriteRecord returned %d\n", ret);
	return -1;
    }

    dlp_ResetSyncFlags(sd, db);
    dlp_CleanUpDatabase(sd, db);

    dlp_CloseDB(sd, db);

    return 0;
}


/***********************************************************************
 *
 * Function:    do_install
 *
 * Summary:     Does some validation to determine if file is a jpeg
 *              installs the image and then the thumbnail
 *
 * Parameters:  sd - open connection to the handheld
 *              install_files - an array of filenames to install
 *
 * Returns:     0 on sucess, -1 on failure
 *
 ***********************************************************************/
int do_install(int sd, const char **install_files)
{
    int n, ret, i;
    const int max_photo_namelen = 30;
    struct pi_file *pf;
    struct DBInfo info;
    FILE *in;
    recordid_t uid;
    time_t ltime;
    unsigned char buf[0x1010];
    char pdb_file[256];
    char log_text[256];
    char installed_name[max_photo_namelen];

    struct stat sbuf;

    int width, height;
    char type[5];
    unsigned short version;
    long pdb_size = 0;
    long mod_time;
    long file_time;

    time(&ltime);

    info.more = 0x80;
    info.flags = dlpDBFlagStream;
    info.miscFlags = 0x40;
    info.version = 0;
    info.type = char4('F', 'o', 't', 'o');
    info.creator = char4('F', 'o', 't', 'o');
    info.modnum = 0;
    info.index = 0;
    info.createDate = ltime;
    info.modifyDate = ltime;
    info.backupDate = -2082816000;

    for (i = 0; install_files[i]; i++) {
	strncpy(installed_name, install_files[i], max_photo_namelen);
	installed_name[max_photo_namelen - 1] = '\0';

	/* todo check maximum file name length */
	if (strlen(install_files[i]) > max_photo_namelen - 5) {
	    strcpy(&installed_name[max_photo_namelen - 5], ".jpg");
	    printf("%s being truncated to %s: ", install_files[i],
		   installed_name);
	}
	strcpy(info.name, installed_name);
	printf("   Installing file %s\n", installed_name);
	fflush(stdout);
	/* Open jpg file */
	in = fopen(install_files[i], "r");
	if (!in) {
	    fprintf(stderr, "Could not open %s for read\n",
		    install_files[i]);
	    continue;
	}

	/* See if its a jpeg file */
	ret = get_jpg_info(in, type, &version, &height, &width);
	rewind(in);

	if (!ret) {
	    /* printf("%s: ", install_files[i]); */
	    printf("   File format: %s, ", type);
	    printf("version %d.%02d\n", (version & 0xFF00) >> 8,
		   version & 0xFF);
	    printf("   Image dimensions (width x height): %d x %d", width,
		   height);
	} else {
	    printf("%s does not appear to be a jpeg file\n",
		   install_files[i]);
	    continue;
	}

	strncpy(pdb_file, installed_name, 255);
	/* pdb_file[255 - 5]='\0'; */
	strcat(pdb_file, ".pdb");
	pf = pi_file_create(pdb_file, &info);
	if (!pf) {
	    fprintf(stderr, "Could not open %s for write\n", pdb_file);
	    dlp_EndOfSync(sd, dlpEndCodeNormal);
	    return -1;
	}
	uid = 0x883001;

	memcpy(buf, "DBLK\0\0", 6);
	while (!feof(in)) {
	    n = fread(buf + 8, 1, 0x1000, in);
	    buf[6] = (n & 0xFF00) >> 8;
	    buf[7] = n & 0xFF;
	    pi_file_append_record(pf, buf, n + 8, 0x40, 0, uid);
	    uid++;
	}
	pi_file_close(pf);
	fclose(in);

	pf = pi_file_open(pdb_file);
	if (!pf) {
	    fprintf(stderr, "Could not open %s\n", pdb_file);
	    exit(-1);
	}

	ret = pi_file_install(pf, sd, 0, NULL);

	if (stat(pdb_file, &sbuf) == 0) {
	    pdb_size = sbuf.st_size;
	}

	printf("   Installed size: %ld bytes...", pdb_size);
	pi_file_close(pf);
	fflush(stdout);
	if (ret < 0) {
	    fprintf(stderr, "Could not install %s\n", pdb_file);
	    return -1;
	}

	mod_time = unix_time_to_pilot_time(ltime);
	file_time = mod_time;	/* XXX just use current time for now */
	ret = install_tux_with_name(sd, installed_name,
				    width, height, pdb_size, mod_time,
				    file_time);
	if (ret < 0) {
	    fprintf(stderr, "Could not install thumbnail for %s\n",
		    pdb_file);
	    /* FIXME remove the pdb file; */
	    return -1;
	}
	printf(" Ok.\n");
	sprintf(log_text, "Photo %s installed on Palm\n", installed_name);
	dlp_AddSyncLogEntry(sd, log_text);
    }

    return 0;
}

/***********************************************************************
 *
 * Function:    pdb_to_jpg
 *
 * Summary:     Converts a jpg.pdb file (palm stream format) to a jpg
 *
 * Parameters:  filename - filename to be converted
 *
 * Returns:     0 on sucess, -1 on failure
 *
 ***********************************************************************/
int pdb_to_jpg(const char *filename)
{
    struct pi_file *pi_fp;
    struct DBInfo info;
    FILE *out;
    int index;
    int ret;
    void *Pbuf;
    size_t size;
    int total_size;
    int attr;
    int cat;
    recordid_t uid;

    printf("converting %s...", filename);
    pi_fp = pi_file_open(filename);
    if (!pi_fp) {
	printf("FAILED: could not open %s\n", filename);
	return -1;
    }
    pi_file_get_info(pi_fp, &info);

    out = fopen(info.name, "w");
    if (!out) {
	printf("FAILED: could not open %s to write\n", info.name);
	return -1;
    }

    index = 0;
    total_size = 0;
    ret = 1;
    while (ret >= 0) {
	ret = pi_file_read_record(pi_fp, index, &Pbuf, &size, &attr,
				  &cat, &uid);
	index++;
	if ((ret >= 0) && (size > 8)) {
	    fwrite(((char *) Pbuf) + 8, size - 8, 1, out);
	    total_size += size - 8;
	}
    }
    fclose(out);

    printf("OK, wrote %d bytes to %s\n", total_size, info.name);
    return 0;
}

/***********************************************************************
 *
 * Function:    main
 *
 * Summary:     Entry point to program
 *
 * Parameters:  argc, argv
 *
 * Returns:     Exit 0 on sucess, -1 on failure
 *
 ***********************************************************************/
int main(int argc, const char *argv[])
{
    int c;
    int sd = 0;
    poptContext po;
    const char **args = NULL;

    enum { mode_none,
	mode_delete = 'd',
	mode_delete_all = 'D',
	mode_install = 'i',
	mode_list = 'l',
	mode_fetch = 'f',
	mode_fetch_all = 'F',
	mode_convert = 'c' 
    } mode = mode_none;

    struct poptOption options[] = {
	USERLAND_RESERVED_OPTIONS
	{"delete",     'd', POPT_ARG_NONE, NULL, 'd', "Delete a jpg file on the handheld"},
	{"delete-all",   0, POPT_ARG_NONE, NULL, 'D', "Delete ALL jpg files on the handheld"},
	{"install",    'i', POPT_ARG_NONE, NULL, 'i', "Install a jpg file"},
	{"list",       'l', POPT_ARG_NONE, NULL, 'l', "List all photos and thumbnails"},
	{"fetch",      'f', POPT_ARG_NONE, NULL, 'f', "Fetch files from the Palm"},
	{"fetch-all",  'F', POPT_ARG_NONE, NULL, 'F', "Fetch ALL jpg files from the Palm"},
	{"convert",    'c', POPT_ARG_NONE, NULL, 'c', "convert [file].jpg.pdb files to jpg"},
	POPT_TABLEEND
    };

    po = poptGetContext("pilot-foto", argc, argv, options, 0);
    poptSetOtherOptionHelp(po, "\n\n"
			   "   Installs and Fetches Palm 'Foto' Image files\n"
			   "   Converts Palm 'Foto' (*.jpg.pdb) Image files to jpg\n\n"
			   "   Example arguments: \n"
			   "      -p /dev/pilot -f\n"
			   "      -i MyImage.jpg\n"
			   "      -i *.jpg\n"
			   "      -f MyImage\n"
			   "      -c MyImage.jpg.pdb\n\n");

    if (argc < 2) {
	poptPrintUsage(po, stderr, 0);
	return 1;
    }

    while ((c = poptGetNextOpt(po)) >= 0) {
	switch (c) {
	  case 'd':
	  case 'D':
	  case 'i':
	  case 'l':
	  case 'f':
	  case 'F':
	  case 'c':
	      if (mode != mode_none) {
		  fprintf(stderr,
			  "   ERROR: Specify only one mode (delete, install, list, fetch, convert).\n");
		  return 1;
	      }
	      mode = c;
	      break;
	  default:
	      fprintf(stderr, "   ERROR: Unhandled option %d.\n", c);
	      return 1;
	}
    }

    if (c < -1) {
	plu_badoption(po, c);
    }

    args = poptGetArgs(po);

    switch (mode) {
      case mode_none:
	  fprintf(stderr,
		  "   ERROR: Specify a mode (delete, install, list, fetch, convert).\n");
	  return 1;
      case mode_convert:
	  while (args != NULL && *args) {
	      pdb_to_jpg(*args++);
	  }
	  return 0;
      default:
	  /* The rest all need a connect to the pilot */
	  break;
    }

    sd = plu_connect();
    if (sd < 0) {
	return 1;
    }

    switch (mode) {
      case mode_none:
      case mode_convert:
	  /* impossible */
	  break;
      case mode_delete_all:
	  if (args != NULL && *args) {
	      /* It's an error here to protect users from an accidental --delete vs.
	       * --delete-all confusion. The other -all actions don't destroy data.
	       */
	      fprintf(stderr,
		      "   ERROR: With --delete-all, do not specify files.\n");
	      pi_close(sd);
	      return 1;
	  }
	  do_delete(sd, NULL, 1);
	  break;
      case mode_list:
	  if (args != NULL && *args) {
	      fprintf(stderr,
		      "   WARNING: With --list, do not specify files.\n");
	  }
	  do_list(sd);
	  break;
      case mode_fetch_all:
	  if (args != NULL && *args) {
	      fprintf(stderr,
		      "   WARNING: With --fetch-all, do not specify files.\n");
	  }
	  do_fetch(sd, NULL, 1);
	  break;
      case mode_delete:
	  do_delete(sd, args, 0);
	  break;
      case mode_fetch:
	  do_fetch(sd, args, 0);
	  break;
      case mode_install:
	  do_install(sd, args);
	  break;
    }

    if (sd) {
	dlp_EndOfSync(sd, dlpEndCodeNormal);
	pi_close(sd);
    }

    return 0;
}

/* A thumbnail image formatted to be installed
 *  The first 40 bytes are header info
 *  The 39th and 40th bytes of the header are the length (n) of the thumbnail
 *   to follow.
 *  The following n bytes are a standard jpg formatted picture
 *  After that is the name of the image.  The number of characters can be
 *   determined from the palm record size - (n + 40 byte header)
 *  i.e. thumbnail image is 1000 bytes and palm record is 1050.  That would
 *   mean the filename is 10 bytes long.
 */
int memcpy_tux_record(unsigned char *dest)
{
    unsigned char tux[] = {
	0x00, 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x84, 0xbc, 0x1e, 0x3b, 0xcc, 0x00, 0x00, 0x1a, 0xe6,
	0x3f, 0x54, 0x10, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x1e, 0x30, 0x10,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xdf, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x05,
	0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08,
	0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09, 0x0c, 0x11, 0x0f, 0x12, 0x12, 0x11, 0x0f, 0x11,
	0x11, 0x13, 0x16, 0x1c, 0x17, 0x13, 0x14, 0x1a, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1a, 0x1d,
	0x1d, 0x1f, 0x1f, 0x1f, 0x13, 0x17, 0x22, 0x24, 0x22, 0x1e, 0x24, 0x1c, 0x1e, 0x1f, 0x1e, 0xff,
	0xdb, 0x00, 0x43, 0x01, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0e, 0x08, 0x08, 0x0e, 0x1e, 0x14,
	0x11, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
	0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
	0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,
	0x1e, 0x1e, 0x1e, 0x1e, 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0x54, 0x00, 0x3f, 0x03, 0x01, 0x22,
	0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
	0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01,
	0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03,
	0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14,
	0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62,
	0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34,
	0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
	0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
	0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
	0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
	0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
	0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5,
	0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff,
	0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
	0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04,
	0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
	0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1,
	0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1,
	0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43,
	0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63,
	0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82,
	0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
	0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
	0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5,
	0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3,
	0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11,
	0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, 0xe6, 0x8a, 0x28, 0xaf, 0x9d, 0x35, 0x11, 0x8e, 0x14, 0x9f,
	0x6a, 0xe9, 0xf4, 0x6f, 0x0e, 0x46, 0x60, 0x2f, 0xa9, 0xa3, 0x33, 0xb8, 0x2a, 0x21, 0x56, 0x23,
	0x67, 0xcc, 0x79, 0xdc, 0xa7, 0x27, 0x20, 0x02, 0x31, 0x8c, 0x64, 0xfd, 0x6b, 0x1f, 0x41, 0xb3,
	0x92, 0xf3, 0x56, 0xb7, 0x54, 0x6d, 0xa9, 0x13, 0x89, 0x64, 0x39, 0xc1, 0xc2, 0x9c, 0x8f, 0xcd,
	0xb6, 0x8f, 0xa1, 0x35, 0xea, 0x5e, 0x0b, 0xf0, 0xdd, 0xe7, 0x8b, 0xfc, 0x5b, 0xa4, 0xf8, 0x6e,
	0xcc, 0x3a, 0x2d, 0xed, 0xc0, 0x4b, 0xa9, 0x91, 0x82, 0xb4, 0x16, 0xe0, 0x16, 0x96, 0x45, 0x24,
	0x1f, 0x9c, 0x22, 0x90, 0xb9, 0x04, 0x6f, 0x65, 0xcf, 0x19, 0xaf, 0x4b, 0x05, 0x46, 0x2e, 0x3c,
	0xf2, 0x33, 0x93, 0xd6, 0xc7, 0x03, 0x7f, 0xa3, 0xe8, 0x51, 0xc1, 0x22, 0xdb, 0xea, 0x16, 0xf6,
	0x52, 0x40, 0xe0, 0xca, 0x66, 0xba, 0xde, 0x06, 0x7f, 0x85, 0x83, 0xb6, 0x46, 0x7b, 0x60, 0x8e,
	0x4f, 0x7e, 0x95, 0xce, 0xb2, 0xb2, 0xbb, 0xc6, 0xea, 0x55, 0xe3, 0x62, 0x8e, 0xa4, 0x82, 0x55,
	0x81, 0xc1, 0x04, 0x8c, 0x82, 0x41, 0x04, 0x71, 0xe9, 0x5f, 0xa6, 0x7a, 0x17, 0x84, 0xfc, 0x37,
	0xa1, 0xf8, 0x5d, 0x3c, 0x35, 0xa7, 0xe8, 0xf6, 0x89, 0xa5, 0x79, 0x7e, 0x5c, 0xb6, 0xef, 0x12,
	0xba, 0xcf, 0x95, 0x0a, 0xcd, 0x2e, 0x47, 0xef, 0x19, 0xb1, 0xf3, 0x33, 0x64, 0xb7, 0x24, 0x93,
	0x9a, 0xf9, 0x17, 0xe3, 0xaf, 0xc3, 0x7f, 0x04, 0x68, 0x1f, 0x05, 0xfc, 0x19, 0xe3, 0x2f, 0x0c,
	0xc7, 0x71, 0x0d, 0xd5, 0xf2, 0xc1, 0x1b, 0x13, 0x33, 0x32, 0xdd, 0x45, 0x3c, 0x4f, 0x3f, 0x9a,
	0xe1, 0xb2, 0x7c, 0xc0, 0x40, 0x1b, 0x81, 0x1c, 0x36, 0x0e, 0x70, 0xb8, 0xbc, 0x5d, 0x18, 0xca,
	0x3c, 0xeb, 0x74, 0x11, 0x67, 0x83, 0x51, 0x45, 0x15, 0xe5, 0x1a, 0x05, 0x14, 0x52, 0x12, 0x00,
	0x24, 0x90, 0x00, 0xea, 0x4f, 0x6a, 0x00, 0xdb, 0xf0, 0x82, 0xbc, 0x57, 0x57, 0x57, 0xf3, 0x31,
	0x8a, 0xd2, 0x08, 0x08, 0x76, 0x23, 0x86, 0x24, 0xe4, 0x9f, 0xf8, 0x08, 0x5f, 0xfc, 0x7a, 0xbe,
	0x9d, 0xf8, 0x33, 0xe1, 0x3f, 0x15, 0xf8, 0x17, 0xc5, 0xb0, 0xf8, 0x83, 0x52, 0xd3, 0xad, 0x2e,
	0xd2, 0x6b, 0x36, 0x81, 0xec, 0xed, 0x2f, 0x94, 0xdd, 0x22, 0xbb, 0x29, 0x24, 0x89, 0x15, 0x23,
	0x3b, 0x4a, 0xa6, 0xec, 0x49, 0x81, 0x93, 0x8d, 0xd8, 0x1b, 0xbe, 0x7b, 0xd1, 0x74, 0xf1, 0x69,
	0xa1, 0x98, 0x2e, 0xd3, 0x06, 0x60, 0xcd, 0x3a, 0x16, 0xdc, 0x06, 0xe1, 0x8c, 0x7a, 0x7d, 0xdc,
	0x74, 0xef, 0x9e, 0x4d, 0x7d, 0x47, 0x67, 0x63, 0x2f, 0x88, 0xae, 0x94, 0x6b, 0x52, 0xea, 0x7e,
	0x19, 0xf1, 0xae, 0x93, 0x14, 0x5f, 0xda, 0x09, 0x61, 0x78, 0x5b, 0xcb, 0x57, 0xdc, 0x53, 0x96,
	0x53, 0x0c, 0xb1, 0x49, 0xb0, 0xb7, 0xdd, 0x38, 0x20, 0x83, 0x86, 0x56, 0xae, 0x3e, 0x22, 0xcc,
	0x71, 0x99, 0x56, 0x1e, 0x95, 0x4a, 0x16, 0x51, 0xd7, 0x99, 0xb5, 0x7b, 0x6d, 0x6b, 0xf5, 0xb3,
	0xee, 0x8d, 0x70, 0xb4, 0xa1, 0x5a, 0x4d, 0x4b, 0x7e, 0x85, 0x8f, 0x1b, 0x78, 0xbb, 0xc7, 0xbe,
	0x2d, 0x9a, 0xf3, 0x40, 0xf0, 0x9e, 0x93, 0x7d, 0xa0, 0x9b, 0x77, 0x2c, 0x9a, 0xb4, 0x97, 0x12,
	0x5b, 0xa3, 0xa9, 0x57, 0x43, 0x1c, 0x82, 0x5b, 0x6c, 0xee, 0xc1, 0xdc, 0xad, 0x0f, 0x9a, 0xa1,
	0xd5, 0x49, 0x25, 0x46, 0x1b, 0xc4, 0x7f, 0x68, 0x4d, 0x3b, 0xe2, 0x1f, 0x88, 0xb5, 0xad, 0x07,
	0x4b, 0x7f, 0x0b, 0xf8, 0x86, 0xea, 0xcf, 0x43, 0xd3, 0x63, 0xb0, 0x82, 0x68, 0x62, 0x17, 0x31,
	0xdd, 0x48, 0x36, 0x89, 0x2e, 0x4f, 0x92, 0xbb, 0x63, 0x2e, 0x42, 0x8d, 0xad, 0xd0, 0x20, 0x38,
	0x5c, 0x91, 0x5e, 0x8f, 0xf1, 0x8b, 0xe2, 0x4d, 0xe7, 0xc3, 0xcb, 0x58, 0x74, 0xeb, 0x6d, 0x56,
	0xcf, 0x51, 0xd6, 0xee, 0x21, 0x32, 0xc1, 0x15, 0xd6, 0x9f, 0x95, 0x09, 0xb8, 0x8d, 0xf2, 0xb4,
	0x72, 0x26, 0xd0, 0x4a, 0xb0, 0x1b, 0x57, 0xaa, 0x9e, 0x31, 0x55, 0x3c, 0x09, 0xfb, 0x40, 0xf8,
	0x5f, 0x55, 0x8e, 0x48, 0xbc, 0x4e, 0x87, 0xc3, 0xf7, 0x08, 0x06, 0xd9, 0x4b, 0x34, 0xb0, 0x49,
	0xc0, 0xcf, 0xcc, 0x17, 0x28, 0x73, 0x9e, 0x08, 0xc0, 0x00, 0x1d, 0xdd, 0x87, 0xce, 0xac, 0xff,
	0x00, 0x3c, 0xa9, 0x47, 0xeb, 0x10, 0xa4, 0xa7, 0x4d, 0xf6, 0xbd, 0xfd, 0x7a, 0x36, 0xbd, 0x17,
	0xf9, 0x9d, 0x2f, 0x0f, 0x87, 0x52, 0xe5, 0x6e, 0xcc, 0xf1, 0x8d, 0x4f, 0xe0, 0x9f, 0xc4, 0x8b,
	0x0d, 0x18, 0xea, 0x52, 0x68, 0x3e, 0x76, 0xd3, 0xf3, 0xdb, 0xdb, 0xdc, 0x47, 0x2c, 0xca, 0x39,
	0xe7, 0x6a, 0x93, 0xbb, 0xa0, 0xe1, 0x49, 0x3c, 0x8e, 0x3a, 0xe3, 0xce, 0xd8, 0x15, 0x62, 0xac,
	0x08, 0x65, 0x38, 0x60, 0x46, 0x08, 0x3e, 0x86, 0xbf, 0x42, 0xb4, 0xeb, 0xdb, 0x3d, 0x4a, 0xca,
	0x2b, 0xfd, 0x3e, 0xf2, 0x0b, 0xcb, 0x59, 0x46, 0x63, 0x9e, 0xde, 0x55, 0x91, 0x18, 0x7b, 0x32,
	0x92, 0x0f, 0x4f, 0xd2, 0xbc, 0x03, 0xf6, 0x9d, 0xf8, 0x67, 0x68, 0xb6, 0x72, 0x78, 0xe3, 0x43,
	0x86, 0x2b, 0x69, 0x56, 0x50, 0x35, 0x58, 0x14, 0x6d, 0x59, 0x77, 0xb0, 0x02, 0x75, 0x1d, 0x37,
	0xee, 0x20, 0x30, 0x03, 0xe6, 0xdd, 0xbb, 0x82, 0x09, 0x6e, 0x7c, 0x9f, 0x8a, 0xa7, 0x88, 0xc4,
	0x7d, 0x5f, 0x17, 0x15, 0x16, 0xf6, 0xdd, 0x6b, 0xd9, 0xa7, 0xf8, 0x0e, 0xbe, 0x0d, 0x46, 0x3c,
	0xd0, 0x77, 0x3e, 0x72, 0x3c, 0x0a, 0xd9, 0xf0, 0xae, 0x9f, 0x1d, 0xed, 0xdb, 0x5c, 0x4c, 0xbb,
	0xa2, 0xb6, 0x70, 0x54, 0x60, 0x10, 0xef, 0xd4, 0x67, 0xfd, 0xde, 0x0e, 0x38, 0xea, 0xbd, 0xb2,
	0x0e, 0x4c, 0x31, 0xb4, 0xd3, 0x24, 0x29, 0x8d, 0xf2, 0x30, 0x45, 0xcf, 0xa9, 0x38, 0x1f, 0x85,
	0x7a, 0x0d, 0x85, 0xb4, 0x76, 0x76, 0x71, 0x5b, 0x45, 0x82, 0xa8, 0xa0, 0x16, 0x0a, 0x06, 0xe3,
	0xdd, 0xb0, 0x3b, 0x9a, 0xfd, 0x13, 0x05, 0x47, 0x9a, 0x5c, 0xef, 0xa1, 0xe6, 0xcd, 0x8b, 0x79,
	0x69, 0x3d, 0xf5, 0xa4, 0xd6, 0x56, 0x89, 0xbe, 0xe2, 0xe2, 0x36, 0x8a, 0x25, 0xfe, 0xf3, 0x30,
	0x20, 0x0f, 0xcc, 0xd7, 0xdd, 0x1e, 0x31, 0xf0, 0xdc, 0xfa, 0xc0, 0x82, 0xff, 0x00, 0x4c, 0xbf,
	0x16, 0x1a, 0xbd, 0xa4, 0x72, 0x25, 0xb4, 0xb2, 0xc6, 0x65, 0x81, 0xd5, 0xf6, 0x96, 0x49, 0x62,
	0x0c, 0xbb, 0x97, 0x28, 0x84, 0x15, 0x65, 0x70, 0x57, 0x86, 0x01, 0x9d, 0x5b, 0xe4, 0x0f, 0x84,
	0x1a, 0x2e, 0xa5, 0xaf, 0xfc, 0x4a, 0xd0, 0x6c, 0xac, 0x14, 0x30, 0xb5, 0xd5, 0x63, 0xbc, 0xbb,
	0x73, 0x83, 0xe5, 0xdb, 0xc5, 0x28, 0x98, 0xe4, 0x1e, 0xb9, 0x01, 0x63, 0xe3, 0xfb, 0xe3, 0xad,
	0x7d, 0xaa, 0xfa, 0xbe, 0x94, 0xba, 0xda, 0x68, 0x2d, 0x7d, 0x07, 0xf6, 0x9c, 0x96, 0xcf, 0x74,
	0xb6, 0x9b, 0xb3, 0x27, 0x92, 0xac, 0xa8, 0xce, 0x47, 0x65, 0xdc, 0xea, 0x32, 0x7a, 0x93, 0xc7,
	0x43, 0x5d, 0xf8, 0x8a, 0x14, 0xf1, 0x14, 0xdd, 0x2a, 0xb1, 0x4e, 0x2f, 0x74, 0xc9, 0x84, 0x9c,
	0x5d, 0xd1, 0xf0, 0xef, 0xed, 0x71, 0xa4, 0x41, 0xa3, 0xfc, 0x54, 0x8a, 0x24, 0x9e, 0x6b, 0x9b,
	0xa9, 0xb4, 0xd8, 0xa6, 0xbc, 0x9e, 0x46, 0x3f, 0xbc, 0x94, 0xbc, 0x9f, 0x75, 0x72, 0x44, 0x68,
	0x14, 0x22, 0xaa, 0x8e, 0x81, 0x41, 0x25, 0x98, 0xb3, 0x1f, 0x1e, 0x3d, 0x2b, 0xdf, 0x3f, 0x6e,
	0x3b, 0x4b, 0xcf, 0xf8, 0x5b, 0x76, 0x77, 0x91, 0x5a, 0x16, 0xb4, 0x4d, 0x0a, 0xdc, 0xcb, 0x22,
	0x0e, 0x77, 0x19, 0xee, 0x46, 0x70, 0x39, 0x61, 0x85, 0x50, 0x7d, 0x38, 0xed, 0x9c, 0x79, 0xa7,
	0xc1, 0xcf, 0x0e, 0x41, 0xe2, 0x6f, 0x1e, 0x59, 0x59, 0xdf, 0x5b, 0xc9, 0x75, 0xa7, 0xdb, 0x83,
	0x73, 0x79, 0x14, 0x7c, 0x79, 0x88, 0xb8, 0xdb, 0x19, 0x23, 0x95, 0x0f, 0x23, 0x47, 0x1e, 0xee,
	0xde, 0x66, 0x6b, 0xc9, 0xa9, 0x86, 0x54, 0xe6, 0xa1, 0x05, 0x65, 0xd0, 0xa9, 0x4e, 0xd1, 0x72,
	0x93, 0x38, 0xe0, 0x17, 0xcc, 0x49, 0x31, 0xb9, 0xa3, 0x60, 0xcb, 0x90, 0x0e, 0xd2, 0x3b, 0x8f,
	0x7f, 0x7a, 0xde, 0x9f, 0xc6, 0x5e, 0x2b, 0x9f, 0xc3, 0x37, 0x9e, 0x1c, 0x9b, 0x5f, 0xbe, 0xb8,
	0xd2, 0xee, 0xf6, 0x79, 0x90, 0x5c, 0xb0, 0x9d, 0x57, 0x63, 0x87, 0x50, 0x82, 0x40, 0xdb, 0x00,
	0x20, 0x70, 0xb8, 0x1c, 0x0f, 0x6a, 0xfb, 0x2a, 0x5f, 0x03, 0x68, 0x4d, 0x6f, 0xba, 0xe6, 0x3d,
	0x5b, 0x52, 0x9d, 0x53, 0x3b, 0x5b, 0x57, 0xb9, 0x5f, 0x39, 0xc7, 0x60, 0x82, 0x40, 0x8b, 0x93,
	0xd1, 0x54, 0x05, 0x19, 0xc0, 0x00, 0x71, 0x5f, 0x35, 0x7e, 0xd2, 0x56, 0xde, 0x1f, 0xd2, 0xfc,
	0x61, 0x6b, 0xa1, 0x68, 0xda, 0x55, 0x96, 0x9f, 0x75, 0xa7, 0x59, 0xc5, 0x15, 0xf2, 0x5a, 0x5b,
	0x79, 0x29, 0xe6, 0x15, 0x07, 0x07, 0x8c, 0xc8, 0xd9, 0x2c, 0xde, 0x69, 0x24, 0xb0, 0x75, 0x07,
	0x95, 0x24, 0x95, 0xb0, 0x90, 0x71, 0xe6, 0x96, 0xb6, 0x6b, 0x75, 0xd4, 0xe2, 0xc1, 0x66, 0x51,
	0xc5, 0x36, 0xa0, 0x9a, 0xb1, 0xc4, 0x78, 0x3a, 0xd1, 0x66, 0xd4, 0x5e, 0xe4, 0xbb, 0x03, 0x6e,
	0x38, 0x41, 0xdc, 0xb0, 0x20, 0x7e, 0x18, 0xdd, 0xf8, 0xe2, 0xba, 0xa9, 0x2e, 0x21, 0x8e, 0xdb,
	0xed, 0x32, 0x49, 0x1a, 0xc3, 0x80, 0x7c, 0xc6, 0x7c, 0x2e, 0x0f, 0x4e, 0x4d, 0x66, 0xf8, 0x4a,
	0x00, 0xba, 0x4c, 0x22, 0x15, 0x92, 0x69, 0x6e, 0x24, 0x38, 0x48, 0xc1, 0x76, 0x67, 0x2d, 0xb1,
	0x51, 0x54, 0x64, 0x92, 0x48, 0x00, 0x01, 0xc9, 0x27, 0x18, 0xc9, 0xaf, 0x62, 0xfd, 0x9e, 0x7c,
	0x13, 0xa9, 0x43, 0xe2, 0xab, 0xaf, 0x11, 0xea, 0xba, 0x7e, 0xa7, 0x65, 0x6f, 0x64, 0x24, 0x36,
	0x09, 0x7f, 0x6d, 0x25, 0xb3, 0x89, 0xe6, 0x18, 0x90, 0xaa, 0x3a, 0xab, 0x10, 0xaa, 0x5f, 0x07,
	0x18, 0xcc, 0xc4, 0x03, 0xf2, 0xf1, 0x58, 0xfc, 0xca, 0x8e, 0x51, 0x81, 0x75, 0xaa, 0x5a, 0xe9,
	0x5d, 0x2b, 0xd9, 0xb6, 0xfa, 0x1d, 0xf4, 0xe9, 0x3a, 0xb5, 0x39, 0x51, 0x9f, 0xf0, 0x07, 0xc5,
	0x7e, 0x0e, 0xf8, 0x7b, 0xe2, 0x5f, 0x10, 0xf8, 0x87, 0x5e, 0xb4, 0xd7, 0x26, 0xbf, 0xbb, 0xb0,
	0xc5, 0xac, 0xd6, 0xd0, 0x99, 0xe2, 0x65, 0x52, 0xbb, 0xa0, 0x44, 0x5e, 0x52, 0x47, 0x2b, 0x11,
	0xdc, 0xdf, 0x21, 0x08, 0x32, 0xcb, 0x8f, 0x99, 0xff, 0x00, 0x10, 0x7e, 0x2c, 0xeb, 0xbe, 0x21,
	0xf1, 0x59, 0xd6, 0xad, 0x2d, 0xec, 0x74, 0x4b, 0x5b, 0x17, 0x49, 0x2c, 0x19, 0x63, 0xcd, 0xd0,
	0xf2, 0xd9, 0x59, 0x5e, 0x69, 0x09, 0x2a, 0x58, 0x62, 0x45, 0x01, 0x46, 0x15, 0x27, 0x99, 0x32,
	0xe1, 0xd8, 0x9b, 0x7e, 0x3b, 0xf8, 0x59, 0xaf, 0x0f, 0x1c, 0x4c, 0xbe, 0x14, 0xd0, 0xe2, 0x7d,
	0x1a, 0xf5, 0xd6, 0x65, 0x71, 0x3c, 0x71, 0x43, 0x66, 0xec, 0x7f, 0x7a, 0x18, 0x33, 0xef, 0x23,
	0x76, 0x64, 0xf9, 0x10, 0x8c, 0x38, 0x50, 0x3e, 0x5a, 0xd8, 0xf8, 0x55, 0xf0, 0x7f, 0x50, 0xbe,
	0xf8, 0x95, 0x71, 0x26, 0xa9, 0x30, 0x5d, 0x1b, 0xc3, 0x5a, 0x85, 0xbc, 0x9e, 0x6c, 0x78, 0x1f,
	0xda, 0x13, 0x88, 0x92, 0x74, 0x8c, 0x2e, 0x72, 0xa8, 0x85, 0xe3, 0x2c, 0x4f, 0xde, 0x23, 0x68,
	0xc8, 0x2c, 0x45, 0xe5, 0xb9, 0xe6, 0x0b, 0x30, 0x51, 0x54, 0x66, 0x9c, 0x9a, 0xe6, 0xe5, 0xea,
	0xbb, 0xdf, 0xd1, 0xb0, 0xa9, 0x42, 0x74, 0xfe, 0x24, 0x72, 0x5f, 0x11, 0x3c, 0x63, 0xad, 0xfc,
	0x43, 0xf0, 0xe6, 0x87, 0xe2, 0x0b, 0xdb, 0x5d, 0x34, 0x4d, 0xa6, 0x5e, 0xdc, 0xe9, 0xf7, 0xe6,
	0xc5, 0x8b, 0x6f, 0x8a, 0x64, 0x49, 0x6d, 0x67, 0x60, 0x7e, 0xea, 0x93, 0x0d, 0xc4, 0x64, 0x64,
	0x8d, 0xf9, 0xc7, 0xde, 0xc0, 0xe8, 0xbe, 0x06, 0xf8, 0x5b, 0x43, 0x3e, 0x14, 0xd5, 0xbc, 0x47,
	0xe7, 0x45, 0xa4, 0xdf, 0xc9, 0x78, 0x63, 0x92, 0x7f, 0xb7, 0x7d, 0x8d, 0xa4, 0xb7, 0x44, 0x2d,
	0xc1, 0x05, 0x49, 0x0d, 0x27, 0x99, 0xb8, 0x96, 0x00, 0x9b, 0x71, 0xfd, 0xc3, 0x8e, 0xbb, 0xe3,
	0x1f, 0x83, 0xfc, 0x5b, 0x6d, 0xe1, 0xfd, 0x77, 0xc3, 0x5e, 0x1b, 0xd3, 0xc5, 0xed, 0xa6, 0xa9,
	0x6c, 0x92, 0xd9, 0xcd, 0x6f, 0x6a, 0x54, 0xdb, 0x2d, 0x9e, 0xfb, 0x88, 0x6d, 0x8a, 0xc6, 0x36,
	0x90, 0x0c, 0x29, 0x00, 0x66, 0x2a, 0xce, 0xb7, 0x31, 0x81, 0x93, 0x03, 0x17, 0xf2, 0x6f, 0x80,
	0xfe, 0x3f, 0xf1, 0x8e, 0xaa, 0xcb, 0xf0, 0xf6, 0x1d, 0x07, 0x49, 0xd7, 0x34, 0x29, 0x83, 0xcb,
	0x3c, 0x17, 0x9b, 0x90, 0x5b, 0xa3, 0x3e, 0xf7, 0x7f, 0x31, 0x7f, 0x83, 0x79, 0xc8, 0x52, 0x18,
	0x96, 0x20, 0x06, 0xc6, 0x00, 0xee, 0xc4, 0xe2, 0x68, 0xe1, 0xe0, 0xe5, 0x5a, 0x56, 0x8a, 0xea,
	0x73, 0x4e, 0x8b, 0xad, 0x1e, 0x45, 0xd4, 0xea, 0xfe, 0x23, 0x78, 0x8f, 0xc5, 0x1e, 0x17, 0xd4,
	0xec, 0x2d, 0xa0, 0x87, 0x59, 0xb5, 0xb9, 0x8a, 0x7f, 0xb4, 0xf9, 0x3a, 0xb4, 0xd1, 0x5c, 0x5b,
	0xba, 0x85, 0x65, 0x47, 0x06, 0x19, 0x19, 0x98, 0x89, 0x08, 0x60, 0x0b, 0x80, 0x0c, 0x5c, 0xaf,
	0xdd, 0xaf, 0x9b, 0xfc, 0x4b, 0x79, 0x79, 0xa8, 0x78, 0x82, 0xfa, 0xf2, 0xfe, 0x61, 0x2d, 0xc4,
	0x93, 0xb1, 0x91, 0x94, 0xb1, 0x5c, 0xe4, 0xf0, 0xbb, 0x89, 0x21, 0x47, 0x40, 0x09, 0x38, 0x03,
	0x15, 0xf6, 0x97, 0xc5, 0x2f, 0x06, 0xaf, 0x8f, 0x56, 0xd3, 0x31, 0x69, 0x9a, 0x1d, 0xc5, 0xbc,
	0xbf, 0x35, 0xf5, 0xb5, 0xbf, 0x9f, 0x73, 0x2c, 0x44, 0xb1, 0x78, 0xd5, 0x8e, 0xc1, 0x1e, 0x59,
	0x8b, 0x64, 0xf9, 0x83, 0x76, 0x0e, 0x3d, 0x7e, 0x6a, 0xf8, 0xe1, 0x07, 0xc2, 0xed, 0x39, 0xad,
	0xb4, 0xaf, 0x00, 0xda, 0x34, 0x97, 0x90, 0xc8, 0x45, 0xdd, 0xca, 0x5d, 0xc9, 0x24, 0x4a, 0x14,
	0x60, 0x27, 0xcf, 0x9f, 0x30, 0x9c, 0xee, 0x2c, 0xac, 0x00, 0xc7, 0x7c, 0x9c, 0x7c, 0xe7, 0xfa,
	0xc1, 0x80, 0xc6, 0xd4, 0x8d, 0x0c, 0x23, 0x73, 0xea, 0xda, 0x4e, 0xcb, 0xd5, 0xb4, 0x8d, 0x70,
	0xf9, 0x73, 0xc2, 0x53, 0xda, 0xdf, 0x9b, 0x33, 0x7c, 0x07, 0x73, 0x15, 0x8c, 0x1a, 0x36, 0xa2,
	0xe9, 0x34, 0x91, 0xd8, 0x5f, 0xc5, 0x78, 0xc9, 0x0e, 0x3c, 0xc7, 0xf2, 0x6e, 0x44, 0x85, 0x57,
	0x3d, 0xce, 0xcc, 0x7e, 0x35, 0xf6, 0x76, 0x91, 0x7f, 0x69, 0xa9, 0xe9, 0x96, 0xba, 0xad, 0x84,
	0xde, 0x75, 0xa5, 0xe4, 0x09, 0x3c, 0x12, 0x60, 0x8d, 0xf1, 0xba, 0x86, 0x53, 0x83, 0xcf, 0x20,
	0x8a, 0xf8, 0x73, 0xc1, 0xfa, 0x8d, 0xb0, 0xb6, 0x1a, 0x73, 0x14, 0x89, 0xd5, 0x9b, 0xca, 0x39,
	0xe2, 0x4d, 0xcd, 0xb8, 0xf6, 0xe0, 0xe5, 0xba, 0x67, 0x9c, 0x8c, 0x77, 0xc7, 0xd0, 0x5f, 0xb3,
	0xff, 0x00, 0x8c, 0x8a, 0xcc, 0x7c, 0x1d, 0xa8, 0xcb, 0x33, 0x3c, 0x8c, 0xf3, 0xe9, 0x92, 0x3f,
	0x20, 0x2e, 0xdd, 0xd2, 0x41, 0x90, 0xa3, 0x18, 0xc3, 0x48, 0xb9, 0x24, 0x90, 0x5c, 0x70, 0x11,
	0x73, 0xe4, 0x71, 0xce, 0x53, 0x2c, 0x6e, 0x0e, 0x18, 0xca, 0x5a, 0xba, 0x7b, 0xaf, 0x2e, 0xbf,
	0x77, 0xe5, 0x73, 0xd0, 0xcb, 0xeb, 0x28, 0x4d, 0xc2, 0x5d, 0x4f, 0x69, 0x62, 0x02, 0x92, 0x58,
	0x01, 0xdf, 0x26, 0xb4, 0xfe, 0x13, 0xb5, 0xf5, 0xc7, 0x82, 0xad, 0x75, 0x5d, 0x4a, 0xc9, 0x6c,
	0xef, 0x35, 0x39, 0x25, 0xbe, 0x78, 0xf6, 0x80, 0xe2, 0x39, 0x1c, 0x98, 0x04, 0x98, 0xcf, 0xef,
	0x04, 0x1e, 0x4a, 0xb7, 0x27, 0x05, 0x71, 0xd0, 0x0a, 0xe6, 0xbc, 0x59, 0x68, 0x75, 0x2f, 0x0b,
	0xea, 0xba, 0x6a, 0xb9, 0x47, 0xbd, 0xb3, 0x96, 0xd6, 0x32, 0x1b, 0x07, 0x7c, 0xaa, 0x63, 0x5c,
	0x1f, 0x5d, 0xcc, 0x00, 0xf7, 0xc5, 0x77, 0x1e, 0x26, 0xf1, 0x47, 0x87, 0x3c, 0x31, 0x69, 0x1d,
	0xcf, 0x88, 0xf5, 0xdd, 0x3b, 0x48, 0x8a, 0x62, 0xcb, 0x11, 0xbc, 0xb9, 0x48, 0x8c, 0xac, 0x06,
	0x4a, 0xa0, 0x62, 0x0b, 0x36, 0x3b, 0x2e, 0x4d, 0x79, 0xfe, 0x1c, 0x61, 0x23, 0xcb, 0x5b, 0x12,
	0xf7, 0xd2, 0x2b, 0xf3, 0x7f, 0xa1, 0xa6, 0x67, 0x3d, 0x63, 0x12, 0xf6, 0xb3, 0x7f, 0x69, 0xa5,
	0x69, 0x37, 0x7a, 0xa6, 0xa1, 0x30, 0x86, 0xce, 0xce, 0x07, 0xb8, 0xb8, 0x90, 0x82, 0x76, 0x46,
	0x8a, 0x59, 0x9b, 0x8e, 0x78, 0x00, 0x9a, 0xf1, 0x7f, 0x83, 0x7e, 0x09, 0xb4, 0xf0, 0x47, 0x83,
	0x2d, 0xed, 0x16, 0x3f, 0xf8, 0x99, 0xdd, 0xc7, 0x1d, 0xc6, 0xa5, 0x21, 0x60, 0xc5, 0xa7, 0x28,
	0x03, 0x28, 0x38, 0x1f, 0x22, 0x92, 0x42, 0x8f, 0x72, 0x7a, 0xb1, 0x26, 0xc5, 0xe7, 0x8a, 0xf4,
	0xff, 0x00, 0x8c, 0xba, 0x34, 0xda, 0x6e, 0x96, 0x10, 0x78, 0x45, 0xa5, 0x68, 0xb5, 0x11, 0x2c,
	0x9b, 0x6e, 0xaf, 0xd5, 0x58, 0xed, 0x55, 0x58, 0xdf, 0x74, 0x11, 0x31, 0x50, 0xdb, 0x9f, 0x0e,
	0xca, 0x19, 0x76, 0x20, 0x3b, 0x8f, 0x8e, 0x7c, 0x78, 0xf8, 0xd3, 0x78, 0xd7, 0xb7, 0x9e, 0x15,
	0xf0, 0x9c, 0xd7, 0x76, 0x46, 0xda, 0x63, 0x15, 0xe6, 0xa7, 0x1c, 0x81, 0x5a, 0x56, 0x1b, 0x95,
	0xe2, 0x8c, 0x63, 0x2a, 0xa0, 0x95, 0xfd, 0xe0, 0x2a, 0xc4, 0x82, 0x06, 0x07, 0x2d, 0xd5, 0xc5,
	0x55, 0x67, 0x9c, 0x57, 0x8e, 0x5b, 0x83, 0x7f, 0x0e, 0xb3, 0x7d, 0x17, 0x65, 0xe7, 0xe8, 0xbc,
	0xbc, 0xed, 0x18, 0x54, 0xa8, 0x45, 0xd5, 0x9f, 0xc8, 0xf5, 0x9f, 0x89, 0x3f, 0x12, 0xfc, 0x25,
	0xe0, 0xa4, 0xfb, 0x1e, 0xb3, 0x70, 0xd7, 0x57, 0x92, 0x26, 0x7f, 0xb3, 0xe0, 0x41, 0x24, 0xa5,
	0x0f, 0x77, 0x04, 0x80, 0xaa, 0x73, 0xfc, 0x44, 0x67, 0x9c, 0x67, 0x06, 0xbe, 0x2a, 0xbf, 0x92,
	0x09, 0x6f, 0xae, 0x25, 0xb5, 0xb4, 0x4b, 0x2b, 0x79, 0x25, 0x67, 0x8a, 0xdd, 0x1c, 0xba, 0xc4,
	0xa4, 0xe4, 0x20, 0x63, 0xcb, 0x00, 0x0e, 0x32, 0x79, 0x35, 0x00, 0x00, 0x74, 0x00, 0x0e, 0xc0,
	0x0e, 0xd4, 0xb5, 0xdb, 0x93, 0x64, 0x94, 0x72, 0xaa, 0x6e, 0x30, 0x93, 0x6e, 0x56, 0xbb, 0xf4,
	0xf2, 0x32, 0xaf, 0x88, 0x95, 0x67, 0xae, 0x82, 0x11, 0x92, 0x09, 0xe4, 0x82, 0x08, 0xf6, 0x20,
	0xe4, 0x7e, 0x46, 0xaf, 0x58, 0x6a, 0xfa, 0xbd, 0x85, 0xd2, 0x5d, 0xd9, 0x6a, 0xfa, 0x8d, 0xbd,
	0xc4, 0x6f, 0xbd, 0x24, 0x8e, 0xee, 0x40, 0xca, 0x7d, 0x8e, 0x78, 0xff, 0x00, 0x0e, 0x3a, 0x51,
	0x45, 0x7b, 0x77, 0x7c, 0xae, 0x3d, 0x19, 0xcf, 0xd4, 0xf4, 0xcf, 0x14, 0xfc, 0x65, 0xf1, 0x9f,
	0x88, 0xbc, 0x25, 0x04, 0x32, 0xcd, 0x6d, 0xa7, 0x49, 0x16, 0xa0, 0xad, 0xf6, 0x8b, 0x01, 0x24,
	0x52, 0xb1, 0x85, 0x51, 0xd0, 0x93, 0xbc, 0xff, 0x00, 0x19, 0x0c, 0x70, 0x07, 0x2a, 0x3a, 0x73,
	0x9f, 0x35, 0xf1, 0x06, 0xb7, 0xac, 0x78, 0x87, 0x50, 0xfe, 0xd0, 0xd7, 0x75, 0x3b, 0xcd, 0x4e,
	0xec, 0x2e, 0xc1, 0x35, 0xd4, 0xcd, 0x23, 0x85, 0xe3, 0x80, 0x4f, 0x41, 0xc0, 0xe9, 0x45, 0x15,
	0xe4, 0x64, 0xb0, 0x8d, 0x2c, 0x3c, 0xa3, 0x05, 0x65, 0xcc, 0xf4, 0x5a, 0x1b, 0xe2, 0x5b, 0x73,
	0x4d, 0x8e, 0xd1, 0x75, 0xed, 0x73, 0x43, 0x79, 0xdf, 0x45, 0xd6, 0x75, 0x1d, 0x31, 0xae, 0x02,
	0x89, 0x8d, 0x9d, 0xcb, 0xc2, 0x64, 0x0b, 0x9d, 0xbb, 0xb6, 0x91, 0x9c, 0x6e, 0x6c, 0x67, 0xa6, 
	0x4f, 0xad, 0x66, 0x9e, 0x4e, 0x4f, 0x5a, 0x28, 0xaf, 0x53, 0x95, 0x5d, 0xbb, 0x6a, 0xcc, 0x6e,
	0x2d, 0x14, 0x51, 0x4c, 0x0f, 0xff, 0xd9
    };
    /* printf("size of tux = %d\n", sizeof(tux)); */
    memcpy(dest, tux, SIZE_OF_TUX);
    return 0;
}

/* vi: set ts=8 sw=4 sts=4 noexpandtab: cin */
/* ex: set tabstop=4 expandtab: */
/* Local Variables: */
/* indent-tabs-mode: t */
/* c-basic-offset: 8 */
/* End: */
