/* fuji.c:
 *
 * Fuji Camera library for the gphoto project.
 *
 * © 2001 Matthew G. Martin <matt.martin@ieee.org>
 *     2002 Lutz Mueller <lutz@users.sourceforge.net>
 *
 * This routine works for Fuji DS-7 and DX-5,7,10 and
 * MX-500,600,700,1200,1700,2700,2900,  Apple QuickTake 200,
 * Samsung Kenox SSC-350N,Leica Digilux Zoom cameras and possibly others.
 *
 * Preview and take_picture fixes and preview conversion integrated
 * by Michael Smith <michael@csuite.ns.ca>.
 *
 * This driver was reworked from the "fujiplay" package:
 *    * A program to control Fujifilm digital cameras, like
 *    * the DS-7 and MX-700, and their clones.
 *    * Written by Thierry Bousch <bousch@topo.math.u-psud.fr>
 *    * and released in the public domain.
 *
 *  Portions of this code were adapted from
 *  GDS7 v0.1 interactive digital image transfer software for DS-7 camera
 *  Copyright 1998 Matthew G. Martin

 *  Some of which was derived from get_ds7 , a Perl Language library
 *  Copyright 1997 Mamoru Ohno
 *
 *  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, or
 *  (at your option) any later version.
 *
 *  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 Street, Fifth Floor,
 *  Boston, MA  02110-1301  USA
 */
#include "config.h"
#include "fuji.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <gphoto2/gphoto2-port-log.h>

#ifdef ENABLE_NLS
#  include <libintl.h>
#  undef _
#  define _(String) dgettext (GETTEXT_PACKAGE, String)
#  ifdef gettext_noop
#    define N_(String) gettext_noop (String)
#  else
#    define N_(String) (String)
#  endif
#else
#  define textdomain(String) (String)
#  define gettext(String) (String)
#  define dgettext(Domain,Message) (Message)
#  define dcgettext(Domain,Message,Type) (Message)
#  define bindtextdomain(Domain,Directory) (Domain)
#  define _(String) (String)
#  define N_(String) (String)
#endif

#ifndef MIN
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
#endif

#define GP_MODULE "fuji"

#define CR(result)    {int __r = (result); if (__r < 0) return (__r);}
#define CRF(result,d) {int __r = (result); if (__r < 0) {free (d); return (__r);}}
#define CLEN(buf_len,required)					\
{								\
	if (buf_len < required) {				\
		gp_context_error (context, _("The camera sent "	\
			"only %i byte(s), but we need at "	\
			"least %i."), buf_len, required);	\
		return (GP_ERROR);				\
	}							\
}


#define STX 0x02 /* Start of data */
#define ETX 0x03 /* End of data */
#define EOT 0x04 /* End of session */
#define ENQ 0x05 /* Enquiry */
#define ACK 0x06
#define ESC 0x10
#define ETB 0x17 /* End of transmission block */
#define NAK 0x15

#define FUJI_ACK 0x00
#define FUJI_NAK 0x01

static int fuji_reset (Camera *camera, GPContext *context);

int
fuji_ping (Camera *camera, GPContext *context)
{
	unsigned char b;
	unsigned int i;
	int r;

	GP_DEBUG ("Pinging camera...");

	/* Drain input */
	while (gp_port_read (camera->port, (char *)&b, 1) >= 0);

	i = 0;
	while (1) {
		b = ENQ;
		CR (gp_port_write (camera->port, (char *)&b, 1));
		r = gp_port_read (camera->port, (char *)&b, 1);
		if ((r >= 0) && (b == ACK))
			return (GP_OK);
		i++;
		if (i >= 3) {
			gp_context_error (context, _("Could not contact "
				"camera."));
			return (GP_ERROR);
		}
	}
}

static int
fuji_send (Camera *camera, unsigned char *cmd, unsigned int cmd_len,
	   unsigned char last, GPContext *context)
{
        unsigned char b[1024], check;
        unsigned int i;

        /* Send header */
        b[0] = ESC;
        b[1] = STX;
        CR (gp_port_write (camera->port, (char *)b, 2));

        /*
	 * Escape the data we are going to send.
	 * Calculate the checksum.
	 */
	check = (last ? ETX : ETB);
        memcpy (b, cmd, cmd_len);
        for (i = 0; i < cmd_len; i++) {
		check ^= b[i];
                if (b[i] == ESC) {
                        memmove (b + i + 1, b + i, cmd_len - i);
                        b[i] = ESC;
                        i++;
                        cmd_len++;
                }
        }

        /* Send data */
        CR (gp_port_write (camera->port, (char *)b, cmd_len));

        /* Send footer */
        b[0] = ESC;
        b[1] = (last ? ETX : ETB);
        b[2] = check;
        CR (gp_port_write (camera->port, (char *)b, 3));

	return (GP_OK);
}

static int
fuji_recv (Camera *camera, unsigned char *buf, unsigned int *buf_len,
	   unsigned char *last, GPContext *context)
{
	unsigned char b[1024], check = 0;
	int i;

	/*
	 * Read the header. The checksum covers all bytes between
	 * ESC STX and ESC ET[X,B].
	 */
	CR (gp_port_read (camera->port, (char *)b, 6));

	/* Verify the header */
	if ((b[0] != ESC) || (b[1] != STX)) {
		gp_context_error (context, _("Received unexpected data "
			"(0x%02x, 0x%02x)."), b[0], b[1]);
		return (GP_ERROR_CORRUPTED_DATA);
	}

	/*
	 * We don't know the meaning of b[2] and b[3].
	 * We have seen 0x00 0x4d and 0x00 0x06.
	 */
	check ^= b[2];
	check ^= b[3];

	/* Determine the length of the packet. */
	*buf_len = ((b[5] << 8) | b[4]);
	check ^= b[4];
	check ^= b[5];
	GP_DEBUG ("We are expecting %i byte(s) data (excluding ESC quotes). "
		  "Let's read them...", *buf_len);

	/* Read the data. Unescape it. Calculate the checksum. */
	for (i = 0; i < *buf_len; i++) {
		CR (gp_port_read (camera->port, (char *)buf + i, 1));
		if (buf[i] == ESC) {
			CR (gp_port_read (camera->port, (char *)buf + i, 1));
			if (buf[i] != ESC) {
				gp_context_error (context,
					_("Wrong escape sequence: "
					"expected 0x%02x, got 0x%02x."),
					ESC, buf[i]);

				/* Dump the remaining data */
				while (gp_port_read (camera->port, (char *)b, 1) >= 0);

				return (GP_ERROR_CORRUPTED_DATA);
			}
		}
		check ^= buf[i];
	}

	/* Read the footer */
	CR (gp_port_read (camera->port, (char *)b, 3));
	if (b[0] != ESC) {
		gp_context_error (context,
			_("Bad data - got 0x%02x, expected 0x%02x."),
			b[0], ESC);
		return (GP_ERROR_CORRUPTED_DATA);
	}
	switch (b[1]) {
	case ETX:
		*last = 1;
		break;
	case ETB:
		*last = 0;
		break;
	default:
		gp_context_error (context,
			_("Bad data - got 0x%02x, expected 0x%02x or "
			  "0x%02x."), b[1], ETX, ETB);
		return (GP_ERROR_CORRUPTED_DATA);
	}
	check ^= b[1];
	if (check != b[2]) {
		gp_context_error (context,
			_("Bad checksum - got 0x%02x, expected 0x%02x."),
			b[2], check);
		return (GP_ERROR_CORRUPTED_DATA);
	}

	return (GP_OK);
}

static int
fuji_transmit (Camera *camera, unsigned char *cmd, unsigned int cmd_len,
	       unsigned char *buf, unsigned int *buf_len, GPContext *context)
{
	unsigned char last = 0, c, p;
	unsigned int b_len = 1024;
	int r, retries = 0, id = 0;

	/*
	 * Send the command. If we fail the first time, we only try once more.
	 * After the third time, the camera would reset itself automatically.
	 */
	retries = 0;
	while (1) {

		/* Give the user the possibility to cancel. */
		if (gp_context_cancel (context) == GP_CONTEXT_FEEDBACK_CANCEL)
			return (GP_ERROR_CANCEL);

		CR (fuji_send (camera, cmd, cmd_len, 1, context));

		/* Receive ACK (hopefully) */
		CR (gp_port_read (camera->port, (char *)&c, 1));
		switch (c) {
		case ACK:
			break;
		case NAK:

			/* Camera didn't like the command */
			if (++retries > 1) {
				gp_context_error (context, _("Camera rejected "
					"the command."));
				return (GP_ERROR);
			}
			continue;

		case EOT:

			/* Camera got fed up and reset itself. */
			gp_context_error (context, _("Camera reset itself."));
			return (GP_ERROR);

		default:
			gp_context_error (context, _("Camera sent unexpected "
				"byte 0x%02x."), c);
			return (GP_ERROR_CORRUPTED_DATA);
		}

		break;
	}

	/*
	 * Read the response. If we expect more than 1024 bytes, show
	 * progress information.
	 */
	p = ((*buf_len > 1024) ? 1 : 0);
	if (p)
		id = gp_context_progress_start (context, *buf_len,
					        _("Downloading..."));
	*buf_len = 0;
	retries = 0;
	while (!last) {
		r = fuji_recv (camera, buf + *buf_len, &b_len, &last, context);
		if (r < 0) {
			retries++;
			while (gp_port_read (camera->port, (char *)&c, 1) >= 0);
			if (++retries > 2)
			    return (r);
			GP_DEBUG ("Retrying...");
			c = NAK;
			CR (gp_port_write (camera->port, (char *)&c, 1));
			continue;
		}

		/* Give the user the possibility to cancel. */
		if (gp_context_cancel (context) == GP_CONTEXT_FEEDBACK_CANCEL) {
			CR (fuji_reset (camera, context));
			return (GP_ERROR_CANCEL);
		}

		/* Acknowledge this packet. */
		c = ACK;
		CR (gp_port_write (camera->port, (char *)&c, 1));
		*buf_len += b_len;

		if (p)
			gp_context_progress_update (context, id, *buf_len);
	}
	if (p)
		gp_context_progress_stop (context, id);

	return (GP_OK);
}

int
fuji_get_cmds (Camera *camera, unsigned char *cmds, GPContext *context)
{
	unsigned char cmd[4], buf[1024];
	unsigned int i, buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_CMDS_VALID;
	cmd[2] = 0;
	cmd[3] = 0;
	CR (fuji_transmit (camera, cmd, 4, buf, &buf_len, context));

	memset (cmds, 0, 0xff);
	for (i = 0; i < buf_len; i++)
		cmds[buf[i]] = 1;

	return (GP_OK);
}

int
fuji_pic_count (Camera *camera, unsigned int *n, GPContext *context)
{
	unsigned char cmd[4], buf[1024];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_PIC_COUNT;
	cmd[2] = 0;
	cmd[3] = 0;
	CR (fuji_transmit (camera, cmd, 4, buf, &buf_len, context));

	CLEN (buf_len, 2);

	*n = (buf[1] << 8) | buf[0];

	return (GP_OK);
}

int
fuji_pic_name (Camera *camera, unsigned int i, const char **name,
	       GPContext *context)
{
	static unsigned char buf[1025];
	unsigned char cmd[6];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_PIC_NAME;
	cmd[2] = 2;
	cmd[3] = 0;
	cmd[4] = i;
	cmd[5] = (i >> 8);

	memset (buf, 0, sizeof (buf));
	CR (fuji_transmit (camera, cmd, 6, buf, &buf_len, context));
	*name = (char *)buf;

	return (GP_OK);
}

int
fuji_version (Camera *camera, const char **version, GPContext *context)
{
	static unsigned char buf[1025];
	unsigned char cmd[4];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_VERSION;
	cmd[2] = 0;
	cmd[3] = 0;

	memset (buf, 0, sizeof (buf));
	CR (fuji_transmit (camera, cmd, 4, buf, &buf_len, context));
	*version = (char *)buf;

	return (GP_OK);
}

int
fuji_model (Camera *camera, const char **model, GPContext *context)
{
	static unsigned char buf[1025];
	unsigned char cmd[4];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_MODEL;
	cmd[2] = 0;
	cmd[3] = 0;

	memset (buf, 0, sizeof (buf));
	CR (fuji_transmit (camera, cmd, 4, buf, &buf_len, context));
	*model = (char *)buf;

	return (GP_OK);
}

int
fuji_id_get (Camera *camera, const char **id, GPContext *context)
{
	static unsigned char buf[1025];
	unsigned char cmd[4];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_ID_GET;
	cmd[2] = 0;
	cmd[3] = 0;

	memset (buf, 0, sizeof (buf));
	CR (fuji_transmit (camera, cmd, 4, buf, &buf_len, context));
	*id = (char *)buf;

	return (GP_OK);
}

int
fuji_id_set (Camera *camera, const char *id, GPContext *context)
{
	unsigned char cmd[14], buf[1025];
	unsigned int buf_len = 0;

	/* It seems that the ID must be 10 chars or less */
	cmd[0] = 0;
	cmd[1] = FUJI_CMD_ID_SET;
	cmd[2] = 0x0a;
	cmd[3] = 0x00;
	memcpy (cmd + 4, id, MIN (strlen (id) + 1, 10));

	CR (fuji_transmit (camera, cmd, 14, buf, &buf_len, context));

	return (GP_OK);
}

int
fuji_pic_del (Camera *camera, unsigned int i, GPContext *context)
{
	unsigned char cmd[6], buf[1024];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_PIC_NAME;
	cmd[2] = 2;
	cmd[3] = 0;
	cmd[4] = i;
	cmd[5] = (i >> 8);

	CR (fuji_transmit (camera, cmd, 6, buf, &buf_len, context));

	return (GP_OK);
}

int
fuji_pic_size (Camera *camera, unsigned int i, unsigned int *size,
	       GPContext *context)
{
	unsigned char cmd[6], buf[1024];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_PIC_SIZE;
	cmd[2] = 2;
	cmd[3] = 0;
	cmd[4] = i;
	cmd[5] = (i >> 8);

	CR (fuji_transmit (camera, cmd, 6, buf, &buf_len, context));
	CLEN (buf_len, 4);

	*size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);

	return (GP_OK);
}

int
fuji_pic_get_thumb (Camera *camera, unsigned int i, unsigned char **data,
		    unsigned int *size, GPContext *context)
{
	unsigned char cmd[6];

	/* It seems that all thumbnails have the size of 60 x 175. */
	*size = 60 * 175;
	*data = malloc (sizeof (char) * *size);
	if (!*data) {
		gp_context_error (context, _("Could not allocate "
			"%i byte(s) for downloading the thumbnail."), *size);
		return (GP_ERROR_NO_MEMORY);
	}

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_PIC_GET_THUMB;
	cmd[2] = 2;
	cmd[3] = 0;
	cmd[4] = i;
	cmd[5] = (i >> 8);

	CRF (fuji_transmit (camera, cmd, 6, *data, size, context), *data);
	GP_DEBUG ("Download of thumbnail completed.");

	return (GP_OK);
}

int
fuji_pic_get (Camera *camera, unsigned int i, unsigned char **data,
	      unsigned int *size, GPContext *context)
{
	unsigned char cmd[6];

	/*
	 * First, get the size of the picture and allocate the necessary
	 * memory. Some cameras don't support the FUJI_CMD_PIC_SIZE command.
	 * We will then assume 66000 bytes.
	 */
	if (fuji_pic_size (camera, i, size, context) < 0)
		*size = 66000;

	*data = malloc (sizeof (char) * *size);
	if (!*data) {
		gp_context_error (context, _("Could not allocate "
			"%i byte(s) for downloading the picture."), *size);
		return (GP_ERROR_NO_MEMORY);
	}

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_PIC_GET;
	cmd[2] = 2;
	cmd[3] = 0;
	cmd[4] = i;
	cmd[5] = (i >> 8);

	CRF (fuji_transmit (camera, cmd, 6, *data, size, context), *data);
	GP_DEBUG ("Download of picture completed (%i byte(s)).", *size);

	return (GP_OK);
}

int
fuji_date_get (Camera *camera, FujiDate *date, GPContext *context)
{
	unsigned char cmd[4], buf[1024];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_DATE_GET;
	cmd[2] = 0;
	cmd[3] = 0;

	CR (fuji_transmit (camera, cmd, 4, buf, &buf_len, context));
	CLEN (buf_len, 14);

	date->year  = buf[0] * 1000 + buf[1] * 100 + buf[ 2] * 10 + buf[ 3];
	date->month =                                buf[ 4] * 10 + buf[ 5];
	date->day   =                                buf[ 6] * 10 + buf[ 7];
	date->hour  =                                buf[ 8] * 10 + buf[ 9];
	date->min   =                                buf[10] * 10 + buf[11];
	date->sec   =                                buf[12] * 10 + buf[13];

	return (GP_OK);
}

int
fuji_avail_mem (Camera *camera, unsigned int *avail_mem, GPContext *context)
{
	unsigned char cmd[4], buf[1024];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_AVAIL_MEM;
	cmd[2] = 0;
	cmd[3] = 0;

	CR (fuji_transmit (camera, cmd, 4, buf, &buf_len, context));
	CLEN (buf_len, 4);

	*avail_mem = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);

	return (GP_OK);
}

int
fuji_date_set (Camera *camera, FujiDate date, GPContext *context)
{
	unsigned char cmd[1024], buf[1024];
	unsigned int buf_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_DATE_SET;
	cmd[2] = 14;
	cmd[3] = 0;
	sprintf ((char*)cmd + 4, "%04d%02d%02d%02d%02d%02d", date.year, date.month,
		 date.day, date.hour, date.min, date.sec);

	CR (fuji_transmit (camera, cmd, 4, buf, &buf_len, context));

	return (GP_OK);
}

int
fuji_upload_init (Camera *camera, const char *name, GPContext *context)
{
	unsigned char cmd[1024], buf[1024];
	unsigned int buf_len = 0, cmd_len = 0;

	cmd[0] = 0;
	cmd[1] = FUJI_CMD_UPLOAD_INIT;
	cmd[2] = strlen (name);
	cmd[3] = 0;
	memcpy (cmd + 4, name, strlen (name));
	cmd_len = strlen (name) + 4;
	CR (fuji_transmit (camera, cmd, cmd_len, buf, &buf_len, context));
	CLEN (buf_len, 1);

	/* Check the response */
	switch (buf[0]) {
	case FUJI_ACK:
		break;
	case FUJI_NAK:
		gp_context_error (context, _("The camera does not "
			"accept '%s' as filename."), name);
		return (GP_ERROR);
	default:
		gp_context_error (context, _("Could not initialize upload "
			"(camera responded with 0x%02x)."), buf[0]);
		return (GP_ERROR);
	}

	return (GP_OK);
}

#define CHUNK_SIZE 512

int
fuji_upload (Camera *camera, const unsigned char *data,
	     unsigned int size, GPContext *context)
{
	unsigned char cmd[1024], c;
	unsigned int cmd_len, chunk_size, retries, i;

	/*
	 * The old driver did upload in chunks of 512 bytes. I don't know
	 * if this is optimal.
	 */
	cmd[0] = 0;
	cmd[1] = FUJI_CMD_UPLOAD;
	for (i = 0; i < size; i+= CHUNK_SIZE) {
		chunk_size = MIN (CHUNK_SIZE, size - i);
		cmd[2] = chunk_size;
		cmd[3] = (chunk_size >> 8);
		memcpy (cmd + 4, data + i, chunk_size);
		cmd_len = chunk_size + 4;

		retries = 0;
		while (1) {

			/* Give the user the possibility to cancel. */
			if (gp_context_cancel (context) ==
					GP_CONTEXT_FEEDBACK_CANCEL) {
				CR (fuji_reset (camera, context));
				return (GP_ERROR_CANCEL);
			}

			CR (fuji_send (camera, cmd, cmd_len,
				(i + CHUNK_SIZE < size) ? 0 : 1, context));

			/* Receive ACK (hopefully) */
			CR (gp_port_read (camera->port, (char *)&c, 1));
			switch (c) {
			case ACK:
				break;
			case NAK:

				/* Camera didn't like the command */
				if (++retries > 1) {
					gp_context_error (context,
						_("Camera rejected "
							"the command."));
					return (GP_ERROR);
				}
				continue;

			case EOT:

				/* Camera got fed up and reset itself. */
				gp_context_error (context, _("Camera reset "
							"itself."));
				return (GP_ERROR);

			default:
				gp_context_error (context, _("Camera sent "
					"unexpected byte 0x%02x."), c);
				return (GP_ERROR_CORRUPTED_DATA);
			}
			break;
		}
	}

	return (GP_OK);
}

int
fuji_set_speed (Camera *camera, FujiSpeed speed, GPContext *context)
{
	unsigned char cmd[5], buf[1024];
	unsigned int buf_len = 0;

	GP_DEBUG("Attempting to set speed to %i",speed);
	cmd[0] = 1;
	cmd[1] = FUJI_CMD_SPEED;
	cmd[2] = 1;
	cmd[3] = 0;
	cmd[4] = speed;
	CR (fuji_transmit (camera, cmd, 5, buf, &buf_len, context));
	CLEN (buf_len, 1);

	/* Check the response */
	switch (buf[0]) {
	case FUJI_ACK:
		break;
	case FUJI_NAK:
		gp_context_error (context, _("The camera does not "
			"support speed %i."), speed);
		return (GP_ERROR_NOT_SUPPORTED);
	default:
		gp_context_error (context, _("Could not set speed to "
			"%i (camera responded with %i)."), speed, buf[0]);
		return (GP_ERROR);
	}

	GP_DEBUG("Success with speed %i.",speed);

	/* Reset the connection */
	CR (fuji_reset (camera, context));

	return (GP_OK);
}

static int
fuji_reset (Camera *camera, GPContext *context)
{
	unsigned char c = EOT;

	CR (gp_port_write (camera->port, (char *)&c, 1));

	return (GP_OK);
}

#if 0

static int fuji_get_flash_mode (void)
{
	cmd0 (0, 0x30);
	return answer[4];
}

static int fuji_set_flash_mode (int mode)
{
	cmd1 (0, 0x32, mode);
	return answer[4];
}

static int fuji_picture_size (int i)
{
	cmd2 (0, FUJI_SIZE, i);
	return answer[4] + (answer[5] << 8) + (answer[6] << 16) + (answer[7] << 24);
}

static int charge_flash (void)
{
	cmd2 (0, FUJI_CHARGE_FLASH, 200);
	return answer[4];
}

static int take_picture (void)
{
	cmd0 (0, FUJI_CMD_TAKE);
	return answer[4] + (answer[5] << 8) + (answer[6] << 16) + (answer[7] << 24);
}

static int del_frame (int i)
{
	cmd2 (0, FUJI_CMD_DELETE, i);
	return answer[4];
}

int
fuji_get_cmds (Camera *camera, unsigned char *cmds, GPContext *context)
{
	unsigned char answer[0xff];
	unsigned int answer_len = 0xff;

	memset (cmds, 0, 0xff);

	CR (fuji_cmd0 (camera, 0, FUJI_CMD_CMDS_VALID, answer, &answer_len,
		       context));
	int i;
	DBG("Get command list");
	memset(fjd->has_cmd, 0, 256);
	if (cmd0 (0, FUJI_CMDS_VALID)) return ;
	for (i = 4; i < answer_len; i++)
	  fjd->has_cmd[answer[i]] = 1;
}

static int get_picture_info(int num,char *name,CameraPrivateLibrary *camdata){

          DBG("Getting name...");

	  strncpy(name,fuji_picture_name(num),100);

	  DBG2("%s\n",name);

	  /*
	   * To find the picture number, go to the first digit. According to
	   * recent Exif specs, n_off can be either 3 or 4.
	   */
	  if (camdata->has_cmd[FUJI_SIZE])   fuji_size=fuji_picture_size(num);
	  else {
	    fuji_size=70000;  /* this is an overestimation for DS7 */
	    DBG2("Image size not obtained, guessing %d",fuji_size);
	  };
	  return (fuji_size);
};

static void get_picture_list (FujiData *fjd)
{
	int i, n_off;
	char *name;
	struct stat st;

	pictures = fuji_nb_pictures();
	maxnum = 100;
	free(pinfo);
	pinfo = calloc(pictures+1, sizeof(struct pict_info));
	if (pinfo==NULL) {
	  DBG("Allocation error");
	};

	for (i = 1; i <= pictures; i++) {
	        DBG("Getting name...");

	        name = strdup(fuji_picture_name(i));
	        pinfo[i].name = name;

		DBG2("%s\n",name);

		/*
		 * To find the picture number, go to the first digit. According to
		 * recent Exif specs, n_off can be either 3 or 4.
		 */
		n_off = strcspn(name, "0123456789");
		if ((pinfo[i].number = atoi(name+n_off)) > maxnum)
			maxnum = pinfo[i].number;
		if (fjd->has_cmd[FUJI_SIZE])	pinfo[i].size = fuji_picture_size(i);
		else pinfo[i].size=65000;
		pinfo[i].ondisk = !stat(name, &st);
	}
}

static int download_picture(int n,int thumb,CameraFile *file,CameraPrivateLibrary *fjd)
{
	clock_t t1, t2;
	char name[100];
	long int file_size;
	char *data;

	DBG3("download_picture: %d,%s",n,thumb?"thumb":"pic");

	if (!thumb) {
	        fuji_size=get_picture_info(n,name,fjd);
		DBG3("Info %3d   %12s ", n, name);
	}
	else fuji_size=10500;  /* Probly not same for all cams, better way ? */

	DBG2("calling download for %d bytes",fuji_size);

	t1 = times(0);
	if (cmd2(0, (thumb?0:0x02), n)==-1) return(GP_ERROR);

	DBG3("Download :%4d actual bytes vs expected %4d bytes\n",
	     fuji_count ,fuji_size);

	/* file->bytes_read=fuji_count; */
	/* add room for thumb-decode */
	/* file->size=fuji_count+(thumb?128:0); */
	file_size=fuji_count+(thumb?128:0);

	data=malloc(file_size);

	if (data==NULL) return(GP_ERROR);

	memcpy(data,fuji_buffer,fuji_count);

	gp_file_set_data_and_size(file,data,file_size);

	t2 = times(0);

	DBG2("%3d seconds, ", (int)(t2-t1) / CLK_TCK);
	DBG2("%4d bytes/s\n",
		fuji_count * CLK_TCK / (int)(t2-t1));

	if (fjd->has_cmd[17]&&!thumb){
	if (!thumb&&(fuji_count != fuji_size)){
	/* gp_frontend_status(NULL,"Short picture file--disk full or quota exceeded\n"); */
	    return(GP_ERROR);
	  };
	};
	return(GP_OK);

}

static int fuji_free_memory (void)
{
	cmd0 (0, FUJI_CMD_FREE_MEM);
	return answer[5] + (answer[6]<<8) + (answer[7]<<16) + (answer[8]<<24);
}

static int upload_pic (const char *picname)
{
	unsigned char buffer[516];
	const char *p;
	struct stat st;
	FILE *fd;
	int c, last, len, free_space;

	fd = fopen(picname, "r");
	if (fd == NULL) {
	  /* update_status("Cannot open file for upload"); */
		return 0;
	}
	if (fstat(fileno(fd), &st) < 0) {
		DBG("fstat");
		return 0;
	}
	free_space = fuji_free_memory();
	fprintf(stderr, "Uploading %s (size %d, available %d bytes)\n",
		picname, (int) st.st_size, free_space);
	if (st.st_size > free_space) {
		fprintf(stderr, "  not enough space\n");
		return 0;
	}
	if ((p = strrchr(picname, '/')) != NULL)
		picname = p+1;
	if (strlen(picname) != 12 || memcmp(picname,"DSC",3) || memcmp(picname+8,".JPG",4)) {
		picname = auto_rename();
		fprintf(stderr, "  file renamed %s\n", picname);
	}
	buffer[0] = 0;
	buffer[1] = 0x0F;
	buffer[2] = 12;
	buffer[3] = 0;
	memcpy(buffer+4, picname, 12);
	cmd(16, buffer);
	if (answer[4] != 0) {
		fprintf(stderr, "  rejected by the camera\n");
		return 0;
	}
	buffer[1] = 0x0E;
	while(1) {
		len = fread(buffer+4, 1, 512, fd);
		if (!len) break;
		buffer[2] = len;
		buffer[3] = (len>>8);
		last = 1;
		if ((c = getc(fd)) != EOF) {
			last = 0;
			ungetc(c, fd);
		}
		if (!last && interrupted) {
			fprintf(stderr, "Interrupted!\n");
			exit(1);
		}
again:
		send_packet(4+len, buffer, last);
		/*wait_for_input(1);*/
		if (get_byte() == 0x15)
			goto again;
	}
	fclose(fd);
	fprintf(stderr, "  looks ok\n");
	return 1;
}

#endif
